【问题标题】:How to constuct a column of data frame recursively with pandas-python?如何用pandas-python递归构造一列数据框?
【发布时间】:2016-06-24 07:55:52
【问题描述】:

给这样一个数据框df

id_      val     
11111    12
12003    22
88763    19
43721    77
...

我希望在df 中添加一列diff,它的每一行等于,比如说,该行中的val 减去前一行中的diff,然后乘以0.4,然后加上前一天diff

diff = (val - diff_previousDay) * 0.4 + diff_previousDay

第一行中的diff 等于该行中的val * 4。也就是说,预期的df 应该是:

id_      val     diff   
11111    12      4.8
12003    22      11.68
88763    19      14.608
43721    77      ...

我已经试过了:

mul = 0.4
df['diff'] = df.apply(lambda row: (row['val'] - df.loc[row.name, 'diff']) * mul + df.loc[row.name, 'diff'] if int(row.name) > 0 else row['val'] * mul, axis=1) 

但是得到了这样的错误:

TypeError: ("unsupported operand type(s) for -: 'float' and 'NoneType'", 'occured at index 1')

你知道如何解决这个问题吗?提前谢谢!

【问题讨论】:

  • 您可以使用itertuplesiterrows

标签: python pandas recursion dataframe multiple-columns


【解决方案1】:

你可以使用:

df.loc[0, 'diff'] = df.loc[0, 'val'] * 0.4

for i in range(1, len(df)):
    df.loc[i, 'diff'] = (df.loc[i, 'val'] - df.loc[i-1, 'diff']) * 0.4  + df.loc[i-1, 'diff']

print (df)
     id_  val     diff
0  11111   12   4.8000
1  12003   22  11.6800
2  88763   19  14.6080
3  43721   77  39.5648

输入取决于先前步骤的结果的计算的迭代性质使向量化复杂化。您也许可以将 apply 与执行与循环相同计算的函数一起使用,但在幕后这也将是一个循环。

【讨论】:

  • 我认为这是唯一的解决方案,因为矢量化方法不可用。但令人惊讶的是速度相当快:)
  • 我很抱歉@user5779223,但这并不快!我有一个 1,7 M 行 x 11 列的数据集,我需要在具有大约 80k 个不同值的列上 groupbyapply 这种运行聚合(里面有一点 if)。 cumsumcumcount 分别运行 800 和 300 微秒。应用的回调,在 GroupByDataframe 上执行 iterrows,运行时间为 4 分钟。我目前正在检查numba 是否可以在这里帮助我。
  • @TomaszGandor 晚了一年问,但 numba 对你有用吗?我有 70 M 行,我正在尝试根据递归值和条件语句生成新变量。我想知道如何尽可能加快这个过程。
  • @Turtle - 我不记得它是如何结束的 ;) 今天面对这个问题,我会安装 modin (modin.readthedocs.io/en/latest/using_modin.html) 并检查它是否有帮助。
  • @TomaszGandor 哦,以后的项目可能会看看它。我在大型嵌套循环上尝试了 numba,时间减少到不到常规 python 代码执行时间的一半。虽然将所有输入从 pandas 转换为 numpy 有点烦人
【解决方案2】:

递归函数不容易向量化。但是,您可以使用 numba 优化您的算法。这应该比常规循环更可取。

from numba import jit

@jit(nopython=True)
def foo(val):
    diff = np.zeros(val.shape)
    diff[0] = val[0] * 0.4
    for i in range(1, diff.shape[0]):
        diff[i] = (val[i] - diff[i-1]) * 0.4 + diff[i-1]
    return diff

df['diff'] = foo(df['val'].values)

print(df)

     id_  val     diff
0  11111   12   4.8000
1  12003   22  11.6800
2  88763   19  14.6080
3  43721   77  39.5648

【讨论】:

    【解决方案3】:

    如果您在 pandas 中使用 apply,则不应在 lambda 函数中再次使用数据框。

    在 lambda 函数中的所有情况下,您的对象都应该是“行”。

    【讨论】:

    • 但是如何提取当前行之前的数据
    • 如果axis = 1,则不能申请。每一行都被视为一个独立的数据结构,行的顺序并不重要。如果要提取以前的值,可以使用 .shift() 创建一个新列,然后在新行中应用并在行内减去。
    【解决方案4】:

    我只想为 jezrael 的答案添加另一种选择。我的答案类似,但我发现要快得多:

    def calc_diff(val: pd.Series) -> pd.Series:
        diff = pd.Series(0.0, index=range(len(val)))
        diff[0] = val[0]
        for i in range(1, len(val)):
            result[i] = (val[i] - diff[i-1]) * 0.4 + diff[i-1]
        return result
    df['diff'] = calc_diff(df['val'])
    

    我使用 10,000 行随机数进行了测试,结果是 jezrael 方法的结果是 194ms vs 4s。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2014-07-08
      • 1970-01-01
      • 2018-07-08
      • 2022-08-20
      • 1970-01-01
      相关资源
      最近更新 更多