inblog logo
|
jodory
    Kaggle

    [Kaggle] 고객들의 물품 구매 내역을 기반으로 어떤 상품을 재구매할지 예측하는 문제 (개인화 추천)

    Dec 14, 2023
    [Kaggle] 고객들의 물품 구매 내역을 기반으로 어떤 상품을 재구매할지 예측하는 문제 (개인화 추천)
    Contents
    개요Data 설명EDA결과필사 참고 URL

    개요

    Instacart Market Basket Analysis
    Which products will an Instacart consumer purchase again?
    Instacart Market Basket Analysis
    https://www.kaggle.com/c/instacart-market-basket-analysis
     
    당신이 꼼꼼하게 계획된 식료품 리스트를 기반으로 쇼핑을 하든, 충독적으로 쇼핑하든, 우리의 음식구매패턴은 우리가 누구인지 정의한다. 식료품 주문 및 배달 앱인 Instacart는 필요할 때 냉장고와 선반안에 당신이 가장 좋아하는 것으로 쉽게 채울 수 있도록 하는 것을 목표로 한다. 인스타카트 앱을 통해 상품을 고른 후, 자신의 장바구니를 검토하고 구매한다.
    Instacart의 데이터 분석팀은 이 즐거운 쇼핑 경험을 제공하는 데 큰 역할을 한다. 현재 그들은 사용자가 어떤 제품을 다시 구매할지 예측하거나, 처음 구매하거나, 다음에 장바구니에 추가할지를 예측하는 모델을 개발하기 위해 트랜잭션 데이터를 사용한다. 최근에, Instacart는 300만 개의 데이터를 오픈했다.
    이 경쟁에서, Instacart는 kaggle 에서 어떤 제품이 사용자의 다음 구매에 포함될 것인지를 예측하기 위한 데이터분석 모델을 찾고 있다.

    Data 설명

    Data
    Data Column
    • order_products_train_df : train(현재) 구매자군의 제품주문내역 • order_products_prior_df : prior(과거) 구매자군의 제품주문내역 • orders_df : 주문정보 • products_df : 제품정보 • aisles_df : 제품 상세카테고리 • departments_df : 제품 카테고리
    • order_id : 주문 고유 아이디 • user_id : 소비자 고유 아이디 • product_id  : 제품 고유 아이디 • eval_set : 소비자 군 ( prior / train / test ) • order_number : 주문번호   • order_dow : 요일 • order_hour_of_day : 일일 중 시간   • days_since_prior_order :  • add_to_cart_order : 장바구니 담은 제품 개수 • reordered : 재주문 제품 개수  • product_name : 제품 이름  • aisle_id : 제품 상세카테고리 고유 아이디 • department_id : 제품 카테고리 고유 아이디 • aisle :  제품 상세카테고리 • department : 제품 카테고리

    EDA

    Step1 . 라이브러리 / DataSet 불러오기
    import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) import matplotlib.pyplot as plt import seaborn as sns color = sns.color_palette() %matplotlib inline pd.options.mode.chained_assignment = None
    # matplotlib 한글 폰트 깨짐 방지 from matplotlib import font_manager, rc font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name() rc('font', family=font_name)
    order_products_train_df = pd.read_csv(r'파일저장 경로\order_products__train.csv') order_products_prior_df = pd.read_csv(r'파일저장 경로\order_products__prior.csv') orders_df = pd.read_csv(r'파일저장 경로\orders.csv') products_df = pd.read_csv(r'파일저장 경로\products.csv') aisles_df = pd.read_csv(r'파일저장 경로\aisles.csv') departments_df = pd.read_csv(r'파일저장 경로\departments.csv')
    Step 2 . EDA
    • 각 그룹군 그래프로 나타내기
    cnt_srs = orders_df.eval_set.value_counts() plt.figure(figsize=(12,8)) sns.barplot(cnt_srs.index, cnt_srs.values, alpha=0.8, color=color[1]) plt.ylabel('총 갯수', fontsize=12) plt.xlabel('고객 Type 군', fontsize=12) plt.title('각 고객군의 수', fontsize=15) plt.show()
    notion image
    def get_unique_count(x): return len(np.unique(x)) cnt_srs = orders_df.groupby("eval_set")["user_id"].aggregate(get_unique_count) cnt_srs
    • 각 고객별로 가장 많이 주문한 개수를 그래프로 나타내기
    cnt_srs = orders_df.groupby("user_id")["order_number"].aggregate(np.max).reset_index() cnt_srs = cnt_srs.order_number.value_counts() plt.figure(figsize=(12,8)) sns.barplot(cnt_srs.index, cnt_srs.values, alpha=0.8, color=color[2]) plt.ylabel('구매 발생 횟수', fontsize=12) plt.xlabel('Maximum 구매 갯수', fontsize=12) plt.xticks(rotation='vertical') plt.show()
    notion image
    • 요일별 구매량 추이 확인
    plt.figure(figsize=(20,15)) sns.countplot(x="order_dow", data=orders_df, color=color[0]) # order_dow 는 요일을 의미 plt.xlabel('요일', fontsize=12) plt.ylabel('구매량', fontsize=12) plt.xticks([2,3,4,5,6,0,1],['월','화','수','목','금','토','일']) plt.title("요일별 구매량", fontsize=15) plt.show()
    notion image
    • 각 시간대별로 구매개수 그래프로 나타내기
    orders_df
    ㅤ
    order_id
    user_id       
    eval_set       
    order_number   
    order_dow
    order_hour_of_day 
    days_since_prior_order
    0
    2539329
    1
    prior
    1
    2
    8
    NaN
    1
    2398795
    1
    prior
    2
    3
    7
    15.0
    2
    473747
    1
    prior
    3
    3
    12
    21.0
    3
    2254736
    1
    prior
    4
    4
    7
    29.0
    4
    431534
    1
    prior
    5
    4
    15
    28.0
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    3421078
    2266710
    206209
    prior
    10
    5
    18
    29.0
    3421079
    1854736
    206209
    prior
    11
    4
    10
    30.0
    3421080
    626363
    206209
    prior
    12
    1
    12
    18.0
    3421081
    2977660
    206209
    prior
    13
    1
    12
    7.0
    3421082
    272231
    206209
    train
    14
    6
    14
    30.0
    plt.figure(figsize=(12,8)) sns.countplot(x="order_hour_of_day", data=orders_df, color=color[1]) plt.ylabel('구매량', fontsize=12) plt.xlabel('하루 중 시간', fontsize=12) plt.title("하루 중 구매량", fontsize=15) plt.show()
    notion image
    • 요일별/ 시간대별 구매 추이 확인
    grouped_df = orders_df.groupby(['order_dow','order_hour_of_day'])['order_number'].aggregate('count').reset_index() grouped_df = grouped_df.pivot('order_dow','order_hour_of_day','order_number') plt.figure(figsize = (18,9)) sns.heatmap(grouped_df) plt.title('요일별/시간별 구매량') plt.yticks([2,3,4,5,6,0,1],['월','화','수','목','금','토','일']) plt.show()
    notion image
    • prior 고객군 대상 분석
    plt.figure(figsize=(12,8)) sns.countplot(x='days_since_prior_order', data= orders_df, color=color[3]) plt.ylabel('판매량',fontsize=12) plt.xlabel('prior 주문 일수', fontsize = 12) plt.xticks(rotation='vertical') plt.title('prior 구매의 일별 빈도분포') #판매량이 가장 높은 요일은 30일, 낮은 요일은 25일 이다. 1일~7일까지는 증가추세이고, 8일~29일까지는 감소추세이다.
    notion image
    # prior 고객군의 재주문율 order_products_prior_df.reordered.sum() / order_products_prior_df.shape[0]
    >>> 0.5896974667922161
    # train 고객군의 재주문율 order_products_train_df.reordered.sum() / order_products_train_df.shape[0]
    >>> 0.5985944127509629
    # prior 고객들 중 재주문한 경험이 있을 경우 1, 없을 경우 0 으로 데이터를 정제한다. grouped_df = order_products_prior_df.groupby('order_id')['reordered'].aggregate('sum').reset_index() grouped_df['reordered'].ix[grouped_df['reordered'] > 1] = 1 grouped_df.reordered.value_counts() / grouped_df.shape[0]
    >>> 1 0.879151
    >>> 0 0.120849
    해석 ) prior 고객들 중 재주문한 경험이 있는 경우는 약 88% prior 고객들 중 재주문한 경험이 없는 경우는 약 12%
    # train 고객들 중 재주문한 경험이 있을 경우 1, 없을 경우 0 으로 데이터를 정제한다. grouped_df = order_products_train_df.groupby('order_id')['reordered'].aggregate('sum').reset_index() grouped_df['reordered'].ix[grouped_df['reordered'] > 1] = 1 grouped_df.reordered.value_counts() / grouped_df.shape[0]
    >>> 1 0.93444
    >>> 0 0.06556
    해석 ) prior 고객들 중 재주문한 경험이 있는 경우는 약 93% prior 고객들 중 재주문한 경험이 없는 경우는 약 7%
    # grouped_df 은 구매자별로 add_to_cart_order 에 담았던 제품의 총 개수 grouped_df = order_products_train_df.groupby('order_id')['add_to_cart_order'].aggregate('max').reset_index() # cnt_srs 는 add_to_cart_order 에 담긴 각각의 값이 나온 개수 cnt_srs = grouped_df['add_to_cart_order'].value_counts()
    plt.figure(figsize=(12,8)) sns.barplot(cnt_srs.index, cnt_srs.values, alpha=0.8) plt.ylabel('발생 건수', fontsize = 12) plt.xlabel('add_to_cart_order 에 담긴 제품의 개수', fontsize = 12) plt.xticks(rotation='vertical') plt.show()
    notion image
    order_products_prior_df.head()
         
    ㅤ
    order_id
    product_id
    add_to_cart_order
    reordered
    0
    2
    33120
    1
    1
    1
    2
    28985
    2
    1
    2
    2
    9327
    3
    0
    3
    2
    45918
    4
    1
    4
    2
    30035
    5
    0
    order_products_prior_df.tail()
     
    ㅤ
    order_id
    product_id
    add_to_cart_order
    reordered
    32434484
    3421083
    39678
    6
    1
    32434485
    3421083
    11352
    7
    0
    32434486
    3421083
    4600
    8
    0
    32434487
    3421083
    24852
    9
    1
    32434488
    3421083
    5020
    10
    1
    order_products_prior_df = pd.merge(order_products_prior_df, products_df, on= "product_id" , how="left") order_products_prior_df = pd.merge(order_products_prior_df, aisles_df, on='aisle_id', how='left') order_products_prior_df = pd.merge(order_products_prior_df, departments_df, on='department_id', how='left') order_products_prior_df.head()
           
    ㅤ
    order_id
    product_id   
    add_to_cart_order
    reordered 
    product_name 
    aisle_id 
    department_id
    aisle
    department
    0
    2
    33120
    1
    1
    Organic Egg Whites
    86
    16
    eggs
    dairy eggs
    1
    2
    28985
    2
    1
    Michigan Organic Kale
    83
    4
    fresh vegetables
    produce
    2
    2
    9327
    3
    0
    Garlic Powder
    104
    13
    spices seasonings
    pantry
    3
    2
    45918
    4
    1
    Coconut Butter
    19
    13
    oils vinegars
    pantry
    4
    2
    30035
    5
    0
    Natural Sweetener
    17
    13
    baking ingredients
    pantry
    • 판매개수 높은 제품 확인
    cnt_srs = order_products_prior_df['product_name'].value_counts().sort_values(ascending=False).reset_index().head(20) cnt_srs.columns = ['제품 이름','판매 갯수'] cnt_srs # organic 제품이 대다수의 상위권을 차지한 것을 알 수 있다
    ㅤ
    제품 이름
    판매 갯수
    0
    Banana
    472565
    1
    Bag of Organic Bananas
    379450
    2
    Organic Strawberries
    264683
    3
    Organic Baby Spinach
    241921
    4
    Organic Hass Avocado
    213584
    5
    Organic Avocado
    176815
    6
    Large Lemon
    152657
    7
    Strawberries
    142951
    8
    Limes
    140627
    9
    Organic Whole Milk
    137905
    10
    Organic Raspberries
    137057
    11
    Organic Yellow Onion
    113426
    12
    Organic Garlic
    109778
    13
    Organic Zucchini
    104823
    14
    Organic Blueberries
    100060
    15
    Cucumber Kirby
    97315
    16
    Organic Fuji Apple
    89632
    17
    Organic Lemon
    87746
    18
    Apple Honeycrisp Organic
    85020
    19
    Organic Grape Tomatoes
    84255
    • 제품의 각 상세 카테고리별 판매량
    cnt_srs = order_products_prior_df['aisle'].value_counts().head(20) plt.figure(figsize=(12,8)) sns.barplot(cnt_srs.index, cnt_srs) plt.xlabel('제품 상세 카테고리' , fontsize= 15) plt.ylabel('구매량', fontsize= 15) plt.xticks(fontsize = 15, rotation='vertical') plt.show()
    notion image
    plt.figure(figsize=(10,10)) temp_series = order_products_prior_df['department'].value_counts() labels = np.array(temp_series.index) sizes = np.array((temp_series / temp_series.sum())* 100) plt.pie(sizes, labels=labels, autopct='%1.1f%%' , startangle= 200) plt.title('제품 카테고리 분포', fontsize= 15) plt.show() # produce(29.2%) 제품군 > dairy eggs(16.7%) > snacks(8.9%) > beverages(8.3%)제품군이 가장 많이 판매됬음을 알 수 있다
    notion image
    • 제품 카테고리 별 재주문 현황
    grouped_df = order_products_prior_df.groupby(['department'])['reordered'].aggregate('mean').reset_index() plt.figure(figsize=(12,8)) sns.pointplot(grouped_df['department'].values,grouped_df['reordered'].values, alpha = 0.8 , color= color[2]) plt.xlabel('제품 카테고리', fontsize = 18) plt.ylabel('재주문율' , fontsize= 18) plt.xticks(rotation = 'vertical', fontsize = 15) plt.yticks(fontsize = 15) plt.title('제품 카테고리의 재주문율 ') plt.show() # dairy eggs 제품군의 재주문율이 가장 높다 personal care 제품군의 재주문율이 가장 낮다.
    notion image
    grouped_df = order_products_prior_df.groupby(['department_id', 'aisle'])['reordered'].aggregate('mean').reset_index() fig, ax = plt.subplots(figsize=(15,20)) ax.scatter(grouped_df.reordered.values, grouped_df.department_id.values) for i, txt in enumerate(grouped_df.aisle.values): ax.annotate(txt, (grouped_df.reordered.values[i],grouped_df.department_id.values[i]), rotation=45 , ha='center',va='center', color='green') plt.xlabel('재주문율',fontsize = 20) plt.ylabel('제품 카테고리 id',fontsize = 20) plt.title('다른 제품 카테고리의 재주문율' , fontsize =20) plt.show()
    notion image
    • 장바구니에 추가와 재주문율의 상관관계에 대해 알아보기
    order_products_prior_df['add_to_cart_order_mod'] = order_products_prior_df['add_to_cart_order'].copy() order_products_prior_df['add_to_cart_order_mod'].ix[order_products_prior_df['add_to_cart_order_mod'] > 70] = 70 grouped_df = order_products_prior_df.groupby(['add_to_cart_order_mod'])['reordered'].aggregate('mean').reset_index() plt.figure(figsize=(12,8)) sns.pointplot(grouped_df['add_to_cart_order_mod'].values, grouped_df['reordered'].values, alpha=0.8, color=color[2]) plt.xlabel('장바구니에 추가' , fontsize = 15) plt.ylabel('재주문율', fontsize= 15) plt.title('장바구니 추가와 재주문율 관계', fontsize=15) plt.xticks(rotation= 'vertical') plt.show() # 제일 처음 장바구니에 추가된 제품이 나중에 추가된 제품에 비해 다시 주문될 가능성이 높다. #이를 통해, 소비자들이 자주 사용하는 제품 먼저 주문 후 새로운 제품을 찾는 경향을 가지고 있음을 알 수 있다.
    notion image
    • 요일별 재주문 추이
    order_products_train_df = pd.merge(order_products_train_df,orders_df,on='order_id',how='left') grouped_df = order_products_train_df.groupby(['order_dow'])['reordered'].aggregate('mean').reset_index() plt.figure(figsize=(12,8)) sns.barplot(grouped_df['order_dow'].values, grouped_df['reordered'].values,alpha=0.8, color = color[3]) plt.xlabel('요일', fontsize = 15) plt.ylabel('재주문율',fontsize =15) plt.xticks(rotation='vertical') plt.ylim(0.5,0.7) plt.show()
    notion image
    • 일일 시간대별 재주문율 추이
    grouped_df= order_products_train_df.groupby('order_hour_of_day')['reordered'].aggregate('mean').reset_index() plt.figure(figsize=(12,8)) sns.barplot(grouped_df['order_hour_of_day'].values, grouped_df['reordered'].values, alpha=0.8,color=color[4]) plt.xlabel('하루 중 시간', fontsize =15) plt.ylabel('재주문율', fontsize =15) plt.title('일별 재주문율', fontsize = 15) plt.ylim(0.5,0.7) plt.show()
    notion image
    • 요일별/ 일일 시간대별 재주문율 현황확인
    order_products_train_df
    ㅤ
    order_id 
    product_id
    add_to_cart_order
    reordered 
    user_id
    eval_set
    order_number
    order_dow
    order_hour_of_day 
    days_since_prior_order
    0
    1
    49302
    1
    1
    112108
    train
    4
    4
    10
    9.0
    1
    1
    11109
    2
    1
    112108
    train
    4
    4
    10
    9.0
    2
    1
    10246
    3
    0
    112108
    train
    4
    4
    10
    9.0
    3
    1
    49683
    4
    0
    112108
    train
    4
    4
    10
    9.0
    4
    1
    43633
    5
    1
    112108
    train
    4
    4
    10
    9.0
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    ...
    1384612
    3421063
    14233
    3
    1
    169679
    train
    30
    0
    10
    4.0
    1384613
    3421063
    35548
    4
    1
    169679
    train
    30
    0
    10
    4.0
    1384614
    3421070
    35951
    1
    1
    139822
    train
    15
    6
    10
    8.0
    1384615
    3421070
    16953
    2
    1
    139822
    train
    15
    6
    10
    8.0
    1384616
    3421070
    4724
    3
    1
    139822
    train
    15
    6
    10
    8.0
    grouped_df = order_products_train_df.groupby(['order_dow', 'order_hour_of_day'])['reordered'].aggregate('mean').reset_index() grouped_df = grouped_df.pivot(values = 'reordered', columns= 'order_hour_of_day',index='order_dow')
    plt.figure(figsize=(12,8)) sns.heatmap(grouped_df) plt.title('요일별 재주문율 vs 일일별 재주문율 ') plt.xlabel('하루 중 구매시간') plt.ylabel('요일') plt.yticks([2,3,4,5,6,0,1],['월','화','수','목','금','토','일']) plt.show()
    notion image

    결과

    • prior 고객군 insightprior
      • 고객군의 최대 구매시간은 토요일 오후 1~3시 와 일요일 10시이다.
    • train 고객군 insighttrain
      • 고객군의 최대 구매시간은 수요일과 토요일 아침 6~7시간대이다.
      • train 고객군은 하루 중 6,7,8시에 재주문율이 가장 높고 9시 ~12시까지 주문율이 감소하는 경향을 보인다.
    • 종합 insight
      • 시간별 판매량
        • 아침 9시에 판매량이 가장 높고, 새벽 3시에 판매량이 가장 낮다.
        • 아침 9시~ 17시까지이 판매량의 70% 를 차지한다.
      • 요일별 판매량
        • 판매량이 가장 높은 요일은 30일, 낮은 요일은 25일 이다.
        • 1일~7일까지는 증가추세이고, 8일~29일까지는 감소추세이다.
      • 제품군에 따른 주문율
        • produce(29.2%) 제품군 > dairy eggs(16.7%) > snacks(8.9%) > beverages(8.3%)제품군이 가장 많이 판매됬음을 알 수 있다.
      • 상세 제품군에 따른 주문율
        • organic 제품이 대다수의 판매 상위권을 차지한 것을 알 수 있다
      • 상세 제품군에 따른 재주문율
        • dairy eggs 제품군의 재주문율이 가장 높다
        • personal care 제품군의 재주문율이 가장 낮다.
      • 장바구니와 관련된 구매성향
        • 제일 처음 장바구니에 추가된 제품이 나중에 추가된 제품에 비해 다시 주문될 가능성이 높다.
        • 이를 통해, 소비자들이 자주 사용하는 제품 먼저 주문 후 새로운 제품을 찾는 경향을 가지고 있음을 알 수 있다.

    필사 참고 URL

    Simple Exploration Notebook - Instacart
    Explore and run machine learning code with Kaggle Notebooks | Using data from Instacart Market Basket Analysis
    Simple Exploration Notebook - Instacart
    https://www.kaggle.com/sudalairajkumar/simple-exploration-notebook-instacart
    Simple Exploration Notebook - Instacart
    Share article
    Contents
    개요Data 설명EDA결과필사 참고 URL

    jodory

    RSS·Powered by Inblog