Python 基于指定的累积和值拆分数据帧

Python 基于指定的累积和值拆分数据帧,python,pandas,dataframe,cumsum,Python,Pandas,Dataframe,Cumsum,我有一个可行的解决方案,但它似乎很麻烦,我想知道是否有更好的方法来实现我想要的。我需要实现两件事: 根据指定的累计值将数据帧拆分为两个数据帧 如果需要拆分一行以满足求和条件,则必须执行此操作 以身作则,千言万语;我有以下数据帧: import pandas as pd max_order_value = 2500 df = pd.DataFrame({'Age': [30, 20, 22, 40, 32, 28, 39], 'vol': [165, 70,

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

  • 根据指定的累计值将数据帧拆分为两个数据帧
  • 如果需要拆分一行以满足求和条件,则必须执行此操作
  • 以身作则,千言万语;我有以下数据帧:

    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\u cs
    (euro volume cumsum)等于2500(
    max\u order\u 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
    
    数据帧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\u cs
    prev\u cs
    ,但也不需要删除它们。

    • 计算您记录的列
    • 查找
      cumsum()
      位于幻数2500上方的行
    • 在该行上,生成vola
      list
      ,这是将一个累积数()限制为幻数的拆分
    • 使用
      explode()
    • 再次计算得出的数字,并重新使用拆分列来确定它是哪个目标DF
    • 最终将目标DFs生成为
      dict
    输出指令
    我需要一分钟的时间来处理这个问题,但它看起来非常整洁!我读得越多,就越喜欢它。上一条语句让我有点不知所措,因为我认为拆分列只适用于拆分的行,但后来我看到该列被重新定义。谢谢分享!当我对某件事的意思理解过度时,我总是发表评论。。。。很高兴有帮助
        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
    
    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}