pandas的groupby能将DataFrame转换成Series吗?

5

我想使用pandas和statsmodels在数据框的子集上拟合线性模型并返回预测值。但是,我不知道该使用何种正确的pandas习惯用法。以下是我的尝试:

import pandas as pd
import statsmodels.formula.api as sm
import seaborn as sns

tips = sns.load_dataset("tips")
def fit_predict(df):
    m = sm.ols("tip ~ total_bill", df).fit()
    return pd.Series(m.predict(df), index=df.index)
tips["predicted_tip"] = tips.groupby("day").transform(fit_predict)

这会引发以下错误:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-139-b3d2575e2def> in <module>()
----> 1 tips["predicted_tip"] = tips.groupby("day").transform(fit_predict)

/Users/mwaskom/anaconda/lib/python2.7/site-packages/pandas/core/groupby.pyc in transform(self, func, *args, **kwargs)
   3033                     return self._transform_general(func, *args, **kwargs)
   3034         except:
-> 3035             return self._transform_general(func, *args, **kwargs)
   3036 
   3037         # a reduction transform

/Users/mwaskom/anaconda/lib/python2.7/site-packages/pandas/core/groupby.pyc in _transform_general(self, func, *args, **kwargs)
   2988                     group.T.values[:] = res
   2989                 else:
-> 2990                     group.values[:] = res
   2991 
   2992                 applied.append(group)

ValueError: could not broadcast input array from shape (62) into shape (62,6)

错误很有道理,因为我认为 .transform 想要将 DataFrame 映射到 DataFrame。但是有没有一种方法可以在 DataFrame 上执行 groupby 操作,将每个块传递到一个函数中,将其减少为一个 Series(具有相同的索引),然后将生成的 Series 组合成可插入原始 DataFrame 的东西?
1个回答

2
这里的顶部部分是相同的,我只是使用了一个玩具数据集,因为我在防火墙后面。
tips = pd.DataFrame({ 'day':list('MMMFFF'), 'tip':range(6), 
                      'total_bill':[10,40,20,80,50,40] })

def fit_predict(df):
    m = sm.ols("tip ~ total_bill", df).fit()
    return pd.Series(m.predict(df), index=df.index)

如果你将'transform'改为'apply',你将得到:
tips.groupby("day").apply(fit_predict)

day   
F    3    2.923077
     4    4.307692
     5    4.769231
M    0    0.714286
     1    1.357143
     2    0.928571

虽然不完全符合你的要求,但是如果你去掉level=0,就可以按照你想要的方式进行操作:

tips['predicted'] = tips.groupby("day").apply(fit_predict).reset_index(level=0,drop=True)

  day  tip  total_bill  predicted
0   M    0          10   0.714286
1   M    1          40   1.357143
2   M    2          20   0.928571
3   F    3          80   2.923077
4   F    4          50   4.307692
5   F    5          40   4.769231

1
有趣的是,这在 seaborn tips 数据集中无法正常工作,因为涉及到 day 是一种分类对象的错误。我想知道这是否是 pandas 中的一个 bug。 - mwaskom
适用于pandas主分支。Categoricals存在一个bug,即没有连接/合并标志。 - TomAugspurger
很好。@TomAugspurger,你认为这是在Pandas中最惯用的方法吗?如果是的话,我会标记为正确的。 - mwaskom
是的,.transform 应该是正确的方法,但它要求输出具有相同的索引和列。 - TomAugspurger
“ignore_index”和/或“ignore_columns”关键字参数是否是一个合理的建议增强功能?如果您认为这将受到良好的反响,我可以打开一个问题。 - mwaskom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接