자신에게 친절할 것 :)

Data Science/Pandas

[pandas] 데이터 전처리 및 분석 연습1

Tashapark 2024. 7. 4. 23:53
728x90
반응형

# 코드잇 데이터 사이언티스트 강의 듣는 중

 


<제주도 관광관련 데이터로 이것저것 확인해보기>

[코드잇 강의 가이드 라인]

1. 데이터 불러오기
data 폴더 안에 있는 jeju_card.csv 파일을 DataFrame으로 불러옵시다.

2. 데이터 탐색 및 전처리
데이터를 간단히 탐색하고 전처리해 봅시다.
- 데이터 개수, 컬럼별 데이터 타입, 통계 정보, 결측값 존재 여부 등을 확인해 보세요.
- 각 컬럼이 어떤 값들로 이루어져 있는지 확인해 보세요.
- 2017년과 2018년의 데이터만 추출해 주세요.

3. 데이터 분석
연월별 카드 이용 추이를 비교해 보세요.
- 2017년과 2018년, 두 연도 사이에 어떤 차이가 있나요? 왜 그런 차이가 나는지도 한번 확인해 보세요.

연령대별로 카드 이용에 어떤 차이가 있는지 비교해 보세요.
- 이용자수, 소비금액, 1회당 소비금액을 비교해 보세요.
- 연령대별로 어떤 업종에 많은 금액을 지출하는지 확인해 보세요.
- 연령대별로 연월별 카드 이용 추이를 확인해 보세요.

더 알아보고 싶은 게 있다면 원하는 대로 자유롭게 데이터를 탐색해 보세요!

 

- 위는 코드잇 실습 데이터 연습 시 요청 과제였고, 아래에는 내가 떠올리지 못했던 방법들을 해설 노트를 보고 정리할 것.

 


<데이터 탐색  및 전처리 >

-  .shape 활용

 

- 나는 그냥 냅다 .head()로만 로나, 칼럼 형태와 마지막에 나오는 개수를 확인했는데, 강의 노트에는 shape을 활용하고, head()를 씀

 

-  .describe(include='all')

-.info() 결측치를 활용하고 .describe()로 대략적 분포를 활용한 것은 같지만, include='all' 파라미터로 문자형 데이터들까지 확인하지 않았음. 

 

-  타입 칼럼의 이름 구분

- 이 부분이 상당히 인상적이었는데, 'object' 타입만 모아서 칼럼 명을 뽑아줘서 이를 비교하려고 한 것이었음. 

object_columns = jeju_card_df.columns[jeju_card_df.dtypes == 'object']
object_columns

#값
Index(['시도명', '시군구명', '지역구분', '업종명', '이용자 구분', '연령대', '성별', '연월'], dtype='object')

 

- index 자료형의 경우, 리스트와 비슷하게 생겼는데 리스트처럼 안에 들어있는 각 요소에 차례대로 접근할 수 있음. 

-so, for문을 이용 가능함. 

- for문에 unique()함수를 활용해서 각각의 값들만 뽑아낼 수가 있음. 

for col in object_columns:
    print(col)
    print(jeju_card_df[col].unique(), '\n')


#값
시도명
['제주도'] 

시군구명
['제주시' '서귀포시'] 

지역구분
['읍면' '도심'] 

업종명
['유흥' '식음료' '숙박' '쇼핑' '소매' '문화/레져' '교통' '기타'] 

이용자 구분
['제주도민' '내국인관광객'] 

연령대
['60대이상' '50대' '40대' '30대' '20대미만' '20대' '20 미만'] 

성별
['여' '남'] 

연월
['2018-12' '2018-11' '2018-10' '2018-09' '2018-08' '2018-07' '2018-06'
 '2018-05' '2018-04' '2018-03' '2018-02' '2018-01' '2017-12' '2017-11'
 '2017-10' '2017-09' '2017-08' '2017-07' '2017-06' '2017-05' '2017-04'
 '2017-03' '2017-02' '2017-01' '2016-12' '2016-11' '2016-10' '2016-09']

 

--> 이 방법은 전반적인 데이터 구조를 활용하기에 너무 좋은 방법으로 여겨짐. 

 

- 일단 여기까지 해설 코드를 쭉 쓰면

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

#값 불러오기
jeju_card_df = pd.read_csv('data/jeju_card.csv')

#데이터 구조확인
jeju_card_df.shape

jeju_card_df.head()

jeju_card_df.info()

#문자형도 확인
jeju_card_df.describe(include='all')

