데이터분석2016. 6. 8. 17:33

7일차네요!!!

저번의 Rossman 가게 마케팅 분석 3번째로 이어서 진행을 하는군요 이번 차수에서는 끝을 봐야 또 새로운 스크립트를 맛볼텐데.. 


저번까지 박스플롯으로 학교 휴일이냐 아니냐에 따라서 Rossman 가게들의 판매 실적을 보았구요.

이번에는 이어서 ggplot으로 Sales & Customers가 0 이 아닌 train 데이터를 가지고  scatter plot을 그려보겠습니다.


ggplot(train[train$Sales != 0 & train$Customers != 0],
       aes(x = log(Customers), y = log(Sales))) + 
    geom_point(alpha = 0.2) + geom_smooth()
## geom_smooth: method="auto" and size of largest group is >=1000, so using gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to change the smoothing method.

당연한 얘기지만 역시 Customers가 많아야 Sales가 많다는 건 당연한건가보네요. 마치 y=x같은 그래프와 유사한 모습을 보여주네요. 상관분석을 했는데 이렇게 이쁘게 나오면 참 기분이 좋을텐데 여튼 넘어가겟습니다.


이번에는 Promo  행사여부에 따른 판매량 추이를 보죠

ggplot(train[train$Sales != 0 & train$Customers != 0],
       aes(x = factor(Promo), y = Sales)) + 
    geom_jitter(alpha = 0.1) +
    geom_boxplot(color = "yellow", outlier.colour = NA, fill = NA)


그릐고 바로 Customers는 얼마나 끌어들이나도 같이 보죠  -> 바로 전 boxplot과 비교를 할 목적인줄 알았는데 한단계 더 나가네요.. 위의 scatter plot 대신에 box plot으로 Cutomers를 카운트해보네요.


ggplot(train[train$Sales != 0 & train$Customers != 0],
       aes(x = factor(Promo), y = Customers)) + 
    geom_jitter(alpha = 0.1) +
    geom_boxplot(color = "yellow", outlier.colour = NA, fill = NA)


절대적이진 않지만 학교휴무여부보다 프로모션여부가 확실히 판매량 차이가 더 극명하네요  그리고 한번 더 강조합니다. Sales와 Customers가 0인것은 제외를 함으로써 biased될 가능성이 있기 때문이라고 합니다.  판매량은 고객수와 꽤 연관이 있다고 언급했고.. 그런데 여기에서 Promo가 보면 고객수의 차이가 그닥 없죠잉? 즉 뭐냐면  Promo가 결국 온 손님들이 물건을 더 사게 만드는 효과가 있지 더 끌어들이는 효과는 없다는걸 알수가 있죠. Promo의 factor 변수값이 0인 boxplot과 1의 boxplot은 상당히 overlap되니깐요.. Customers는 그닥 변화가 없지만 Sales은 꽤 차이나는걸 보고 우린 알수가 있어야 합니다. 고객별 소비금액을 봅시다!

with(train[train$Sales != 0 & train$Promo == 0], mean(Sales / Customers))
## [1] 8.941128
with(train[train$Sales != 0 & train$Promo == 1], mean(Sales / Customers))
## [1] 10.17896

이게 유로인데요 한 1유로 이상을 더 지갑에서 여는걸 볼수가 있습니다


여기에서 테이블로 가게 오픈여부와 프로모션 여부에 대한 카운팅 테이블을 봅시다.

table(ifelse(train$Sales != 0, "Sales > 0", "Sales = 0"),
      ifelse(train$Promo, "Promo", "No promo"))
##            
##             No promo  Promo
##   Sales = 0   161666  11205
##   Sales > 0   467463 376875

가게들이 문을 닫았을 때 프로모션기간인 경우가 좀 있네요 그리고 가게 문을 열었을 경우 45프로의 가게가 프로모션 진행중이구요

다음은 웃픈 얘기인데 오픈해서 손님이 있어도 판매가 없는 가게가 54곳이나 뽑히네요.

table(ifelse(train$Open == 1, "Opened", "Closed"),
      ifelse(train$Sales > 0, "Sales > 0", "Sales = 0"))
##         
##          Sales = 0 Sales > 0
##   Closed    172817         0
##   Opened        54    844338

그 54개를 자세히 보죠....


