파이썬 데이터 분석 실무 테크닉 100

01장(웹에서 주문 수를 분석하는 테크닉), 02장(대리점 데이터를 가공하는 테크닉)

채영sw 2024. 9. 18. 18:36

01장. 웹에서 주문 수를 분석하는 테크닉 10

목적 : 어떤 기업 쇼핑몰 사이트의 상품 주문 수의 추세를 분석함으로써 판매량 개선의 방향을 찾는 것

전제 조건

→ 이 쇼핑몰의 주요 품목은 컴퓨터

가격대별로 5개의 상품 존재

4종류의 6개 데이터

데이터 정보

 

데이터베이스에 들어 있는 데이터를 시스템에서 가져올 때는 월별이나 1,000건 단위로 분할하는 경우가 많음

→ 데이터를 결합해 사용해야 함


<001. 데이터를 읽어 들이자>

# 경고(warning) 비표시
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
customer_master = pd.read_csv('customer_master.csv')
customer_master.head()

 

 

- 파이썬 라이브러리 판다스(pandas) 임포트

- pandas의 read_csv를 사용해 프레임형 변수 customer_master에 저장

- 읽어 들인 customer_master의 처음 5행 표시

 

item_master = pd.read_csv('item_master.csv')
item_master.head()

transaction_1 = pd.read_csv('transaction_1.csv')
transaction_1.head()

transaction_detail_1 = pd.read_csv('transaction_detail_1.csv')
transaction_detail_1.head()

 

→ 데이터를 확인함으로써 어떤 데이터가 존재하고 각 데이터 간에 어떤 관계가 있는지와 같은 데이터의 큰 틀 확인 가능

 

먼저 데이터 전체를 파악하는 것이 중요하기 때문에 되도록 상세하게 나와 있는 쪽에 맞춰 데이터 가공

→ 이 사례의 경우, 매출과 연관이 있고, 가장 상세한 데이터는 매출 관련 데이터이므로 transaction_detail을 기준으로 생각

→ 크게 2가지의 데이터 가공이 필요

   - transaction_detail_1, transaction_detail_2 / transaction_1, transaction_2 세로 결합(유니언)

   - transaction_detial을 기준으로, transaction, customer_master, item_master 가로 결합(조인)


<002. 데이터를 결합(유니언)해 보자>

transaction_2 = pd.read_csv('transaction_2.csv')
transaction = pd.concat([transaction_1, transaction_2], ignore_index = True)
transaction.head()

- pd.concat으로 유니언

- head()로는 유니언이 잘 됐는지 확인 불가 세로 결합이므로 데이터 개수로 유니언 검증 필요

- ignore_index = Ture : 이를 무시하지 않으면 이전 데이터에 있던 index를 그대로 가져옴, 특별히 인덱스를 유지해야 하는 경우가 아니라면 옵션을 추가함 (↔ index = ) 

print(len(transaction_1))
print(len(transaction_2))
print(len(transaction))

transaction_detail_2 = pd.read_csv('transaction_detail_2.csv')
transaction_detail = pd.concat([transaction_detail_1, transaction_detail_2], ignore_index = True)
transaction_detail.head()

 

 - transaction_detail도 유니언


<003. 매출 데이터끼리 결합(조인)해 보자>

기준이 되는 데이터를 결정하고 어떤 칼럼을 로 조인할지 생각해야 함 → transaction_detail을 기준 데이터로 결정

- 부족한(추가하고 싶은) 데이터 칼럼이 무엇인가

→ transaction의 payment_date, customer_id 추가, price는 추가 x (quantity와 item_price의 곱으로 알 수 있음)

- 공통되는 데이터 칼럼은 무엇인가

transaction_id

join_data = pd.merge(transaction_detail, transaction[["transaction_id", "payment_date", "customer_id"]], on="transaction_id", how = "left")
join_data.head()

 

- pd.merge 로 조인

- transaction_detial을 기준으로

