2024. 4. 21. 03:37ㆍ공부
이번에는 넘파이와 양대산맥으로 많이 사용하는 Pandas(판다스)에대해서 알아보겠다.
판다스를 사용하는 이유?
판다스는 주로 Data wrangling, 데이터분석을 위해서 사용하며, 원본자료를 쉽게접근하고 분석하기위해서 데이터를 정리하고 통합하기위해서 사용한다.
다양한 소스(csv, 웹파일)들을 읽어 들여와 처리할수있기 때문에, 데이터 관련쪽을 익히려면 꼭알아야하는 라이브러리중 하나다.
또한 넘파이를 기반으로 데이터를 처리하기때문에 처리속도가 우월하고, 넘파이가 같은 자료형의 자료값을 가지는 단순한 수치로 되어있었다면, 판다스에서는 시리즈, 데이터프레임과 같은 여러가지 데이터구조로 다양한 처리를 가능하게 만들어 준다.
그렇다면 판다스에서 사용되는 시리즈, 데이터프레임은 어떤 구조일까? 지금부터 알아보자.
시리즈
판다스의 시리즈는 같은 자료형의 데이터를 저장하는 인덱싱되어있는 1차원 배열이다.
시리즈는 Series함수를 사용한다.
import numpy as np
import pandas as pd
se = pd.Series([1, 2, np.nan, 4]) # np.nan은 결측값(결측값은 이후에 설명)
se
# return
# 0 1.0
# 1 2.0
# 3 NaN
# 4 4.0
# dtype : float64
코드를 확인하면 Series값에 NaN값이 들어가있는것을 확인할수있다. 이것을 결손값(값을 찾을수없음,혹은 데이터타입에 맞지않음), 결측값이라 표현하고 판다스내부에는 이것을 탐지하고 수정함수를 제공한다.
se.isna() # 결측값을 감지하는 함수
# return
# 0 False
# 1 False
# 2 True
# 3 False
# dtype : bool
이때 결측값에는 2가지가 존재하는데 None 과 NaN이다.
None은 타언어의 Null 즉, 값이 비어있다는 의미이고, NaN의 경우 Not a Number. 숫자가아니거나, ∞를 의미한다.
데이터프레임에서는 None값은 NaN으로 자동으로 변환해준다. 하지만, 두값은 엄연히 다르니 주의를 해야한다.
시리즈에서는 기존에 넘파이에서는 할수없었던 또다른 값을 넣을수있다. 바로 인덱스 값을 넣을수있게된다.
긴말 필요없이 바로 아래코드를 확인해보자.
se = [1, 2, 3, 4]
#index 변수 추가
index_se = pd.Series(se, index = ['a', 'b', 'c', 'd'])
index_se
# return
# a 1.0
# b 2.0
# c 3.0
# d 4.0
# dtype :float64
기존에 시리즈를 생성하는과정에서 내부에 index 변수가 추가되었다.
이를 확인해보면 char형태로 배열을 집어넣었고 이에대한 결과를 확인해보니, 위에서는 0,1,2,3 이였던것이 넣어주었던 char값으로 대체되어있다. 이처럼 내부에 인덱스값을 넣어주게되면 원하는값으로 index를 변경할수있다.
이것을 딕셔너리 구조로 정의되어있는 값들을 시리즈로 변환하면 인덱스와 값의 배치가 자연스러워진다.
weather = {'1월' : 500, '2월' : 400, '3월' 605, '4월' : 456}
weather_se = pd.Series(weather)
printf(weather_se)
# return
# 1월 500
# 2월 400
# 3월 605
# 4월 456
# dtype : int64
자연스럽게 맞춰져 출력되는것을 확인할수있다.
하지만, 이런 1차원 배열구조로는 세부정보를 표기할수없어 곧 한계에 도닳하게된다. -> 2차원 배열구조 필요
문제점을 해결하기위해 사용하는것이 바로 데이터 프레임이다. 데이터 프레임은 쉽게 생각하면 시리즈의 배열이다.
여러 시리즈 배열을 묶어 한곳에 나타내도록 하는 것이 바로 데이터 프레임 이기 때문이다.
month_se = pd.Series(['1월', '2월', '3월', '4월'])
weather_se = pd.Series([400, 500, 605, 456])
chance_se = pd.Series([50, 40, 60, 50])
# columns(열)의 이름만 지정한다.
df = pd.DataFrame({'월': month_se, '강수량':weather_se, '강수확률':chance_se})
df

