Post

파이썬 데이터분석 - aarrr 분석 실습

1
2
3
4
import os
import pandas as pd
import numpy as np
import seaborn as sns

데이터 준비하기 + 전처리

  • 실습내용 : 브라질의 이커머스 기업 Olist 데이터분석 (EDA + AARRR)
  • 데이터출처 : 캐글

각 테이블 별로 간단한 EDA를 진행해보고 각 테이블이 어떤 의미와 특성을 가지고 있는지 파악하고, 테이블 간의 관계도 파악해보기

1. Customers (olist_customers_dataset.csv)

컬럼 이름데이터 타입설명
customer_idVARCHAR고객 고유 식별자
customer_unique_idVARCHAR고객의 고유 ID
customer_zip_code_prefixINT고객의 우편번호 앞부분
customer_cityVARCHAR고객의 도시 정보
customer_stateVARCHAR고객의 주 정보
1
2
df = pd.read_csv('/content/drive/MyDrive/archive/olist_customers_dataset.csv')
df
customer_idcustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_state
006b8999e2fba1a1fbc88172c00ba8bc7861eff4711a542e4b93843c6dd7febb014409francaSP
118955e83d337fd6b2def6b18a428ac77290c77bc529b7ac935b93aa66c333dc39790sao bernardo do campoSP
24e7b3e00288586ebd08712fdd0374a03060e732b5b29e8181a18229c7b0b2b5e1151sao pauloSP
3b2b6027bc5c5109e529d4dc6358b12c3259dac757896d24d7702b9acbbff3f3c8775mogi das cruzesSP
44f2d8ab171c80ec8364f7c12e35b23ad345ecd01c38d18a9036ed96c73b8d06613056campinasSP
..................
9943617ddf5dd5d51696bb3d7c6291687be6f1a29b476fee25c95fbafc67c5ac95cf83937sao pauloSP
99437e7b71a9017aa05c9a7fd292d714858e8d52a67c98be1cf6a5c84435bd38d095d6764taboao da serraSP
994385e28dfe12db7fb50a4b2f691faecea5ee9f50caf99f032f0bf3c55141f019d9960115fortalezaCE
9943956b18e2166679b8a959d72dd06da27f973c2643a0a458b49f58cea58833b192e92120canoasRS
99440274fa6071e5e17fe303b9748641082c884732c5050c01db9b23e19ba398993986703cotiaSP

99441 rows × 5 columns

1
2
# 결측치 확인
df.isna().sum().sort_values(ascending=False)
0
customer_id0
customer_unique_id0
customer_zip_code_prefix0
customer_city0
customer_state0


1
2
# 중복값 확인
df.duplicated().sum()
1
0
1
2
# 중복값 확인
df['customer_id'].duplicated().sum()
1
0
1
df['customer_unique_id'].duplicated().sum()
1
3345
1
df[df['customer_unique_id'].duplicated(keep=False)].sort_values(by=['customer_unique_id'])
customer_idcustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_state
3560824b0e2bd287e47d54d193e7bbb51103f00172711b30d52eea8b313a7f2cced0245200jequieBA
192991afe8a9c67eec3516c09a8bdcc53909000172711b30d52eea8b313a7f2cced0245200jequieBA
200231b4a75b3478138e99902678254b260f4004288347e5e88a27ded2bb23747066c26220nova iguacuRJ
22066f6efe5d5c7b85e12355f9d5c3db46da2004288347e5e88a27ded2bb23747066c26220nova iguacuRJ
7245149cf243e0d353cd418ca77868e24a670004b45ec5c64187465168251cd1c9c2f57055maceioAL
..................
750571ae563fdfa500d150be6578066d83998ff922bdd6bafcdf99cb90d7f39cea5b317340barra bonitaSP
27992bec0bf00ac5bee64ce8ef5283051a70cff922bdd6bafcdf99cb90d7f39cea5b317340barra bonitaSP
79859d064be88116eb8b958727aec4cf56a59ff922bdd6bafcdf99cb90d7f39cea5b317340barra bonitaSP
643234b231c90751c27521f7ee27ed2dc3b8fffe254cc039740e17dd15a530503592837640extremaMG
121330088395699ea0fcd459bfbef084997dbffe254cc039740e17dd15a530503592837640extremaMG

6342 rows × 5 columns

1
2
# 보유 고객중 2번이상 구매를 시도한 고객 비중은?
round(df['customer_unique_id'].duplicated().sum() / (df.shape[0] - df['customer_unique_id'].duplicated().sum()) * 100,2)
1
3.48
  • 고객마다 고유 customer_unique_id가 있으며, 주문마다 고유의 customer_id가 생성된다
  • customer_unique_id 중복값이 존재한다 → 2번이상 구매를 시도한 고객(재주문 혹은 주문취소포함 등) → 3,345명의 고객이 관찰됨 (약 3.5%)
  • 대부분 1회성 고객임을 알 수 있다 → 재구매율을 높일지 회원수 확보를 높일지 전략을 세울필요가 있음
  • customer_id컬럼과 orders데이터의 order_id컬럼이 1:1 매칭된다. 특정 고객이 주문하면 주문아이디와 매칭되는 커스텀아이디를 부여한다. 그렇기때문에 customer_id가 중복되는 경우는 존재하지 않는다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 'customer_city'와 'customer_state' 상위 10 확인
city_counts = df['customer_city'].value_counts().head(10)
state_counts = df['customer_state'].value_counts().head(10)

# subplot 설정
fig, axs = plt.subplots(1, 2, figsize=(12, 5))

# 'customer_city' 그래프
axs[0].barh(city_counts.index[::-1], city_counts.values[::-1], color='skyblue')
axs[0].set_title('상위 10개 고객 도시')

# 빈도수 표시
for index, value in enumerate(city_counts.values[::-1]):
    axs[0].text(value, index, str(value))

axs[0].spines['top'].set_visible(False)
axs[0].spines['right'].set_visible(False)

# 'customer_state' 그래프
axs[1].barh(state_counts.index[::-1], state_counts.values[::-1], color='salmon')
axs[1].set_title('상위 10개 고객 주')

# 빈도수 표시
for index, value in enumerate(state_counts.values[::-1]):
    axs[1].text(value, index, str(value))

axs[1].spines['top'].set_visible(False)
axs[1].spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • olist고객 중 약 70%는 3개 주 (SP, RJ, MG)에 거주하고있고, 상파울로주(SP)는 전체 고객 중 43%가 거주하고있다

2. Orders (olist_orders_dataset.csv)

컬럼 이름데이터 타입설명
order_idVARCHAR주문 고유 식별자
customer_idVARCHAR고객 테이블과 연결되는 고객 식별자
order_statusVARCHAR주문 상태 (배송 완료, 결제 완료 등)
order_purchase_timestampTIMESTAMP고객이 주문한 시각
order_delivered_carrier_dateTIMESTAMP운송사가 배송을 시작한 시각
order_delivered_customer_dateTIMESTAMP고객에게 최종 배송된 날짜
order_estimated_delivery_dateTIMESTAMP예상 배송 날짜
1
2
df2 = pd.read_csv('/content/drive/MyDrive/archive/olist_orders_dataset.csv')
df2
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_date
0e481f51cbdc54678b7cc49136f2d6af79ef432eb6251297304e76186b10a928ddelivered2017-10-02 10:56:332017-10-02 11:07:152017-10-04 19:55:002017-10-10 21:25:132017-10-18 00:00:00
153cdb2fc8bc7dce0b6741e2150273451b0830fb4747a6c6d20dea0b8c802d7efdelivered2018-07-24 20:41:372018-07-26 03:24:272018-07-26 14:31:002018-08-07 15:27:452018-08-13 00:00:00
247770eb9100c2d0c44946d9cf07ec65d41ce2a54c0b03bf3443c3d931a367089delivered2018-08-08 08:38:492018-08-08 08:55:232018-08-08 13:50:002018-08-17 18:06:292018-09-04 00:00:00
3949d5b44dbf5de918fe9c16f97b45f8af88197465ea7920adcdbec7375364d82delivered2017-11-18 19:28:062017-11-18 19:45:592017-11-22 13:39:592017-12-02 00:28:422017-12-15 00:00:00
4ad21c59c0840e6cb83a9ceb5573f81598ab97904e6daea8866dbdbc4fb7aad2cdelivered2018-02-13 21:18:392018-02-13 22:20:292018-02-14 19:46:342018-02-16 18:17:022018-02-26 00:00:00
...........................
994369c5dedf39a927c1b2549525ed64a053c39bd1228ee8140590ac3aca26f2dfe00delivered2017-03-09 09:54:052017-03-09 09:54:052017-03-10 11:18:032017-03-17 15:08:012017-03-28 00:00:00
9943763943bddc261676b46f01ca7ac2f7bd81fca14ff2861355f6e5f14306ff977a7delivered2018-02-06 12:58:582018-02-06 13:10:372018-02-07 23:22:422018-02-28 17:37:562018-03-02 00:00:00
9943883c1379a015df1e13d02aae0204711ab1aa71eb042121263aafbe80c1b562c9cdelivered2017-08-27 14:46:432017-08-27 15:04:162017-08-28 20:52:262017-09-21 11:24:172017-09-27 00:00:00
9943911c177c8e97725db2631073c19f07b62b331b74b18dc79bcdf6532d51e1637c1delivered2018-01-08 21:28:272018-01-08 21:36:212018-01-12 15:35:032018-01-25 23:32:542018-02-15 00:00:00
9944066dea50a8b16d9b4dee7af250b4be1a5edb027a75a1449115f6b43211ae02a24delivered2018-03-08 20:57:302018-03-09 11:20:282018-03-09 22:11:592018-03-16 13:08:302018-04-03 00:00:00

99441 rows × 8 columns

1
2
# 결측치 확인
df2.isna().sum().sort_values(ascending=False)
0
order_delivered_customer_date2965
order_delivered_carrier_date1783
order_approved_at160
order_id0
customer_id0
order_status0
order_purchase_timestamp0
order_estimated_delivery_date0


1
df2[df2['order_delivered_customer_date'].isna()]
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_date
6136cce7faa42fdb2cefd53fdc79a6098ed0271e0b7da060a393796590e7b737ainvoiced2017-04-11 12:22:082017-04-13 13:25:17NaNNaN2017-05-09 00:00:00
44ee64d42b8cf066f35eac1cf57de1aa85caded193e8e47b8362864762a83db3c5shipped2018-06-04 16:44:482018-06-05 04:31:182018-06-05 14:32:00NaN2018-06-28 00:00:00
1030760a852e4e9d89eb77bf631eaaf1c84d2a79636084590b7465af8ab374a8cf5invoiced2018-08-03 17:44:422018-08-07 06:15:14NaNNaN2018-08-21 00:00:00
12815bed8e2fec7fdbadb186b57c46c92f2f3f0e613e0bdb9c7cee75504f0f90679processing2017-09-03 14:22:032017-09-03 14:30:09NaNNaN2017-10-03 00:00:00
1546942b8da583c2f9957e990d02860701952006a9383bf149a4fb24226b173106fshipped2018-01-10 11:33:072018-01-11 02:32:302018-01-11 19:39:23NaN2018-02-07 00:00:00
...........................
992833a3cddda5a7c27851bd96c33134128400b0d6095c5555fe083844281f6b093bbcanceled2018-08-31 16:13:44NaNNaNNaN2018-10-01 00:00:00
99313e9e64a17afa9653aacf2616d94c005b8b4cd0522e632e481f8eaf766a2646e86processing2018-01-05 23:07:242018-01-09 07:18:05NaNNaN2018-02-06 00:00:00
99347a89abace0dcc01eeb267a9660b5ac1262f0524a7b1b3845a1a57fcf3910c4333canceled2018-09-06 18:45:47NaNNaNNaN2018-09-27 00:00:00
99348a69ba794cc7deb415c3e15a0a3877e69726f0894b5becdf952ea537d5266e543unavailable2017-08-23 16:28:042017-08-28 15:44:47NaNNaN2017-09-15 00:00:00
994155fabc81b6322c8443648e1b21a6fef2132c9df889d41b0ee8309a5efb6855dcbunavailable2017-10-10 10:50:032017-10-14 18:35:57NaNNaN2017-10-23 00:00:00

2965 rows × 8 columns

1
2
mask1 = df2[df2['order_delivered_customer_date'].isna()]
mask1['order_status'].value_counts()
count
order_status
shipped1107
canceled619
unavailable609
invoiced314
processing301
delivered8
created5
approved2


1
mask1[mask1['order_status'] == 'delivered']
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_date
30022d1e2d5bf4dc7227b3bfebb81328c15fec05a6d8558c6455f0cbbd8a420ad34fdelivered2017-11-28 17:44:072017-11-28 17:56:402017-11-30 18:12:23NaN2017-12-18 00:00:00
20618f5dd62b788049ad9fc0526e3ad11a0975e89028e024b381dc84a13a3570decb4delivered2018-06-20 06:58:432018-06-20 07:19:052018-06-25 08:05:00NaN2018-07-16 00:00:00
438342ebdfc4f15f23b91474edf87475f108e29f0540231702fda0cfdee0a310f11aadelivered2018-07-01 17:05:112018-07-01 17:15:122018-07-03 13:57:00NaN2018-07-30 00:00:00
79263e69f75a717d64fc5ecdfae42b2e8e086cfda40ca8dd0a5d486a9635b611b398adelivered2018-07-01 22:05:552018-07-01 22:15:142018-07-03 13:57:00NaN2018-07-30 00:00:00
828680d3268bad9b086af767785e3f0fc01334f1d63d35fb7c8999853b2699f5c7649delivered2018-07-01 21:14:022018-07-01 21:29:542018-07-03 09:28:00NaN2018-07-24 00:00:00
926432d858f451373b04fb5c984a1cc2defafe08caf668d499a6d643dafd7c5cc498adelivered2017-05-25 23:22:432017-05-25 23:30:16NaNNaN2017-06-23 00:00:00
97647ab7c89dc1bf4a1ead9d6ec1ec8968a84dd1b84a7286eb4524d52af4256c0ba24delivered2018-06-08 12:09:392018-06-08 12:36:392018-06-12 14:10:00NaN2018-06-26 00:00:00
9803820edc82cf5400ce95e1afacc25798b3128c37425f1127d887d7337f284080a0fdelivered2018-06-27 16:09:122018-06-27 16:29:302018-07-03 19:26:00NaN2018-07-19 00:00:00
1
2
mask2 = df2[~df2['order_delivered_customer_date'].isna()]
mask2['order_status'].value_counts()
count
order_status
delivered96470
canceled6


1
2
mask3 = df2[df2['order_delivered_carrier_date'].isna()]
mask3['order_status'].value_counts()
count
order_status
unavailable609
canceled550
invoiced314
processing301
created5
approved2
delivered2


1
2
mask4 = df2[df2['order_approved_at'].isna()]
mask4['order_status'].value_counts()
count
order_status
canceled141
delivered14
created5


  • 일부 결측치가 관찰(배달이 완료됐는데 배송날짜가 안적혀있다던지 등)되었지만, 그 수가 100개 미만이고, 해당 결측치들에서 활용할수있는 유의미한 정보가(어떤것을 주문하고 판매됐고 가격, 카테고리 정보 등) 남아있다고 판단하기에 결측치를 제거하지않음
1
2
# 중복값 확인
df2.duplicated().sum()
1
0
1
df2['order_id'].duplicated().sum() + df2['customer_id'].duplicated().sum()
1
0
1
2
3
# customers 와 orders 데이터 customer_id 확인
merged_check_customer = pd.merge(df, df2, on='customer_id', how='outer', indicator=True)
merged_check_customer['_merge'].value_counts()
count
_merge
both99441
left_only0
right_only0


  • customers와 orders 데이터의 customer_id는 1:1로 정확히 매칭된다
  • 다른데이터들과 order_id와 customer_id로 연결되고있다
1
df2['order_status'].value_counts()
count
order_status
delivered96478
shipped1107
canceled625
unavailable609
invoiced314
processing301
created5
approved2