- 조인키(join key)는 결합할 transaction 데이터의 필요한 칼럼 transaction_id

- 조인 종류 : Left Join으로 지정

print(len(transaction_detail))
print(len(transaction))
print(len(join_data))

→ 세로로 데이터가 늘어나지 않고 가로로 늘어나 조인된 것을 확인할 수 있음

→ 조인할 데이터의 조인키(transaction_id)에 중복 데이터가 존재하는 경우 데이터 개수가 늘어날 수 있음

더보기
가짜 데이터 생성 - 데이터의 무결성 위반

<부록 001. 데이터 결합과 정규화>

데이터 정규화 : 데이터를 관리하는 데이터베이스 설계 개념 중 하나로, '데이터의 정합성'을 담보하는 설계 사상

→ 데이터 정규화의 진행 정도에 따라 비정규화, 제1정규화, 제2정규화, 제3정규화로 분류

더보기

제1정규화 - 도메인 원자값 조건 만족 필요

- 반복 그룹 존재하면 안됨

-  모든 행은 식별자로 완전하게 구분되어야 함

 

제2정규화 - 부분함수 종속 제거

- key가 아닌 값들은 모두 key에 종속되어야 함

 

제3정규화 - 이행적 함수 종속 제거
- 모든 non-key 컬럼은 key 컬럼에만 종속되어야 함

결합 : 데이터를 합치는 것

- 내부 결합(inner join) : 결합할 2개의 데이터에 모두 존재하는 데이터로만 구성

   → 정확하게 결합 조건을 설정하지 않으면 데이터 누락이 생길 수 있음

- 외부 결합(outer join) : 어느 한 쪽만 존재하면 데이터 결합 가능

   → 레프트 조인(결합되는 쪽의 좌측 데이터가 모두 사용됨), 라이트 조인(결합되는 쪽의 우측 데이터가 모두 사용됨) 포함

   → 데이터 1에만 있는 데이터의 데이터 2 부분에는 NULL이 들어감


<004. 마스터데이터를 결합(조인)해 보자>

- 부족한(추가하고 싶은) 데이터 칼럼이 무엇인가

→ customer_master, item_master에 포함된 데이터

- 공통되는 데이터 칼럼은 무엇인가

 각각 customer_id, item_id

join_data = pd.merge(join_data, customer_master, on = "customer_id", how = 'left')
join_data = pd.merge(join_data, item_master, on = "item_id", how = 'left')
join_data.head()

 

- 결합의 영향으로 매출(price)이 사라졌기 때문에 다시 계산해야 함


<005. 필요한 데이터 칼럼을 만들자>

매출 = quantity x item_price

join_data["price"] = join_data["quantity"] * join_data["item_price"]
join_data[["quantity", "item_price", "price"]].head()

 

- 판다스의 데이터프레임형 곱셈 : 행마다(가로 방향) 계산 실행


<006. 데이터를 검산하자>

검산이 가능한 데이터 칼럼을 찾고 검산하기 : 이번 사례는 price 칼럼

print(join_data["price"].sum())
print(transaction["price"].sum())

데이터 가공 전 transaction의 price 총합과 데이터 가공 후 price 총합이 같음

join_data["price"].sum() == transaction["price"].sum()

#True

 

True / False로도 확인 가능


<007. 각종 통계량을 파악하자>

데이터 분석을 진행할 때는 크게 두 가지를 파악해야 함

- 결손치의 개수 : 제거하거나 보간해야 함

- 전체를 파악할 수 있는 숫자감 : 이번 달 상품 A의 매출이 10만원 → 전체 매출 단위 10억/100만원인지로 의미 달라짐

join_data.isnull().sum()

 

- 결손치의 개수 출력

- isnull()은 결손치가 True/False로 반환되기 때문에 True의 개수를 칼럼마다 sum()으로 계산

- 결손치 없음

join_data.describe()

 

 