# That tends to happen on consecutive days. Some stores even had customers
# (who bought nothing?)
train[Open == 1 & Sales == 0]
##     Store DayOfWeek       Date Sales Customers Open Promo StateHoliday
##  1:   762         4 2013-01-17     0         0    1     0            0
##  2:   232         4 2013-01-24     0         0    1     1            0
##  3:   339         3 2013-01-30     0         0    1     0            0
##  4:   339         4 2013-01-31     0         0    1     0            0
##  5:   259         4 2013-02-07     0         0    1     1            0
##  6:   353         6 2013-03-16     0         0    1     0            0
##  7:   948         4 2013-04-25     0         5    1     1            0
##  8:   589         1 2013-04-29     0         0    1     1            0
##  9:   364         2 2013-05-07     0         0    1     0            0
## 10:   364         3 2013-05-08     0         0    1     0            0
## 11:   681         5 2013-05-10     0         0    1     0            0
## 12:   700         3 2013-06-05     0         0    1     1            0
## 13:   665         5 2013-06-28     0         0    1     0            0
## 14:   665         6 2013-06-29     0         0    1     0            0
## 15:  1039         2 2013-07-09     0         0    1     0            0
## 16:  1039         3 2013-07-10     0         0    1     0            0
## 17:   927         4 2013-08-08     0         0    1     0            0
## 18:   391         3 2013-08-28     0         0    1     1            0
## 19:   663         1 2013-09-02     0         0    1     0            0
## 20:   983         5 2014-01-17     0         0    1     0            0
## 21:   983         6 2014-01-18     0         0    1     0            0
## 22:   623         5 2014-01-24     0         0    1     1            0
## 23:   623         6 2014-01-25     0         0    1     0            0
## 24:    25         3 2014-02-12     0         0    1     0            0
## 25:    25         4 2014-02-13     0         0    1     0            0
## 26:   327         3 2014-03-12     0         0    1     0            0
## 27:   986         2 2014-03-18     0         0    1     1            0
## 28:   850         6 2014-03-29     0         0    1     0            0
## 29:   661         5 2014-04-04     0         0    1     1            0
## 30:  1100         2 2014-04-29     0         3    1     1            0
## 31:  1100         3 2014-04-30     0         0    1     1            0
## 32:  1017         3 2014-06-04     0         0    1     1            0
## 33:  1017         4 2014-06-05     0         0    1     1            0
## 34:    57         2 2014-07-01     0         0    1     1            0
## 35:   925         4 2014-07-03     0         0    1     1            0
## 36:   102         6 2014-07-12     0         0    1     0            0
## 37:   882         3 2014-07-23     0         0    1     0            0
## 38:   887         3 2014-07-23     0         0    1     0            0
## 39:   102         4 2014-07-24     0         0    1     0            0
## 40:   238         4 2014-07-24     0         0    1     0            0
## 41:   303         4 2014-07-24     0         0    1     0            0
## 42:   387         4 2014-07-24     0         0    1     0            0
## 43:    28         2 2014-09-02     0         0    1     1            0
## 44:    28         3 2014-09-03     0         0    1     1            0
## 45:    28         4 2014-09-04     0         0    1     1            0
## 46:   548         5 2014-09-05     0         0    1     1            0
## 47:   835         3 2014-09-10     0         0    1     0            0
## 48:   227         4 2014-09-11     0         0    1     0            0
## 49:   835         4 2014-09-11     0         0    1     0            0
## 50:   357         1 2014-09-22     0         0    1     0            0
## 51:   708         3 2014-10-01     0         0    1     1            0
## 52:   699         4 2015-02-05     0         0    1     1            0
## 53:   674         4 2015-03-26     0         0    1     0            0
## 54:   971         5 2015-05-15     0         0    1     0            0
##     Store DayOfWeek       Date Sales Customers Open Promo StateHoliday
##     SchoolHoliday
##  1:             0
##  2:             0
##  3:             0
##  4:             0
##  5:             0
##  6:             0
##  7:             0
##  8:             0
##  9:             0
## 10:             0
## 11:             0
## 12:             0
## 13:             0
## 14:             0
## 15:             0
## 16:             0
## 17:             1
## 18:             1
## 19:             1
## 20:             0
## 21:             0
## 22:             0
## 23:             0
## 24:             0
## 25:             0
## 26:             0
## 27:             0
## 28:             0
## 29:             0
## 30:             0
## 31:             0
## 32:             0
## 33:             0
## 34:             0
## 35:             0
## 36:             0
## 37:             1
## 38:             0
## 39:             1
## 40:             1
## 41:             1
## 42:             1
## 43:             1
## 44:             1
## 45:             0
## 46:             1
## 47:             0
## 48:             0
## 49:             0
## 50:             0
## 51:             0
## 52:             0
## 53:             0
## 54:             1
##     SchoolHoliday

