파이썬 데이터 분석 실무 테크닉 100 -데이터 가공(2)

1부 데이터 가공

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

 분석 목표 : '지저분한' 데이터를 처리하는 경험 쌓고, 실제 현장에서 여러 가지 상황에 대처할 수 있는 능력 키우기

 

 

전제 조건

  • 상품 A ~ Z까지 26개의 상품 취급
  • 매출 일과 고객정보 데이터를 직접 입력
No. 파일 이름 개요
1 uriage.csv 매출이력
기간은 2019년 1월 ~ 2019년 7월
2 kokyaku_daicho.xlsx 대리점에서 관리하는 고객 정보

 

 

테크닉011. 데이터 읽기

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

결과값은 다음과 같다.

똑같이 다른 데이터도 불러온다.

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

 

실행결과를 확인하면 매출 이력 데이터에서 item_name이나 item_price에 결측치나 오류가 보이는 것을 확인할 수 있다.

이처럼 데이터에 나타나는 입력 오류나 표기 방법의 차이가 부정합을 일으킬 때 '데이터의 정합성에 문제가 있다'라고 한다.

정합성을 갖추기 위해서는 먼저 데이터의 속성이나 의미를 이해해야 한다. 

 

 

테크닉012. 데이터 오류 살펴보기

uriage_data['item_name'].head()

 

item_name을 추출해서 보면 '상품A', '상 품 a', '상품a'등 각각 다른 상품으로 집계되어 원래 하나인 '상품A'에 관한 정확한 집계가 불가능하다.

 

uriage_data['item_price'].head()

 

마찬가지로, item_price도 결측치 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

 

1~2행에서 날짜를 연월 형태로 변환하고 3행에서 세로축에 구입 연월, 가로축에 상품의 건수로 집계한 후, 4행에서 집계 결과를 표시한다. 상품의 개수는 26개이지만 오류가 있는 상태로 집계를 진행하면 99개가 나온 것을 알 수 있다.

마찬가지로 가로축에 item_price를 설정해서 집계해보자.

 

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

 

이렇게 데이터가 오류가 있는 상태로 집계 및 분석을 실시하면 전혀 의미 없는 결과가 나오기 때문에 데이터 가공이 분석의 전처리로 얼마나 중요한지 확인할 수 있다.

 

 

테크닉014. 상품명 오류 수정하기

 

상품명의 유니크 수를 확인해보자.

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

원래 A ~ Z까지 26개 상품이 99개로 늘어나 있는 것을 확인할 수 있다.

이제 데이터의 오류를 수정해보자.

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)

 

1행에서 상품명에 있는 소문자를 str.upper()를 이용해 대문자로 변환하고, 2~3행은 str.replace()로 공백을 제거한다. 4행에서는 데이터를 item_name 순으로 정렬한다. 데이터를 보면 바르게 수정된 것처럼 보이지만, 반드시 결과를 검증해야 한다.

 

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

 

unique() 함수로 상품명과 그 개수를 가져온다. A~Z까지 26건의 상품으로 통일되어 오류가 없어진 것을 확인할 수 있다.

 

테크닉015. 금액의 결측치 수정

 

데이터에 결측치가 있는지 확인하자.

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

 

item_price 값에 결측치가 있는 것을 확인할 수 있다. 그럼 금액의 결측치를 수정해보자. 전제 조건에 집계 기간에 상품 단가의 변동이 없다는 것이 있었기 때문에 같은 상품의 단가를 이용하면 수정이 가능하다.

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()

 

1행에서 item_price 중에 결측치가 있는 곳을 조사한다. 그리고 flg_is_null 변수에 어떤 행에 결측치가 있는지를 저장한다. 2행의 반복문 처리에는 unique()함수를 이용한다. 앞에서 생성한 flg_is_null을 이용해서 결측치가 있는 상품명 리스트를 작성한다. list()는 변수의 값을 리스트 형식으로 변환한다. 그리고 loc 함수는 조건에 일치하는 데이터를 추출한다.

2번째 item_name은 조건과 일치하는 데이터 중에서 어떤 칼럼을 가져올지를 지정하고 결측치가 존재하는 상품명을 추출한다. 마지막 unique()는 추출한 상품명에서 중복을 제거한다.

 

오류가 처리되었는지 검증을 실행한다.

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

 

결측치가 없어진 것을 확인할 수 있다.

 

다음으로, 각 상품의 금액이 정상적으로 수정됐는지 확인한다.

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)))

1행에서 모든 상품에 대해서 반복문 처리를 한다. 반복문에서는 상품에 설정된 최대 금액과 최소 금액을 출력해서 보여준다. min(skipna = False)에서 skipna는 NaN의 무시 여부를 설정한다. False로 지정했기 때문에 NaN이 존재할 경우 최솟값이 NaN으로 표시된다.

 

테크닉016. 고객 이름의 오류 수정하기

 

데이터를 확인해보면 고객 이름에는 성과 이름 사이에 공백이 있지만, customer_name에서의 이름에는 공백이 없다. 이것을 통일시켜야 한다.

 

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

 

상품명 수정에도 사용했던 str.replace() 함수로 공백을 제거하고 결과를 표시한다.

 

테크닉017. 날짜 오류 수정

 

테크닉011에서 확인한 등록일을 보면 '42782'처럼 날짜 형식이 아닌 숫자가 몇 개 보인다. 이것을 동일한 포맷으로 통일시켜야 한다.

 

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

 

22건의 숫자 데이터가 있음을 알 수 있다. 그럼 숫자로 등록된 부분을 수정해보자.

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

pd.to_timedelta() 함수를 이용해서 숫자를 날짜로 변환한다. loc()를 이용해서 flg_is_serial 조건으로 데이터를 추출하고 날짜로 변경한다. 숫자가 날짜형으로 변환된 것을 확인할 수 있다.

 

다음으로 날짜로 변환된 데이터도 서식을 통일시켜보자.

 

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

숫자를 날짜로 수정한 데이터와 서식을 변경한 데이터를 결합하여 데이터를 갱신한다.

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

숫자를 날짜로 수정한 데이터 fromSerial과 서식을 변경한 데이터 fromString을 concat으로 결합하고 원래 '등록일'에 대입해 갱신한다. 결과를 보면 등록일이 깨끗하게 수정된 것을 알 수 있다.

 

테크닉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

left_on의 인수로 customer_name 칼럼을 키로 지정하고, right_on의 인수로는 고객 이름 칼럼을 키로 지정한다. how는 결합 방법으로 left로 지정한다. 이것은 uriage_data를 기준으로 kokyaku_data를 결합한다는 의미이다. 이로써 데이터 정제를 마무리했다.

 

 

테크닉019. 정제한 데이터를 덤프하자

 

 

테크닉020. 데이터를 집계하자

 

우선 덤프 파일을 읽어 들이자.

 

계속해서 purchase_month를 세로축으로 해서 상품별로 집계해 보자.

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

하단에 26 columns라고 적혀 있으므로 정확하게 상품 A~Z까지 26개의 상품이 구입 연월 별로 집계된 것을 알 수 있다.

계속해서 purchase_month를 세로축으로 지정하고, 매출 금액, 고객, 지역을 집계해 보자.

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

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

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

 

마지막으로, 집계 기간에 구매 이력이 없는 사용자를 확인해 보자.

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

집계 기간 내 이탈 고객

 

데이터를 분석함에 있어 가장 중요한 데이터 가공의 기초를 배웠다. 데이터의 상태를 살펴보고 어떻게 가공할 것인가를 잘 상의하고 가공해야 한다.

TAGS.

Comments