- describe()를 이용해 데이터 개수(count), 평균값(mean), 표준편차(std), 최솟값(min), 사분위수(25%, 75%), 중앙값(50%), 최댓값(max) 각종 통계량 출력 가능

- describe()는 숫자 데이터를 집계함

print(join_data["payment_date"].min())
print(join_data["payment_date"].max())

 

- 데이터의 기간 파악 가능


<008. 월별로 데이터를 집계해 보자>

시계열 상황 살펴보기

- 이번 케이스처럼 반년 정도의 데이터라면 별로 영향이 없겠지만, 과거 수년의 데이터에는 여러가지 비즈니스 모델이 포함되어 있을 수 있기 때문에 전체 데이터를 한 번에 분석하면 데이터의 시계열 변화를 잘못 파악하는 경우가 있을 수 있음

→ 데이터 범위를 좁혀 분석하는 것도 하나의 방법!

 

전체적으로 매출 변화를 파악해보기

- 구입일 payment_date에서 연월을 추출해 새롭게 칼럼을 작성하고, 연월 단위로 price 집계해서 표시

 

join_data.dtypes

 

- 데이터타입 확인

- payment_date는 object형

 

join_data["payment_date"] = pd.to_datetime(join_data["payment_date"])
join_data["payment_month"] = join_data["payment_date"].dt.strftime("%Y%m")
join_data[["payment_date", "payment_month"]].head()

 

- datetime형으로 데이터 타입 변환 (to_datetime)

- 새로운 칼럼 payment_month를 연월 단위로 작성

  → 판다스의 datetime의 dt를 사용하면 년, 월 추출 가능

  → strftime을 사용해 연월 작성

더보기

- datetime objectdt 접근자를 사용해 datetime 속성이나 메서드를 사용해 시계열 데이터를 처리할 수 있음

- dt.strftime() : pandas의 datetime object를 지정된 date format으로 변환

from datetime import datetime 

# 현재 날짜 
now = datetime.now() 

# 특정 형식의 문자열로 변환 
formatted_date = now.strftime('%d-%m-%Y %H:%M:%S') 

print(formatted_date)

#result = sr.dt.strftime('% d % m % Y, % r')

 

%Y : 연도 0001~9999

%m : 달 01~12

%d : 날짜 01~31

%H : 24시간 형식의 하루의 시간 00~23

%I : 12시간 형식의 하루의 시간 01~12

%M : 분 00~59

%S : 초 00~59

join_data.groupby("payment_month").sum()["price"]

 

 

- 월별 매출 집계

- groupby : 집계하고 싶은 칼럼(payment_month)과 집계 방법(sum) 기술, price만 표시하기 위해 price 칼럼 지정

  → 5월 매출이 조금 내려갔지만, 6월, 7월 회복, 가장 매출이 높은 달은 7월 등의 정보를 알 수 있음

더보기
데이터프레임.groupby(그룹이되는컬럼)[계산하고 싶은 컬럼].집계함수()

                            or

데이터프레임.groupby(그룹이되는컬럼).집계함수()[계산하고 싶은 컬럼]

 

<집계함수(집계 방법) 종류>

- mean(평균), median(중위수)

- count(개수), sum(합산)

- min(최소), max(최대)

- std(표준편차), var(분산), quantile(특정 백분위)

                                          (ex. df.groupby("월")["야근"].quantile(0.9) ← 월별 상위 10% 야근일수)

- describe(전반적인 지표)

 

< multiple column / groupby >

데이터프레임.groupby([컬럼1,컬럼2]).agg({계산하고싶은컬럼명1 : 집계함수 , 계산하고싶은컬럼명2 : 집계함수})

 

ex. 월별 인당 휴일 평균과 야근 총합

df.groupby(["월","이름"]).agg({"휴일":"mean","야근":"sum"})

 

< reset_index로 index 초기화해서 데이터 활용하기 >

