AI SCHOOL/Python

[Python] 서울 코로나 데이터 분석(EDA) - 1

moru_xz 2023. 1. 30. 20:29

서울시에서 코로나 확진자 데이터 제공 이력

  1. 처음에는 html 형식 1페이지로 제공 -> 데이터 많지 않았기 때문에 
  2. 그 다음에는 여러 페이지 제공
  3. API로 변경
  4. API 페이징 추가 
  5. 요약된 데이터만 제공 

=> 스타트업에서 서비스 운영하는 것과 비슷 : 처음부터 잘 만들어진 서비스를 제공하기 어렵고, 상황이 어떻게 변화할지 예측하기 어렵게 때문에 서비스를 하면서 프로덕트를 보완해 나간다. 

 

데이터 시각화 도구 

라이브러리 로드

# pandas, numpy, matplotlib.pyplot 불러오기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

pandas : 분석

numpy : 다차원 연산

pyplot  : 시각화

 

matplotlib 한글 설정 방법 

import matplotlib.pyplot as plt

# 한글폰트 사용을 위해 설치
!pip install koreanize-matplotlib #pip~ 해서 prompt 설치도 가능

import koreanize_matplotlib

# 그래프에 retina display 적용
%config InlineBackend.figure_format = 'retina' #뚜렷하게

-> 작년에 나온 라이브러리임 koreanize_matplotlib => 간편! (그동안 jupyter notebook에서 한글 폰트 지정해도 오류 났었는데 이 방법으로 하니까 드디어 한글이 보임...

- 아나콘다는 여러 도구를 한번에 설치해 주기도 하지만 가상환경을 제공

- 설치했음에도 불구하고 No Module Not Found 오류가 발생할 때는 보통 여러 버전의 파이썬 혹은 아나콘다 등이 설치되어 있는데 현재 사용하고 있는 위치가 아닌 다른 위치에 설치되었을 때 이런 오류가 발생

- 보통 오류 메시지에 보면 어느 경로에 없다는 메시지가 나오게 됩니다. 해당 경로에 가서 보면 여러 라이브러리가 설치되어있는 폴더를 볼 수 있는데, 해당 위치에 사용하고자 하는 라이브러리를 다운로드 받아 옮겨주면 보통 잘 import 가 됩니다.

- (base) <- 아나콘다의 기본 가상환경이라서 해당 가상환경에 설치해 주면 보통 문제 적게 발생 

 

https://github.com/seongminp/koreanize-matplotlib

 

GitHub - seongminp/koreanize-matplotlib: install & import하는 것만으로 matplotlib에서 한국어를 표시할 수 있습

install & import하는 것만으로 matplotlib에서 한국어를 표시할 수 있습니다. - GitHub - seongminp/koreanize-matplotlib: install & import하는 것만으로 matplotlib에서 한국어를 표시할 수 있습니다.

github.com

 

파일 불러오기

from glob import glob

-> glob는 파일들의 리스트를 뽑을 때 사용

from glob import glob
# 파일 목록 보기
# 파일 이름이 길거나 특수문자 등이 포함되었을 때 직접 타이핑 하면 한 글자라도 틀리면 오류가 남
files = glob('data/seoul-covid*.csv')
file_paths = sorted(files)
file_paths

>> ['data\\seoul-covid19-2021-12-18.csv', 'data\\seoul-covid19-2021-12-26.csv']
df_01 = pd.read_csv(file_paths[0])
df_01.shape

>> (200000, 7)
df_02 = pd.read_csv(file_paths[1])

 

+) 추가 설명

>>> from glob import glob
>>> glob('*.exe')               # 현재 디렉터리의 .exe 파일
['python.exe', 'pythonw.exe']
>>> glob('*.txt')               # 현재 디렉터리의 .txt 파일
['LICENSE.txt', 'NEWS.txt']

https://wikidocs.net/83

위의 glob() 함수는 인자로 받은 패턴과 이름이 일치하는 모든 파일과 디렉터리의 리스트를 반환합니다. 패턴을 그냥 *라고 주면 모든 파일과 디렉터리를 볼 수 있음

 

데이터 합치기 및 중복 제거

# pd.concat 으로 [df_01, df_02] 합치고 df 변수에 할당하기

df = pd.concat([df_01, df_02])
df = df.drop_duplicates()
df

인덱스 값 설정

# shape
df.shape
>> (218646, 7)


# 연번의 nunique()
df.nunique()
>>
연번      218646
환자      218644
확진일        671
거주지         34
여행력        183
접촉력       1417
퇴원현황         2
dtype: int64
# set_index 를 통해 인덱스 값을 변
df = df.set_index('연번')
df = df.sort_index()
df

df.set_index('연번') 두 번 하면 오류 why?
-> 이미 '연번'이 인덱스이기 때문에 '연번' 칼럼이 더이상 없음 