1
round(df2[df2['order_status'] == 'canceled'].shape[0] / df2.shape[0] * 100, 2)
1
0.63
1
round(((df2[df2['order_status'] == 'canceled'].shape[0]) + (df2[df2['order_status'] == 'unavailable'].shape[0])) / df2.shape[0] * 100, 2)
1
1.24
  • 대부분의 주문이 배달이 완료된 상황이고, 취소율은 0.6%(unavailable 포함시 1.2%)로 낮은수준으로 관찰됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 'order_purchase_timestamp' 컬럼을 datetime 형식으로 변환
df2['order_purchase_timestamp'] = pd.to_datetime(df2['order_purchase_timestamp'])

# 연도와 월별로 그룹화하여 주문 수 계산
monthly_orders = df2.groupby(df2['order_purchase_timestamp'].dt.to_period('M')).size()

# 시각화
plt.figure(figsize=(10, 6))
bars = plt.bar(monthly_orders.index.astype(str), monthly_orders.values, color='skyblue')

# 주문 수 표시
for bar in bars:
    yval = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2, yval, str(yval), ha='center', va='bottom')

plt.title('월별 주문 수')
plt.xlabel('연도-월')
plt.ylabel('주문 수')
plt.xticks(rotation=45)
plt.grid(axis='y')

# 레이아웃 조정 및 그래프 표시
plt.tight_layout()
plt.show()

png

  • 월별 주문수는 상승곡선을 보이고 있음
1
2
3
# 파생변수 배송기간 생성 (고객 배송일 - 고객 주문일 )
df2['order_delivered_customer_date'] = pd.to_datetime(df2['order_delivered_customer_date'])
df2['delivery_time'] = (df2['order_delivered_customer_date'] - df2['order_purchase_timestamp']).dt.days
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
plt.figure(figsize=(12, 6))
n, bins, patches = plt.hist(df2['delivery_time'], bins=range(0, int(df2['delivery_time'].max()) + 5, 5), color='skyblue', edgecolor='black')

for count, x in zip(n, bins):
    plt.text(x + 2.5, count, str(int(count)), ha='center', va='bottom')

plt.title('배송 기간 분포 (5일 단위)')
plt.xlabel('배송 기간 (일)')
plt.ylabel('주문 수')
plt.xticks(range(0, int(df2['delivery_time'].max()) + 5, 5))
plt.grid(axis='y')

# 레이아웃 조정 및 그래프 표시
plt.tight_layout()
plt.show()

png

  • 대부분 1달 이내 배달이 완료되고 5일~10일 분포가 가장 많음. 1달 뒤에 배송되는것도 일부 존재하고 배송에 2달 걸리는 배달도 다수있음
1
2
merged_df_delivery = pd.merge(df, df2[['customer_id', 'delivery_time']], on='customer_id', how='left')
merged_df_delivery
customer_idcustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_statedelivery_time
006b8999e2fba1a1fbc88172c00ba8bc7861eff4711a542e4b93843c6dd7febb014409francaSP8.0
118955e83d337fd6b2def6b18a428ac77290c77bc529b7ac935b93aa66c333dc39790sao bernardo do campoSP16.0
24e7b3e00288586ebd08712fdd0374a03060e732b5b29e8181a18229c7b0b2b5e1151sao pauloSP26.0
3b2b6027bc5c5109e529d4dc6358b12c3259dac757896d24d7702b9acbbff3f3c8775mogi das cruzesSP14.0
44f2d8ab171c80ec8364f7c12e35b23ad345ecd01c38d18a9036ed96c73b8d06613056campinasSP11.0
.....................
9943617ddf5dd5d51696bb3d7c6291687be6f1a29b476fee25c95fbafc67c5ac95cf83937sao pauloSP6.0
99437e7b71a9017aa05c9a7fd292d714858e8d52a67c98be1cf6a5c84435bd38d095d6764taboao da serraSP7.0
994385e28dfe12db7fb50a4b2f691faecea5ee9f50caf99f032f0bf3c55141f019d9960115fortalezaCE30.0
9943956b18e2166679b8a959d72dd06da27f973c2643a0a458b49f58cea58833b192e92120canoasRS12.0
99440274fa6071e5e17fe303b9748641082c884732c5050c01db9b23e19ba398993986703cotiaSP7.0

99441 rows × 6 columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
top_states = ['SP', 'RJ', 'MG']
fig, axs = plt.subplots(len(top_states), 1, figsize=(10, 15))

for i, state in enumerate(top_states):
    state_data = merged_df_delivery[merged_df_delivery['customer_state'] == state]

    # max 값을 int로 변환하여 range에 사용
    max_delivery_time = int(state_data['delivery_time'].max())

    axs[i].hist(state_data['delivery_time'],
                 bins=range(0, max_delivery_time + 10, 10),
                 color='skyblue', edgecolor='black', alpha=0.7)

    axs[i].set_title(f'{state} - 배송 기간 분포 (10일 단위)')
    axs[i].set_xlabel('배송 기간 (일)')
    axs[i].set_ylabel('주문 수')
    axs[i].grid(axis='y')

    # 각 막대 위에 카운트 표시
    counts, bins = np.histogram(state_data['delivery_time'], bins=range(0, max_delivery_time + 10, 10))
    for j in range(len(counts)):
        axs[i].text(bins[j] + 5, counts[j], str(counts[j]), ha='center', va='bottom')

plt.tight_layout()
plt.show()

png

  • 상위 3개 주 (전체 고객 중 70%) 배송기간은 RJ가 장기배송이 일부 있지만 대부분 20일 이내 배송되고 있음

3. Order Items (olist_order_items_dataset.csv)

컬럼 이름데이터 타입설명
order_idVARCHAR주문 고유 식별자
order_item_idINT주문 내에서의 각 상품의 식별자
product_idVARCHAR상품 고유 식별자
seller_idVARCHAR판매자 고유 식별자
shipping_limit_dateTIMESTAMP판매자가 상품을 발송해야 하는 마감일
priceFLOAT주문된 상품의 가격
freight_valueFLOAT상품의 배송비
1
2
df3 = pd.read_csv('/content/drive/MyDrive/archive/olist_order_items_dataset.csv')
df3
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
000010242fe8c5a6d1ba2dd792cb1621414244733e06e7ecb4970a6e2683c13e6148436dade18ac8b2bce089ec2a0412022017-09-19 09:45:3558.9013.29
100018f77f2f0320c557190d7a144bdd31e5f2d52b802189ee658865ca93d83a8fdd7ddc04e1b6c2c614352b383efe2d362017-05-03 11:05:13239.9019.93
2000229ec398224ef6ca0657da4fc703e1c777355d18b72b67abbeef9df44fd0fd5b51032eddd242adc84c38acab88f23d2018-01-18 14:48:30199.0017.87
300024acbcdf0a6daa1e931b038114c7517634da152a4610f1595efa32f14722fc9d7a1d34a5052409006425275ba1c2b42018-08-15 10:10:1812.9912.79
400042b26cf59d7ce69dfabb4e55b4fd91ac6c3623068f30de03045865e4e10089df560393f3a51e74553ab94004ba5c872017-02-13 13:57:51199.9018.14
........................
112645fffc94f6ce00a00581880bf54a75a03714aa6014eceb682077f9dc4bffebc05b0b8bc237ba3788b23da09c0f1f3a3288c2018-05-02 04:11:01299.9943.41
112646fffcd46ef2263f404302a634eb57f7eb132e07fd915822b0765e448c4dd74c828f3c38ab652836d21de61fb8314b691822018-07-20 04:31:48350.0036.53
112647fffce4705a9662cd70adb13d4a31832d172a30483855e2eafc67aee5dc2560482c3cfdc648177fdbbbb35635a37472c532017-10-30 17:14:2599.9016.95
112648fffe18544ffabc95dfada21779c9644f19c422a519119dcad7575db5af1ba540e2b3e4a2a3ea8e01938cabda2a3e5cc792017-08-21 00:04:3255.998.72
112649fffe41c64501cc87c801fd61db3f62441350688d9dc1e75ff97be326363655e01f7ccf836d21b2fb1de37564105216cc12018-06-12 17:10:1343.0012.79

112650 rows × 7 columns

1
2
# 결측치 확인
df3.isna().sum().sort_values(ascending=False)
0
order_id0
order_item_id0
product_id0
seller_id0
shipping_limit_date0
price0
freight_value0


1
2
# 중복값 확인
df3.duplicated().sum()
1
0
1
df3['order_id'].duplicated().sum()
1
13984
1
df3[df3['order_id'].duplicated()].sort_values(by=['product_id', 'order_item_id'])
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
82591bb9552306cf6879fde49f4ba3bd9429920011c512eb256aa0dbbb544d8dffcf6eb4ffb71f0cb1b1c3d63fad021ecf93e12017-12-22 20:38:2952.0015.80
86374c432657bb18ddf7f48b7227db09048d42001795ec6f1b187d37335e1c4704762e8b321bb669392f5163d04c59e235e0662017-12-18 00:39:2538.9016.11
97530dd436680fbd2d38edb26277f5b8379dc2001795ec6f1b187d37335e1c4704762e8b321bb669392f5163d04c59e235e0662017-12-29 15:30:5038.909.34
4968970ed857e24fd6bf1e25a9bc791a2f6b92001b72dfd63e9833e8c02742adf472e38a32e327fe2c1b3511609d81aaf9f0422017-09-06 12:35:1634.999.90
85437c214276ccd69c3953f880b487209f47e2001b72dfd63e9833e8c02742adf472e38a32e327fe2c1b3511609d81aaf9f0422017-07-13 15:43:1534.997.78
........................
56508808b7fff91e537a5df90717957ee5bb12ffef256879dbadcab7e77950f4f4a195113e3a788b935f48aad63e1c41dac1bd2018-06-15 19:54:4231.7818.23
83782be48bdef069ed1eb0d320bfe65d263512fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-07 19:53:447.5012.69
106253f179e0782e0180bc2ec9ce167d4cf2452fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.5016.11
106254f179e0782e0180bc2ec9ce167d4cf2453fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.5016.11
106255f179e0782e0180bc2ec9ce167d4cf2454fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.5016.11

13984 rows × 7 columns

1
df3.sort_values(by=['order_item_id'])
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
000010242fe8c5a6d1ba2dd792cb1621414244733e06e7ecb4970a6e2683c13e6148436dade18ac8b2bce089ec2a0412022017-09-19 09:45:3558.9013.29
72706a5c7406fd66b64f69acd95538f35b97e106edb72f1e0c64b14c5b79353f7abea3391fc6631aebcf3004804e51b40bcf1e2017-08-28 21:25:1945.9515.10
72705a5c681209e1bcb90066e530c285ce2c51eec68ed7d496bb2ee6aa0a69bb78acd25f5b43b2bffa8656e4bc6efeb13cc6492017-12-21 20:51:3689.009.44
72704a5c654c2a0126153f98af71a65a159de1b37a8cda46313ac91d79f16601ca5253955fee9216a65b617aa5c0531780ce602018-06-12 12:10:3595.0020.72
72703a5c523f7f14f85ee88f26643f9a99e6614b96786612ebe7463132fce2c4dca136d94a40fd42351c259927028d163af8422018-06-14 08:31:15129.0026.05
........................
119501b15974a0141d54e36626dca3fdc731a19ee3d532c8a438679776d222e997606b38e6d7754bc7e0f22c96d255ebda59eba2018-03-01 02:50:48100.0010.12
119511b15974a0141d54e36626dca3fdc731a20ee3d532c8a438679776d222e997606b38e6d7754bc7e0f22c96d255ebda59eba2018-03-01 02:50:48100.0010.12
75122ab14fdcfbe524636d65ee38360e22ce8209571759451b1d780ee7c15012ea109d4ce27a3cc3c8cc1ea79d11e561e9bebb62017-08-30 14:30:2398.7014.44
573168272b63d03f5f79c56e9e4120aec44ef20270516a3f41dc035aa87d220228f844c2709af9587499e95e803a6498a5a56e92017-07-21 18:25:231.207.89
573178272b63d03f5f79c56e9e4120aec44ef2179ce45dbc2ea29b22b5a261bbb7b7ee72709af9587499e95e803a6498a5a56e92017-07-21 18:25:237.806.57

112650 rows × 7 columns

1
df3[df3['product_id'] == 'fff0a542c3c62682f23305214eaeaa24']
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
318554838d1c1cbef87593a3921429e633ccc1fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-11-17 20:50:317.315.10
479636d03ab0713a35b9475f6c5ed0d9899761fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-07 14:11:227.516.11
83781be48bdef069ed1eb0d320bfe65d263511fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-07 19:53:447.512.69
83782be48bdef069ed1eb0d320bfe65d263512fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-07 19:53:447.512.69
106252f179e0782e0180bc2ec9ce167d4cf2451fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.516.11
106253f179e0782e0180bc2ec9ce167d4cf2452fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.516.11
106254f179e0782e0180bc2ec9ce167d4cf2453fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.516.11
106255f179e0782e0180bc2ec9ce167d4cf2454fff0a542c3c62682f23305214eaeaa2408d2d642cf72b622b14dde1d2f5eb2f52017-12-12 02:35:397.516.11
  • 같은 주문번호/상품번호는 같은 상품을 n개 샀다는 의미이다 → 해당주문번호의 payment_value값과 (price + freight_value)*n이 동일함
  • 또한, 같은 상품번호는 해당 상품을 현재 데이터기간동안(2016년~2018년) n개 판매했다는 의미이다. 예시에서 ‘fff0a542c3c62682f23305214eaeaa24’ 상품이 총 8번 판매되었다
1
df3['order_item_id'].value_counts()
count
order_item_id
198666
29803
32287
4965
5460
6256
758
836
928
1025
1117
1213
138
147
155
163
173
183
193
203
211


  • order_item_id가 order 데이터의 order_id 수보다 775개 작다 (99441 > 98666)
1
2
3
4
5
6
7
8
not_in_df2 = df3[~df3['order_id'].isin(df2['order_id'])]
not_in_df3 = df2[~df2['order_id'].isin(df3['order_id'])]

print("df3에만 있는 order_id:")
print(not_in_df2['order_id'])

