Python/데이터 분석(전처리, 시각화)

파이썬(10)-Boxplot, barplot으로 데이터분석

두설날 2024. 5. 29. 09:21

*이 글을 읽기전에 작성자 개인의견이 있으니, 다른 블로그와 교차로 읽는것을 권장합니다.*

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
park = pd.read_csv('/content/drive/MyDrive/KDT/5. 데이터 분석/데이터/전국도시공원표준데이터.csv', encoding='ms949')
park
# UnicodeDecodeError: 'utf-8' codec can't decode utf-8 인코딩 오류 -> ms949로 인코딩 변경

park.shape

!sudo apt-get install -y fonts-nanum
!sudo fc-cache -fv
!rm ~/.cache/matplotlib -rf
plt.rc('font', family='NanumBarunGothic')

column 가지치기

# 데이터분석할때, info()로 column에서 뭘 표현하는지 알고 필요없는 column을 걸려야 함.
# 맨 마지막에 object Dtype을 어떻게 모두 숫자(numeric, int, float)로 바꿔야 할 것.
park.info()

park.columns

column 삭제하는 방식으로 소거

# 아까는 column을 거르는 방법이였다면, 이번엔 column을 삭제하는 식으로 소거
# inplace=True 는 원본 데이터프레임 변경 True
park.drop(columns=['공원보유시설(운동시설)', '공원보유시설(유희시설)', '공원보유시설(편익시설)', '공원보유시설(교양시설)', '공원보유시설(기타시설)', '지정고시일', '관리기관명', 'Unnamed: 19'], inplace=True)
park.head()

park.plot.scatter(x='경도', y='위도', figsize=(8,10), grid=True)

sns.boxplot() : 데이터의 분포를 boxplot모형으로 시각화하여 표현

  • 최소값, 1사분위수(Q1), 중위수(중앙값, Q2), 3사분위수(Q3), 최대값 순으로 표현
  • 이상치: 최소값, 최대값 범위밖의 수치, 산점도
# boxplot
# 데이터의 분포를 시각화하는데 유용
# 주로 데이터의 중앙값(=중위수), 사분위수(Q1[25%], Q2[50%], Q3[75%], Q4[100%]), 이상치 등을 표현하는데 활용
# 상자: Q1과 Q3 사이의 범위
# 상자 내부 선: Q2(중앙값)
# IQR: Q3-Q1, 데이터의 변동성을 평가하는데 사용
# 수염: 데이터의 최소값과 최댓값, 아래 수염(Q1-1.5*IQR), 위 수염(Q3+1.5*IQR)
# 이상치: 수염 범위를 벗어난 값, 수치
sns.boxplot(y=park['위도'])

sns.boxplot(x=park['경도'])

Series 판단시, or 표시는 |(역슬래시) 사용

# 위도와 경도의 이상치로 판별되는 데이터 확인
# Series 판단할 때 or는 | 표시
park_loc_error = park.loc[(park['위도']<32) | (park['경도']>132)]
park_loc_error

Series에서도 and 표시는 & 사용

park = park.loc[(park['위도']>=32) & (park['경도']<=132)]
park.shape

park.head()

# '소재지도로명주소'가 입력되지 않고, '소재지지번주소'만 입력된 데이터를 확인
park.loc[park['소재지도로명주소'].isnull() & park['소재지지번주소'].notnull()]

# '소재지도로명주소'와 '소재지지번주소'가 모두 입력되지 않은 데이터를 확인
park.loc[park['소재지도로명주소'].isnull() & park['소재지지번주소'].isnull()]

DataFrame 없는 것 확인

# '소재지도로명주소'가 입력되지 않은 데이터는 '소재지지번주소'로 대신 채움
# fillna()를 쓸때는 반드시 inplace=True를 작성
park['소재지도로명주소'].fillna(park['소재지지번주소'], inplace=True)

park.loc[park['소재지도로명주소'].isnull() & park['소재지지번주소'].notnull()]

str.split() : 문자열 분리

# park['소재지도로명주소'].str.split(' ')
# 예) [부산광역시, 강서구, 구랑동, 1199-7]
# park['소재지도로명주소'].str.split(' ',expand=True)
# 데이터프레임으로 데이터가 분리되고 인덱싱과 슬라이싱이 가능
# column 분리
park['소재지도로명주소'].str.split(' ', expand=True)

str.split()을 통해 단어 단위로 해당 column안의 value값들 문자열 분리.

# '소재지도로명주소'에서 '시도'만 추출하여 '시도' 파생변수를 생성
park['시도'] = park['소재지도로명주소'].str.split(' ', expand=True)[0]
park.head()

park['시도'].value_counts()

# '시도'에 '강원'을 '강원도'로 변경
park['시도'][park['시도']=='강원'] ='강원도'

park['시도'].value_counts()

시도 column에서 강원 변경 확인

plt.figure(figsize=(8,10))
sns.scatterplot(data=park, x='경도', y='위도', hue='시도')

# value_counts() 안에 매개변수로 넣는 값들로 달라지는 것들
park_sido = pd.DataFrame(park['시도'].value_counts())
park_sido

pd.DataFrame으로 해당 value값을 column으로 .value_counts()으로 value값으로 갖는 새로운 DataFrame 만들기.

# value_counts(normalize=True) : 전체합계에 대한 비율(%)이 계산
# 전체 합이 1이 되는 비율로 계산함.
park_sido_normalize = pd.DataFrame(park['시도'].value_counts(normalize=True))
park_sido_normalize

# value_counts(ascending=False): 내림차순으로 정렬, 순위보기가 편하게 만들어줌.
park_sido_ascending = pd.DataFrame(park['시도'].value_counts(ascending=False))
park_sido_ascending

# 시도별 합계 데이터(park_sido)와 비율 데이터(park_sido_normalize)를 병합
# Index이름이 지역명이라서 합칠 수 있어용.
# .concat()
pd.concat([park_sido, park_sido_normalize], axis=1)

# .merge(): merge는 column명을 새로 정립하기
# left_index와 right_index는 왼쪽 오른쪽 데이터프레임의 index를 선택한다는 의미
# reset_index()는 index 재생성하기
park_sido = park_sido.merge(park_sido_normalize, left_index=True, right_index=True).reset_index()
park_sido

# columns 이름 변경
park_sido.columns = ['시도', '합계', '평균']
park_sido

# 막대그래프
plt.figure(figsize=(6,4))
plt.xticks(rotation=45) #x축 각도
sns.barplot(data=park_sido, x='시도', y='합계')

# 막대그래프
plt.figure(figsize=(6,4))
# column명이 index이름과 동일하기 때문에, column이름=index이름이라서 x,y축 이름을 바꾸면 서로 도치됨.
sns.barplot(data=park_sido, x='합계', y='시도')