如何对数据框架中的每一行应用函数?

我是 Python 的新手,不知道如何解决下面的问题。

我有一个功能:

def EOQ(D,p,ck,ch):
Q = math.sqrt((2*D*ck)/(ch*p))
return Q

假设我有数据框

df = pd.DataFrame({"D": [10,20,30], "p": [20, 30, 10]})


D   p
0   10  20
1   20  30
2   30  10


ch=0.2
ck=5

chck是浮点类型。现在我想对数据框中的每一行应用公式,并将其作为额外的一行‘ Q’返回。例如(不起作用的) :

df['Q']= map(lambda p, D: EOQ(D,p,ck,ch),df['p'], df['D'])

(只返回“ map”类型)

我将需要在我的项目中更多的这种类型的处理,我希望找到一些工作。

109782 次浏览

下列措施应该奏效:

def EOQ(D,p,ck,ch):
Q = math.sqrt((2*D*ck)/(ch*p))
return Q
ch=0.2
ck=5
df['Q'] = df.apply(lambda row: EOQ(row['D'], row['p'], ck, ch), axis=1)
df

如果你所做的只是计算某个结果的平方根,那么使用 np.sqrt方法,这种方法是向量化的,而且会快得多:

In [80]:
df['Q'] = np.sqrt((2*df['D']*ck)/(ch*df['p']))


df
Out[80]:
D   p          Q
0  10  20   5.000000
1  20  30   5.773503
2  30  10  12.247449

时机

30k 行 df:

In [92]:


import math
ch=0.2
ck=5
def EOQ(D,p,ck,ch):
Q = math.sqrt((2*D*ck)/(ch*p))
return Q


%timeit np.sqrt((2*df['D']*ck)/(ch*df['p']))
%timeit df.apply(lambda row: EOQ(row['D'], row['p'], ck, ch), axis=1)
1000 loops, best of 3: 622 µs per loop
1 loops, best of 3: 1.19 s per loop

您可以看到,np 方法的速度快了约1900倍

在 DataFrame 的每一行上应用函数的方法还有很多。

(1)您可以稍微修改 EOQ,让它接受一行(一个 Series 对象)作为参数,并使用函数中的列名访问相关元素。此外,还可以使用关键字向 apply传递参数,例如 chck:

def EOQ1(row, ck, ch):
Q = math.sqrt((2*row['D']*ck)/(ch*row['p']))
return Q


df['Q1'] = df.apply(EOQ1, ck=ck, ch=ch, axis=1)

(2)结果表明,apply通常比列表内涵慢(在下面的基准测试中,它慢了20倍)。要使用列表内涵,可以进一步修改 EOQ,以便通过它的索引访问元素。然后在循环中对转换为列表的 df行调用该函数:

def EOQ2(row, ck, ch):
Q = math.sqrt((2*row[0]*ck)/(ch*row[1]))
return Q


df['Q2a'] = [EOQ2(x, ck, ch) for x in df[['D','p']].to_numpy().tolist()]

(3)碰巧的是,如果目标是迭代地调用一个函数,那么 map通常比列表内涵更快。所以你可以把 df转换成一个列表,把函数 map转换成它,然后把结果解压缩成一个列表:

df['Q2b'] = [*map(EOQ2, df[['D','p']].to_numpy().tolist(), [ck]*len(df), [ch]*len(df))]

(4)作为 @ EdChum 备注,如果可能的话,最好使用向量化方法,而不是逐行应用函数。熊猫提供的矢量化方法可以媲美 numpy 的方法。以 EOQ为例,你可以使用大熊猫的 pow方法代替 math.sqrt(在下面的基准中,使用大熊猫向量化方法比使用 numpy 快约20%) :

df['Q_pd'] = df['D'].mul(2*ck).div(ch*df['p']).pow(0.5)

产出:

    D   p          Q       Q_np         Q1        Q2a        Q2b       Q_pd
0  10  20   5.000000   5.000000   5.000000   5.000000   5.000000   5.000000
1  20  30   5.773503   5.773503   5.773503   5.773503   5.773503   5.773503
2  30  10  12.247449  12.247449  12.247449  12.247449  12.247449  12.247449

时间:

df = pd.DataFrame({"D": [10,20,30], "p": [20, 30, 10]})
df = pd.concat([df]*10000)


>>> %timeit df['Q'] = df.apply(lambda row: EOQ(row['D'], row['p'], ck, ch), axis=1)
623 ms ± 22.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


>>> %timeit df['Q1'] = df.apply(EOQ1, ck=ck, ch=ch, axis=1)
615 ms ± 39.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


>>> %timeit df['Q2a'] = [EOQ2(x, ck, ch) for x in df[['D','p']].to_numpy().tolist()]
31.3 ms ± 479 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


>>> %timeit df['Q2b'] = [*map(EOQ2, df[['D','p']].to_numpy().tolist(), [ck]*len(df), [ch]*len(df))]
26.9 ms ± 306 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit df['Q_np'] = np.sqrt((2*df['D']*ck)/(ch*df['p']))
1.19 ms ± 53.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)


>>> %timeit df['Q_pd'] = df['D'].mul(2*ck).div(ch*df['p']).pow(0.5)
966 µs ± 27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)