<jupyter_start><jupyter_text>e-Commerce Cart Data Analysis- Obj    - 수천만 건의 커머스 데이터를 이용하여 재구매 예측 모형 개발- ML-Model     - XGboost- Process    - Data Load -> EDA -> Data Wrangling -> Data Modeling (ML)- Data Description    - aisles : 상품 카테고리 (소분류, 1~6)        - aisle_id : 소분류 번호        - aisle : 소분류명            - departments : 상품 카테고리 (대분류, 1~6)        - department_id : 대분류 번호        - department : 대분류명            - orderp : __?__        - order_id : 주문번호        - product_id : 상품번호        - add_to_cart_order : 카트에 담은 순서        - reordered : 재주문 여부 (False : 0, True: 1)    - ordert : __훈련 데이터?__        - order_id : 주문번호        - product_id : 상품번호        - add_to_cart_order : 카트에 담은 순서        - reordered : 재주문 여부 (False : 0, True: 1)    - orders : 주문정보가 담겨 있는 테이블 ?        - user_id 기준으로 days_since_prior_order 를 갱신하는 것인지?        - order_id : 주문번호        - user_id : 유저번호        - eval_set : __?__ (prior / train / test)        - order_number : __주문순서 ?__ (1~100)        - order_dow : __?__        - order_hour_of_day : __?__        - days_since_prior_order : 재주문한 날짜    - products : 제품 정보가 담겨 있는 테이블        - product_id : 상품번호        - product_name : 상품명        - aisle_id : 소분류 번호        - department_id : 대분류 번호    - library    - data.table        -         - fread    - dplyr        - 데이터 전처리 라이브러리    - tidyr        -     - CKmeans.1d.dp        -     - DBI        -     - RMYSQL        - Database와 연동하는 라이브러리    - XGboost        - Machine Learning Library        -     - Decision Tree        - Machine Learning Library        - 의사결정나무        - https://docs.google.com/spreadsheets/d/1Di_diD4tTtSwFH-7bvopdLzsc602HrLrLnCty_xOQ1A/editgid=0- [instacart](http://bongholee.com/2017/07/%EC%9D%B8%EC%8A%A4%ED%83%80%EC%B9%B4%ED%8A%B8instacart-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%84%EC%84%9D%ED%95%B4%EB%B3%B4%EA%B8%B01/)- [insta-cart](https://www.kaggle.com/philippsp/exploratory-analysis-instacart/notebook)- [통계가 탐정이라면 데이터는 단서다](http://bongholee.com/2017/08/%EC%B1%85-%ED%86%B5%EA%B3%84%EA%B0%80-%ED%83%90%EC%A0%95%EC%9D%B4%EB%9D%BC%EB%A9%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%8A%94-%EB%8B%A8%EC%84%9C%EC%9D%B4%EB%8B%A4-%EB%B2%8C%EA%B1%B0%EB%B2%97%EC%9D%80/)- [불확실한 시대에 확률적 사고가 필요한 이유](http://bongholee.com/2017/09/%EB%B6%88%ED%99%95%EC%8B%A4%ED%95%9C-%EC%8B%9C%EB%8C%80%EC%97%90-%ED%99%95%EB%A5%A0%EC%A0%81-%EC%82%AC%EA%B3%A0%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0-%ED%96%89%EC%9A%B4%EC%97%90/)- [예측은 애시당초 정답을 찾으려는 게 아니다](http://bongholee.com/2017/06/%EC%98%88%EC%B8%A1%EC%9D%80-%EC%95%A0%EC%8B%9C%EB%8B%B9%EC%B4%88-%EC%A0%95%EB%8B%B5%EC%9D%84-%EC%B0%BE%EC%9C%BC%EB%A0%A4%EB%8A%94%EA%B2%8C-%EC%95%84%EB%8B%88%EB%8B%A4%EC%8B%A0%ED%98%B8%EC%99%80/)- [Data Science For Good: DonorsChoose.org](https://www.kaggle.com/donorschoose/io) 큰 그림을 그릴 수 있는 설명이 필요한 듯 싶어요- 이전 시간에 엑셀 파일로 시각화해서 보여주셨듯이요!- 어떤 목적을 갖고, 어떤 어떤 데이터가(테이블이) 있고, 거기에서 어떤 테이블의 어떤 요소들을 조합해서 어떤 feature들을 만들 것이고, 어떤 새로운 테이블을 만들어서, 전처리한 다음에, 어떤 식으로 모델링을 해서, 어떤 결과를 도출할 것이다! 이런 요약이 필요한 것 같아요 수영님!  1. Import Libraries<jupyter_code>## Import Library ---------------------------------------------------------------
library('data.table')
library('dplyr')
library('tidyr')
library('Ckmeans.1d.dp')
library('DBI')
library('RMySQL')<jupyter_output><empty_output><jupyter_text>2. Connect R to Mysql<jupyter_code>## Connect R to Mysql ---------------------------------------------------------------
mydb = dbConnect(MySQL(),	 user='id',	 password='<PASSWORD>',	
                 dbname='mkt', host='127.0.0.1')
