寻找一种有效的迭代方式

looking for an efficient way to iterate

我可以征求建议以更有效(更快)地迭代吗? 这是问题所在,我正在寻找一种在确定的 window 大小内在 pandas DataFrame 中向下传播零的方法:

import numpy as np
import pandas as pd

A = np.matrix([[ 0.,  1.,  1.,  1.,  1.],
           [ 1.,  0.,  1.,  1.,  1.],
           [ 1.,  1.,  0.,  1.,  1.],
           [ 1.,  1.,  1.,  0.,  1.],
           [ 1.,  1.,  1.,  1.,  0.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  0.],
           [ 1.,  1.,  0.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  0.,  1.],
           [ 1.,  1.,  1.,  1.,  1.],
           [ 1.,  1.,  0.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  0.],
           [ 1.,  0.,  1.,  1.,  1.],
           [ 1.,  1.,  1.,  1.,  1.]])

df = pd.DataFrame(A)

现在我们要以 windows 的增量填充,每行 3 行 顶部的值。 3 行中的每行 window 从 window_start 开始,定义为:

window_size = 3
window_start = [i for i in range(0, df.shape[0]) 
                if i % window_size == 0]
print(df)
gf = df.copy()
print('\n')

现在制作零从 window:

上面的前几行
for i in window_start:
for j in range(1, window_size):
    try: gf.iloc[i + j] = gf.iloc[i + j - 1] * gf.iloc[i + j]
    except: pass

print(gf)

最后一点对于非常大的数据集来说效率很低而且很耗时,有更好的方法吗?

您应该能够在 groupby.

内使用累计产品完成此任务
df.groupby(np.arange(len(df)) // 3).cumprod()

      0    1    2    3    4
0   0.0  1.0  1.0  1.0  1.0
1   0.0  0.0  1.0  1.0  1.0
2   0.0  0.0  0.0  1.0  1.0
3   1.0  1.0  1.0  0.0  1.0
4   1.0  1.0  1.0  0.0  0.0
5   1.0  1.0  1.0  0.0  0.0
6   1.0  1.0  1.0  1.0  1.0
7   1.0  1.0  1.0  1.0  1.0
8   1.0  1.0  1.0  1.0  0.0
9   1.0  1.0  0.0  1.0  1.0
10  1.0  1.0  0.0  1.0  1.0
11  1.0  1.0  0.0  1.0  1.0
12  1.0  1.0  1.0  1.0  1.0
13  1.0  1.0  1.0  1.0  1.0
14  1.0  1.0  1.0  0.0  1.0
15  1.0  1.0  1.0  1.0  1.0
16  1.0  1.0  0.0  1.0  1.0
17  1.0  1.0  0.0  1.0  0.0
18  1.0  0.0  1.0  1.0  1.0
19  1.0  0.0  1.0  1.0  1.0

我们可以通过使用 concat 来查看它是否按照我们的要求进行更好的查看。

pd.concat([df.iloc[:6, :2], d1.iloc[:6, :2]], axis=1, keys=['Before', 'After'])

  Before      After     
       0    1     0    1
0    0.0  1.0   0.0  1.0
1    1.0  0.0   0.0  0.0
2    1.0  1.0   0.0  0.0
3    1.0  1.0   1.0  1.0
4    1.0  1.0   1.0  1.0
5    1.0  1.0   1.0  1.0

我对 numpy 方法的看法
请参阅@Divakar 的解决方案,因为我借用了他函数的一些元素

def prop_zero(df, window_size=3):
    a = df.values
    W = window_size
    m, n = a.shape

    pad = np.zeros((W - m % W, n))
    b = np.vstack([a, pad])

    return pd.DataFrame(
        b.reshape(-1, W, n).cumprod(1).reshape(-1, n)[:m],
        df.index, df.columns
    )

prop_zero(df)

你可以用 cummin 做一个 groupby:

In [46]: out = df.groupby(np.arange(len(df))//3).cummin()

In [47]: df.head(6)
Out[47]: 
     0    1    2    3    4
0  0.0  1.0  1.0  1.0  1.0
1  1.0  0.0  1.0  1.0  1.0
2  1.0  1.0  0.0  1.0  1.0
3  1.0  1.0  1.0  0.0  1.0
4  1.0  1.0  1.0  1.0  0.0
5  1.0  1.0  1.0  1.0  1.0

In [48]: out.head(6)
Out[48]: 
     0    1    2    3    4
0  0.0  1.0  1.0  1.0  1.0
1  0.0  0.0  1.0  1.0  1.0
2  0.0  0.0  0.0  1.0  1.0
3  1.0  1.0  1.0  0.0  1.0
4  1.0  1.0  1.0  0.0  0.0
5  1.0  1.0  1.0  0.0  0.0

这假设所有值都是 0 和 1。如果您有非 1 值但您仍然想要零后零的行为,您可以做类似的事情

df.where(~(df == 0).groupby(np.arange(len(df))//3).cummax(), 0)

这不是很漂亮,但不会被 0.5 之类的值(因为将 cummin 直接应用于这些值)或潜在的溢出(因为将 cumprod 直接应用于值确实如此)。

这是一个 NumPy 方法,它拆分第一个轴给我们一个 3D 数组,然后沿第一个轴使用 cumprod,然后重塑回 2D。对于行数不能被 window_size 整除的情况,我们会有剩余的元素不会成为重塑的一部分,这些元素将被单独处理。

因此,实施将是 -

def numpy_cumprod(df, window_size=3):
    a = df.values
    W = window_size
    m,n = a.shape
    N = m//W
    M = N*W

    out0 = a[:M].reshape(-1,W,n).cumprod(1).reshape(-1,n)
    out = np.vstack(( out0, a[M:].cumprod(0)))
    return pd.DataFrame(out)

示例 运行 -

In [279]: df
Out[279]: 
    0  1  2  3  4
0   2  2  2  0  1
1   1  2  0  2  2
2   1  1  0  0  1
3   2  0  2  0  1
4   0  0  0  1  0
5   0  0  1  2  1
6   1  1  0  0  1
7   0  0  1  2  1
8   2  2  2  1  1
9   2  1  2  1  0
10  1  1  1  1  2
11  0  2  2  1  2

In [280]: numpy_cumprod(df, window_size=3)
Out[280]: 
    0  1  2  3  4
0   2  2  2  0  1
1   2  4  0  0  2
2   2  4  0  0  2
3   2  0  2  0  1
4   0  0  0  0  0
5   0  0  0  0  0
6   1  1  0  0  1
7   0  0  0  0  1
8   0  0  0  0  1
9   2  1  2  1  0
10  2  1  2  1  0
11  0  2  4  1  0

更大数据集的运行时测试 -

In [275]: df = pd.DataFrame(np.random.randint(0,3,(10000,5)))

# @piRSquared's soln-1 using pandas groupby
In [276]: %timeit df.groupby(np.arange(len(df)) // 3).cumprod()
100 loops, best of 3: 2.49 ms per loop

# @piRSquared's soln-2 using NumPy
In [261]: %timeit prop_zero(df, window_size=3)
1000 loops, best of 3: 285 µs per loop

# Proposed in this post
In [262]: %timeit numpy_cumprod(df, window_size=3)
1000 loops, best of 3: 262 µs per loop