개발공부/SK Networks Family AI bootcamp 강의노트

14일차 [ pandas 심화 ]

HyunJung_Jo 2025. 2. 3. 14:42
책을 5번 읽는다고 하면, 1번째는 3개월, 2번째는 1개월, 3번째는 일주일, 4번째는 하루 걸렸다. 반복횟수가 늘어갈 수록 더 깊게 이해가 된다. 처음부터 두꺼운책보단 얇은 책을 여러번 읽으면서 공부하자. 블로그도 이런식으로 활용하면 좋을 것 같다. 

금요일에는 캐글 경진대회 진행 예정

자격증이 있다고 실력이 있다는 것은 아니다. 실제 실력을 올려라. 
노가다처럼 막무가내로 공부하지 말고, 프로젝트를 하면서 자기만의 서비스를 만들면서 실력을 올려라.

현타오거나 그러면 혼자 해결하려고 하지 말고 주변에 SOS 치면 된다.

 

https://colab.research.google.com/drive/1-ssBL1MApwlnxDo9A46GF_A78479Tt6m#scrollTo=QF3TxOcWUDer

 

Google Colab Notebook

Run, share, and edit Python notebooks

colab.research.google.com

groupby

명목변수로 그룹을 나눈다. 서열변수로 나누지 않는다.

nunique개수가 적은 걸로 나눠야 한다.

import pandas as pd
import numpy as np
import seaborn as sns
# 타이타닉 데이터셋
df = sns.load_dataset('titanic')

# 특정 컬럼만 뽑아서 df 만들기
df_tmp = df[['survived','age','fare','pclass','sex']]
df_tmp['sex'].unique()
# array(['male', 'female'], dtype=object)
df_tmp['sex'].nunique() # 2

# df_tmp라는 데이터 프레임을 성별을 기준으로 나눈다.
groups=df_tmp.groupby('sex')

# 성별 기준 그룹 뜯어보기
for group in groups:
  print(f"group type {type(group)} / 개수: {len(group)}")
  print(f'group key 타입: {type(group[0])},group data 타입:{type(group[1])}')
  print("group data shape",group[1].shape)
  print(f"\n[group 실제 모습]\n{group}\n","=="*30)
  
  """
  0초
for group in groups:
  print(f"group type {type(group)} / 개수: {len(group)}")
  print(f'group key 타입: {type(group[0])},group data 타입:{type(group[1])}')
  print("group data shape",group[1].shape)
  print(f"\n[group 실제 모습]\n{group}\n","=="*30)
group type <class 'tuple'> / 개수: 2
group key 타입: <class 'str'>,group data 타입:<class 'pandas.core.frame.DataFrame'>
group data shape (314, 5)

[group 실제 모습]
('female',      survived   age     fare  pclass     sex
1           1  38.0  71.2833       1  female
2           1  26.0   7.9250       3  female
3           1  35.0  53.1000       1  female
8           1  27.0  11.1333       3  female
9           1  14.0  30.0708       2  female
..        ...   ...      ...     ...     ...
880         1  25.0  26.0000       2  female
882         0  22.0  10.5167       3  female
885         0  39.0  29.1250       3  female
887         1  19.0  30.0000       1  female
888         0   NaN  23.4500       3  female

[314 rows x 5 columns])
 ============================================================
group type <class 'tuple'> / 개수: 2
group key 타입: <class 'str'>,group data 타입:<class 'pandas.core.frame.DataFrame'>
group data shape (577, 5)

[group 실제 모습]
('male',      survived   age     fare  pclass   sex
0           0  22.0   7.2500       3  male
4           0  35.0   8.0500       3  male
5           0   NaN   8.4583       3  male
6           0  54.0  51.8625       1  male
7           0   2.0  21.0750       3  male
..        ...   ...      ...     ...   ...
883         0  28.0  10.5000       2  male
884         0  25.0   7.0500       3  male
886         0  27.0  13.0000       2  male
889         1  26.0  30.0000       1  male
890         0  32.0   7.7500       3  male

[577 rows x 5 columns])
 ============================================================
  """
  
  groups.mean()
  """
  
		survived	age			fare	pclass
sex				
female	0.742038	27.915709	44.479818	2.159236
male	0.188908	30.726645	25.523893	2.389948
  """

# ================================================================================
# 성별과 좌석 등급에 따라 보기 (2개 성별 * 등급 3개 = 6개 그룹)
for key,each_group in groups_sex_pclass:
  print(f"key:{key},shape:{each_group.shape}")
  print(each_group.head())
  print('-'*20)