가슴 아프지만 우리는 이 가게들을 캐내서 판매부진 아니.. 판매불능의 상황을 따져봅시다...!! 우선 Store의 리스트를 가져와 sales가 0인것들이 많은 순대로 sort를 했네요. 여기에서 sort는 별로 무의미해보이고요 여튼 zeroPerStore의 가게를



zerosPerStore <- sort(tapply(train$Sales, list(train$Store), function(x) sum(x == 0)))
hist(zerosPerStore,100)

여기에서 histogramd 한참 봤습니다 ㅜ.ㅜ  Exploratory Analysis의 경우는 왔다갔다 합니다. 여튼 위에 zeroStore에서 구한 가게별 문닫는 일수에 대한 데이터를 놓고 보네요. 가게별 판매가 0인 날로 그치는 날수가 제일 많은게 160~200일 동안 장사가 안되는 날이 있네요..  자세히 들여다볼까요?


여기에서는 이 구한 zerosPerStore은 sort가 되어 있죠 default가 ascending order라 판매가 0인 날이 가장 많은 가게들 10개를 가져와봅니다.  그리고 그 가게별로 plot을 찍어보네요 Sales에 대한 ploat을 찍고 보니  특정 구간에 판매량이 0으로 몰린다는겁니다. 중간에 뻥 아니면 시작부분에 뻥

# Stores with the most zeros in their sales:
tail(zerosPerStore, 10)
## 105 339 837  25 560 674 972 349 708 103 
## 188 188 191 192 195 197 240 242 255 311
# Some stores were closed for some time, some of those were closed multiple times
plot(train[Store == 972, Sales], ylab = "Sales", xlab = "Days", main = "Store 972")

plot(train[Store == 103, Sales], ylab = "Sales", xlab = "Days", main = "Store 103")

plot(train[Store == 708, Sales], ylab = "Sales", xlab = "Days", main = "Store 708")

물론 판매량에 있어 0을 안 찍은 가게들도 있고 일요일/휴무일날 오픈해서 판매한 exceptions들도 있다고 합니다. 특히 일요일은 판매가 잘된다고 하네요.. 


ggplot(train[Store == 85], 
       aes(x = Date, y = Sales, 
           color = factor(DayOfWeek == 7), shape = factor(DayOfWeek == 7))) + 
    geom_point(size = 3) + ggtitle("Sales of store 85 (True if sunday)")

ggplot(train[Store == 262], 
       aes(x = Date, y = Sales, 
           color = factor(DayOfWeek == 7), shape = factor(DayOfWeek == 7))) + 
    geom_point(size = 3) + ggtitle("Sales of store 262 (True if sunday)")


그리고 주일별로 한번 판매량을 boxplot찍어보니!!! 일요일은 판매량의 변동성이 꽤 높네요 ㄷㄷㄷㄷ


ggplot(train[Sales != 0],
       aes(x = factor(DayOfWeek), y = Sales)) + 
    geom_jitter(alpha = 0.1) + 
    geom_boxplot(color = "yellow", outlier.colour = NA, fill = NA)


자 이제 train데이터는 그만 잠시 접어두고 주어진 데이터 셋중에 store 즉   가게 자체애 대한 정보를 받았죠. 가게 대장이라고 부를께요.

이데이터를summary()함수를 통해서 살펴보겠습니다.