#문자형 데이터 타입 벡터만 모아서 변수 만들기
object_columns = jeju_card_df.columns[jeju_card_df.dtypes == 'object']

#칼럼 별로, 유니크한 값들만 출력
for col in object_columns:
    print(col)
    print(jeju_card_df[col].unique(), '\n')

 

-  2017, 18 데이터만 추출하기

- 이 부분이 나랑 다르게 했는데, 본 데이터를 나두려고 17, 18에 해당 하는 값을 따로 추출했음. 

- 내 ver.

#데이트 타입으로 바꿈
jeju_df['연월'] = pd.to_datetime(jeju_df['연월'])

#연도 순서로 맞춰줌
jeju_df = jeju_df.set_index('연월').sort_index()

#2017, 2018만 빼서 값을 만들어줬음
jeju_17_to_18 = jeju_df.loc[['2017', '2018']]

 

- 해설 ver

#연도 값을 우선 분리함.
jeju_card_df['연도'] = jeju_card_df['연월'].str.split('-').str[0]

#2016이 아닌 데이터만 추출해서 다시 데이터에 담아줬음. 
jeju_card_df = jeju_card_df[jeju_card_df['연도'] != '2016']

 

--> 이 방법이 간단하고 전체 데이터를 전부 볼 수는 있지만, 이러면 되돌아가기가 어렵기 때문에,

--> 나라면 데이터 프레임을 또 만들 것임. 무조건. 원본은 살아있어야 함. 

 

-  연령대 처리

- 나는 10대로 묶어 줬는데 해설은 한 값으로 통일시켜줬음. 

#10대로 묶어주기
condition = (jeju_df['연령대'] == '20대미만') | (jeju_df['연령대'] == '20 미만')
jeju_df.loc[condition, '연령대'] = '10대'
jeju_df['연령대'].unique()
#값
array(['20대', '60대이상', '30대', '40대', '50대', '10대'], dtype=object)

 

- 해설 ver

- 값 합칠 때 그냥 , 로만 해도 된다는 것을 지금 처음 알았음. 

#20대미만으로 묶기
jeju_card_df.loc[jeju_card_df['연령대'] == '20 미만', '연령대'] = '20대미만'

jeju_card_df['연령대'].unique()
#값
array(['60대이상', '50대', '40대', '30대', '20대미만', '20대'], dtype=object)

 


<연월별 이용 금액 시각화>

-  .gorupby

 

- 나는 아까 만들어준 변수를 계속 활용했음. 

- 이미, 17-18년도만 추출 했기에 그걸로 계속 만들어줬음. 

++ 이렇게 하면 안됌.. 어디서 부터 잘못됐는지 모르겠으나, 연도가 다 01로 되어 있어서.. 월별을 볼 수가 없음. 

#날짜별 값 계산되게 resample 처리
jeju_17_to_18.resample('Y')

#년도 별 평균, sum 값을 비교했음.
jeju_17_to_18.resample('Y').mean(numeric_only = True)
jeju_17_to_18.resample('Y').sum(numeric_only = True)

 

- 반면 해설은 이미 연월 값이 17-18도만 남았기에 그걸로 groupby를 해줬음. 

jeju_card_df.groupby('연월').sum(numeric_only=True)

#위는 인덱스 위치에 들어가 있기에 더 편하게 그래프를 만들기 위해서 변수에 따로 담아서, 만들어줌. 
groupby_ym = jeju_card_df.groupby('연월').sum(numeric_only=True).reset_index()
groupby_ym

 

 

--> 위는 인덱스 위치에 들어가 있기에 더 편하게 그래프를 만들기 위해서 변수에 따로 담아서, 만들어줌.

--> 내가 놓친 게 이 부분으로 이것 때문에 그래프가 계속 이상하게 그려지는 데 방법을 못 찾았음..

 

 

 

 

sns.barplot(data=groupby_ym, x='연월', y='이용금액')

 

--> 나 역시 이런 식으로 계속 깨졌음..

--> 그냥 실습에서 나눔고딕만 써있어서.. 생각없이 그것만 해서 인듯. 

--> 애플 고딕인데....

 

 

 

 

 

# Windows
plt.rc('font', family='Malgun Gothic')

# macOS
plt.rc('font', family='AppleGothic')

 

--> 사실 나는 그래프도 예쁘게 안 그려졌지만, 그 상태로도 해석을 하는 것에는 무리가 없다고 판단했기에, 그냥 넘겼음. 

 

- but, 그래프를 조정하면 되는 것인데 생각을 못 한 것. 