print("\ndf2에만 있는 order_id:")
print(not_in_df3['order_id'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df3에만 있는 order_id:
Series([], Name: order_id, dtype: object)

df2에만 있는 order_id:
266      8e24261a7e58791d10cb1bf9da94df5c
586      c272bcd21c287498b4883c7512019702
687      37553832a3a89c9b2db59701c357ca67
737      d57e15fb07fd180f06ab3926b39edcd2
1130     00b1cb0320190ca0daa2c88b35206009
                       ...               
99252    aaab15da689073f8f9aa978a390a69d1
99283    3a3cddda5a7c27851bd96c3313412840
99347    a89abace0dcc01eeb267a9660b5ac126
99348    a69ba794cc7deb415c3e15a0a3877e69
99415    5fabc81b6322c8443648e1b21a6fef21
Name: order_id, Length: 775, dtype: object
1
2
# order_items에 없는 주문들은 무엇일까
not_in_df3
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
2668e24261a7e58791d10cb1bf9da94df5c64a254d30eed42cd0e6c36dddb88adf0unavailable2017-11-16 15:09:282017-11-16 15:26:57NaNNaT2017-12-05 00:00:00NaN
586c272bcd21c287498b4883c75120197029582c5bbecc65eb568e2c1d839b5cba1unavailable2018-01-31 11:31:372018-01-31 14:23:50NaNNaT2018-02-16 00:00:00NaN
68737553832a3a89c9b2db59701c357ca677607cd563696c27ede287e515812d528unavailable2017-08-14 17:38:022017-08-17 00:15:18NaNNaT2017-09-05 00:00:00NaN
737d57e15fb07fd180f06ab3926b39edcd2470b93b3f1cde85550fc74cd3a476c78unavailable2018-01-08 19:39:032018-01-09 07:26:08NaNNaT2018-02-06 00:00:00NaN
113000b1cb0320190ca0daa2c88b352060093532ba38a3fd242259a514ac2b6ae6b6canceled2018-08-28 15:26:39NaNNaNNaT2018-09-12 00:00:00NaN
..............................
99252aaab15da689073f8f9aa978a390a69d1df20748206e4b865b2f14a5eabbfcf34unavailable2018-01-16 14:27:592018-01-17 03:37:34NaNNaT2018-02-06 00:00:00NaN
992833a3cddda5a7c27851bd96c33134128400b0d6095c5555fe083844281f6b093bbcanceled2018-08-31 16:13:44NaNNaNNaT2018-10-01 00:00:00NaN
99347a89abace0dcc01eeb267a9660b5ac1262f0524a7b1b3845a1a57fcf3910c4333canceled2018-09-06 18:45:47NaNNaNNaT2018-09-27 00:00:00NaN
99348a69ba794cc7deb415c3e15a0a3877e69726f0894b5becdf952ea537d5266e543unavailable2017-08-23 16:28:042017-08-28 15:44:47NaNNaT2017-09-15 00:00:00NaN
994155fabc81b6322c8443648e1b21a6fef2132c9df889d41b0ee8309a5efb6855dcbunavailable2017-10-10 10:50:032017-10-14 18:35:57NaNNaT2017-10-23 00:00:00NaN

775 rows × 9 columns

1
2
3
4
print(not_in_df3['order_status'].value_counts())
print(not_in_df3['order_purchase_timestamp'].isna().sum())
print(not_in_df3['order_delivered_carrier_date'].unique())
print(not_in_df3['order_delivered_customer_date'].unique())
1
2
3
4
5
6
7
8
9
10
11
12
order_status
unavailable    603
canceled       164
created          5
invoiced         2
shipped          1
Name: count, dtype: int64
0
[nan '2016-11-07 16:37:37']
<DatetimeArray>
['NaT']
Length: 1, dtype: datetime64[ns]
1
not_in_df3[not_in_df3['order_status'] == 'shipped']
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
23254a68ce1686d536ca72bd2dadc4b8671e5d7bed5fac093a4136216072abaf599d5shipped2016-10-05 01:47:402016-10-07 03:11:222016-11-07 16:37:37NaT2016-12-01 00:00:00NaN
  • 상품 취소나 시스템 오류 등으로 주문번호는 남아있지만 최종적으로 고객에게 배송되지않고 상품관련정보도 전부삭제된 데이터이다. 결측치로 제거할수도있지만, 사용자가 구매시도한 데이터에는 결측치가 없었다. 따라서 사용자가 구매를 시도한후 변경된 데이터들이다. 따라서 해당 데이터를 제거하지 않기로 결정함
1
df3['shipping_limit_date'].isna().sum()
1
0
1
df3['shipping_limit_date'].describe()
shipping_limit_date
count112650
unique93318
top2017-07-21 18:25:23
freq21


1
(df3['freight_value'] > df3['price']).sum()
1
4124
  • 상품보다 배송비가 더 비싼 건이 전체 거래 대비 약 4% 발생한것으로 관찰됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 연도와 월별로 그룹화하여 주문 수 계산
monthly_orders = df2.groupby(df2['order_purchase_timestamp'].dt.to_period('M')).size()

# 1. 'shipping_limit_date'를 datetime 형식으로 변환
df3['shipping_limit_date'] = pd.to_datetime(df3['shipping_limit_date'])

# 2. 월별 매출액 집계
monthly_sales = df3.resample('M', on='shipping_limit_date')['price'].sum()

# 3. 2018년 8월까지의 데이터만 필터링
monthly_sales = monthly_sales[monthly_sales.index <= '2018-08-31']

# 4. subplot 생성
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 12))

# 첫 번째 차트: 월별 주문 수
bars = ax1.bar(monthly_orders.index.astype(str), monthly_orders.values, color='skyblue')

# 주문 수 표시
for bar in bars:
    yval = bar.get_height()
    ax1.text(bar.get_x() + bar.get_width()/2, yval, str(yval), ha='center', va='bottom')

ax1.set_title('월별 주문 수')
ax1.set_xlabel('연도-월')
ax1.set_ylabel('주문 수')
ax1.set_xticks(range(len(monthly_orders.index)))  # x축 위치 설정
ax1.set_xticklabels(monthly_orders.index.astype(str), rotation=45)  # 레이블과 회전 설정
ax1.grid(axis='y')

# 두 번째 차트: 월별 매출액 추이
ax2.plot(monthly_sales.index, monthly_sales.values, marker='o')

# 매출액 숫자 표기
for x, y in zip(monthly_sales.index, monthly_sales.values):
    ax2.text(x, y, f'{y:,.0f}', fontsize=9, ha='center', va='bottom')

# x축을 3개월 단위로 설정
ax2.set_xticks(monthly_sales.index[::3])
ax2.set_title('월별 매출액 추이')
ax2.set_xlabel('')
ax2.set_ylabel('매출액')
ax2.set_xlim(pd.Timestamp('2016-09-01'), pd.Timestamp('2018-08-31'))  # x축 범위 설정
ax2.grid()

# 레이아웃 조정 및 그래프 표시
plt.tight_layout()
plt.show()

png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 1. 상품별 판매 빈도수와 총 매출액 계산
product_sales = df3.groupby('product_id').agg(
    sales_count=('price', 'size'),  # 판매 빈도수
    total_revenue=('price', 'sum')  # 총 매출액
).reset_index()

# 2. 총 매출액 기준으로 정렬하고 상위 10개 선택
top_products = product_sales.sort_values(by='total_revenue', ascending=False).head(10)

# 3. 상품 순서를 총 매출액 기준으로 역순으로 설정
top_products = top_products.sort_values(by='total_revenue', ascending=True)

# 4. subplot 생성
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))

# 5. 왼쪽 차트: 총 매출액
bars1 = ax1.barh(top_products['product_id'], top_products['total_revenue'], color='skyblue')
ax1.set_title('상품별 총 매출액 (상위 10개 상품)')
ax1.set_xlabel('총 매출액')
ax1.grid(axis='x')
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)

# 6. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2, f'{bar.get_width():,.0f}',
             va='center', ha='left', fontsize=9)

# 7. 오른쪽 차트: 판매 빈도수
bars2 = ax2.barh(top_products['product_id'], top_products['sales_count'], color='lightgreen')
ax2.set_title('상품별 판매 빈도수 (상위 10개 상품)')
ax2.set_xlabel('판매 빈도수')
ax2.grid(axis='x')
ax2.set_yticklabels([])
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)

# 8. 숫자 표기
for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2, f'{bar.get_width():,.0f}',
             va='center', ha='left', fontsize=9)

# 9. 레이아웃 조정 및 그래프 표시
plt.tight_layout()
plt.show()

png

  • 주문수와 매출액 추이는 비슷한 흐름으로 이루어져있음
  • 총 매출액이 높다고 많이 팔리는 상품은 아닌것으로 관찰됐고, 해당 상품의 카테고리는 products데이터를 활용하면 확인할수있음

4. Products (olist_products_dataset.csv)

컬럼 이름데이터 타입설명
product_idVARCHAR상품 고유 식별자
product_category_nameVARCHAR상품의 카테고리 이름
product_name_lengthINT상품 이름의 길이
product_description_lengthINT상품 설명의 길이
product_photos_qtyINT상품 사진의 개수
product_weight_gFLOAT상품의 무게 (그램)
product_length_cmFLOAT상품 길이 (센티미터)
product_height_cmFLOAT상품 높이 (센티미터)
product_width_cmFLOAT상품 폭 (센티미터)
1
2
df4 = pd.read_csv('/content/drive/MyDrive/archive/olist_products_dataset.csv')
df4
product_idproduct_category_nameproduct_name_lenghtproduct_description_lenghtproduct_photos_qtyproduct_weight_gproduct_length_cmproduct_height_cmproduct_width_cm
01e9e8ef04dbcff4541ed26657ea517e5perfumaria40.0287.01.0225.016.010.014.0
13aa071139cb16b67ca9e5dea641aaa2fartes44.0276.01.01000.030.018.020.0
296bd76ec8810374ed1b65e291975717fesporte_lazer46.0250.01.0154.018.09.015.0
3cef67bcfe19066a932b7673e239eb23dbebes27.0261.01.0371.026.04.026.0
49dc1a7de274444849c219cff195d0b71utilidades_domesticas37.0402.04.0625.020.017.013.0
..............................
32946a0b7d5a992ccda646f2d34e418fff5a0moveis_decoracao45.067.02.012300.040.040.040.0
32947bf4538d88321d0fd4412a93c974510e6construcao_ferramentas_iluminacao41.0971.01.01700.016.019.016.0
329489a7c6041fa9592d9d9ef6cfe62a71f8ccama_mesa_banho50.0799.01.01400.027.07.027.0
3294983808703fc0706a22e264b9d75f04a2einformatica_acessorios60.0156.02.0700.031.013.020.0
32950106392145fca363410d287a815be6de4cama_mesa_banho58.0309.01.02083.012.02.07.0

32951 rows × 9 columns

1
2
# 결측치 찾기
df4.isna().sum().sort_values(ascending=False)
0
product_category_name610
product_name_lenght610
product_description_lenght610
product_photos_qty610
product_weight_g2
product_length_cm2
product_height_cm2
product_width_cm2
product_id0


1
2
mask1_df4 = df4[df4['product_category_name'].isna()]
df3[df3['product_id'].isin(mask1_df4['product_id'])]
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
1230046e1d57f4c07c8c92ab26be8c3dfc01ff6caf9340512b8bf6d2a2a6df032cfa38e6dada03429a47197d5d584d793b412017-10-02 15:49:177.797.78
12500482f2670787292280e0a8153d824671a9c404971d1a5b1cbc2e4070e02731fd702835e4b785b67a084280efca3557562017-02-17 16:18:077.6010.96
132004f5d8f238e8908e6864b874eda339115a848e4ab52fd5445cdc07aab1c40e48c826c40d7b19f62a09e2d7c5e7295ee22018-03-06 09:29:25122.9915.61
1420057199db02d1a5ef41bacbf41f8f63b141eee23c25f7a574dfaf8d5c151dbb12e5a3438891c0bfdb9394643f95273d8e2018-01-25 09:07:5120.3016.79
171006cb7cafc99b29548d4f412c7f9f4931e10758160da97891c2fdcbc35f0f031d323ce52b5b81df2cd804b017b7f09aa72018-02-22 13:35:2856.0014.14
........................
112306ff24fec69b7f3d30f9dc1ab3aee7c17915a848e4ab52fd5445cdc07aab1c40e48c826c40d7b19f62a09e2d7c5e7295ee22018-02-01 02:40:12122.9915.61
112333ff3024474be86400847879103757d1fd1f9b1795281ce51b1cf39ef6d101ae8ab3771c85bac139d2344864ede5d9341e32017-11-21 03:55:3939.909.94
112350ff3a45ee744a7c1f8096d2e72c1a23e41b61d1388a17e3f547d2bc218df02335b07017df32dc5f2f1d2801e579548d6202017-05-10 10:15:19139.0021.42
112438ff7b636282b98e0aa524264b295ed9281431df35e52c10451171d8037482eeb436cd68b3ed6d59aaa9fece558ad360c0a2018-02-22 15:35:3549.9015.11
112501ffa5e4c604dea4f0a59d19cc2322ac192bd421826916d3e1d445cb860cea3c0fb59cd88080b93f3c18508673122d261692017-12-11 08:41:2029.9915.10

1603 rows × 7 columns

1
2
mask2_df4 = df3[df3['product_id'].isin(mask1_df4['product_id'])]
df2[df2['order_id'].isin(mask2_df4['order_id'])]
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
6136cce7faa42fdb2cefd53fdc79a6098ed0271e0b7da060a393796590e7b737ainvoiced2017-04-11 12:22:082017-04-13 13:25:17NaNNaT2017-05-09 00:00:00NaN
107bfe42c22ecbf90bc9f35cf591270b6a7803ac05904124294f8767894d6da532bdelivered2018-01-27 22:04:342018-01-27 22:16:182018-02-03 03:56:002018-02-09 20:16:402018-02-26 00:00:0012.0
18058ac1947c1a9067b9f416cba6d844a3fee8e1d37f563ecc11cc4dcb4dfd794c2delivered2017-09-13 09:18:502017-09-13 13:45:432017-09-14 21:20:032017-09-21 21:16:172017-09-25 00:00:008.0
228e22b71f6e4a481445ec4527cb4c405f71faf89c8f142db3fca6cf314c51a37b6delivered2017-04-22 13:48:182017-04-22 14:01:132017-04-24 19:08:532017-05-02 15:45:272017-05-11 00:00:0010.0
263a094215e786240fcfefb83d18036a1cd86acfb656743da0c113d176832c9d535delivered2018-02-08 18:56:452018-02-08 19:32:182018-02-09 21:41:542018-02-19 13:28:502018-02-22 00:00:0010.0
..............................
990691a10e938a1c7d8e5eecc3380f71ca76b8a81607347c25d881d995d94de6ad824delivered2018-07-25 08:58:352018-07-26 03:10:202018-07-27 11:32:002018-08-01 19:28:202018-08-10 00:00:007.0
99215e33865519137f5737444109ae843863364b086bdcc54458af3ea3bd838db54a5delivered2018-05-28 00:44:062018-05-29 03:31:172018-05-30 13:13:002018-06-01 22:25:392018-06-20 00:00:004.0
99222f0dd9af88d8ef5a8e4670fbbedaf19c430ddb50bd22ee927ebe308ea3da60735delivered2017-09-02 20:38:292017-09-05 04:24:122017-09-14 23:13:412017-09-15 14:59:502017-09-19 00:00:0012.0
99228272874573723eec18f23c0471927d77848e080c8001e92ebea2b64e474f91a60delivered2017-12-20 23:10:332017-12-20 23:29:372017-12-21 21:49:352017-12-26 22:29:322018-01-09 00:00:005.0
99245dff2b9b8d7cfc595836945e1443789c32436fb2666a65fbacae82532e797cabfdelivered2018-07-16 12:59:022018-07-17 04:21:002018-07-17 15:08:002018-07-20 20:41:322018-08-07 00:00:004.0

1451 rows × 9 columns

  • 상품이 카테고리화되지 않았지만 배송완료된 주문이 있다. 카테고리 미분류 상품으로 취급해야한다.
1
2
# 중복값 확인
df4['product_id'].duplicated().sum()
1
0
  • products 데이터는 주문된 상품들의 정보를 보여주고있음

(참고) 상품명 번역 정보 (product_category_name_translation.csv)

1
2
df4_name = pd.read_csv('/content/drive/MyDrive/archive/product_category_name_translation.csv')
df4_name
product_category_nameproduct_category_name_english
0beleza_saudehealth_beauty
1informatica_acessorioscomputers_accessories
2automotivoauto
3cama_mesa_banhobed_bath_table
4moveis_decoracaofurniture_decor
.........
66floresflowers
67artes_e_artesanatoarts_and_craftmanship
68fraldas_higienediapers_and_hygiene
69fashion_roupa_infanto_juvenilfashion_childrens_clothes
70seguros_e_servicossecurity_and_services

71 rows × 2 columns