summary(store)
##      Store         StoreType          Assortment       
##  Min.   :   1.0   Length:1115        Length:1115       
##  1st Qu.: 279.5   Class :character   Class :character  
##  Median : 558.0   Mode  :character   Mode  :character  
##  Mean   : 558.0                                        
##  3rd Qu.: 836.5                                        
##  Max.   :1115.0                                        
##                                                        
##  CompetitionDistance CompetitionOpenSinceMonth CompetitionOpenSinceYear
##  Min.   :   20.0     Min.   : 1.000            Min.   :1900            
##  1st Qu.:  717.5     1st Qu.: 4.000            1st Qu.:2006            
##  Median : 2325.0     Median : 8.000            Median :2010            
##  Mean   : 5404.9     Mean   : 7.225            Mean   :2009            
##  3rd Qu.: 6882.5     3rd Qu.:10.000            3rd Qu.:2013            
##  Max.   :75860.0     Max.   :12.000            Max.   :2015            
##  NA's   :3           NA's   :354               NA's   :354             
##      Promo2       Promo2SinceWeek Promo2SinceYear PromoInterval     
##  Min.   :0.0000   Min.   : 1.0    Min.   :2009    Length:1115       
##  1st Qu.:0.0000   1st Qu.:13.0    1st Qu.:2011    Class :character  
##  Median :1.0000   Median :22.0    Median :2012    Mode  :character  
##  Mean   :0.5121   Mean   :23.6    Mean   :2012                      
##  3rd Qu.:1.0000   3rd Qu.:37.0    3rd Qu.:2013                      
##  Max.   :1.0000   Max.   :50.0    Max.   :2015                      
##                   NA's   :544     NA's   :544


1115개의 가게별로 StoreType / Assortment 같은 구분이 있고 경쟁업체 위치 그리고 경쟁업체 오픈 년월과 Promotion 에대한 추가 정보가 있네요. 프로모션2는 뭐지? 흠흠..

table(store$StoreType)
## 
##   a   b   c   d 
## 602  17 148 348
table(store$Assortment)
## 
##   a   b   c 
## 593   9 513
# There is a connection between store type and type of assortment
table(data.frame(Assortment = store$Assortment, StoreType = store$StoreType))
##           StoreType
## Assortment   a   b   c   d
##          a 381   7  77 128
##          b   0   9   0   0
##          c 221   1  71 220

이렇게 구분 변수 2개는 살펴보았습니다.  넘어가죠!


hist(store$CompetitionDistance, 100)

경쟁업체거리는 있다면 뭐 가까운데 제일 많이 있다라는거죠 거리가 멀수록 경쟁업체가 있어도 없다고 체크하겠죠 뭐 정보관리할때 말이죠..


뭐 이건 년월을 "-"로 묶어서 CompetitoinOpenSince라는 값에 담았습니다. 그리고 2015년 10월 기준으로 오픈한 년수를 구해서 historgram으로 찍어봤을때 보통 20년 이내의 역사를 가지고 있는 가게가 대부분이네요.

# Convert the CompetitionOpenSince... variables to one Date variable
store$CompetitionOpenSince <- as.yearmon(paste(store$CompetitionOpenSinceYear, 
                                               store$CompetitionOpenSinceMonth, sep = "-"))
# One competitor opened 1900
hist(as.yearmon("2015-10") - store$CompetitionOpenSince, 100, 
     main = "Years since opening of nearest competition")


그다음은 promo2가 시작된 이후의 날수를 계산하네요... 이건 머여 날수를 구해서 머하자는건지....넘어가거씁니다. 아마 제가 데이터셋 설명을 다시 봐서 업데이트하든가 해야겠네요.

 Convert the Promo2Since... variables to one Date variable
# Assume that the promo starts on the first day of the week
store$Promo2Since <- as.POSIXct(paste(store$Promo2SinceYear, 
                                   store$Promo2SinceWeek, 1, sep = "-"),
                             format = "%Y-%U-%u")
hist(as.numeric(as.POSIXct("2015-10-01", format = "%Y-%m-%d") - store$Promo2Since), 
     100, main = "Days since start of promo2")


프로모션 주기구요.

table(store$PromoInterval)
## 
##                   Feb,May,Aug,Nov  Jan,Apr,Jul,Oct Mar,Jun,Sept,Dec 
##              544              130              335              106


이건 프로모션 주기별로 boxplot을 그렸는데 글쎄요 뭐 둘쑥날쑥하네요 프로모션기별마다.