위에서도 설명했듯 판다스의 강점중하나는 넘파이에서 사용하는 함수들을 사용할수있다는 점이다. 이점을 이용해서 현재
weatehr_se에서 최대값과 평균값을 출력해보겠다.
max_idx = np.argmax(weather_se)
print('최대 강수량 월 :', month_se[max_idx])
# return
# 최대 강수량 월 : 3월
print('월 최대 강수량',weather_se.max(), '월 평균 강수량', weather_se.mean())
# return
# 월 최대 강수량 605 월 평균 강수량 490.25
이렇듯 넘파이 함수를 사용할수있어 다양한 처리를 손십게 진행할수있다.
하지만, 판다스는 보통 데이터를 분석할때 사용하기에 더욱 많은 데이터를 기반으로 진행해야한다. 그렇기에 이제부터는 외부에서 파일을 하나 불러와 진행하겠다. 이것또한 판다스의 특징인데 엑셀파일(csv파일)을 읽어들여와 데이터프레임화 할수잇다.
예시로 국가별 차량 생산수가 담겨있는 vehicle_prod.csv파일을 읽어들여와 진행하겠다.
# 파일을 외부에서 불러오는경우
path = 'https://github.com/dongupak/DataML/raw/main/csv/'
file = path + 'vehicle_prod.csv'
df = pd.read_csv(file)
df
# 파일을 가지고있는경우
df = pd.read_csv('파일경로\vehicle_prod.csv')
df

데이터프레임으로 정상적으로 출력되어나왔지만, 무언가 불편하다. 인덱스와 Unnamed로 되어있는부분, 그옆에있는 열이 인덱스라면 정말 좋을것 같다. 그럴때이를 해결해줄수있는것이 바로 index_col 매개변수다. index_col은 지정해준 숫자의 열을 인덱스로 만들어주는 매개변수다 그렇다면 이를 위의 코드에 넣고 다시하번 데이터 프레임을 생성해보자.
path = 'https://github.com/dongupak/DataML/raw/main/csv/'
file = path + 'vehicle_prod.csv'
df = pd.read_csv(file, index_col = 0)
df

한눈에보기에도 굉장히 깔끔하게 변한것을 확인할수있다. 이처럼 인덱스가 지정되어있지않아, 구분이 어려운 경우라면, 이와같은 매개변수를 적용해보는것도 방법이다
만약 이에대한 특정연도에 대해서 확인하고 싶다면, 이를 인덱스 슬라이싱을 이용해서 간단하게 확인할수있다.
df['2008'] # 데이터프레임의 2008년 열값
df.columns.tolist() # 데이터프레임의 열값들을 리스트로 변환
df['2008'].tolist() # 데이터프레임의 2008년 열값을 리스트로 변환
. 이값들은 또한 각각 확인할수있으며, 이를 리스트로 변환하여 가공, 활용할수있다.
그렇다면 데이터프레임에서는 새로운 열을 생성하고자 한다면 어떻게 해야할까?
이는 매우 간단하다. 추가하고자하는 열의 이름을 데이터프레임의 배열에 집어넣고 넣을값만을 정해주면된다.
여기서는 각행의 연도들의 합을 추가하는 열을 새로 생성해보겠다.
df['total'] = df.sum(axis = 1) # 모든 원소를 더하는 함수 axis = 1은 열기준으로 진행한다는 의미

이렇게 새로구한열이나 기존에있던 열을 삭제할때는 drop명령어를 사용할수있다.
df.drop('2007', inplace =True, axis = 1)
# 2007년도의 값들을 열기준으로 삭제하겠다.
# inplace는 drop이나 데이터프레임에 변형을 주는 명령어를 적용했을때,
# 그변화를 원본 데이터프레임에 적용할지 적용하지않을지 결정하는 매개변수이다.
# True경우 원본에 적용 False인경우 복사본을 만들어 반환한다.

지금까지 데이터프레임에대해서 알아보고 이를 가공하는과정을 배웠다. 이것으로 기존의 표보다는 훨씬 보기 간편해졌지만, 역시 숫자로만 되어있으면 한번에 알아보기는 여전히 어려움이 존재한다. 이를 해결하기위해서 판다스에서는 시각화를 위한 명령어 역시 지원하고있다.
그것은 바로 .plot이다 이는 본래 다른라이브러리인 matplotlib에 있는 함수지만, 판다스내부에서 자체적으로 import하여사용한다.
사용방법은
데이터프레임 df가 있다고 가정했을때
df['확인할 컬럼명'].plot(kind = '사용하고자하는 그래프의 종류', color = (각 그래프별 색상)) 이 된다. 이렇게 보면 알아보기 어려우니 예제를 하나 들어보겠다.
# 2009년의 칼럼을 bar그래프로 그린다.
df['2009'].plot(kind = 'bar' color =('orange','r','b','m','c','k'))