## Connect R to Mysql ---------------------------------------------------------------
mydb = dbConnect(MySQL(),	 user='root',	 password='<PASSWORD>!',	
                 dbname='mkt', host='127.0.0.1')<jupyter_output><empty_output><jupyter_text>3. Load Data<jupyter_code>## Load Data ---------------------------------------------------------------
aisles = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/aisles.csv")
departments = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/departments.csv")
orderp = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/order_products__prior.csv")
ordert = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/order_products__train.csv")
orders = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/orders.csv")
products = fread("/Users/mac/Downloads/dev/study/SQL&R/rawdata/products.csv")
# products -----------
head(aisles)       # 소분류(상품 종류)
head(departments)  # 대분류(상품 종류)
head(orderp)       # 주문번호, 상품번호, 카트에 넣은 순서, 재구매 여부(0: False, 1: True)
head(ordert)       # 학습데이터
head(orders)       # order_id는 unique 값
head(products)     # aisle_id, department_id 와 join<jupyter_output><empty_output><jupyter_text>4. Reshape Data- 1) inner_join, match, mutate, group_by, summarise, arrange- 2) as.factor- inner_join : dataframe 간 join- match : 벡터간 위치 비교 Q. 2주차에 했던 방식과 다른데 그 이유는?- as.factor는 한 컬럼을 factor로 만들어준다는 명령어일텐데, factor가 무엇인지? 이렇게 하는 이유는?> - factor는 모델링 돌릴 때, 회귀분석을 돌린다면... 모델의 종속/독립 변수라는 걸 설정해주면 R이 조금 더 정확하게 이해를 한다.> - 굳이 하지 않아도 돌아가기는 하는데, 변경을 해서 돌린다.> - 변수 설정<jupyter_code>## Reshape data ------------------------------------------------------------
## ---------------------------------------------------------------
aisles$aisle <- as.factor(aisles$aisle)
departments$department <- as.factor(departments$department)
orders$eval_set <- as.factor(orders$eval_set)
products$product_name <- as.factor(products$product_name)<jupyter_output><empty_output><jupyter_text>Q.- 이전에 작성했던 쿼리는 aisle_id, department_id를 기준으로 inner_join 하는 건데, 지금 쿼리는 제외(-aisle_id, -department_id)하고 지우기?- rm을 하는 이유는?- 예전 쿼리처럼 주석이 달려있으면 더 좋을 것 같아요!- 주석처리된 과거 코드```SQLselect  *from products ainner join  aisles b        on  a.aisle_id = b.aisle_idinner join departments c        on a.department_id = c.department_id```- __현재 코드는?__<jupyter_code># products에 aisle, department 정보 붙이기 (inner_join)
# products <- products %>%
#     inner_join(aisles, by='aisle_id') %>%
#     inner_join(departments, by='department_id')

## products 테이블에 aisle, department 정보 붙이고, 기존 테이블(aisles, departments) 제거---------------------------------------------------------------
products <- products %>%
    inner_join(aisles) %>%
    inner_join(departments) %>%
    select(-aisle_id, -department_id)
rm(aisles, departments)

## match == inner_join ---------------------------------------------------------------
# ordert와 orders의 교집합 중에 orders의 위치를 찾아 ordert에 넣는다
ordert$user_id <- orders$user_id[match(ordert$order_id, orders$order_id)]

## 새로운 테이블(orders_products) 생성 후 기존 테이블(orderp) 제거 ---------------------------------------------------------------
# orders 테이블에 orderp inner_join(on order_id)
orders_products <- orders %>% inner_join(orderp, by = "order_id")
rm(orderp)