# Merge store and train 
train_store <- merge(train, store, by = "Store")
ggplot(train_store[Sales != 0], aes(x = factor(PromoInterval), y = Sales)) + 
    geom_jitter(alpha = 0.1) + 
    geom_boxplot(color = "yellow", outlier.colour = NA, fill = NA)


그다음으로  store type과 assortment types을 시각분석을 통해 한번 뭔지 보자. 

그래프가 마치 지렁이같이 그려지는건 geom_smooth 때문이고 여튼 factor를 storeType과 assortment type을 넣고 돌려봤더니 뭔가 차이가 확실히 있네요. 


ggplot(train_store[Sales != 0], 
       aes(x = as.Date(Date), y = Sales, color = factor(StoreType))) + 
    geom_smooth(size = 2)
## geom_smooth: method="auto" and size of largest group is >=1000, so using gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to change the smoothing method.

ggplot(train_store[Customers != 0], 
       aes(x = as.Date(Date), y = Customers, color = factor(StoreType))) + 
    geom_smooth(size = 2)
## geom_smooth: method="auto" and size of largest group is >=1000, so using gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to change the smoothing method.

ggplot(train_store[Sales != 0], 
       aes(x = as.Date(Date), y = Sales, color = factor(Assortment))) + 
    geom_smooth(size = 2)
## geom_smooth: method="auto" and size of largest group is >=1000, so using gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to change the smoothing method.

ggplot(train_store[Sales != 0], 
       aes(x = as.Date(Date), y = Customers, color = factor(Assortment))) + 
    geom_smooth(size = 2)
## geom_smooth: method="auto" and size of largest group is >=1000, so using gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to change the smoothing method.



storetype과 assortment type을 보면 b가 우세하네요. 고객수면이나 판매량측면에서 말이죠!!

그리고 경쟁업체와의 거리는 좀 보면 직관적이지 못합니다. 여기에서 이러네요. 아마 자기 추측에는 다음 경쟁업체와의 거리가 가까운 가게의 경우는 일반적으로 도심안과 같은 이미 붐비는 지역이라 그럴거라고. 그러니 경쟁업체가 몰려있겠죠... 그래서 good bad를 가리기엔 그냥 뭐.cancel out 즉 상쇄된다는 말인듯합니다.

salesByDist <- aggregate(train_store[Sales != 0 & !is.na(CompetitionDistance)]$Sales, 
               by = list(train_store[Sales != 0 & !is.na(CompetitionDistance)]$CompetitionDistance), mean)
colnames(salesByDist) <- c("CompetitionDistance", "MeanSales")
ggplot(salesByDist, aes(x = log(CompetitionDistance), y = log(MeanSales))) + 
    geom_point() + geom_smooth()
## geom_smooth: method="auto" and size of largest group is <1000, so using loess. Use 'method = x' to change the smoothing method.




CompetitionOpenSinceYear값이 없을 경우때문인지 걍 CompetitionOpenSinceYear의 값 존재여부를 가지고 체크한거같습니다. 보시죠. 

ggplot(train_store[Sales != 0],
       aes(x = factor(!is.na(CompetitionOpenSinceYear)), y = Sales)) +
    geom_jitter(alpha = 0.1) +
    geom_boxplot(color = "yellow", outlier.colour = NA, fill = NA) +
    ggtitle("Any competition?")



별차이가 없어보이는데 우선 뭐.. Sales가 있는경우가 더 높네요. 방금 말한 그런 이유의 연장선인거같습니다.


이 다음은 아주 쪼금남았는데 시간 나면 바로 업데이트하는걸로하고. 마칩니다.

캐글도 뭔가 완전하진 않습니다. 결국 데이터 모델을 만들어내느걸 해야하는데 그런건 안다뤄지고 Exploratory Analysis 단계까지만 공개되는거 같네요.  금주와 다음달부터 빅데이터/통계스터디를 하게 되는데 심화해서 진행되는건 업데이트해서 공유를 하도록 하겠슴니다. 그럼 조만간 업데이트할게요~~

















'데이터분석' 카테고리의 다른 글

Kaggler's Day #9  (0) 2017.01.12
Kaggler's Day #8  (0) 2016.06.10
Kaggler's Day #6  (0) 2016.05.31
Kaggler's Day #5  (0) 2016.05.27
Kaggler's Day #3  (0) 2016.05.16
Posted by 억사마