### 7.3.2부터 복습
7장 데이터 정제 및 준비¶
7.1 누락된 데이터 처리하기¶
isnull¶
산술 데이터에 한해 pandas는 누락된 데이터를 실숫값인 NaN으로 취급
string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])
string_data
0 aardvark 1 artichoke 2 NaN 3 avocado dtype: object
string_data.isnull()
0 False 1 False 2 True 3 False dtype: bool
파이썬의 내장 None값 또한 NA 값으로 취급
string_data[0] = None
string_data
0 None 1 artichoke 2 NaN 3 avocado dtype: object
string_data.isnull()
0 True 1 False 2 True 3 False dtype: bool
*NA 처리 메서드*
dropna¶
누락된 데이터가 있는 축(로우, 컬럼)을 제외시킴
어느 정도의 누락 데이터까지 용인할 것인지 지정 가능
col = ['col1','col2','col3','col4','col5']
row = ['row1','row2','row3','row4']
data = [[1,2,3,pd.NA,5],[6,pd.NA,8,pd.NA,10],[11,12,13,14,15],[pd.NA,pd.NA,pd.NA,pd.NA,pd.NA]]
df = pd.DataFrame(data,row,col)
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row2 | 6 | <NA> | 8 | <NA> | 10 |
row3 | 11 | 12 | 13 | 14 | 15 |
row4 | <NA> | <NA> | <NA> | <NA> | <NA> |
axis값에 따른 결측치 제거 수행¶
axis가 0인 경우 행에 대해서,
axis가 1인 경우 열에 대해서 결측치 제거 수행
axis=0인 경우 결측치가 포함된 모든 행이 제거됨
df.dropna(axis=0)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row3 | 11 | 12 | 13 | 14 | 15 |
axis=1인 경우 결측치가 포함된 모든 열이 제거됨
예시의 경우 모든열에 NA가 존재하므로 빈 값 반환
df.dropna(axis=1)
row1 |
---|
row2 |
row3 |
row4 |
how로 연산기준 정할 경우¶
기본적으로 how는 'any'이며 이 경우 한 값이라도 NA를 가진다면 해당 레이블 제거
df.dropna(how='any')
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row3 | 11 | 12 | 13 | 14 | 15 |
how를 'all'로 할 경우 모든값이 NA인 레이블만 삭제
df.dropna(how='all')
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row2 | 6 | <NA> | 8 | <NA> | 10 |
row3 | 11 | 12 | 13 | 14 | 15 |
thresh 이용하는 경우¶
thresh를 이용해 정상값의 수를 보장 가능
thresh가 3일 경우 정상값(<NA>
가 아닌)이 3개 미만인 경우 그 축을 삭제
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row2 | 6 | <NA> | 8 | <NA> | 10 |
row3 | 11 | 12 | 13 | 14 | 15 |
row4 | <NA> | <NA> | <NA> | <NA> | <NA> |
df.dropna(thresh=3)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row2 | 6 | <NA> | 8 | <NA> | 10 |
row3 | 11 | 12 | 13 | 14 | 15 |
thresh가 4인 경우 정상값이 4개 미만인 경우 그 축을 삭제
df.dropna(thresh=4)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row3 | 11 | 12 | 13 | 14 | 15 |
subset 인수를 통한 레이블 지정¶
subset에 리스트형태의 값을 입력함으로써 결측치 제거를 수행할 레이블 지정 가능
col1, col2에 대해 결측치가 있는 경우만 제거
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row2 | 6 | <NA> | 8 | <NA> | 10 |
row3 | 11 | 12 | 13 | 14 | 15 |
row4 | <NA> | <NA> | <NA> | <NA> | <NA> |
df.dropna(subset=['col1', 'col2'])
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 1 | 2 | 3 | <NA> | 5 |
row3 | 11 | 12 | 13 | 14 | 15 |
inplace 인수를 통한 원본의 수정¶
Pandas에서 공통적으로 inplace인수는 원본의 수정을 의미
inplace가 True인 경우 원본이 수정됨
df.dropna(inplace=True)
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row3 | 11 | 12 | 13 | 14 | 15 |
fillna¶
누락된 데이터를 대신할 값을 채우거나 'ffill'이나 'bfill' 같은 보간 메서드 적용
col = ['col1','col2','col3','col4','col5']
row = ['row1','row2','row3','row4','row5']
na = np.nan
data = [[na, 2,na, 4,na],
[ 6, 7,na, 9,na],
[11,na,na,14,15],
[na,17,na,na,20],
[na,22,na,na,25]]
df = pd.DataFrame(data,row,col)
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | NaN | 2.0 | NaN | 4.0 | NaN |
row2 | 6.0 | 7.0 | NaN | 9.0 | NaN |
row3 | 11.0 | NaN | NaN | 14.0 | 15.0 |
row4 | NaN | 17.0 | NaN | NaN | 20.0 |
row5 | NaN | 22.0 | NaN | NaN | 25.0 |
value의 형식에 따른 사용¶
value가 숫자나 문자일 경우 그대로 결측값 대체.
df.fillna('A')
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | A | 2.0 | A | 4.0 | A |
row2 | 6.0 | 7.0 | A | 9.0 | A |
row3 | 11.0 | A | A | 14.0 | 15.0 |
row4 | A | 17.0 | A | A | 20.0 |
row5 | A | 22.0 | A | A | 25.0 |
dict 형태로 입력할 경우 각각 레이블값에 대해 원하는 값으로의 변경 가능
dict = {'col1':'A', 'col2':'B', 'col3':'C', 'col4':'D', 'col5':'E'}
df.fillna(value=dict)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | A | 2.0 | C | 4.0 | E |
row2 | 6.0 | 7.0 | C | 9.0 | E |
row3 | 11.0 | B | C | 14.0 | 15.0 |
row4 | A | 17.0 | C | D | 20.0 |
row5 | A | 22.0 | C | D | 25.0 |
method 인수 사용¶
method 인수에 bfill을 입력할 경우 결측값이 바로 아래값과 동일하게 설정됨
df.backfill()이나 df.bfill()과 완전히 동일한 기능 수행
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | NaN | 2.0 | NaN | 4.0 | NaN |
row2 | 6.0 | 7.0 | NaN | 9.0 | NaN |
row3 | 11.0 | NaN | NaN | 14.0 | 15.0 |
row4 | NaN | 17.0 | NaN | NaN | 20.0 |
row5 | NaN | 22.0 | NaN | NaN | 25.0 |
df.fillna(method='bfill')
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | 6.0 | 2.0 | NaN | 4.0 | 15.0 |
row2 | 6.0 | 7.0 | NaN | 9.0 | 15.0 |
row3 | 11.0 | 17.0 | NaN | 14.0 | 15.0 |
row4 | NaN | 17.0 | NaN | NaN | 20.0 |
row5 | NaN | 22.0 | NaN | NaN | 25.0 |
method 인수에 ffill을 입력할 경우 결측값이 바로 위값과 동일하게 설정
df.pad()나 df.ffill()과 완전히 동일한 기능 수행
df.fillna(method='ffill')
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | NaN | 2.0 | NaN | 4.0 | NaN |
row2 | 6.0 | 7.0 | NaN | 9.0 | NaN |
row3 | 11.0 | 7.0 | NaN | 14.0 | 15.0 |
row4 | 11.0 | 17.0 | NaN | 14.0 | 20.0 |
row5 | 11.0 | 22.0 | NaN | 14.0 | 25.0 |
limit 인수 사용¶
limit인수는 각 레이블 값에 대해서 결측치 변경을 수행할 횟수
행 기준일 경우 왼쪽부터, 열 기준일 경우 위에서부터 수행
df
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | NaN | 2.0 | NaN | 4.0 | NaN |
row2 | 6.0 | 7.0 | NaN | 9.0 | NaN |
row3 | 11.0 | NaN | NaN | 14.0 | 15.0 |
row4 | NaN | 17.0 | NaN | NaN | 20.0 |
row5 | NaN | 22.0 | NaN | NaN | 25.0 |
df.fillna('A', limit=2)
col1 | col2 | col3 | col4 | col5 | |
---|---|---|---|---|---|
row1 | A | 2.0 | A | 4.0 | A |
row2 | 6.0 | 7.0 | A | 9.0 | A |
row3 | 11.0 | A | NaN | 14.0 | 15.0 |
row4 | A | 17.0 | NaN | A | 20.0 |
row5 | NaN | 22.0 | NaN | A | 25.0 |
isnull(isna)¶
누락되거나 NA인 값을 알려주는 불리언값이 저장된 같은 형의 객체 반환
col = ['col1','col2','col3','col4']
row = ['row1','row2','row3']
data = [[1,2,pd.NA,4],
[np.nan,6,7,8],
[9,10,11,None]]
df = pd.DataFrame(data,row,col)
df
col1 | col2 | col3 | col4 | |
---|---|---|---|---|
row1 | 1.0 | 2 | <NA> | 4.0 |
row2 | NaN | 6 | 7 | 8.0 |
row3 | 9.0 | 10 | 11 | NaN |
isna나 isnull의 경우 결측값이면 True 반환
df.isnull()
col1 | col2 | col3 | col4 | |
---|---|---|---|---|
row1 | False | False | True | False |
row2 | True | False | False | False |
row3 | False | False | False | True |
notnull(notna)¶
isnull과 반대
notna나 notnull의 경우 결측값이면 False 반환
df.notnull()
col1 | col2 | col3 | col4 | |
---|---|---|---|---|
row1 | True | True | False | True |
row2 | False | True | True | True |
row3 | True | True | True | False |
7.1.1 누락된 데이터 골라내기¶
dropna¶
누락된 데이터를 골라내는 몇 가지 방법이 있는데,
pandas.isnull이나 불리언 색인을 사용해 직접 손으로 제거하는 것도 한 가지 방법이지만,
dropna을 매우 유용하게 사용 가능
Series에서 dropna 메서드를 적용하면 널이 아닌(non-null) 데이터와 색인값만 들어 있는 Series 반환
from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
data
0 1.0 1 NaN 2 3.5 3 NaN 4 7.0 dtype: float64
data.dropna()
0 1.0 2 3.5 4 7.0 dtype: float64
위 코드는 다음과 동일
notnull¶
data[data.notnull()]
0 1.0 2 3.5 4 7.0 dtype: float64
DataFrame 객체의 경우 조금 복잡한데, 모두 NA 값인 로우나 컬럼을 제외시키거나
NA 값을 하나라도 포함하고 있는 로우나 컬럼 제외시킴
dropna는 기본적으로 NA 값을 하나라도 포함하고 있는 로우 제외시킴
data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
[NA, NA, NA], [NA, 6.5, 3.]])
data
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 6.5 | 3.0 |
1 | 1.0 | NaN | NaN |
2 | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
cleaned = data.dropna()
cleaned
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 6.5 | 3.0 |
how='all' 옵션을 넘기면 모두 NA 값인 로우만 제외
data.dropna(how='all')
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 6.5 | 3.0 |
1 | 1.0 | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
컬럼을 제외시키는 방법도 동일하게 동작. 옵션으로 axis=1 넘겨주기
data[4]= NA
data
0 | 1 | 2 | 4 | |
---|---|---|---|---|
0 | 1.0 | 6.5 | 3.0 | NaN |
1 | 1.0 | NaN | NaN | NaN |
2 | NaN | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 | NaN |
data.dropna(axis=1, how='all')
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 6.5 | 3.0 |
1 | 1.0 | NaN | NaN |
2 | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
몇 개 이상의 값이 들어 있는 로우만 살펴보고 싶다면 thresh 인자에 원하는 값 넘기기
df = pd.DataFrame(np.random.randn(7,3))
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | -1.037365 | -1.077267 |
1 | -0.964502 | -1.123697 | -0.019287 |
2 | -0.509794 | -1.116728 | 0.725433 |
3 | -0.202545 | -1.504613 | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
df.iloc[:4, 1] = NA
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | NaN | -1.077267 |
1 | -0.964502 | NaN | -0.019287 |
2 | -0.509794 | NaN | 0.725433 |
3 | -0.202545 | NaN | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
df.iloc[:2, 2] = NA
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | NaN | NaN |
1 | -0.964502 | NaN | NaN |
2 | -0.509794 | NaN | 0.725433 |
3 | -0.202545 | NaN | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
df.dropna()
0 | 1 | 2 | |
---|---|---|---|
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
df.dropna(thresh=2)
0 | 1 | 2 | |
---|---|---|---|
2 | -0.509794 | NaN | 0.725433 |
3 | -0.202545 | NaN | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
7.1.2 결측치 채우기¶
fillna¶
fillna 메서드에 채워주고 싶은 값을 넘겨주면 된다
df.fillna(0)
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | 0.000000 | 0.000000 |
1 | -0.964502 | 0.000000 | 0.000000 |
2 | -0.509794 | 0.000000 | 0.725433 |
3 | -0.202545 | 0.000000 | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
fillna에 사전값 넘겨서 각 컬럼마다 다른 값 채우기
df.fillna({1:0.5, 2:0})
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | 0.500000 | 0.000000 |
1 | -0.964502 | 0.500000 | 0.000000 |
2 | -0.509794 | 0.500000 | 0.725433 |
3 | -0.202545 | 0.500000 | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
기존 객체 변경
_ = df.fillna(0,inplace=True)
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.059722 | 0.000000 | 0.000000 |
1 | -0.964502 | 0.000000 | 0.000000 |
2 | -0.509794 | 0.000000 | 0.725433 |
3 | -0.202545 | 0.000000 | -0.720672 |
4 | -1.000958 | -0.020754 | 0.561334 |
5 | -1.469984 | 0.189896 | 1.294239 |
6 | -0.798884 | 0.573638 | -0.516727 |
재색인에서 사용 가능한 보간 메서드는 fillna 메서드에서도 사용 가능
df = pd.DataFrame(np.random.randn(6,3))
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.123367 | 0.651640 | -0.080896 |
1 | 1.135335 | 2.878529 | 0.209940 |
2 | -0.359406 | -2.514877 | -1.076355 |
3 | 0.609690 | 0.764702 | 1.757571 |
4 | -0.597993 | -0.178420 | -1.306245 |
5 | -1.356092 | -0.181653 | 1.192103 |
df.iloc[2:, 1] = NA
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.123367 | 0.651640 | -0.080896 |
1 | 1.135335 | 2.878529 | 0.209940 |
2 | -0.359406 | NaN | -1.076355 |
3 | 0.609690 | NaN | 1.757571 |
4 | -0.597993 | NaN | -1.306245 |
5 | -1.356092 | NaN | 1.192103 |
df.iloc[4:, 2] = NA
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.123367 | 0.651640 | -0.080896 |
1 | 1.135335 | 2.878529 | 0.209940 |
2 | -0.359406 | NaN | -1.076355 |
3 | 0.609690 | NaN | 1.757571 |
4 | -0.597993 | NaN | NaN |
5 | -1.356092 | NaN | NaN |
ffill¶
df.fillna(method='ffill')
0 | 1 | 2 | |
---|---|---|---|
0 | 1.123367 | 0.651640 | -0.080896 |
1 | 1.135335 | 2.878529 | 0.209940 |
2 | -0.359406 | 2.878529 | -1.076355 |
3 | 0.609690 | 2.878529 | 1.757571 |
4 | -0.597993 | 2.878529 | 1.757571 |
5 | -1.356092 | 2.878529 | 1.757571 |
df.fillna(method='ffill', limit=2)
0 | 1 | 2 | |
---|---|---|---|
0 | 1.123367 | 0.651640 | -0.080896 |
1 | 1.135335 | 2.878529 | 0.209940 |
2 | -0.359406 | 2.878529 | -1.076355 |
3 | 0.609690 | 2.878529 | 1.757571 |
4 | -0.597993 | NaN | 1.757571 |
5 | -1.356092 | NaN | 1.757571 |
Series의 평균값이나 중간값 전달
data = pd.Series([1, NA, 3.5, NA, 7])
data
0 1.0 1 NaN 2 3.5 3 NaN 4 7.0 dtype: float64
data.fillna(data.mean())
0 1.000000 1 3.833333 2 3.500000 3 3.833333 4 7.000000 dtype: float64
- fillna 함수 인자
인자 | 설명 |
---|---|
value | 비어 있는 값을 채울 스칼라값이나 사전 형식의 객체 |
method | 보간 방식. 기본적으로 'ffill' 사용 |
axis | 값을 채워 넣을 축. 기본값은 axis=0 |
inplace | 복사본을 생성하지 않고 호출한 객체 변경. 기본값은 False |
limit | 값을 앞 혹은 뒤에서부터 몇 개까지 채울지 지정 |
7.2 데이터 변형¶
7.2.1 중복 제거¶
data = pd.DataFrame({'k1':['one', 'two']*3 + ['two'],
'k2':[1,1,2,3,3,4,4]})
data
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
6 | two | 4 |
duplicated¶
DataFrame의 duplicated 메서드는 각 로우가 중복인지 아닌지 알려주는 불리언 Series 반환
data.duplicated()
0 False 1 False 2 False 3 False 4 False 5 False 6 True dtype: bool
drop_duplicates¶
drop_duplicates는 duplicated 배열이 False인 DataFrame 반환
data.drop_duplicates()
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
이 두 메서드는 기본적으로 모든 컬럼에 적용되며 중복을 찾아내기 위한 부분합 따로 지정가능
새로운 컬럼을 하나 추가하고 'k1' 컬럼에 기반해 중복을 걸러내려면 다음과 같이 함
data['v1'] = range(7)
data
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
5 | two | 4 | 5 |
6 | two | 4 | 6 |
data.drop_duplicates(['k1'])
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
duplicated와 drop_duplicates는 기본적으로 처음 발견된 값 유지
keep= 'last'옵션을 넘기면 마지막으로 발견된 값 반환
data.drop_duplicates(['k1', 'k2'], keep='last')
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
6 | two | 4 | 6 |
7.2.2 함수나 매핑을 이용해 데이터 변형¶
data = pd.DataFrame({'food':['bacon', 'pulled pork', 'bacon',
'Pastrami', 'corned beef', 'Bacon',
'pastrami', 'honey ham', 'nova lox'],
'ounces':[4,3,12,6,7.5,8,3,5,6]})
data
food | ounces | |
---|---|---|
0 | bacon | 4.0 |
1 | pulled pork | 3.0 |
2 | bacon | 12.0 |
3 | Pastrami | 6.0 |
4 | corned beef | 7.5 |
5 | Bacon | 8.0 |
6 | pastrami | 3.0 |
7 | honey ham | 5.0 |
8 | nova lox | 6.0 |
해당 육류가 어떤 동물의 고기인지 알려주는 컬럼 하나 추가
meat_to_animal = {
'bacon':'pig',
'pulled pork':'pig',
'pastrami':'cow',
'corned beef':'cow',
'honey ham':'pig',
'nova lox':'salmon'
}
str.lower¶
str.lower메서드로 모두 소문자로 변경
lowercased = data['food'].str.lower()
lowercased
0 bacon 1 pulled pork 2 bacon 3 pastrami 4 corned beef 5 bacon 6 pastrami 7 honey ham 8 nova lox Name: food, dtype: object
map¶
data['animal'] = lowercased.map(meat_to_animal)
data
food | ounces | animal | |
---|---|---|---|
0 | bacon | 4.0 | pig |
1 | pulled pork | 3.0 | pig |
2 | bacon | 12.0 | pig |
3 | Pastrami | 6.0 | cow |
4 | corned beef | 7.5 | cow |
5 | Bacon | 8.0 | pig |
6 | pastrami | 3.0 | cow |
7 | honey ham | 5.0 | pig |
8 | nova lox | 6.0 | salmon |
함수로도 같은 일 수행가능
data['food'].map(lambda x: meat_to_animal[x.lower()])
0 pig 1 pig 2 pig 3 cow 4 cow 5 pig 6 cow 7 pig 8 salmon Name: food, dtype: object
7.2.3 값 치환¶
replace¶
fillna 메서드를 사용해 누락된 값을 채우는 일은 일반적인 값 치환 작업
위에서 살펴봤듯이 map 메서드를 한 객체 안에서 값의 부분집합을 변경하는 데 사용했다면
replace 메서드는 같은 작업에 대해 좀 더 간단하고 유연한 방법 제공
다음 Series 객체를 살펴보자
data = pd.Series([1., -999., 2., -999., -1000., 3.])
data
0 1.0 1 -999.0 2 2.0 3 -999.0 4 -1000.0 5 3.0 dtype: float64
-999는 누락된 데이터를 나타내기 위한 값
replace메서드를 이용하면 이 값을 pandas에서 인식할 수 있는 NA 값으로 치환한 새로운 Series생성가능(인자로 inplace=True를 넘기지 않았다면)
data.replace(-999, np.nan)
0 1.0 1 NaN 2 2.0 3 NaN 4 -1000.0 5 3.0 dtype: float64
여러 개의 값을 한 번에 치환하려면 하나의 값 대신 치환하려는 값의 리스트 넘기기
data.replace([-999, -1000], np.nan)
0 1.0 1 NaN 2 2.0 3 NaN 4 NaN 5 3.0 dtype: float64
치환하려는 값마다 다른 값으로 치환하려면 누락된 값 대신 새로 지정할 값의 리스트 사용
data.replace([-999, -1000], [np.nan, 0])
0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
두 개의 리스트 대신 사전 이용 가능
data.replace({-999:np.nan, -1000:0})
0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
7.2.4 축 색인 이름 바꾸기¶
data = pd.DataFrame(np.arange(12).reshape((3,4)),
index=['Ohio', 'Colorado', 'New York'],
columns=['one', 'two', 'three', 'four'])
data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colorado | 4 | 5 | 6 | 7 |
New York | 8 | 9 | 10 | 11 |
index.map¶
Series와 마찬가지로 축 색인에도 map 메서드 있음
transform = lambda x:x[:4].upper()
data.index.map(transform)
Index(['OHIO', 'COLO', 'NEW '], dtype='object')
대문자로 변경된 축 이름을 DataFrame의 index에 바로 대입 가능
data.index = data.index.map(transform)
data
one | two | three | four | |
---|---|---|---|---|
OHIO | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
rename¶
원래 객체를 변경하지 않고 새로운 객체 생성 시에는 rename 메서드 사용
data.rename(index=str.title, columns=str.upper)
ONE | TWO | THREE | FOUR | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colo | 4 | 5 | 6 | 7 |
New | 8 | 9 | 10 | 11 |
str.title은 각 단어의 첫 번째 문자를 대문자로 나머지는 소문자로 변환
특히 rename 메서드는 사전 형식의 객체를 이용해 축 이름 중 일부만 변경하는 것도 가능
data.rename(index = {'OHIO':'INDIANA'},
columns={'three':'peekaboo'})
one | two | peekaboo | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
inplace = True¶
원본 데이터 바로 변경하려면 inplace=True 옵션 사용
data.rename(index={'OHIO':'INDIANA'}, inplace=True)
data
one | two | three | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
7.2.5 개별화와 양자화¶
연속성 데이터는 종종 개별로 분할하거나 아니면 분석을 위해 그룹별로 나누기도 하는데,
수업에 참여하는 학생 그룹 데이터가 있고, 나이대에 따라 분류한다고 가정
ages = [20,22,25,27,21,23,37,31,61,45,41,32]
cut¶
이 데이터를 pandas의 cut 함수를 이용해 18-25, 26-35, 35-60, 60 이상 그룹으로 나누어보자
bins = [18,25,35,60,100]
cats = pd.cut(ages, bins)
cats
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]] Length: 12 Categories (4, interval[int64, right]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
pandas에서 반환하는 객체는 Categorical이라는 특수한 객체
결과에서 보이는 그룹은 pandas.cut으로 계산된 것
이 객체는 그룹이름이 담긴 배열
이 Categorical 객체는 codes 속성에 있는 ages 데이터에 대한 카테고리 이름을 Categories라는 배열에 내부적으로 담고 있다
codes¶
cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
categories¶
cats.categories
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], dtype='interval[int64, right]')
pd.value_counts¶
pd.value_counts(cats)
(18, 25] 5 (25, 35] 3 (35, 60] 3 (60, 100] 1 dtype: int64
pd.value_counts(cats)는 pandas.cut 결과에 대한 그룹 수
간격을 나타내는 표기법은 중괄호로 시작해서 대괄호로 끝나는데 *중괄호 쪽의 값은 포함하지 않고 대괄호 쪽의 값을 포함*하는 간격을 나타냄
right=False를 넘겨서 중괄호 대신 대괄호쪽이 포함되지 않도록 바꿀 수 있음
pd.cut(ages,[18,26,36,61,100], right=False)
[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)] Length: 12 Categories (4, interval[int64, left]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]
labels 옵션으로 그룹의 이름을 직접 넘겨줄 수도 있음
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)
['Youth', 'Youth', 'Youth', 'YoungAdult', 'Youth', ..., 'YoungAdult', 'Senior', 'MiddleAged', 'MiddleAged', 'YoungAdult'] Length: 12 Categories (4, object): ['Youth' < 'YoungAdult' < 'MiddleAged' < 'Senior']
만약 cut 함수에 명시적으로 그룹의 경곗값을 넘기지 않고 *그룹의 개수*를 넘겨주면
데이터에서 최솟값과 최댓값을 기준으로 균등한 길이의 그룹을 자동 계산
어떤 균등분포 내에서 4개의 그룹으로 나누는 경우를 생각해보자
import numpy as np
data = np.random.rand(20)
data
array([0.65079047, 0.739267 , 0.33241366, 0.57308546, 0.1491171 , 0.85153479, 0.52057412, 0.4809686 , 0.69404239, 0.49728343, 0.88690792, 0.14018818, 0.22045589, 0.84936994, 0.13688589, 0.8055531 , 0.1013762 , 0.70275279, 0.39766615, 0.66281261])
precision¶
pd.cut(data, 4, precision=2)
[(0.71, 0.94], (0.26, 0.49], (0.026, 0.26], (0.49, 0.71], (0.71, 0.94], ..., (0.026, 0.26], (0.26, 0.49], (0.71, 0.94], (0.49, 0.71], (0.49, 0.71]] Length: 20 Categories (4, interval[float64, right]): [(0.026, 0.26] < (0.26, 0.49] < (0.49, 0.71] < (0.71, 0.94]]
precision=2 옵션은 소수점 아래 2자리까지로 제한
qcut¶
이를 위한 가장 적합한 함수로 qcut이 있는데 표본 변위치를 기반으로 데이터를 나누어줌
cut 함수를 사용하면 데이터의 분산에 따라 각각의 그룹마다 데이터 수가 다르게 나뉘는 경우가 많음
qcut은 표준 변위치를 사용하기 때문에 적당히 같은 크기의 그룹으로 나누기 가능
data = np.random.randn(1000) # 정규 분포
cats = pd.qcut(data, 4) # 4분위로 분류
cats
[(-3.54, -0.65], (-0.65, 0.025], (0.025, 0.684], (0.025, 0.684], (-3.54, -0.65], ..., (-0.65, 0.025], (0.025, 0.684], (-3.54, -0.65], (0.684, 3.387], (-3.54, -0.65]] Length: 1000 Categories (4, interval[float64, right]): [(-3.54, -0.65] < (-0.65, 0.025] < (0.025, 0.684] < (0.684, 3.387]]
pd.value_counts(cats)
(-3.54, -0.65] 250 (-0.65, 0.025] 250 (0.025, 0.684] 250 (0.684, 3.387] 250 dtype: int64
cut 함수와 유사하게 변위치 직접 지정 가능(변위치는 0부터 1까지)
pd.qcut(data, [0,0.1,0.5, 0.9, 1.])
[(-1.255, 0.025], (-1.255, 0.025], (0.025, 1.215], (0.025, 1.215], (-1.255, 0.025], ..., (-1.255, 0.025], (0.025, 1.215], (-1.255, 0.025], (0.025, 1.215], (-1.255, 0.025]] Length: 1000 Categories (4, interval[float64, right]): [(-3.54, -1.255] < (-1.255, 0.025] < (0.025, 1.215] < (1.215, 3.387]]
numpy의 np.random.randint vs rand/randn¶
np.random.*seed* seed를 통한 난수 생성
np.random.*randint* 균일 분포의 정수 난수 1개 생성
np.random.*rand* 0부터 1 사이의 균일 분포에서 난수 matrix array 생성
np.random.*randn* 가우시안 표준 정규 분포에서 난수 matrix array 생성
np.random.shuffle 기존의 데이터의 순서 바꾸기
np.random.choice 기존의 데이터에서 sampling
np.unique 데이터에서 중복된 값을 제거하고 중복되지 않는 값의 리스트를 출력
np.bincount 발생하지 않은 사건에 대해서도 카운트를 해줌
np.random.randint¶
(시작, n-1) 사이의 랜덤숫자 1개 뽑아내기
np.random.randint(6) #0부터 5까지 랜덤한 숫자 1개
5
np.random.randint(1,20) # 1부터 19까지 랜덤숫자 1개
14
np.random.rand(m,n)¶
0 ~ 1의 균일분포 표준정규분포 난수를 matrix array(m,n) 생성
np.random.rand(6)
array([0.5319145 , 0.77896499, 0.21196557, 0.71900784, 0.32207577, 0.37443035])
np.random.rand(3,2)
array([[0.07705894, 0.55700212], [0.29827558, 0.56253635], [0.66198224, 0.64458529]])
np.random.randn(m,n)¶
평균 0, 표준편차 1의 가우시안 표준정규분포 난수를 matrix array(m,n) 생성
np.random.randn(6)
array([ 1.22839343, -0.27137404, -2.37149264, 0.8740423 , -1.02416159, -0.64960738])
np.random.randn(3,2)
array([[-0.67742682, 0.26907058], [-0.65633506, 0.54955242], [ 2.16694812, -0.44285335]])
7.2.6 특잇값을 찾고 제외하기¶
배열 연산을 수행할 때는 특잇값(outlier)를 제외하거나 적당한 값으로 대체하는 것이 중요
적절히 분산된 값이 담겨 잇는 다음 DataFrame을 살펴보자.
data = pd.DataFrame(np.random.randn(1000, 4))
data
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.773577 | -0.552240 | -0.068250 | 0.337443 |
1 | 1.104798 | 1.613470 | -0.343073 | 0.241429 |
2 | -0.216391 | -0.509969 | -0.880070 | -1.280800 |
3 | 3.379592 | -1.192191 | 1.467162 | 1.830935 |
4 | 3.387019 | -0.708173 | -0.395083 | -1.232405 |
... | ... | ... | ... | ... |
995 | 0.716992 | 0.852429 | 1.127615 | 0.184839 |
996 | 0.483992 | 0.995459 | -1.882597 | 0.818562 |
997 | 0.568487 | 0.350285 | -0.019806 | 0.003262 |
998 | -0.140211 | 0.297317 | -0.049651 | 0.368414 |
999 | 0.797491 | 0.317061 | 0.277754 | -1.230214 |
1000 rows × 4 columns
data.describe()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 0.021956 | -0.021349 | -0.030139 | -0.053767 |
std | 1.015655 | 0.932248 | 0.970404 | 0.995527 |
min | -4.098433 | -2.656883 | -3.040000 | -3.346149 |
25% | -0.638619 | -0.679098 | -0.694012 | -0.737299 |
50% | -0.012248 | -0.032211 | -0.047610 | -0.059516 |
75% | 0.720834 | 0.647759 | 0.599406 | 0.603536 |
max | 3.387019 | 2.807338 | 3.517849 | 2.887271 |
이 DataFrame의 한 컬럼에서 절댓값이 3을 초과하는 값 찾아내기
col = data[2]
col
0 -0.068250 1 -0.343073 2 -0.880070 3 1.467162 4 -0.395083 ... 995 1.127615 996 -1.882597 997 -0.019806 998 -0.049651 999 0.277754 Name: 2, Length: 1000, dtype: float64
col[np.abs(col)>3]
453 3.517849 628 -3.040000 Name: 2, dtype: float64
절댓값이 3을 초과하는 값이 들어 있는 모든 로우를 선택하려면 불리언 DataFrame에서 any 메서드 사용
data[(np.abs(data)> 3).any(1)]
C:\Users\user\AppData\Local\Temp\ipykernel_16460\2539779106.py:1: FutureWarning: In a future version of pandas all arguments of DataFrame.any and Series.any will be keyword-only. data[(np.abs(data)> 3).any(1)]
0 | 1 | 2 | 3 | |
---|---|---|---|---|
3 | 3.379592 | -1.192191 | 1.467162 | 1.830935 |
4 | 3.387019 | -0.708173 | -0.395083 | -1.232405 |
21 | -3.139872 | 1.400512 | -0.226815 | -2.051160 |
241 | 0.488862 | 0.513119 | -0.154404 | -3.314437 |
327 | 0.620175 | 1.206607 | 0.185722 | -3.346149 |
453 | -0.866080 | -0.218469 | 3.517849 | -0.780566 |
628 | -0.167762 | -0.505919 | -3.040000 | 0.849586 |
670 | -4.098433 | -0.213114 | -0.961243 | -0.981130 |
718 | -3.025873 | 0.319829 | -1.954982 | 1.198572 |
855 | 0.113234 | 0.045608 | 1.215440 | -3.105813 |
861 | 3.100030 | -1.268264 | -0.040728 | -0.440121 |
np.sign¶
이 기준대로 쉽게 값 선택 가능
아래 코드로 -3이나 3을 초과하는 값을 -3 또는 3으로 지정 가능
data[np.abs(data)>3] = np.sign(data)*3
data.describe()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 0.022354 | -0.021349 | -0.030616 | -0.053001 |
std | 1.008567 | 0.932248 | 0.968522 | 0.993138 |
min | -3.000000 | -2.656883 | -3.000000 | -3.000000 |
25% | -0.638619 | -0.679098 | -0.694012 | -0.737299 |
50% | -0.012248 | -0.032211 | -0.047610 | -0.059516 |
75% | 0.720834 | 0.647759 | 0.599406 | 0.603536 |
max | 3.000000 | 2.807338 | 3.000000 | 2.887271 |
np.sign(data)는 data 값이 양수인지 음수인지에 따라 1이나 -1이 담긴 배열을 반환
data
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.773577 | -0.552240 | -0.068250 | 0.337443 |
1 | 1.104798 | 1.613470 | -0.343073 | 0.241429 |
2 | -0.216391 | -0.509969 | -0.880070 | -1.280800 |
3 | 3.000000 | -1.192191 | 1.467162 | 1.830935 |
4 | 3.000000 | -0.708173 | -0.395083 | -1.232405 |
... | ... | ... | ... | ... |
995 | 0.716992 | 0.852429 | 1.127615 | 0.184839 |
996 | 0.483992 | 0.995459 | -1.882597 | 0.818562 |
997 | 0.568487 | 0.350285 | -0.019806 | 0.003262 |
998 | -0.140211 | 0.297317 | -0.049651 | 0.368414 |
999 | 0.797491 | 0.317061 | 0.277754 | -1.230214 |
1000 rows × 4 columns
np.sign(data).head()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | -1.0 | -1.0 | 1.0 |
1 | 1.0 | 1.0 | -1.0 | 1.0 |
2 | -1.0 | -1.0 | -1.0 | -1.0 |
3 | 1.0 | -1.0 | 1.0 | 1.0 |
4 | 1.0 | -1.0 | -1.0 | -1.0 |
7.2.7 치환과 임의 샘플링¶
np.random.permutation¶
numpy.random.permutation 함수를 이용하면 Series나 DataFrame의 로우를 쉽게 임의 순서로 재배치 가능
순서로 바꾸고 싶은 만큼의 길이를 permutation 함수로 넘기면 바뀐 순서가 담긴 정수 배열 생성
df = pd.DataFrame(np.arange(5*4).reshape((5,4)))
df
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
4 | 16 | 17 | 18 | 19 |
sampler = np.random.permutation(5)
sampler
array([1, 2, 4, 0, 3])
이 배열은 iloc 기반의 색인이나 take 함수에서 사용 가능
take¶
이처럼 5개의 순열을 랜덤으로 섞은 뒤 df.take(samper)를 하면 행이 섞임
df.take(sampler)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
4 | 16 | 17 | 18 | 19 |
0 | 0 | 1 | 2 | 3 |
3 | 12 | 13 | 14 | 15 |
sample¶
만약 df에서 랜덤으로 3개를 샘플링 하고 싶다면, sample(n=3) 사용
df.sample(n=3)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 4 | 5 | 6 | 7 |
0 | 0 | 1 | 2 | 3 |
4 | 16 | 17 | 18 | 19 |
replace=True¶
랜덤으로 뽑을 숫자가 전체 데이터보다 크다면, 중복으로 뽑음
반복 선택을 허용하려면 sample에 replace=True 옵션 사용
choices = pd.Series([5,7,-1,6,4])
choices
0 5 1 7 2 -1 3 6 4 4 dtype: int64
draws = choices.sample(n=10, replace=True)
draws
2 -1 3 6 1 7 1 7 4 4 0 5 0 5 1 7 1 7 0 5 dtype: int64
샘플 추출(sample)¶
sample 메서드는 DataFrame이나 Series에서 무작위로 몇 개의 값(레이블)을 출력하는 메서드
col = ['col1','col2','col3']
row = ['row1','row2','row3','row4','row5']
data = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
df = pd.DataFrame(data,row,col)
df
col1 | col2 | col3 | |
---|---|---|---|
row1 | 1 | 2 | 3 |
row2 | 4 | 5 | 6 |
row3 | 7 | 8 | 9 |
row4 | 10 | 11 | 12 |
row5 | 13 | 14 | 15 |
n의 사용과 replace의 사용¶
n을 설정함으로써 원하는 갯수의 추출 가능
n을 2로 설정함으로써 2개의 행 추출
df.sample(2)
col1 | col2 | col3 | |
---|---|---|---|
row4 | 10 | 11 | 12 |
row2 | 4 | 5 | 6 |
행이 5이지만 replace=True로 설정해 중복 추출을 허용한다면,
n이 5보다 커도됨
df.sample(10, replace=True)
col1 | col2 | col3 | |
---|---|---|---|
row1 | 1 | 2 | 3 |
row2 | 4 | 5 | 6 |
row4 | 10 | 11 | 12 |
row3 | 7 | 8 | 9 |
row1 | 1 | 2 | 3 |
row2 | 4 | 5 | 6 |
row3 | 7 | 8 | 9 |
row5 | 13 | 14 | 15 |
row5 | 13 | 14 | 15 |
row1 | 1 | 2 | 3 |
frac을 사용하는 경우¶
frac을 통해 전체에 대한 추출 비율 정할 수 있음
frac을 0.4로 설정함으로써, 전체에서 40%인 2개 추출
df.sample(frac=0.4)
col1 | col2 | col3 | |
---|---|---|---|
row1 | 1 | 2 | 3 |
row4 | 10 | 11 | 12 |
7.2.8 표시자/더미 변수 계산¶
get_dummies¶
통계 모델이나 머신러닝 애플리케이션을 위한 또 다른 데이터 변환은 분류값을 '더미'나 '표시자'행렬로 전환하는 것
만약 어떤 DataFrame의 한 컬럼에 k가지 값이 있다면 k개의 컬럼이 있는 DataFrame이나 행렬을 만들고 값으로는 1과 0을 채워넣을 것
pandas의 get_dummies가 이를 위한 함수
df = pd.DataFrame({'key': ['b','b','a','c','a','b'],
'data1':range(6)})
df
key | data1 | |
---|---|---|
0 | b | 0 |
1 | b | 1 |
2 | a | 2 |
3 | c | 3 |
4 | a | 4 |
5 | b | 5 |
pd.get_dummies(df['key'])
a | b | c | |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 0 | 1 |
4 | 1 | 0 | 0 |
5 | 0 | 1 | 0 |
prefix¶
표시자 DataFrame 안에 있는 컬럼에 접두어(prefix)를 추가한 후 다른 데이터와 병합하고 싶을 경우가 있다.
get_dummies 함수의 prefix 인자를 사용하면 이를 수행 가능
dummies = pd.get_dummies(df['key'], prefix='key')
dummies
key_a | key_b | key_c | |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 0 | 1 |
4 | 1 | 0 | 0 |
5 | 0 | 1 | 0 |
df[['data1']]
data1 | |
---|---|
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
df_with_dummy = df[['data1']].join(dummies)
df_with_dummy
data1 | key_a | key_b | key_c | |
---|---|---|---|---|
0 | 0 | 0 | 1 | 0 |
1 | 1 | 0 | 1 | 0 |
2 | 2 | 1 | 0 | 0 |
3 | 3 | 0 | 0 | 1 |
4 | 4 | 1 | 0 | 0 |
5 | 5 | 0 | 1 | 0 |
DataFrame의 한 로우가 여러 카테고리에 속한다면 조금 복잡해지는데, 자세한 내용은 'MovieLens의 영화 평점 데이터'에서 살펴보자.
mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('movielens/movies.dat', sep='::', header=None, names=mnames)
C:\Users\ADMIN\AppData\Local\Temp\ipykernel_24236\527416868.py:1: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'. movies = pd.read_table('movielens/movies.dat', sep='::', header=None, names=mnames)
movies[:10]
movie_id | title | genres | |
---|---|---|---|
0 | 1 | Toy Story (1995) | Animation|Children's|Comedy |
1 | 2 | Jumanji (1995) | Adventure|Children's|Fantasy |
2 | 3 | Grumpier Old Men (1995) | Comedy|Romance |
3 | 4 | Waiting to Exhale (1995) | Comedy|Drama |
4 | 5 | Father of the Bride Part II (1995) | Comedy |
5 | 6 | Heat (1995) | Action|Crime|Thriller |
6 | 7 | Sabrina (1995) | Comedy|Romance |
7 | 8 | Tom and Huck (1995) | Adventure|Children's |
8 | 9 | Sudden Death (1995) | Action |
9 | 10 | GoldenEye (1995) | Action|Adventure|Thriller |
각 장르마다 표시자 값을 추가하려면 먼저 데이터 묶음에서 유일한 장르 목록을 추출해야 함
all_genres = []
for x in movies.genres:
all_genres.extend(x.split('|'))
genres = pd.unique(all_genres)
이제 장르는 아래와 같은 모양
genres
array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy', 'Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror', 'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir', 'Western'], dtype=object)
이제 표시자 DataFrame을 생성하기 위해 0으로 초기화된 DataFrame 생성
zero_matrix = np.zeros((len(movies), len(genres)))
dummies = pd.DataFrame(zero_matrix, columns = genres)
dummies
Animation | Children's | Comedy | Adventure | Fantasy | Romance | Drama | Action | Crime | Thriller | Horror | Sci-Fi | Documentary | War | Musical | Mystery | Film-Noir | Western | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3878 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3879 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3880 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3881 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3882 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3883 rows × 18 columns
각 영화를 순회하면서 dummies의 각 로우의 항목을 1로 설정
각 장르의 컬럼 색인을 계산하기 위해 dummies.columns 사용
gen = movies.genres[0]
gen.split('|')
['Animation', "Children's", 'Comedy']
dummies.columns.get_indexer(gen.split('|'))
array([0, 1, 2], dtype=int64)
그리고 .iloc를 이용해 색인에 맞게 값 대입
for i, gen in enumerate(movies.genres):
indices = dummies.columns.get_indexer(gen.split('|'))
dummies.iloc[i, indices] =1
그리고 앞에서 한 것처럼 movies와 조합
movies_windic = movies.join(dummies.add_prefix('Genre_'))
movies_windic.iloc[0]
movie_id 1 title Toy Story (1995) genres Animation|Children's|Comedy Genre_Animation 1.0 Genre_Children's 1.0 Genre_Comedy 1.0 Genre_Adventure 0.0 Genre_Fantasy 0.0 Genre_Romance 0.0 Genre_Drama 0.0 Genre_Action 0.0 Genre_Crime 0.0 Genre_Thriller 0.0 Genre_Horror 0.0 Genre_Sci-Fi 0.0 Genre_Documentary 0.0 Genre_War 0.0 Genre_Musical 0.0 Genre_Mystery 0.0 Genre_Film-Noir 0.0 Genre_Western 0.0 Name: 0, dtype: object
get_dummies와 cut 같은 이산함수를 잘 조합하면 통계 애플리케이션에서 유용하게 사용 가능
np.random.seed(12345)
values = np.random.rand(10)
values
array([0.92961609, 0.31637555, 0.18391881, 0.20456028, 0.56772503, 0.5955447 , 0.96451452, 0.6531771 , 0.74890664, 0.65356987])
bins = [0,0.2,0.4,0.6, 0.8, 1]
pd.get_dummies(pd.cut(values, bins))
(0.0, 0.2] | (0.2, 0.4] | (0.4, 0.6] | (0.6, 0.8] | (0.8, 1.0] | |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 |
3 | 0 | 1 | 0 | 0 | 0 |
4 | 0 | 0 | 1 | 0 | 0 |
5 | 0 | 0 | 1 | 0 | 0 |
6 | 0 | 0 | 0 | 0 | 1 |
7 | 0 | 0 | 0 | 1 | 0 |
8 | 0 | 0 | 0 | 1 | 0 |
9 | 0 | 0 | 0 | 1 | 0 |
여기서는 예제 값이 불변하도록 numpy.random.seed 함수를 이용해 난수 시드값을 정함
7.3 문자열 다루기¶
7.3.1 문자열 객체 메서드¶
쉼표로 구분된 문자열은 split 메서드 이용해 분리
val = 'a,b, guido'
val.split(',')
['a', 'b', ' guido']
split 메서드는 종종 공백 문자(줄바꿈 문자 포함)을 제거하는 strip 메서드와 조합해 사용하기도 함
pieces = [x.strip() for x in val.split(',')]
pieces
['a', 'b', 'guido']
이렇게 분리된 문자열은 더하기 연산을 사용해 ::문자열과 합칠 수도 있음
first, second, third = pieces
first + '::' + second + '::' + third
'a::b::guido'
하지만 이 방법은 실용적이면서 범용적인 메서드는 아니다.
빠르고 좀 더 파이썬스러운 방법은 리스트나 튜플을 ::문자열의 join 메서드로 전달하는 것
'::'.join(pieces)
'a::b::guido'
일치하는 부분문자열의 위치를 찾는 방법도 있다.
index나 find를 사용하는 것도 가능하지만 파이썬의 in 예약어를 사용하면 일치하는 부분문자열을 쉽게 찾기 가능
val
'a,b, guido'
'guido' in val
True
val.index(',')
1
val.find(':')
-1
find와 index의 차이점은 index의 경우 문자열을 찾지 못하면 예외를 발생시킴
find의 경우 -1 반환
val.index(':')
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[123], line 1 ----> 1 val.index(':') ValueError: substring not found
count는 특정 부분문자열이 몇 건 발견되었는지 반환
val.count(',')
2
replace는 찾아낸 패턴을 다른 문자열로 치환
이 메서드는 대체할 문자열로 비어 있는 문자열을 넘겨서 패턴을 삭제하기 위한 방법으로 자주 사용되기도 함
val.replace(',', '::')
'a::b:: guido'
val.replace(',', '')
'ab guido'
- 파이썬 내장 문자열 함수
count¶
문자열에서 겹치지 않는 부분문자열의 개수 반환
ex_str = "following dialogue"
ex_str.count('o')
3
type(ex_str.count('o'))
int
ex_str.count('o', 0, 8)
2
ex_str.count('o', 3, 8)
1
endswith¶
문자열이 주어진 접미사로 끝날 경우 True 반환
ex_str = "It’s been a long time!"
ex_str.endswith('time!')
True
ex_str.endswith('!')
True
startswith¶
문자열이 주어진 접두사로 시작할 경우 True 반환
ex_url = "https://wikidocs.net"
ex_url.startswith('https')
True
ex_url.startswith('Https')
False
ex_url[8]
'w'
ex_url.startswith('wiki',8)
True
join¶
문자열을 구분자로 하여 다른 문자열을 순서대로 이어붙임
li = ['A', 'B', 'C', 'D', 'E']
'-'.join(li)
'A-B-C-D-E'
'**'.join(li)
'A**B**C**D**E'
' '.join(li)
'A B C D E'
''.join(li)
'ABCDE'
index¶
부분문자열의 첫 번째 글자의 위치 반환
부분문자열이 없을 경우 ValueError 예외 발생
find¶
*첫 번째* 부분문자열의 첫 번째 글자의 위치 반환
index와 유사하지만 부분문자열이 없을 경우 -1 반환
rfind¶
*마지막* 부분문자열의 첫 번째 글자의 위치 반환
부분문자열이 없을 경우 -1 반환
replace¶
문자열을 다른 문자열로 치환
strip, rstrip, lstrip¶
개행 문자를 포함한 공백 문자 제거
lstrip은 문자열의 시작 부분에 있는 공백 문자만 제거하며,
rstrip은 문자열의 마지막 부분에 있는 공백 문자만 제거
ex_str = " hello "
ex_str.strip()
'hello'
split¶
문자열을 구분자를 기준으로 부분문자열의 리스트로 분리
A = '1,2,3'.split(',')
A
['1', '2', '3']
C = '1,2,,3,'.split(',')
C
['1', '2', '', '3', '']
lower¶
알파벳 문자를 소문자로 변환
url = "HTTP://WIKIDOCS.NET"
url.lower()
'http://wikidocs.net'
upper¶
알파벳 문자를 대문자로 변환
url = 'http://wikidocs.net'
url.upper()
'HTTP://WIKIDOCS.NET'
casefold¶
문자를 소문자로 변환
지역 문자들은 그에 상응하는 대체 문자로 교체됨
ljust, rjust¶
문자열을 오른쪽 또는 왼쪽으로 정렬하고 주어진 길이에서 문자열의 길이를 제외한 나머지 부분은 공백 문자를 채워 넣음
7.3.2 정규 표현식¶
여러 가지 공백 문자(탭, 스페이스, 개행 문자)가 포함된 문자열을 나누고 싶다면
하나 이상의 공백 문자를 의미하는 \s+를 사용해 문자열 분리
import re
text = 'foo bar\t baz \tqux'
text
'foo bar\t baz \tqux'
re.split('\s+', text)
['foo', 'bar', 'baz', 'qux']
re.split('\s+', text)를 사용하면 먼저 정규 표현식이 *컴파일*되고 그 다음에 split 메서드가 실행됨
re.compile로 직접 정규 표현식을 컴파일하고 그렇게 얻은 정규 표현식 객체를 재사용하는 것도 가능
regex = re.compile('\s+')
regex.split(text)
['foo', 'bar', 'baz', 'qux']
정규 표현식에 매칭되는 모든 패턴의 목록을 얻고 싶다면 findall 메서드 사용
regex.findall(text)
[' ', '\t ', ' \t']
match와 search는 findall 메서드와 관련이 있다
findall은 문자열에서 일치하는 모든 부분문자열을 찾아주지만 search 메서드는 패턴과 일치하는 첫 번째 존재 반환
match메서드는 이보다 더 엄격해서 문자열의 시작부분에서 일치하는 것만 찾아줌
다음은 이메일 주소를 검사하는 정규 표현식
text = """Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
#re.IGNORECASE는 정규 표현식이 대소문자를 가지리 않도록 함
regex = re.compile(pattern, flags=re.IGNORECASE)
findall 메서드를 사용해 이메일 주소의 리스트 생성
regex.findall(text)
['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']
search는 텍스트에서 첫 번째 이메일 주소만을 찾아줌.
위 정규 표현식에 대한 match 객체는 그 정규 표현 패턴이 문자열 내에서 위치하는 시작점과 끝점만을 알려줌
m = regex.search(text)
m
<re.Match object; span=(5, 20), match='dave@google.com'>
text[m.start():m.end()]
'dave@google.com'
regex.match는 None을 반환. 왜냐하면 그 정규 표현 패턴이 문자열의 시작점에서부터 일치하는지 검사하기 때문
print(regex.match(text))
None
sub 메서드는 찾은 패턴을 주어진 문자열로 치환해 새로운 문자열을 반환
print(regex.sub('REDACTED', text))
Dave REDACTED Steve REDACTED Rob REDACTED Ryan REDACTED
이메일 주소를 찾아서 동시에 각 이메일 주소를 사용자 이름, 도메인 이름, 도메인 접미사 세가지 컴포넌트로 나눠야 한다면 각 패턴을 괄호로 묶어줌
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
regex = re.compile(pattern, flags=re.IGNORECASE)
이렇게 만든 match 객체를 이용하면 groups 메서드로 각 패턴 컴포넌트의 튜플을 얻을 수 있음
m = regex.match('wesm@bright.net')
m.groups()
('wesm', 'bright', 'net')
패턴에 그룹이 존재한다면 findall 메서드는 튜플의 목록 반환
regex.findall(text)
[('dave', 'google', 'com'), ('steve', 'gmail', 'com'), ('rob', 'gmail', 'com'), ('ryan', 'yahoo', 'com')]
sub 역시 마찬가지로 \1, \2 같은 특수한 기호를 이용해 각 패턴 그룹에 접근 가능
\1은 첫 번째로 찾은 그룹을, \2는 두 번째로 찾은 그룹을 의미
print(regex.sub(r'Username:\1, Domain:\2, Suffix:\3', text))
Dave Username:dave, Domain:google, Suffix:com Steve Username:steve, Domain:gmail, Suffix:com Rob Username:rob, Domain:gmail, Suffix:com Ryan Username:ryan, Domain:yahoo, Suffix:com
정규표현식 메서드 302쪽 참고
7.3.3 pandas의 벡터화된 문자열 함수¶
data = {'Dave':'dave@google.com', 'Steve':'steve@gmail.com',
'Rob':'rob@gmail.com', 'Wes':np.nan}
data = pd.Series(data)
data
Dave dave@google.com Steve steve@gmail.com Rob rob@gmail.com Wes NaN dtype: object
data.isnull()
Dave False Steve False Rob False Wes True dtype: bool
각 이메일 주소가 'gmail'을 포함하고 있는지 str.contains를 이용해 검사
data.str.contains('gmail')
Dave False Steve True Rob True Wes NaN dtype: object
정규 표현식을 IGNORECASE 같은 re 옵션과 함께 사용하는 것도 가능
pattern
'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'
data.str.findall(pattern, flags=re.IGNORECASE) #대소문자 무시
Dave [(dave, google, com)] Steve [(steve, gmail, com)] Rob [(rob, gmail, com)] Wes NaN dtype: object
벡터화된 요소를 꺼내오는 몇 가지 방법이 있는데 str.get을 이용하거나 str 속성의 색인 이용
matches = data.str.match(pattern, flags=re.IGNORECASE)
matches
Dave True Steve True Rob True Wes NaN dtype: object
내재된 리스트의 원소에 접근하려면 색인 넘기기
304까지정리
표 형식의 데이터를 재배치하는 다양한 기본 연산이 존재
이런 연산을 *재형성* 또는 *피벗*이라고 함
데이터베이스나 CSV 파일에 여러 개의 시계열 데이터를 저장하는 일반적인 방법은 시간 순서대로 나열하는 것
예제 데이터를 읽어서 시계열 데이터를 다뤄보자
data = pd.read_csv('macrodata.csv')
data.head()
year | quarter | realgdp | realcons | realinv | realgovt | realdpi | cpi | m1 | tbilrate | unemp | pop | infl | realint | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1959.0 | 1.0 | 2710.349 | 1707.4 | 286.898 | 470.045 | 1886.9 | 28.98 | 139.7 | 2.82 | 5.8 | 177.146 | 0.00 | 0.00 |
1 | 1959.0 | 2.0 | 2778.801 | 1733.7 | 310.859 | 481.301 | 1919.7 | 29.15 | 141.7 | 3.08 | 5.1 | 177.830 | 2.34 | 0.74 |
2 | 1959.0 | 3.0 | 2775.488 | 1751.8 | 289.226 | 491.260 | 1916.4 | 29.35 | 140.5 | 3.82 | 5.3 | 178.657 | 2.74 | 1.09 |
3 | 1959.0 | 4.0 | 2785.204 | 1753.7 | 299.356 | 484.052 | 1931.3 | 29.37 | 140.0 | 4.33 | 5.6 | 179.386 | 0.27 | 4.06 |
4 | 1960.0 | 1.0 | 2847.699 | 1770.5 | 331.722 | 462.199 | 1955.5 | 29.54 | 139.6 | 3.50 | 5.2 | 180.007 | 2.31 | 1.19 |
periods = pd.PeriodIndex(year = data.year, quarter=data.quarter, name='date')
columns = pd.Index(['realgdp', 'infl', 'unemp'], name = 'item')
data = data.reindex(columns=columns)
data.index = periods.to_timestamp('D', 'end')
ldata = data.stack().reset_index().rename(columns={0:'value'})
PeriodIndex는 시간 간격을 나타내기 위한 자료형으로, 연도(year)과 분기(quarter) 컬럼을 합친다.
ldata는 이제 다음과 같다
ldata[:10]
date | item | value | |
---|---|---|---|
0 | 1959-03-31 23:59:59.999999999 | realgdp | 2710.349 |
1 | 1959-03-31 23:59:59.999999999 | infl | 0.000 |
2 | 1959-03-31 23:59:59.999999999 | unemp | 5.800 |
3 | 1959-06-30 23:59:59.999999999 | realgdp | 2778.801 |
4 | 1959-06-30 23:59:59.999999999 | infl | 2.340 |
5 | 1959-06-30 23:59:59.999999999 | unemp | 5.100 |
6 | 1959-09-30 23:59:59.999999999 | realgdp | 2775.488 |
7 | 1959-09-30 23:59:59.999999999 | infl | 2.740 |
8 | 1959-09-30 23:59:59.999999999 | unemp | 5.300 |
9 | 1959-12-31 23:59:59.999999999 | realgdp | 2785.204 |
pivoted = ldata.pivot('date', 'item', 'value')
C:\Users\user\AppData\Local\Temp\ipykernel_2920\3911351351.py:1: FutureWarning: In a future version of pandas all arguments of DataFrame.pivot will be keyword-only. pivoted = ldata.pivot('date', 'item', 'value')
pivoted
item | infl | realgdp | unemp |
---|---|---|---|
date | |||
1959-03-31 23:59:59.999999999 | 0.00 | 2710.349 | 5.8 |
1959-06-30 23:59:59.999999999 | 2.34 | 2778.801 | 5.1 |
1959-09-30 23:59:59.999999999 | 2.74 | 2775.488 | 5.3 |
1959-12-31 23:59:59.999999999 | 0.27 | 2785.204 | 5.6 |
1960-03-31 23:59:59.999999999 | 2.31 | 2847.699 | 5.2 |
... | ... | ... | ... |
2008-09-30 23:59:59.999999999 | -3.16 | 13324.600 | 6.0 |
2008-12-31 23:59:59.999999999 | -8.79 | 13141.920 | 6.9 |
2009-03-31 23:59:59.999999999 | 0.94 | 12925.410 | 8.1 |
2009-06-30 23:59:59.999999999 | 3.37 | 12901.504 | 9.2 |
2009-09-30 23:59:59.999999999 | 3.56 | 12990.341 | 9.6 |
203 rows × 3 columns
pivot 메서드의 처음 두 인자는 로우와 컬럼 색인으로 사용될 컬럼 이름이고 마지막 두 인자는 DataFrame에 채워 넣을 값을 담고 있는 컬럼 이름
한 번에 두 개의 컬럼을 동시에 변형한다고 하자
ldata['value2'] = np.random.randn(len(ldata))
ldata[:10]
date | item | value | value2 | |
---|---|---|---|---|
0 | 1959-03-31 23:59:59.999999999 | realgdp | 2710.349 | 0.559769 |
1 | 1959-03-31 23:59:59.999999999 | infl | 0.000 | -1.929406 |
2 | 1959-03-31 23:59:59.999999999 | unemp | 5.800 | -1.536088 |
3 | 1959-06-30 23:59:59.999999999 | realgdp | 2778.801 | -0.325483 |
4 | 1959-06-30 23:59:59.999999999 | infl | 2.340 | -1.606365 |
5 | 1959-06-30 23:59:59.999999999 | unemp | 5.100 | -0.033743 |
6 | 1959-09-30 23:59:59.999999999 | realgdp | 2775.488 | -0.367464 |
7 | 1959-09-30 23:59:59.999999999 | infl | 2.740 | 0.062062 |
8 | 1959-09-30 23:59:59.999999999 | unemp | 5.300 | 0.057192 |
9 | 1959-12-31 23:59:59.999999999 | realgdp | 2785.204 | -1.495289 |
마지막 인자를 생략해 계층적 컬럼을 가지는 DataFrame을 얻을 수 있다
pivoted = ldata.pivot('date', 'item')
C:\Users\user\AppData\Local\Temp\ipykernel_2920\2725291772.py:1: FutureWarning: In a future version of pandas all arguments of DataFrame.pivot will be keyword-only. pivoted = ldata.pivot('date', 'item')
pivoted[:5]
value | value2 | |||||
---|---|---|---|---|---|---|
item | infl | realgdp | unemp | infl | realgdp | unemp |
date | ||||||
1959-03-31 23:59:59.999999999 | 0.00 | 2710.349 | 5.8 | -1.929406 | 0.559769 | -1.536088 |
1959-06-30 23:59:59.999999999 | 2.34 | 2778.801 | 5.1 | -1.606365 | -0.325483 | -0.033743 |
1959-09-30 23:59:59.999999999 | 2.74 | 2775.488 | 5.3 | 0.062062 | -0.367464 | 0.057192 |
1959-12-31 23:59:59.999999999 | 0.27 | 2785.204 | 5.6 | 0.540428 | -1.495289 | 1.040933 |
1960-03-31 23:59:59.999999999 | 2.31 | 2847.699 | 5.2 | -0.032334 | 0.111673 | -0.270219 |
pivoted['value'][:5]
item | infl | realgdp | unemp |
---|---|---|---|
date | |||
1959-03-31 23:59:59.999999999 | 0.00 | 2710.349 | 5.8 |
1959-06-30 23:59:59.999999999 | 2.34 | 2778.801 | 5.1 |
1959-09-30 23:59:59.999999999 | 2.74 | 2775.488 | 5.3 |
1959-12-31 23:59:59.999999999 | 0.27 | 2785.204 | 5.6 |
1960-03-31 23:59:59.999999999 | 2.31 | 2847.699 | 5.2 |
pivot은 단지 set_index를 사용해서 계층적 색인을 만들고 unstack 메서드를 이용해서 형태를 변경하는 단축키 같은 메서드
unstacked = ldata.set_index(['date', 'item']).unstack('item')
unstacked[:7]
value | value2 | |||||
---|---|---|---|---|---|---|
item | infl | realgdp | unemp | infl | realgdp | unemp |
date | ||||||
1959-03-31 23:59:59.999999999 | 0.00 | 2710.349 | 5.8 | -1.929406 | 0.559769 | -1.536088 |
1959-06-30 23:59:59.999999999 | 2.34 | 2778.801 | 5.1 | -1.606365 | -0.325483 | -0.033743 |
1959-09-30 23:59:59.999999999 | 2.74 | 2775.488 | 5.3 | 0.062062 | -0.367464 | 0.057192 |
1959-12-31 23:59:59.999999999 | 0.27 | 2785.204 | 5.6 | 0.540428 | -1.495289 | 1.040933 |
1960-03-31 23:59:59.999999999 | 2.31 | 2847.699 | 5.2 | -0.032334 | 0.111673 | -0.270219 |
1960-06-30 23:59:59.999999999 | 0.14 | 2834.390 | 5.2 | 0.774434 | -0.301433 | -0.239348 |
1960-09-30 23:59:59.999999999 | 2.70 | 2839.022 | 5.6 | 0.733531 | 1.160128 | -0.406044 |
### 8.3.3 넓은 형식에서
'Python, Jupyter 🐍 > [python]파이썬 데이터분석' 카테고리의 다른 글
9장 (0) | 2023.04.18 |
---|---|
10장 데이터 집계와 그룹 연산 (0) | 2023.04.17 |
[오류] 'jupyter'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는배치 파일이 아닙니다. (0) | 2023.04.09 |
[오류] TypeError: concat() got an unexpected keyword argument 'join_axes' (0) | 2023.04.07 |
numpy (0) | 2023.03.30 |