微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

根据特定的 cumsum 值拆分数据帧 输出字典

如何解决根据特定的 cumsum 值拆分数据帧 输出字典

我有一个有效的解决方案,但它看起来很麻烦,我想知道是否有更好的方法来实现我想要的。我需要实现两件事:

  1. 根据特定的 cumsum 值将数据帧拆分为两个数据帧。
  2. 如果需要拆分一行来满足 cumsum 条件,则必须发生这种情况。

一个例子说一千个字;我有以下数据框:

import pandas as pd

max_order_value = 2500
df = pd.DataFrame({'Age': [30,20,22,40,32,28,39],'vol': [165,70,120,80,180,172,150],'price': [4.6,8.3,9.0,3.3,1.8,9.5,2.2],},index=['A','B','C','D','E','F','G']
                  )
df["eurvol"] = df.vol * df.price
df["eurvol_cs"] = df.eurvol.cumsum()
df["prev_cs"] = df["eurvol_cs"].shift(fill_value=0)
print(df)

请注意,最后三列不在我的原始数据框中,我需要计算它们。

   Age  vol  price  eurvol  eurvol_cs  prev_cs
A   30  165    4.6   759.0      759.0      0.0
B   20   70    8.3   581.0     1340.0    759.0
C   22  120    9.0  1080.0     2420.0   1340.0
D   40   80    3.3   264.0     2684.0   2420.0
E   32  180    1.8   324.0     3008.0   2684.0
F   28  172    9.5  1634.0     4642.0   3008.0
G   39  150    2.2   330.0     4972.0   4642.0

现在,我需要将它们分成基本上两个数据帧。 df1 将保存所有行,直到 eurvol_cs 列(欧元交易量总和)等于 2500 (max_order_value)。另一个数据框 df2 将保存之后的所有行。请注意,在这种情况下,这意味着 D 行将部分在 df1 中,部分在 df2 中。

我从 df2 开始:

#create new df with only remaining orders
df2 = df[df["eurvol_cs"] > max_order_value].copy()

#make sure we save the price of the last order (D) and calculate how much of the volume we have used
used_volume_of_last_row = ((max_order_value-df2["prev_cs"].iloc[0]) / df2["price"].iloc[0])

#Recalculate the new volume,eurvol for (D) and new cumsum for the df
df2["vol"].iloc[0] = df2["vol"].iloc[0] - used_volume_of_last_row
df2["eurvol"].iloc[0] = df2["vol"].iloc[0] * df2["price"].iloc[0]
df2["eurvol_cs"] = df2["eurvol"].cumsum()
print(df2.head())
#    Age         vol  price  eurvol  eurvol_cs  prev_cs
# D   40   55.757576    3.3   184.0      184.0   2420.0
# E   32  180.000000    1.8   324.0      508.0   2684.0
# F   28  172.000000    9.5  1634.0     2142.0   3008.0
# G   39  150.000000    2.2   330.0     2472.0   4642.0

到目前为止一切顺利,但有点难看,尤其是因为我必须重新计算第一行 (D) 的特定字段。

前往df1

df1 = df[df["prev_cs"] < 2500].copy()
df1["vol"].iloc[-1] = used_volume_of_last_row
df1["eurvol"] = df1["vol"] * df1["price"]
df1["eurvol_cs"] = df1["eurvol"].cumsum()
print(df1.head())
#    Age         vol  price  eurvol  eurvol_cs  prev_cs
# A   30  165.000000    4.6   759.0      759.0      0.0
# B   20   70.000000    8.3   581.0     1340.0    759.0
# C   22  120.000000    9.0  1080.0     2420.0   1340.0
# D   40   24.242424    3.3    80.0     2500.0   2420.0

#df_first_order is Now correct,so we can calculate average price:
avg_price = max_order_value/df1["vol"].sum()
print(avg_price)
# 6.592089492608869

如您所见,总的来说它有效。但是,这超过了 15 个 LoC。我希望有人可以阐明如何以不同的方式完成这项工作。请注意,整个代码块执行了数百万次(它是在另一个数据帧上 apply-ed 的函数的一部分)。因此,性能很重要,但不是非常重要。我只是觉得我做的事情不对。

编辑: 在它上面睡了一晚后,我想它可能不完全清楚我想要什么。我希望我的原始数据框(年龄、卷、价格)像这样分割:

数据帧 1:

    Age         vol  price  eurvol  
 A   30  165.000000    4.6   759.0 
 B   20   70.000000    8.3   581.0
 C   22  120.000000    9.0  1080.0
 D   40   24.242424    3.3    80.0

数据框 2:

    Age         vol  price  eurvol  
 D   40   55.757576    3.3   184.0
 E   32  180.000000    1.8   324.0
 F   28  172.000000    9.5  1634.0
 G   39  150.000000    2.2   330.0

生成的数据帧中本身不需要列 eurvol_csprev_cs,但也不需要删除它们。

解决方法

  • 计算你记下的列
  • 找到 cumsum() 高于幻数 2500
  • 在该行使 vol 成为一个 list,这是将 cumsum() 限制为幻数的拆分
  • 使用 explode() 将列表重新展开
  • 再次计算导出的数字并重新使用拆分列来确定它是哪个目标 DF
  • 最终生成目标 DF 作为 dict
df = pd.DataFrame({'Age': [30,20,22,40,32,28,39],'vol': [165,70,120,80,180,172,150],'price': [4.6,8.3,9.0,3.3,1.8,9.5,2.2],},index=['A','B','C','D','E','F','G']
                  )
magicv = 2500

df = (df.assign(eurvol=df.vol*df.price,eurvol_cs=lambda dfa: dfa.eurvol.cumsum(),# find row where cumsum goes above magic number
         split=lambda dfa: dfa.eurvol_cs.gt(magicv) & dfa.eurvol_cs.shift().lt(magicv),# split vol on row where it goes above magic number into a list
          vol=lambda dfa: np.where(dfa.split,dfa.apply(lambda r: [r.vol-((r.eurvol_cs-magicv)/r.price),(r.eurvol_cs-magicv)/r.price],axis=1),dfa.vol),)
 # explode list
 .explode("vol")
 # recalc and group DF
 .assign(eurvol=lambda dfa: dfa.vol*dfa.price,split=lambda dfa: dfa.eurvol.cumsum().gt(magicv),)
 .drop(columns="eurvol_cs")
)

# finally a dict of multiple dataframes
dfs = {f"df_{i+1}":df.loc[df.split.eq(v),[c for c in df.columns if c!="split"]] for i,v in enumerate(df.split.unique())}

输出字典

{'df_1':    Age        vol  price  eurvol
 A   30        165    4.6   759.0
 B   20         70    8.3   581.0
 C   22        120    9.0  1080.0
 D   40  24.242424    3.3    80.0,'df_2':    Age        vol  price  eurvol
 D   40  55.757576    3.3   184.0
 E   32        180    1.8   324.0
 F   28        172    9.5  1634.0
 G   39        150    2.2   330.0}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。