1
2
3
# 카테고리 영어로 변경
category_map = df4_name.set_index('product_category_name')['product_category_name_english'].to_dict()
df4['product_category_name'] = df4['product_category_name'].map(category_map).fillna(df4['product_category_name'])
1
df4['product_category_name'].unique()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
array(['perfumery', 'art', 'sports_leisure', 'baby', 'housewares',
       'musical_instruments', 'cool_stuff', 'furniture_decor',
       'home_appliances', 'toys', 'bed_bath_table',
       'construction_tools_safety', 'computers_accessories',
       'health_beauty', 'luggage_accessories', 'garden_tools',
       'office_furniture', 'auto', 'electronics', 'fashion_shoes',
       'telephony', 'stationery', 'fashion_bags_accessories', 'computers',
       'home_construction', 'watches_gifts',
       'construction_tools_construction', 'pet_shop', 'small_appliances',
       'agro_industry_and_commerce', nan, 'furniture_living_room',
       'signaling_and_security', 'air_conditioning', 'consoles_games',
       'books_general_interest', 'costruction_tools_tools',
       'fashion_underwear_beach', 'fashion_male_clothing',
       'kitchen_dining_laundry_garden_furniture',
       'industry_commerce_and_business', 'fixed_telephony',
       'construction_tools_lights', 'books_technical',
       'home_appliances_2', 'party_supplies', 'drinks', 'market_place',
       'la_cuisine', 'costruction_tools_garden', 'fashio_female_clothing',
       'home_confort', 'audio', 'food_drink', 'music', 'food',
       'tablets_printing_image', 'books_imported',
       'small_appliances_home_oven_and_coffee', 'fashion_sport',
       'christmas_supplies', 'fashion_childrens_clothes', 'dvds_blu_ray',
       'arts_and_craftmanship', 'pc_gamer', 'furniture_bedroom',
       'cine_photo', 'diapers_and_hygiene', 'flowers', 'home_comfort_2',
       'portateis_cozinha_e_preparadores_de_alimentos',
       'security_and_services', 'furniture_mattress_and_upholstery',
       'cds_dvds_musicals'], dtype=object)
1
2
merged_df_product = pd.merge(df3, df4[['product_id', 'product_category_name']], on='product_id', how='left')
merged_df_product
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_valueproduct_category_name
000010242fe8c5a6d1ba2dd792cb1621414244733e06e7ecb4970a6e2683c13e6148436dade18ac8b2bce089ec2a0412022017-09-19 09:45:3558.9013.29cool_stuff
100018f77f2f0320c557190d7a144bdd31e5f2d52b802189ee658865ca93d83a8fdd7ddc04e1b6c2c614352b383efe2d362017-05-03 11:05:13239.9019.93pet_shop
2000229ec398224ef6ca0657da4fc703e1c777355d18b72b67abbeef9df44fd0fd5b51032eddd242adc84c38acab88f23d2018-01-18 14:48:30199.0017.87furniture_decor
300024acbcdf0a6daa1e931b038114c7517634da152a4610f1595efa32f14722fc9d7a1d34a5052409006425275ba1c2b42018-08-15 10:10:1812.9912.79perfumery
400042b26cf59d7ce69dfabb4e55b4fd91ac6c3623068f30de03045865e4e10089df560393f3a51e74553ab94004ba5c872017-02-13 13:57:51199.9018.14garden_tools
...........................
112645fffc94f6ce00a00581880bf54a75a03714aa6014eceb682077f9dc4bffebc05b0b8bc237ba3788b23da09c0f1f3a3288c2018-05-02 04:11:01299.9943.41housewares
112646fffcd46ef2263f404302a634eb57f7eb132e07fd915822b0765e448c4dd74c828f3c38ab652836d21de61fb8314b691822018-07-20 04:31:48350.0036.53computers_accessories
112647fffce4705a9662cd70adb13d4a31832d172a30483855e2eafc67aee5dc2560482c3cfdc648177fdbbbb35635a37472c532017-10-30 17:14:2599.9016.95sports_leisure
112648fffe18544ffabc95dfada21779c9644f19c422a519119dcad7575db5af1ba540e2b3e4a2a3ea8e01938cabda2a3e5cc792017-08-21 00:04:3255.998.72computers_accessories
112649fffe41c64501cc87c801fd61db3f62441350688d9dc1e75ff97be326363655e01f7ccf836d21b2fb1de37564105216cc12018-06-12 17:10:1343.0012.79bed_bath_table

112650 rows × 8 columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 1. 'product_category_name'에 대한 빈도수 계산
category_counts = merged_df_product['product_category_name'].value_counts()
category_revenue = merged_df_product.groupby('product_category_name')['price'].sum()

# 2. 첫 번째 줄을 위한 상위 10개 카테고리 선택 및 정렬
top_categories_counts = category_counts.head(10).sort_values(ascending=True)
top_categories_revenue = category_revenue[top_categories_counts.index]

# 3. 두 번째 줄을 위한 총매출액 상위 10개 카테고리 선택 (정렬)
top_revenue_categories = category_revenue.nlargest(10).sort_values(ascending=True)

# 4. subplot 생성 (2x2)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))

# 5. 차트 생성 함수
def create_bar_chart(ax, x_data, y_data, title, xlabel, color):
    bars = ax.barh(x_data, y_data, color=color)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.grid(axis='x')
    for bar in bars:
        ax.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=9)

# 6. 첫 번째 차트: 'product_category_name' 빈도수 (상위 10개)
create_bar_chart(ax1, top_categories_counts.index, top_categories_counts.values,
                 '상품 카테고리별 빈도수 (상위 10개)', '빈도수', 'skyblue')

# 7. 두 번째 차트: 'product_category_name'의 총 매출액 (상위 10개)
create_bar_chart(ax2, top_categories_counts.index, top_categories_revenue,
                 '상품 카테고리별 총 매출액 (상위 10개)', '총 매출액', 'lightgreen')

# 8. 세 번째 차트: 총매출액 상위 10개의 상품 카테고리
create_bar_chart(ax3, top_revenue_categories.index, top_revenue_categories.values,
                 '총 매출액 상위 10개 상품 카테고리', '총 매출액', 'orange')

# 9. 네 번째 차트: 빈도수 (상위 10개 카테고리, 정렬하지 않음)
create_bar_chart(ax4, top_revenue_categories.index, category_counts[top_revenue_categories.index],
                 '상품 카테고리별 빈도수 (상위 10개)', '빈도수', 'skyblue')

ax2.yaxis.set_visible(False)
ax4.yaxis.set_visible(False)

for ax in [ax1, ax2, ax3, ax4]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 1. 'product_category_name'에 대한 빈도수 계산
category_counts = merged_df_product['product_category_name'].value_counts()
category_revenue = merged_df_product.groupby('product_category_name')['price'].sum()

# 2. 전체 매출액 및 전체 빈도수 계산
total_revenue = category_revenue.sum()
total_count = category_counts.sum()

# 3. 첫 번째 줄을 위한 상위 10개 카테고리 선택 및 정렬
top_categories_counts = category_counts.head(10).sort_values(ascending=True)
top_categories_revenue = category_revenue[top_categories_counts.index]

# 4. 상위 10개 카테고리의 매출 비중 및 빈도 비중 계산
top_revenue_percentage = (top_categories_revenue / total_revenue) * 100
top_count_percentage = (top_categories_counts / total_count) * 100

# 5. subplot 생성 (2x2)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))

# 6. 차트 생성 함수
def create_bar_chart(ax, x_data, y_data, title, xlabel, color):
    bars = ax.barh(x_data, y_data, color=color)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.grid(axis='x')
    for bar in bars:
        ax.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                f'{bar.get_width():,.1f}%', va='center', ha='left', fontsize=9)

# 7. 첫 번째 차트: 'product_category_name' 빈도수 비중 (상위 10개)
create_bar_chart(ax1, top_categories_counts.index, top_count_percentage,
                 '상품 카테고리별 빈도수 비중 (%) (상위 10개)', '빈도수 비중 (%)', 'skyblue')

# 8. 두 번째 차트: 상위 10개 카테고리의 매출 비중
create_bar_chart(ax2, top_categories_counts.index, top_revenue_percentage,
                 '상품 카테고리별 매출 비중 (%) (상위 10개)', '매출 비중 (%)', 'lightgreen')

# 9. 세 번째 차트: 전체 매출액 상위 10개의 상품 카테고리 비중
top_revenue_categories = category_revenue.nlargest(10).sort_values(ascending=True)
top_revenue_categories_percentage = (top_revenue_categories / total_revenue) * 100
create_bar_chart(ax3, top_revenue_categories.index, top_revenue_categories_percentage,
                 '총 매출액 상위 10개 상품 카테고리 비중 (%)', '매출 비중 (%)', 'orange')

# 10. 네 번째 차트: 빈도수 (상위 10개 카테고리, 비중)
top_revenue_categories_counts = category_counts[top_revenue_categories.index]
top_revenue_categories_counts_percentage = (top_revenue_categories_counts / total_count) * 100
create_bar_chart(ax4, top_revenue_categories.index, top_revenue_categories_counts_percentage,
                 '상품 카테고리별 빈도수 비중 (%) (상위 10개)', '빈도수 비중 (%)', 'skyblue')

ax2.yaxis.set_visible(False)
ax4.yaxis.set_visible(False)

for ax in [ax1, ax2, ax3, ax4]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 판매량과 매출액이 반드시 비례하는것은 아닌것으로 관찰됨
  • 특정 카테고리 매출비중이 높지않고 고르게 매출이 발생하고있으며, 건강과 미용 상품과 선물용 시계 상품이 각각 전체매출에 약 9% 비중을 차지하는 상위품목으로 관찰됨

5. Sellers (olist_sellers_dataset.csv)

컬럼 이름데이터 타입설명
seller_idVARCHAR판매자 고유 식별자
seller_zip_code_prefixINT판매자의 우편번호 앞부분
seller_cityVARCHAR판매자의 도시 정보
seller_stateVARCHAR판매자의 주 정보
1
2
df5 = pd.read_csv('/content/drive/MyDrive/archive/olist_sellers_dataset.csv')
df5
seller_idseller_zip_code_prefixseller_cityseller_state
03442f8959a84dea7ee197c632cb2df1513023campinasSP
1d1b65fc7debc3361ea86b5f14c68d2e213844mogi guacuSP
2ce3ad9de960102d0677a81f5d0bb7b2d20031rio de janeiroRJ
3c0f3eea2e14555b6faeea3dd58c1b1c34195sao pauloSP
451a04a8a6bdcb23deccc82b0b80742cf12914braganca paulistaSP
...............
309098dddbc4601dd4443ca174359b23716687111sarandiPR
3091f8201cab383e484733266d1906e2fdfa88137palhocaSC
309274871d19219c7d518d0090283e03c1374650sao pauloSP
3093e603cf3fec55f8697c9059638d6c8eb596080pelotasRS
30949e25199f6ef7e7c347120ff175652c3b12051taubateSP

3095 rows × 4 columns

1
2
# 결측치 확인
df5.isna().sum().sort_values(ascending=False)
0
seller_id0
seller_zip_code_prefix0
seller_city0
seller_state0


1
2
# 중복값 확인
df5['seller_id'].duplicated().sum()
1
0
  • sellers 데이터는 olist에서 판매중인 고객 정보를 담고있으며, 총 3,095명 고객이 판매를 하고있다
1
2
3
4
df3_new = df3.copy()
df3_new = df3_new.merge(df2, on='order_id', how='left')
df3_new = df3_new.merge(df, on='customer_id', how='left')
df3_new = df3_new.merge(df5, on='seller_id', how='left')
1
df3_new
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_valuecustomer_idorder_statusorder_purchase_timestamp...order_delivered_customer_dateorder_estimated_delivery_datedelivery_timecustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_stateseller_zip_code_prefixseller_cityseller_state
000010242fe8c5a6d1ba2dd792cb1621414244733e06e7ecb4970a6e2683c13e6148436dade18ac8b2bce089ec2a0412022017-09-19 09:45:3558.9013.293ce436f183e68e07877b285a838db11adelivered2017-09-13 08:59:02...2017-09-20 23:43:482017-09-29 00:00:007.0871766c5855e863f6eccc05f988b23cb28013campos dos goytacazesRJ27277volta redondaSP
100018f77f2f0320c557190d7a144bdd31e5f2d52b802189ee658865ca93d83a8fdd7ddc04e1b6c2c614352b383efe2d362017-05-03 11:05:13239.9019.93f6dd3ec061db4e3987629fe6b26e5ccedelivered2017-04-26 10:53:06...2017-05-12 16:04:242017-05-15 00:00:0016.0eb28e67c4c0b83846050ddfb8a35d05115775santa fe do sulSP3471sao pauloSP
2000229ec398224ef6ca0657da4fc703e1c777355d18b72b67abbeef9df44fd0fd5b51032eddd242adc84c38acab88f23d2018-01-18 14:48:30199.0017.876489ae5e4333f3693df5ad4372dab6d3delivered2018-01-14 14:33:31...2018-01-22 13:19:162018-02-05 00:00:007.03818d81c6709e39d06b2738a8d3a247435661para de minasMG37564borda da mataMG
300024acbcdf0a6daa1e931b038114c7517634da152a4610f1595efa32f14722fc9d7a1d34a5052409006425275ba1c2b42018-08-15 10:10:1812.9912.79d4eb9395c8c0431ee92fce09860c5a06delivered2018-08-08 10:00:35...2018-08-14 13:32:392018-08-20 00:00:006.0af861d436cfc08b2c2ddefd0ba07462212952atibaiaSP14403francaSP
400042b26cf59d7ce69dfabb4e55b4fd91ac6c3623068f30de03045865e4e10089df560393f3a51e74553ab94004ba5c872017-02-13 13:57:51199.9018.1458dbd0b2d70206bf40e62cd34e84d795delivered2017-02-04 13:57:51...2017-03-01 16:42:312017-03-17 00:00:0025.064b576fb70d441e8f1b2d7d446e483c513226varzea paulistaSP87900loandaPR
..................................................................
112645fffc94f6ce00a00581880bf54a75a03714aa6014eceb682077f9dc4bffebc05b0b8bc237ba3788b23da09c0f1f3a3288c2018-05-02 04:11:01299.9943.41b51593916b4b8e0d6f66f2ae24f2673ddelivered2018-04-23 13:57:06...2018-05-10 22:56:402018-05-18 00:00:0017.00c9aeda10a71f369396d0c04dce13a6465077sao luisMA88303itajaiSC
112646fffcd46ef2263f404302a634eb57f7eb132e07fd915822b0765e448c4dd74c828f3c38ab652836d21de61fb8314b691822018-07-20 04:31:48350.0036.5384c5d4fbaf120aae381fad077416eaa0delivered2018-07-14 10:26:46...2018-07-23 20:31:552018-08-01 00:00:009.00da9fe112eae0c74d3ba1fe16de0988b81690curitibaPR1206sao pauloSP
112647fffce4705a9662cd70adb13d4a31832d172a30483855e2eafc67aee5dc2560482c3cfdc648177fdbbbb35635a37472c532017-10-30 17:14:2599.9016.9529309aa813182aaddc9b259e31b870e6delivered2017-10-23 17:07:56...2017-10-28 12:22:222017-11-10 00:00:004.0cd79b407828f02fdbba457111c38e4c44039sao pauloSP80610curitibaPR
112648fffe18544ffabc95dfada21779c9644f19c422a519119dcad7575db5af1ba540e2b3e4a2a3ea8e01938cabda2a3e5cc792017-08-21 00:04:3255.998.72b5e6afd5a41800fdf401e0272ca74655delivered2017-08-14 23:02:59...2017-08-16 21:59:402017-08-25 00:00:001.0eb803377c9315b564bdedad67203930613289vinhedoSP4733sao pauloSP
112649fffe41c64501cc87c801fd61db3f62441350688d9dc1e75ff97be326363655e01f7ccf836d21b2fb1de37564105216cc12018-06-12 17:10:1343.0012.7996d649da0cc4ff33bb408b199d4c7dcfdelivered2018-06-09 17:00:18...2018-06-14 17:56:262018-06-28 00:00:005.0cd76a00d8e3ca5e6ab9ed9ecb6667ac418605botucatuSP14940ibitingaSP

112650 rows × 22 columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 1. customer_state와 seller_state의 빈도수 계산
customer_state_counts = df3_new['customer_state'].value_counts().head(10).sort_values(ascending=True)
seller_state_counts = df3_new['seller_state'].value_counts().head(10).sort_values(ascending=True)

# 2. subplot 생성 (1x2)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))