## 메모리 초기화(정리) ---------------------------------------------------------------
gc()<jupyter_output>Joining, by = "aisle_id"
Joining, by = "department_id"<jupyter_text>5. prd (products)- __train data ?__> - __orders_x ? == prd ?__```r 1. product_time column 생성 reodered는 0과 1로 이루어져 있음 (0: False, 1: True) product_time은 재구매 횟수orders_products %>%  arrange(user_id, order_number, product_id) %>%  group_by(user_id, product_id) %>%  mutate(product_time = row_number()) 2. ungroup ungroup을 해주는 이유는 group_by를 하기 위함일텐데 group한 것에 다시 group을 할 수 없어서 이런 것을 하는 것인가? sql의 having 같은 기능구 현을 위한 것인가? group_by에 다시 조건을 주기 위해orders_products %>%  arrange(user_id, order_number, product_id) %>%  group_by(user_id, product_id) %>%  mutate(product_time = row_number()) %>%  ungroup() %>% 3. group & summarise product_id로 다시 묶은 이유는?orders_products %>%  arrange(user_id, order_number, product_id) %>%  group_by(user_id, product_id) %>%  mutate(product_time = row_number()) %>%  ungroup() %>%  group_by(product_id) %>%  summarise(    prod_orders = n(),    prod_reorders = sum(reordered),    prod_first_orders = sum(product_time == 1),    prod_second_orders = sum(product_time == 2)  )```- orders_x 테이블 생성> - keyword: group_by, mutate, rnk, ungroup> - 1) user_id와 product_id를 group_by 한 다음에, mutate 함수로 rnk를 생성>> - rnk의 약자, row_number는 넘버링을 위한 것인데, cnt를 하기 위한 것?>> - 유저별, 상품별 구매한 순서를 매기기 위한 것>> - 한 유저가 상품을 몇 번 구매했는지 알기 위해 넘버링<jupyter_code>names(orders_products)
str(orders_products)
orders_products %>% head(10)
orders_products %>%
  arrange(user_id, order_number, product_id) %>%
  group_by(user_id, product_id) %>%
  mutate(product_time = row_number()) %>% head(10)<jupyter_output><empty_output><jupyter_text>5-1.<jupyter_code>## Products ----------------------------------------------------------------
## 새로운 테이블(prd) 생성 후, 기존 테이블(products) 제거
## 쿼리 처리 순서 : ?
## 1) orders_products 테이블에서 user_id, product_id를 grouping해서 user_id, order_number, product_id를 오름차순으로 araange 한 다음
## 2) ungroup 하여, product_time 변수(컬럼)을 새로 생성해서
## 3) 다시 ungroup하고, 다시 product_id를 기준으로 grouping해서
## 4) 새로운 집계 함수를 만든다
## product_time == row_number?
## group_by(user_id, product_id)를 기준으로 묶었기에 count가 됨?

prd <- orders_products %>%
  arrange(user_id, order_number, product_id) %>%
  group_by(user_id, product_id) %>%
  mutate(product_time = row_number()) %>%
  ungroup() %>%
  group_by(product_id) %>%

  summarise(
    prod_orders = n(),
    prod_reorders = sum(reordered),
    prod_first_orders = sum(product_time == 1),
    prod_second_orders = sum(product_time == 2)
  )

prd %>% head(10)<jupyter_output><empty_output><jupyter_text>5-2.<jupyter_code>## 재구매율
prd$prod_reorder_probability <- prd$prod_second_orders / prd$prod_first_orders
## 재구매건수
prd$prod_reorder_times <- 1 + prd$prod_reorders / prd$prod_first_orders
## 상품 전체 주문 건수 중에서 재구매 건수의 비중을 본 것
prd$prod_reorder_ratio <- prd$prod_reorders / prd$prod_orders
## 특정 컬럼 제외하기
prd <- prd %>% select(-prod_reorders, -prod_first_orders, -prod_second_orders)

## products 테이블 제거
rm(products)
## 메모리 초기화
gc()
prd %>%
    arrange(desc(prd$prod_reorder_times)) %>%
    head()
ordert %>% head<jupyter_output><empty_output><jupyter_text>[최종 결과물]- 최종: user_id, order_id, product_id, 상품 특성, user 특성, 재구매여부> - 1차 : user_id, order_id, product_id, 상품 특성 ... prd> - 2차 : user 특성 테이블> - 3차 : inner_join으로 1+2차 합치기> - 4차 : 모델링(알고리즘 생성)> - 5차 : 평가  6. Users & Us- Obj : Create __??__ table 6-1. users table- obj : __??__- use table & group    - table : orders_products    - group_by : user_id- feature : __??_    - order_id : 사용 X ?    - eval_set : filtering 조건에서 사용(train)    - order_number : 사용 X ?        - user_id : 유저번호(pk)    - user_orders : user_id별 총 구매수    - user_period : 총 구매기간 간격 합    - user_mean_days_since_prior : 구매기간 간격 평균- Process    - 1) orders 테이블에서 user_id로 grouping하여,    - 2) 집계함수 (user_orders(user_id별 총 구매수) / user_period(총 구매기간 간격 합) / user_mean_days_since_prior(구매기간 간격 평균))    - 3) 최종적으로 prior로 필터링- etc decription    - orders 테이블 중 order_number는 user_id의 몇번째 구매 건수인지를 알려줌    - 필터 조건의 대상(eval_set == "prior")으로 함수를 적용하겠다는 뜻 ... eval_set이란?    - prior를 이용해서 만든 테이블을 test와 train에 붙일 것 ... 최종으로 남을 건 test, train<jupyter_code>## users table ----------------------------------------------------------------
# feature : order_id, user_id, eval_set, order_number, user_orders (user_id별 총 구매수), user_period (총 구매기간 간격 합), user_mean_days_since_prior(구매기간 간격 평균)
# obj : ?
# order_number는 user_id의 몇번째 구매 건수를 알려줌
# 필터 조건의 대상(eval_set == "prior")으로 함수를 적용하겠다는 뜻! // eval_set이란?
# prior를 이용해서 만든 테이블을 test와 train에 붙일 것 ... 최종으로 남을 건 test, train

