我想对Python数据框中的每个组应用自定义归约函数。该功能通过执行合并组中几列的操作,将组减少为一行。
我已经这样实现了:
import pandas as pd
import numpy as np
df = pd.DataFrame(data={
"afac": np.random.random(size=1000),
"bfac": np.random.random(size=1000),
"class":np.random.randint(low=0,high=5,size=1000)
})
def f(group):
total_area = group['afac'].sum()
per_area = (group['afac']/total_area).values
per_pop = group['bfac'].values
return pd.DataFrame(data={'per_apop': [np.sum(per_area*per_pop)]})
aggdf = df.groupby('class').apply(f)
我的输入数据框df
如下:
>>> df
afac bfac class
0 0.689969 0.992403 0
1 0.688756 0.728763 1
2 0.086045 0.499061 1
3 0.078453 0.198435 2
4 0.621589 0.812233 4
但是我的代码给出了这个多索引数据框:
>>> aggdf
per_apop
class
0 0 0.553292
1 0 0.503112
2 0 0.444281
3 0 0.517646
4 0 0.503290
我尝试了各种方法来恢复“正常”数据帧,但似乎没有任何作用。
>>> aggdf.reset_index()
class level_1 per_apop
0 0 0 0.553292
1 1 0 0.503112
2 2 0 0.444281
3 3 0 0.517646
4 4 0 0.503290
>>> aggdf.unstack().reset_index()
class per_apop
0
0 0 0.553292
1 1 0.503112
2 2 0.444281
3 3 0.517646
4 4 0.503290
如何执行此操作并在之后获得正常的数据帧?
更新:输出数据框应具有class
和per_apop
的列。理想情况下,函数f
可以返回多列,也可能返回多行。也许使用
return pd.DataFrame(data={'per_apop': [np.sum(per_area*per_pop),2], 'sue':[1,3]})
答案 0 :(得分:1)
使自定义函数返回Series
def f(group):
total_area = group['afac'].sum()
per_area = (group['afac']/total_area).values
per_pop = group['bfac'].values
return pd.Series(data={'per_apop': np.sum(per_area*per_pop)})
df.groupby('class').apply(f).reset_index()
class per_apop
0 0 0.508332
1 1 0.505593
2 2 0.488117
3 3 0.481572
4 4 0.500401
答案 1 :(得分:1)
您可以使用reset_index
选择要重置的级别以及是否要保留索引。在您的情况下,您最终得到了一个具有2个级别的多索引:class
和一个未命名的索引。 reset_index
允许您重置整个索引(默认)或仅重置所需的级别。在以下示例中, last 级别(-1)被拉出索引。通过同时使用drop=True
,它会被删除,而不是作为一列添加到数据框中。
aggdf.reset_index(level=-1, drop=True)
per_apop
class
0 0.476184
1 0.476254
2 0.509735
3 0.502444
4 0.525287
要将索引的class
级别推回到数据帧,只需再次调用.reset_index()
。丑陋,但行得通。
aggdf.reset_index(level=-1, drop=True).reset_index()
class per_apop
0 0 0.515733
1 1 0.497349
2 2 0.527063
3 3 0.515476
4 4 0.494530
或者,您也可以重置索引,然后删除多余的列。
aggdf.reset_index().drop('level_1', axis=1)
class per_apop
0 0 0.515733
1 1 0.497349
2 2 0.527063
3 3 0.515476
4 4 0.494530
答案 2 :(得分:0)
虽然你有一个很好的答案,但一个建议:
在第一组中测试 func
的 df.groupby(...).apply( func )
,如下所示:
agroupby = df.groupby(...)
for key, groupdf in agroupby: # an iterator -> (key, groupdf) ... pairs
break # get the first pair
print( "\n-- first groupdf: len %d type %s \n%s" % (
len(groupdf), type(groupdf), groupdf )) # DataFrame
test = myfunc( groupdf )
# groupdf .col [col] [[col ...]] .set_index .resample ... as usual