如何矢量化(利用 pandas/numpy)而不是使用嵌套 for 循环

M H*_*ley 3 python numpy vectorization pandas

我希望有效地使用pandas(或numpy) 而不是for带有if语句的嵌套循环来解决特定问题。这是一个玩具版本:

假设我有以下两个 DataFrame

import pandas as pd
import numpy as np

dict1 = {'vals': [100,200], 'in': [0,1], 'out' :[1,3]}
df1 = pd.DataFrame(data=dict1)

dict2 = {'vals': [500,800,300,200], 'in': [0.1,0.5,2,4], 'out' :[0.5,2,4,5]}
df2 = pd.DataFrame(data=dict2)
Run Code Online (Sandbox Code Playgroud)

现在我希望循环遍历每个数据帧的每一行,并在满足特定条件时将值相乘。这段代码适用于我想要的

ans = []

for i in range(len(df1)):
    for j in range(len(df2)):
        if (df1['in'][i] <= df2['out'][j] and df1['out'][i] >= df2['in'][j]):
            ans.append(df1['vals'][i]*df2['vals'][j])

np.sum(ans)
Run Code Online (Sandbox Code Playgroud)

然而,显然这是非常低效的,实际上我的 DataFrame 可能有数百万个条目,这使得它无法使用。我也不是在创造pandasnumpy高效的向量实现。有谁知道如何有效地矢量化这个嵌套循环?

我觉得这段代码类似于矩阵乘法,所以可以利用它取得进展outer吗?这是if我发现很难融入的情况,因为if逻辑需要将 中的每个条目df1与 中的所有条目进行比较df2

max*_*111 5

您还可以使用 Numba 等编译器来完成这项工作。这也将优于矢量化解决方案,并且不需要临时数组。

例子

import numba as nb
import numpy as np
import pandas as pd
import time

@nb.njit(fastmath=True,parallel=True,error_model='numpy')
def your_function(df1_in,df1_out,df1_vals,df2_in,df2_out,df2_vals):
  sum=0.
  for i in nb.prange(len(df1_in)):
      for j in range(len(df2_in)):
          if (df1_in[i] <= df2_out[j] and df1_out[i] >= df2_in[j]):
              sum+=df1_vals[i]*df2_vals[j]
  return sum
Run Code Online (Sandbox Code Playgroud)

测试

dict1 = {'vals': np.random.randint(1, 100, 1000),
         'in': np.random.randint(1, 10, 1000),
         'out': np.random.randint(1, 10, 1000)}
df1 = pd.DataFrame(data=dict1)
dict2 = {'vals': np.random.randint(1, 100, 1500),
         'in': 5*np.random.random(1500),
         'out': 5*np.random.random(1500)}
df2 = pd.DataFrame(data=dict2)

# First call has some compilation overhead
res=your_function(df1['in'].values, df1['out'].values, df1['vals'].values,
                  df2['in'].values, df2['out'].values, df2['vals'].values)

t1 = time.time()
for i in range(1000):
  res = your_function(df1['in'].values, df1['out'].values, df1['vals'].values,
                      df2['in'].values, df2['out'].values, df2['vals'].values)

print(time.time() - t1)
Run Code Online (Sandbox Code Playgroud)

时间安排

vectorized solution @AGN Gazer: 9.15ms
parallelized Numba Version: 0.7ms
Run Code Online (Sandbox Code Playgroud)