"""
key:('female', 1),shape:(94, 5)
    survived   age      fare  pclass     sex
1          1  38.0   71.2833       1  female
3          1  35.0   53.1000       1  female
11         1  58.0   26.5500       1  female
31         1   NaN  146.5208       1  female
52         1  49.0   76.7292       1  female
--------------------
key:('female', 2),shape:(76, 5)
    survived   age     fare  pclass     sex
9          1  14.0  30.0708       2  female
15         1  55.0  16.0000       2  female
41         0  27.0  21.0000       2  female
43         1   3.0  41.5792       2  female
53         1  29.0  26.0000       2  female
--------------------
key:('female', 3),shape:(144, 5)
    survived   age     fare  pclass     sex
2          1  26.0   7.9250       3  female
8          1  27.0  11.1333       3  female
10         1   4.0  16.7000       3  female
14         0  14.0   7.8542       3  female
18         0  31.0  18.0000       3  female
--------------------
key:('male', 1),shape:(122, 5)
    survived   age      fare  pclass   sex
6          0  54.0   51.8625       1  male
23         1  28.0   35.5000       1  male
27         0  19.0  263.0000       1  male
30         0  40.0   27.7208       1  male
34         0  28.0   82.1708       1  male
--------------------
key:('male', 2),shape:(108, 5)
    survived   age  fare  pclass   sex
17         1   NaN  13.0       2  male
20         0  35.0  26.0       2  male
21         1  34.0  13.0       2  male
33         0  66.0  10.5       2  male
70         0  32.0  10.5       2  male
--------------------
key:('male', 3),shape:(347, 5)
    survived   age     fare  pclass   sex
0          0  22.0   7.2500       3  male
4          0  35.0   8.0500       3  male
5          0   NaN   8.4583       3  male
7          0   2.0  21.0750       3  male
12         0  20.0   8.0500       3  male
--------------------

"""

 

groupby - agg

전역 변수 쓸 때 주의!
함수명도 변수다. 다른 전역변수와 같으면 안된다.
for문 쓸 때 쓴 변수는 다른 변수와 같으면 안된다.
함수명은 동사로 쓴다.
groups_sex_pclass.agg(['mean','median'])
"""
			survived	age			fare
			mean	median	mean	median	mean	median
sex	pclass						
female	1	0.968085	1.0	34.611765	35.0	106.125798	82.66455
2	0.921053	1.0	28.722973	28.0	21.970121	22.00000
3	0.500000	0.5	21.750000	21.5	16.118810	12.47500
male	1	0.368852	0.0	41.281386	40.0	67.226127	41.26250
2	0.157407	0.0	30.740707	30.0	19.741782	13.00000
3	0.135447	0.0	26.507589	25.0	12.661633	7.92500
"""
# 특정 컬럼에 대한 평균과 중앙값
groups_sex_pclass['fare'].agg(['mean','median'])

# apply
groups['fare'].agg(lambda x: x.mean() + x.median())

"""
fare
sex	
female	67.479818
male	36.023893
"""

dict_agg = {
    'fare':['min','max'],
    'age':['mean','median']
}
groups_sex_pclass.agg(dict_agg)

"""
fare	age
min	max	mean	median
sex	pclass				
female	1	25.9292	512.3292	34.611765	35.0
2	10.5000	65.0000	28.722973	28.0
3	6.7500	69.5500	21.750000	21.5
male	1	0.0000	512.3292	41.281386	40.0
2	0.0000	73.5000	30.740707	30.0
3	0.0000	69.5500	26.507589	25.0
"""

두 개 df  조작

merge, 교집합, inner join

left = pd.DataFrame(
    {
        'key':['K0','K1','K2','K3'],
        'A':['A0','A1','A2','A3'],
        'B':['B0','B1','B2','B3']
    }
)
right = pd.DataFrame(
    {'key':['K0','K1','K2','K3'],
    'C':['C0','C1','C2','C3'],
    'D':['D0','D1','D2','D3']
     }
    )
# print(f"left:\n{left.shape}\nright:\n{right.shape}")
print(left.shape,right.shape,sep='\n')


df = pd.merge(left,right,on='key')
df # row가 아닌 col이 늘어난다. 

key	A	B	C	D
0	K0	A0	B0	C0	D0
1	K1	A1	B1	C1	D1
2	K2	A2	B2	C2	D2
3	K3	A3	B3	C3	D3

#============================
# col 기준으로 합쳐짐
# 공통 key1,key2: (K0,K0),(K1,K0)
# left에는 공통키가 2개, right에는 공통키가 3개 (K1,K0 이 2개 있음)
# 따라서 합쳐질 때 row가 3개가 됨.
left = pd.DataFrame(
    {
        "key1": ["K0", "K0", "K1", "K2"],
        "key2": ["K0", "K1", "K0", "K1"],
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
    }
)