unique(orders$eval_set)
head(orders)<jupyter_output><empty_output><jupyter_text>Q.- 결측치 제외하고 계산하는 것이 가장 간단한 방법이지만 다른 방법이 있는지?    - null 값은 첫구매일테니 제외?    - na.rm=T 대신에 F나 생략하는 경우에는?    - 연산할 때, na.rm=T를 지정하지 않아도 알아서 되지 않으려나?    - 업무에서 결측치를 만난다면 어떻게 처리하는지?            - median이나 제외를 하고 본다.    - 결측치가 많으면 아예 그 값을 안 본다.    - 결측치가 적으면 결측치를 제외하고 보던지 같이 뽑아버리던지<jupyter_code>#### users table 생성
## 1) orders 테이블에서 user_id로 grouping하여,
## 2) 집계함수 (user_orders(user_id별 총 구매수) / user_period(총 구매기간 간격 합) / user_mean_days_since_prior(구매기간 간격 평균))
## 3) 최종적으로 prior로 필터링

users = orders %>% filter(eval_set == "prior") %>% group_by(user_id) %>%
    summarise(user_orders = max(order_number),
              user_period = sum(days_since_prior_order, na.rm=T), ## na.rm=T == 결측치 처리 (결측치 제외하고 계산 ?)
              user_mean_days_since_prior = mean(days_since_prior_order, na.rm=T))

users %>% head()<jupyter_output><empty_output><jupyter_text>6-2. Us table- obj    - 유저별 총 구매건수, 그 사람이 재구매를 얼마나 했는지, 그리고 얼마나 다양한 상품을 샀는지 파악- use table & group    - table : orders_products    - group_by : user_id- feature    - user_id별 user_total_products (총 구매상품수)    - user_reorder_ratio (재구매율)    - user_distinct_products (구매 상품 수 unique)- etc description    - ?<jupyter_code>orders_products %>% head()
## us table ----------------------------------------------------------------
# feature : user_id별 user_total_products (총 구매상품수), user_reorder_ratio (재구매율), user_distinct_products (구매 상품 수 unique)
# obj : 유저별 총 구매건수, 그 사람이 재구매를 얼마나 했는지, 그리고 얼마나 다양한 상품을 샀는지 파악

us = orders_products %>%
    group_by(user_id) %>%
    summarise(user_total_product = n() # user_id별 총 구매 상품수
              , user_reorder_ratio = sum(reordered == 1) / sum(order_number > 1) # 재구매율 ## order_number가 1보다 크면(조건을 만족하면), 1이 나옴. 그 다음행으로 가서 그 조건을 만족하면 1 아니면 0 그래서 각각 더해줌... 결론적으로 count와 같음!
              , user_distinct_products = n_distinct(product_id)
             )

str(us)
str(users)
us %>% head()
users %>% head()
## 전처리한 users와 us 테이블을 inner_join

users = users %>% inner_join(us)

str(users)
users %>% head()
## 평균 바스켓 사이즈 (구매상품수...user_orders / 주문건수...user_total_product)
# 새로운 열에 추가, 

users = users %>% mutate(user_average_basket = user_total_product / user_orders)

##### 우리가 최종으로 만들었던 정보들은 users에 있음
str(users)
users %>% head()<jupyter_output>Classes ‘tbl_df’, ‘tbl’ and 'data.frame':	206209 obs. of  8 variables:
 $ user_id                   : int  1 2 3 4 5 6 7 8 9 10 ...
 $ user_orders               : num  10 14 12 5 4 3 20 3 3 5 ...
 $ user_period               : num  176 198 133 55 40 18 203 60 36 79 ...
 $ user_mean_days_since_prior: num  19.6 15.2 12.1 13.8 13.3 ...
 $ user_total_product        : int  59 195 88 18 37 14 206 49 76 143 ...
 $ user_reorder_ratio        : num  0.7593 0.511 0.7051 0.0714 0.5385 ...
 $ user_distinct_products    : int  18 102 33 17 23 12 68 36 58 94 ...
 $ user_average_basket       : num  5.9 13.93 7.33 3.6 9.25 ...<jupyter_text>6-3. Train & Test Data Q. 위에 만든 users, us table(기준=prior)과 train & test의 users, us table 이름이 동일하면 위에 껀 다 지워지는 게 아닌지? test_user, test_us, train_user, test_us 이런 식으로 만들어야 하는 건 아닌지?<jupyter_code>str(us)
