【问题标题】:Pandas dataframe vectorized bucketing/aggregation?熊猫数据框矢量化分桶/聚合?
【发布时间】:2021-09-16 02:34:23
【问题描述】:

任务

我有一个如下所示的数据框:

date money_spent ($) meals_eaten weight
2021-01-01 10:00:00 350 5 140
2021-01-02 18:00:00 250 2 170
2021-01-03 12:10:00 200 3 160
2021-01-04 19:40:00 100 1 150

我想对此进行离散化,以便它每隔$X“切割”行。我想知道一些关于我花费的每一个$X 完成了多少的统计数据。

因此,如果我使用$500 作为阈值,前两行将落在第一次剪切中,我可以将剩余的列聚合如下:

  • 剪辑的第一个date
  • 平均meals_eaten
  • 最小weight
  • 最大weight

所以最终表格将是这样的两行:

date cumulative_spent ($) meals_eaten min_weight max_weight
2021-01-01 10:00:00 600 3.5 140 170
2021-01-03 12:10:00 300 2 150 160

我的方法:

我的第一个直觉是计算money_spent的cumsum()(假设数据是按日期排序的),然后我用pd.cut()基本上新建一个列,我们称之为spent_bin,这决定了每一行的 bin。

注意:在这个玩具示例中,spent_bin 基本上是:[0,500] 用于前两行,(500-1000] 用于后两行。

那么就比较简单了,我做一个groupbyspent_bin然后聚合如下:

.agg({
    'date':'first', 
    'meals_eaten':'mean', 
    'returns': ['min', 'max']
})

我的尝试

import pandas as pd


rows = [
{"date":"2021-01-01 10:00:00","money_spent":350, "meals_eaten":5, "weight":140},
{"date":"2021-01-02 18:00:00","money_spent":250, "meals_eaten":2, "weight":170},
{"date":"2021-01-03 12:10:00","money_spent":200, "meals_eaten":3, "weight":160},
{"date":"2021-01-05 22:07:00","money_spent":100, "meals_eaten":1, "weight":150}]

df = pd.DataFrame.from_dict(rows)
df['date'] = pd.to_datetime(df.date)
df['cum_spent'] = df.money_spent.cumsum()

print(df)
print(pd.cut(df.cum_spent, 500))

由于某种原因,我无法让cut 步骤工作。这是我上面的toy code。由于某种原因,标签不干净[0-500], (500,1000]。老实说,我会满足于[350,500],(500-800](这是削减边缘的实际 cum sum 值),但即使我正在做与文档示例完全相同的操作,我什至无法让它工作.有什么帮助吗?


注意事项和困难:

当然,在 for 循环中编写它很容易,只需执行 while cum_spent < 500:。问题是我的实际数据集中有数百万行,目前以这种方式处理单个 df 需要 20 分钟。

还有一个小问题,有时行会打破间隔。发生这种情况时,我希望包括最后一行。这个问题出现在玩具示例中,其中第 2 行实际上以 600 美元而不是 500 美元结束。但它是结束于或超过 500 美元的第一行,所以我将它包含在第一个 bin 中。

【问题讨论】:

    标签: python pandas dataframe vectorization


    【解决方案1】:

    实现cumsum带复位限制的自定义函数

    df['new'] = cumli(df['money_spent ($)'].values,500)
    out = df.groupby(df.new.iloc[::-1].cumsum()).agg(
        date = ('date','first'),
        meals_eaten = ('meals_eaten','mean'),
        min_weight = ('weight','min'),
        max_weight = ('weight','max')).sort_index(ascending=False)
    Out[81]: 
                date  meals_eaten  min_weight  max_weight
    new                                                  
    1    2021-01-01           3.5         140         170
    0    2021-01-03           2.0         150         160
    

    from numba import njit
    @njit
    def cumli(x, lim):
        total = 0
        result = []
        for i, y in enumerate(x):
            check = 0
            total += y
            if total >= lim:
                total = 0
                check = 1
            result.append(check)
        return result
    

    【讨论】:

    • 所以理想情况下我想使用 pandas 的内置代码,但如果 @njit(我刚刚了解到)可以矢量化分桶,我将使用它。我只是无法让它在我的真实代码库中工作。你介意解释一下cumli 函数吗?
    • @QuantumHoneybees 将值相加直到达到限制,总数将重置为 0 并重新开始计数
    • 好的,这将我的运行时间从 26 分钟减少到 6 秒。惊人。我认为其中很大一部分是因为njit。很好的学习资源
    • @QuantumHoneybees yelp,通常 numba 比简单的 python 解决方案更快,pandas 应用
    猜你喜欢
    • 2018-02-05
    • 2022-01-21
    • 2016-06-24
    • 2018-09-20
    • 2015-05-07
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多