right = pd.DataFrame(
    {
        "key1": ["K0", "K1", "K1", "K2"],
        "key2": ["K0", "K0", "K0", "K0"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    }
)

df=pd.merge(left,right,on=['key1','key2'])
df
"""

key1	key2	A	B	C	D
0	K0	K0	A0	B0	C0	D0
1	K1	K0	A2	B2	C1	D1
2	K1	K0	A2	B2	C2	D2
"""

 

차집합

 

# 왼쪽 기준으로 합집합
# 왼쪽 기준으로 오른쪽에 없는 데이터는 NaN값으로 표시
df = pd.merge(left,right,on=['key1','key2'],how='left')
df

	key1	key2	A	B	C	D
0	K0	K0	A0	B0	C0	D0
1	K0	K1	A1	B1	NaN	NaN
2	K1	K0	A2	B2	C1	D1
3	K1	K0	A2	B2	C2	D2
4	K2	K1	A3	B3	NaN	NaN

# 전체 합집합
df = pd.merge(left,right,on=['key1','key2'],how='outer')
df

	key1	key2	A	B	C	D
0	K0	K0	A0	B0	C0	D0
1	K0	K1	A1	B1	NaN	NaN
2	K1	K0	A2	B2	C1	D1
3	K1	K0	A2	B2	C2	D2
4	K2	K0	NaN	NaN	C3	D3
5	K2	K1	A3	B3	NaN	NaN

 

concat(), outer join, 

concat의 기준은 인덱스이다.

default: outer join

df1 = pd.DataFrame(
    {
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    },
    index=[0, 1, 2, 3],
)

s1 = pd.Series(
    ["E0", "E1", "E2", "E3"],
    name="E",
)

# df + s1 ========================
# col기준으로 합치기=> axis=1
concated_df =pd.concat([df1,s1],axis=1)
concated_df

A	B	C	D	E
0	A0	B0	C0	D0	E0
1	A1	B1	C1	D1	E1
2	A2	B2	C2	D2	E2
3	A3	B3	C3	D3	E3


df4 = pd.DataFrame(
    {
        "B": ["B2", "B3", "B6", "B7"],
        "D": ["D2", "D3", "D6", "D7"],
        "F": ["F2", "F3", "F6", "F7"],
    },
    index=[2, 3, 6, 7],
)
df = pd.concat([s1,df4],axis=0) # outer join
df


E	B	D	F
0	E0	NaN	NaN	NaN
1	E1	NaN	NaN	NaN
2	E2	NaN	NaN	NaN


# df + df ======================
df = pd.concat([df1,df4],axis=1) # outer join
df
3	E3	NaN	NaN	NaN
2	NaN	B2	D2	F2
3	NaN	B3	D3	F3
6	NaN	B6	D6	F6
7	NaN	B7	D7	F7

# index 재정의, 기존 인덱스 살아남
df = pd.concat([df1,df4],axis=1).reset_index() 
df

index	A	B	C	D	B	D	F
0	0	A0	B0	C0	D0	NaN	NaN	NaN
1	1	A1	B1	C1	D1	NaN	NaN	NaN
2	2	A2	B2	C2	D2	B2	D2	F2
3	3	A3	B3	C3	D3	B3	D3	F3
4	6	NaN	NaN	NaN	NaN	B6	D6	F6
5	7	NaN	NaN	NaN	NaN	B7	D7	F7

# 중복 인덱스 피하기
df = pd.concat([df1,df4],axis=1).reset_index(drop=True) # method
df = pd.concat([df1,df4],axis=1,ignore_index=True) # option
df

	A	B	C	D	B	D	F
0	A0	B0	C0	D0	NaN	NaN	NaN
1	A1	B1	C1	D1	NaN	NaN	NaN
2	A2	B2	C2	D2	B2	D2	F2
3	A3	B3	C3	D3	B3	D3	F3
4	NaN	NaN	NaN	NaN	B6	D6	F6


# row 방향으로 조작
df = pd.concat([df1,df4],axis=0)
df

A	B	C	D	F
0	A0	B0	C0	D0	NaN
1	A1	B1	C1	D1	NaN
2	A2	B2	C2	D2	NaN
3	A3	B3	C3	D3	NaN
2	NaN	B2	NaN	D2	F2
3	NaN	B3	NaN	D3	F3
6	NaN	B6	NaN	D6	F6
7	NaN	B7	NaN	D7	F7

5	NaN	NaN	NaN	NaN	B7	D7	F7

 

groupby remind

import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
df_anscombe = sns.load_dataset("anscombe")
"""
	dataset	x	y
0	I	10.0	8.04
1	I	8.0	6.95
2	I	13.0	7.58
3	I	9.0	8.81
4	I	11.0	8.33

"""

# 조건식으로 group가져오기
df_2=df_anscombe[df_anscombe['dataset'] == 'II']

# groupby로 가져오기
# 아래는 groupby를 4번이나 더 하기 때문 CPU 입장에서 부담이 간다.
df_1 = df_anscombe.groupby('dataset').get_group('I')
df_2 = df_anscombe.groupby('dataset').get_group('II')
df_3 = df_anscombe.groupby('dataset').get_group('III')
df_4 = df_anscombe.groupby('dataset').get_group('IV')

# 이와 같이 groupby를 한번만 해서 메모리 부하를 줄이는 것이 좋다.
groups=df_anscombe.groupby('dataset')
df_1=groups.get_group('I')
df_2=groups.get_group('II')
df_3=groups.get_group('III')
df_4=groups.get_group('IV')