str(orders)

us %>% head(5)
orders %>% head(5)
## train & test
# process : eval_set을 기준으로 select
us = orders %>%
    filter(eval_set != "prior") %>%
    select(user_id, order_id, eval_set, time_since_last_order = days_since_prior_order)

str(us)
head(us)
# train과 test데이터만 들어감
users = users %>% inner_join(us)

str(users)
head(users)<jupyter_output>Joining, by = "user_id"<jupyter_text>여기까지 user 특성은 완성!  7. extra feature ...- obj    - create data table    - __?__    - process    -    - description    - 상품 특성에 feature를 추가할 것임 ?        - 상품 구매건수, 해당 아이템 첫 구매 구매 순서, 해당 아이템 마지막 구매 순서, 카트 평균 순위            - group_by        - 구매자별        - 상품별 정보            - up_orders        - 사람의 상품별 구매건수, ?        - 해당 아이템의 첫 구매 순서        - 사람이 상품을 총 구매한 순서 중에서 특정 아이템        - 최근에 구매한 것이 재구매할 확률이 높다고 판단하기에 넣은 것?            - up_average_cart_position        - user_id가 product_id를 몇 번째에 담았느냐!<jupyter_code>orders_products %>% head(100)
### extra feautures
# 상품특성에서 추가로 더 넣어줄 것임
# 상품 구매건수, 해당 아이템 첫구매 구매순서, 해당 아이템 마지막 구매순서, 카트 평균 순위 
## group_by 구매자별, 상품별 정보를 넣을 것임
# up_orders = 사람의 상품별 구매건수, 
# 해당 아이템의 첫 구매 순서
# 사람이 상품을 총 구매한 순서 중에서 특정 아이템
# 최근에 구매한 것이 재구매할 확률이 높다고 판단
# up_average_cart_position : user_id가 product_id를 몇 번째에 담았느냐!

data = orders_products %>%
    group_by(user_id, product_id) %>%
    summarise(up_orders = n(),
              up_first_order = min(order_number),
              up_last_order = max(order_number),
              up_average_cart_position = mean(add_to_cart_order))

data %>% head()
## 필요 없는 테이블 제거
rm(orders_products, orders)<jupyter_output><empty_output><jupyter_text>8. Merge Table- obj    - 상품 특성 테이블과 유저특성 테이블 합치기    - prd        - 상품 특성 테이블    - users        - 유저 특성 테이블    - join table & column    - prd        - product_id     - users        - user_id<jupyter_code>## 상품특성 테이블과 유저특성 테이블 합치기
# prd = 상품특성, users = 유저특성
## 회원별로 구매했던 상품들을 쭉 만든 다음에
## 우리가 만들었던 상품정보 테이블과 구매정보 테이블을 붙이는 것

data = data %>%
    inner_join(prd, by="product_id") %>%
    inner_join(users, by="user_id")
str(data)
head(data)<jupyter_output>Classes ‘grouped_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':	13307953 obs. of  20 variables:
 $ user_id                   : int  1 1 1 1 1 1 1 1 1 1 ...
 $ product_id                : int  196 10258 10326 12427 13032 13176 14084 17122 25133 26088 ...
 $ up_orders                 : int  10 9 1 10 3 2 1 1 8 2 ...
 $ up_first_order            : num  1 2 5 1 2 2 1 5 3 1 ...
 $ up_last_order             : num  10 10 5 10 10 5 1 5 10 2 ...
 $ up_average_cart_position  : num  1.4 3.33 5 3.3 6.33 ...
 $ prod_orders               : int  35791 1946 5526 6476 3751 379450 15935 13880 6196 2523 ...
 $ prod_reorder_probability  : num  0.583 0.553 0.522 0.529 0.48 ...
 $ prod_reorder_times        : num  4.47 3.49 2.87 3.86 2.92 ...
 $ prod_reorder_ratio        : num  0.776 0.714 0.652 0.741 0.657 ...
 $ user_orders               : num  10 10 10 10 10 10 10 10 10 10 ...
 $ user_period               : num  176 176 176 176 176 176 176 176 176 176 ...
 $ user_mean_days_since_prior: num  19.6 19.6 19.6 19.6 19[...]<jupyter_text>9. exrta feture- 변수 추가    - feature        - 해당 상품 거래비중(전체대비), 거래경과일, 추가변수            - description        - 전체 거래 건수 중에 아이템의 비중이 얼마나 되는지        - 추가변수라고 임의로 만듬 ?        - user_orders            - 전체거래건수        - up_orders            - 그 사람의 거래건수<jupyter_code># 변수 추가
