【问题标题】:How to use `apply()` or other vectorized approach when previous value matters当先前的值很重要时如何使用`apply()`或其他矢量化方法
【发布时间】:2017-08-16 11:18:48
【问题描述】:

假设我有一个如下形式的 DataFrame,其中第一列是随机数,其他列将基于上一列中的值。

为了便于使用,假设我希望每个数字都是前一个数字的平方。所以它看起来像下面。

我知道我可以编写一个非常简单的循环来执行此操作,但我也知道循环在 python/pandas 中通常不是最有效的。 apply()rolling_apply() 怎么能做到这一点?或者,以其他方式更有效地完成?

我的(失败的)尝试如下:

In [12]: a = pandas.DataFrame({0:[1,2,3,4,5],1:0,2:0,3:0})

In [13]: a
Out[13]: 
   0  1  2  3
0  1  0  0  0
1  2  0  0  0
2  3  0  0  0
3  4  0  0  0
4  5  0  0  0

In [14]: a = a.apply(lambda x: x**2)

In [15]: a
Out[15]: 
    0  1  2  3
0   1  0  0  0
1   4  0  0  0
2   9  0  0  0
3  16  0  0  0
4  25  0  0  0


In [16]: a = pandas.DataFrame({0:[1,2,3,4,5],1:0,2:0,3:0})

In [17]: pandas.rolling_apply(a,1,lambda x: x**2)
C:\WinPython64bit\python-3.5.2.amd64\lib\site-packages\spyderlib\widgets\externalshell\start_ipython_kernel.py:1: FutureWarning: pd.rolling_apply is deprecated for DataFrame and will be removed in a future version, replace with 
        DataFrame.rolling(center=False,window=1).apply(args=<tuple>,kwargs=<dict>,func=<function>)
  # -*- coding: utf-8 -*-
Out[17]: 
      0    1    2    3
0   1.0  0.0  0.0  0.0
1   4.0  0.0  0.0  0.0
2   9.0  0.0  0.0  0.0
3  16.0  0.0  0.0  0.0
4  25.0  0.0  0.0  0.0

In [18]: a = pandas.DataFrame({0:[1,2,3,4,5],1:0,2:0,3:0})

In [19]: a = a[:-1]**2

In [20]: a
Out[20]: 
    0  1  2  3
0   1  0  0  0
1   4  0  0  0
2   9  0  0  0
3  16  0  0  0

In [21]: 

所以,我的问题主要是如何在我的 DataFrame 计算中引用前一列的值。

【问题讨论】:

  • a[1] = a[0].apply(lambda x: x**2) 等等

标签: python python-3.x pandas


【解决方案1】:
a[1] = a[0].apply(lambda x: x**2)
a[2] = a[1].apply(lambda x: x**2)
a[3] = a[2].apply(lambda x: x**2)

会给你

    0   1   2   3
0   1   1   1   1
1   2   4   16  256
2   3   9   81  6561
3   4   16  256 65536
4   5   25  625 390625

【讨论】:

    【解决方案2】:

    不幸的是,据我所知,没有一种没有循环的方法。但是,您不必遍历每个值,只需遍历每一列。您可以在上一列上调用 apply 并将下一列设置为返回值:

    a = pd.DataFrame({0:[1,2,3,4,5],1:0,2:0,3:0})
    
    for i in range(3):
        a[i+1] = a[i].apply(lambda x: x**2)
    

    【讨论】:

    • 我喜欢这个。很好很简单。我之前尝试使用完整循环(以及我的完整数据源,具有附加逻辑并应用 scipy.fv())花了 2 分钟,这种方法用了不到一秒钟。我觉得真的很愚蠢:)。
    【解决方案3】:

    您所描述的是递归关系,我认为目前没有任何非循环方式可以做到这一点。像applyrolling_apply 这样的事情仍然依赖于在开始之前获得所有需要的数据,并在最后一次输出所有结果数据。也就是说,它们不允许您使用同一系列的早期值来计算下一个值。请参阅 this questionthis one 以及 this pandas issue

    实际上,对于您的示例,您只有三列要填写,因此执行三遍循环(如其他一些答案所示)可能不会对性能造成重大影响。

    【讨论】:

    • 感谢您的回复。我的实际 DataFrame 要大得多,并且包含一些逻辑。这只是一个简化的示例,用于从效率的角度了解如何最好地解决这个问题。这些链接很有帮助。
    【解决方案4】:

    在这种特殊情况下,我们知道关于列的这一点

    • 0 将成为 1 的强大力量
    • 1 将是 0 列中的任何内容 2
    • 2 将是 1 列中的任何内容 2...
      • 或将成为0 列中4 的幂
    • 3 将是 2 列中的任何内容 2...
      • 或者将是 1 列中的任何内容到 4 的幂...
      • 或将是0 列中的任何内容8

    所以我们确实可以用矢量化你的例子

    np.power(df.values[:, [0]], np.power(2, np.arange(4)))
    
    array([[     1,      1,      1,      1],
           [     2,      4,     16,    256],
           [     3,      9,     81,   6561],
           [     4,     16,    256,  65536],
           [     5,     25,    625, 390625]])
    

    将其包装在一个漂亮的数据框中

    pd.DataFrame(
        np.power(df.values[:, [0]], np.power(2, np.arange(4))),
        df.index, df.columns)
    
       0   1    2       3
    0  1   1    1       1
    1  2   4   16     256
    2  3   9   81    6561
    3  4  16  256   65536
    4  5  25  625  390625
    

    【讨论】:

    • 我真的很喜欢这种方法,这确实回答了我的问题。但是,我接受了另一个答案,因为我的问题很笼统。我应该想到有一种更专业的方法来解决我的具体问题。
    • @Kyle 几点。一:知道如果您发现它比当前接受的答案更合适,您可以接受另一个答案。事实上,这是鼓励的。我的答案中有很多未被接受,我对此表示满意。二:有时更难的问题需要更多的时间来回答。如果您想对具有挑战性的问题提供高质量的答案,请耐心等待。没有必要急于接受答案。您也可以对那些已经回答您正在等待的人直言不讳。三:我不太关心 15 个代表点,更关心什么是正确的。
    • @Kyle 说了这么多。我完全同意 Kewl 的回答。其他人会在他们认为合适的时候投票赞成或反对。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 2020-01-12
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 2016-03-27
    • 2019-03-13
    相关资源
    最近更新 更多