g1_df["이름"]   #컬럼명 자체가 인덱스로 들어가 있기 때문에 오류 발생
g2_df = df.groupby(["월", "이름"]).agg({"휴일":"mean", "야근":"sum"}).reset_index()
g2_df

<009. 월별, 상품별로 데이터를 집계해 보자>

월별, 상품별로 매출의 합계, 수량 표시

join_data.groupby(["payment_month", "item_name"]).sum()[["price", "quantity"]]

 

- 월별, 상품별로 price와 quantity의 집계 결과 표시

- groupby에서 출력하고 싶은 칼럼이 여러 개 있을 경우, 리스트형으로 지정

 

pd.pivot_table(join_data, index='item_name', columns='payment_month', values=['price', 'quantity'], aggfunc='sum')

 

- pivot_table칼럼 지정 가능

- 에는 상품명, 칼럼에는 월이 오도록 indexcolumns로 지정

- values에는 집계하고 싶은 칼럼(price, quantity) 지정

- aggfunc에는 집계 방법(sum) 지정

- 월별, 상품별로 추세 파악하기 쉬움

  → 매출 합계는 PC-E가 가장 높지만, 수량에서는 가장 싼 PC-A가 많음, 5월 매출 감소의 원인은 PC-E의 영향이 큼

더보기
pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All', observed=_NoDefault.no_default, sort=True)

 

- fill_value : 집계 후, 결과 피벗 테이블에서 누락된 값을 바꿀 값

- margins = True : 행과 열의 범주에 부분 그룹 집계가 적용되어 특수 열과 행 추가(여백)

- dropna = True : 모든 열에 NaN 값이 있는 행은 마진 계산 전 생략

- sort : 결과 정렬 여부 지정


<010. 상품별 매출 추이를 가시화해 보자>

월별, 상품별 매출 추이 그래프로 표현

- 데이터 집계

- 집계된 데이터를 이용해 그래프 그리기

graph_data = pd.pivot_table(join_data, index='payment_month', columns='item_name', values='price', aggfunc='sum')
graph_data.head()

 

- pivot_table를 이용해 데이터 집계

- 그래프를 그리기 위해 가로축에 payment_month, 세로축에 graph_data의 해당 상품명 지정

 

import matplotlib.pyplot as plt  # from matplotlib import pyplot as plt
%matplotlib inline
plt.plot(list(graph_data.index), graph_data["PC-A"], label='PC-A')
plt.plot(list(graph_data.index), graph_data["PC-B"], label='PC-B')
plt.plot(list(graph_data.index), graph_data["PC-C"], label='PC-C')
plt.plot(list(graph_data.index), graph_data["PC-D"], label='PC-D')
plt.plot(list(graph_data.index), graph_data["PC-E"], label='PC-E')
plt.legend()

 

- matplotlib 임포트

  → 가로축, 세로축 순서로 지정

  → 가로축은 payment_month를 표시해야 하므로 graph_data.index를 리스트형으로 변환하여 지정

  → 세로축은 상품별 매출로 graph_data 칼럼을 지정

  → label로 범례 표시

- 상품마다 그래프를 그리고, legend()는 범례 표시

더보기

- %matplotlib inline : notebook을 실행한 브라우저에서 바로 그림을 볼 수 있도록 브라우저 내부에 그려지도록 함

  → 도표, 그래프, 그림, 소리, 애니메이션 등과 같은 산출물(Rich output)을 표현하는 방법 중 하나

 

- plt.legend() : 그래프에 범례 추가 (plot 함수 안에 입력한 label이 사용)

  plt.legend(('Y1', 'Y2')) 와 같이 legend 함수 안에 label 직접 입력 가능


02장. 대리점 데이터를 가공하는 테크닉 10

전제조건

→ 상품 A~Z까지 26개의 상품 취급

→ 매출 이력과 고객정보 데이터는 담당사원이 시스템에 직접 입력 : '지저분한 데이터'

데이터 정보


<011. 데이터를 읽어 들이자>

