DataFrame apply() 메서드
DataFrame에 대해 Function을 적용하고 싶다면 apply()함수를 활용하면 좋습니다. 이 메서드는 첫 인자로 함수를 필수값으로 받습니다. 경우에 따라 두번째 인자로 axis를 사용할 수 있습니다. axis 인자는 0이 default입니다.
- axis가 0 or 'index'인 경우 각 column에 대해 함수를 적용합니다.
- axis가 1 or 'columns'인 경우 각 row에 대해 함수를 적용합니다.
아래의 예제에서는 Numpy의 np.sort를 사용하여 적용해보았습니다. np.sqrt는 각 요소마다 적용되는 함수(universal function, ufunc)로 이 경우에는 np.sqrt(df)와 동일한 결과를 가져옵니다.
df = pd.DataFrame([4,9] * 3 , columns = ['A','B'])
df.apply(np.sqrt) ## 아래와 동일한 결과를 가져온다.
np.sqrt(df)
이번 예제에서는 차원 축소 함수(reducing function)인 sum을 활용해보겠습니다. 이때는 axis의 값에 따라 값의 축소되는 방향이 달라서 서로 결과가 다릅니다.
# axis가 0일 때 각 column별 집계합니다.
df.apply(np.sum, axis = 0)
# axis가 1일 때 각 row별 집계합니다.
df.apply(np.sum, axis = 1)
함수의 return이 column 마다 리스트를 반환하면 DataFrame의 결과를 얻을 수 있습니다.
함수의 return이 row마다 리스트를 반환하면 각 row마다 리스트를 하나의 값으로 취급하는 Series 타입의 결과가 나옵니다.
df.apply(lambda x : [1,2], axis = 0)
df.apply(lambda x : [1,2], axis = 1)
DataFrame apply() 메서드 : result_type
result_type = 'broadcast'를 인수로 전달하면 동일한 shape의 결과를 보장합니다.
함수로부터 반환되는게 리스트인지 스칼라인지 상관없이 axis 방향으로 브로드캐스트합니다.
결과의 column label은 본래의 column label을 유지합니다.
df.apply(lambda x : [1,2], axis = 1, result_type = 'broadcast'
@ 그림 추가
result_type = 'broadcast'를 인수로 전달할 때 함수로부터 return되는 값이 기존 shape로 브로드캐스트 할 수 없는 shape이라면 ValueError가 발생합니다.
DataFrame apply() 메서드 활용
예를 들어 column마다 최대값 최소값의 차이를 구하고 싶으면 다음과 같은 lambda 함수를 넣으면 됩니다.
df3.apply(lambda x : x.max() - x.min())
다음과 같이 타이타닉호의 승객 중 나이 20살을 기준으로 성인(adult)과 미성년자(child)를 구별하는 label column을 만들 수 있습니다.
titanic['adult/child'] = titanic.apply(lambda x : 'adult' if x.age > 2 else 'child', axis = 1)
DataFrame fillna() 메서드
fillna 메서드를 활용하여 Nan 값을 0으로 변경하는 예제
df.fillna(0)
fillna 메서드 value 값으로 column label을 key로 갖는 딕셔너리를 전달할 수 있습니다.
그러면 column마다 NaN을 대치하는 값을 각각 다르게 지정할 수 있습니다.
values = {"A" : 0, "B" : 1, "C" : 2, "D" :3}
df.fillna(value = values)
limit 키워드 인자에 숫자를 전달하여 그 숫자만큼 column마다 변경 횟수를 제한할 수 있습니다.
values = {"A" : 0, "B" : 1, "C" : 2, "D" :3}
df.fillna(value = values, limit = 1) # 제일 위에 있는 것부터
DataFrame을 value로 전달해서 NaN 값을 대체할 수 있습니다. 다만 column label과 row index가 일치하지 않으면 적용되지 않습니다.
@ 그림대체
DataFrame astype() 메서드
이번에는 dtype이 모두 int64인 DataFrame에 대해 astype('int32')를 호출해서 모든 column의 dtype을 형변환 시켰습니다.
df.astype('int32')
DataFrame 실수 값을 카테고리 값으로 변환
실수 값을 크기 기준으로 하여 카테고리 값으로 변환하고 싶을 때는 다음과 같은 명령을 사용합니다.
- cut : 실수 값의 경계선을 지정하는 경우
- x = 1차원 형태의 배열 형태가 옵니다.
- bins = int, 스칼라를 요소로 갖는 시퀀스가 옵니다.
- qcut : 개수가 똑같은 구간으로 나누는 경우(분위수)
- x = 1d ndarray 혹은 Sereis
- q = int 혹은 분위수를 나타내는 1.이하의 실수를 요소로 갖는 리스트(e.g. [0, .25, .75, 1.])예를 들어 다음과 같은 나이데이터가 있습니다.
ages = [0, 2, 10, 21, 23, 37, 31, 61, 20, 41, 32, 101]
cut 명령을 사용하면 실수 값을 다음 값처럼 카테고리 값으로 바꿀 수 있습니다. bins 인수는 카테고리를 나누는 기준 값이 됩니다.
영역을 넘는 값은 NaN으로 처리됩니다.
bins = [1, 20, 30, 50, 70, 100]
labels = ['미성년자', '청년', '장년', '중년', '노년']
cats = pd.cut(ages,bins, labels = labels)
cats
cut 명령이 반환하는 값은 Categorical 클래스 객체입니다. 이 객체는 categories 속성으로 label 문자열을, codes 속성으로 정수로 인코딩한 카테고리 값을 가집니다.
cats.categories
cats.codes
category는 문자열이 아닙니다. 이를 문자열로 만들려면 astype() 메서드를 사용해야합니다.
qcut 명령은 구간 경계선을 지정하지 않고 분위수와 같이 데이터 개수가 같도록 구간을 나눕니다. 예를 들어 다음코드는 1000개의 데이터를 4개의 구간으로 나누는데 각 구간은 250개씩의 데이터를 가집니다.
data = np.random.randn(1000)
cats = pd.qcut(data, 4, labels = \["Q1","Q2","Q3","Q4"\])
연습문제
import pandas as pd
import seaborn as sns
titanic = sns.load_dataset("titanic")
titanic.loc[titanic['age'].isna() == True, 'age'] = titanic['age'].mean()
bins = [1, 20, 30, 50, 70, 100]
labels = ["미성년자", "청년", "장년", "중년", "노년"]
titanic['age'] = pd.cut(titanic['age'], bins, labels = labels)
# value_counts(normalize = True) 이면 상대적인 비율값을 구해준다.
titanic['age'].value_counts(normalize = True)
DataFrame 인덱스 설정 및 제거
때로는 DataFrame에 인덱스로 들어가 있어야할 데이터가 일반 데이터 column에 들어가 있거나 반대로 일반 데이터 column에 있어야할 것이 인덱스로 되어 있을 수 있습니다. 이 때는 set_index 명령이나 reset_index 명령으로 인덱스와 일반 데이터 column을 교환할 수 있습니다.
- set_index : 기존의 row 인덱스를 제거하고 데이터 column 중 하나를 인덱스로 지정합니다. 이 때 기존의 인덱스는 없어집니다.
df1.set_index("c1")
df1.reset_index()
df1.reset_index(drop = True)
df1
DataFrame 다중인덱스
row나 column에 여러 계층을 가지는 인덱스 즉, 다중인덱스(multi-index)를 설정할 수도 있습니다.
DataFrame을 생성할 때 columns 인수에 다음 예제처럼 리스트의 리스트(행렬) 형태로 인덱스를 넣으면 다중인덱스로 됩니다.
다중 인덱스는 이름을 지정하면 더 편리하게 사용할 수 있습니다. column 인덱스들의 이름 지정은 columns 객체의 names 속성에
리스트를 넣어서 지정합니다.
df3.columns.names = ["Cidx1", "Cidx2"]
df3.index.names = ["Ridx1", "Ridx2"]
df3
DataFrame row 인덱스와 column 인덱스 교환
stack() 메서드나 unstack() 메서드를 쓰면 column 인덱스를 row 인덱스로 바꾸거나 반대로 row 인덱스를 column 인덱스로
바꿀 수 있습니다.
- stack() 메서드 : column 인덱스 -> row 인덱스로 변환
stack 메서드를 실행하면 column 인덱스가 반시계 방향으로 90도 회전한 것과 비슷한 모양이 됩니다. - unstack() 메서드 : row 인덱스 -> column 인덱스로 변환
unstack 메서드를 실행하면 row 인덱스가 시계방향으로 90도 회전한 것과 비슷합니다.
DataFrame 다중인덱스가 있는 경우의 인덱싱
DataFrame이 다중 인덱스를 가지는 경우에는 인덱스 값이 하나의 label이나 숫자가 아니라 ()로 둘러싸인 튜플이 되어야합니다.
예를 들어 앞에서 만든 df3 DataFrame의 경우 다음 코드와 같이 인덱싱 할 수 있습니다.
df3[('B','C')]
loc 인덱서를 사용하는 경우에도 마찬가지로 튜플을 사용해서 인덱싱 해야합니다.
df3.loc[0, ('B','C')]
만약 하나의 레벨 값만 넣으면 다중 인덱스 중에서 가장 상위의 값을 지정한 것으로 반환합니다.
df3['A']
특정 레벨의 모든 인덱스 값을 인덱싱 할 때는 슬라이스를 사용합니다. 다만 다중 인덱스의 튜플 내에서는 콜론(:), 즉 슬라이스 기호를 사용할 수 없고, 대신 slice(None) 값을 사용행햐ㅏㅂ니다.
df4.loc[("M", slice(None)), :]
다중 인덱스가 있는 DataFrame을 정렬할 때는 level 인수를 사용하여 어떤 인덱스를 기준으로 정렬하는지 알려주어야합니다.
df5.sort_index(level = 0)
df5.sort_index(axis = 1, level = 0)
'데이터 분석 및 시각화 > 파이썬' 카테고리의 다른 글
[Python] Pandas 시리즈 문자열 변경 메소드(replace) (1) | 2023.01.30 |
---|---|
[Python] 기초정리(Pandas(Merge, 시계열 자료, Groupby)) (0) | 2023.01.20 |
[Python] 기초 정리(Pandas_DataFrame2) (2) | 2023.01.19 |
[Python] 클래스 연습문제 (1) | 2023.01.18 |
[Python] 기초 정리(Pandas_DataFrame) (0) | 2023.01.18 |