- 사이즈를 조정해주고 글자 겹치는 것은 값에 rotation을 주면 되는 것임. 

#플랏 사이즈 조절
plt.rcParams['figure.figsize'] = (10, 5)

#글자 겹치는 것은 xticks()에 rotation을 주면 됨
sns.barplot(data=groupby_ym, x='연월', y='이용금액')
plt.xticks(rotation=90)
plt.title('연월별 카드 이용 금액')

 

 

--> 타이틀도 넣어주면 됨 

--> 그런데 이용금액이 과학적 표기법이라서 읽기가 어렵기 때문에 

--> 1e11 이라서.. 지금 10의 11승을 곱해야 해서 1.50 값이 1,500억을 의미함. 

- so, 표기법을 바꿔줘야 함. 

 

 

 

plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))

 

- y축 값의 형식을 지정해주고 있는데, x를 1억 단위로 나눠서 단위를 1억으로 고쳐줌. 

- 고정 소수점 표기법으로  , --> 천 단위로 ,를 찍는 것을 의미

- .0f --> 소수점 아래 숫자가 0개임을 의미

sns.barplot(data=groupby_ym, x='연월', y='이용금액')
plt.xticks(rotation=90)
plt.title('연월별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> 다시 그리면 이렇게 바뀜. 

--> label 까지 해주면 됨. 

 

--> 이러면 전체 값의 비교가 됨 

--> 둘 다 겨울에 이용금액이 감소하고 봄 여름에 상승하지만, 2018년도에 전반적로 소비액이 감소했음을 확인 가능.

 

 

 

 


<2017년과 2018년 이용 금액 비교>

-  .gorupby

 

-  여기서.. 부터 상당히 꼬이기 시작했음. 그래서 플랏으로 비교하기 보단, 숫자로 확인하기도 했음. 

- 아니면 플랏을 그려도 보기도 힘든데 차이만 확인할 수 있는 정도였음. 

groupby_ym_age = jeju_card_df.groupby(['연도', '연령대']).sum(numeric_only=True).reset_index()
groupby_ym_age

 

--> 아마도.. 반복적으로 reset_index()를 하지 않아서 그런 듯. 

 

 

 

 

 

sns.barplot(data=groupby_ym_age, x='연도', y='이용금액', hue='연령대')
plt.title('연도별/연령대별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> hue도 계속 주고 싶었는데, 내가 짠 코드에서는 계속 에러가 떴었다. 

 

--> 이건 연령대 오름차순이 아직 적용 안되었음. 

 

 

 

 

 

-  .Categorical()

-  범주에 순서를 주고 싶을 때 사용한다고 보면 됨. 파라미터로 ordered = True 를 주면 됨. 

#카테고리컬로 해주고 
jeju_card_df['연령대'] = pd.Categorical(jeju_card_df['연령대'],
                                      categories=['20대미만', '20대', '30대', '40대', '50대', '60대이상'],
                                      ordered=True)

#그래프 다시 그리기 
groupby_ym_age = jeju_card_df.groupby(['연도', '연령대']).sum(numeric_only=True).reset_index()

sns.barplot(data=groupby_ym_age, x='연도', y='이용금액', hue='연령대')
plt.title('연도별/연령대별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

 

--> 사실 큰 차이가 없음. 

--> 그냥 전반적으로 값이 감소한 것임. 

 

 

 

 

 

 

 

- 그래서 제주도민과 내국인관광객의 차이가 있는지 확인하고자 함. 

groupby_ym_user = jeju_card_df.groupby(['연도', '이용자 구분']).sum(numeric_only=True).reset_index()
groupby_ym_user

 

--> 그냥 표로도 충분하지만, 그래프 그림.

 

 

 

 

sns.barplot(data=groupby_ym_user, x='연도', y='이용금액', hue='이용자 구분')
plt.title('연도별/이용자 구분별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

 

--> 둘 다 줄어서 딱히 큰 의미가 없다고 했지만, 

--> 도민의 수는 크게 변화가 없을 것으로 여겨지기에 

이 부분은 확인을 해야 하지 않나 싶음..??

+ 2000억대의 차이는.. 꽤 큰데?

 

 

 

 

 

- 업종의 차이를 확인하고자 함.

groupby_ym_market = jeju_card_df.groupby(['연도', '업종명']).sum(numeric_only=True).reset_index()

sns.barplot(data=groupby_ym_market, x='연도', y='이용금액', hue='업종명')
plt.title('연도별/업종별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

 

-->  기타가 2018년도에 사라짐. 

--> 확인해 볼 필요가 있음. 

 

 

 

 

 

 

 

- 기타를 확인할 필요가 있음. 

jeju_card_df[jeju_card_df['업종명'] == '기타']

 

-->  데이터를 보면 18년도가 아예 없음..

--> 이것은 18년도의 '기타' 업종의 데이터가 누락되어 있는 것으로 여겨짐. 

 

 

 

 

 

 

- unique()해서 제대로 확인해도 없음

jeju_card_df[jeju_card_df['업종명'] == '기타']['연월'].unique()

#값
array(['2017-11', '2017-10', '2017-09', '2017-08', '2017-07', '2017-06',
       '2017-05', '2017-04', '2017-03', '2017-02', '2017-01'],
      dtype=object)

 

- then, 기타를 제외하고 계산하는 것이 더 좋을 수 있음. 

jeju_card_df = jeju_card_df[jeju_card_df['업종명'] != '기타']

- 불린 인덱싱으로 제외하는 것이 더 편한 듯.

- 다시 그림을 그려보면, 

groupby_ym_age = jeju_card_df.groupby(['연도', '연령대']).sum(numeric_only=True).reset_index()

sns.barplot(data=groupby_ym_age, x='연도', y='이용금액', hue='연령대')
plt.title('연도별/연령대별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> 이렇게 하면, 차이가 크지 않음.

 

 

 

 

 

 

 

 

groupby_ym_user = jeju_card_df.groupby(['연도', '이용자 구분']).sum(numeric_only=True).reset_index()

sns.barplot(data=groupby_ym_user, x='연도', y='이용금액', hue='이용자 구분')
plt.title('연도별/이용자 구분별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> 이것도 차이가 없음.

 

 

 

 

 

 

 

 

groupby_ym_market = jeju_card_df.groupby(['연도', '업종명']).sum(numeric_only=True).reset_index()

sns.barplot(data=groupby_ym_market, x='연도', y='이용금액', hue='업종명')
plt.title('연도별/업종별 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> 이것도 차이가 없어서 사실상 연간 차이가 없다고 보는 게 맞을 듯. 

 

 

 

 

 

 

 


<연령대별 이용 금액 시각화>

-  .gorupby

 

-  그룹바이 별로 새로운 데이터를 만들어줌. 

groupby_age = jeju_card_df.groupby('연령대').sum(numeric_only=True).reset_index()
groupby_age

 

--> 파이 차트를 그려줌.

--> 나도 시도는 했는데.. 너무 잘게 쪼개졌고, 어떻게 처리할 지를 몰라서 포기 했음. 

 

 

 

 

 

 

- labes 에 연령대 칼럼을 넣음. 

groupby_age.plot(kind='pie', y='이용자수', labels=groupby_age['연령대'])
plt.title('연령대별 카드 이용자 수 비중')

 

--> 나는 이게 안 됐었는데,, 아마도 데이터 전처리 과정에 실수였던 것 같음. 

 

 

 

 

 

 

 

 

 

 

groupby_age.plot(kind='pie', y='이용자수', labels=groupby_age['연령대'], autopct='%.1f%%')
plt.title('연령대별 카드 이용자 수 비중')

 

 

--> automatic percentage인 autopct를 파라미터로 넣어서 퍼센트를 확인함. 

--> %.1%는 25.3%처럼 소수 첫째 자리까지 표현된 숫자값 뒤에 % 기호를 붙인 형태를 의미함. 

 

 

 

 

 

 

 

 

- 범례 안 겹치게 하기

groupby_age.plot(kind='pie', y='이용자수', labels=groupby_age['연령대'], autopct='%.1f%%')
plt.title('연령대별 카드 이용자 수 비중')
plt.legend(loc='upper left', bbox_to_anchor=(1, 1))

 

--> 그래프의 왼쪽 아래의 좌표가 (0,0)이면 

파이 차트의 가로와 세로의 길이를 각각 1로 보면 (1,1)이 왼쪽 상단의 꼭짓점이 됨. 

 

 

 

 

 

 

 

 

 

 

 

- 이용 금액도 확인. y값만 바꾸면 됨. 

groupby_age.plot(kind='pie', y='이용금액', labels=groupby_age['연령대'], autopct='%.1f%%')
plt.title('연령대별 카드 이용 금액 비중')
plt.legend(loc='upper left', bbox_to_anchor=(1, 1))

 

--> 2-30대는 이용자 수에 비해 이용 금액의 비중이 비교적 작고, 5-60대 이상은 이용자 수에 비해 이용 금액의 비중이 큼. 

 

 

 

 

 

 

 

 

 

 

 

- 인당 이용금액 확인하기 

groupby_age['인당이용금액'] = groupby_age['이용금액'] / groupby_age['이용자수']
groupby_age

 

--> 새로운 칼럼을 만들어줌. 

--> 이건 나도 했음.. 플랏을 계속 실패했지만,,

 

 

 

 

 

sns.barplot(data=groupby_age, x='연령대', y='인당이용금액')
plt.title('연령대별 인당 카드 이용 금액')

 

-->  연령대가 높아질 수록 소비액이 증가하는 것 확인 가능

 

 

 

 

 

 

 

 

 

- 이전에는 억으로 나눠줬지만 굳이 그럴 필요가 없이 , 만 찍어주면 됌. 

 

plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x:,.0f}'))
sns.barplot(data=groupby_age, x='연령대', y='인당이용금액')
plt.title('연령대별 인당 카드 이용 금액')

 

--> 그러면 수정 됌. 

 

 

 

 

 

 

 

 

 

 


<연령대별/업종별 이용 금액 시각화>

-  .gorupby

 

-  그룹바이 별로 새로운 데이터를 만들어줌. 

groupby_age_market = jeju_card_df.groupby(['연령대', '업종명']).sum(numeric_only=True).reset_index()
groupby_age_market.head()

 

--> 그냥 리셋 인덱스는 한 세트라고 생각하면 됨. 

 

 

 

 

 

- 앞에서 활용했던 것 다 넣어서 막대그래프 그리기

sns.barplot(data=groupby_age_market, x='연령대', y='이용금액', hue='업종명')
plt.title('연령대별/업종별 인당 카드 이용 금액')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
plt.ylabel('이용금액(억)')

 

--> hue,,, 가 2개가 아니라 여러 개도 가능임.. 

--> 이렇게 생각못하고 업종을 축으로 넣었던 것 같은데

그래서 그래프가 이상해졌음. 

 

 

--> 20대미만은 값이 너무 작아서 확인이 안됨

 

 

 

 

- 20대미만 확인하려고 인당이용금액 추가해줌. 

- 값이 작아지니깐 굳이 억으로 나눌 필요 없고, 걍 천단위로 , 만 찍어줌

groupby_age_market['인당이용금액'] = groupby_age_market['이용금액'] / groupby_age_market['이용자수']

sns.barplot(data=groupby_age_market, x='연령대', y='인당이용금액', hue='업종명')
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x:,.0f}'))
plt.title('연령대별/업종별 인당 카드 이용 금액')

 

--> ... 가독성이 너무 떨어짐.

 

 

 

 

 

 

 

 

 

- so, 연령대 별로 파이 차트를 그려주는  for문을 만듦

-... 진짜 생각도 못해봤음...

- 파이썬을 잘 해야 해..   for문은 무조건 하나씩 거치게 되는 거니깐.. 맞는 말임...

for age in groupby_age_market['연령대'].unique():
    data = groupby_age_market[groupby_age_market['연령대'] == age]
    data.plot(y='인당이용금액', labels=data['업종명'], kind='pie', autopct='%.1f%%')
    plt.title(f'업종별 인당 이용 금액({age})')
    plt.xticks(rotation=90)
    plt.show()

 

- 이미지는 생략

 


<연령대별/연월별 이용 금액 시각화>

-  .gorupby

 

-  그룹바이 별로 새로운 데이터를 만들어줌. 

groupby_age_ym = jeju_card_df.groupby(['연령대', '연월']).sum(numeric_only=True).reset_index()
sns.barplot(data=groupby_age_ym, x='연령대', y='이용자수', hue='연월')

 

--> hue에 넣을 순 있지만, 사실상 가독성이 제로임..

 

 

 

--> so, 마찬가지로 for 문을 사용해서 연령대별로 반복문을 돌면서 bargraph 를 그리도록 함. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

for age in groupby_age_ym['연령대'].unique():
    data = groupby_age_ym[groupby_age_ym['연령대'] == age]
    sns.barplot(data=data, x='연월', y='이용금액')
    plt.title(f'{age} 연월별 카드 이용 금액')
    plt.xticks(rotation=90)
    plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x/100000000:,.0f}'))
    plt.ylabel('이용금액(억)')
    plt.show()

 

--> .. for문 생각도 안하는 나란 사람...

다음 실습에서는 적용해서 해봐야 겠음...

--> 국비 시작하기 전에 파이썬 강의 많이 들어야 할 듯.. 

728x90
반응형