import pandas as pd
uriage_data = pd.read_csv("uriage.csv")
uriage_data.head()

매출 이력

 

- item_name, item_price에 결측치/오류 확인 가능 

kokyaku_data = pd.read_excel("kokyaku_daicho.xlsx")
kokyaku_data.head()

고객 정보

 

→ 데이터의 정합성에 문제가 있음 : 입력 오류나 표기 방법의 차이가 부정합을 일으킴

ex) 날짜 포맷 차이, 공백 유무

→ 정합성을 갖추기 위해 데이터의 속성과 의미를 이해하고 데이터의 오류를 파악해야 함


<012. 데이터의 오류를 살펴보자>

- 매출 이력에서 item_name을 추출해 데이터 오류 확인

uriage_data["item_name"].head()

 

- 공백, 대소문자 섞여있음

uriage_data["item_price"].head()

- 결측치 NaN 확인 가능


<013. 데이터에 오류가 있는 상태로 집계해 보자>

데이터 정합성의 중요성 파악

 

- 매출 이력 데이터에서 상품별로 월 매출 합계 집계

uriage_data["purchase_date"] = pd.to_datetime(uriage_data["purchase_date"])
uriage_data["purchase_month"] = uriage_data["purchase_date"].dt.strftime("%Y%m")
res = uriage_data.pivot_table(index = "purchase_month", columns = "item_name", aggfunc = "size", fill_value = 0)
res

 

- 날짜를 연월 형태로 반환

- pivot_table (구입 연월)와 칼럼(삼품 이름) 지정

→ 상품의 건수로 집계

- aggfunc 집계 방법(size) 지정

- fill_value = 0 : 집계 후, 결과 피벗 테이블에서 누락된 값을 바꿀 값을 0으로 설정

 

- 동일한 상품이 다른 상품으로 집계됨, 26개의 상품이 99개의 상품으로 늘어남

 

res = uriage_data.pivot_table(index = "purchase_month", columns = "item_name", values = "item_price", aggfunc = "sum", fill_value = 0)
res

 

- values 집계하고 싶은 칼럼(item_price)을 지정해서 집계

 

→ 데이터 가공 없이 집계 및 분석을 실시하면 의미 없는 결과가 나옴! 


<014. 상품명 오류를 수정하자>

 

- 공백 오류 수정하기

print(len(pd.unique(uriage_data.item_name)))

99

 

→ 26개의 상품이 99개로 늘어나 있음

- len + pd.unique로 매출 이력의 item_name의 중복을 제외한 데이터 건수 확인 가능

더보기
print(uriage_data["item_name"].nunique())

 

- len + pd.unique를 nunique로 대체 가능

 

- 데이터 오류 수정하기

uriage_data["item_name"] = uriage_data["item_name"].str.upper()
uriage_data["item_name"] = uriage_data["item_name"].str.replace("  ", "")
uriage_data["item_name"] = uriage_data["item_name"].str.replace(" ", "")
uriage_data.sort_values(by = ["item_name"], ascending = True)

 

- str.upper()로 소문자를 대문자로 변환

- str.replace()공백 제거

- item_name을 기준으로 정렬해 화면에 표시

 

더보기

<문자열 내장 함수>

문자열.replace(기존 값, 변경할 값, 변경횟수)

문자열.upper() : 소문자를 대문자로 변환

문자열.lower() : 대문자를 소문자로 변환

 

print(len(pd.unique(uriage_data["item_name"])))
print(pd.unique(uriage_data["item_name"]))


<015. 금액의 결측치를 수정하자>

상품 단가가 집계 중에 변하지 않았기 때문에 프로그램으로 결측치 수정 가능

 

- 결측치 확인하기

uriage_data.isnull().any(axis = 0)

 

- isnull() 을 이용해 매출 이력 데이터의 결측치 유무 확인 가능

- item_price에 결측치가 있음을 확인할 수 있음

더보기

- 데이터프레임.isnull() : 데이터프레임에서 결측치가 있는지 확인

