如何在不使用 for 循环的情况下使用涉及前几行单元格的计算来填充 Pandas 列

How to Fill Pandas Column with calculations involving cells from previous rows without Using for loop

以绿色突出显示的单元格是需要在 pandas 数据框中完成的计算,无需应用 for 循环。

我尝试了下面的代码。 但它在“Val”列中给出了错误的值。

从代码中获得的输出是

数据框:df 帕瓦尔 0 50 50.0 1 60 57.0 2 70 67.0 3 80 77.0 4 90 87.0 5 100 97.0

在不使用循环应用程序的情况下获取图像中显示的值时需要帮助。 [![在此处输入图片描述][2]][2]

[![在此处输入图片描述][2]][2]

df = pd.DataFrame()
df['Par'] = [50,60,70,80,90,100]
k = df['Par'].shift(1).fillna(df['Par'][0]).astype(float)

df['Val'] = (df['Par']*0.7) + (k*0.3)

print("DataFrame: df\n",df)`

我不确定这个答案是否足够好。 首先,您的代码无法像 Excel 那样工作的原因是 Excel 使用的是累积计算,这意味着 k 值是根据公式的结果更新的df["par"]k 的前一行中的值。问题本身的性质必须通过使用前一行来解决;这意味着逐行。

如何做到这一点很简单。

import pandas as pd

df = pd.DataFrame()
df['Par'] = [50,60,70,80,90,100]
df.loc[0,"Val"] = df.loc[0,"Par"] #similar to =A5 in excel

   Par   Val
0   50  50.0
1   60   NaN
2   70   NaN
3   80   NaN
4   90   NaN
5  100   NaN

的概念是na的值会填充上一行和同行不同的计算。但是第三、四、五行没有之前的值需要计算,所以,它们会重新用na填充。

df["Val"] = df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))
   Par   Val
0   50  50.0
1   60  57.0
2   70   NaN
3   80   NaN
4   90   NaN
5  100   NaN

在我们估算了 Val 的第二行之后,第三行现在有一个以前的值来估算自己。所以,我们运行再次上线。

   Par   Val
0   50  50.0
1   60  57.0
2   70  66.1
3   80   NaN
4   90   NaN
5  100   NaN

所以,现在第四次也是如此,然后是第五次。但是当你有超过 5 行时,一次又一次地 运行 宁这一行对你来说将是一件苦差事。所以,在这种情况下,我们会使用(是的)循环。 但是您的请求包括 without Using for loop 部分,我对此感到不知所措。可能有办法做到这一点,但对我来说,我坚持计算 -> 获取价值 -> 计算。因此,这里有几个循环,它们不使用数据帧本身的任何数据,而只是重复代码多次。

i == 0
if i < len(df):
    df["Val"]=df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))
    ++i

for i in range(len(df)):
    df["Val"]=df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))
    ++i

import itertools

for _ in itertools.repeat(None, len(df)):
    df["Val"] = df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))

如果您找到了不使用循环的方法,请与我们分享。多亏了你们,我今天度过了一个非常有趣的夜晚。

递归计算不可向量化,为了提高性能使用 numba:

from numba import jit

@jit(nopython=True)
def f(a):
    d = np.empty(a.shape)
    d[0] = a[0]
    for i in range(1, a.shape[0]):
        d[i] = d[i-1] * 0.3 + a[i] * 0.7
    return d

df['Val'] = f(df['Par'].to_numpy())
print (df)
   Par      Val
0   50  50.0000
1   60  57.0000
2   70  66.1000
3   80  75.8300
4   90  85.7490
5  100  95.7247

1k 行的性能差异:

from numba import jit
import itertools

np.random.seed(2022)

df = pd.DataFrame({'Par': np.random.randint(100, size=1000)})


In [64]: %%timeit
    ...: 
    ...: df['Val1'] = f(df['Par'].to_numpy())
    ...: 
    ...: import itertools
    ...: 
    ...: df.loc[0,"Val"] = df.loc[0,"Par"]
    ...: for _ in itertools.repeat(None, len(df)):
    ...:     df["Val"] = df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))
    ...:     
1.05 s ± 193 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [65]: %%timeit
    ...: @jit(nopython=True)
    ...: def f(a):
    ...:     d = np.empty(a.shape)
    ...:     d[0] = a[0]
    ...:     for i in range(1, a.shape[0]):
    ...:         d[i] = d[i-1] * 0.3 + a[i] * 0.7
    ...:     return d
    ...: 
    ...: df['Val1'] = f(df['Par'].to_numpy())
    ...: 
121 ms ± 3.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

测试 100krows:

np.random.seed(2022)
df = pd.DataFrame({'Par': np.random.randint(100, size=100000)})


In [70]: %%timeit
    ...: 
    ...: df['Val1'] = f(df['Par'].to_numpy())
    ...: 
    ...: import itertools
    ...: 
    ...: df.loc[0,"Val"] = df.loc[0,"Par"]
    ...: for _ in itertools.repeat(None, len(df)):
    ...:     df["Val"] = df["Val"].fillna((df["Par"]*0.7)+(df["Val"].shift(1)*(0.3)))
    ...:     
4min 47s ± 5.39 s per loop (mean ± std. dev. of 7 runs, 1 loop each)



In [71]: %%timeit
    ...: @jit(nopython=True)
    ...: def f(a):
    ...:     d = np.empty(a.shape)
    ...:     d[0] = a[0]
    ...:     for i in range(1, a.shape[0]):
    ...:         d[i] = d[i-1] * 0.3 + a[i] * 0.7
    ...:     return d
    ...: 
    ...: df['Val1'] = f(df['Par'].to_numpy())
    ...: 
    ...: 
129 ms ± 11.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)