# 3. 첫 번째 차트: customer_state 빈도수
bars1 = ax1.barh(customer_state_counts.index, customer_state_counts.values, color='skyblue')
ax1.set_title('상위 10개 고객 주(State) 빈도수')
ax1.set_xlabel('빈도수')
ax1.grid(axis='x')

# 4. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=9)

# 5. 두 번째 차트: seller_state 빈도수
bars2 = ax2.barh(seller_state_counts.index, seller_state_counts.values, color='lightgreen')
ax2.set_title('상위 10개 판매자 주(State) 빈도수')
ax2.set_xlabel('빈도수')
ax2.grid(axis='x')

for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=9)

ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 대부분의 상품은 SP주에서 배송되고있으며, 구매 고객과 판매 고객의 위치 차이로 배송기간이 길어질 수 있음

6. Order Payments (olist_order_payments_dataset.csv)

컬럼 이름데이터 타입설명
order_idVARCHAR주문 고유 식별자
payment_sequentialINT각 주문의 결제 수단 순서
payment_typeVARCHAR결제 방식 (신용카드, 현금 등)
payment_installmentsINT할부 개월 수
payment_valueFLOAT결제 금액
1
2
df6 = pd.read_csv('/content/drive/MyDrive/archive/olist_order_payments_dataset.csv')
df6
order_idpayment_sequentialpayment_typepayment_installmentspayment_value
0b81ef226f3fe1789b1e8b2acac839d171credit_card899.33
1a9810da82917af2d9aefd1278f1dcfa01credit_card124.39
225e8ea4e93396b6fa0d3dd708e76c1bd1credit_card165.71
3ba78997921bbcdc1373bb41e913ab9531credit_card8107.78
442fdf880ba16b47b59251dd489d4441a1credit_card2128.45
..................
1038810406037ad97740d563a178ecc7a2075c1boleto1363.31
1038827b905861d7c825891d6347454ea7863f1credit_card296.80
10388332609bbb3dd69b3c066a6860554a77bf1credit_card147.77
103884b8b61059626efa996a60be9bb9320e101credit_card5369.54
10388528bbae6599b09d39ca406b747b6632b11boleto1191.58

103886 rows × 5 columns

1
2
# 결측치 확인
df6.isna().sum().sort_values(ascending=False)
0
order_id0
payment_sequential0
payment_type0
payment_installments0
payment_value0


1
2
# 중복값 확인
df6['order_id'].duplicated().sum()
1
4446
1
df6['payment_sequential'].value_counts()
count
payment_sequential
199360
23039
3581
4278
5170
6118
782
854
943
1034
1129
1221
1313
1410
158
186
196
166
176
214
204
223
262
242
232
252
291
281
271


1
df6['payment_installments'].value_counts()
count
payment_installments
152546
212413
310461
47098
105328
55239
84268
63920
71626
9644
12133
1574
1827
1123
2418
2017
1316
1415
178
165
213
02
221
231


1
round((df6[df6['payment_installments'] >= 6].shape[0] / df6.shape[0]) * 100, 2)
1
15.52
1
df6['payment_type'].value_counts()
count
payment_type
credit_card76795
boleto19784
voucher5775
debit_card1529
not_defined3


1
round((df6[df6['payment_type'] == 'boleto'].shape[0] / df6.shape[0])*100, 2)
1
19.04
  • 주문별 결제수단을 1번혹은 여러번 사용
  • order 데이터의 order_id보다 주문수가 적은것을 보아 취소나 변경 등 주문후 결제정보가 삭제된 데이터들이 존재함
  • 6개월 이상 장기 할부 거래가 15% 정도 발생하고 있음(boleto 거래 포함시 약 34%)

7. Order Reviews (olist_order_reviews_dataset.csv)

컬럼 이름데이터 타입설명
review_idVARCHAR리뷰 고유 식별자
order_idVARCHAR리뷰와 연결된 주문 고유 식별자
review_scoreINT고객이 남긴 평점
review_comment_titleVARCHAR리뷰 제목
review_comment_messageTEXT리뷰 내용
review_creation_dateTIMESTAMP리뷰가 생성된 날짜
review_answer_timestampTIMESTAMP리뷰에 답변한 시각
1
2
df7 = pd.read_csv('/content/drive/MyDrive/archive/olist_order_reviews_dataset.csv')
df7
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
07bc2406110b926393aa56f80a40eba4073fc7af87114b39712e6da79b0a377eb4NaNNaN2018-01-18 00:00:002018-01-18 21:46:59
180e641a11e56f04c1ad469d5645fdfdea548910a1c6147796b98fdf73dbeba335NaNNaN2018-03-10 00:00:002018-03-11 03:05:13
2228ce5500dc1d8e020d8d1322874b6f0f9e4b658b201a9f2ecdecbb34bed034b5NaNNaN2018-02-17 00:00:002018-02-18 14:36:24
3e64fb393e7b32834bb789ff8bb30750e658677c97b385a9be170737859d3511b5NaNRecebi bem antes do prazo estipulado.2017-04-21 00:00:002017-04-21 22:02:06
4f7c4243c7fe1938f181bec41a392bdeb8e6bfb81e283fa7e4f11123a3fb894f15NaNParabéns lojas lannister adorei comprar pela I...2018-03-01 00:00:002018-03-02 10:26:53
........................
99219574ed12dd733e5fa530cfd4bbf39d7c92a8c23fee101d4d5662fa670396eb8da5NaNNaN2018-07-07 00:00:002018-07-14 17:18:30
99220f3897127253a9592a73be9bdfdf4ed7a22ec9f0669f784db00fa86d035cf86025NaNNaN2017-12-09 00:00:002017-12-11 20:06:42
99221b3de70c89b1510c4cd3d0649fd30247255d4004744368f5571d1f590031933e45NaNExcelente mochila, entrega super rápida. Super...2018-03-22 00:00:002018-03-23 09:10:43
992221adeb9d84d72fe4e337617733eb851497725825d039fc1f0ceb7635e3f7d92064NaNNaN2018-07-01 00:00:002018-07-02 12:59:13
99223efe49f1d6f951dd88b51e6ccd4cc548f90531360ecb1eec2a1fbb265a0db05081NaNmeu produto chegou e ja tenho que devolver, po...2017-07-03 00:00:002017-07-03 21:01:49

99224 rows × 7 columns

1
2
# 결측치 확인
df7.isna().sum().sort_values(ascending=False)
0
review_comment_title87656
review_comment_message58247
review_id0
order_id0
review_score0
review_creation_date0
review_answer_timestamp0


  • 리뷰를 안적는사람이 있기때문에 결측치가 아님
1
2
3
# 중복값 확인
print(df7['order_id'].duplicated().sum())
print(df7['review_id'].duplicated().sum())
1
2
551
814
1
df7[df7['order_id'].duplicated(keep=False)].sort_values(by=['order_id'])
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
2561289a02c45c340aeeb1354a24e7d4b2c1e0035246a40f520710769010f752e75075NaNNaN2017-08-29 00:00:002017-08-30 01:59:12
224232a74b0559eb58fc1ff842ecc999594cb0035246a40f520710769010f752e75075NaNEstou acostumada a comprar produtos pelo barat...2017-08-25 00:00:002017-08-29 21:45:57
22779ab30810c29da5da8045216f0f62652a2013056cfe49763c6f66bda03396c5ee35NaNNaN2018-02-22 00:00:002018-02-23 12:12:30
6863373413b847f63e02bc752b364f6d05ee9013056cfe49763c6f66bda03396c5ee34NaNNaN2018-03-04 00:00:002018-03-05 17:02:00
854830636803620cdf8b6ffaf1b2f6e92b20176a6846bcb3b0d3aa3116a9a7685975NaNNaN2017-12-30 00:00:002018-01-02 10:54:06
........................
274655e78482ee783451be6026e5cf0c72de1ff763b73e473d03c321bcd5a053316e83NaNNão sei que haverá acontecido os demais chegaram2017-11-18 00:00:002017-11-18 09:02:48
4135539de8ad3a1a494fc68cc2d5382f052f4ff850ba359507b996e8b2fbb26df8d035NaNEnvio rapido... Produto 100%2017-08-16 00:00:002017-08-17 11:56:55
1878380f25f32c00540d49d57796fb6658535ff850ba359507b996e8b2fbb26df8d035NaNEnvio rapido, produto conforme descrito no anu...2017-08-22 00:00:002017-08-25 11:40:22
92230870d856a4873d3a67252b0c51d79b950ffaabba06c9d293a3c614e0515ddbabc3NaNNaN2017-12-20 00:00:002017-12-20 18:50:16
539625476dd0eaee7c4e2725cafb011aa758cffaabba06c9d293a3c614e0515ddbabc3NaNNaN2017-12-20 00:00:002017-12-21 13:24:55

1098 rows × 7 columns

1
df7[df7['order_id'] == 'ff850ba359507b996e8b2fbb26df8d03']
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
1878380f25f32c00540d49d57796fb6658535ff850ba359507b996e8b2fbb26df8d035NaNEnvio rapido, produto conforme descrito no anu...2017-08-22 00:00:002017-08-25 11:40:22
4135539de8ad3a1a494fc68cc2d5382f052f4ff850ba359507b996e8b2fbb26df8d035NaNEnvio rapido... Produto 100%2017-08-16 00:00:002017-08-17 11:56:55
1
df3[df3['order_id'] == 'ff850ba359507b996e8b2fbb26df8d03']
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
112446ff850ba359507b996e8b2fbb26df8d031d2bea3c01e172037caa99b2d138f39d09674754b5a0cb32b638cec001178f7992017-08-10 20:20:0716.916.11
1
df6[df6['order_id'] == 'ff850ba359507b996e8b2fbb26df8d03']
order_idpayment_sequentialpayment_typepayment_installmentspayment_value
8576ff850ba359507b996e8b2fbb26df8d031credit_card333.01
1
df2[df2['order_id'] == 'ff850ba359507b996e8b2fbb26df8d03']
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
68231ff850ba359507b996e8b2fbb26df8d03219399e5496f8ca1dc6f68753131c084delivered2017-08-06 19:38:002017-08-06 20:20:072017-08-08 12:23:162017-08-21 22:15:412017-08-31 00:00:0015.0
  • 해당주문번호로 리뷰를 2번썼는데, 첫번째는 도착하기도전에 리뷰가 적혔다