→ 결측치 있으면 True, 없으면 False로 표시

 

- any() : True가 하나라도 존재하는지 확인하는 함수

axis = 0  : 열(column)단위로 연산 → 각 열을 기준으로, 해당 열에 하나라도 결측치가 있으면 True, 그렇지 않으면 False 반환하므로 이를 통해 열별로 결측값이 존재하는지 여부 확인 가능

axis=1 : 행(row) 단위로 연산    각 행을 기준으로 계산

 

- all() : 모든 값이 True일 때만 True 반환

 

- 결측치 수정하기

→ 집계 기간에 상품 단가의 변동이 없다는 전제조건이 있으므로, 같은 상품의 단가를 이용해 수정 가능

flg_is_null = uriage_data["item_price"].isnull()
for trg in list(uriage_data.loc[flg_is_null, "item_name"].unique()):
    price = uriage_data.loc[(~flg_is_null) & (uriage_data["item_name"] == trg), "item_price"].max()
    uriage_data["item_price"].loc[(flg_is_null) & (uriage_data["item_name"] == trg)] = price
uriage_data.head()

 

- item_price 중에 결측치 있는 곳 조사하여 flg_is_null 변수에 저장 (True, False로 저장)

 

- list(uriage_data.loc[flg_is_null, "item_name"].unique()) : flg_is_null을 이용해 결측치가 있는 상품명 리스트 작성

list() : 변수의 값을 리스트 형식으로 변환

loc 함수 : 조건이 일치하는 데이터 추출

  - '금액에 결측치가 있다'를 조건으로 하기 위해 flg_is_null로 조건 지정 (flg_is_null이 True 인 행)

  - 조건과 일치하는 데이터 중에서 어떤 칼럼(item_name)을 가져올지 지정 가능

    → 결측치가 존재하는 상품명 추출

  - unique() : 추출한 상품명에서 중복을 제거하여 고유한 상품명 리스트 제작

 

- price = uriage_data.loc[(~flg_is_null) & (uriage_data["item_name"] == trg), "item_price"].max()

→ 결측치가 있는 상품과 같은 상품이며 금액이 올바르게 입력된 행을 loc으로 찾고, 그 금액을 가져옴

  - loc()의 조건 : 부정 연산자 ~을 이용 ( flg_is_null == False )

  - 현재 상품의 결측치가 없는 item_price 값 중에서 최대값을 찾아 결측치를 채우기 위해 price에 저장

 

- uriage_data["item_price"].loc[(flg_is_null) & (uriage_data["item_name"] == trg)] = price

→ 가져온 금액으로 데이터 수정

  - 매출 이력의 item_price 칼럼에서 loc()으로 결측치가 있는 데이터 추출 후, 금액 데이터 price로 대체

→ 결측치가 있는 상품명에 대해 위에서 찾은 최대 가격을 해당 상품의 결측치에 할당

 

 

- 검증하기

uriage_data.isnull().any(axis = 0)

 

- item_price 의 결측치가 없어진 것을 확인 가능

 

for trg in list(uriage_data["item_name"].sort_values().unique()):
    print(trg + "의최고가:" + str(uriage_data.loc[uriage_data["item_name"]==trg]["item_price"].max()) 
          + "의최저가:" + str(uriage_data.loc[uriage_data["item_name"]==trg]["item_price"].min(skipna=False)))

 - 모든 상품에 대해 반복문 처리

- 모든 상품의 최대금액과 최소금액이 일치하는 것으로 봐서 성공적으로 금액을 수정했다는 것을 알 수 있음

skipna : NaN의 무시 여부 설정 가능 , False로 지정하여 NaN이 존재할 경우 최솟값이 NaN으로 표시되도록 함


<016. 고객 이름의 오류를 수정하자>

- 데이터 확인하기

kokyaku_data["고객이름"].head()

uriage_data["customer_name"].head()

 

