여러 열을 사용하는 Pandas DataFrame 집계 함수
집계 DataFrame.agg
되는 데이터의 둘 이상의 열에 액세스 할 수 있는 메서드에서 사용되는 집계 함수를 작성하는 방법이 있습니까? 일반적인 사용 사례는 가중 평균, 가중 표준 편차 함수입니다.
다음과 같이 쓸 수 있기를 바랍니다.
def wAvg(c, w):
return ((c * w).sum() / w.sum())
df = DataFrame(....) # df has columns c and w, i want weighted average
# of c using w as weight.
df.aggregate ({"c": wAvg}) # and somehow tell it to use w column as weights ...
예; .apply(...)
각 하위에서 호출되는 함수를 사용하십시오 DataFrame
. 예를 들면 :
grouped = df.groupby(keys)
def wavg(group):
d = group['data']
w = group['weights']
return (d * w).sum() / w.sum()
grouped.apply(wavg)
내 솔루션은 Nathaniel의 솔루션과 유사합니다. 단 하나의 열에 대한 것이며 매번 전체 데이터 프레임을 딥 복사하지 않아 엄청나게 느릴 수 있습니다. groupby (...). apply (...) 솔루션에 비해 성능 향상은 약 100x (!)입니다.
def weighted_average(df, data_col, weight_col, by_col):
df['_data_times_weight'] = df[data_col] * df[weight_col]
df['_weight_where_notnull'] = df[weight_col] * pd.notnull(df[data_col])
g = df.groupby(by_col)
result = g['_data_times_weight'].sum() / g['_weight_where_notnull'].sum()
del df['_data_times_weight'], df['_weight_where_notnull']
return result
다음 (Wes McKinney의 답변에 기반)은 내가 찾던 것을 정확하게 수행합니다. .NET에서이 작업을 수행하는 더 간단한 방법이 있는지 배우게되어 기쁩니다 pandas
.
def wavg_func(datacol, weightscol):
def wavg(group):
dd = group[datacol]
ww = group[weightscol] * 1.0
return (dd * ww).sum() / ww.sum()
return wavg
def df_wavg(df, groupbycol, weightscol):
grouped = df.groupby(groupbycol)
df_ret = grouped.agg({weightscol:sum})
datacols = [cc for cc in df.columns if cc not in [groupbycol, weightscol]]
for dcol in datacols:
try:
wavg_f = wavg_func(dcol, weightscol)
df_ret[dcol] = grouped.apply(wavg_f)
except TypeError: # handle non-numeric columns
df_ret[dcol] = grouped.agg({dcol:min})
return df_ret
이 함수 df_wavg()
는 "groupby"열로 그룹화 된 데이터 프레임을 반환하고 가중치 열에 대한 가중치의 합계를 반환합니다. 다른 열은 가중 평균이거나 숫자가 아닌 경우 min()
함수가 집계에 사용됩니다.
를 사용하여 groupby 개체에서 집계 된 값을 원하는만큼 반환 할 수 apply
있습니다. 간단히 Series를 반환하면 인덱스 값이 새 열 이름이됩니다.
간단한 예를 보겠습니다.
df = pd.DataFrame({'group':['a','a','b','b'],
'd1':[5,10,100,30],
'd2':[7,1,3,20],
'weights':[.2,.8, .4, .6]},
columns=['group', 'd1', 'd2', 'weights'])
df
group d1 d2 weights
0 a 5 7 0.2
1 a 10 1 0.8
2 b 100 3 0.4
3 b 30 20 0.6
Define a custom function that will be passed to apply
. It implicitly accepts a DataFrame - meaning the data
parameter is a DataFrame. Notice how it uses multiple columns, which is not possible with the agg
groupby method:
def weighted_average(data):
d = {}
d['d1_wa'] = np.average(data['d1'], weights=data['weights'])
d['d2_wa'] = np.average(data['d2'], weights=data['weights'])
return pd.Series(d)
Call the groupby apply
method with our custom function:
df.groupby('group').apply(weighted_average)
d1_wa d2_wa
group
a 9.0 2.2
b 58.0 13.2
You can get better performance by precalculating the weighted totals into new DataFrame columns as explained in other answers and avoid using apply
altogether.
I do this a lot and found the following quite handy:
def weighed_average(grp):
return grp._get_numeric_data().multiply(grp['COUNT'], axis=0).sum()/grp['COUNT'].sum()
df.groupby('SOME_COL').apply(weighed_average)
This will compute the weighted average of all the numerical columns in the df
and drop non-numeric ones.
Accomplishing this via groupby(...).apply(...)
is non-performant. Here's a solution that I use all the time (essentially using kalu's logic).
def grouped_weighted_average(self, values, weights, *groupby_args, **groupby_kwargs):
"""
:param values: column(s) to take the average of
:param weights_col: column to weight on
:param group_args: args to pass into groupby (e.g. the level you want to group on)
:param group_kwargs: kwargs to pass into groupby
:return: pandas.Series or pandas.DataFrame
"""
if isinstance(values, str):
values = [values]
ss = []
for value_col in values:
df = self.copy()
prod_name = 'prod_{v}_{w}'.format(v=value_col, w=weights)
weights_name = 'weights_{w}'.format(w=weights)
df[prod_name] = df[value_col] * df[weights]
df[weights_name] = df[weights].where(~df[prod_name].isnull())
df = df.groupby(*groupby_args, **groupby_kwargs).sum()
s = df[prod_name] / df[weights_name]
s.name = value_col
ss.append(s)
df = pd.concat(ss, axis=1) if len(ss) > 1 else ss[0]
return df
pandas.DataFrame.grouped_weighted_average = grouped_weighted_average
'Development Tip' 카테고리의 다른 글
Linux reboot () 시스템 호출의 매직 넘버 (0) | 2020.11.24 |
---|---|
Matplotlib-각 빈에 레이블 지정 (0) | 2020.11.24 |
SQL 분할 값을 여러 행으로 (0) | 2020.11.24 |
sdkman은 어디에 패키지를 설치합니까? (0) | 2020.11.24 |
“ (0) | 2020.11.24 |