- unique() 는 데이터에 고유값들의 종류를 알고 싶을때

- nunique()는 데이터의 고유값들의 수를 출력

 

연번은 서울, 환자는 전국

 

데이터 요약 / 결측치 보기 / 기술통계

df.info()
# 결측치의 합
df.isnull().sum()
# 결측치의 평균
df.isnull().mean()

#기술통계
df.describe()
df.describe(include = 'object')

날짜 데이터 타입 변경 / 파생변수 만들기

df['확진일'] = pd.to_datetime(df['확진일'])
df['연도'] = df['확진일'].dt.year
df['월'] = df['확진일'].dt.month
df['일'] = df['확진일'].dt.day
df['요일'] = df['확진일'].dt.dayofweek

참고 

https://pandas.pydata.org/docs/reference/series.html#accessors

확진일, 연도, 월, 일, 요일 컬럼만 가져오기

df[['확진일', '연도', '월', '일', '요일']].head(2)

- 2차원 형태로 가져올 때는 대괄호 [] 두 개! 1차원 형태로 가져올 대는 대괄호 []하나

연도-월 만들기

- 연도-월 파생변수 만들기

- astype(str) 을 통해 수치 데이터를 문자 데이터로 변환하고 문자열 연결하기
- df["연도월"]
- df['연도'].astype(str) + '-'+ df['월'].astype(str) 할 수도 있지만 

- 문자열 슬라이싱으로도 만들 수 있음

df['연도월'] = df['확진일'].astype(str).str[:7]
df['연도월']

value_counts

- 1개의 변수에 대한 빈도, 비율 확인

 

요일 한글로 만들기

- 함수와 Series의 map 활용

dayofweek = "월화수목금토일"
dayofweek[1]

>> '화'

find_dayofweek 함수로 요일 숫자를 넘겨주면 요일명을 반환하는 함수

def find_dayofweek(day_no) :
    dayofweek = "월화수목금토일"
    day = dayofweek[day_no]
    return day

map을 사용해서 요일 칼럼을 요일명으로 변환하고 '요일명'이라는 새로운 칼럼에 저장

df["요일명"] = df['요일'].map(find_dayofweek)
df[['요일', '요일명']].sample(5)

 

 

전체 수치 변수 히스토그램 그리기

https://wikidocs.net/92112

 

20. Matplotlib 히스토그램 그리기