## 해당 상품 거래비중(전체대비), 거래경과일, 추가변수
# 전체 거래 건수 중에 아이템의 비중이 얼마나 되는지
# 추가변수라고 임의로 만듬
# user_orders : 그 사람의 전체 거래건수
# up_orders : 그 사람의 그 상품의 전체 거래건수

## 전체 거래 건 중에 그 상품이 차지하는 거래 비중이 어떠할지
data$up_order_rate = data$up_orders / data$user_orders

## 그 사람의 특정 상품 마지막 거래가 그 사람의 전체 거래에서 몇 번째 인가?
## 전체 거래 건 중에 뺀 거니까 , 해당 상품을 구매하고 다른 상품을 몇 건 더 거래 했는지
data$up_orders_since_last_order = data$user_orders - data$up_last_order

## 비율, 분자는 상품의 전체 거래 건수, 분모는 그 사람의 특정 상품 첫 번째 거래가 전체 거래에서 몇 번째 인지의 비율을 보는 것
data$up_orders_since_first_order = data$up_orders / (data$user_orders - data$up_first_order + 1)
str(data)
data %>% head()<jupyter_output>Classes ‘grouped_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':	13307953 obs. of  23 variables:
 $ user_id                    : int  1 1 1 1 1 1 1 1 1 1 ...
 $ product_id                 : int  196 10258 10326 12427 13032 13176 14084 17122 25133 26088 ...
 $ up_orders                  : int  10 9 1 10 3 2 1 1 8 2 ...
 $ up_first_order             : num  1 2 5 1 2 2 1 5 3 1 ...
 $ up_last_order              : num  10 10 5 10 10 5 1 5 10 2 ...
 $ up_average_cart_position   : num  1.4 3.33 5 3.3 6.33 ...
 $ prod_orders                : int  35791 1946 5526 6476 3751 379450 15935 13880 6196 2523 ...
 $ prod_reorder_probability   : num  0.583 0.553 0.522 0.529 0.48 ...
 $ prod_reorder_times         : num  4.47 3.49 2.87 3.86 2.92 ...
 $ prod_reorder_ratio         : num  0.776 0.714 0.652 0.741 0.657 ...
 $ user_orders                : num  10 10 10 10 10 10 10 10 10 10 ...
 $ user_period                : num  176 176 176 176 176 176 176 176 176 176 ...
 $ user_mean_days_since_prior : num  19.6 19.6[...]<jupyter_text>예측할 변수 재구매 여부<jupyter_code>data = data %>% 
  left_join(ordert %>% select(user_id, product_id, reordered), 
            by = c("user_id", "product_id"))

rm(ordert, prd, users)
gc()
# Train / Test datasets ---------------------------------------------------
## 아래에 재구매 영향 주요 지표를 봤을 때 빼고 싶은 변수가 있다면...
## train = train %>% select(-var1, -var2, -var3)

train = as.data.frame(data[data$eval_set == "train",])

train$eval_set = NULL
train$user_id = NULL
train$product_id = NULL
train$order_id = NULL
train$reordered[is.na(train$reordered)] = 0

# train$reordered가 NA이면 0 으로 대체하라
# is.na(train$reordered)

test = as.data.frame(data[data$eval_set == "test",])
test$eval_set = NULL
test$user_id = NULL
test$reordered = NULL

rm(data)
gc()<jupyter_output><empty_output><jupyter_text>Grid Search- GBM을 돌릴 때, parameter으로 튜닝하면서 최적값을 찾아야하는데- 파라미터들의 범위를 정해준 다음에- 이제 걔들로 만들 수 있는 조합 가지고- 한개씩 CV를 돌려보는 것이다.- https://brunch.co.kr/@snobberys/137<jupyter_code>install.packages('xgboost')
library(xgboost)
# Grid Search ------------------------------------------------------------------
subtrain = train %>% sample_frac(0.2) ### 너무 많으면 무거우니까 20%만 떼서 
X = xgb.DMatrix(as.matrix(subtrain %>% select(-reordered)), label = subtrain$reordered) # 예측할 변수를 label로 넣고, 넣을 데이터는 라벨을 빼주고, 그렇게 해서 x라는 곳에 xgb.DMatrix를 만들 것이다.