kind 의 종류는 매우 다양하며 상황에 맞게 사용하면된다.
- bar
- line
- pie
- hist
- kde
- box
- scatter
- area
또 굳이 plot내부에서 매개변수로 사용할필요없이 외부로 꺼내서 사용하는것도 가능하다.
df.plot.line()
# == kind = 'line'
df.plot.bar()
# == kind = 'bar'
시각화에 대해서 배웠으니, 이제다시 판다스의 기능 부분으로 돌아와서 나머지 기능들을 확인하겠다.
슬라이싱과 인덱싱
판다스 역시 넘파이와 동일하게 슬라이싱과 인덱싱이 가능하며, 이에대한 수행방법역시 넘파이와 동일하게 기능한다.
ex)
df[2:6]
# 데이터프레임의 2번째인덱스부터 6번째인덱스 전까지의 행의 모든 열값을 가져온다
인덱스 이름을 기준으로 행에대해 확인하고싶은경우 .loc함수를 사용하여 인덱스에 접근할수있다.
ex)
df.loc['Korea'] 행이름이 Korea인값에 접근해 데이터를 뽑아낸다
여러개의 인덱스에 접근할경우에는 주의해야할점이있다. 바로 내부에 배열을 하나 더 선언하여 주어야한다는 점이다.
그렇게 수행하지않으면 오류를 발생시킬수있으니 주의해야한다.
df.loc[['China','Korea']]
# df.loc['China','Korea'] 는 오류
만약 열과 행을 동시에 선택하려면 어떻게 해야할까?
해답은 기본적인틀은 데이터프레임에서 열을 선택할때의 구조를 따라가며 이후 인덱싱을 통해 값을 지정한다.
여기서 주의해야할점은 여기서 행을 지정할때에도 역시 내부에 배열을 하나더 선언해야한다는 점이다.
df['2009'][[0,4]]
# 2009년의 0번째와 4번째 인덱스의 행,열값을 뽑아낸다
반대로 행렬 결합선택이 아닌, 특정한 요소하나만을 찾을때에는 loc()함수를 사용한다.
df.loc['China', '2012']
# 중국의 2012년자료를 찾는다
또한 loc()함수를 string타입이 아닌 정수형인덱스로 지정해서 찾고싶다면 iloc을 사용해야한다.
사용법은 loc과 동일하지만, 내부변수타입만 바뀐것이다.
이번에는 다른csv파일을 통해 또다른 함수에 대해서 알아보도록하겠다.
path = 'https://github.com/dongupak/DataML/raw/main/csv/'
file = path + 'weather.csv'
# 한글을 불러올수있게 인코딩 타입을 지정해준다.
df =pd.read_csv(file, index_col = 0, encoding='CP949')

불러온 파일은 다음과 같다. 이는 충분히 좋은 데이터프레임이지만, 이를 한번에 분석하기에는 조금 난해한 감이 있다.
이를 위해 describe함수를 호출한다. 함수를 호출하게되면, 각 컬럼의 개수, 평균값, 표준편차, 최소값,최대값등 다양한값이 등장하게된다.