![](https://wikidocs.net/images/page/92112/histogram_00.png) **히스토그램 (Histogram)은 도수분포표를 그래프로 나타…

wikidocs.net

- df.hist로 히스토그램 그리기
- bins 잘게 나눌수록 구간을 자세히 볼 수 있음

- 월요일이 적은 이유는 일요일 선별진료소 안 해서 검사를 적게 해서 결과가 늦게 뜸
- 50개로 해도 요일은 막대가 늘어나지 않는다
- 월말 적은 이유 31일ㅇ ㅣ매달 있는게 아니라서
- 수치 데이터일지라도 연속된 데이터가 아니라 끊어진 데이터가 있음을 볼 수 있음
- 수치데이터임에도 범주형으로 볼 수 있는 데이도 있다는 것을 볼 수 있습니다
- 월요일 이런거는 막대가 이어지지 않음 환자나 확진일 같은 경우 아무리 구간을 나누어도 붙어잇음 -> 연속된 수치데이터 -> 아 이데이터는 범주형 데이터에 더 가깝구나 

df.hist(bins = 50, figsize = (12, 5))

 

value_counts로 하나의 변수에 대한 빈도수 구하기

연도

# "연도" 컬럼을 통해 빈도수 구하기
df['연도'].value_counts()

df['연도'].value_counts(normalize = True) * 100

df['연도'].value_counts(1)

연도 시각화

df['연도'].value_counts(1).plot.bar(rot = 0) # rot 은 x축 각도

연도월

- 연도월에 대한 빈도수 구하기

- 빈도수를 구하고 sort_index로 정렬

year_month = df['연도월'].value_counts().sort_index()
df['연도월'].value_counts().sort_index()

year_month.plot()

year_month.plot(kind = 'bar', figsize = (12, 3), title = '월별 확진자 수', rot = 80)

요일 / 요일명

연도월 한 것 처럼 하면

df["요일명"].value_counts().sort_index().plot.bar(rot = 0, figsize = (6, 3))

하지만 '월화수목금토일' 순서대로 하고 싶음

1. 빈도수 구하고 인덱스 번호로 정렬

- weekday_count 변수에 담아 재사용

weekday_count = df["요일"].value_counts().sort_index()
weekday_count

 

2. 리스트컴프리헨션 사용해서 리스트 만들기

3. '월화수목긐토일' 리스트로 만들어 weekday_list 변수로 재사

weekday_list = [w for w in "월화수목금토일"]
weekday_list

>> ['월', '화', '수', '목', '금', '토', '일']

list('월화수목금토일')과 동일

4. 인덱스 값을 요일명으로 변경하고 시각화

weekday_count.index = weekday_list
weekday_count.plot.bar(rot=0, figsize=(6, 3), title="요일별 확진수") ;

1. df['요일명']을 value_counts()해서 w_count라는 변수에 넣어줌ㅈ

w_count = df["요일명"].value_counts()
w_count[["월", "화", "수"]]

>>  
월    24516
화    35471
수    34548
Name: 요일명, dtype: int64
weekday_list

>> ['월', '화', '수', '목', '금', '토', '일']
w_count[weekday_list]

w_count[list("월화수목금토일")].plot.bar(figsize=(6, 3), rot=0) ;

 

확진일 빈도수 구하기

df['확진일'] 빈도수 구하고 인덱스 값인 날짜로 정렬하기

day_count = df['확진일'].value_counts().sort_index()
day_count

 

 

day_count.plot(figsize = (10,3), title = '확진일 별 빈도수') ;

; 한 이유는 로그 안 뜨게 하기 위해

 

전체 확진일 데이터 만들기

day_count = day_count.sort_index()
day_count.head(10)

day_count.head(10).plot.bar(figsize = (6, 3), rot = 60) ;

-> 0명인 날의 날짜는 보이지 않음

 

- 기존 확진일수로 연산을 한다면 확진자가 있는 날만 연산을 하게 됨
- 확진자가 없던 날은 0으로 표기하기 위해 전체 날짜를 구하고
- 전체 날짜에서 확진자가 없는 날은 0으로 결측치를 대체할 예정

첫 확진일과 마지막 확진일자 찾기(1)

last_day = day_count.index[-1]
first_day = day_count.index[0]

첫 확진일과 마지막 확진일자 찾기(2)

last_day = day_count.index.max()
first_day = day_count.index.min()
first_day, last_day

>> (Timestamp('2020-01-24 00:00:00'), Timestamp('2021-12-26 00:00:00'))

date_range

 

all_day = pd.date_range(start = first_day, end = last_day)
all_day

all_day = pd.date_range(start = first_day, end = last_day)
all_day

 

all_day를 데이터 프레임으로 변환

df_all_day = all_day.to_frame()

- '확진수'라는 칼럼을 생성해서 위에서 구한 day_cout 를 추가한다

- 확진자가 없는 날도 인덱스에 생성됨

- 필요 없는 0칼럼은 제

df_all_day['확진수'] = day_count
df_all_day.head(5)
del df_all_day[0]`

del 하기 전

결측치 채우기

- 비어있는 값은 확진자가 없었던 날이기 때문에 0을 채워 넣음

df_all_day['확진수'] = df_all_day.fillna(0).astype(int)
df_all_day

누적 확진수 구하기

- cumsum으로 '누적확진수' 구해서 새로운 변수에 넣기

df_all_day["누적확진수"] = df_all_day['확진수'].cumsum()
df_all_day

시각화 1

df_all_day.plot(figsize = (10,3)) ;

 

시각화 2 : subplots

df_all_day.plot(figsize = (10,3), subplots = True) ;

 

시각화 3 : 2축 그래

df_all_day.plot(figsize = (10,3), secondary_y = '누적확진수') ;

거주지

df['거주지'].value_counts()
송파구     13235
강남구     12150
타시도     11320
관악구     10992
구로구     10346
영등포구    10225
강서구     10150
은평구      9393
노원구      9327
성북구      9142
동대문구     9091
강동구      8882
동작구      8558
중랑구      8236
서초구      8087
양천구      7709
마포구      7370
광진구      6819
도봉구      6501
강북구      6449
서대문구     5946
금천구      5635
성동구      5530
용산구      5137
기타       4995
종로구      3838
중구       3570
양천구         5
용산구         2
동작구         2
타시도         1
금천구         1
마포구         1
강동구         1
Name: 거주지, dtype: int64

-> 강동구 두 개 있음 

타시도, 기타 전처리

- 공백 없애기

- 타시도 -> 기타

- 거주구 빈도수 구하기

df['거주구'] = df['거주지'].str.strip()
df['거주구'] = df['거주구'].str.replace('타시도', '기타')
gu_count = df['거주구'].value_counts()

시각화

- gu_count 변수에 담긴 값 시각화 하기

- 선그래프 -> 연속형 데이터 시각화

- 막대그래프 -> 범주형(끊어져 있는) 데이터 시각화 

gu_count.plot.bar()
plt.axhline(5000, c = 'r', ls = ':') ;

 

 

 

 


[더하기]

- plt.axhline(): 수평선( horizontal line )을 그리는 함수입니다.
- plt.axvine(): 수직선( vertical line )을 그리는 함수입니다.

 

- map 은 시리즈에만 !