【问题标题】:Cumulative sum that resets when turning negative/positive转负/正时重置的累计和
【发布时间】:2022-10-04 21:27:09
【问题描述】:

[在此处输入图像描述]

我正在尝试向我的 polars 数据框添加一列(C 列),该数据框计算数据框的一个列(A 列)的值大于/小于另一列(B 列)的值的次数。一旦值从较小/较大变为较大/较小,累积和应重置并再次从 1/-1 开始计数。

【问题讨论】:

    标签: python-polars


    【解决方案1】:

    数据

    我将更改您提供的示例中的数据。

    df = pl.DataFrame(
        {
            "a": [11, 10, 10, 10, 9, 8, 8, 8, 8, 8, 15, 15, 15],
            "b": [11, 9, 9, 9, 9, 9, 10, 8, 8, 10, 11, 11, 15],
        }
    )
    print(df)
    
    shape: (13, 2)
    ┌─────┬─────┐
    │ a   ┆ b   │
    │ --- ┆ --- │
    │ i64 ┆ i64 │
    ╞═════╪═════╡
    │ 11  ┆ 11  │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 10  ┆ 9   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 10  ┆ 9   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 10  ┆ 9   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 9   ┆ 9   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 8   ┆ 9   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 8   ┆ 10  │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 8   ┆ 8   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 8   ┆ 8   │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 8   ┆ 10  │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 15  ┆ 11  │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 15  ┆ 11  │
    ├╌╌╌╌╌┼╌╌╌╌╌┤
    │ 15  ┆ 15  │
    └─────┴─────┘
    

    注意两列相同的情况。你的帖子没有说明在这些情况下该怎么做,所以我对应该发生的事情做了一些假设。 (您可以调整代码以不同方式处理这些情况。)

    算法

    df = (
        df
        .with_column((pl.col("a") - pl.col("b")).sign().alias("sign_a_minus_b"))
        .with_column(
            pl.when(pl.col("sign_a_minus_b") == 0)
            .then(None)
            .otherwise(pl.col("sign_a_minus_b"))
            .forward_fill()
            .alias("run_type")
        )
        .with_column(
            (pl.col("run_type") != pl.col("run_type").shift_and_fill(1, 0))
            .cumsum()
            .alias("run_id")
        )
        .with_column(pl.col("sign_a_minus_b").cumsum().over("run_id").alias("result"))
    )
    print(df)
    
    shape: (13, 6)
    ┌─────┬─────┬────────────────┬──────────┬────────┬────────┐
    │ a   ┆ b   ┆ sign_a_minus_b ┆ run_type ┆ run_id ┆ result │
    │ --- ┆ --- ┆ ---            ┆ ---      ┆ ---    ┆ ---    │
    │ i64 ┆ i64 ┆ i64            ┆ i64      ┆ u32    ┆ i64    │
    ╞═════╪═════╪════════════════╪══════════╪════════╪════════╡
    │ 11  ┆ 11  ┆ 0              ┆ null     ┆ 1      ┆ 0      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 10  ┆ 9   ┆ 1              ┆ 1        ┆ 2      ┆ 1      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 10  ┆ 9   ┆ 1              ┆ 1        ┆ 2      ┆ 2      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 10  ┆ 9   ┆ 1              ┆ 1        ┆ 2      ┆ 3      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 9   ┆ 9   ┆ 0              ┆ 1        ┆ 2      ┆ 3      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 8   ┆ 9   ┆ -1             ┆ -1       ┆ 3      ┆ -1     │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 8   ┆ 10  ┆ -1             ┆ -1       ┆ 3      ┆ -2     │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 8   ┆ 8   ┆ 0              ┆ -1       ┆ 3      ┆ -2     │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 8   ┆ 8   ┆ 0              ┆ -1       ┆ 3      ┆ -2     │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 8   ┆ 10  ┆ -1             ┆ -1       ┆ 3      ┆ -3     │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 15  ┆ 11  ┆ 1              ┆ 1        ┆ 4      ┆ 1      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 15  ┆ 11  ┆ 1              ┆ 1        ┆ 4      ┆ 2      │
    ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤
    │ 15  ┆ 15  ┆ 0              ┆ 1        ┆ 4      ┆ 2      │
    └─────┴─────┴────────────────┴──────────┴────────┴────────┘
    

    我将中间计算留在了输出中,只是为了展示算法的工作原理。 (你可以放下它们。)

    基本思想是为每次运行正值或负值计算run_id。然后,我们将使用cumsum 函数和over 窗口表达式来创建每个run_id 的正数/负数的运行计数。

    关键假设ab 列中的联系不会中断运行,但它们不会对正/负值运行的总数做出贡献。

    sign_a_minus_b 做了两件事:它识别运行是正还是负,以及列 ab 中是否存在平局。

    run_type 扩展任何运行以包括在列ab 中出现平局的任何情况。列顶部的 null 值是预期的 - 它显示了当第一行出现平局时会发生什么。

    result 是输出列。请注意,已绑定的列不会中断运行,但它们不会对该运行的总数做出贡献。

    最后一点:如果不允许列ab 中的关系,那么这个算法可以被简化......并且运行得更快。

    【讨论】:

      【解决方案2】:

      不是很优雅或 Pythonic,但类似下面的东西应该可以工作:

      import pandas as pd
      
      df = pd.DataFrame({'a': [10, 10, 10, 8, 8, 8, 15, 15]
      ,'b': [9, 9, 9, 9, 10, 10, 11, 11]})
      
      df['c'] = df.apply(lambda row: 1 if row['a'] > row['b'] else 0, axis=1)
      df['d'] = df.apply(lambda row: 0 if row['a'] > row['b'] else -1, axis=1)
      for i in range(1, len(df)):
          if df.loc[i, 'a'] > df.loc[i, 'b']: 
              df.loc[i, 'c'] = df.loc[i-1, 'c']  + 1
              df.loc[i, 'd'] = 0
          else:
              df.loc[i, 'd'] = df.loc[i-1, 'd']  - 1
              df.loc[i, 'c'] = 0
              
      df['ans'] = df['c'] + df['d']
      print(df)
      

      此外,您可能需要考虑当 a 列和 b 列相等时,特定情况下的值应该是多少。

      【讨论】:

      • 非常感谢,但我已经在 pandas 中有一个可行的解决方案,看起来很像上面的那个。不幸的是,它非常缓慢,整个过程需要很长时间,这就是为什么我将整个过程转移到极地并试图摆脱熊猫。所以,我希望其他人能想出一个更优雅的极地版本。
      猜你喜欢
      • 2020-12-25
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多