- 두 데이터 서식이 일치하지 않으므로 결합 불가능

 

kokyaku_data["고객이름"] = kokyaku_data["고객이름"].str.replace("  ", "")
kokyaku_data["고객이름"] = kokyaku_data["고객이름"].str.replace(" ", "")
kokyaku_data["고객이름"].head()

 

- str.replace()로 고객 정보의 고객 이름에서 공백 제거

 f. 같은 이름의 데이터가 존재하는 경우 등록일, 생년월일 등 다른 정보를 이용해서 구분해야 함


<017. 날짜 오류를 수정하자>

- 고객 등록일 오류 수정하기

고객 정보

 

엑셀 데이터로, 서식이 다른 데이터가 섞여 있을 수 있으니 주의 필요

 

- 날짜 동일한 포맷으로 통일하기

flg_is_serial = kokyaku_data["등록일"].astype("str").str.isdigit()
flg_is_serial.sum()

22

 

- str.isdigit() : 고객 정보의 등록일이 숫자인지 아닌지 판정

→ 숫자로 된 곳을 flg_is_serial에 저장

더보기

.astype("str") : 열의 값을 모두 문자열로 변환

→ 날짜 데이터의 경우 문자열로 변환하고, 결측치(NaN)은 "nan"이라는 문자열로 변환

 

str.isdigit() : 각 문자열이 숫자로만 이루어졌는지 확인하는 함수

→ 문자열의 모든 문자가 숫자(0~9)로만 이루어졌다면 True를, 그렇지 않다면 False를 반환

→ 공백, 기호, 날짜형식(ex. 2022-08-15), 결측치(NaN) 등은 Flase

 

fromSerial = pd.to_timedelta(kokyaku_data.loc[flg_is_serial, "등록일"].astype("float"), unit="D") + pd.to_datetime("1900/01/01")
fromSerial

 

 

엑셀에서 사용하는 시리얼 날짜 형식을 변환하여 Pandas에서 처리하는 과정

(엑셀에서 날짜는 1900년 1월 1일부터의 경과일수를 숫자로 기록하는 방식으로 표현하므로, 이 시리얼 넘버를 실제 날짜로 변환해야 함)

 

 

  loc() : flg_is_serial이 True인 행의 "등록일" 값 추출 ( 숫자로만 이루어진 시리얼 날짜 값 선택)

- .astype("float") : 추출한 "등록일" 값을 실수형으로 변환

  → 엑셀 시리얼 날짜 값은 거의 정수로 표현되지만, 소수점을 포함하여 시간을 나타낼 수 있기에 실수형으로 변환하는 것이 적합 (ex. 2.5 = 1900년 1월 2일 오후 12시)

- pd.to_timedelta() 함수 : 시간의 차이를 표현하는 함수

  unit ="D" : 일(Day) 단위로 계산한다는 의미

  → 일 단위로 숫자를 날짜로 변환

- pd.to_datetime("1900/01/01") : 1900/01/01 을 1로 기준 날짜 설정

  → 시리얼 넘버로 계산된 시간 차이에 '+' 로 더함으로써 실제 날짜 계산

 

더보기

<이틀이 어긋난 이유>

 

fromSerial = pd.to_timedelta(kokyaku_data.loc[flg_is_serial, "등록일"].astype("float") - 2, unit="D") + pd.to_datetime("1900/01/01")
fromSerial

 

- 엑셀 숫잣값에서 2를 빼서 처리

fromString = pd.to_datetime(kokyaku_data.loc[~flg_is_serial, "등록일"])
fromString

 

- 날짜로 변환된 데이터도 서식 통일

- 2018/01/04와 같이 슬래시로 구분된 서식을 하이픈으로 구분된 서식으로 통일하기 위한 처리 과정

- ~flg_is_serial : 부정 연산자 ~로 Flase인 것을 loc()함수의 조건으로 설정

→ 숫자가 아닌 값들로 이루어진 데이터도 pd.to_datetime을 이용해 날짜 형식으로 변환

 

