저번 시간까지 Series, DataFrame, Slicing과 같은 아주 기초적인 Pandas의 내용을 배워보았다. 이제 이를 살짝 응용하는 시간을 가져볼 것이다.
글쓴이도 python이 거의 2년만이라 아직 서툰점 참고바란다.
먼저 본격적으로 Masking을 다루기 전에 numpy를 집고갈 필요가 있다.
numpy 라이브러리를 사용하는 방법은 다음과 같다.
import numpy as np
c = np.random.randint(1, 5, size(2, 3))
print(c)
[[4 1 2]
[2 1 4]]
numpy라는 라이브러리를 가져와서 np라는 별칭으로 사용하는 모습을 볼 수 있다. numpy는 수치 계산을 위한 파이썬 라이브러리로, 대규모의 다차원 배열과 행렬 연산에 필요한 다양한 함수를 제공한다.
다음 부분인
c = np.random.randint(1, 5, size=(2, 3)):
이 부분은 1 이상 5 미만의 정수를 무작위로 선택하여 2x3 크기의 배열을 생성한다. np.random.randint(a, b, size(c, d))라고 했을 때,
a 이상, b 미만에 주의하도록 하자
이제 다음 코드를 살펴보면
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(1, 10, (2, 2)), index = [0, 1], columns["A", "B"])
#DataFrame 출력
print(df)
#Column A의 각 원소가 5보다 작거나 같은지 출력
print(df["A"] <= 5)
#Column A의 원소가 5보다 작고, Column B의 원소가 8보다 작은 행 추출
print(df.query("A <= 5 and B <= 8"))
A B
0 3 8
1 9 4
0 True
1 False
Name: A, dtype: bool
A B
0 3 8
그렇다 보시는 것처럼 출력문 안에 조건을 걸어서 df["A"] <= 5라고 하면 Column A의 각 원소를 탐색하고 각 원소들이 작거나 같은지 출력한다. 그렇기 때문에 True, False를 사용하는 Bool자료형의 DataFrame을 가진다.
import pandas as pd
import numpy as np
df = pd.DataFrame([[1, 2, 3, 4], [1, 2, 3, 4]], index =[0, 1], columns["A", "B", "C", "D"])
print(df)
print('----------------')
df = df.apply(lambda x : x + 1)
print(df)
print('----------------')
def addOne(x):
return x + 1
df = df + apply(addOne)
print(df)
A B C D
0 1 2 3 4
1 1 2 3 4
----------------
A B C D
0 2 3 4 5
1 2 3 4 5
----------------
A B C D
0 3 4 5 6
1 3 4 5 6
이 코드를 살펴보면 2가지 형태의 연산방식을 보여주고 있다.
그전에, DataFrame을 선언하는 방식을 복습할겸 다시 살펴보자
df = pd.DataFrame([[1, 2, 3, 4], [1, 2, 3, 4]], index =[0, 1], columns["A", "B", "C", "D"])
이 부분은 어떻게 설정이 되었는가? 이전 글을 살펴보면 알 수 있듯이, 2X4의 DataFrame을 만들고 그 안의 원소들을 [1, 2, 3, 4], [1, 2, 3, 4]로 설정한다. 그리고 index번호를 각각 0, 1로 설정하고 Column번호를 각각 A, B, C, D로 설정하는 그런 문장이다. 이제 익숙해져야 한다.
자 이제
첫번째 연산형식을 살펴보자면,
df = df.apply(lambda x : x + 1)
apply 함수를 사용하여 DataFrame의 각 요소에 1을 더하는 문장이다.
두번째 연산형식을 살펴보면
def addOne(x):
return x + 1
df = df.apply(addOne)
이 부분은 따로 각 원소에 1을 더해주는 addOne 함수를 새로 생성한 후 apply 함수를 사용하여 DataFrame의 각 요소에 1을 더하는 문장이다.
그렇다면, 각 원소들이 정수형이 아닌 다른 자료형이면 어떠할까? 다음 코드로 살펴보도록 하자.
import pandas as pd
import numpy as np
df = pd.DataFrame([
['Apple', 'Apple', 'Carrot', 'Banana'],
['Durian', 'Banana', 'Apple', 'Carrot']],
index = [0, 1],
columns["A", "B", "C", "D"]
)
print(df)
df = df.replace({"Apple" : "Airport"})
print(df)
A B C D
0 Apple Apple Carrot Banana
1 Durian Banana Apple Carrot
A B C D
0 Airport Airport Carrot Banana
1 Durian Banana Airport Carrot
자 코드에서 알 수 있듯이 문자형이나 다른 자료형 같은 경우 replace()를 통해서 DataFrame안 원소를 교체가 가능하다.
Pandas 내 DataFrame의 그룹화 방법은 마찬가지로 여러가지가 있는데 보통 gruopby를 통해서 그룹화가 가능하다.
(아마 이 부분이 생각보다 중요할 것이다.)
일단 코드를 먼저 보여주겠다.
import pandas as pd
ds = pd.DataFrame([
['Apple', 7, 'Fruit'],
['Banana', 3, 'Fruit'],
['Beef', 5, 'Meal'],
['Kimchi', 4, 'Meal'],
columns=["Name", "Frequency", "Type"])
print(df)
print(df.groupby(['Type']).sum())
Name Frequency Type
0 Apple 7 Fruit
1 Banana 3 Fruit
2 Beef 5 Meal
3 Kimchi 4 Meal
Frequency
Type
Fruit 10
Meal 9
다른 부분보다 이해하기 살짝 까다로울 수 있으나, 설명하자면
셍성된 DataFrame에서 'Type' 열을 기준으로 그룹화하고, 각 그룹의 'Frequency' 값을 합산한다. 그 부분이
print(df.groupby(['Type']).sum())
이 부분이다. 이해되었는가? 그렇다면 여러가지 응용버전을 보여주겠다.
import numpy as np
import pandas as pd
df = pd.DataFrame([
['Apple', 7, 5, 'Fruit'],
['Banana', 3, 6, 'Fruit'],
['Beef', 5, 2, 'Meal'],
['Kimchi', 4, 8, 'Meal'],
columns=["Name", "Frequency", "Importance", "Type"])
print(df)
print(df.groupby(["Type"]).aggregate([min, max, np.average]))
Name Frequency Importance Type
0 Apple 7 5 Fruit
1 Banana 3 6 Fruit
2 Beef 5 2 Meal
3 Kimchi 4 8 Meal
Frequency Importance
min max average min max average
Type
Fruit 3 7 5.0 5 6 5.5
Meal 4 5 4.5 2 8 5.0
import pandas as pd
df = pd.DataFrame([
['Apple', 7, 5, 'Fruit'],
['Banana', 3, 6, 'Fruit'],
['Beef', 5, 2, 'Meal'],
['Kimchi', 4, 8, 'Meal']],
columns=["Name", "Frequency", "Importance", "Type"])
def my_filter(data) :
return data["Frequency"].mean() >= 5
print(df)
df = df.groupby("Type").filter(my_filter)
print(df)
Name Frequency Importance Type
0 Apple 7 5 Fruit
1 Banana 3 6 Fruit
2 Beef 5 2 Meal
3 Kimchi 4 8 Meal
Name Frequency Importance Type
0 Apple 7 5 Fruit
1 Banana 3 6 Fruit
import pandas as pd
df = pd.DataFrame([
['Apple', 7, 5, 'Fruit'],
['Banana', 3, 6, 'Fruit'],
['Beef', 5, 2, 'Meal'],
['Kimchi', 4, 8, 'Meal']],
columns=["Name", "Frequency", "Importance", "Type"])
df = df.groupby("Type").get_group("Fruit")
print(df)
Name Frequency Importance Type
0 Apple 7 5 Fruit
1 Banana 3 6 Fruit
이렇게 여러가지 응용버전들이 있다.
1. aggregate()를 통해 그룹화된 DataFrame내 정수형 원소들의 최솟값, 최댓값, 평균을 구할 수 있다.
2. filter()를 통해 DataFrame내 원하는 정보들만 따로 빼내서 출력할 수 있다.
3. get_group()도 마찬가지로 원하는 정보들만 따로 빼내서 출력할 수 있다.
다음은 조금 어려운 응용버전이다.
import pandas as pd
import numpy as np
df = pd.DataFrame([
['Apple', 7, 5, 'Fruit'],
['Banana', 3, 6, 'Fruit'],
['Beef', 5, 2, 'Meal'],
['Kimchi', 4, 8, 'Meal']],
columns=["Name", "Frequency", "Importance", "Type"])
df["Gap"] = df.groupby("Type")["Frequency"].apply(lambda x : x - x.mean())
print(df)
이해할 수 있겠는가?
Type별로 Grouping을 한 다음 Frequency로 나누어서 x - x.mean를 적용시킨 Gap이란 새로운 column을 추가하는 방식이다. 이때까지 사용한 여러 문법들을 짬뽕시킨 느낌이다.
이 말은 쉽게 말하자면 index나 column에 다중 설정이 들어간다고 이해하면 편하다. 예제 코드는 다음과 같다.
import numpy as np
import pandas as pd
df = pd.DataFrame(
np.random.randint(1, 10, (4, 4)),
index=[['1차', '1차', '2차', '2차'], ['공격', '수비', '공격', '수비']],
columns=[['1회', '2회', '3회', '4회']]
)
print(df)
print(df[["1회", "2회"]].loc["2차"])
1회 2회 3회 4회
1차 공격 7 6 8 8
수비 9 1 5 3
2차 공격 4 6 5 9
수비 8 5 1 5
1회 2회
공격 4 6
수비 8 5
이렇게 보면 다중화작업을 통해서 index에 ['1차', '2차'] 뿐만 아니라
['공격', '수비', '공격', '수비']라는 다중 색인이 이루어진걸 볼 수 있다.
자 이렇게 어떻게보면(?) 기본적인 pandas 문법들이다.
이제 마지막으로 pivot table에 대해 알아보겠다. 개인적으로 글이 길어지는걸 싫어하는 편인데 조금 길어져서 죄송하다.
pivot table은 보통 데이터 시각화가 이루어지는데 이를 위해서 우리는 'seaborn'이란 새로운 라이브러리를 사용할 수 있다. 밑에 예제코드를 통해 이해해보자
import pandas as pd
import seaborn as sns
df = sns.load_dataset('titanic')[['age', 'sex', 'class', 'fare', 'survived']]
df.head()
age sex class fare survived
0 22.0 male Third 7.2500 0
1 38.0 female First 71.2833 1
2 26.0 female Third 7.9250 1
3 35.0 female First 53.1000 1
4 35.0 male Third 8.0500 0
자 이때까지 쓰던 코드랑은 살짝 다른 모습을 보이고 있다. 하나씩 자세히 살펴보면,
import seaborn as sns
seaborn은 데이터 시각화를 위한 파이썬 라이브러리이다.
matplotlib 기반으로 만들어져 있으며, 보다 아름답고 복잡한 시각화를 쉽게 만들 수 있게 해준다. 여기서는 sns라는 별칭을 설정하면서 seaborn을 임포트하고 있다.
df = sns.load_dataset('titanic')[['age','sex','class','fare','survived']]
sns.load_dataset('titanic'): seaborn 라이브러리에 내장된 load_dataset 함수를 사용하여 'titanic' 데이터셋을 로드한다.
'titanic' 데이터셋은 타이타닉 호의 승객 정보를 담고 있다.
[['age','sex','class','fare','survived']]: 로드된 데이터셋에서 'age', 'sex', 'class', 'fare', 'survived'라는 컬럼만 선택하여 가져온다.
df.head()
이 부분은 로드된 정보를 담고 있는 df의 내용을 출력하는 부분이다. 저 괄호안에 아무것도 설정이 되어있지 않으면, 기본값으로 df안에 상위 5행을 우선 출력한다.
자 이제 df안에 정보를 로드했으니 이 정보들을 가지고 pivot table을 설정해보자.
pdf1 = pd.pivot_table(df, # 피벗할 데이터프레임
index = 'class', # 행 위치에 들어갈 열
columns = 'sex', # 열 위치에 들어갈 열
values = 'age', # 데이터로 사용할 열
aggfunc = 'mean') # 데이터 집계함수
pdf1
#pdf1 출력결과
sex female male
class
First 34.611765 41.281386
Second 28.722973 30.740707
Third 21.750000 26.507589
pdf2 = pd.pivot_table(df, # 피벗할 데이터프레임
index = 'class', # 행 위치에 들어갈 열
columns = 'sex', # 열 위치에 들어갈 열
values = 'survived', # 데이터로 사용할 열
aggfunc = ['mean', 'sum']) # 데이터 집계함수
pdf2
#pdf2 출력결과
mean sum
sex female male female male
class
First 0.968085 0.368852 91 45
Second 0.921053 0.157407 70 17
Third 0.500000 0.135447 72 47
이렇게 응용할 수 있다.
pivot table을 만들땐 코드에서 적혀놓은것처럼 4개의 기본 요소를 가지고 있다.
1. 행 인덱스(index)
2. 열 인덱스(columns)
3. 데이터 값(values)
4. 데이터 집계함수(aggfunc)
이렇게만 pivot table을 설정해준다면, 무리없이 사용가능하다.
자 이제 그럼 이전 글에서 알아보았던 DataFrame에서 pivot table을 응용하는 예제코드를 알아보면서 마무리하도록 하겠다.
import numpy as np
import pandas as pd
df = pd.DataFrame([
['Apple', 7, 5, 'Fruit'],
['Banana', 3, 6, 'Fruit'],
['Coconut', 2, 6, 'Fruit'],
['Rice', 8, 2, 'Meal'],
['Beef', 5, 2, 'Meal'],
['Kimchi', 4, 8, 'Meal']],
columns=["Name", "Frequency", "Importance", "Type"])
print(df)
df = df.pivot_table(
index="Importance", columns="Type", values="Frequency",
aggfunc=np.max
)
print(df)
Name Frequency Importance Type
0 Apple 7 5 Fruit
1 Banana 3 6 Fruit
2 Coconut 2 6 Fruit
3 Rice 8 2 Meal
4 Beef 5 2 Meal
5 Kimchi 4 8 Meal
Type Fruit Meal
Importance
2 NaN 8.0
5 7.0 NaN
6 3.0 NaN
8 NaN 4.0