【问题标题】:Python Pandas - 3D dataframe cumsum based on criteria across multiple columns until value is reachedPython Pandas - 基于多列标准的 3D 数据帧 cumsum 直到达到值
【发布时间】:2019-03-19 20:25:56
【问题描述】:

我在 python 中获取 3D 数据帧的累积总和时遇到了一些困难。

我创建的示例数据框是:

import numpy as np
import pandas as pd

arr=np.array([[[23, 10],
        [ 24,  5],
        [ 28,  8],
        [ 30, 11],
        [ 31, 1]],

       [[20, 11],
        [21, 3],
        [22, 5],
        [29, 15],
        [30, 10]],

       [[22, 26],
        [23, 29],
        [25, 32],
        [33, 10],
        [34, 15]]])

names = ['x', 'y', 'z']
index = pd.MultiIndex.from_product([range(s)for s in arr.shape], names=names)
df = pd.DataFrame({'Day': arr.flatten()}, index=index)['Day']
df = df.unstack(level='z')
df.columns = ['Price', 'Qty']
df.index.names = ['DATE', 'i']

在指定的日期范围内,如果价格低于某个值(x),我想找到商品数量的总和。但是当总和超过某个数字(y)时,我会停止,无论其他商店或以后的日期是否还有满足最低要求。价格标准。我会先从最早的日期开始总结,在每个日期,从最低的价格开始总结。然后我会找到直到停止点的加权平均价格。

在上面的数据框中,假设我的标准是(1)日期 0 和 1,(2)价格等于或低于 25,(3)当数量总和首次超过 20 时停止。在这种情况下,相关数据是日期 0 的价格 23 和 24,日期 1 的价格 20。这是因为日期 0 的价格 23 和 24 的数量之和为 15,因此小于 20,但加上日期 1 的价格 20 的数量,则cumsum 变为 26,因此该过程停止。因此加权平均值为 (23*10)+(24*5)+(20*5) / 20

我目前的方法太麻烦了,使用while循环遍历时间轴,并为每个日期使用另一个while循环,这样如果价格比我的标准便宜,我会将数量和价格加权数量添加到跟踪总和。当跟踪总和大于指定值时,我将停止该过程并计算加权平均值。然后我也可以返回进程停止的位置。

想就如何以更有效的方式实现这一目标获得一些建议吗?

【问题讨论】:

  • 很容易完成,但如果您提供具有所需输出的MCVE,您将更有可能得到答案
  • 嗨乔希,谢谢你的建议。我现在已经包含了一个代码来生成一个示例数据框并将一些数字放在所需的输出上。希望这将有助于使事情更清楚?

标签: python pandas cumsum


【解决方案1】:

这是一个执行此操作的自定义函数,只需像示例中一样插入您的变量。

def weighted_average(df, dates, price_limit, stop_sum):
    # filter multiindex for your dates, plus price_limits
    tmp = df.loc[dates].loc[df['Price'] <= price_limit]
    # find index of halting cumsum condition, take tmp until there
    tmp = tmp.loc[:(tmp['Qty'].cumsum() > stop_sum).idxmax()]
    # update last value
    tmp.iat[-1, df.columns.get_loc('Qty')] -= tmp['Qty']sum() - stop_sum
    # return the weighted average
    return tmp.product(axis=1).sum() / stop_sum

dates = [0, 1]
price_limit = 25
stop_sum = 20

weighted_average(df, dates, price_limit, stop_sum)
> 22.5

过滤器 (tmp = df.loc[dates].loc[df['Price'] &lt;= price_limit]) 的替代方案(对于大型数据集可能性能更高)是

tmp = df[(df.index.get_level_values(0).isin(dates)) & (df['Price'] <= price_limit)]

【讨论】:

  • 嗨乔希,非常感谢你!它肯定让我几乎到达了我想要的地方。为了回答你的问题,我除以 20 的原因是因为我的 stop_sum 是 20,因此对于日期 1 价格 20,我只计算 5 作为数量而不是 df 中指示的 11,因此我的加权平均值包括 20*5 20*11,然后我将整个加权总和除以 20。我将尝试使用您提供的代码来达到该状态,但感谢您提供了一个良好的起点!
  • 好的,知道了。因此,在这种情况下,您可以只除以 stop_sum,因为您总会达到它。相应地进行了编辑。很高兴它有帮助!
  • 谢谢乔希。我仍然需要更改的是将最后一个数量(价格为 20 的数量)替换为 5 而不是原来的 11。这样加权平均值将被正确计算。我正在考虑这样做:tmp['Qty'][-1]= stop_sum - tmp['Qty'][:-1].sum()您认为这是否足够有效或有更好的方法来做到这一点?
  • 最好使用iloc(或iat ,专门用于标量)来更改值,我已经编辑了代码以显示这一点
  • 嗨,乔希,再次感谢您。我意识到有时 stop_sum 大于 ['Qty'] 列的 cumsum。在这些情况下,我只会得到最后一行。但是,tmp = tmp.loc[:(tmp['Qty'].cumsum() &gt; stop_sum).idxmax()] 当前返回第一行。您能否建议我如何在这种特殊情况下获取最后一行 ID,但继续使用 tmp = tmp.loc[:(tmp['Qty'].cumsum() &gt; stop_sum).idxmax()] 其余部分?
猜你喜欢
  • 1970-01-01
  • 2015-01-23
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-11
相关资源
最近更新 更多