1
df7.groupby('order_id').filter(lambda x: len(x) >= 3)
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
1985ffb8cff872a625632ac983eb1f88843cc88b1d1b157a9999ce368f218a4071413NaNNaN2017-07-22 00:00:002017-07-26 13:41:07
2952c444278834184f72b1484dfe47de7f97df56136b8031ecd28e200bb18e6ddb2e5NaNNaN2017-02-08 00:00:002017-02-14 13:58:48
8273b04ed893318da5b863e878cd3d0511df03c939fd7fd3b38f8485a0f95798f1f63NaNUm ponto negativo que achei foi a cobrança de ...2018-03-20 00:00:002018-03-21 02:28:23
1398272a1098d5b410ae50fbc0509d26daeb9df56136b8031ecd28e200bb18e6ddb2e5NaNNaN2017-02-07 00:00:002017-02-10 10:46:09
4469467c2557eb0bd72e3ece1e03477c9dff58e17072ec97ce29f0e1f111e598b0c851NaNEntregou o produto errado.2018-04-07 00:00:002018-04-08 22:48:27
51527f4bb9d6dd4fb6dcc2298f0e7b17b8e1e03c939fd7fd3b38f8485a0f95798f1f64NaNNaN2018-03-29 00:00:002018-03-30 00:29:09
6272844f3e54834d23c5570c1d010824d4d59df56136b8031ecd28e200bb18e6ddb2e5NaNNaN2017-02-09 00:00:002017-02-09 09:07:28
645102d6ac45f859465b5c185274a1c9296378e17072ec97ce29f0e1f111e598b0c851NaNComprei 3 unidades do produto vieram 2 unidade...2018-04-07 00:00:002018-04-07 21:13:05
69438405eb2ea45e1dbe2662541ae5b47e2aa03c939fd7fd3b38f8485a0f95798f1f63NaNSeria ótimo se tivesem entregue os 3 (três) pe...2018-03-06 00:00:002018-03-06 19:50:32
82525202b5f44d09cd3cfc0d6bd12f01b044cc88b1d1b157a9999ce368f218a4071415NaNNaN2017-07-22 00:00:002017-07-26 13:40:22
89360fb96ea2ef8cce1c888f4d45c8e22b793c88b1d1b157a9999ce368f218a4071415NaNNaN2017-07-21 00:00:002017-07-26 13:45:15
923006e4c4086d9611ae4cc0cc65a262751fe8e17072ec97ce29f0e1f111e598b0c851NaNEmbora tenha entregue dentro do prazo, não env...2018-04-14 00:00:002018-04-16 11:37:31
1
df7[df7['order_id'] == 'c88b1d1b157a9999ce368f218a407141']
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
1985ffb8cff872a625632ac983eb1f88843cc88b1d1b157a9999ce368f218a4071413NaNNaN2017-07-22 00:00:002017-07-26 13:41:07
82525202b5f44d09cd3cfc0d6bd12f01b044cc88b1d1b157a9999ce368f218a4071415NaNNaN2017-07-22 00:00:002017-07-26 13:40:22
89360fb96ea2ef8cce1c888f4d45c8e22b793c88b1d1b157a9999ce368f218a4071415NaNNaN2017-07-21 00:00:002017-07-26 13:45:15
1
df3[df3['order_id'] == 'c88b1d1b157a9999ce368f218a407141']
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_value
88316c88b1d1b157a9999ce368f218a4071411b1acb7e8152c90c9619897753a75c973cc419e0650a3c5ba77189a1882b7556a2017-07-26 22:50:1234.997.78
1
df6[df6['order_id'] == 'c88b1d1b157a9999ce368f218a407141']
order_idpayment_sequentialpayment_typepayment_installmentspayment_value
103471c88b1d1b157a9999ce368f218a4071411credit_card442.77
1
df7[df7['review_id'].duplicated(keep=False)].sort_values(by=['review_id']).head(30)
review_idorder_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
4667800130cbe1f9d422698c812ed8ded1919dfcdfc43867d1c1381bfaf62d6b9c1951NaNO cartucho "original HP" 60XL não é reconhecid...2018-03-07 00:00:002018-03-20 18:08:23
2984100130cbe1f9d422698c812ed8ded191904a28263e085d399c97ae49e0b477efa1NaNO cartucho "original HP" 60XL não é reconhecid...2018-03-07 00:00:002018-03-20 18:08:23
906770115633a9c298b6a98bcbe4eee75345f78a4201f58af3463bdab842eea4bc8015NaNNaN2017-09-21 00:00:002017-09-26 03:27:47
631930115633a9c298b6a98bcbe4eee75345f0c9850b2c179c1ef60d2855e2751d1fa5NaNNaN2017-09-21 00:00:002017-09-26 03:27:47
928760174caf0ee5964646040cd94e15ac95ef93a732712407c02dce5dd5088d0f47b1NaNProduto entregue dentro de embalagem do fornec...2018-03-07 00:00:002018-03-08 03:00:53
572800174caf0ee5964646040cd94e15ac95e74db91e33b4e1fd865356c89a61abf1f1NaNProduto entregue dentro de embalagem do fornec...2018-03-07 00:00:002018-03-08 03:00:53
54832017808d29fd1f942d97e50184dfb4c138daaa9e99d60fbba579cc1c3e3bfae015NaNNaN2018-03-02 00:00:002018-03-05 01:43:30
99167017808d29fd1f942d97e50184dfb4c13b1461c8882153b5fe68307c46a506e395NaNNaN2018-03-02 00:00:002018-03-05 01:43:30
206210254bd905dc677a6078990aad3331a365bf226cf882c5bf4247f89a97c86f2731NaNO pedido consta de 2 produtos e até agora rece...2017-09-09 00:00:002017-09-13 09:52:44
960800254bd905dc677a6078990aad3331a36331b367bdd766f3d1cf518777317b5d91NaNO pedido consta de 2 produtos e até agora rece...2017-09-09 00:00:002017-09-13 09:52:44
897120288d42bef3dfe36930740c9588a570f33d8795f04dd631f3480d7aaf90da3dc3NaNNaN2017-07-29 00:00:002017-07-31 17:11:04
948510288d42bef3dfe36930740c9588a570ff889a5a0b44adc29c5465b99395ac3c13NaNNaN2017-07-29 00:00:002017-07-31 17:11:04
4749502aa7f5f75e964e3c7efa59a1f515281d8e17d5f7dacf0970d316e7c03e741e83NaNNaN2017-07-27 00:00:002017-07-29 15:09:50
9586002aa7f5f75e964e3c7efa59a1f515281db92613c074f00e53066388d48ad75123NaNNaN2017-07-27 00:00:002017-07-29 15:09:50
71165034528cb00b6bc981847acafbdf2ae0be1e8e3bca903de27e9a1c72b5a5795e01NaNNaN2017-05-14 00:00:002017-05-15 02:17:38
81094034528cb00b6bc981847acafbdf2ae0b3d3742a96f24a8fe4e2e57628807e4761NaNNaN2017-05-14 00:00:002017-05-15 02:17:38
198803a6a25db577d06894409330551118972acfdc5131ff2cf4433e668454c9784c5NaNMuito Bom! Gostei Bastante! Tecido Ótimo! Aten...2017-12-15 00:00:002017-12-16 01:32:18
901303a6a25db577d06894409330551118973fde8b7313af6b37b84b5c7594d7add05NaNMuito Bom! Gostei Bastante! Tecido Ótimo! Aten...2017-12-15 00:00:002017-12-16 01:32:18
215870467560f511c516ddaa54a60edb0c2910c995611a99f81268d859184a416f1db5NaNNaN2017-02-09 00:00:002017-02-14 15:58:44
249010467560f511c516ddaa54a60edb0c29155b2e390d5d80ada31ad1b795ebeb0875NaNNaN2017-02-09 00:00:002017-02-14 15:58:44
52021047fd109ced39e02296f6aeb74f6a6f1236e6ec6171c1870d4bcf4ccfad87f494NaNNaN2018-07-31 00:00:002018-08-02 15:11:49
13872047fd109ced39e02296f6aeb74f6a6f1a89abace0dcc01eeb267a9660b5ac1264NaNNaN2018-07-31 00:00:002018-08-02 15:11:49
966810498e9722f8757426c3c3ee0b91e666df318811b0fd898d1edf78d6841470be24NaNChego certo adoro comprar na lannister.com.br\...2018-03-03 00:00:002018-03-06 01:00:33
774440498e9722f8757426c3c3ee0b91e666d041aa5c38550649d5b51f38ba03a29a44NaNChego certo adoro comprar na lannister.com.br\...2018-03-03 00:00:002018-03-06 01:00:33
76290501aab2f381486c36bf0f071442c0c20068c109948b9a1dfb8530d1978acef31NaNEspero obter uma resposta para minha encomenda...2018-02-09 00:00:002018-02-10 23:55:18
669520501aab2f381486c36bf0f071442c0c2d75cb3755738c4ae466303358f97bc551NaNEspero obter uma resposta para minha encomenda...2018-02-09 00:00:002018-02-10 23:55:18
586210546d398a833d4c33dec480bedeecfbde72a8568c8622825a95439791f668e855NaNNaN2017-10-05 00:00:002017-10-24 16:16:33
928420546d398a833d4c33dec480bedeecfbd879d57dc015759bf30e71a20b5ae06525NaNNaN2017-10-05 00:00:002017-10-24 16:16:33
505170655af56f10bc3ef8e3810610828f294d9e44c3fd2ce16086619f299e92e12d85NaNMuito rápido a entrega2017-05-18 00:00:002017-05-19 19:15:22
239870655af56f10bc3ef8e3810610828f29484f5e6c0a0e3155e38c00f434ba90ce85NaNMuito rápido a entrega2017-05-18 00:00:002017-05-19 19:15:22
1
df2[df2['order_id'] == 'dfcdfc43867d1c1381bfaf62d6b9c195']
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
74048dfcdfc43867d1c1381bfaf62d6b9c195a7026133ddbd2e86c83ecd4dfa4dbe01delivered2018-02-02 18:01:082018-02-02 18:31:172018-02-05 23:46:292018-02-26 18:18:452018-03-09 00:00:0024.0
1
df[df['customer_id'] == 'a7026133ddbd2e86c83ecd4dfa4dbe01']
customer_idcustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_state
60938a7026133ddbd2e86c83ecd4dfa4dbe01f30856ad31d3e74253a3f4ccef67064871955brasiliaDF
1
df2[df2['order_id'] == '04a28263e085d399c97ae49e0b477efa']
order_idcustomer_idorder_statusorder_purchase_timestamporder_approved_atorder_delivered_carrier_dateorder_delivered_customer_dateorder_estimated_delivery_datedelivery_time
2824104a28263e085d399c97ae49e0b477efafef2e5e63da9f3e1dd89e8e319468657delivered2018-02-02 18:01:102018-02-02 18:31:272018-02-21 02:38:292018-03-11 11:32:242018-03-05 00:00:0036.0
1
df[df['customer_id'] == 'fef2e5e63da9f3e1dd89e8e319468657']
customer_idcustomer_unique_idcustomer_zip_code_prefixcustomer_citycustomer_state
62395fef2e5e63da9f3e1dd89e8e319468657f30856ad31d3e74253a3f4ccef67064871955brasiliaDF
  • 주문번호 중복 : 오류거나, 배송이 변경(교환 등)되거나, 추가되거나 등 여러상황이있고 상품이 도착하기도전에 리뷰가있는 수상한 부분도있지만 같은주문번호에 각각 다른 리뷰메세지가 존재해 데이터를 제거하지않음
  • 리뷰아이디 중복 : 사용자가 구매한 주문들을 한번에 같이 리뷰했을때 발생함
1
df7['review_score'].value_counts()
count
review_score
557328
419142
111424
38179
23151


1
df3_new = df3_new.merge(df7, on='order_id', how='left')
1
df3_new
order_idorder_item_idproduct_idseller_idshipping_limit_datepricefreight_valuecustomer_idorder_statusorder_purchase_timestamp...customer_stateseller_zip_code_prefixseller_cityseller_statereview_idreview_scorereview_comment_titlereview_comment_messagereview_creation_datereview_answer_timestamp
000010242fe8c5a6d1ba2dd792cb1621414244733e06e7ecb4970a6e2683c13e6148436dade18ac8b2bce089ec2a0412022017-09-19 09:45:3558.9013.293ce436f183e68e07877b285a838db11adelivered2017-09-13 08:59:02...RJ27277volta redondaSP97ca439bc427b48bc1cd7177abe713655.0NaNPerfeito, produto entregue antes do combinado.2017-09-21 00:00:002017-09-22 10:57:03
100018f77f2f0320c557190d7a144bdd31e5f2d52b802189ee658865ca93d83a8fdd7ddc04e1b6c2c614352b383efe2d362017-05-03 11:05:13239.9019.93f6dd3ec061db4e3987629fe6b26e5ccedelivered2017-04-26 10:53:06...SP3471sao pauloSP7b07bacd811c4117b742569b04ce35804.0NaNNaN2017-05-13 00:00:002017-05-15 11:34:13
2000229ec398224ef6ca0657da4fc703e1c777355d18b72b67abbeef9df44fd0fd5b51032eddd242adc84c38acab88f23d2018-01-18 14:48:30199.0017.876489ae5e4333f3693df5ad4372dab6d3delivered2018-01-14 14:33:31...MG37564borda da mataMG0c5b33dea94867d1ac402749e5438e8b5.0NaNChegou antes do prazo previsto e o produto sur...2018-01-23 00:00:002018-01-23 16:06:31
300024acbcdf0a6daa1e931b038114c7517634da152a4610f1595efa32f14722fc9d7a1d34a5052409006425275ba1c2b42018-08-15 10:10:1812.9912.79d4eb9395c8c0431ee92fce09860c5a06delivered2018-08-08 10:00:35...SP14403francaSPf4028d019cb58564807486a6aaf338174.0NaNNaN2018-08-15 00:00:002018-08-15 16:39:01
400042b26cf59d7ce69dfabb4e55b4fd91ac6c3623068f30de03045865e4e10089df560393f3a51e74553ab94004ba5c872017-02-13 13:57:51199.9018.1458dbd0b2d70206bf40e62cd34e84d795delivered2017-02-04 13:57:51...SP87900loandaPR940144190dcba6351888cafa43f3a3a55.0NaNGostei pois veio no prazo determinado .2017-03-02 00:00:002017-03-03 10:54:59
..................................................................
113309fffc94f6ce00a00581880bf54a75a03714aa6014eceb682077f9dc4bffebc05b0b8bc237ba3788b23da09c0f1f3a3288c2018-05-02 04:11:01299.9943.41b51593916b4b8e0d6f66f2ae24f2673ddelivered2018-04-23 13:57:06...MA88303itajaiSC9185f849f32d82e216a4e025e0c50f5c5.0NaNNaN2018-05-11 00:00:002018-05-14 12:53:47
113310fffcd46ef2263f404302a634eb57f7eb132e07fd915822b0765e448c4dd74c828f3c38ab652836d21de61fb8314b691822018-07-20 04:31:48350.0036.5384c5d4fbaf120aae381fad077416eaa0delivered2018-07-14 10:26:46...PR1206sao pauloSPbe803f6a93d64719fd685c1cc610918a5.0NaNNaN2018-07-24 00:00:002018-07-25 09:25:29
113311fffce4705a9662cd70adb13d4a31832d172a30483855e2eafc67aee5dc2560482c3cfdc648177fdbbbb35635a37472c532017-10-30 17:14:2599.9016.9529309aa813182aaddc9b259e31b870e6delivered2017-10-23 17:07:56...SP80610curitibaPRdbdd81cd59a1a9f94a10a990b4d48dce5.0NaNNaN2017-10-29 00:00:002017-10-29 21:33:52
113312fffe18544ffabc95dfada21779c9644f19c422a519119dcad7575db5af1ba540e2b3e4a2a3ea8e01938cabda2a3e5cc792017-08-21 00:04:3255.998.72b5e6afd5a41800fdf401e0272ca74655delivered2017-08-14 23:02:59...SP4733sao pauloSPfba117c9ac40d41ca7be54741f4713035.0NaNRecebi o produto antes do prazo e corretamente.2017-08-17 00:00:002017-08-18 12:24:05
113313fffe41c64501cc87c801fd61db3f62441350688d9dc1e75ff97be326363655e01f7ccf836d21b2fb1de37564105216cc12018-06-12 17:10:1343.0012.7996d649da0cc4ff33bb408b199d4c7dcfdelivered2018-06-09 17:00:18...SP14940ibitingaSPb2700869a37f1aafc9dda829dc2f90275.0NaNNaN2018-06-15 00:00:002018-06-17 21:27:09

113314 rows × 28 columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 1. 상위 10개 고객 주(state) 빈도수 계산
customer_state_counts = df3_new['customer_state'].value_counts().head(10)

top_states = customer_state_counts.index
average_review_scores = df3_new[df3_new['customer_state'].isin(top_states)].groupby('customer_state')['review_score'].mean()

# 2. 평균 평점이 가장 낮은 10개 고객 주(state) 선택
average_review_scores_all = df3_new.groupby('customer_state')['review_score'].mean()
bottom_states = average_review_scores_all.nsmallest(10)

# 3. 하위 10개 고객 주의 빈도수 계산
bottom_states_counts = df3_new['customer_state'].value_counts().loc[bottom_states.index]

# 4. subplot 생성
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 12))

# 5. 첫 번째 차트: 상위 10개 고객 주(state) 빈도수
sorted_counts = customer_state_counts.sort_values(ascending=True)  # 빈도수를 오름차순 정렬
bars1 = ax1.barh(sorted_counts.index, sorted_counts.values, color='skyblue')
ax1.set_title('상위 10개 고객 주(State) 빈도수')
ax1.set_xlabel('빈도수')
ax1.grid(axis='x')

# 6. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 7. 두 번째 차트: 상위 10개 고객 주(state) 평균 평점
bars2 = ax2.barh(sorted_counts.index, average_review_scores[sorted_counts.index], color='lightgreen')
ax2.set_title('상위 10개 고객 주(State) 평균 평점')
ax2.set_xlabel('평균 평점')
ax2.grid(axis='x')

# 8. 숫자 표기
for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():.2f}', va='center', ha='left', fontsize=10)

# 9. 네 번째 차트: 하위 10개 고객 주(state) 빈도수
bars4 = ax4.barh(bottom_states_counts.index, bottom_states_counts.values, color='salmon')
ax4.set_title('하위 10개 고객 주(State) 빈도수')
ax4.set_xlabel('빈도수')
ax4.grid(axis='x')

# 10. 숫자 표기
for bar in bars4:
    ax4.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 11. 세 번째 차트: 하위 10개 고객 주(state) 평균 평점
bars3 = ax3.barh(bottom_states.index, bottom_states.values, color='lightcoral')
ax3.set_title('하위 10개 고객 주(State) 평균 평점')
ax3.set_xlabel('평균 평점')
ax3.grid(axis='x')

for bar in bars3:
    ax3.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():.2f}', va='center', ha='left', fontsize=10)

for ax in [ax1, ax2, ax3, ax4]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 고객의 거주 주 별 평균 평점은 3.5 이상으로 나타나며, RJ주는 고객 비중이 높은 주이면서도 평균 평점이 낮은편에 속함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
low_score_counts = df3_new[df3_new['review_score'].isin([1, 2])].groupby('customer_state')['review_score'].count()

top_low_score_states = low_score_counts.nlargest(10).sort_values(ascending=True)

plt.figure(figsize=(10, 6))
bars = plt.barh(top_low_score_states.index, top_low_score_states.values, color='lightcoral')
plt.title('리뷰점수가 1점 또는 2점이 많은 상위 10개 고객 주(State)')
plt.xlabel('빈도수')
plt.grid(axis='x')

for bar in bars:
    plt.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

plt.tight_layout()
plt.show()

png

  • 거래가 많이되는 곳에 낮은 점수도 많이 분포되고 있음을 알수있다
1
df3_new = df3_new.merge(df4, on='product_id', how='left')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1. 1점과 2점인 경우의 수를 계산
low_score_counts = df3_new[df3_new['review_score'].isin([1, 2])].groupby('product_category_name')['review_score'].count()

# 2. 상위 10개 품목 선택
top_low_score_categories = low_score_counts.nlargest(10).sort_values(ascending=True)

# 3. 5점인 경우의 수를 계산
high_score_counts = df3_new[df3_new['review_score'] == 5].groupby('product_category_name')['review_score'].count()

# 4. 상위 10개 품목 선택
top_high_score_categories = high_score_counts.nlargest(10).sort_values(ascending=True)

# 5. subplot 생성
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))

# 6. 첫 번째 차트: 1점 및 2점이 많은 상위 10개 품목
bars1 = ax1.barh(top_low_score_categories.index, top_low_score_categories.values, color='lightcoral')
ax1.set_title('1점 및 2점이 많은 상위 10개 품목')
ax1.set_xlabel('빈도수')
ax1.grid(axis='x')

# 7. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 8. 두 번째 차트: 5점이 많은 상위 10개 품목
bars2 = ax2.barh(top_high_score_categories.index, top_high_score_categories.values, color='lightgreen')
ax2.set_title('5점이 많은 상위 10개 품목')
ax2.set_xlabel('빈도수')
ax2.grid(axis='x')

for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

for ax in [ax1, ax2]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 평점이 높은 카테고리는 낮은 평점도 공존하고있음

현재 주어진 데이터 셋으로 어떤 비즈니스 지표들을 만들 수 있는지 제시하고, 해당 지표들로 AARRR 프레임워크를 구성할 수 있는지 판단하기

