-
실전 예제 - 온-오프라인 비지니스 분석 09빅데이터/Data-Analysis 2022. 3. 1. 12:45
패스트캠퍼스 '직장인을 위한 파이썬 데이터분석 올인원 패키치 Online' 참조
- 엑셀은 사람들이 가장 쉽게 접하고 데이터를 쉽게 다를 수 있는 툴이다. 하지만 어느정도 데이터를 다룰 수 있으나 데이터가 점점 방대해지면 한계점이 극명해진다.
- 각기 다른 데이터 형태, 시각화 등 엑셀에서 하기 정말 어려운 데이터들이 많이 있다. 파이썬은 엑셀의 단점을 완벽히 커버 할 수 있다
- 이번 강의는 이커머스 데이터와 패스트푸드점 데이터로 진행 될 예정
01 기본 설정
- 강의를 진행하기 위해서 두가지 라이브러리를 설치 해 주자
pip install missingno # 판다스의 데이터 프레임 결측치를 찾는 기능 제공 pip install squarify # 트리맵 시각화 라이브러리
- 필요 라이브러리
# 필수 라이브러리 import warnings warnings.filterwarnings('ignore') import pandas as pd import numpy as np %matplotlib inline import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns import missingno as msno import squarify
- 데이터 다운 주소
https://www.kaggle.com/olistbr/brazilian-ecommerce
02 하나의 데이터 훑어 보기
df = pd.read_csv('olist_orders_dataset.csv') df.head()
df.shape (99441, 8) df.columns Index(['order_id', 'customer_id', 'order_status', 'order_purchase_timestamp', 'order_approved_at', 'order_delivered_carrier_date', 'order_delivered_customer_date', 'order_estimated_delivery_date'], dtype='object') df.info() # Column Non-Null Count Dtype --- ------ -------------- ----- 0 order_id 99441 non-null object 1 customer_id 99441 non-null object 2 order_status 99441 non-null object 3 order_purchase_timestamp 99441 non-null object 4 order_approved_at 99281 non-null object 5 order_delivered_carrier_date 97658 non-null object 6 order_delivered_customer_date 96476 non-null object 7 order_estimated_delivery_date 99441 non-null object
- 현재 모든 데이터가 오브젝트 형태이다. 여기서 쓰려고 하는 데이터들은 처리가 되도록 형태를 바꿔줘야 한다
# 필요한 데이터 타입 변경 # 필요한 데이터 타입 변경 df['order_purchase_timestamp'] = pd.to_datetime(df['order_purchase_timestamp']) df['order_approved_at'] = pd.to_datetime(df['order_approved_at']) df['order_delivered_carrier_date'] = pd.to_datetime(df['order_delivered_carrier_date']) df['order_delivered_customer_date'] = pd.to_datetime(df['order_delivered_customer_date']) df['order_estimated_delivery_date'] = pd.to_datetime(df['order_estimated_delivery_date']) df.info() 3 order_purchase_timestamp 99441 non-null datetime64[ns] 4 order_approved_at 99281 non-null datetime64[ns] 5 order_delivered_carrier_date 97658 non-null datetime64[ns] 6 order_delivered_customer_date 96476 non-null datetime64[ns] 7 order_estimated_delivery_date 99441 non-null datetime64[ns]
03 EDA
df.isnull().sum() order_id 0 customer_id 0 order_status 0 order_purchase_timestamp 0 order_approved_at 160 order_delivered_carrier_date 1783 order_delivered_customer_date 2965 order_estimated_delivery_date 0
# 결측치가 있는 row 출력 df_null = df[df.isnull().any(axis=1)] df_null 2980 rows × 8 columns # 특정 컬럼에 null 출력 df_col1 = df[df['order_approved_at'].isnull()] df_col1
▶ 결측치 제거
- 크게 삭제하거나 특정값으로 채우는 방법이 있다
- 데이터가 많을 경우는 삭제가 가장 베스트지이지만 그렇지 않은 경우는 하나하나의 데이터가 소중하기때문에 특정값으로 대체하게됨
# 결측치 처리 # axis=0 로우, 1= 컬럼 df_clean = df.dropna(axis=0) # 인덱싱 재지정 df_clean.reset_index(drop=True, inplace=True) df_clean
▶ 한 컬럼 분석
# 한 컬럼 분석 df_clean['order_status'].unique() array(['delivered', 'canceled'], dtype=object) # 주문 상태 개수 df_clean['order_status'].value_counts() delivered 96455 canceled 6 # 주문 상태 널값 체크 df_null['order_status'].unique() array(['invoiced', 'shipped', 'processing', 'unavailable', 'canceled', 'delivered', 'created', 'approved'], dtype=object) df_null['order_status'].value_counts() shipped 1107 canceled 619 unavailable 609 invoiced 314 processing 301 delivered 23 created 5 approved 2
- 이렇게 보면 이해하기가 편하지 않다. 주문 상태의 취소와 배송중 취소건을 시각화 해 보자
# 취소건 시각화 A = df_clean[df_clean['order_status'] == 'canceled'].shape[0] #취소건 (6,8) 중 6개 B = df_null[df_null['order_status']== 'canceled'].shape[0] #취소건 (619,8) 중 619개 temp = pd.DataFrame(columns=['del_finished', 'del_not_finished'], index=['cancel_cnt']) # temp에 값 넣기 temp.loc['cancel_cnt', 'del_finished'] = A temp.loc['cancel_cnt', 'del_not_finished'] = B temp del_finished del_not_finished cancel_cnt 6 619
▶ 막대 그래프로 표현
- 차트를 그릴시에 x,y축에 무엇을 넣느냐에따라 방향을 바꿔 줄 수 있다
temp.T
데이터 프레임 축이 변경 된것을 볼 수 있다. 04 결측치를 다른 데이터로 채우기
- 현재 'olist_orders_dataset' 테이블에 있는 컬럼들 중 4가지 컬럼은 아래와 같다
order_purchase_timestamp - 구매 시작 날짜/시간
order_approved_at - 결제 완료 날짜/시간
order_delivered_customer_date - 실제 고객한테 배달 완료된 날짜/시간
order_estimated_delivery_date - 시스템에서 고객한테 표시되는 예상배달날짜 - 이 컬럼들을 가지고 새로운 정보들을 채울 예정
order_approved_at - order_purchase_timestamp : pay_lead_time(단위: 분) - 고객이 구매 후 결제까지의 시간
order_delivered_customer_date - order_approved_at : delivery_lead_time(단위: 일) - 배달이 걸리는 시간
order_estimated_delivery_date - order_delivered_customer_date : estimated_date_miss(단위: 일) - 실제로 걸린 시간 (즉 배송 예측 시간과 실제 시간의 차를 보여줌)
# 고객이 구매 후 결제까지의 시간 df_clean['pay_lead_time'] = df_clean['order_approved_at'] - df_clean['order_purchase_timestamp'] # 추후 계산이 용이하도록 분단위로 변경 df_clean['pay_lead_time_m'] = df_clean['pay_lead_time'].astype('timedelta64[m]') # 배달 리드 타임 df_clean['delivery_lead_time'] = df_clean['order_delivered_customer_date'] - df_clean['order_approved_at'] # 일 단위로 변경 df_clean['delivery_lead_time_d'] = df_clean['delivery_lead_time'].astype('timedelta64[D]') # 예상날짜 차이 # 배달 리드 타임 df_clean['estimated_date_miss'] = df_clean['order_estimated_delivery_date'] - df_clean['order_delivered_customer_date'] # 일 단위로 변경 df_clean['estimated_date_miss_d'] = df_clean['estimated_date_miss'].astype('timedelta64[D]') # 세 컬럼 모두 정수형으로 통일 df_clean['pay_lead_time_m'] = df_clean['pay_lead_time_m'].astype(int) df_clean['delivery_lead_time_d'] = df_clean['delivery_lead_time_d'].astype(int) df_clean['estimated_date_miss_d'] = df_clean['estimated_date_miss_d'].astype(int) order_id object customer_id object order_status object order_purchase_timestamp datetime64[ns] order_approved_at datetime64[ns] order_delivered_carrier_date datetime64[ns] order_delivered_customer_date datetime64[ns] order_estimated_delivery_date datetime64[ns] pay_lead_time timedelta64[ns] pay_lead_time_m int32 delivery_lead_time timedelta64[ns] delivery_lead_time_d int32 estimated_date_miss timedelta64[ns] estimated_date_miss_d int32
▶ 시각화
# 새로운 데이터로 시각화 # 결제까지 걸리는 시간 plt.figure(figsize=(12,6)) sns.distplot(df_clean['pay_lead_time_m']) plt.show()
결제 리드 타임은 대부분 짧은 시간안에 이루어지고 전문 용어로 'right skewed'로 오른쪽으로 치우쳤다고 한다. # 배달까지 걸리는 시간 plt.figure(figsize=(12,6)) sns.distplot(df_clean['delivery_lead_time_d'])
똑같이 오른쪽으로 치우쳐져있다. # 실제 걸리는 시간 plt.figure(figsize=(12,6)) sns.distplot(df_clean['estimated_date_miss_d'])
양/음수값을 모두 가지고 있고 음수는 시스템에서 알려준날짜보다 더 빨리 도착했다고 예상 할 수 있다 ▶ 다시 이 컬럼들을 요약을 해보면
# 요약 df_clean[['pay_lead_time_m', 'delivery_lead_time_d','estimated_date_miss_d']].describe()
- 결제까지는 616분, 배달완료는 11일, 시스템 예상 날짜 차이는 10일 정도
- 시스템 예상 날짜는 조금 줄여도 될 듯
- max값을 보면 재미있는데 결제까지 44486분이 걸린경우는 장바구니에 넣어놓고 진행을 안했다거나, 208일의 배달이 걸린 데이터를 보는등 재미있는요소들이 많다
- 또한 배달 시간에 -7 이 있는데 이는 잘못된 데이터일 가능성이 높다. (배달전에 이미 배달됬다고 찍힌 경우)
# 이상한 데이터 체크 df_clean[df_clean['delivery_lead_time_d']==-7]
결제를 9월13일에 했는데 배달이 9월6일로 찍혀있다. 잘못된 데이터! ▶ 이렇게 데이터 양이 많을 때 쉽게 어떠한 오류(이상치) 데이터들이 있는지 보는 방법이 'BoxPlot'. 데이터가 많은 경우 박스를 각각 찍어주는것이 좋다.
# 각각 찍어서 보자 plt.figure(figsize=(12,6)) sns.boxplot(data=df_order_time['pay_lead_time_m'], color='red'); # 각각 찍어서 보자 plt.figure(figsize=(12,6)) sns.boxplot(data=df_order_time['delivery_lead_time_d'], color='yellow'); # 각각 찍어서 보자 plt.figure(figsize=(12,6)) sns.boxplot(data=df_order_time['estimated_date_miss_d'], color='green');
- 이상치들이 박스 바깥에 많이 몰려있다
▶ 이상치들을 걸러 내자
즉 박스 위아래로 벗어나는 데이터들을 구하는 것 # 이상치 검출 코드 def outliers_iqr(data): q1, q3 = np.percentile(data, [25, 75]) #박스플롯의 Q1,Q3 영역 설정 iqr = q3 - q1 # Q1-Q3 사이의 범위 lower_bound = q1 - (iqr * 1.5) # 그 범위보다 작거나 upper_bound = q3 + (iqr * 1.5) # 그 범위보다 크거나 return np.where((data > upper_bound)|(data < lower_bound))
# pay_lead_time_m 이상치 outliers_iqr(df_order_time['pay_lead_time_m'])[0].shape[0] 8915 # delivery_lead_time_d 이상치 outliers_iqr(df_order_time['delivery_lead_time_d'])[0].shape[0] 4772 # estimated_date_miss_d 이상치 outliers_iqr(df_order_time['estimated_date_miss_d'])[0].shape[0] 4300
# 각각의 이상치 해당값 출력 df_order_time.loc[pay_lead_outlier_index, 'pay_lead_time_m'] # 각각의 이상치 해당값 출력 df_order_time.loc[del_lead_outlier_index, 'delivery_lead_time_d'] # 각각의 이상치 해당값 출력 df_order_time.loc[est_lead_outlier_index, 'estimated_date_miss_d']
▶ 이 이상치를 제거
# 이상치에 대한 세 컬럼을 합치기 lead_outlier_index = np.concatenate((pay_lead_outlier_index, del_lead_outlier_index, est_lead_outlier_index), axis=None) print(len(lead_outlier_index)) lead_outlier_index # for문을 통해 이상치가 아닌 리드타임 값의 인덱스를 추리기 lead_not_outlier_index = [] for i in df_order_time.index: #lead_outlier_index 에 포함 되지 않으면 추가 if i not in lead_outlier_index: lead_not_outlier_index.append(i) print(lead_not_outlier_index[:20]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 17, 18, 20, 21, 22] # 이상치가 아닌 값들 출력 df_order_time_clean = df_order_time.loc[lead_not_outlier_index] df_order_time_clean # 인덱스를 다시 재 정렬 df_order_time_clean = df_order_time_clean.reset_index(drop=True) df_order_time_clean
이상치를 제거하고 인덱싱을 제정렬 하지 않으면 같은 데이터 개수이지만 인덱싱이 정렬되지않은 데이터가 그대로 꼽히게 된다. 반드시 인덱싱을 해줘야한다. ▶ 다시 요약
이상치 제거 후 vs 제거 전 - 이상치를 제거하니 데이터가 더 깔끔하고 작은 수치들로 변화 되었다.
'빅데이터 > Data-Analysis' 카테고리의 다른 글
실전 예제 - 온-오프라인 비지니스 분석 11 (0) 2022.03.02 실전 예제 - 온-오프라인 비지니스 분석 10 (0) 2022.03.02 실전 예제 - 코로나 데이터 분석 08 (0) 2022.02.28 실전 예제 - 유투브 채널 및 영상 분석 07 (0) 2022.02.26 실전 예제 - 마케팅 데이터 분석 06 (Referral) (0) 2022.02.25