尝试从groupby
计算中创建一个新列。在下面的代码中,我得到了每个日期的正确计算值(参见下面的组),但当我尝试创建新列时(df['Data4']
)我得到了NaN,所以我尝试在 Dataframe 中创建一个新列,所有日期的总和为Data3
,并将其应用于每一个日期行。2015 - 05 - 08分2行(总计为50 + 5 = 55),在这个新列中,我希望两行均为55。
import pandas as pd
df = pd.DataFrame({
'Date' : ['2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05', '2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05'],
'Sym' : ['aapl', 'aapl', 'aapl', 'aapl', 'aaww', 'aaww', 'aaww', 'aaww'],
'Data2': [11, 8, 10, 15, 110, 60, 100, 40],
'Data3': [5, 8, 6, 1, 50, 100, 60, 120]
})
group = df['Data3'].groupby(df['Date']).sum()
df['Data4'] = group
group
:
Date
2015-05-05 121
2015-05-06 66
2015-05-07 108
2015-05-08 55
Name: Data3, dtype: int64
结束时df
:
Date Sym Data2 Data3 Data4
0 2015-05-08 aapl 11 5 NaN
1 2015-05-07 aapl 8 8 NaN
2 2015-05-06 aapl 10 6 NaN
3 2015-05-05 aapl 15 1 NaN
4 2015-05-08 aaww 110 50 NaN
5 2015-05-07 aaww 60 100 NaN
6 2015-05-06 aaww 100 60 NaN
7 2015-05-05 aaww 40 120 NaN
4条答案
按热度按时间zvms9eto1#
你需要使用
transform
,这将返回一个Series,其索引与df对齐,这样你就可以将其添加为一个新列:xtupzzrd2#
有两种方法--一种直截了当,另一种稍微有趣一些。
每个人的最爱:
GroupBy.transform()
与'sum'
@EdChum的答案可以简化一点。调用
DataFrame.groupby
而不是Series.groupby
。这会使语法更简单。它稍微快一点,
与众不同,但值得考虑:
GroupBy.sum()
+Series.map()
我在API中偶然发现了一个有趣的特性。据我所知,你可以在0.20以上的任何主要版本上重现这个特性(我在0.23和0.24上测试了这个特性)。如果你使用
GroupBy
的直接函数并使用map
广播它,你似乎可以始终如一地节省transform
所花费的几毫秒的时间:与上面
df.groupby('Date')['Data3'].transform('sum')
的输出进行比较:它们是一样的。我的测试表明,如果你能负担得起直接使用
GroupBy
函数(如mean
,min
,max
,first
等),map
会更快一些。在大多数情况下,大约20万条记录的速度会更快一些。之后,性能就取决于数据了。(Left:版本0.23,右侧:第0.24版)
这是一个很好的替代方案,如果你有更小的框架和更少的组,效果会更好,但我会推荐
transform
作为首选。认为无论如何这是值得分享的。对标代码,供参考:
mpbci0fu3#
一般来说,我建议使用功能更强大的
apply
,使用它可以在单个表达式中编写查询,甚至可以用于更复杂的用途,例如定义一个新列,该列的值被定义为对组的操作,并且在同一个组中也可以有不同的值!这比为每个组定义一个具有相同值的列的简单情况(如本问题中的
sum
,它随组而变化,但在同一组中相同)更通用。简单大小写(组内具有相同值的新列,组间具有不同值):
输出:
| | 日期|塞姆|数据2|数据3|数据4|
| - ------|- ------|- ------|- ------|- ------|- ------|
| 三个|2015年5月5日|阿普尔|十五|1个|一百二十一|
| 七|2015年5月5日|奥维|四十|一百二十|一百二十一|
| 第二章|2015年5月6日|阿普尔|十个|六个|六十六|
| 六个|2015年5月6日|奥维|一百|六十|六十六|
| 1个|2015年5月7日|阿普尔|八个|八个|一百零八|
| 五个|2015年5月7日|奥维|六十|一百|一百零八|
| 无|2015年5月8日|阿普尔|十一|五个|五十五|
| 四个|2015年5月8日|奥维|一百一十|五十|五十五|
它的强大之处在于它充分利用了"拆分-应用-组合"范式的全部力量。它允许您从"将 Dataframe 拆分为块"和"在这些块上运行任意操作"的Angular 进行思考,而无需减少/聚合,即无需减少行数。(而且不需要编写显式的冗长循环,也不需要使用代价高昂的连接或串联来将结果粘回去。)
让我们考虑一个更复杂的例子。在这个例子中,您的 Dataframe 中有多个时间序列的数据。您有一列表示一种产品,一列有时间戳,还有一列包含该产品在一年中某个时间的销售数量。您希望按产品分组并获得一个新列。它包含每个类别中销售的商品的累计总数。我们需要一个列,该列在具有相同产品的每个"块"内仍然是时间序列,并且单调递增(仅在块内)。
我们如何才能做到这一点?用
groupby
+apply
!输出:
| | 日期|产品|售出商品|累计销售项目|
| - ------|- ------|- ------|- ------|- ------|
| 无|2021年3月11日|衬衫|三百|三百|
| 1个|2021年3月12日|衬衫|四百|七百|
| 第二章|2021年3月13日|衬衫|二百三十四|九三四|
| 三个|2021年3月11日|鞋|八十|八十|
| 四个|2021年3月12日|鞋|十个|九十|
| 五个|2021年3月13日|鞋|一百二十|二一零|
此方法的另一个优点是:即使必须按多个字段分组,此方法也有效!例如,如果我们的产品有一个
'Color'
字段,并且我们希望按(Product, Color)
分组累积系列,则可以:输出:
| | 日期|产品|颜色|售出商品|累计销售项目|
| - ------|- ------|- ------|- ------|- ------|- ------|
| 六个|2021年3月11日|衬衫|蓝色|一百二十三|一百二十三|
| 七|2021年3月12日|衬衫|蓝色|八十四|二百零七|
| 八个|2021年3月13日|衬衫|蓝色|九二三|小行星|
| 无|2021年3月11日|衬衫|黄色|三百|三百|
| 1个|2021年3月12日|衬衫|黄色|四百|七百|
| 第二章|2021年3月13日|衬衫|黄色|二百三十四|九三四|
| 九|2021年3月11日|鞋|蓝色|无|无|
| 十个|2021年3月12日|鞋|蓝色|二百二十|二百二十|
| 十一|2021年3月13日|鞋|蓝色|九十四|三一四|
| 三个|2021年3月11日|鞋|黄色|八十|八十|
| 四个|2021年3月12日|鞋|黄色|十个|九十|
| 五个|2021年3月13日|鞋|黄色|一百二十|二一零|
groupby
的参数始终放在列表中的原因,即使它是一个名称,如上一个示例中的'Product'。)*你可以在一个表达式中综合地完成所有这些(当然,如果python的lambdas看起来更漂亮一点,它会看起来更漂亮)。
为什么我要过一遍一般的案例呢?因为这是在谷歌上搜索"Pandas新专栏groupby"之类的东西时弹出的第一个so问题之一。
关于此类操作的API的其他想法
基于对组进行的任意计算添加列很像defining new column using aggregations over Windows in SparkSQL的好习惯用法。
例如,你可以这样想(这是Scala代码,但PySpark中的等价代码看起来几乎是一样的):
就像这样(用我们上面看到的Pandas的例子):
(注意Spark的例子是多么的合成和漂亮。panda的等价物看起来有点笨拙。panda的API并没有使编写这种"流畅"的操作变得容易。)
这个习惯用法又来自SQL's Window Functions,PostgreSQL文档对此给出了一个非常好的定义:(着重号是我的)
窗口函数对一组表行执行计算**,这些表行以某种方式与当前行相关**。这与聚合函数可以完成的计算类型类似。但与常规聚合函数不同,使用窗口函数不会导致行被分组到单个输出行中-这些行保留其单独的标识。在幕后,窗口函数能够访问的不仅仅是查询结果的当前行。
并给出了一个漂亮的SQL一行代码示例:(组内排名)
| 部门名称|恩普诺|薪金|秩|
| - ------|- ------|- ------|- ------|
| 发展|八个|六千|1个|
| 发展|十个|小行星5200|第二章|
| 发展|十一|小行星5200|第二章|
| 发展|九|小行星4500|四个|
| 发展|七|小行星4200|五个|
| 人事|第二章|小行星3900|1个|
| 人事|五个|3500|第二章|
| 销售额|1个|五千|1个|
| 销售额|四个|小行星4800|第二章|
| 销售额|三个|小行星4800|第二章|
最后一件事:你可能也会对panda的
pipe
感兴趣,它类似于apply
,但工作原理稍有不同,并且给了内部操作更大的工作范围。7rtdyuoh4#
输出