Acquisition (획득): 사용자를 획득하는 단계

  • 신규 고객 수 : customer_unique_id 활용 신규 고객 수 계산
  • 지역별 신규 고객 분포 : customer_citycustomer_state 활용 지역별 신규 고객 수 계산
1
2
new_customers = df3_new['customer_unique_id'].nunique()
print("신규 고객 수:", new_customers)
1
신규 고객 수: 95420
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 1. 도시별 신규 고객 수 계산
city_new_customers = df3_new.groupby('customer_city')['customer_unique_id'].nunique()

# 2. 주별 신규 고객 수 계산
state_new_customers = df3_new.groupby('customer_state')['customer_unique_id'].nunique()

# 3. 상위 10개 및 하위 10개 고객 수 선택
top_cities = city_new_customers.nlargest(10).sort_values(ascending=True)
bottom_cities = city_new_customers.nsmallest(10).sort_values(ascending=True)

top_states = state_new_customers.nlargest(10).sort_values(ascending=True)
bottom_states = state_new_customers.nsmallest(10).sort_values(ascending=True)

# 4. subplot 생성
fig, axs = plt.subplots(2, 2, figsize=(14, 12))

# 5. 첫 번째 차트: 상위 10개 도시
bars1 = axs[0, 0].barh(top_cities.index, top_cities.values, color='skyblue')
axs[0, 0].set_title('상위 10개 도시 신규 고객 분포')
axs[0, 0].set_xlabel('신규 고객 수')
axs[0, 0].grid(axis='x')

# 6. 오른쪽과 위쪽 테두리 제거
axs[0, 0].spines['top'].set_visible(False)
axs[0, 0].spines['right'].set_visible(False)

# 7. 숫자 표기
for bar in bars1:
    axs[0, 0].text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                   f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 8. 두 번째 차트: 하위 10개 도시
bars2 = axs[0, 1].barh(bottom_cities.index, bottom_cities.values, color='lightcoral')
axs[0, 1].set_title('하위 10개 도시 신규 고객 분포')
axs[0, 1].set_xlabel('신규 고객 수')
axs[0, 1].grid(axis='x')

# 9. 오른쪽과 위쪽 테두리 제거
axs[0, 1].spines['top'].set_visible(False)
axs[0, 1].spines['right'].set_visible(False)

# 10. 숫자 표기
for bar in bars2:
    axs[0, 1].text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                   f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 11. 세 번째 차트: 상위 10개 주
bars3 = axs[1, 0].barh(top_states.index, top_states.values, color='lightgreen')
axs[1, 0].set_title('상위 10개 주 신규 고객 분포')
axs[1, 0].set_xlabel('신규 고객 수')
axs[1, 0].grid(axis='x')

# 12. 오른쪽과 위쪽 테두리 제거
axs[1, 0].spines['top'].set_visible(False)
axs[1, 0].spines['right'].set_visible(False)

# 13. 숫자 표기
for bar in bars3:
    axs[1, 0].text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                   f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 14. 네 번째 차트: 하위 10개 주
bars4 = axs[1, 1].barh(bottom_states.index, bottom_states.values, color='lightblue')
axs[1, 1].set_title('하위 10개 주 신규 고객 분포')
axs[1, 1].set_xlabel('신규 고객 수')
axs[1, 1].grid(axis='x')

# 15. 오른쪽과 위쪽 테두리 제거
axs[1, 1].spines['top'].set_visible(False)
axs[1, 1].spines['right'].set_visible(False)

# 16. 숫자 표기
for bar in bars4:
    axs[1, 1].text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                   f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

plt.tight_layout()
plt.show()

png

1
state_new_customers.nlargest(3).sum() / new_customers * 100
1
66.50806958708866
1
state_new_customers.nlargest(5).sum() / new_customers * 100
1
77.08132466988053
1
state_new_customers.nlargest(7).sum() / new_customers * 100
1
84.1762733179627
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 1. customer_state와 seller_state의 빈도수 계산
customer_state_counts = df3_new['customer_state'].value_counts().head(10).sort_values(ascending=True)
seller_state_counts = df3_new['seller_state'].value_counts().head(10).sort_values(ascending=True)

# 2. subplot 생성 (1x2)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))

# 3. 첫 번째 차트: customer_state 빈도수
bars1 = ax1.barh(customer_state_counts.index, customer_state_counts.values, color='skyblue')
ax1.set_title('상위 10개 고객 주(State) 빈도수')
ax1.set_xlabel('빈도수')
ax1.grid(axis='x')

# 4. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=9)

# 5. 두 번째 차트: seller_state 빈도수
bars2 = ax2.barh(seller_state_counts.index, seller_state_counts.values, color='lightgreen')
ax2.set_title('상위 10개 판매자 주(State) 빈도수')
ax2.set_xlabel('빈도수')
ax2.grid(axis='x')

for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=9)

ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 전체 고객 중 약 84%가 7개 주(SP, RJ, MG, RS, PR, SC, BA)에서 서비스를 이용하고있다. 판매자도 대부분 7개 주에서 판매를 하고있는것으로 관찰된다.
  • 배송비와 타지역 신규유입에 들어가는 시간과 비용을 고려했을때, 전국단위보다는 고객이 모여있는 주를 중심으로 시장을 운영하는것이 효과적으로 보인다

Activation (활성화): 사용자가 제품/서비스를 처음 사용하는 단계

  • 첫 구매 완료율 : order_status가 delivered인 주문 건수를 전체 주문 건수로 나눠서 계산
  • 리드 타임 : order_purchase_timestamporder_delivered_customer_date 컬럼을 이용하여 첫 주문까지의 리드 타임 계산
1
2
3
4
5
6
# 활성화 단계(Activation)
first_purchase_completion_rate = df3_new[df3_new['order_status'] == 'delivered'].shape[0] / df3_new.shape[0]
lead_time = df3_new['delivery_time'].mean()

print("\n첫 구매 완료율:", round(first_purchase_completion_rate * 100, 2))
print("\n리드 타임:", round(lead_time,2))
1
2
3
첫 구매 완료율: 97.82

리드 타임: 12.01
  • 데이터 수집기간동안 98% 고객이 구매를 완료하였고, 리드 타임은 평균 12일로 준수함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. 주별 신규 고객 수 계산
state_new_customers = df3_new.groupby('customer_state')['customer_unique_id'].nunique()

# 2. 상위 10개 고객 주 선택
top_states = state_new_customers.nlargest(10).sort_values(ascending=True).index

# 3. 해당 주들의 평균 배송 시간 계산
average_delivery_time = df3_new[df3_new['customer_state'].isin(top_states)].groupby('customer_state')['delivery_time'].mean()

# 4. 평균 배송 시간을 빈도수 기준으로 정렬
average_delivery_time = average_delivery_time.reindex(top_states)

plt.figure(figsize=(10, 6))
bars = plt.barh(average_delivery_time.index, average_delivery_time.values, color='lightblue')
plt.title('상위 10개 고객 주의 평균 배송 시간')
plt.xlabel('평균 배송 시간')
plt.grid(axis='x')

for bar in bars:
    plt.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():.2f}', va='center', ha='left', fontsize=10)

plt.tight_layout()
plt.show()

png

  • 국가 특성을 자세히는 모르지만, 상위 10개 고객 주의 리드타임은 대체로 14일 이내로 짧은편이다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1. 'review_score'가 1점 또는 2점인 데이터 필터링
filtered_data = df3_new[df3_new['review_score'].isin([1, 2])]

# 2. 주별 신규 고객 수 계산
state_new_customers = filtered_data.groupby('customer_state')['customer_unique_id'].nunique()

# 3. 상위 10개 고객 주 선택
top_states = state_new_customers.nlargest(10).sort_values(ascending=True).index

# 4. 해당 주들의 평균 배송 시간 계산
average_delivery_time = filtered_data[filtered_data['customer_state'].isin(top_states)].groupby('customer_state')['delivery_time'].mean()

# 5. 평균 배송 시간을 빈도수 기준으로 정렬
average_delivery_time = average_delivery_time.reindex(top_states)

plt.figure(figsize=(10, 6))
bars = plt.barh(average_delivery_time.index, average_delivery_time.values, color='lightblue')
plt.title('상위 10개 고객 주의 평균 배송 시간 (리뷰 점수 1점 및 2점)')
plt.xlabel('평균 배송 시간')
plt.grid(axis='x')

for bar in bars:
    plt.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():.2f}', va='center', ha='left', fontsize=10)

plt.tight_layout()
plt.show()

png

  • 평점이 낮은 상품들은 리드 타임이 길어진 경향을 보이고있다. 물리적으로 가능하다면 리드타임을 줄이는 방안을 고민해보고, 구매자와 판매자의 거리상 리드타임이 길어질수밖에 없다면 시스템으로 명확하게 배송기간이 길어질수 있음을 사전에 명시하는 이벤트를 추가하여 고객이 인지하도록 한다. 이를통해 구매를 취소하는 빈도와 평점 등을 종합적으로 고려하여 시스템을 개선해본다

Retention (유지): 사용자가 제품/서비스를 반복적으로 사용하는 단계

  • 고객별 주문 횟수 : customer_unique_id를 이용하여 고객별 주문 횟수 계산
  • 코호트별 재구매 분석 : order_purchase_timestamp를 이용하여 코호트를 구분하고, 각 코호트의 재구매율을 계산
  • 이탈 고객 비율 : 특정 기간 동안 구매하지 않은 고객 비율을 계산
1
2
3
customer_order_count = df3_new.groupby('customer_unique_id')['order_id'].nunique()
print("\n고객별 주문 횟수:")
customer_order_count.value_counts()
1
고객별 주문 횟수:
count
order_id
192507
22673
3192
429
59
65
73
91
161


1
round(((customer_order_count == 2).sum() / customer_order_count.shape[0])*100,2)
1
2.8
  • 대부분 1회성 구매 고객이며, 주문을 2번한 고객은 약 3% 수준으로 관찰됐다. 고객이 재구매를 할수있도록 어떤것들이 문제가있고 개선할 필요가있는지 확인할 필요가 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
df4_new = df3_new.copy()
df4_new['order_purchase_month'] = df4_new['order_purchase_timestamp'].dt.to_period('M')
first_order_month = df4_new.groupby('customer_unique_id')['order_purchase_month'].min()
orders = pd.merge(df4_new, first_order_month.rename('cohort'), on='customer_unique_id')

# 코호트별 재구매율
cohort_repurchase = orders.groupby(['cohort', 'order_purchase_month'])['customer_unique_id'].nunique().reset_index()
cohort_repurchase['period_number'] = (cohort_repurchase['order_purchase_month'] - cohort_repurchase['cohort']).apply(lambda x: x.n)
cohort_pivot = cohort_repurchase.pivot_table(index='cohort', columns='period_number', values='customer_unique_id')
cohort_size = cohort_pivot.iloc[:, 0]
retention_matrix = cohort_pivot.divide(cohort_size, axis=0)

# 시각화: 히트맵 생성
plt.figure(figsize=(12, 8))
sns.heatmap(retention_matrix, annot=True, fmt='.0%', cmap='Blues', cbar_kws={'label': '재구매율'})
plt.title('코호트별 재구매율')
plt.xlabel('기간 번호 (개월)')
plt.ylabel('코호트 (첫 주문 월)')
plt.xticks(rotation=45)
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()

png

1
2
3
4
5
6
7
8
9
10
11
12
13
# 이탈 고객 비율 계산
churn_rate = 1 - retention_matrix.mean(axis=1)

# 시각화: 이탈 고객 비율 바 차트 생성
plt.figure(figsize=(12, 6))
sns.barplot(x=churn_rate.index.astype(str), y=churn_rate.values, palette='Reds')
plt.title('코호트별 이탈 고객 비율')
plt.xlabel('코호트 (첫 주문 월)')
plt.ylabel('이탈 고객 비율')
plt.xticks(rotation=45)
plt.ylim(0, 1)  # 비율이 0%에서 100%까지이므로 y축 범위를 설정
plt.tight_layout()
plt.show()

png

1
churn_rate
0
cohort
2016-090.000000
2016-100.885246
2016-120.000000
2017-010.941866
2017-020.942750
2017-030.942175
2017-040.939114
2017-050.934744
2017-060.930679
2017-070.926174
2017-080.920352
2017-090.913794
2017-100.906598
2017-110.898005
2017-120.886990
2018-010.872691
2018-020.854629
2018-030.831174
2018-040.797466
2018-050.747464
2018-060.664364
2018-070.497440
2018-080.499920


  • 월별 코호트 분석을 수행해도 앞서 고객별 주문 횟수를 확인한 결과와 동일하게 대부분의 고객(90% 이상)이 1회 구매후 이탈하고있다. (2018년도 데이터는 거래가 작아서 이탈율이 전월대비 낮아보인다)
  • 앞서 EDA진행시 제품 평점은 5점 비율이 높았으며, 평균 평점도 제일 낮은지역도 3.5이상으로 나타났다. 그럼에도 이탈률이 높다는 것은 마케팅 전략이나 서비스 품질이 문제가 있다고 판단할 수 있다.
  • 고객 유지 전략을 강화하기위해 고객 경험을 개선할 필요가 있다. 예를들어, 고객의 피드백을 수용하는 시스템을 만들거나 프로모션이나 서비스 개선을 검토해볼 수 있다

Revenue (수익): 사용자가 제품/서비스를 통해 수익을 창출하는 단계

  • 총 매출 : price 컬럼의 합 계산
  • 평균 주문 금액 : price컬럼과 order_purchase_timestamp 이용하여 평균 주문 금액 계산
  • 카테고리별 매출 분석 : priceproduct_category_name을 이용하여 카테고리별 매출 계산
1
print("\n총 매출:", df3_new['price'].sum())
1
총 매출: 13651923.47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 연도와 월별로 그룹화하여 주문 수 계산
monthly_orders = df2.groupby(df2['order_purchase_timestamp'].dt.to_period('M')).size()

# 1. 'shipping_limit_date'를 datetime 형식으로 변환
df3['shipping_limit_date'] = pd.to_datetime(df3['shipping_limit_date'])

# 2. 월별 매출액 집계
monthly_sales = df3.resample('M', on='shipping_limit_date')['price'].sum()

# 3. 2018년 8월까지의 데이터만 필터링
monthly_sales = monthly_sales[monthly_sales.index <= '2018-08-31']

# 4. subplot 생성
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 12))

# 첫 번째 차트: 월별 주문 수
bars = ax1.bar(monthly_orders.index.astype(str), monthly_orders.values, color='skyblue')

# 주문 수 표시
for bar in bars:
    yval = bar.get_height()
    ax1.text(bar.get_x() + bar.get_width()/2, yval, str(yval), ha='center', va='bottom')

ax1.set_title('월별 주문 수')
ax1.set_xlabel('연도-월')
ax1.set_ylabel('주문 수')
ax1.set_xticks(range(len(monthly_orders.index)))  # x축 위치 설정
ax1.set_xticklabels(monthly_orders.index.astype(str), rotation=45)  # 레이블과 회전 설정
ax1.grid(axis='y')

# 두 번째 차트: 월별 매출액 추이
ax2.plot(monthly_sales.index, monthly_sales.values, marker='o')

# 매출액 숫자 표기
for x, y in zip(monthly_sales.index, monthly_sales.values):
    ax2.text(x, y, f'{y:,.0f}', fontsize=9, ha='center', va='bottom')

# x축을 3개월 단위로 설정
ax2.set_xticks(monthly_sales.index[::3])
ax2.set_title('월별 매출액 추이')
ax2.set_xlabel('')
ax2.set_ylabel('매출액')
ax2.set_xlim(pd.Timestamp('2016-09-01'), pd.Timestamp('2018-08-31'))  # x축 범위 설정
ax2.grid()

# 레이아웃 조정 및 그래프 표시
plt.tight_layout()
plt.show()