# 변수 범위를 정해줌, 보통은 이 값을 튜닝한다.
searchGridSubCol <- expand.grid(subsample = c(1.0),
                                colsample_bytree = c(1.0), # 모델을 만들 때, 모든 feature를 가지고 만드는 경우도 있고, 몇 %만 가지고 만드는 경우도 있지만, 1.0은 100% 가지고 만드는 것!
                                max_depth = c(8, 10), # 얼마나 깊게 학습을 시킬 것이냐 값이 8, 10. 8로 한 번 10으로 한 번 해서 총 두 번 돌리는 것이다.
                                min_child = seq(1),
                                eta = c(0.2)
)

## 총 두번의 그리드 서치를 진행
## grid search에서는 7~8~9~부터 시작
## 그런 수치가 가늠이 안 되면 kaggle을 보면 좋다. 사람들이 대충 지정하는 범위가 있다.
## 이 두개 중에서 어떤 게 성능이 좋은지 알아보려는 것
## 1, 1, 8, 1, 0.2 / 1, 1, 10, 1, 0.2<jupyter_output><empty_output><jupyter_text>- 오차를 줄이는 게 우리의 목적인데- 오차를 뭘로 설명할 거싱냐 그게 바로 eval_metric = "logloss"- kaggle에는 보통 어떤 지표로 평가할 것인지 쓰여져 있다.- 그렇지 않은 경우에는 보통은 logloss나 auc로 본다.- roc curve    - auc는 roc에서 커브 아래에 있는 면적을 auc라고 한다.    - auc가 클수록 분류 성능이 좋은 함수라고 한다.    - logloss는 반대로 작아야 좋다.    - 보통은 auc를 판단할 때, 이런 식으로 평가한다고 하지만 good auc in google    - kaggle에서는 auc가 높을 수록 좋다지만<jupyter_code>searchGridSubCol
ntrees <- 5 ## 학습을 몇 번 돌릴 것이냐. 5번을 돌릴 것인가?<jupyter_output><empty_output><jupyter_text>searchGridSubCol는 dataframe이고1이면 행에다가 뒤에 있는 함수를 적용하라는 뜻```RloglossErrorsHyperparameters <- apply(searchGridSubCol, 1, function(parameterList){```xgb.cv에 있는 metrics을 변경해주면 된다.<jupyter_code>system.time(
  loglossErrorsHyperparameters <- apply(searchGridSubCol, 1, function(parameterList){
    
    #Extract Parameters to test
    currentSubsampleRate <- parameterList[["subsample"]]
    currentColsampleRate <- parameterList[["colsample_bytree"]]
    currentDepth <- parameterList[["max_depth"]]
    currentEta <- parameterList[["eta"]]
    currentMinChild <- parameterList[["min_child"]]
      
    xgboostModelCV <- xgb.cv(data =  X, nrounds = ntrees, nfold = 2, showsd = TRUE, 
                             metrics = "logloss", verbose = TRUE, "eval_metric" = "logloss",
                             "objective" = "reg:logistic", "max.depth" = currentDepth, "eta" = currentEta,                               
                             "subsample" = currentSubsampleRate, "colsample_bytree" = currentColsampleRate
                             , print_every_n = 10, "min_child_weight" = currentMinChild, booster = "gbtree",
                             early_stopping_rounds = 10)
                              ## nrounds = 학습 횟수, nfold = Train data를 몇 개로 쪼갤 것이냐, metrics = logloss... 측정 오차를 logloss로 본다는 뜻
                              ## GB를 한 번 돌리고, 오차를 줄이는 방향으로 한 번 더 돌리고, *5
                              ## 사용자가 적어야 할 부분은 metrics 와 objective
                              ## reg:logistic == binary:logistic
    
    xvalidationScores <- as.data.frame(xgboostModelCV$evaluation_log)
    logloss <- tail(xvalidationScores$test_logloss_mean, 1)
    tlogloss <- tail(xvalidationScores$train_logloss_mean,1)
    output <- return(c(logloss, tlogloss, currentSubsampleRate, currentColsampleRate, currentDepth, currentEta, currentMinChild))
    
  }))
