์ด ๋ฌธ์ ๋ groupby.agg
์์ dicts์ ๋ ์ด๋ธ ์ฌ์ง์ ์ ์ค๋จํ ํ #15931์ ํ ๋ก ์ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋์์ต๋๋ค. ์๋์ ์์ฝ๋ ๋ง์ ๋ด์ฉ์ ์ด์ ๋
ผ์์์ ์ด๋ฏธ ๋
ผ์๋์์ต๋๋ค. ํนํ https://github.com/pandas-dev/pandas/pull/15931#issuecomment -336139085 ๋ฌธ์ ๊ฐ ๋ช
ํํ๊ฒ ๋ช
์๋์ด ์๋ ๊ณณ์ ์ถ์ฒํฉ๋๋ค.
#15931 ์ฌ์ฉ ์ค๋จ์ ๋๊ธฐ๋ ์ฃผ๋ก Series์ Dataframe ์ฌ์ด์ agg()
๋ํ ์ผ๊ด๋ ์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค(์ปจํ
์คํธ๋ #14668 ์ฐธ์กฐ).
์ค์ฒฉ๋ ์ฌ์ ์ ์ฌ์ฉํ์ฌ ๋ ์ด๋ธ์ ๋ค์ ์ง์ ํ๋ ๊ธฐ๋ฅ์ ๋๋ฌด ๋ณต์กํ๊ฑฐ๋ ์ผ๊ด์ฑ์ด ์์ด ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ๊ฒ์ผ๋ก ์ผ๋ถ ์ค๋ช ๋์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ฌ๊ธฐ์๋ ๋๊ฐ๊ฐ ๋ฐ๋ฆ ๋๋ค. ์ง๊ณ์ ์ด๋ฆ ๋ณ๊ฒฝ์ด ๋์์ ๋ถ๊ฐ๋ฅํ๋ฉด ๋งค์ฐ ์ฑ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ ํฉ๋ฆฌ์ ์ธ ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ์๋ ์ด์ ๋ฒ์ ๊ณผ์ ๋นํธํ์ฑ์ด ๋ฐ์ํฉ๋๋ค.
__name__
์์ฑ์ ๋ณ๊ฒฝํ์ง ์๋ ํ ๋ถ๋ถ ํจ์์์ ๋ ์ด์์ ์ง๊ณ์๋ฅผ ๋ ์ด์ ์ ์ฉํ ์ ์์ต๋๋ค._(์ด๊ฒ์ ๊ฐ๋ฅํ ํ ์งง์ ์ฝ๋๋ก ๋ฌธ์ ๋ฅผ ์์ฐํ ๋ชฉ์ ์ผ๋ก ์ ์๋ ์์ด์ง๋ง ์ฌ๊ธฐ์์ ์์ฐ๋ ๋ชจ๋ ๋ฌธ์ ๋ ๋ณ๊ฒฝ ์ดํ ์ค์ํ์์, ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์ฒ๋ผ ๊ฐ๋จํ์ง ์์ ์ํฉ์์ ์ ๋ฅผ ๋ฌผ์์ต๋๋ค. )_
mydf = pd.DataFrame(
{
'cat': ['A', 'A', 'A', 'B', 'B', 'C'],
'energy': [1.8, 1.95, 2.04, 1.25, 1.6, 1.01],
'distance': [1.2, 1.5, 1.74, 0.82, 1.01, 0.6]
},
index=range(6)
)
cat distance energy
0 A 1.20 1.80
1 A 1.50 1.95
2 A 1.74 2.04
3 B 0.82 1.25
4 B 1.01 1.60
5 C 0.60 1.01
์ฐ๊ธฐ ์ฝ๊ณ ์ฝ๊ธฐ ์ฝ๊ณ ์์๋๋ก ์๋ํฉ๋๋ค.
import numpy as np
import statsmodels.robust as smrb
from functools import partial
# median absolute deviation as a partial function
# in order to demonstrate the issue with partial functions as aggregators
mad_c1 = partial(smrb.mad, c=1)
# renaming and specifying the aggregators at the same time
# note that I want to choose the resulting column names myself
# for example "total_xxxx" instead of just "sum"
mydf_agg = mydf.groupby('cat').agg({
'energy': {
'total_energy': 'sum',
'energy_p98': lambda x: np.percentile(x, 98), # lambda
'energy_p17': lambda x: np.percentile(x, 17), # lambda
},
'distance': {
'total_distance': 'sum',
'average_distance': 'mean',
'distance_mad': smrb.mad, # original function
'distance_mad_c1': mad_c1, # partial function wrapping the original function
},
})
๊ฒฐ๊ณผ
energy distance
total_energy energy_p98 energy_p17 total_distance average_distance distance_mad distance_mad_c1
cat
A 5.79 2.0364 1.8510 4.44 1.480 0.355825 0.240
B 2.85 1.5930 1.3095 1.83 0.915 0.140847 0.095
C 1.01 1.0100 1.0100 0.60 0.600 0.000000 0.000
๊ทธ๋ฆฌ๊ณ ๋จ์ ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
# get rid of the first MultiIndex level in a pretty straightforward way
mydf_agg.columns = mydf_agg.columns.droplevel(level=0)
ํ๋ค๋ฅผ ์ฐฌ์ํ๋ฉฐ ์ฆ๊ฒ๊ฒ ์ถค์ ์ถฐ์๐ ๐บ !
import numpy as np
import statsmodels.robust as smrb
from functools import partial
# median absolute deviation as a partial function
# in order to demonstrate the issue with partial functions as aggregators
mad_c1 = partial(smrb.mad, c=1)
# no way of choosing the destination's column names...
mydf_agg = mydf.groupby('cat').agg({
'energy': [
'sum',
lambda x: np.percentile(x, 98), # lambda
lambda x: np.percentile(x, 17), # lambda
],
'distance': [
'sum',
'mean',
smrb.mad, # original function
mad_c1, # partial function wrapping the original function
],
})
๋๋ค ํจ์๊ฐ ๋ชจ๋ <lambda>
๋ผ๋ ์ด์ ์์ฑํ๊ธฐ ๋๋ฌธ์ ์์ ๋ด์ฉ์ด ์ค๋จ๋ฉ๋๋ค.
SpecificationError: Function names must be unique, found multiple named <lambda>
์ด์ ๋ฒ์ ๊ณผ ํธํ๋์ง ์๋ ํ๊ท: ๋์ผํ ์๋ ์ด์ ๋ ๊ฐ์ ๋ค๋ฅธ ๋๋ค๋ฅผ ๋ ์ด์ ์ ์ฉํ ์ ์์ต๋๋ค.
์์์ lambda x: np.percentile(x, 98)
๋ฅผ ์ ๊ฑฐํ๋ฉด ์๋ ํจ์์์ ํจ์ ์ด๋ฆ์ ์์ํ๋ ๋ถ๋ถ ํจ์์ ๋์ผํ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
SpecificationError: Function names must be unique, found multiple named mad
๋ง์ง๋ง์ผ๋ก ๋ถ๋ถ์ __name__
์์ฑ์ ๋ฎ์ด์ด ํ(์: mad_c1.__name__ = 'mad_c1'
) ๋ค์์ ์ป์ต๋๋ค.
energy distance
sum <lambda> sum mean mad mad_c1
cat
A 5.79 1.8510 4.44 1.480 0.355825 0.240
B 2.85 1.3095 1.83 0.915 0.140847 0.095
C 1.01 1.0100 0.60 0.600 0.000000 0.000
์ฌ์ ํ
๋ณ๋์ ๋จ๊ณ์์ ์ฒ๋ฆฌํฉ๋๋ค.
์ง๊ณ ํ ์ด ์ด๋ฆ์ ๋ํ ์ ์ด๋ ๋ถ๊ฐ๋ฅํฉ๋๋ค. ์๋ํ๋ ๋ฐฉ์์ผ๋ก ์ป์ ์ ์๋ ์ต์ ์ ์๋ ์ด ์ด๋ฆ๊ณผ _aggregate ํจ์์ ์ด๋ฆ_์ ๋ค์๊ณผ ๊ฐ์ด ์กฐํฉํ๋ ๊ฒ์ ๋๋ค.
mydf_agg.columns = ['_'.join(col) for col in mydf_agg.columns]
๊ฒฐ๊ณผ:
energy_sum energy_<lambda> distance_sum distance_mean distance_mad distance_mad_c1
cat
A 5.79 1.8510 4.44 1.480 0.355825 0.240
B 2.85 1.3095 1.83 0.915 0.140847 0.095
C 1.01 1.0100 0.60 0.600 0.000000 0.000
๋ค๋ฅธ ์ด๋ฆ์ด ํ์ํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์์ต๋๋ค.
mydf_agg.rename({
"energy_sum": "total_energy",
"energy_<lambda>": "energy_p17",
"distance_sum": "total_distance",
"distance_mean": "average_distance"
}, inplace=True)
ํ์ง๋ง ์ด๋ ์ง๊ณ๊ฐ ์ ์๋ ์ฝ๋์ ๋๊ธฐํ๋ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ์ฝ๋(์ด์ ์ฝ๋์ ๋ค๋ฅธ ์์น์ ์์ด์ผ ํจ)๋ฅผ ์ ์งํ๋๋ก ์ฃผ์ํด์ผ ํจ์ ์๋ฏธํฉ๋๋ค...
์ํ๊น์ด ํ๋ค ์ ์ ๐ข (์์ง๋ ํ๋ค๋ฅผ ์ฌ๋ํฉ๋๋ค)
์ผ๊ด์ฑ์ ์ ์งํ๊ธฐ ์ํด ์ต์ ์ ๋คํ๋ ๋์์ _aggregate ๋ฐ rename_ ๊ธฐ๋ฅ์ ์ฌ์ฉ ์ค๋จ์ ๋ํด ๊น์ด ์ ๊ฐ์ค๋ฝ๊ฒ ์๊ฐํฉ๋๋ค. ์์ ์๋ฅผ ํตํด ๋ฌธ์ ์ ์ด ๋ช ํํด์ง๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
_์ ํ์ ์ฝ๊ธฐ:_
์ด๋ฏธ ๋ช ๊ฐ์ ๋์ ์งํ๋๊ณ ์๋ pull request์์ ์์ ์ธ๊ธํ ๋
ผ์์ ๊ด๋ จํ์ฌ, ์ ๋ ์ต๊ทผ์ ์์์ผ ์ด ์ง์ ์ค๋จ์ ๋ํด ๊ฑฑ์ ํ๋ ์ด์ ์ค ํ๋๋ฅผ ๊นจ๋ฌ์์ต๋๋ค. "์ง๊ณ ๋ฐ ์ด๋ฆ ๋ณ๊ฒฝ"์ SQL์์๋ ์ผ๋ฐ์ ์ผ๋ก ์ง๊ณ ํํ์ ๋ฐ๋ก ์์ ๋์ ์ด ์ด๋ฆ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ SQL์ GROUP BY ์ง๊ณ(์: SELECT col1, avg(col2) AS col2_mean, stddev(col2) AS col2_var FROM mytable GROUP BY col1
.
๋ด๊ฐ _not_ ํฌ๋๊ฐ ๋ฐ๋์ ๋ฌผ๋ก SQL๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํด์ผํ๋ค๊ณ ๋งํ๊ณ ์๋ค. ๊ทธ๋ฌ๋ ์์ ์ ๊ณต๋ ์๋ dict-of-dict API๊ฐ ๋ง์ ์ฌ์ฉ ์ฌ๋ก์ ๋ํ ๊นจ๋ํ๊ณ ๊ฐ๋จํ ์๋ฃจ์ ์ธ ์ด์ ๋ฅผ ๋ณด์ฌ์ค๋๋ค.
(* dict-of-dict ์ ๊ทผ ๋ฐฉ์์ด ๋ณต์กํ๋ค๋ ๋ฐ ๊ฐ์ธ์ ์ผ๋ก ๋์ํ์ง ์์ต๋๋ค.)
@zertrin : ํจ๊ปํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ๋๋ ์ด๊ฒ์ ๋ํด #15931์์ ๋ง์ ํ ๋ก ์ด ์์๋ ๊ฒ์ ๋ณด์๋ค. ์ ๊ฐ ์ด ๊ธ์ ๋ค ์ฝ์ง ๋ชปํด์ ์ง๊ธ์ ๋๊ธ์ ๋ฌ ์ ์์ต๋๋ค. ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ํ์ ๋ณด๊ฒ ์ต๋๋ค.
@jjjjjjjjjjjjjjjjjjj ๋ ๋๋ ์์ ์จใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ ใ
์ด ์์์ ํ์ฌ agg
๊ตฌํ์ผ๋ก ์ด๋ฆ์ ๋ฐ๊พธ๋ ๊ฒ์ด ๋งค์ฐ ํฌ๋ฐํ๊ณ ๊นจ์ ธ ์๋ค๋ ์ ์ ๋์ํฉ๋๋ค. ์ค์ฒฉ๋ ๋์
๋๋ฆฌ๋ ๋ค์ ๋ณต์กํ์ง๋ง ์ฌ๋ฌ๋ถ์ด ํ๋ ๊ฒ์ฒ๋ผ ์์ฑํ๋ฉด ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ๋งค์ฐ ๋ช
ํํด์ง๋๋ค.
๋๋์ด์์ ์๋์์ ๊ฒ ๊ฐ๊ตฐ์ names
์ถ๊ฐ ๋งค๊ฐ ๋ณ์ agg
์๋ก์ด ์ด๋ฆ์ผ๋ก ์ฌ์ ๋งคํ์๊ฒ ์ง๊ณ ์ด์ ๊ฑธ๋ฆด ๊ฒ์ด๋ค. ์์ ์ธ๋ฑ์ค ์์ค์ ์ ์งํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๊ธฐ ์ํด ๋ค๋ฅธ ๋งค๊ฐ๋ณ์ drop_index
๋ฅผ ๋ถ์ธ๋ก ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
๋ฐ๋ผ์ ๊ตฌ๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๋๋๋ค.
agg_dict = {'energy': ['sum',
lambda x: np.percentile(x, 98), # lambda
lambda x: np.percentile(x, 17), # lambda
],
'distance': ['sum',
'mean',
smrb.mad, # original function
mad_c1, # partial function wrapping the original function
]
}
name_dict = {'energy':['energy_sum', 'energy_p98', 'energy_p17'],
'distance':['distance_sum', 'distance_mean', 'distance_mad', 'distance_mad_c1']}
mydf.groupby('cat').agg(agg_dict, names=name_dict, drop_index=True)
๋๋ DataFrame.assign
์ ์ ์ฌํ๊ฒ ์๋ํ๋ ์์ ํ ์๋ก์ด agg_assign
๋ฉ์๋๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
mydf.groupby('cat').agg_assign(energy_sum=lambda x: x.energy.sum(),
energy_p98=lambda x: np.percentile(x.energy, 98),
energy_p17=lambda x: np.percentile(x.energy, 17),
distance_sum=lambda x: x.distance.sum(),
distance_mean=lambda x: x.distance.mean(),
distance_mad=lambda x: smrb.mad(x.distance),
distance_mad_c1=lambda x: mad_c1(x.distance))
์ ๋ ์ฌ์ค ์ด ์ต์ ์ด ํจ์ฌ ์ข์ต๋๋ค.
๊ทธ๋งํ ๊ฐ์น๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๋ ๋ํ ๊ธฐ๋ฅ์ ํ๊ฐ์ ํํ์ง ์๋ ๊ฒ์ ๊ฐ๋ ฅํ๊ฒ ์ฐฌ์ฑํฉ๋๋ค.
๋์๊ฒ ํฐ ์ด์ ๋ ํ์ด์ฌ์ ํจ์ ์ด๋ฆ ๊ณต๊ฐ(ํน์ ๊ตฌํ๊ณผ ๊ด๋ จ๋ ๊ฒ)์ ์ด ์ด๋ฆ๊ณผ ๋ฐ์ดํฐ(๊ตฌํ์ ๋ํด ํ์คํ ์์์ผ ํ๋ ๊ฒ)์ ํผํฉํ๋ ๋ฐ ๋งค์ฐ ์ด์ํ ์ ์ด ์๊ธฐ ๋๋ฌธ์
๋๋ค. '<lambda>'
๋ผ๋ ์ด(์ฌ๋ฌ ์ด)์ด ํ์๋๋ค๋ ์ฌ์ค์ ์ฌ๊ฐํ ์ธ์ง ๋ถ์กฐํ๋ฅผ ์ ๋ฐํฉ๋๋ค.
๋ถํ์ํ(๋ ธ์ถ๋) ์ด ์ด๋ฆ์ด ์ฌ์ฉ๋๋ ์ค๊ฐ ๋จ๊ณ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ์ ๊ทผ ๋ฐฉ์์ด ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค. ๋ํ ๊ตฌํ์ ์ ์ฌ์ ์ผ๋ก ์ข ์์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ์์ ์ ์ด๊ณ ์ฒด๊ณ์ ์ผ๋ก ์ด๋ฆ์ ๋ฐ๊พธ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค.
๊ทธ ์ธ์๋ ์ค์ฒฉ๋ dict ๊ธฐ๋ฅ์ ํ์คํ ๋ณต์กํ์ง๋ง ์ํ ์ค์ธ ๋ณต์กํ ์์ ์ ๋๋ค.
TL;DR ํ๊ฐ์ ํํ์ง ๋ง์ญ์์ค. :)
๋์ ๊ธฐ์ฌ๋ ๋ ๊ฐ์ง์ ์ํด ๋๊ธฐ๊ฐ ๋ถ์ฌ๋๋ค.
๋ํ Pandas Series ๋ฐ DataFrame ๊ฐ์ฒด์๋ ํ์ดํ๋ผ์ด๋์ ์ฉ์ดํ๊ฒ ํ๋ pipe
๋ฉ์๋๊ฐ ์์ต๋๋ค. ์ด ๋ฌธ์ ๋ถ๋ถ ์์๋ pipe
๋ฅผ ์ฌ์ฉํ์ฌ ์๋ธํด๋์ฑ ๋์ ๋ฉ์๋๋ฅผ ๋๋ฆฌํ ์ ์๋ค๊ณ ์ค๋ช
ํฉ๋๋ค. ๊ฐ์ ์ ์ ์ผ๋ก ์๋ก์ด GroupBy.pipe
๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฌํ ์ญํ ์ ์ํํ๊ณ groupby ๊ฐ์ฒด์ ๋ํ ํ๋ก์ ๋ฉ์๋๋ฅผ ๋น๋ํ ์ ์์ต๋๋ค.
@zertrin ์ ์๋ฅผ ์ฌ์ฉ
import numpy as np
import statsmodels.robust as smrb
from functools import partial
# The DataFrame offered up above
mydf = pd.DataFrame(
{
'cat': ['A', 'A', 'A', 'B', 'B', 'C'],
'energy': [1.8, 1.95, 2.04, 1.25, 1.6, 1.01],
'distance': [1.2, 1.5, 1.74, 0.82, 1.01, 0.6]
},
index=range(6)
)
# Identical dictionary passed to `agg`
funcs = {
'energy': {
'total_energy': 'sum',
'energy_p98': lambda x: np.percentile(x, 98), # lambda
'energy_p17': lambda x: np.percentile(x, 17), # lambda
},
'distance': {
'total_distance': 'sum',
'average_distance': 'mean',
'distance_mad': smrb.mad, # original function
'distance_mad_c1': mad_c1, # partial function wrapping the original function
},
}
# Write a proxy method to be passed to `pipe`
def agg_assign(gb, fdict):
data = {
(cl, nm): gb[cl].agg(fn)
for cl, d in fdict.items()
for nm, fn in d.items()
}
return pd.DataFrame(data)
# All the API we need already exists with `pipe`
mydf.groupby('cat').pipe(agg_assign, fdict=funcs)
๊ฒฐ๊ณผ๋
distance energy
average_distance distance_mad distance_mad_c1 total_distance energy_p17 energy_p98 total_energy
cat
A 1.480 0.355825 0.240 4.44 1.8510 2.0364 5.79
B 0.915 0.140847 0.095 1.83 1.3095 1.5930 2.85
C 0.600 0.000000 0.000 0.60 1.0100 1.0100 1.01
pipe
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ง์ ๊ฒฝ์ฐ์ ์ API๋ฅผ ์ถ๊ฐํ ํ์๊ฐ ์์ต๋๋ค. ๋ํ ์ฐ๋ฆฌ๊ฐ ๋
ผ์ํ๊ณ ์๋ ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ๊ธฐ๋ฅ์ ๋์ฒดํ ์ ์๋ ์๋จ์ ์ ๊ณตํฉ๋๋ค. ๋ฐ๋ผ์ ์ง์ ์ค๋จ์ ์งํํ๋ ๊ฒฝํฅ์ด ์์ต๋๋ค.
๋๋ tdpetrou์ ์์ด๋์ด๋ฅผ ์ ๋ง ์ข์ํฉ๋๋ค - names=name_dict
.
์ด๊ฒ์ ๋ชจ๋๋ฅผ ํ๋ณตํ๊ฒ ํ ์ ์์ต๋๋ค. ๊ทธ๊ฒ์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋๋ก ์ฝ๊ฒ ์ด์ ์ด๋ฆ์ ๋ฐ๊ฟ
์ค์ ๋ก๋ ์ด๊ธฐ ๊ฒ์๋ฌผ์์ ์ธ๊ธํ๋ฏ์ด ์ง๊ณ ์์ ์ด ๊ฒฐ๊ณผ ์ด์ ์ด๋ฆ์์ ์ ์๋ ์์น๋ฅผ ๋ถ๋ฆฌํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ง ๋ชปํ๋ฏ๋ก ๋ ๋ค "๋๊ธฐํ"๋์๋์ง ํ์ธํ๊ธฐ ์ํด ์ถ๊ฐ ๋ ธ๋ ฅ์ด ํ์ํฉ๋๋ค.
๋๋ ๊ทธ๊ฒ์ด ๋์ ํด๊ฒฐ์ฑ ์ด๋ผ๊ณ ๋งํ์ง๋ ์์ง๋ง(๊ฒฐ๊ตญ ๋ค๋ฅธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํจ) dict of dict ์ ๊ทผ ๋ฐฉ์๋งํผ ์ฝ๊ณ ๋ช ํํ์ง ์์ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์ ๋ด ๋ง์ ๊ธ์ ์ธ ๋ ๋ชฉ๋ก์ ๋ ์ฌ์ ์ ๋๊ธฐํ๋ ์ํ๋ก ์ ์งํด์ผ ํ๊ณ ์์ค๋ฅผ ์ฝ์ ๋ ๋ ์๋ ๋ชฉ๋ก์ ๋ ๋ฒ์งธ ์ฌ์ ์ ์๋ ์ด๋ฆ์ ๋ชฉ๋ก์ ์ฒซ ๋ฒ์งธ ์ฌ์ ์ ์๋ ์ง๊ณ ์ ์์ ์ผ์น์ํค๋ ค๊ณ ๋ ธ๋ ฅํด์ผ ํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ๊ทธ๊ฒ์ ๊ฐ๊ฐ์ ๊ฒฝ์ฐ์ ๋ ๋ฐฐ์ ๋ ธ๋ ฅ์ ๋๋ค.
์ค์ฒฉ๋ ๋์ ๋๋ฆฌ๋ ๋ค์ ๋ณต์กํ์ง๋ง ์ฌ๋ฌ๋ถ์ด ํ๋ ๊ฒ์ฒ๋ผ ์์ฑํ๋ฉด ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ๋งค์ฐ ๋ช ํํด์ง๋๋ค.
๋๋ ์์ง๋ ์ ๋ชจ๋ ์ฌ๋๋ค์ด dict of dict๊ฐ ๋ณต์กํ๋ค๊ณ ๋งํ๋์ง ์ดํดํ์ง ๋ชปํฉ๋๋ค. ๋์๊ฒ ๊ทธ๊ฒ์ ๊ทธ๊ฒ์ ํ๋ ๊ฐ์ฅ ๋ช ํํ ๋ฐฉ๋ฒ์ด๋ค.
์ฆ, names
ํค์๋๊ฐ pandas ํ์ด ํธ์ํ ์ ์ผํ ์๋ฃจ์
์ด๋ผ๋ฉด ํ์ฌ ์ํฉ์ ๋นํด ์ฌ์ ํ ๊ฐ์ ๋ ๊ฒ์
๋๋ค.
@pirsquared ๋ ํ์ฌ API๋ฅผ ์ฌ์ฉํ๋ ํฅ๋ฏธ๋ก์ด ์๋ฃจ์ ์ ๋๋ค. ๋ด ์๊ฒฌ์ผ๋ก๋ ์ดํดํ๊ธฐ ์ฝ์ง ์์ง๋ง (๋๋ ๊ทธ๊ฒ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ์ ๋ง๋ก ์ดํดํ์ง ๋ชปํฉ๋๋ค : ํผ๋ ์ค๋ฝ์ต๋๋ค : )
๋ฐ์ดํฐ ๊ณผํ ํ์ ๋ ๋ง์์ ์ค๋ ๋๋ฅผ ์์ํ์ต๋๋ค. ํฌ๋์ ๋ํด ๋ฌด์์ ์ซ์ดํฉ๋๊น? . ๋๊ตฐ๊ฐ groupby
๋ค์ ๋ฐํ๋ MultiIndex ์ ๋ํ ๊ฒฝ๋ฉธ์ ์ ๊ธฐํ๊ณ plydata ์ ๊ตฌํ๋ dplyr do
๋์ฌ๋ฅผ ๊ฐ๋ฆฌ์ผฐ์ต๋๋ค . ๊ทธ๊ฒ์ agg_assign
๋ก ์ ํํ ์๋ํ๋ฏ๋ก ๊ฝค ํฅ๋ฏธ๋ก์ ์ต๋๋ค.
@zertrin agg_assign
dict of dict ์ ๊ทผ ๋ฐฉ์๋ณด๋ค ์ฐ์ํ๊ณ SQL ์ง๊ณ์ ๋์ผํ ๋ฟ๋ง ์๋๋ผ ์ง๊ณ ๋ด์์ ์ฌ๋ฌ ์ด์ด ์๋ก ์ํธ ์์ฉํ ์ ์์ต๋๋ค. DataFrame.assign
๋์ผํ๊ฒ ์๋ํฉ๋๋ค.
@jreback @TomAugspurger์ ๋ํ ์๊ฐ์ด ์์ต๋๊น?
...
mydf.groupby('cat').agg(agg_dict, ์ด๋ฆ=name_dict, drop_index=True)
์ด๋ ๊ฒ ํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์ง๋ง ํค์ ๊ฐ์ ๋ ์์น์ ์ ๋ ฌํด์ผ ํฉ๋๋ค. ์ด๋ฌํ ๋ถ๊ธฐ ์ฝ๋๊ฐ ํ์ํ์ง ์์ API( .agg_assign
์๋จ)๋ ์ค๋ฅ๊ฐ ๋ ๋ฐ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
API ์ฌ์ฉ ํ ์ ๋ฆฌ ์ฝ๋ ๋ฌธ์ ๋ ์์ต๋๋ค. groupby
์์
์ด MultiIndex
๋ฐ์ดํฐ ํ๋ ์์ ๋ฐํํ๋ฉด ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ MultiIndex
์คํ ์ทจ์ํฉ๋๋ค. .agg_assign
๋ฅผ ์ฌ์ฉํ๋ ์ง์ ์ ์ธ ์ ์ธ์ ๋ฐฉ๋ฒ์ ๊ณ์ธต ๊ตฌ์กฐ, MultiIndex
์ถ๋ ฅ, ๋์ค์ ์ ๋ฆฌํ์ง ์์์ ์ ์ํฉ๋๋ค.
์ฌ์ฉ ํจํด์ ๋ฐ๋ผ ๋ค์ค ์ธ๋ฑ์ค ์ถ๋ ฅ์ ์ตํธ์์์ด ์๋ ์ตํธ์ธ์ด์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋๋ ์ฒ์์ agg_assign
์ ์์ ๋ํด ํ์์ ์ด์์ง๋ง ๋ง์ง๋ง ๋ ๊ฐ์ง ์๊ฒฌ์ ๋ณด๊ณ ์ด๊ฒ์ด ์ข์ ํด๊ฒฐ์ฑ
์ด ๋ ์ ์๋ค๋ ํ์ ์ ๊ฐ๊ฒ ๋์์ต๋๋ค.
ํนํ agg_assign(**relabeling_dict)
ํ์์ผ๋ก ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ์ ๋ํด ์๊ฐํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ๋ด relabeling_dict
๋ฅผ ์ ์ํ ์ ์์ต๋๋ค.
relabeling_dict = {
'energy_sum': lambda x: x.energy.sum(),
'energy_p98': lambda x: np.percentile(x.energy, 98),
'energy_p17': lambda x: np.percentile(x.energy, 17),
'distance_sum': lambda x: x.distance.sum(),
'distance_mean': lambda x: x.distance.mean(),
'distance_mad': lambda x: smrb.mad(x.distance),
'distance_mad_c1': lambda x: mad_c1(x.distance)
}
๊ทธ๊ฒ์ ๋งค์ฐ ์ ์ฐํ๊ณ ๋ด OP์์ ์ธ๊ธ ํ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ฒ์ ๋๋ค.
@zertrin @has2k1
๋๋ ์ด๊ฒ์ ๋ํด ์กฐ๊ธ ๋ ์๊ฐํ๊ณ ์์๊ณ ์ด ๊ธฐ๋ฅ์ ์ด๋ฏธ apply
ํฉ๋๋ค. ์ธ๋ฑ์ค๋ฅผ ์ ์ด ์ด๋ฆ์ผ๋ก, ๊ฐ์ ์ง๊ณ๋ก ์ฌ์ฉํ์ฌ Series๋ฅผ ๋ฐํํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์ด๊ฒ์ ์ด๋ฆ์ ๊ณต๋ฐฑ์ ํ์ฉํ๊ณ ์ํ๋ ๋๋ก ์ ํํ ์ด์ ์ ๋ ฌํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
def my_agg(x):
data = {'energy_sum': x.energy.sum(),
'energy_p98': np.percentile(x.energy, 98),
'energy_p17': np.percentile(x.energy, 17),
'distance sum' : x.distance.sum(),
'distance mean': x.distance.mean(),
'distance MAD': smrb.mad(x.distance),
'distance MAD C1': mad_c1(x.distance)}
return pd.Series(data, index=list_of_column_order)
mydf.groupby('cat').apply(my_agg)
๋ฐ๋ผ์ ์๋ก์ด ๋ฐฉ๋ฒ์ด ํ์ํ์ง ์์ ์ ์์ผ๋ฉฐ ๋์ ๋ฌธ์์์ ๋ ๋์ ์๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
@tdpetrou , ๋น์ ์ด ๋ง์ต๋๋ค. ๋ด ์์ ์ ๋ฒ์ ์ ์ฌ์ฉํ๋ ๋์ ๋น ๋ฅธ ๋๋ฆฐ ๊ฒฝ๋ก ์ ํ ํ๋ก์ธ์ค์ ์ด์ค ์คํ์ผ๋ก ์ธํด apply
์๋ํ๋ ๋ฐฉ์์ ์์์ต๋๋ค.
ํ , ๋ฌธ์๋ฅผ ์ฝ๋ ๊ฒ๋ง์ผ๋ก๋ ์ง๊ณ ์ปจํ
์คํธ์์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ํด ์๊ฐํ ๊ธฐํ๋ ์์์ง๋ง...
๊ฒ๋ค๊ฐ, ๋๋ ์ฌ์ ํ apply
์ ์๋ฃจ์
์ด ๋๋ฌด ๋ณต์กํ๋ค๊ณ ์๊ฐํฉ๋๋ค. agg_assign
์ ๊ทผ ๋ฐฉ์์ด ๋ ๊ฐ๋จํ๊ณ ์ดํดํ๊ธฐ ์ฌ์ ๋ณด์์ต๋๋ค.
๊ทธ๊ฒ์ ๋ํด ์ค์ ๋ก ์ง์ ํ ์ ์ด ์์๊ธฐ ๋๋ฌธ์ dict-of-dict
์ ๊ทผ ๋ฐฉ์(ํ์ฌ ์ฌ์ฉ๋์ง๋ ์์ง๋ง ์ด๋ฏธ ๊ตฌํ๋์์ผ๋ฉฐ ์ด๋ฌํ ๋ชจ๋ ๋ฌธ์ ๋ ํด๊ฒฐํจ)์ด ์ ๋ง ํ์คํ๊ฒ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๊น?
agg_assign
์ ๊ทผ ๋ฐฉ์์ ์ ์ธํ๊ณ ๋ dict-of-dict
์ฌ์ ํ ๊ฐ์ฅ ๊ฐ๋จํ ๊ฒ์ผ๋ก ๋ณด์ด๋ฉฐ ์ฝ๋ฉ์ด ํ์ํ์ง ์์ผ๋ฉฐ ๋จ์ง ๋น์ถ์ฒ์
๋๋ค.
agg_assign
์ ๊ทผ ๋ฐฉ์์ ์ฅ์ ๊ณผ ๋จ์ ์ ์ด ์ ํ ์ ์ง๊ณ ๋ฐฉ๋ฒ์ผ๋ก ํธ์ํ๋ค๋ ๊ฒ์
๋๋ค. ๋ชจ๋ ์์ ์์, x
์ ์ ๋ฌ lambda
์ฒ๋ผ ๋ญ๊ฐ self.get_group(group)
๊ฐ ๊ทธ๋ฃน์ ๋ํด self
ํ๋ DataFrameGroupBy
๊ฐ์ฒด์
๋๋ค. ์ด๊ฒ์ **kwargs
์๋ ๋ค์ด๋ฐ์ ํจ์์ ์๋ ์ ํ ๊ณผ ๋ช
ํํ๊ฒ ๋ถ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ข์ต๋๋ค.
๋จ์ ์ ๋ฉ์ง๊ณ ์ผ๋ฐ์ ์ธ ์ง๊ณ ํจ์๊ฐ ์ด์ ์ด ์ ํ๊ณผ ๊ด๋ จ๋์ด์ผ ํ๋ค๋ ๊ฒ์
๋๋ค. ๊ณต์ง ์ ์ฌ์ ์๋ค! ์ฆ, lambda x: x[col].min
์ ๊ฐ์ ๋ง์ ๋์ฐ๋ฏธ๊ฐ ์๊น๋๋ค. ๋น์ ์ ๋ํ ๊ฐ์ ๊ฒ๋ค์์ฃผ์ํด์ผํฉ๋๋ค np.min
๋, ๋ชจ๋ ์ฐจ์์ ๊ฑธ์ณ ๊ฐ์ํ๋ pd.DataFrame.min
์ด์ ๊ฐ์ํ๋ axis=0
. ์ด๊ฒ์ด agg_assign
์ ๊ฐ์ ๊ฒ์ด apply
์ ๊ฐ์ง ์์ ์ด์ ์
๋๋ค. apply
์ฌ์ ํ ํน์ ๋ฉ์๋์ ๋ํด ์ด ๋จ์๋ก ์๋ํฉ๋๋ค.
์ด๋ฌํ ์ ์ถฉ์ ๊ณผ dict-of-dicts ๋ฐฉ๋ฒ์ ๋ํด์๋ ์ ๋ชจ๋ฅด๊ฒ ์ง๋ง ๋ค๋ฅธ ์ฌ๋๋ค์ ์๊ฐ์ด ๊ถ๊ธํฉ๋๋ค. ๋ค์์ agg_assign
์ ๋๋ต์ ์ธ ์ค์ผ์น์
๋๋ค. ์ด๊ฒ์ agg_table
๋ผ๊ณ ๋ถ๋ ์ต๋๋ค. ํจ์๊ฐ ์ด์ด ์๋๋ผ ํ
์ด๋ธ๋ก ์ ๋ฌ๋๋ค๋ ์ ์ ๊ฐ์กฐํ๊ธฐ ์ํด ํธ์ถํ ๊ฒ์
๋๋ค.
from collections import defaultdict
import pandas as pd
import numpy as np
from pandas.core.groupby import DataFrameGroupBy
mydf = pd.DataFrame(
{
'cat': ['A', 'A', 'A', 'B', 'B', 'C'],
'energy': [1.8, 1.95, 2.04, 1.25, 1.6, 1.01],
'distance': [1.2, 1.5, 1.74, 0.82, 1.01, 0.6]
},
index=range(6)
)
def agg_table(self, **kwargs):
output = defaultdict(dict)
for group in self.groups:
for k, v in kwargs.items():
output[k][group] = v(self.get_group(group))
return pd.concat([pd.Series(output[k]) for k in output],
keys=list(output),
axis=1)
DataFrameGroupBy.agg_table = agg_table
์ฉ๋ฒ
>>> gr = mydf.groupby("cat")
>>> gr.agg_table(n=len,
foo=lambda x: x.energy.min(),
bar=lambda y: y.distance.min())
n foo bar
A 3 1.80 1.20
B 2 1.25 0.82
C 1 1.01 0.60
์ด ์ฑ๋ฅ์ ๋ ๋์ฐํ๊ฒ ๋ง๋ค๊ธฐ ์ํด ์ฐ๋ฆฌ๊ฐ ์ฝ๊ฐ ํ ์ ์๋ค๊ณ ์๊ฐํ์ง๋ง .agg
๋งํผ์ ์๋๋๋ค...
Pandas Core Team์ ๋๊ตฐ๊ฐ๊ฐ groupby.agg
์์ dicts์ ๋ ์ด๋ธ์ ๋ค์ ์ง์ ํ์ง ์๋ ์ฃผ๋ ์ด์ ๊ฐ ๋ฌด์์ธ์ง ์ค๋ช
ํด ์ฃผ์๊ฒ ์ต๋๊น?
์ฝ๋๋ฅผ ์ ์ง ๊ด๋ฆฌํ๋ ๋ฐ ๋๋ฌด ๋ง์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง ์ฝ๊ฒ ์ดํดํ ์ ์์ง๋ง ์ต์ข ์ฌ์ฉ์์ ๋ณต์ก์ฑ์ ๊ดํ ๊ฒ์ด๋ผ๋ฉด ํ์ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋นํด ๋งค์ฐ ๋ช ํํ๊ธฐ ๋๋ฌธ์ ๋ค์ ๊ฐ์ ธ์ค๋ ๊ฒ๋ ์ ํํฉ๋๋ค...
๊ฐ์ฌํฉ๋๋ค!
Pandas Core Team์ ๋๊ตฐ๊ฐ๊ฐ groupby.agg์์ dicts์ ๋ ์ด๋ธ์ ๋ค์ ์ง์ ํ์ง ์๋ ์ฃผ๋ ์ด์ ๊ฐ ๋ฌด์์ธ์ง ์ค๋ช ํด ์ฃผ์๊ฒ ์ต๋๊น?
https://github.com/pandas-dev/pandas/pull/15931/files#diff -52364fb643114f3349390ad6bcf24d8fR461 ๋ณด์ จ๋์?
์ฃผ๋ ์ด์ ๋ dict-key๊ฐ ๋ ๊ฐ์ง ์์ ์ ์ํํ๊ธฐ ์ํด ์ค๋ฒ๋ก๋๋์๊ธฐ ๋๋ฌธ์ ๋๋ค. Series / SeriesGroupBy์ ๊ฒฝ์ฐ ์ด๋ฆ ์ง์ ์ฉ์ ๋๋ค. DataFrame/DataFrameGroupBy์ ๊ฒฝ์ฐ ์ด์ ์ ํํ๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
In [32]: mydf.aggregate({"distance": "min"})
Out[32]:
distance 0.6
dtype: float64
In [33]: mydf.aggregate({"distance": {"foo": "min"}})
/Users/taugspurger/Envs/pandas-dev/bin/ipython:1: FutureWarning: using a dict with renaming is deprecated and will be removed in a future version
#!/Users/taugspurger/Envs/pandas-dev/bin/python3.6
Out[33]:
distance
foo 0.6
In [34]: mydf.distance.agg({"foo": "min"})
Out[34]:
foo 0.6
Name: distance, dtype: float64
In [35]: mydf.groupby("cat").agg({"distance": {"foo": "min"}})
/Users/taugspurger/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/groupby.py:4201: FutureWarning: using a dict with renaming is deprecated and will be removed in a future version
return super(DataFrameGroupBy, self).aggregate(arg, *args, **kwargs)
Out[35]:
distance
foo
cat
A 1.20
B 0.82
C 0.60
In [36]: mydf.groupby("cat").distance.agg({"foo": "min"})
/Users/taugspurger/Envs/pandas-dev/bin/ipython:1: FutureWarning: using a dict on a Series for aggregation
is deprecated and will be removed in a future version
#!/Users/taugspurger/Envs/pandas-dev/bin/python3.6
Out[36]:
foo
cat
A 1.20
B 0.82
C 0.60
์ด๊ฒ์ ํ๋ค์์ ์๋ง๋ ๊ฐ์ฅ ํผ๋์ค๋ฌ์ด ๊ฒ์ด ์๋๋ฏ๋ก ์๋ง๋ ๋ค์ ๋ฐฉ๋ฌธํ ์ ์์ ๊ฒ์ ๋๋ค. :) ๋๋ ์๋ง๋ ๋ช ๊ฐ์ง ์์ธ์ ์ธ ๊ฒฝ์ฐ๋ฅผ ๋์น๊ณ ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฌ๋ dict-of-dicts ์ง๊ณ๋ฅผ ์ ๊ฑฐํ๋๋ผ๋ ์ด๋ฆ ์ง์ ๊ณผ ์ด ์ ํ ๊ฐ์ ์ฌ์ ํ ๋ถ์ผ์น๊ฐ ์์ต๋๋ค.
Series / SeriesGroupBy ์ฌ์ ํค๋ ํญ์ ์ถ๋ ฅ ์ด๋ฆ์ ์ง์ ํ๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
DataFrame / DataFrameGroupby์ ๊ฒฝ์ฐ ์ฌ์ ํค๋ ํญ์ ์ ํ์ฉ์ ๋๋ค. dict-of-dicts๋ฅผ ์ฌ์ฉํ์ฌ ์ด์ ์ ํํ ๋ค์ ๋ด๋ถ dict๋ Series / SeriesGroupBy์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ถ๋ ฅ ์ด๋ฆ์ ์ง์ ํ๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
์ฐ๋ฆฌ๋ ์ด์ ์ ์ด๊ฒ์ ๊ฐ๋ตํ๊ฒ ๋ ผ์ํ์ผ๋ฉฐ(์ง์ ์ค๋จ์ ๋ํ ๊ธด ํ ๋ก ์ ์ด๋๊ฐ) ์ฌ๊ธฐ์์ ๋น์ทํ ๊ฒ์ ์ ์ํ์ต๋๋ค: https://github.com/pandas-dev/pandas/pull/14668#issuecomment -274508089. ๊ทธ๋ฌ๋ ๊ฒฐ๊ตญ์๋ ์ฌ์ฉ ์ค๋จ๋ง ๊ตฌํ๋์์ผ๋ฉฐ dicts('์ด๋ฆ ๋ฐ๊พธ๊ธฐ' ๊ธฐ๋ฅ)๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ๋ ์ฝ๊ฒ ๋ง๋ค๊ธฐ ์ํ ์์ด๋์ด๋ ๊ตฌํ๋์ง ์์์ต๋๋ค.
๋ฌธ์ ๋ ๋์
๋๋ฆฌ๊ฐ '์ ํ'(์ด๋ค ์ด์ ์ด ๊ธฐ๋ฅ์ ์ ์ฉํ์๊ฒ ์ต๋๊น)๊ณผ '์ด๋ฆ ๋ณ๊ฒฝ'(์ด ํจ์๋ฅผ ์ ์ฉํ ๋ ๊ฒฐ๊ณผ ์ด ์ด๋ฆ์ด ๋์ด์ผ ํจ)์ ๋ชจ๋ ์ฌ์ฉ๋์๋ค๋ ๊ฒ์
๋๋ค. ์ฌ๊ธฐ agg_assign
์ ์์์ ๋
ผ์๋ ๊ฒ์ฒ๋ผ dicts์ ๋ณ๊ฐ๋ก ๋์ฒด ๊ตฌ๋ฌธ์ ํค์๋ ์ธ์๊ฐ ๋ ์ ์์ต๋๋ค.
agg
์์ฒด์ ์๋ agg_assign
์ ๊ฐ์ ์๋ก์ด ๋ฐฉ๋ฒ์ ์๋ ๋๋ ์ฌ์ ํ ์ด ๊ฐ๋ฅ์ฑ์ ํ๊ตฌํ๋ ๋ฐ ์ฐฌ์ฑํฉ๋๋ค.
๊ทธ๋ ๋ด๊ฐ ์ ์ํ ๊ฒ์ agg_assign
์ ๋น์ทํ์ง๋ง ๋๋ค ํจ์ ๋์ ํค์๋๋น dict๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด์์ต๋๋ค. ์ฌ๊ธฐ ์์ ๋ก ๋ฒ์ญํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
mydf.groupby('cat').agg(
energy_sum={'energy': 'sum'},
energy_p98={'energy': lambda x: np.percentile(x, 98)},
energy_p17={'energy': lambda x: np.percentile(x, 17)},
distance_sum={'distance': 'sum'},
distance_mean={'distance': 'mean'},
distance_mad={'distance': smrb.mad},
distance_mad_c1={'distance': mad_c1})
์ด๊ฒ์ด ๋ฐ๋์ ๋ชจ๋ ๋๋ค๊ฐ ์๋ ๋ฒ์ ์ผ๋ก ๋ ์ฝ๊ธฐ ์ฝ๊ณ ์ฐ๊ธฐ ์ฌ์ด์ง๋ ํ์คํ์ง ์์ง๋ง, pandas๊ฐ ์ฌ์ ํ ํด๋น ์ด์์ ํฉ๊ณ, ํ๊ท ๋ฑ์ ๋ํด ์ต์ ํ๋ ๊ตฌํ์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด ๋ฒ์ ์ด ์ ์ฌ์ ์ผ๋ก ๋ ์ฑ๋ฅ์ด ์ข์ ์ ์์ต๋๋ค. ๋๋ค ๋๋ ์ฌ์ฉ์ ์ง์ ํจ์๊ฐ ์์ต๋๋ค.
์ด ์ ๊ทผ ๋ฐฉ์์ ๋ํ ํฐ ์ง๋ฌธ์ df.groupby('cat').agg(foo='mean')
๊ฐ ๋ฌด์์ ์๋ฏธํ ๊น์? ์ ํํ์ง ์์๊ธฐ ๋๋ฌธ์ ๋
ผ๋ฆฌ์ ์ผ๋ก ๋ชจ๋ ์ด์ 'ํ๊ท '์ด ์ ์ฉ๋ฉ๋๋ค(์ด์ {'col1' : {'foo': 'mean'}, 'col2': {'foo':'mean'}, 'col3': ...}
์ ์ ์ฌ). ๊ทธ๋ฌ๋ ๊ทธ ๊ฒฐ๊ณผ ๋ค์ค ์ธ๋ฑ์ฑ๋ ์ด์ด ์์ฑ๋์ง๋ง ์์ ์์์๋ MI ์ด๋ก ๋๋์ง ์๋ ๊ฒ์ด ์ข์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
์์ ์์
์ ๊ธฐ์กด agg
๋ด๋ถ์์ ์ด์ ๋ฒ์ ๊ณผ ํธํ๋ ์ ์๋ค๊ณ ์๊ฐํ์ง๋ง ์ด๊ฒ์ด ํ์ํ์ง ์ฌ๋ถ๊ฐ ๋ฌธ์ ์
๋๋ค.
๋๋ ๋ํ ์ด๊ฒ์ด ๋ค์๊ณผ ๊ฐ์ series
์ฌ๋ก๋ก ๋ฉ์ง๊ฒ ํ์ฅ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
mydf.groupby('cat').distance.agg(
distance_sum='sum',
distance_mean='mean',
distance_mad=smrb.mad,
distance_mad_c1=mad_c1)
(๊ทธ๋ฆฌ๊ณ '๊ฑฐ๋ฆฌ'์ ๋ํด ํ ๋ฒ, '์๋์ง'์ ๋ํด ํ ๋ฒ ์์ ์์ ์ ์ํํ๊ณ ๋ชจ๋ ๋์ ๋๋ฆฌ/๋๋ค๊ฐ ๋ง์์ ๋ค์ง ์์ผ๋ฉด ๊ฒฐ๊ณผ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ์ ๊ณ ๋ คํ ์๋ ์์ต๋๋ค)
@TomAugspurger agg_table
์ ๊ฐ๋จํ ๊ตฌํ ์์์ ๊ทธ๋ฃน์ ๋ฐ๋ณตํ๋ ๋์ ์ ์ฉํ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ๋ฐ๋ณตํ๊ณ ๊ฒฐ๊ตญ์๋ axis=1๋ก ์ ์ด์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ๋ ๋ซ์ง ์์๊น์? ์๋ก ํ์ฑ๋ ํ์ axis=0 ์ผ๋ก ์ฐ๊ฒฐํ๋ ๋์ ?
BTW, @zertrin @tdpetrou @smcateer @pirsquared ๋ฑ, ์ด ๋ฌธ์ ๋ฅผ ์ ๊ธฐํ๊ณ ์์ธํ ํผ๋๋ฐฑ์ ์ ๊ณตํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์ด๋ฌํ ํผ๋๋ฐฑ๊ณผ ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ๋ ๋งค์ฐ ์ค์ํฉ๋๋ค!
์ ๋ ์ค์ ๋ก @tdpetrou๊ฐ ์ ์ํ ํจํด์ ์ ๋ง ์ข์ํฉ๋๋ค(์๋ฆฌ์ฆ๋ฅผ ๋ฐํํ๋ ํจ์์ ํจ๊ป ์ ์ฉ ์ฌ์ฉ) - ์๋ง๋ dicts๋ณด๋ค ๋ ๋์ ๊ฒ์ ๋๋ค.
ํจ์๊ฐ pd.Series(data, index=data.keys())
๋ฐํํ๋ฉด ์ธ๋ฑ์ค๋ฅผ ์ฌ๋ฐ๋ฅธ ์์๋ก ์ป์ ์ ์์ต๋๊น? (๋ด ์ฝ๋์์ ํจํด์ ๊ฐ์ฅ ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์๊ฐํ๋ ๊ฒ - ์ฃผ์ ์์ ๋ฒ์ด๋ ์ํ์ด ์์).
ํธ์ง: ์ฃ์กํฉ๋๋ค. ์ธ๋ฑ์ค ์ธ์์ ์์ ์ ์๋ชป ์ดํดํ์ต๋๋ค(์ฌ๊ธฐ์๋ ์ ํ ์ฌํญ์ด๋ฉฐ ์ด์ ์์๋ฅผ ์ง์ ํ๋ ค๋ ๊ฒฝ์ฐ์๋ง ํ์ํฉ๋๋ค. pd.Series(data)
๋ฐํํ๋ฉด ์์
์ด ์ํ๋ฉ๋๋ค).
@tdpetrou ์ ์์ ๋ first
& last
์ง๊ณ์ ํจ๊ป ์๋ํฉ๋๊น?
๋๋ ์ด๋ ๊ฒ ๋จธ๋ฆฌ / ๊ผฌ๋ฆฌ์ ์์งํด์ผํ์ต๋๋ค.
def agg_funcs(x):
data = {'start':x['DATE_TIME'].head(1).values[0],
'finish':x['DATE_TIME'].tail(1).values[0],
'events':len(x['DATE_TIME'])}
return pd.Series(data, index = list(data.keys()))
results = df.groupby('col').apply(agg_funcs)
๋๋ ์ฌ์ ํ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ถ์ง๋ง 0.23์์๋ ๋๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
@tdpetrou ์ ์ ๊ทผ ๋ฐฉ์์ ์ฝ๋์์ ๋ค์๋ ์ฌ์ฉํ์ง ์์ ํจ์๋ฅผ ์ ์ํ์ง ์๊ณ ์๋ํ ์ ์์ต๋๊น? Q/Kdb+ ์ธ๊ณ(SQL๊ณผ ์ ์ฌ)์์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ ์ ํ ๋ฌธ์ ๋ํด ์์ ๋ณ์/ํจ์๋ฅผ ๋ง๋ค์ด์ผ ํ๋ ์ด์ ๊ฐ ํผ๋์ค๋ฝ์ต๋๋ค.
์ฌ๊ธฐ OP.
์์งํ ๋งํด์, ์ด ๋ชจ๋ ์๊ฐ๊ณผ #15931 ๋ฐ ์ฌ๊ธฐ์์ ๋ง์ ํ ๋ก ์ ํ์์๋ ๋ถ๊ตฌํ๊ณ , ๋๋ ์ด๊ฒ์ด relabeling dicts๋ฅผ ๋ ์ด์ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข์ ์๊ฐ์ธ์ง ์ฌ์ ํ ํ์ ํ์ง ๋ชปํฉ๋๋ค.
๊ฒฐ๊ตญ, ์ฌ๊ธฐ์ ์ ์๋ ๋์ ์ค ์ด๋ค ๊ฒ๋ ํ์ฌ relabeling dict ์ ๊ทผ ๋ฐฉ์ IMHO๋ณด๋ค ์ฌ์ฉ์์๊ฒ ๋ ์ง๊ด์ ์ด์ง ์์ต๋๋ค. ๋ฌธ์์ ์์์ ๋ ํ ๊ฐ์ง ์๋ง ๋ค์ด๋ ์ด๊ฒ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ๋ช ํํ๊ณ ๋งค์ฐ ์ ์ฐํ์ต๋๋ค.
๋ฌผ๋ก ํฌ๋ ๊ฐ๋ฐ์๋ ์ฌ์ ํ ์ฌ์ฉ์์ ๊ด์ ์ ๋ฐ๋ผ ๋ค๋ฅธ ์๊ฐ์ ํ ์ ์์ต๋๋ค.
relabeling dict ์ ๊ทผ ๋ฐฉ์์กฐ์ฐจ๋ ๋งค์ฐ ์ง๊ด์ ์ด์ง ์์ต๋๋ค. ์ ์๊ฐ์๋ ๊ตฌ๋ฌธ์ด SQL - func(column_name) as new_column_name
์ ์ ์ฌํด์ผ ํฉ๋๋ค. Python์์๋ 3๊ฐ ํญ๋ชฉ ํํ์ ์ฌ์ฉํ์ฌ ์ด ์์
์ ์ํํ ์ ์์ต๋๋ค. (func, column_name, new_column_name)
. ์ด๊ฒ์ด dexplo๊ฐ groupby ์ง๊ณ๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ์
๋๋ค.
@zertrin ์์ ๋ด ์ ์์ ๋ํ ํผ๋๋ฐฑ์ด ์์ต๋๊น: https://github.com/pandas-dev/pandas/issues/18366/#issuecomment -349089667
๊ฒฐ๊ตญ "{col: {name: func}}" ๋์ "**{name: {col: func}}"์ ๊ฐ์ด ์ฌ์ ์ ์์๋ฅผ ๋ค์ง์ต๋๋ค.
@jorisvandenbossche ๊ทํ์ ์ ๊ทผ ๋ฐฉ์์ ๊ณ ๋ คํ์ต๋๋ค. ๋ฌธ์ ๋ ํ์ฌ ์ ๊ทผ ๋ฐฉ์์ ๋นํด ์ด๋ค ์ถ๊ฐ ์ด์ ์ด ์๋์ง ์ค์ ๋ก ์์ง ๋ชปํ๋ค๋ ๊ฒ์ ๋๋ค.
๋ ์ง์ค์ ์ผ๋ก ๋งํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ ํ์ด ์ฃผ์ด์ง๋๋ค.
๊ฐ๋ฐ์์ ์ฌ์ฉ์์ ๊ด์ ์์ ์๋ฏธ ์๊ณ ์ค์ง์ ์ธ ์ด์ ์ ์ ๊ณตํ์ง ์๋ ํ ์ 2๋ฅผ ์ ํํด์ผ ํ๋์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
์ ์ ์์์ ๋ช ๊ฐ์ง ์์ ์ ํด๊ฒฐํ๋ ค๋ฉด:
๋ฌธ์ ๋ ๋์ ๋๋ฆฌ๊ฐ '์ ํ'(์ด๋ค ์ด์ ์ด ๊ธฐ๋ฅ์ ์ ์ฉํ์๊ฒ ์ต๋๊น)๊ณผ '์ด๋ฆ ๋ณ๊ฒฝ'(์ด ํจ์๋ฅผ ์ ์ฉํ ๋ ๊ฒฐ๊ณผ ์ด ์ด๋ฆ์ด ๋์ด์ผ ํจ)์ ๋ชจ๋ ์ฌ์ฉ๋์๋ค๋ ๊ฒ์ ๋๋ค.
์ด์ ์ ์ ๋ฌธ์ํ๋์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์์๊ฒ ๋ฌธ์ ๊ฐ ๋์ง ์์๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ฐ์ธ์ ์ผ๋ก ์ค๋ช ์์ ์์ ๋ฅผ ๋ณด๊ณ ์ฆ์ ์์ ์ ์์์ต๋๋ค. (ํธ์ง: ๊ทธ๋ฆฌ๊ณ ๋๋ ์๊ฐํ์ต๋๋ค: _"์! ๋งค์ฐ ์ ์ฉํ ๊ตฌ์กฐ, ๋ด๊ฐ ์ฐพ๋ ๊ฒ๊ณผ ์ ํํ ์ผ์นํฉ๋๋ค. ์ข์ต๋๋ค."_)
dicts์ ๋ณ๊ฐ๋ก ๋์ฒด ๊ตฌ๋ฌธ์ ํค์๋ ์ธ์์ผ ์ ์์ต๋๋ค.
dict-of-dict ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ๋ ๋งค๋ ฅ์ ์ธ ์ ์ค ํ๋๋ ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ๋์ ์ผ๋ก ์์ฑํ ์ ์๋ค๋ ๊ฒ์
๋๋ค. ์ด ํญ๋ชฉ ๋ฐ๋ก ์์ ์ฃผ์์์ ์ง์ ํ๋ฏ์ด ์ ์์์์ ๊ฐ์ด ํค์๋ ์ธ์๋ก ์ด๋ํ๋ฉด **{name: {col: func}}
๊ตฌ์กฐ๋ฅผ ํตํด ์ฌ์ ํ ์ด๋ฅผ ํ์ฉํฉ๋๋ค. ๊ทธ๋์ ๋๋ ๋น์ ์ ์ ์์ ๋ฐ๋ํ์ง ์์ต๋๋ค. ํ์ฌ ๊ตฌํ๋ ์์คํ
์ผ๋ก ์ด๋ฏธ ๋์ผํ ์์ค์ ๊ธฐ๋ฅ์ ๋ฌ์ฑํ์ ๋ ๋ถ๊ฐ ๊ฐ์น์ ๊ทธ๋ฌํ ๋ณ๊ฒฝ์ ํ์์ฑ์ ์์ง ๋ชปํฉ๋๋ค.
๊ฒฐ๊ตญ, pandas core dev๊ฐ ํ์ฌ ์ ๊ทผ ๋ฐฉ์์ ๋ํด ๊ฐํ ๋๋์ ๊ฐ์ง๊ณ ์๋ค๋ฉด ๋น์ ์ ์ ์์ _๊ด์ฐฎ์ต๋๋ค_. ์ ๋ _user_๋ก์ ์ด๋ค ์ด์ ๋ ๋๋ผ์ง ๋ชปํฉ๋๋ค. (์ฌ์ค ๋๋ ๋ชจ๋ ๊ธฐ์กด ์ฌ์ฉ์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ฌ ์๋ก์ด ์ ์์์ ๋ค์ ์๋ํ๋๋ก ํ๋ ๋จ์ ์ ๋ด ๋๋ค.)
@zertrin ์ฐ๋ฆฌ๋ ์ด์ ์ผ๋ถ ํต์ฌ ๊ฐ๋ฐ์์ ์ด์ ๋ํด ๋ ผ์ํ์ง๋ง ์ฌ๊ธฐ์ ์์ฝ์ ์์ฑํ์ง ๋ชปํ์ต๋๋ค. ๊ทธ๋์ ์ด์ ๋๋ ๋น์ ์ ์๊ฒฌ์ ๋ตํ๊ธฐ ์ ์ ์ด์ ์ ์ฐ๋ฆฌ ์๊ฐ์ ๋ฐ์
๋ฐ๋ผ์ ๋จผ์ SQL "SELECT avg(col2) as col2_avg"์ ๊ฐ์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ด ์๋ํ๊ณ ์ฌ์์ผ ํ๋ค๋ ๊ฐ๋ ์ ์ ์ ์ผ๋ก ๋์ํ๋ฉฐ ์ด์ ๋ํ ์๋ฃจ์ ์ ๊ฐ๊ณ ์ถ์ต๋๋ค.
์ด๊ฒ์ ๋น์ ์ด ์ค์ ๋ก ์ถ์ง ์์ํ๋ MultiIndex์ ๋ง๋ค์ด์ผ๋ก ๋ณ๋๋ก ์๋์ ์ด์ ์์ ์ฐ๋ฆฌ๋ dicts์ ํ์ฌ (์ฌ์ฉ๋์ง ์์) dicts๋์๋ ๊ทธ ์ด์์ ์ ๋๋ค (๋๋ ๊ฐํํ์ง ์์ ์์๋ค)์ด ๋ ์ด์ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค :
In [1]: df = pd.DataFrame({'A': ['a', 'b', 'a'], 'B': range(3), 'C': [.1, .2, .3]})
In [3]: gr = df.groupby('A')
In [4]: gr.agg({'B': {'b_sum': 'sum'}, 'C': {'c_mean': 'mean', 'c_count': 'count'}})
Out[4]:
C B
c_count c_mean b_sum
A
a 2 0.2 2
b 1 0.2 1
์์ ๊ฒฝ์ฐ MultiIndex์ ์ฒซ ๋ฒ์งธ ์์ค์ ๋ถํ์ํฉ๋๋ค. ์ด๋ฏธ ๊ตฌ์ฒด์ ์ผ๋ก ์ด ์ด๋ฆ์ ๋ณ๊ฒฝํ๊ธฐ ๋๋ฌธ์
๋๋ค(OP์ ์์์ ์ด์ ์ฒซ ๋ฒ์งธ ์์ค์ ์ญ์ ํ ๋ค์๋ ๋ฐ๋ก ์ด์ด์ง).
๊ทธ๋ฌ๋ MultiIndex๊ฐ ํ์ํ๊ณ ์๋ฏธ๊ฐ ์๋ gr.agg(['sum', 'mean'])
๋๋ (ํผํฉ) gr.agg({'B': ['sum', 'mean'], 'C': {'c_mean': 'mean', 'c_count': 'count'}})
์ ๊ฐ์ ์์
๋ ์ํํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ์ด๋ ต์ต๋๋ค.
๊ทธ๋์ ์์ ๋
ผ์์์ ์ธ๊ธํ ์ ์ ์ค ํ๋๋ ์ต์ข
์ด ์ด๋ฆ์ ๋ณ๋๋ก ์ง์ ํ๋ ๋ฐฉ๋ฒ์ด์์ต๋๋ค(์: https://github.com/pandas-dev/pandas/issues/18366#issuecomment-346683449).
์๋ฅผ ๋ค์ด aggregate
์ ์ถ๊ฐ ํค์๋๋ฅผ ์ถ๊ฐํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ์ด ์ด๋ฆ์ ์ง์ ํฉ๋๋ค.
gr.agg({'B': 'sum', 'C': ['mean', 'count']}, columns=['b_sum', 'c_mean', 'c_count'])
๊ฐ๋ฅํ ๊ฒ์
๋๋ค.
๊ทธ๋ฌ๋ ์ด/ํจ์ ์ฌ์๊ณผ ์ ์ด ์ด๋ฆ์ ๋ถํ ํ๋ฉด ์ด๋ฅผ ์ ํค์๋๋ณด๋ค ๋ ์ผ๋ฐ์ ์ผ๋ก ๋ง๋ค๊ณ ๋ค์๊ณผ ๊ฐ์ด ํ ์๋ ์์ต๋๋ค.
gr.agg({'B': 'sum', 'C': ['mean', 'count']}).rename(columns=['b_sum', 'c_mean', 'c_count'])
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด https://github.com/pandas-dev/pandas/issues/14829 ๊ฐ ํ์ํฉ๋๋ค(0.24.0์์ ํ๊ณ ์ถ์ ๊ฒ).
(์ค์ ์ฌํญ : ์ฐ๋ฆฌ๋์ด ์๋ฃจ์
์ ์ง์ํ๋ ค๋ ๊ฒฝ์ฐ ์ฐ๋ฆฌ๋ ์ด๋ฆ์ ์๋ ์ค๋ณต ์ ๊ฑฐ์ ์ด๋ค ์ข
๋ฅ๋ฅผํด์ผํ๋ฏ๋ก์ด๋ฅผ ์ํด ์ฐ๋ฆฌ๋ ๋๋ค ํจ์์ ์ด๋ฆ์ด ์ค๋ณต ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ ์์ต๋๋ค.)
๊ทธ๋ฐ ๋ค์, ์ฐ๋ฆฌ๋ ์ฌ์ ํ ์ด๋ฆ ๋ณ๊ฒฝ์ ์ํ ํค์๋ ์ธ์์ ๋ฐฉ์์ ์ข์ํฉ๋๋ค. ๊ทธ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
assign
๊ฐ ํฌ๋์์ ์๋ํ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ๊ณ groupby().aggregate()
๊ฐ ibis์์ ์๋ํ๋ ๋ฐฉ์๊ณผ๋ ์ผ์นํฉ๋๋ค(์: R์ dplyr์์ ๋ณด์ด๋ ๋ฐฉ์๊ณผ ์ ์ฌํจ).์ฐ๋ฆฌ๋ ๊ทธ๊ฒ์ด ์ด๋ป๊ฒ ์๊ฒผ๋์ง์ ๋ํด ์ฝ๊ฐ์ ํ ๋ก ์ ํ์ต๋๋ค. ์์์ ์ ์ํ ๊ฒ์ (์ฒซ ๋ฒ์งธ ์์ ์์์ ๋์ผํ ์ด/ํจ์ ์ ํ์ ์ฌ์ฉํ๊ธฐ ์ํจ):
gr.agg(b_sum={'B': 'sum'}, c_mean={'C': 'mean'}, c_count={'C': 'count'})
์ฌ์ ํ ์ด ์ฌ์์ dict์ dict๋ก ๊ตฌ์ถํ ์ ์์ง๋ง ํ์ฌ(์ฌ์ฉ๋์ง ์๋) ๋ฒ์ ๊ณผ ๋น๊ตํ์ฌ ๋ด๋ถ ๋ฐ ์ธ๋ถ ์์ค์ด ๋ฐ๋์์ต๋๋ค.
gr.agg(**{'b_sum': {'B': 'sum'}, 'c_mean': {'C': 'mean'}, 'c_count': {'C': 'count'})
(์ฐ๋ฆฌ๋ dicts์ ๊ธฐ์กด dicts๋ฅผ ์ด ๋ฒ์ ์ผ๋ก ๋ณํํ๋ ์์ ๋์ฐ๋ฏธ ํจ์๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค)
๊ทธ๋ฌ๋ dict๋ ํญ์ ๋จ์ผ {col: func}
์ด๊ณ ์ฌ๋ฌ ๋จ์ผ ์์ dicts๋ ์ฝ๊ฐ ์ด์ํด ๋ณด์
๋๋ค. ๊ทธ๋์ ์ฐ๋ฆฌ๊ฐ ์๊ฐํ ๋์์ ํํ์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
gr.agg(b_sum=('B', 'sum'), c_mean=('C', 'mean'), c_count=('C', 'count'))
์ด๊ฒ์ ์กฐ๊ธ ๋ ์ข์ ๋ณด์ด์ง๋ง ๋ค๋ฅธ ํํธ์ผ๋ก {'B': 'sum'}
dict๋ ํจ์๋ฅผ ์ ์ฉํ ์ด์ ์ง์ ํ๊ธฐ ์ํ ๋ค๋ฅธ API์ ์ผ์นํฉ๋๋ค.
์์ ๋ ์ ์(๋์ค์ ๋ ์ฌ์ด ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ๋ฐ ํค์๋ ๊ธฐ๋ฐ ์ด๋ฆ ์ง์ )์ ์์น์ ์ผ๋ก ์ง๊ตํ์ง๋ง ๋ ๋ค ์์ผ๋ฉด ์ข์ ์ ์์ต๋๋ค(๋๋ ์ถ๊ฐ ๋ ผ์์ ๋ฐ๋ผ ๋ค๋ฅธ ๊ฒ).
๊ฐ๋ฐ์์ ํ์ฌ ์๊ฐ์ ์ฌ๊ธฐ์ ์ ๋ฌํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค ๐
๋๋ ๊ฒฐ๊ณผ์ ์ผ๋ก MultiIndex๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ dict-of-dict ์ ๊ทผ ๋ฐฉ์์ (๋ด ์๊ฐ์๋ ์ ์ผํ) ๋จ์ ์ ์ธ์ ํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ์ถ๊ฐ ์ต์ (์, YAO :-/ )์ ์ ๋ฌํ๋ฉด ํ๋ฉดํ๋ ์ ์์ต๋๋ค.
์ธ๊ธํ ๋ฐ์ ๊ฐ์ด, ์ ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ๋ฅํ ํ ๋ ๋ฒ์งธ ๋ฒ์ ์ ๋ฐ๋ํ์ง ์์ต๋๋ค.
**{}
๊ตฌ์กฐ ๋๋ถ์ Python!)๋ฐ๋ผ์ ๋ง์ง๋ง ์ ์(col>func ๋งคํ์ ๋ํ ์ฌ์ ๋๋ ํํ ํฌํจ)์ ๊ด์ฐฎ๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ด์ ์๊ฒฌ์ ์ฒซ ๋ฒ์งธ ์ ์์ ์ ๋ง ์ํ๋ฉด ๊ตฌํํ ์ ์์ง๋ง ์ด์ ๋ํ ์ ํผ๋๋ฐฑ์ ์ฌ์ฉ์๋ก์ ๋ ๋ฒ์งธ ๋์๋ณด๋ค ๋๊ธฐํ๋ฅผ ์ ์งํด์ผ ํ๋ ๊ณ ํต ๋๋ฌธ์ ์ ํํ์ง ์์ ๊ฒ์ด๋ผ๋ ๊ฒ์ ๋๋ค. ๋ ๊ฐ์ ๋ชฉ๋ก.
์ค๋ ๊ฐ๋ฐ ํ์์์ ๋ ผ์๋์์ต๋๋ค.
์งง์ ์์ฝ
gr.agg(b_sum=("B", "sum), ...)
๊ตฌํ์ ์๋ํฉ๋๋ค. ์ฆ arg
์ ๋ฌ๋ *GroupBy.agg
๊ฐ ์์ผ๋ฉด kwargs๋ฅผ <output_name>=(<selection>, <aggfunc>)
๋ก ํด์ํฉ๋๋ค.flatten=True
ํค์๋ .agg
์๋ง๋ ์ด๊ฒ์ด ๋์์ด ๋ ๊ฒ์ ๋๋ค. ์ฌ์ฉ ์ค๋จ์ ๋ํ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ alias->aggr ๋งต์ ์ฌ๋ฐ๋ฅธ ์ด๋ฆ์ ํจ์ ๋ชฉ๋ก์ผ๋ก ๋์ฒดํ๋ ๋ค์ ๋์ฐ๋ฏธ ํจ์์ ๋๋ค.
def aliased_aggr(aggr, name):
if isinstance(aggr,str):
def f(data):
return data.agg(aggr)
else:
def f(data):
return aggr(data)
f.__name__ = name
return f
def convert_aggr_spec(aggr_spec):
return {
col : [
aliased_aggr(aggr,alias) for alias, aggr in aggr_map.items()
]
for col, aggr_map in aggr_spec.items()
}
๋ค์๊ณผ ๊ฐ์ด ์ด์ ๋์์ ์ ๊ณตํฉ๋๋ค.
mydf_agg = mydf.groupby('cat').agg(convert_aggr_spec{
'energy': {
'total_energy': 'sum',
'energy_p98': lambda x: np.percentile(x, 98), # lambda
'energy_p17': lambda x: np.percentile(x, 17), # lambda
},
'distance': {
'total_distance': 'sum',
'average_distance': 'mean',
'distance_mad': smrb.mad, # original function
'distance_mad_c1': mad_c1, # partial function wrapping the original function
},
}))
์ ๊ฐ์ ๊ฒ
mydf_agg = mydf.groupby('cat').agg({
'energy': [
aliased_aggr('sum', 'total_energy'),
aliased_aggr(lambda x: np.percentile(x, 98), 'energy_p98'),
aliased_aggr(lambda x: np.percentile(x, 17), 'energy_p17')
],
'distance': [
aliased_aggr('sum', 'total_distance'),
aliased_aggr('mean', 'average_distance'),
aliased_aggr(smrb.mad, 'distance_mad'),
aliased_aggr(mad_c1, 'distance_mad_c1'),
]
})
์ด๊ฒ์ ๋๋ฅผ ์ํด ์๋ํ์ง๋ง ์ผ๋ถ ์ฝ๋ ์ผ์ด์ค์์๋ ์๋ํ์ง ์์ ๊ฒ์ ๋๋ค ...
์ ๋ฐ์ดํธ : ์ง๊ณ ์ฌ์์ ํํ์ด (๋ณ์นญ, agr)๋ก ํด์๋๋ฏ๋ก ์ด๋ฆ์ ๋ฐ๊ฟ ํ์๊ฐ ์์์ ์์์ต๋๋ค. ๋ฐ๋ผ์ alias_aggr ํจ์๋ ํ์ํ์ง ์์ผ๋ฉฐ ๋ณํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
def convert_aggr_spec(aggr_spec):
return {
col : [
(alias,aggr) for alias, aggr in aggr_map.items()
]
for col, aggr_map in aggr_spec.items()
}
์ด๋ค ํจ์์์๋ ์ด์ ์ง๊ณํ๊ณ ๊ฐ์ ํ์์ ์ฆ์ ์ด๋ฆ์ ๋ฐ๊พธ๋ ๊ธฐ๋ฅ์ด ์ ๋ง ์๋ ๋ ๋ค๋ฅธ ์ฌ์ฉ์๋ก์ ์ฌ๊ธฐ์ ์ฐธ์ฌํ๊ณ ์ถ์ต๋๋ค. ๋๋ pandas์์ ๋ฐํ๋ MultiIndex๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์ ๋ฐ๊ฒฌํ ์ ์ด _never_ ์์ต๋๋ค. ์ฆ์ ๋ณํฉํ๊ฑฐ๋ ์ค์ ๋ก ํน์ ์๋ฏธ๋ฅผ ์๋ฏธํ๋ ์ด ์ด๋ฆ์ ์๋์ผ๋ก ์ง์ ํ๊ณ ์ถ์ต๋๋ค.
์ฌ๊ธฐ์ ์ ์๋ ์ ๊ทผ ๋ฐฉ์ ์ค ํ๋์ ๋ง์กฑํ ๊ฒ์
๋๋ค. SQL๊ณผ ์ ์ฌํ ๊ตฌ๋ฌธ(์ค์ ๋ก ์ด๋ฏธ pandas์์ .query()
๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ณ ์์), ๊ฐ๊ฐ์๊ฐ ๋์์ผ๋ก ๋๋๋ฆฌ๊ธฐ, ๋ค๋ฅธ ์ ์. ํ์ฌ์ ์ ๊ทผ ๋ฐฉ์์ ์ด๋ฏธ R์ ์ฌ์ฉํ๋ ๋๋ฃ๋ค๋ก๋ถํฐ ์กฐ๋กฑ์ ๋ฐ์์ต๋๋ค.
๋๋ ์ต๊ทผ์ Pandas ๋์ PySpark๋ฅผ ์ฌ์ฉํ๊ณ ์์์ ๋ฐ๊ฒฌํ์ต๋๋ค. ํ์ํ ๊ฒ์ ์๋์ง๋ง ๊ตฌ๋ฌธ์ ํจ์ฌ ๋ ์ข์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
df.groupby("whatever").agg(
F.max("col1").alias("my_max_col"),
F.avg("age_col").alias("average_age"),
F.sum("col2").alias("total_yearly_payments")
)
๋ํ PySpark๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํฌ๋๋ณด๋ค ์ฐ๊ธฐ์ ํจ์ฌ ๋ ๋ณต์กํฉ๋๋ค. ํจ์ฌ ๋ ๊น๋ํด ๋ณด์ ๋๋ค! ๊ทธ๋์ ๋๋ ์ด๊ฒ์ ๋ํ ์์ ์ด ์์ง ์๋ฃ๋์๋ค๋ ์ ์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค :-)
์ด ๊ธฐ๋ฅ์ ๋ํด ๋์ํ ๊ตฌ๋ฌธ์ด ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ฐ๋ฆฌ๋ ๋๊ตฐ๊ฐ๊ฐ ํ์ํฉ๋๋ค
๊ตฌํํฉ๋๋ค.
2019๋
3์ 27์ผ ์์์ผ ์ค์ 9:01 Thomas Kastl [email protected]
์ผ๋ค:
๋๋ ๋จ์ง ์ ๋ง๋ก, ์ ๋ง๋ก
๋ชจ๋ ํจ์์์ ์ด์ ์ง๊ณํ๋ ๊ธฐ๋ฅ์ด ๋๋ฝ๋ ๊ฒฝ์ฐ
์ฆ์ ๊ฐ์ ํ์์ ์ด๋ฆ์ ๋ฐ๊ฟ๋๋ค. ๋๋ ๋ ์์ ์ ๋ฐ๊ฒฌํ ์ ์ด ์๋ค
ํฌ๋๊ฐ ๋ฐํํ MultiIndex ์ฌ์ฉ - ์ฆ์ ๋ณํฉํ๊ฑฐ๋,
๋๋ ์ค์ ๋ก ์ด ์ด๋ฆ์ ์๋์ผ๋ก ์ง์ ํ๊ณ ์ถ์ต๋๋ค.
์ค์ ๋ก ํน์ ํ ๊ฒ์ ์๋ฏธํฉ๋๋ค.์ฌ๊ธฐ์ ์ ์๋ ์ ๊ทผ ๋ฐฉ์ ์ค ํ๋์ ๋ง์กฑํ ๊ฒ์ ๋๋ค. SQL๊ณผ ์ ์ฌํ ๊ตฌ๋ฌธ
(์ค์ ๋ก ์ด๋ฏธ ํ๋ค์์ .query()๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ณ ์์ต๋๋ค),
๊ฐ๊ฐ ์๊ฐ ๋ ํ๋์ผ๋ก ๋๋๋ฆฌ๊ธฐ, ๋ค๋ฅธ ์ ์. ๊ทธ๋งํผ
ํ์ฌ์ ์ ๊ทผ ๋ฐฉ์์ ์ด๋ฏธ R์ ์ฌ์ฉํ๋ ๋๋ฃ๋ค๋ก๋ถํฐ ์กฐ๋กฑ์ ๋ฐ์์ต๋๋ค.๋๋ ์ต๊ทผ์ Pandas ๋์ PySpark๋ฅผ ์ฌ์ฉํ๊ณ ์์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
๊ตฌ๋ฌธ์ด ํจ์ฌ ๋ ๋ง์์ ๋ค๊ธฐ ๋๋ฌธ์ ํ์ํ์ง ์์์ต๋๋ค.df.groupby("๋ฌด์์ด๋ ").agg( F.max("col1").alias("my_max_col"),
F.avg("age_col").alias("average_age"),
F.sum("col2").alias("total_yearly_payments") )๋ํ PySpark๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํฌ๋๋ณด๋ค ์ฐ๊ธฐ์ ํจ์ฌ ๋ ๋ณต์กํฉ๋๋ค.
์ด๊ฒ์ ํจ์ฌ ๋ ๊นจ๋ํด ๋ณด์ ๋๋ค! ๊ทธ๋์ ๋๋ ํ์คํ ๊ทธ ์์ ์ ๊ฐ์ฌํฉ๋๋ค
์ด๊ฒ์ ์ฌ์ ํ โโ์ํ๋ฉ๋๋ค :-)โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธํ์ธ์.
https://github.com/pandas-dev/pandas/issues/18366#issuecomment-477168767 ,
๋๋ ์ค๋ ๋ ์์๊ฑฐ
https://github.com/notifications/unsubscribe-auth/ABQHIkCYYsah5siYA4_z0oop_ufIB3h8ks5va3nJgaJpZM4QjSLL
.
๋๋ 0.25.0์ ๋ํด ์ด๊ฒ์ ์ป์ผ๋ ค๊ณ ๋ ธ๋ ฅํ ๊ฒ์ ๋๋ค.
https://github.com/pandas-dev/pandas/pull/26399์ PR์ ์ฌ๋ ธ์ต๋๋ค (selection, aggfunc)
ํํ์ด์ด์ผ ํ๋ค๋ ์ดํด์ ํจ๊ป **kwargs
๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ๋ฐ ์ด๋ณ ์ง๊ณ์ ํผํฉ์ ํ์ฉํ๋ ๊ฒ์
๋๋ค.
In [2]: df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
...: 'height': [9.1, 6.0, 9.5, 34.0],
...: 'weight': [7.9, 7.5, 9.9, 198.0]})
In [3]: df
Out[3]:
kind height weight
0 cat 9.1 7.9
1 dog 6.0 7.5
2 cat 9.5 9.9
3 dog 34.0 198.0
In [4]: df.groupby('kind').agg(min_height=('height', 'min'), max_weight=('weight', 'max'))
Out[4]:
min_height max_weight
kind
cat 9.1 9.9
dog 6.0 198.0
์ด๊ฒ์ ๋ช ๊ฐ์ง ์ ํ ์ฌํญ์ด ์์ต๋๋ค
(output_name=(selection, aggfunc))
๋ ์ค์ ๋ก ๋ค๋ฅธ ๊ณณ์์๋ ๋ํ๋์ง ์์ต๋๋ค( .assign
๋ output_name=...
ํจํด์ ์ฌ์ฉํ์ง๋ง).agg(**{'output name': (col, func)})
**kwargs
์ ์์๊ฐ ์ด์ ์ ๋ณด์กด๋์ง ์์๊ธฐ ๋๋ฌธ์ 3.5 ๋ฐ ์ด์ ๋ฒ์ ์ ๋ํด ์ถ์
ํ ํดํน์ด ํ์ํฉ๋๋ค.๊ทธ๋ฆฌ๊ณ ๊ตฌํ ์ธ๋ถ ์ฌํญ์ด ์์ต๋๋ค. ๋์ผํ ์ด์ ๋ํ ์ฌ๋ฌ lambda
aggfuncs๋ ์์ง ์ง์๋์ง ์์ง๋ง ๋์ค์ ์์ ํ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ ๊ฐ์ ํ ๋๋ถ๋ถ์ ์ฌ๋๋ค์ด ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ๋์์ ๋ํ ๋์์ ์ง์งํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ฌ๋๋ค์ ์ด๊ฒ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋ป๊ฒ ์๊ฐํฉ๋๊น?
cc @WillAyd ๊ทํ์ ์ฐ๋ ค ์ฌํญ์ ๋์น ๊ฒฝ์ฐ.
์๋ ํ์ธ์ @TomAugspurger ๋ ,
์งํํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
์ด๊ฒ์ ๋ช ๊ฐ์ง ์ ํ ์ฌํญ์ด ์์ต๋๋ค
- ๋ค๋ฅธ ํ๋ค๋ค์๊ฒ๋ ๋ค์ ๋ ํนํฉ๋๋ค. sytanx
(output_name=(selection, aggfunc))
๋ ์ค์ ๋ก ๋ค๋ฅธ ๊ณณ์๋ ๋ํ๋์ง ์์ต๋๋ค(.assign
๋output_name=...
ํจํด์ ์ฌ์ฉํ์ง๋ง)
์ด๋ฐ ์ข ๋ฅ์ ์ฃผ์ฅ์ ์ ์ด์ ๊ธฐ์กด ๊ตฌํ์ ํํํ๋ ๋๊ธฐ๋ฅผ ๋ถ์ฌํ ์ฃผ์ฅ๊ณผ ์๋นํ ์ ์ฌํ๋ค๋ ๋๋์ ์ง์ธ ์ ์์ต๋๋ค.
ํน์ ์ฃผ์ฅ๊ณผ ๊ด๋ จํ์ฌ ๊ธฐ์กด ๋ฐฉ์๋ณด๋ค ์ด ์๋ก์ด ๋ฐฉ์์์ ๋ ๋ง์ ์ด์ ์ ์ป๋ ์ด์ ๋ฅผ ๊ณต์ ํ ์ ์์ต๋๊น?
๋ด๊ฐ ์ด๋ฏธ ์๊ฐํ ์ ์๋ ํ ๊ฐ์ง ์ด์ ์ (py3.6+์ ๊ฒฝ์ฐ) ์ด์ ์ถ๋ ฅ ์์๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ์ ํํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค.
- python ์๋ณ์๊ฐ ์๋ ์ถ๋ ฅ ์ด๋ฆ์ ์ฒ ์๋ ๋ณด๊ธฐ ํํฉ๋๋ค:
.agg(**{'output name': (col, func)})
๊ทธ๋ฐ ์ ์์ ์๋ ๋ฐฉ์์ด ๋ ์ข์๋ค. ๊ทธ๋ฌ๋ ์์ ๋งํ๋ฏ์ด **{...}
๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ์ง๊ณ๋ฅผ ๋์ ์ผ๋ก ๊ตฌ์ถํ ์๋ง ์๋ค๋ฉด ์ถฉ๋ถํ ๋ง์กฑํ ๊ฒ์
๋๋ค.
- Python 3.6+ ์ ์ฉ์ด๊ฑฐ๋
**kwargs
์ ์์๊ฐ ์ด์ ์ ๋ณด์กด๋์ง ์์๊ธฐ ๋๋ฌธ์ 3.5 ๋ฐ ์ด์ ๋ฒ์ ์ ๋ํด ์ถ์ ํ ํดํน์ด ํ์ํฉ๋๋ค.
์ด์ ์๋ ์ด๋ป๊ฒ ์๋ํ์ต๋๊น(๊ธฐ์กด dict-of-dict ๊ธฐ๋ฅ)? ์ด๋ค ์ ์ผ๋ก๋ ์ฃผ๋ฌธ์ด ๋ณด์ฅ ๋์์ต๋๊น?
- aggfunc๋ ๋จํญ ํจ์์ฌ์ผ ํฉ๋๋ค. ์ฌ์ฉ์ ์ ์ aggfunc์ ์ถ๊ฐ ์ธ์๊ฐ ํ์ํ ๊ฒฝ์ฐ ๋จผ์ ๋ถ๋ถ์ ์ผ๋ก ์ ์ฉํด์ผ ํฉ๋๋ค.
๋ด ์ดํด๋ฅผ ํ์ธํ๊ธฐ ์ํด: aggfunc๋ ์ ํจํ ๊ฐ์ ๋ฐํํ๋ ๋ชจ๋ ํธ์ถ ๊ฐ๋ฅ ๊ฐ์ฒด๊ฐ ๋ ์ ์์ต๋๊น? ( 'min'
, 'max'
๋ฑ๊ณผ ๊ฐ์ "์์ฃผ ์ฌ์ฉ๋๋" ๋ฌธ์์ด aggfungs ์ธ์๋). ์ ๊ณผ ๋น๊ตํ์ ๋ ๋ฌ๋ผ์ง ์ ์ด ์๋์? (์ฆ, ๋จํญ ์ ํ์ด ์ด๋ฏธ ์กด์ฌํ์ง ์์์ต๋๊น?)
๊ทธ๋ฆฌ๊ณ ๊ตฌํ ์ธ๋ถ ์ฌํญ์ด ์์ต๋๋ค. ๋์ผํ ์ด์ ๋ํ ์ฌ๋ฌ
lambda
aggfuncs๋ ์์ง ์ง์๋์ง ์์ง๋ง ๋์ค์ ์์ ํ ์ ์์ต๋๋ค.
์, ๊ทธ๊ฒ์ ์ผ์ข ์ ์ฑ๊ฐ์ ์ผ์ด์ง๋ง ์ผ์์ ์ธ ์ ํ์ด๊ณ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ด๋ ค ์๋ ํ ์๋ํ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ ๊ฐ์ ํ ๋๋ถ๋ถ์ ์ฌ๋๋ค์ด ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ๋์์ ๋ํ ๋์์ ์ง์งํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ฌ๋๋ค์ ์ด๊ฒ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋ป๊ฒ ์๊ฐํฉ๋๊น?
๊ธ์, ์ด์จ๋ ๋๋ ํ ๋จ๊ณ์์ ์ง๊ณํ๊ณ ์ด๋ฆ์ ๋ฐ๊พธ๋ ๊ฒ์ด ์ ๋ง ์ค์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ด์ ๋์์ด ์ค์ ๋ก ์ต์ ์ด ์๋ ๊ฒฝ์ฐ ์ด ๋์์ด ๊ฐ๋ฅํฉ๋๋ค.
ํน์ ์ฃผ์ฅ๊ณผ ๊ด๋ จํ์ฌ ๊ธฐ์กด ๋ฐฉ์๋ณด๋ค ์ด ์๋ก์ด ๋ฐฉ์์ด ๋ ๋ง์ ์ด์ ์ ์ ๊ณตํ๋ ์ด์ ๋ฅผ ๊ณต์ ํด ์ฃผ์๊ฒ ์ต๋๊น?
๋ด๊ฐ ์๋ชป ๊ธฐ์ตํ๊ณ ์์์ง๋ ๋ชจ๋ฅด์ง๋ง SeriesGroupby.agg์ DataFrameGroupby.agg๋ ์ฌ์ ์ ์ธ๋ถ ํค ์ฌ์ด์ ๋ค๋ฅธ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ์๊ฐํฉ๋๋ค(์ด ์ ํ์ ๋๊น ์๋๋ฉด ์ถ๋ ฅ ์ด๋ฆ ์ง์ ์ ๋๊น?). ์ด ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ฉด ํค์๋๊ฐ ์ถ๋ ฅ ์ด๋ฆ์ ์๋ฏธํ๋๋ก ์ผ๊ด๋๊ฒ ํ ์ ์์ต๋๋ค.
๊ทธ๋ฐ ์ ์์ ์๋ ๋ฐฉ์์ด ๋ ์ข์๋ค.
์ฐจ์ด์ ์ **
๊น? ๊ทธ๋ ์ง ์์ผ๋ฉด ๋์ผํ ์ ํ ์ฌํญ์ด ๊ณต์ ๋๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ด์ ์๋ ์ด๋ป๊ฒ ์๋ํ์ต๋๊น(๊ธฐ์กด dict-of-dict ๊ธฐ๋ฅ)? ์ด๋ค ์ ์ผ๋ก๋ ์ฃผ๋ฌธ์ด ๋ณด์ฅ ๋์์ต๋๊น?
์ง๊ธ PR์์ ํ๊ณ ์๋ ํค ์ ๋ ฌ์ ๋๋ค.
๋ด ์ดํด๋ฅผ ํ์ธํ๊ธฐ ์ํด: aggfunc๋ ์ ํจํ ๊ฐ์ ๋ฐํํ๋ ๋ชจ๋ ํธ์ถ ๊ฐ๋ฅ ๊ฐ์ฒด๊ฐ ๋ ์ ์์ต๋๊น?
์ฐจ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
In [21]: df = pd.DataFrame({"A": ['a', 'a'], 'B': [1, 2], 'C': [3, 4]})
In [22]: def aggfunc(x, myarg=None):
...: print(myarg)
...: return sum(x)
...:
In [23]: df.groupby("A").agg({'B': {'foo': aggfunc}}, myarg='bar')
/Users/taugspurger/sandbox/pandas/pandas/core/groupby/generic.py:1308: FutureWarning: using a dict with renaming is deprecated and will be removed in a future version
return super().aggregate(arg, *args, **kwargs)
None
Out[23]:
B
foo
A
a 3
๋์ ์ ์์ผ๋ก ์ฐ๋ฆฌ๋ ์ถ๋ ฅ ์ด ์ด๋ฆ์ ์ํด **kwargs
๋ฅผ ์์ฝํฉ๋๋ค. ๋ฐ๋ผ์ functools.partitial(aggfunc, myarg='bar')
ํฉ๋๋ค.
๋ค, ๊ฐ์ฌํฉ๋๋ค. ์ ์๋ ์ ๊ทผ ๋ฐฉ์์ ์ฒซ ๋ฒ์งธ ๋ฐ๋ณต์ ๋ํด ๐๋ผ๊ณ ์๊ฐํฉ๋๋ค(๋ค์ค ๋๋ค ๊ตฌํ ์ ํ์ด ์ ๊ฑฐ๋๋ ์ฆ์ ๊ต์ฒด๋ก ๊ด์ฐฎ์ ๊ฒ์ ๋๋ค).
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๊ทธ๋งํ ๊ฐ์น๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๋ ๋ํ ๊ธฐ๋ฅ์ ํ๊ฐ์ ํํ์ง ์๋ ๊ฒ์ ๊ฐ๋ ฅํ๊ฒ ์ฐฌ์ฑํฉ๋๋ค.
๋์๊ฒ ํฐ ์ด์ ๋ ํ์ด์ฌ์ ํจ์ ์ด๋ฆ ๊ณต๊ฐ(ํน์ ๊ตฌํ๊ณผ ๊ด๋ จ๋ ๊ฒ)์ ์ด ์ด๋ฆ๊ณผ ๋ฐ์ดํฐ(๊ตฌํ์ ๋ํด ํ์คํ ์์์ผ ํ๋ ๊ฒ)์ ํผํฉํ๋ ๋ฐ ๋งค์ฐ ์ด์ํ ์ ์ด ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
'<lambda>'
๋ผ๋ ์ด(์ฌ๋ฌ ์ด)์ด ํ์๋๋ค๋ ์ฌ์ค์ ์ฌ๊ฐํ ์ธ์ง ๋ถ์กฐํ๋ฅผ ์ ๋ฐํฉ๋๋ค.๋ถํ์ํ(๋ ธ์ถ๋) ์ด ์ด๋ฆ์ด ์ฌ์ฉ๋๋ ์ค๊ฐ ๋จ๊ณ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ์ ๊ทผ ๋ฐฉ์์ด ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค. ๋ํ ๊ตฌํ์ ์ ์ฌ์ ์ผ๋ก ์ข ์์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ์์ ์ ์ด๊ณ ์ฒด๊ณ์ ์ผ๋ก ์ด๋ฆ์ ๋ฐ๊พธ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค.
๊ทธ ์ธ์๋ ์ค์ฒฉ๋ dict ๊ธฐ๋ฅ์ ํ์คํ ๋ณต์กํ์ง๋ง ์ํ ์ค์ธ ๋ณต์กํ ์์ ์ ๋๋ค.
TL;DR ํ๊ฐ์ ํํ์ง ๋ง์ญ์์ค. :)