png

  • 제품 판매수와 매출은 상승곡선을 그리고있다. 리텐션을 개선한다면 수익을 극대화 할 수 있다. 획득부분에서 얻은 고객이 거주하는 상위 3개 주를 우선적으로 마케팅 전략을 세우고, 상위 7개 주까지 확대방안을 검토해본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 코호트 분석을 위한 평균 주문 금액 계산
cohort_order_value = orders.groupby(['cohort', 'order_purchase_month'])['price'].mean().reset_index()
cohort_order_value['period_number'] = (cohort_order_value['order_purchase_month'] - cohort_order_value['cohort']).apply(lambda x: x.n)
cohort_order_value_pivot = cohort_order_value.pivot_table(index='cohort', columns='period_number', values='price')

plt.figure(figsize=(12, 8))
sns.heatmap(cohort_order_value_pivot, annot=True, fmt='.0f', cmap='YlGnBu', cbar_kws={'label': '평균 주문 금액'})
plt.title('코호트별 평균 주문 금액')
plt.xlabel('기간 번호 (개월)')
plt.ylabel('코호트 (첫 주문 월)')
plt.xticks(rotation=45)
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()

png

  • 대부분의 코호트는 평균주문금액이 초기의 높게 형성됐으며, 시간이 지남에 따라 감소하는 경향을 보이고있다. 다만, 앞서 살펴본 재구매율이 3%수준이기 때문에 근본적으로 고객이 재구매할 수 있는 환경을 조성해야한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 1. 'product_category_name'에 대한 빈도수 계산
category_counts = merged_df_product['product_category_name'].value_counts()
category_revenue = merged_df_product.groupby('product_category_name')['price'].sum()

# 2. 전체 매출액 및 전체 빈도수 계산
total_revenue = category_revenue.sum()
total_count = category_counts.sum()

# 3. 첫 번째 줄을 위한 상위 10개 카테고리 선택 및 정렬
top_categories_counts = category_counts.head(10).sort_values(ascending=True)
top_categories_revenue = category_revenue[top_categories_counts.index]

# 4. 상위 10개 카테고리의 매출 비중 및 빈도 비중 계산
top_revenue_percentage = (top_categories_revenue / total_revenue) * 100
top_count_percentage = (top_categories_counts / total_count) * 100

# 5. subplot 생성 (2x2)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))

# 6. 차트 생성 함수
def create_bar_chart(ax, x_data, y_data, title, xlabel, color):
    bars = ax.barh(x_data, y_data, color=color)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.grid(axis='x')
    for bar in bars:
        ax.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
                f'{bar.get_width():,.1f}%', va='center', ha='left', fontsize=9)

# 7. 첫 번째 차트: 'product_category_name' 빈도수 비중 (상위 10개)
create_bar_chart(ax1, top_categories_counts.index, top_count_percentage,
                 '상품 카테고리별 빈도수 비중 (%) (상위 10개)', '빈도수 비중 (%)', 'skyblue')

# 8. 두 번째 차트: 상위 10개 카테고리의 매출 비중
create_bar_chart(ax2, top_categories_counts.index, top_revenue_percentage,
                 '상품 카테고리별 매출 비중 (%) (상위 10개)', '매출 비중 (%)', 'lightgreen')

# 9. 세 번째 차트: 전체 매출액 상위 10개의 상품 카테고리 비중
top_revenue_categories = category_revenue.nlargest(10).sort_values(ascending=True)
top_revenue_categories_percentage = (top_revenue_categories / total_revenue) * 100
create_bar_chart(ax3, top_revenue_categories.index, top_revenue_categories_percentage,
                 '총 매출액 상위 10개 상품 카테고리 비중 (%)', '매출 비중 (%)', 'orange')

# 10. 네 번째 차트: 빈도수 (상위 10개 카테고리, 비중)
top_revenue_categories_counts = category_counts[top_revenue_categories.index]
top_revenue_categories_counts_percentage = (top_revenue_categories_counts / total_count) * 100
create_bar_chart(ax4, top_revenue_categories.index, top_revenue_categories_counts_percentage,
                 '상품 카테고리별 빈도수 비중 (%) (상위 10개)', '빈도수 비중 (%)', 'skyblue')

ax2.yaxis.set_visible(False)
ax4.yaxis.set_visible(False)

for ax in [ax1, ax2, ax3, ax4]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 판매량과 매출액이 반드시 비례하는것은 아닌것으로 관찰됨
  • 특정 카테고리 매출비중이 높지않고 고르게 매출이 발생하고있으며, 건강과 미용 상품과 선물용 시계 상품이 각각 전체매출에 약 9% 비중을 차지하는 상위품목으로 관찰됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
total_sales_by_state = df3_new.groupby('customer_state')['price'].sum().reset_index()

# 전체 매출액 계산
total_sales = total_sales_by_state['price'].sum()

# 퍼센트 계산
total_sales_by_state['percentage'] = (total_sales_by_state['price'] / total_sales) * 100

# 매출액 기준으로 상위 10개 주 선택
top_10_states = total_sales_by_state.nlargest(10, 'price')

# 시각화: 상위 10개 주의 총 매출액 차트
plt.figure(figsize=(10, 6))
bars = sns.barplot(y='customer_state', x='price', data=top_10_states.sort_values(by='price', ascending=False), palette='Blues')
plt.title('고객 주별 총 매출액 상위 10개 (퍼센트 포함)')
plt.xlabel('총 매출액')
plt.ylabel('고객 주')

# 퍼센트 표시
for index, bar in enumerate(bars.patches):
    plt.text(bar.get_x() + bar.get_width() + 10000, bar.get_y() + bar.get_height() / 2,
             f'{top_10_states["percentage"].iloc[index]:.1f}%', color='black', va='center')

plt.tight_layout()
plt.show()

png

  • 매출 비중은 SP에서 약 40%로 대부분의 매출이 발생하고있으며, 상위 5개 주를 포함하면 74%에 해당되고있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
top_states = df3_new['customer_state'].value_counts().nlargest(5).index

# 상위 5개 주에서의 총 매출액 계산
top_states_data = df3_new[df3_new['customer_state'].isin(top_states)]
total_sales_by_category = top_states_data.groupby('product_category_name')['price'].sum().reset_index()

# 매출액 기준으로 상위 10개 상품 카테고리 선택
top_10_categories = total_sales_by_category.nlargest(10, 'price')

# 전체 매출액 계산
total_sales = top_states_data['price'].sum()

# 퍼센트 계산
top_10_categories['percentage'] = (top_10_categories['price'] / total_sales) * 100

# 상품 카테고리 상위 10개 계산
category_counts = top_states_data['product_category_name'].value_counts().nlargest(10).reset_index()
category_counts.columns = ['product_category_name', 'count']

# 시각화: 서브플롯 생성
fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# 첫 번째 차트: 상위 10개 상품 카테고리 총 매출액
bars1 = sns.barplot(y='product_category_name', x='price', data=top_10_categories, palette='Blues', ax=axes[0])
axes[0].set_title('고객 상위 5개 주(state)의 상위 10개 상품 카테고리 총 매출액')
axes[0].set_xlabel('총 매출액')
axes[0].set_ylabel('상품 카테고리')

# 퍼센트 표시
for index, bar in enumerate(bars1.patches):
    axes[0].text(bar.get_x() + bar.get_width() + 10000, bar.get_y() + bar.get_height() / 2,
                  f'{top_10_categories["percentage"].iloc[index]:.1f}%', color='black', va='center')

# 두 번째 차트: 상품 카테고리 상위 10개
bars2 = sns.barplot(y='product_category_name', x='count', data=category_counts, palette='Greens', ax=axes[1])
axes[1].set_title('고객 상위 5개 주(state)의 상품 카테고리 상위 10개')
axes[1].set_xlabel('주문 수')
axes[1].set_ylabel('상품 카테고리')

# 퍼센트 표시
total_count = category_counts['count'].sum()
for index, row in category_counts.iterrows():
    axes[1].text(row['count'] + 0.5, index, f'{(row["count"] / total_count) * 100:.1f}%', color='black', va='center')

for ax in axes:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • olist 이용 고객 77%가 속한 상위 5개 주(state)기준 상위 10개 상품 카테고리가 약 63% 매출 비중을 차지한다. 즉, 전체 매출 74% 중 63%의 매출이 상위 10개 상품 카테고리에서 발생하고 있다
  • 수익을 우선적으로 극대화하기 위해 상위 5개 주에서 매출비중이 높은 상품을 확장시키는 방안을 검토해본다.
  • 다양한 상품은 전지역에 파는 방향보다는 잘팔리는 상품을 구매자와 판매자가 밀집된 지역에 우선적으로 마케팅 전략을 세우는 방향을 도입해볼 필요가 있다

Referral (추천): 사용자가 제품/서비스를 다른 사람에게 추천하는 단계

  • 제품별/카테고리별 평점 비교 : product_id, product_category_namereview_score컬럼을 이용하여 제품별 평점 비교
  • 순 추천지수 : 리뷰 점수를 추천그룹(5점)과 비추천그룹(1점,2점)의 비율을 계산
1
2
3
product_ratings = round(df3_new.groupby('product_category_name')['review_score'].mean(),2).sort_values(ascending=False).reset_index()
print("\n카테고리별 평점 비교:")
product_ratings
1
카테고리별 평점 비교:
product_category_namereview_score
0cds_dvds_musicals4.64
1fashion_childrens_clothes4.50
2books_general_interest4.45
3costruction_tools_tools4.44
4flowers4.42
.........
68office_furniture3.49
69pc_gamer3.33
70portateis_cozinha_e_preparadores_de_alimentos3.27
71diapers_and_hygiene3.26
72security_and_services2.50

73 rows × 2 columns

1
product_ratings.nlargest(10, 'review_score')
product_category_namereview_score
11cds_dvds_musicals4.64
29fashion_childrens_clothes4.50
8books_general_interest4.45
22costruction_tools_tools4.44
35flowers4.42
9books_imported4.40
10books_technical4.37
37food_drink4.32
53luggage_accessories4.32
66small_appliances_home_oven_and_coffee4.30
1
product_ratings.nsmallest(10, 'review_score')
product_category_namereview_score
63security_and_services2.50
23diapers_and_hygiene3.26
62portateis_cozinha_e_preparadores_de_alimentos3.27
59pc_gamer3.33
57office_furniture3.49
46home_comfort_23.63
30fashion_male_clothing3.64
34fixed_telephony3.68
58party_supplies3.77
27fashio_female_clothing3.78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
product_ratings = round(df3_new.groupby('product_category_name')['review_score'].mean(), 2).reset_index()

# 상위 10개와 하위 10개 상품 카테고리 선택
top_10_categories = product_ratings.nlargest(10, 'review_score')
bottom_10_categories = product_ratings.nsmallest(10, 'review_score')

# 상위 10개와 하위 10개를 하나의 데이터프레임으로 결합
top_10_categories['category_type'] = '상위'
bottom_10_categories['category_type'] = '하위'
combined_categories = pd.concat([top_10_categories, bottom_10_categories])

# 시각화: 평균 평점 차트
plt.figure(figsize=(10, 6))
bars = sns.barplot(y='product_category_name', x='review_score', data=combined_categories.sort_values(by='review_score', ascending=False), palette='coolwarm', hue='category_type')
plt.title('상품 카테고리 평균 평점 (상위 10개 및 하위 10개)')
plt.xlabel('평균 평점')
plt.ylabel('상품 카테고리')

# 평균 평점 표시
for index, bar in enumerate(bars.patches):
    plt.text(bar.get_x() + bar.get_width() + 0.01, bar.get_y() + bar.get_height() / 2,
             f'{bar.get_width():.2f}', color='black', va='center')

plt.tight_layout()
plt.show()

png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1. 1점과 2점인 경우의 수를 계산
low_score_counts = df3_new[df3_new['review_score'].isin([1, 2])].groupby('product_category_name')['review_score'].count()

# 2. 상위 10개 품목 선택
top_low_score_categories = low_score_counts.nlargest(10).sort_values(ascending=True)

# 3. 5점인 경우의 수를 계산
high_score_counts = df3_new[df3_new['review_score'] == 5].groupby('product_category_name')['review_score'].count()

# 4. 상위 10개 품목 선택
top_high_score_categories = high_score_counts.nlargest(10).sort_values(ascending=True)

# 5. subplot 생성
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))

# 6. 첫 번째 차트: 1점 및 2점이 많은 상위 10개 품목
bars1 = ax1.barh(top_low_score_categories.index, top_low_score_categories.values, color='lightcoral')
ax1.set_title('1점 및 2점이 많은 상위 10개 품목')
ax1.set_xlabel('빈도수')
ax1.grid(axis='x')

# 7. 숫자 표기
for bar in bars1:
    ax1.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

# 8. 두 번째 차트: 5점이 많은 상위 10개 품목
bars2 = ax2.barh(top_high_score_categories.index, top_high_score_categories.values, color='lightgreen')
ax2.set_title('5점이 많은 상위 10개 품목')
ax2.set_xlabel('빈도수')
ax2.grid(axis='x')

for bar in bars2:
    ax2.text(bar.get_width(), bar.get_y() + bar.get_height()/2,
             f'{bar.get_width():,.0f}', va='center', ha='left', fontsize=10)

for ax in [ax1, ax2]:
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

png

  • 잘팔리는 상품들은 하위 평점은 아니지만 낮은평점과 높은평점이 공존하고있다
  • aarrr 관점에서 우리는 고객이 밀집한 주(state)에서 잘팔리는 상품들을 확대하기로 설정했다.
  • 따라서 잘팔리는 상품들의 평점이 1점이나 2점을 받은 낮은 사례들의 리뷰를 확인해서 고객의 피드백을 반영하도록 노력한다
  • 추가적으로 리드타임이 길어지면 평점이 낮아지는 경향을 활성화 단계에서 발견했다. 이부분도 리드타임을 개선할 수 있는 방안이나 고객이 인지할수있는 방안을 강구해본다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
df3_new['review_answer_timestamp'] = pd.to_datetime(df3_new['review_answer_timestamp'])

# 추천 및 비추천 그룹 분류
df3_new['group'] = df3_new['review_score'].apply(lambda x: '추천' if x == 5 else ('비추천' if x in [1, 2] else '중립'))

# 월별 집계
df3_new['month'] = df3_new['review_answer_timestamp'].dt.to_period('M')
monthly_counts = df3_new.groupby(['month', 'group']).size().unstack(fill_value=0)

# NPS 계산
monthly_counts['NPS'] = (monthly_counts['추천'] - monthly_counts['비추천']) / (monthly_counts['추천'] + monthly_counts['비추천']) * 100

# 시각화
plt.figure(figsize=(12, 6))
sns.lineplot(data=monthly_counts, x=monthly_counts.index.astype(str), y='NPS', marker='o')
plt.title('월별 NPS (추천 비율 - 비추천 비율)')
plt.xlabel('')
plt.ylabel('NPS (%)')
plt.xticks(rotation=45)
plt.axhline(0, color='red', linestyle='--', linewidth=1)

# NPS 값 표시
for i, nps_value in enumerate(monthly_counts['NPS']):
    plt.text(i, nps_value, f'{nps_value:.1f}', color='black', ha='center', va='bottom')

plt.tight_layout()
plt.show()

png

  • 월별 순 추천지수(NPS)는 대부분 40점 이상으로 굉장히 높게 나타났는데, 실제 재구매율은 3%로 굉장히 낮은 수치를 보이고있다. 리뷰를 5점을 주는것이 과장된 부분이 없는지(리뷰이벤트나 평점을 조작하지 않았는지 등) 감안할 필요가 있다.
  • 재구매율을 감안했을때 리뷰점수가 낮은 1점과 2점을 진실된 리뷰로 해석할 수도 있다. 따라서 평점이 낮은 부분을 적극적으로 확인하고, 추가적으로 리뷰 외에도 설문조사 등으로 고객의 의견을 받을 수 있도록 다양한 채널을 만들어 확인할 필요가 있다.
This post is licensed under CC BY 4.0 by the author.