일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 백준파이썬1157
- 알고리즘
- 코딩테스트
- 백준
- 파이썬리스트문법
- 백준단어공부
- python set
- 백준초보
- python list 문법
- Python dictionary
- 인공지능사관학교 5기
- 파이썬
- 백준파이썬
- 파이썬 시간복잡도
- 파이썬 딕셔너리 집합 차이점
- 파이썬 집합문법
- 백준3052번나머지
- Today
- Total
종원
인공지능 사관학교 - 2주차 (데이터분석 / 판다스, 넘파이) 본문
[실습1] 데이터 살펴보기
이번 실습에서는 데이터를 불러오고 확인하는 과정을 수행합니다.
라이브러리 불러오기
데이터 분석에서 널리 사용되는 파이썬 패키지 중 하나인 pandas는 데이터를 처리하고 조작하는 데 필수적인 도구를 제공합니다. pandas는 NumPy를 기반으로 구현되었으므로 NumPy도 함께 import 해주어야 합니다.
아래 코드에서는 pandas를 pd 라는 이ㅁ름으로 import하고, NumPy를 np 라는 이름으로 import합니다. 이렇게 하면 pandas나 NumPy의 함수나 클래스를 호출할 때 매번 긴 이름을 쓰지 않고 간결하게 사용할 수 있습니다.
import pandas as pd
import numpy as np
실습에 사용할 데이터는 2016년 1월 1일부터 2019년 3월 31일까지의 서울대공원 입장객 데이터입니다. 데이터에는 날짜, 공휴일 여부, 입장객 연령 등 다양한 정보가 저장되어 있습니다.
데이터 불러오기: read_csv()
이제 data폴더에 저장되어있는 데이터인 seoul_park.csv 파일을 불러와 데이터프레임 df에 저장합니다. csv 파일 형태를 read_csv()를 활용하여 불러옵니다. read_csv()에는 데이터가 저장되어있는 파일의
df=pd.read_csv("./data/seoul_park.csv")
경로를 입력받습니다.
이제 데이터프레임 df에 서울대공원 입장객 데이터가 저장되어있습니다.
[TODO] data 폴더에 저장되어있는 미세먼지 데이터를 불러와 변수 mm에 저장하세요.
- 미세먼지 데이터의 파일의 경로는 ./data/misemunji.csv 입니다.
mm=pd.read_csv("./data/misemunji.csv")
데이터 살펴보기: head(), tail()
데이터의 갯수는 총 1085개로 데이터가 너무 커 한눈에 들어오지 않습니다. pandas 라이브러리에는 데이터를 살펴보기에 유용한 기능들이 있습니다. head(), tail()을 사용하면 데이터의 처음과 끝의 일부를 확인할 수 있습니다.
df.head()
df.tail()
데이터 정보 확인: info()
데이터가 몇 개의 값을 가지는지, 어떤 자료형으로 저장되어있는지를 확인하는데에는 info()를 활용할 수 있습니다.
컬럼 이름을 활용한 특정 컬럼 추출
데이터프레임은 여러개의 시리즈가 열을 이루고 있는 자료형입니다. 대괄호를 사용하면 데이터프레임에서 특정 시리즈(열)를 추출할 수 있습니다. 예를 들어 데이터프레임 df에서 "청소년" 열을 추출하는 코드는 다음과 같습니다.
df["청소년"]
데이터 변환하기1
데이터의 숫자들에는 천 단위로 콤마(,)가 찍혀있습니다. 또한 데이터의 "단체" 컬럼의 윗부분과 아래부분을 보면 윗쪽의 데이터에는 0명이 0 으로 적혀있지만, 아래쪽에는 - 으로 표기되어있습니다. 이는 년도가 지나면서 기록 방식이 바뀌면서 생긴 문제입니다.
info()를 사용해 데이터의 타입을 확인해보면 모든 데이터가 수가 아닌 텍스트(Object) 타입으로 저장되어 있습니다. 이는 즉 데이터에 있는 3,359는 숫자 3359가 아닌 텍스트 "3,359"가 저장되어있는 상태입니다. 이 상태로는 숫자의 연산, 나아가 평균값과 같은 통계량을 측정할 수 없습니다. 따라서 우리는 이 텍스트(Object)들을 모두 정수형(int) 으로 바꾸어야 합니다.
정수형으로 바꾸기 위해선 텍스트에서 숫자만을 남기고 모두 제거해야 합니다. 이를 위해 숫자로 바꾸어야 하는 컬럼들에서 str.replace()를 활용하여 -를 0으로 바꾸고 콤마를 제거합니다.
# 쉽게 데이터 바꾸기
columns=['유료합계', '어른', '청소년', '어린이', '외국인', '단체', '무료합계', '총계']
for i in columns:
df[i]=df[i].str.replace('-','0')
df[i]=df[i].str.replace(',','')
데이터의 숫자들이 깔끔하게 정리된 것을 확인할 수 있습니다.
데이터 타입 변환: astype()
데이터의 숫자들이 깔끔하게 정리되기는 했지만, info를 활용하여 살펴보면 여전히 데이터 타입은 텍스트(Object)인 것을 알 수 있습니다. 통계적인 분석을 위해 해당 데이터들을 정수(int)형태로 바꿔주어야 합니다.
astype()을 사용하면 원하는 타입으로 변환할 수 있습니다. astype메서드를 활용해 "어른" 컬럼의 데이터를 정수 형태로 변환하는 코드는 다음과 같습니다.데이터의 숫자들이 깔끔하게 정리되기는 했지만, info를 활용하여 살펴보면 여전히 데이터 타입은 텍스트(Object)인 것을 알 수 있습니다. 통계적인 분석을 위해 해당 데이터들을 정수(int)형태로 바꿔주어야 합니다.
astype()을 사용하면 원하는 타입으로 변환할 수 있습니다. astype메서드를 활용해 "어른" 컬럼의 데이터를 정수 형태로 변환하는 코드는 다음과 같습니다.
df["어른"].astype(int)
이제 "어른" 컬럼의 형태가 제대로 변환되었는지 확인해보겠습니다.
df의 어른 컬럼의 데이터타입은 object로, astype()을 사용했지만 바뀌지 않은 것을 확인할 수 있습니다. 어떻게 된 일일까요.
astype()을 비롯해 데이터프레임에 뭔가 변형을 가하거나 작업을 하는 메서드들은 데이터프레임 자체를 변환하지 않고, 변환된 새로운 데이터프레임을 반환합니다.
따라서 df["어른"].astype(int)이라는 코드는 df의 "어른" 컬럼을 정수형으로 변환하기는 하지만, 그냥 정수형으로 바뀐 "어른" 컬럼을 시리즈 형태로 나타낼 뿐 df의 "어른" 컬럼 그 자체가 바뀌는 것이 아닙니다.
df의 "어른" 컬럼을 바꾸고 싶다면 astype()을 활용해 변환하여 생성한 "어른" 컬럼 시리즈를 df의 "어른" 컬럼에 덮어씌워주는 작업이 필요합니다. 이는 다음과 같이 수행할 수 있습니다.
df["어른"]=df["어른"].astype(int)
df.info()
덮어씌워주는 과정을 통해 어른 컬럼의 데이터타입이 정수형(int)으로 바뀐 것을 확인할 수 있습니다. 이런 식으로 원본의 데이터프레임을 가공하기 위해선 덮어씌워주는 작업이 필요하다는 사실과 해당 코드를 익혀두시면 앞으로 강의의 코드를 이해하시기 한결 수월할 것입니다.
데이터 타입 변환: to_numeric()¶
to_numeric()은 astype()과 같이 데이터의 형태를 변환하지만 원하는 타입을 지정할 수 있는 astype()과 달리 숫자로만 변환한다는 차이점이 존재합니다. 특히나 데이터분석에서는 데이터를 숫자로 변환할 일이 많기 때문에 유용하게 사용할 수 있습니다.
df["유료합계"] = pd.to_numeric(df["유료합계"])
df.info()
유료합계 컬럼이 정수형(int)으로 바뀐 것을 확인할 수 있습니다.
[TODO] df의 "외국인" 컬럼의 자료형을 숫자형으로 변환해 변수 foreigner에 저장.
- astype() 또는 to_numeric()을 활용
foreigner=df["외국인"].astype(int)
이제 for문을 사용해 입장객 수에 관련된 컬럼들을 모두 숫자형으로 바꿔보도록 하겠습니다.
columns=["유료합계", "어른", "청소년", "어린이", "단체", "무료합계", "총계"]
for i in columns:
df[i]=pd.to_numeric(df[i])
df.info()
입장객 수에 관련된 데이터가 모두 숫자형태로 바뀐 것을 확인할 수 있습니다. 이제 이 입장객 수를 활용해 평균, 최대값 등을 계산할 수 있습니다.
데이터 타입 변환: to_datetime()
날짜 컬럼에는 데이터가 수집된 날짜가 기록되어있습니다. 하지만 위의 정보를 보면 알 수 있다시피 날짜는 텍스트로 저장되어있습니다. 다시 말해 날짜 컬럼의 "2016-01-01"는 텍스트일 뿐 컴퓨터가 이것을 날짜로 인식할 수 없습니다. 이 날짜를 보다 효과적으로 활용하기 위해 to_datetime()을 활용해 날짜컬럼 전체를 datetime형식으로 변환해줍니다.
df["날짜"]= pd.to_datetime(df["날짜"])
df.info()
이번엔 날짜 데이터를 사용해보겠습니다. 데이터프레임에서 datetime 형태의 데이터에는 연, 월, 일 정보가 담겨있어 이를 활용하면 날짜정보를 비롯해 요일과 같은 새로운 정보를 생성할 수 있습니다.
이를 위해 dt 속성을 사용하여 datetime 형태의 데이터에서 특정 정보들을 추출하고 연, 월, 일, 요일 컬럼을 새롭게 생성해보겠습니다.
df['연']=df['날짜'].dt.year
df['월']=df['날짜'].dt.month
df['일']=df['날짜'].dt.day
df['요일']=df['날짜'].dt.dayofweek
df.head()
연, 월, 일, 요일 컬럼이 생성되고 해당하는 정보가 저장된 것을 확인할 수 있습니다. 그런데 요일이 글자가 아닌 숫자로 기록되어 있는 것을 확인할 수 있습니다.
데이터 변환하기 2
데이터 불러오기
이전 실습에서 우리는 데이터의 형 변환을 통해 숫자와 날짜 데이터들의 형태를 변환해주고, 날짜 데이터를 활용해 연, 월, 일, 요일 컬럼을 생성하였습니다. 지난 실습을 통해 정제된 데이터를 불러옵니다.
import numpy as np
import pandas as pd
df=pd.read_csv("./data/seoul_park02.csv")
df.head()
시리즈 연산을 통한 변환
- 데이터프레임의 각 열은 시리즈로, 연산이 가능합니다
- 다양한 컬럼의 연산을 활용해 데이터를 변환하거나 새로운 컬럼 생성이 가능합니다.
df[['어른', '청소년', '어린이']].apply(lambda x : np.average(x))
청소년의 값이 NaN이 나오는 이유는 결측치가 있기 때문에 계산이 안되어 NaN이 나옵니다.
df[df['청소년'].notna()][['어른', '청소년', '어린이']].apply(np.average, axis = 0)
요일 컬럼이 글자가 아닌 숫자로 기록되어 있는 것을 확인할 수 있습니다. 이대로도 데이터 분석을 진행할 수는 있지만, 알아보기가 조금 어렵습니다.
열 전체 변환: map()
이 숫자들은 0부터 6까지, 월요일부터 일요일까지 숫자가 매칭되어있습니다. 이 정보를 활용하여 "요일" 컬럼 전체 데이터를 변환하고자 map()을 사용합니다.
먼저 map()에 변환 정보를 입력해주기 위해 숫자를 키로, 그에 대응하는 요일 글자를 값으로 가지는 week 딕셔너리를 생성합니다.
week={0:'월', 1:'화', 2:'수', 3:'목', 4:'금', 5:'토',6:'일'}
이제 map()과 요일 정보가 담긴 week 딕셔너리를 사용해서 df의 "요일" 컬럼을 변환합니다.
df['요일']=df['요일'].map(week)
df.head()
요일 컬럼의 데이터들이 딕셔너리에 맞춰 변환된 것을 확인할 수 있습니다.
데이터에 함수 적용: apply() (특정컬럼)
이번엔 "날씨" 컬럼을 살펴보겠습니다.
df['날씨'].value_counts()
날씨 컬럼 데이터에는 '눈', '비', '눈/비' 가 있습니다. 날씨와 입장객수의 관계는 매우 밀접하지만, 이것은 어디까지나 날씨가 맑은지 흐린지가 중요할 뿐 비인지 눈인지는 크게 중요하지 않습니다. 그렇기 때문에 세 종류의 데이터를 모두 '눈/비'로 통일해서 합치고자 합니다.
먼저 '눈', '비' 를 입력받으면 '눈/비'로 바꿔주는 함수를 선언합니다.
# 함수를 쓰는 방법
def weather(e):
if e=='눈' or e=='비':
return '눈/비'
else:
return e
df['날씨'] = df['날씨'].apply(lambda e : '눈/비' if e == '눈' or e == '비' else e)
# 람다함수를 사용
이제 이 함수를 컬럼 전체에 적용하기 위해 apply()를 사용합니다. apply()는 특정 컬럼 혹은 데이터프레임 전체에 특정 함수를 적용할 때 사용 가능합니다.
df의 "날씨" 컬럼에 weather 함수를 적용하려면 아래와 같이 사용할 수 있습니다.
df["날씨"]=df["날씨"].apply(weather)
df["날씨"].value_counts()
"날짜" 컬럼의 데이터에서 '눈', '비'가 '눈/비'로 통일되어 111개가 되었음을 확인할 수 있습니다.
이렇게 원하는 함수를 정의해서 apply()를 활용해 적용하는 과정은 굉장히 많이 사용되게 됩니다. 하지만 다양한 변환이 필요할수록 매번 함수를 만들고 사용해야하고, 이는 코드를 지저분하게 만듭니다.
파이썬에서는 이러한 문제를 해결하기 위해 일회용으로 사용할 함수를 정의하는데 유용한 람다함수 기능을 지원합니다. 람다함수를 사용하면 위에서 weather() 함수를 정의하는 과정을 생략하고 다음과 같이 사용할 수 있습니다.
[실습4] 데이터 요약하기
데이터 불러오기
지난 실습에서 데이터 변환을 통해 "요일", "날씨" 컬럼을 알아보기 쉽게 변환하였습니다. 지난 실습을 통해 정제된 데이터를 불러옵니다.
import numpy as np
import pandas as pd
df=pd.read_csv("./data/seoul_park03.csv")
mm=pd.read_csv("./data/misemunji.csv")
df.head()
데이터 통계값 확인: mean(), min(), max(), median()
데이터의 통계값을 확인하기 위해 각종 집계함수(mean(), min(), max(), median() 등)를 사용할 수 있습니다. 예를들어 mean()을 활용해 "어른" 컬럼의 평균값을 구하려면 다음과 같이 사용할 수 있습니다.
df["어른"].mean()
전체 일차의 어른 입장객 수의 평균을 구할 수 있습니다. 이번엔 총 입장객 수("총계")가 가장 적은 날의 입장객 수를 알아보도록 하겠습니다.
df["총계"].min()
총 인원이 2명만 입장한 날이 있다는 사실을 알 수 있습니다. 여담이지만 해당 날짜인 2017년 3월 28일은 조류독감으로 인해 폐쇄되었던 서울대공원의 재개장을 알리는 언론보도가 나온 날짜로 재개장을 위한 입장 및 집계 시스템의 내부 테스트 정도로 유추해볼 수 있습니다.
[TODO] 집계함수를 사용해 미세먼지가 가장 높은 날의 수치를 변수 badair에 저장합니다.
- "미세먼지" 컬럼 값이 가장 높은 날의 수치를 저장합니다.
- 최대값을 구하는 집계함수는 max() 입니다.
# 변수 mm에는 미세먼지 데이터가 저장되어 있습니다.
mm.head()
# None을 지우고 알맞은 코드를 입력하세요.
badair=mm["미세먼지"].max()
badair
데이터 전체 통계: describe()
위에서 배운 집계함수를 활용하면 내가 원하는 통계값을 확인할 수 있지만, 때로는 전체 통계값을 보고 이를 통해 데이터의 분석 방향을 결정하기도 합니다. 이를 위해 데이터프레임의 다양한 통계값을 정리해서 보여주는 describe()를 사용합니다.
describe()는 숫자형 데이터들로 이루어진 컬럼들의 데이터의 갯수(count), 평균(mean), 표준편차(std), 최소값(min), 사분위수(25/50/75%), 최대값(max)를 보여줍니다.
df.describe()
describe()를 사용해 우리는 입장객의 수가 가장 많았던 날은 58,688명이었다는 것("총계" 컬럼의 max값), 평균적으로 어른, 청소년, 어린이, 외국인 순으로 입장객의 수가 많았다는 것(각 컬럼의 mean값 비교) 등 다양한 정보를 얻을 수 있습니다.
데이터 그룹화: groupby()
다음은 데이터를 그룹으로 묶어서 확인할 수 있는 groupby()입니다. groupby()를 사용하면 특정 기준에 따라 데이터를 정리해서 분석할 수 있습니다. 예를 들어 서울대공원 데이터를 통해서 날씨에 따른 입장객의 수가 궁금할 때, 우리는 groupby()를 사용해서 "날씨"를 기준으로 "총계"값의 평균을 구할 수 있습니다.
df.groupby("날씨")["총계"].mean()
예상대로 날씨가 맑음일 때 가장 평균 입장객 수가 많고, 눈/비일 때 가장 적은 것을 확인할 수 있습니다. 더 나아가 날씨가 맑을때와 눈/비가 올 때의 입장객 수가 수치적으로 2배가 넘게 차이가 난다는 것은 물론 구름의 많고적음은 입장객의 수에 큰 영향을 끼치지 않는다는 사실까지 알 수 있습니다.
이렇게 groupby()를 사용하면 데이터를 그룹별로 분할하고 각 그룹에 대한 통계량을 확인하여 특정 기준에 따른 데이터의 추세를 확인할 수 있습니다.
이 데이터를 분할하는 기준은 한 개 이상이 될 수도 있습니다. 앞서 날씨에 따라 입장객의 총 수를 확인하였는데, 입장객의 수에 큰 영향을 미치는 또 하나의 요인은 바로 공휴일 여부입니다. groupby()에서 그룹화 기준이 되는 컬럼을 "날씨"와 "공휴일" 2개의 컬럼으로 설정하면 전체 데이터를 날씨에 따라서 그룹으로 한번 묶고, 각 날씨별 그룹 안에서 다시 공휴일 여부에 따라 두 분류로 데이터를 분류합니다.
df.groupby(["날씨","공휴일"])["총계"].mean()
날씨에 따라 묶었던 데이터를 공휴일 여부에 따라서 데이터를 한번 더 분류했더니 좀 더 자세한 정보들을 알 수 있습니다. 공휴일이 아닌 날의 경우 날씨에 따라 입장객의 수 차이가 그리 크지 않다는 사실을 알 수 있습니다. 또한 날씨가 맑음이거나 구름일 때에는 공휴일 여부에 입장객의 수가 따라 월등히 차이가 나지만 눈/비 일때는 오히려 날씨에 따른 입장객 수의 차이가 크지 않음을 알 수 있습니다.
groupby()를 활용해 2개 이상의 컬럼을 확인하는 것 역시 가능합니다. 이 경우 집계 결과가 데이터프레임으로 출력됩니다
df.groupby(["날씨","공휴일"])[["어른", "어린이"]].mean()
[TODO] 목요일이면서 공휴일인 날짜의 어른 입장객 수 최대값을 변수 thursday에 저장하세요.
- groupby()를 사용해 "요일" 컬럼과 "공휴일" 컬럼으로 데이터를 묶을 수 있습니다.
- 최대값을 구하기 위한 집계함수는 max() 입니다.
# 여기서 Thursday는 그냥 시리즈
thursday = df.groupby(['요일','공휴일'])['어른'].max()
thursday
# 여기서 Thursday는 데이터 프레임
thursday = df.groupby(['요일','공휴일'])['어른'].max().reset_index()
thursday
thursday.loc['목']['O']
[실습5] 데이터 추출하기
데이터 불러오기
import numpy as np
import pandas as pd
df=pd.read_csv("./data/seoul_park03.csv")
mm=pd.read_csv("./data/misemunji.csv")
df.head()
조건에 따른 인덱싱: Boolean indexing
전체 데이터프레임에서 Boolean indexing을 통해 특정 조건에 맞는 데이터를 추출할 수 있습니다. 예를 들어 어른 입장객 수가 5000보다 큰 날짜들의 데이터를 추출하려면 다음과 같이 코드를 작성할 수 있습니다.
Pandas 논리연산자를 활용하면 좀 더 복잡하게 조건을 설정할 수 있습니다. 두 개 이상의 조건을 and(&)나 or(|)을 활용하여 같이 사용하는데, 이 때 각 조건식은 소괄호로 묶여있어야 합니다.
어른 입장객의 수가 10000명이 넘는 공휴일의 데이터만을 추출하려면 and(&) 연산자를 활용해 다음과 같이 추출할 수 있습니다.
이번에는 or(|)연산자를 활용해 어른 입장객의 수가 10000명이 넘거나 어린이 입장객 수가 2000명이 넘는 날짜의 데이터를 추출해보도록 하겠습니다.
라벨을 활용한 데이터 추출: loc
다시 데이터를 살펴보겠습니다.
우리가 일반적으로 데이터프레임에서 원하는 데이터를 추출한다고 가정해봅시다. 가령 2016년 1월 4일의 어른 입장객 데이터를 알고싶은 경우, 데이터프레임을 쭉 보면서 2016년 1월 4일의 행을 찾고, 그 행에서 "어른" 열을 찾아 그 값을 확인합니다.
loc은 이러한 데이터 탐색 및 추출과정을 활용한 데이터 추출 메서드입니다. 입력받은 데이터의 행과 열의 인덱스를 활용하여 그 위치에 해당하는 데이터를 추출합니다.
여기서 유의할 점은 우리가 사용하고있는 데이터의 행 인덱스는 날짜가 아니라 인덱스 숫자입니다. 2016년 1월 4일 데이터의 인덱스값은 3이므로, loc을 사용하여 2016년 1월 4일의 어른 데이터를 추출하려면 다음과 같이 사용할 수 있습니다.
연속적인 객체(데이터프레임의 인덱스) 범위를 지정해 가져오는 방법인 슬라이싱을 활용하면 범위를 지정하여 해당 범위에 해당하는 데이터들을 불러올 수도 있습니다.
이 때 유의할 점은, loc은 라벨 기반 인덱싱을 사용하기 때문에 A:B로 슬라이싱을 하면 A 부터 B까지, 즉 B포함한 범위를 인덱싱 한다는 점입니다. 예를들어 3:6 의 범위를 지정한다면 인덱스가 3부터 6까지인 데이터, "어른":"외국인" 의 범위를 지정한다면 "어른"부터 "외국인" 까지의 데이터를 지정하게 됩니다.
또한 이렇게 슬라이싱을 활용해 추출한 데이터들은 복수의 데이터이므로, 시리즈 혹은 데이터프레임 형태라는 사실을 알아두면 좋습니다.
loc과 Boolean indexing을 활용한 데이터 추출
앞서 배웠던 Boolean indexing처럼 조건식과 논리연산자를 loc과 같이 활용하면 조건에 맞는 데이터들만을 추출할 수 있습니다. 예를 들어 다음과 같이 어른과 어린이의 입장객 수가 둘다 1000보다 큰 날짜의 "날짜"부터 "총계" 행을 추출할 수 있습니다.
순서를 활용한 데이터 추출: iloc
다음으로 행과 열의 정수 위치를 이용해 데이터를 추출하는 iloc에 대해 알아보겠습니다. 앞서 loc이 행과 열의 이름을 좌표로 삼아 해당 위치의 데이터를 추출했다면, iloc은 행과 열의 정수형 위치, 즉 순서를 좌표로 삼아 해당 위치의 데이터를 추출합니다. 예를 들어 4번 행(2016년 1월 5일)의 7번 열(외국인) 데이터를 추출하면 다음과 같습니다.
iloc 역시 슬라이싱을 활용하여 지정한 범위의 데이터를 추출할 수 있습니다.
여기서 loc과의 중요한 차이점이 있는데, iloc은 위치 기반 인덱싱을 사용하여 범위를 지정하기 때문에 시작은 포함되고 끝은 포함되지 않습니다. 즉 iloc에서 A:B로 슬라이싱을 하면 A부터 B-1까지, 즉 B를 포함하지 않는 범위를 인덱싱합니다.
B를 포함해서 인덱싱하는 loc과는 다르기 때문에 코드를 작성하거나 해석할 때 유의하셔야 합니다.
iloc을 활용해서 인덱싱을 3~6번 행, 4~6번 열의 값을 추출하면 다음과 같습니다.
iloc은 정수 위치를 사용하기 때문에 단순한 작업보다는, for문 등을 활용한 반복작업시에 매우 유용하게 활용할 수 있습니다.
loc과 iloc을 활용하면 특정 위치에 해당하는 데이터 값을 추출할 수도 있고, 그 값을 다른 값으로 바꿔넣을 수도 있습니다. 예를 들어 2016년 1월 5일의 "청소년" 컬럼 값을 확인해보겠습니다.
만약 이 값을 다른 값으로 바꿔주고 싶다면 loc이나 iloc을 활용해 값을 불러온 다음, 바꿔줄 값을 =을 활용해 저장해주면 됩니다.
2016년 1월 5일의 "청소년" 컬럼의 값이 100으로 바뀐 것을 확인할 수 있습니다.
[TODO] 2018년 전체의 날짜, 미세먼지, 초미세먼지 데이터를 변수 dust에 저장하세요.
- 미세먼지 데이터 mm에서 2018년 1월 1일의 행 인덱스는 731, 2018년 12월 31일의 행 인덱스는 1095 입니다.
- loc 또는 iloc을 활용해 2018년의 "날짜"와 "미세먼지", "초미세먼지" 컬럼 값을 추출해 데이터프레임으로 저장하세요.
[실습6] 데이터 정제하기
데이터 불러오기
이번에는 데이터를 다양하게 정제해서 사용하는 방법에 대해 알아보겠습니다.
데이터 정렬: sort_values
먼저 데이터를 정렬해서 사용하는 방법에 대해 알아보겠습니다. 현재의 데이터는 시간의 순서대로 기록되어 있는데, 특정 상황에서는 입장객 수에 따라서 정렬을 하면 사용이 용이한 경우가 많습니다.
이럴 때 우리는 sort_values()를 사용할 수 있습니다. sort_values()는 특정 컬럼의 값을 기준으로 전체 데이터를 오름차순 혹은 내림차순으로 정렬합니다. 입장객 수가 많은 순서를 알아보기 위해 "총계" 컬럼을 기준으로 내림차순으로 정렬해보도록 하겠습니다.
입장객 수가 많았던 순서대로 정렬해보니 데이터가 집계된 기간의 어린이날들이 모두 최상위권에 위치해 있어 어린이날의 위엄을 알 수 있습니다. 이는 단순히 어린이날이라는 이유 뿐 만 아니라, 서울대공원의 경우 어린이날 당일에는 13세 미만 어린이들에게 무료 개방을 하고 있기 때문입니다. 이러한 정보를 통해 어린이날의 "어린이" 컬럼의 값이 비정상적으로 낮고 "무료합계" 컬럼이 높은 이유 또한 알 수 있습니다.
또한 전부 공휴일인 것은 모두가 예상하신대로 일 것이고, 어린이날을 제외한 상위권을 살펴보면 2016년 6월 5일은 다음날이 공휴일인 일요일, 2017년 4월 8일과 9일은 서울대공원 벚꽃축제, 2017년 4월 23일은 서울대공원 겹벚꽃 원더풀 데이 축제 날짜였습니다. 이렇게 특정 이벤트가 있는 날짜의 입장객 수가 평범한 날들에 비해 월등히 높은 것을 알 수 있습니다.
이번에는 입장객 수 하위 10개 데이터를 살펴보겠습니다.
하위 2개 데이터는 폐쇄 후 재개장을 위한 테스트로 추정되기 때문에 제외하고, 나머지 데이터를 살펴보면 일단 전부 공휴일이 아니라는 것을 알 수 있습니다. 최하위권에 2018년 1월 24일부터 25일까지가 위치해있는데, 날씨가 맑음임에도 불구하고 아무리 평일이라도 입장객 수가 지나치게 낮은 것을 확인할 수 있습니다. 이유를 알아보면 해당 일차의 날씨 자체는 맑음이 맞지만 강렬한 한파로 인해 서울의 최저기온이 영하 18도까지 내려갔던 기간이었습니다.
이렇게 데이터를 정렬하면 추가 정보를 얻을 수 있고, 데이터 분석의 방향을 결정할 수도 있습니다.
[TODO] 미세먼지 데이터에서 초미세먼지 농도가 3번째로 심했던 날짜를 변수 pollution에 저장하세요.¶
- mm에는 미세먼지 데이터가 데이터프레임 형태로 저장되어있습니다.
- sort_values를 활용해 "초미세먼지" 컬럼을 기준으로 내림차순 정렬하고, 3번째 데이터를 확인합니다.
- 초미세먼지 농도가 3번째로 높았던 날짜를 "yyyymmdd" 형식의 문자열로 변수 pollution에 저장하세요. (ex. 2016년 1월 5일 -> pollution="20160105")
인덱스 재지정: reset_index
이런식으로 데이터를 정렬하여 데이터를 사용하다보면 한 가지 문제가 발생합니다. 다시 df_sorted를 살펴보시면 데이터는 "총계"컬럼에 맞춰서 정렬되었지만 이 과정에서 기존의 인덱스를 그대로 가지고 정렬된 사실을 확인할 수 있습니다. 이렇게 될 경우 앞서 배웠던 loc 등을 활용하기가 어렵기 때문에, 이 데이터를 가지고 계속 데이터 분석을 할 예정이라면 인덱스를 재정렬해줄 필요가 있습니다. 이 때 우리는 reset_index()를 활용합니다.
df_sorted의 인덱스가 깔끔하게 재정렬된 것을 확인할 수 있습니다. 이렇게 인덱스를 재지정은 지금처럼 데이터를 정렬하거나, 일부 데이터를 삭제하는 등의 작업을 통해 인덱스가 흐트러졌을 때 수행해주면 유용한 작업입니다.
데이터 삭제: drop
데이터에는 다양한 정보가 저장되어 있지만, 데이터분석 목적에 따라 크게 중요하지 않은 정보들이 있을 수 있습니다. 가령 서울대공원 데이터를 다시 살펴보면 "유료합계" 컬럼과 "무료합계" 컬럼은 입장객 수를 분석하는 데에 있어서 크게 중요하지 않습니다. 이런 경우 해당 컬럼을 drop()을 활용하여 삭제하여 좀 더 깔끔하고 명확한 데이터를 분석하는데 활용할 수 있습니다. "유료합계"와 "무료합계" 컬럼을 삭제하여 df에 덮어씌우는 과정은 다음과 같습니다.
필요 없는 컬럼이 삭제되어 좀더 깔끔한 데이터가 된 것을 확인할 수 있습니다.
열 이름 바꾸기: rename
떄로는 데이터의 컬럼이름이 잘못 설정되어있거나, 알아보기 어렵게 설정되어있는 경우가 있습니다. 이런 경우 rename()을 활용해 컬럼의 이름을 바꿀 수 있습니다. 서울대공원 데이터에서 "총계" 컬럼의 경우 총 관람객 수를 의미한다는 것을 유추할 수 있지만, 좀 더 명확하게 "총계" 컬럼의 이름을 "총입장객수" 로 바꿔보도록 하겠습니다.
컬럼의 이름이 "총입장객수" 로 바뀐것을 확인할 수 있습니다.
결측치 처리
우리가 실습에서 사용하고 있는 데이터는 실제 서울대공원의 입장객 수를 기록한 데이터입니다. 그런데 실제로 데이터를 수집하다보면 다양한 문제가 야기됩니다. 앞서 처리했던 표기의 문제도 있었고, 데이터 자료형의 문제도 있었습니다. 그리고 또 다른 문제로는 데이터의 누락이 있습니다. 오랜 기간 데이터를 수집하다보면 데이터가 수집되지 않는 경우들이 발생할 수 있습니다.
info()를 활용하여 다시 한번 데이터를 살펴보겠습니다.
데이터의 갯수를 보면 대부분의 컬럼이 1086개의 데이터를 가지고 있지만 "날씨" 컬럼과 "청소년" 컬럼의 데이터가 부족한것을 확인할 수 있습니다. 잘보면 데이터의 갯수가 Non-Null Count라고 되어있는데, 즉 "날씨" 컬럼과 "청소년" 컬럼에는 Null이 존재한다는 뜻입니다.
이 Null이 어떻게 생겼는지는 데이터프레임에서 확인할 수 있습니다.
2016년 1월 4일의 데이터의 "청소년" 컬럼을 확인하면 NaN(Not a Number)라고 되어있습니다. 이는 0이 아니라 아예 공백, 즉 수집되지 않아서 어떤 값인지 알 수 없는 데이터입니다. 이런 값들을 결측치 라고 합니다.
당연한 얘기지만, 이런 결측치들은 데이터 분석이나 인공지능 적용 과정에서 걸림돌로 작용합니다. 이제부터 이 결측치를 처리하는 방법을 배워보겠습니다.
결측치 탐색: isnull()
먼저 결측치가 얼마나 존재하는지 알아보기 위해 isnull()을 활용합니다. 물론 앞서 봤듯이 info()를 활용해서도 알 수 있지만, isnull()을 활용하면 추가적인 계산 필요 없이 직관적으로 어떤 컬럼에 몇 개의 결측치가 존재하는지 확인할 수 있습니다. 먼저 데이터프레임에 isnull()을 적용해보겠습니다.
isnull()은 데이터프레임의 각 원소가 결측치인지 아닌지를 검사합니다. 결측치가 아닌 제대로된 값은 False, 결측치는 True로 채워진것을 확인할 수 있습니다. 하지만 저 표를 가지고는 결측치가 컬럼별로 몇개인지 확인할 수가 없습니다. 이 때 True는 1로, False는 0으로 계산되어 sum()을 활용해서 각 컬럼별로 전체값의 합을 구하면 결과값은 컬럼에 존재하는 True의 갯수, 즉 결측치의 갯수가 됩니다.
"날씨" 컬럼에 140개, "청소년" 컬럼에 5개의 결측치가 있는 것을 알 수 있습니다. 이렇게 결측치의 갯수를 파악하는 것은 결측치를 어떻게 처리할지 방법을 결정하는데에 중요합니다.
결측치를 처리하는 방법은 대표적으로 2가지가 있습니다. 하나는 결측치를 특정 값으로 채워넣는 것이고, 하나는 결측치가 존재하는 데이터를 삭제하는 것입니다.
결측치 채우기: fillna() (결측치 대체)
먼저 결측치를 채워넣는 방법에 대해 알아보겠습니다. fillna()는 결측치를 특정 값으로 채워넣는데 활용하는 메서드로, 어떤 값으로 채워넣는지는 다양한 방법을 사용합니다. 이번 실습에서는 "청소년" 컬럼의 결측치를 전체 데이터의 청소년 입장객의 평균값으로 채워보도록 하겠습니다.
원래는 결측치였던 2016년 1월 4일의 청소년 데이터가 463명이라는 펑균값으로 채워진것을 확인할 수 있습니다. 물론 이 값이 정확한 값이라고는 할 수 없지만, 최대한 확률이 높은 값으로 채워넣었다고 생각하면 됩니다.
결측치 데이터 삭제하기: dropna() (결측치 삭제)
결측치가 많지 않을 경우, 결측치가 있는 데이터를 삭제해버리는 것도 좋은 방법입니다. 가령 청소년 컬럼에서 결측치가 있는 데이터를 삭제하면 5일치의 데이터가 사라지는 것입니다. 이는 전체 기간 1086일의 데이터에서 비중이 크지 않기 때문에 괜찮은 방법이 될 수도 있습니다. 이럴 때 dropna()를 활용하면 알아서 기준 컬럼에서 결측치를 탐사하고 해당하는 데이터를 삭제합니다.
이번에는 "청소년" 컬럼에 결측치가 있었던 2016년 1월 4일의 데이터가 아예 삭제된 것을 확인할 수 있습니다.
이렇게 결측치를 처리하는 방법은 다양하지만, 이번 강의에서는 이후 강의진행의 편의성을 위해 결측치를 채워넣는 방식으로 df를 변형하도록 하겠습니다.
[TODO] 미세먼지 데이터에서 결측치가 있는 컬럼을 파악하고 해당 컬럼의 중앙값으로 채운 뒤 mm에 덮어씌워 저장하세요.
- 미세먼지 데이터가 담겨있는 데이터프레임 mm의 한 컬럼에 결측치가 4개 존재합니다.
- 먼저 isnull()과 sum()을 활용하여 어떤 컬럼에 결측치가 존재하는지 확인하세요.
- 결측치를 결측치가 있는 컬럼 전체의 중앙값으로 채운 뒤, mm의 해당 컬럼에 덮어씌워 저장하세요. 중간값 집계함수는 median()입니다.
- 모든 지시사항을 수행한 뒤 데이터프레임 mm에는 결측치가 없어야 합니다.
[실습7] 데이터 병합하기
데이터 병합은 여러 개의 데이터셋을 하나로 합치는 과정을 말합니다. 여러 개의 데이터셋이 각각 다른 정보를 담고 있을 때 이 데이터들을 합치면 데이터의 크기를 늘리거나 종합적인 정보를 얻을 수 있습니다.
데이터 불러오기
데이터 병합: concat()
먼저 데이터 확장을 위해 기존의 서울대공원 입장객 데이터에 이후 기간 데이터를 병합하는 과정을 알아보도록 하겠습니다.
실습에 활용하고있는 서울대공원 입장객 데이터는 2019년 3월 31일까지의 데이터가 담겨 있습니다. 이 데이터의 뒤에 2019년 4월 한달간의 데이터를 불러와서 합치면 2016년 1월 1일부터 2019년 4월 30일까지의 데이터를 만들 수 있습니다.
concat()을 활용하면 두 개 이상의 데이터프레임을 행 또는 열 방향으로 단순히 이어붙이는데 활용합니다. 지금의 경우 2019년 3월 31일까지의 데이터인 df의 아래방향으로 2019년 4월의 데이터 df2를 이어붙이면 되기 때문에 concat()을 활용합니다.
아래 방향으로 붙이기 때문에 axis를 0으로, 두 데이터프레임에 모두 존재하는 컬럼만을 남기기 위해 join을 inner로, 인덱스를 전체 초기화하기 위해 ignore_index를 True로 설정합니다.
concat()을 활용하여 2016년 1월 1일부터 2019년 4월 30일까지의 데이터를 만들었습니다. 이런식으로 데이터를 늘리면 더 긴 기간동안의 데이터를 분석할 수 있고 데이터 분석 결과의 정확도를 높일 수 있습니다.
데이터 병합: merge()
데이터 병합은 정보의 종류를 늘리는데도 활용할 수 있습니다. 서울대공원 데이터셋과 미세먼지 데이터셋을 합치면, 날씨 뿐 만 아니라 미세먼지 농도에 따른 입장객 수의 정보를 확인할 수도 있습니다. 서울대공원 데이터셋과 같은 기간동안 수집된 미세먼지 데이터를 불러오도록 하겠습니다.
2016년 1월 1일부터 2019년 3월 31일까지의 서울대공원 입장객 데이터에 2016년 1월 1일부터 2019년 3월 31일까지의 미세먼지 데이터를 합쳐보도록 하겠습니다.
merge()를 활용해서 데이터를 합칠 때에는 기준이 될 컬럼이 필요합니다. 이번 경우에는 날짜를 기준으로 입장객수와 미세먼지 데이터간의 관계를 확인하기 위해 기준 컬럼인 on을 날짜로 하겠습니다.
how는 데이터를 합치는 방법을 지정합니다. 데이터를 합치는 방법은 데이터 분석의 목적에 따라 달라지므로 각 방법을 선택하는 이유에 대한 이해가 필요합니다.
우리의 관심사는 미세먼지에 따른 입장객의 수이기 때문에, 입장객 수 데이터가 없는 부분은 필요하지 않습니다. 두 데이터의 수집 기간은 같지만, 서울대공원 입장객 데이터의 경우 조류독감으로 인해 폐쇄된 구간의 데이터가 없기 때문에 해당 기간의 미세먼지 데이터는 필요가 없습니다. 따라서 입장객 수의 데이터가 존재하는 구간의 데이터만을 얻기 위해 how를 입장객 수 데이터, left에 맞춘 것입니다. (이번 실습의 경우 inner를 활용해도 같은 결과를 얻을 수 있습니다)
이렇게 두 데이터를 날짜를 기준으로 합쳐서 더욱 더 다양한 정보가 담긴 데이터를 만들었습니다. 만약 이 데이터를 활용해서 데이터 분석이나 그래프를 그리는 시각화 등을 수행한다면 입장객 수와 미세먼지 농도의 관계를 파악할 수 있습니다.
[TODO] 다양한 데이터 병합 실습을 수행합니다.¶
# 실습을 위한 데이터를 생성합니다.
score = pd.DataFrame({
'이름': ['민수', '도윤', '서아', '연우', '하준'],
'국어': [95, 72, 83, 89, 100],
'수학': [88, 72, 93, 80, 95],
'영어': [96, 68, 85, 94, 93]},
)
add1 = pd.DataFrame({
'이름': ['지호', '준우', '예서'],
'국어': [88, 92, 100],
'수학': [97, 91, 100],
'영어': [87, 95, 97],
'역사': [95, 100, 92]}
)
add2 = pd.DataFrame({
'이름': ['민수', '서아', '하준'],
'과학': [93, 82, 89],
'프로그래밍': [97, 96, 94]}
)
1. score과 add1을 활용해 전체 특별반 8명 학생들의 국어, 수학, 영어 점수데이터를 만들어 ans1에 저장하세요.
- Hint: pd.concat()을 활용하세요.
- ans1에는 다음과 같은 데이터프레임이 저장되어야 합니다.
2. score과 add2을 활용해 기존 특별반 학생들 중 국어, 수학, 영어, 과학, 프로그래밍 5개 과목의 시험을 모두 본 학생들의 점수데이터를 만들어 ans2에 저장하세요.
- Hint: pd.merge()를 활용하세요.
- ans2에는 다음과 같은 데이터프레임이 저장되어야 합니다.
'인공지능사관학교' 카테고리의 다른 글
인공지능 사관학교 - 2주차 (9일차, 웹크롤링) (0) | 2024.06.07 |
---|---|
인공지능 사관학교 - 2주차 (7일차, 정렬알고리즘) (0) | 2024.06.04 |
인공지능 사관학교 - 2주차 (DFS, BFS / 깊이 우선 탐색, 너비 우선 탐색) (0) | 2024.06.03 |
인공지능 사관학교 - 1주차 복습 문제 모음 (0) | 2024.06.03 |
인공지능 사관학교 - 1주차 기초 파이썬 (5일차, 재귀함수/완전탐색) (0) | 2024.05.31 |