output <- as.data.frame(t(loglossErrorsHyperparameters)) ## t는 transpose, 행렬을 서로 바꿈
head(output)
varnames <- c("Testlogloss", "Trainlogloss", "SubSampRate", "ColSampRate", "Depth", "eta", "currentMinChild")
names(output) <- varnames
head(output)<jupyter_output><empty_output><jupyter_text>어떤 parameter가 제일 성능이 좋을지 판단하는 지표- 낮을수록 좋은 건데 실제로 test data에 적용했을 때 안 맞을 수 있다는 것- train 내에서 보기에는 score가 낮을수록 좋은 분류기다. 위에 나온 지표들을 토대로 아래 params를 수정<jupyter_code># model --------------------------------------------------------------------
params = list(
  "objective"           = "reg:logistic",
  "eval_metric"         = "logloss",
  "eta"                 = 0.01,
  "max_depth"           = 10,
  "min_child_weight"    = 10,
  "gamma"               = 0.70,
  "subsample"           = 1.0,
  "colsample_bytree"    = 1.0,
  "alpha"               = 2e-05,
  "lambda"              = 10
)
subtrain = train %>% sample_frac(0.2) ## 20%만
X = xgb.DMatrix(as.matrix(subtrain %>% select(-reordered)), label = subtrain$reordered) ## xgboost 패키지에서 알고리즘을 돌리려면 xgb.dmatrix를 x로 바꿔주어야 한다.
# xgboost로 모델링을 하는데 30번 한다.
model = xgboost(data = X, params = params, nrounds = 30)
# 변수들의 중요성을 보기 위해
importance = xgb.importance(colnames(X), model = model)<jupyter_output><empty_output><jupyter_text>재구매에 영향을 미치는 중요 지표 시각화- up_order_rate : 전체 주문 건수 중에서 그 상품의 주문 건수 비중> - 해당상품을 많이 사면 재구매를 많이 한다> - 파생변수를 생성할 수도 있다.>> - 예를 들면, departments, aisle 카테고리 전체 판ao 건수 중에서 해당 상품의 거래 건수- - feature가 많은 것 같으면... 위로 다시 올라가서 제거<jupyter_code>xgb.ggplot.importance(importance)
rm(X, importance, subtrain)
gc()
# Apply model -------------------------------------------------------------
X = xgb.DMatrix(as.matrix(test %>% select(-order_id, -product_id)))
test$reordered = predict(model, X) ## test table의 reordered에 predict한 확률이 들어감
head(test)
# test$reordered = (test$reordered > 0.5) * 1 ## 50% 이상이면 재구매한다고 보고, 50% 이상이면 1이 되고, 이하면 0이 된다.
test$reordered = (test$reordered > 0.21) * 1 ## 50% 이상이면 재구매한다고 보고, 50% 이상이면 1이 되고, 이하면 0이 된다.<jupyter_output><empty_output><jupyter_text>- 실제로 하려면 train, test로 나눠야 한다.- test 데이터에 대한 재구매 여부가 없다.- test data에는 실제로 이 사람이 구매했는지 안 했는지에 대한 여부가 없다.- 우리가 예측한 게 맞는지 안 맞는지 확인할 길이 없다.- 이럴 때 train 데이터를 train/test로 쪼개서 예측을 한다.- 그게 맞는지 안 맞는지 예측을 한다.<jupyter_code>head(test) # 그런식으로 하면 구매를 할 지 안 할지 1, 0 으로 나눠질 것이다.
# order_id 별로 재구매할(1) 상품들을 보여주는 것
# kaggle에 제출할 때 그런 식으로 제출하라고 요구한 것
# 옆에는 실제로 user_id도 있을 것.

submission = test %>%
  filter(reordered == 1) %>%
  group_by(order_id) %>%
  summarise(
    products = paste(product_id, collapse = " ") ## paste는 문자와 문자를 붙이라는 뜻
  )
head(submission)<jupyter_output><empty_output><jupyter_text>없는 값<jupyter_code>missing = data.frame(
  order_id = unique(test$order_id[!test$order_id %in% submission$order_id]),   # %in% test$order_id에 submission$order_id가 있느냐를 True&False로 반환해서 !는 반대니깐 ... 실제적으로 없는 게 나온다. ## test orderid중에서 submission에 없는 것 중의 unique를 뽑는 것
  products = "None"
)<jupyter_output><empty_output><jupyter_text>있는 값 + 없는 값<jupyter_code>submission = submission %>% bind_rows(missing) %>% arrange(order_id)
submission %>% head()
# write.csv(submission, file = paste0(path,"/submit3.csv"), row.names = F)
write.csv(submission, file = ("./submit3.csv"), row.names = F)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1599850551092845, 'fraction_numerical': 0.04292919858023538, 'mean_word_length': 2.6233924461892513, 'pattern_counts': {'":': 0, '<': 124, '<?xml version=': 0, '>': 196, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '327596', 'license': 'MIT', 'n_tokens_mistral': 13921, 'n_tokens_neox': 15184, 'n_words': 2708, 'path': 'data-analysis/r/e-commerce-analysis-machine-learning.ipynb', 'repo_name': 'data-block/dev', 'size': '26759'}