또한 기존에 사용하던 mean() std() 와같이 특정값만을 확인하는것도 가능하다. 이렇게 데이터를 확인하다 보면, 값이 이상하거나, NaN이라고 되어있는부분을 종종 확인할수있을것이다. 이러한 부분들은 데이터를 분석하는데 좋은영향을끼치지않으므로, 이를 청소하는 데이터 클랜징 과정이 필요하다.
데이터클랜징을 하기위해서는 우선 지워야하는값(NA값)이 있는지를 확인해야한다.(이는 맨위에있는 isna를 통해 확인
missing = weather[weather['평균풍속'].isna()]
# weather 데이터프레임에서 na가 포함되어있는 행렬을 꺼내 따로 저장한다

누락값의 형태는 무조건 NaN만 존재하는것이 아닌 다양한형태로 존재할수있다(NaN,0, '-', Na 등) 누락값이 다르다면 이를 통일해줘야 데이터 클랜징을 진행할수있다. df.replace()사용.
결손값(누락값)을 통일한후 이를 다루는 가장 간단한 방법은 결손값이 존재하는 행이나 열 전체를 삭제하는것이다. (dropna)
ex)
weather.dropna(axis=0, how='any', inplace = true)
- axis = 0 : 행을 기준으로 삭제할지 열을 기준으로 삭제할지결정한다.
- how = 'any' 결손값에 대해 삭제할 기준을 결정한다. 'any'일경우 결손값이 하나라도 존재하면삭제 'all'이면 axis에따라 행또는 열전체가 결손값인 경우에만 삭제한다.
하지만 이런식으로 데이터를 삭제해 버리면 데이터의 수량이 적어져, 기존의 결과와 다른결과를 발생시킬수도있어 신중하게 생각해야한다.
다른 방법으로는 기존의 NaN의 위치에 새로운값을 채워넣는것이다(ex : 0) fillna()를 사용하면, 결손값을 원하는값으로 변경할수있다. (일반적으로는 0으로 대체한다)
하지만, 결손값을 0으로 대체할경우, 데이터의 개수가 적을때 평균값이 왜곡할수있어 이역시 주의해야한다.
그렇다면 도대체 어떻게 해야할까? 바로 na값이 존재하는 열의 평균값으로 대체하는것이다. 이렇게하면, 평균값의 왜곡도 없으며, 데이터가 삭제되버리지도 않아, 결과를 유지하며 진행할수있다.
시계열 자료분석 DatetimeIndex
판다스에는 데이터를 분석하기위한 편리한 기능들이 몇가지 존재한다. DatetimeIndex는 그중 시간 순서대로 관측한값들의 집합을 분석하기위해서 사용되며, 연, 월, 일,시간데이터를 따로따로 떼어내 데이터의분류를 세분화 시킬수있게한다.
그룹핑과 필터링
다음으로 알아볼것은 데이터를 그룹화 하여 특정 정보에대한 값들을 확인할수있게하는 groupby다. groupby를 사용하게되면 데이터를 특정값의 그룹으로 변경하여, 데이터의 특징,변화를 확인하기 더욱 용이해진다는 장점이있다.
weather['month'] = pd.DatetimeIndex(weather['일시']).month
monthly_means = weather.groupby('month').mean()
monthly_means
# DatetimeIndex로 달별 평균기온, 최대풍속,평균풍속을 확인하여 차이를 확인할수있다.
이조건을 기반으로 조건을 세워 논리인덱싱을 수행하여, 특정 년도 월에 어떤것이 작용하는지 확인할수있게한다.
concat() merge()
데이터를 다루다 보면 여러개의 작은 테이블을 하나로 합쳐야하는경우가 종종있다. 이럴때 사용하는것이 바로 concat과 merge()함수이다. 둘은 데이터프레임을 합친다는점에서는 같지만, 매개변수와 수행기능에는 약간의 차이가 존재한다.
concat()경우 axis와 join을 기준으로 데이터프레임을 합치는데, axis = 0인경우 테이블의 행을 늘려서 붙이고 1인경우 열을 늘려붙여나간다. 또한 join은 테이블을 붙일때 내부 레이블값을 합집합(outer)으로 생성할지 교집합(inner)으로 생성할지 결정한다.
합집합 : 겹치는 값은 합치고 나머지는 유지함
교집합 : 겹치는값은 합치고 나머지는 버림
이와다른게 merge()는 고유 키값을 사용해서 그값을 기준으로 병합하게되는데, 이때 주의해야할점은 merge를 사용할때는 두데이터프레임에 동일한 레이블이 존재해야하며, 결합방식은 left(좌측데이터프레임기준 합집합) right(우측데이터프레임기준 합집합), outer(병합 데이터프레임들에대한 합집합) inner(병합 데이터프레임들에대한 교집합) 4가지가 존재한다.
ex) df1.merge(df2, how = 'left', on = 'B') )
how : 결합방식
on : 키로 사용할 레이블
또한 키 레이블(on 매개변수)외에도 중복되는 레이블이 존재할경우, 둘중하나가 사라지는것이 아닌, 왼쪽테이블에서는
_x, 오른쪽테이블에서는 _y를 붙여 새로운 테이블을 만들어 삽입한다.
또한 인덱스가 키의 역할을 수행하는경우도 존재하는데, 이런경우에는 양쪽테이블의 인덱스를 모두 사용하여 키를 만들게된다.
ex)
df3 = df1.merge(df2, how = 'outer', left_index = True, right_index = True)
- left_index, right_index 매개변수는 인덱스를 키로 사용해서 병합을 진행할때 필요한 매개변수다.
- 'outer'인경우 두인덱스의 합집합으로 새로운인덱스가 생성되며, '
- 'inner'인경우 교집합으로 모두 행만 남게되버린다.
'공부' 카테고리의 다른 글
| [파이썬] 내가보려고 정리하는 파이썬 : Seaborn (0) | 2024.04.21 |
|---|---|
| [파이썬] 내가보려고 정리하는 파이썬 : Matplotlib (0) | 2024.04.21 |
| [파이썬] 내가보려고 정리하는 파이썬 : Numpy (0) | 2024.04.20 |
| [그래픽스]Viewing (1) | 2024.04.19 |
| [그래픽스]GeoMetry(지오메트리) (0) | 2024.04.17 |