kokyaku_data["등록일"] = pd.concat([fromSerial, fromString])
kokyaku_data

 

 

- 숫자를 날짜로 수정한 데이터(fromSerial)와 서식을 변경한 데이터(fromString)를 결합해서 데이터 갱신

 

kokyaku_data["등록연월"] = kokyaku_data["등록일"].dt.strftime("%Y%m")
rslt = kokyaku_data.groupby("등록연월").count()["고객이름"]
print(rslt)
print(len(kokyaku_data))

 

- 수정한 날짜에서 strftime을 이용해 등록연월을 작성하고 groupby()로 집계

데이터프레임.groupby(그룹이되는컬럼).집계함수()[계산하고 싶은 컬럼]

→ groupby 후, count()로 데이터 개수 집계

 

flg_is_serial = kokyaku_data["등록일"].astype("str").str.isdigit()
flg_is_serial.sum()

0

 

- 등록일 칼럼에 숫자 데이터가 남아 있는지 다시 확인해보면 모든 숫자 데이터가 날짜로 잘 수정되었다는 것을 알 수 있음!


<018. 고객 이름을 키로 두 개의 데이터를 결합(조인)하자>

join_data = pd.merge(uriage_data, kokyaku_data, left_on="customer_name", right_on="고객이름", how="left")
join_data = join_data.drop("customer_name", axis=1)
join_data

 

 

- pd.merge() : 두 개의 데이터의 서로 다른 열을 지정하여 결합

- left_on 의 인수로 uriage_data의 customer_name 칼럼을 키로, right_on의 인수로 kokyaku_data의 고객 이름 칼럼을 키로 지정

- how로 결합 방법을 left로 지정 (  uriage_data을 기준으로  일치하는 kokyaku_data를 결합 )

 

- join_data.drop("customer_name", axis=1)

→ "고객 이름"과 중복되는 customer_name 열 제거

 

데이터 정제 완료!


<019. 정제한 데이터를 덤프하자>

정제한 데이터를 파일로 출력(덤프)해두고, 분석할 때 위 파일을 읽어 들이는 형식으로 데이터 분석 진행

 

- 먼저 칼럼의 배치를 다루기 쉽게 조정하기

dump_data = join_data[["purchase_date", "purchase_month", "item_name", "item_price", "고객이름", "지역", "등록일"]]
dump_data

 

- join_data에서 필요한 칼럼을 임의의 순서로 정렬 후, dump_data에 저장

 

dump_data.to_csv("dump_data.csv", index=False)

 

- to_csv를 이용해 파일로 출력


<020. 데이터를 집계하자>

 

import_data = pd.read_csv("dump_data.csv")
import_data

 

- 덤프 데이터 불러오기

 

byItem = import_data.pivot_table(index="purchase_month", columns="item_name", aggfunc="size", fill_value=0)
byItem

 

- purchase_month를 세로축으로 상품별로 집계

byPrice = import_data.pivot_table(index="purchase_month", columns="item_name", values="item_price", aggfunc="sum", fill_value=0)
byPrice

 

- purchase_month를 세로축으로 매출금액별로 집계

 

byCustomer = import_data.pivot_table(index="purchase_month", columns="고객이름", aggfunc="size", fill_value=0)
byCustomer

 

- purchase_month를 세로축으로 고객별로 집계

 

byRegion = import_data.pivot_table(index="purchase_month", columns="지역", aggfunc="size", fill_value=0)
byRegion

 

- purchase_month를 세로축으로 지역별로 집계

 

away_data = pd.merge(uriage_data, kokyaku_data, left_on="customer_name", right_on="고객이름", how="right")
away_data[away_data["purchase_date"].isnull()][["고객이름", "등록일"]]

 

- 집계 기간에 구매 이력이 없는 사용자 확인

- right join으로 결합

→ 집계 기간 내에 구매하지 않은 고객은 NaN으로 채워짐