避免使用 iterrows 查询局部异常值

avoiding iterrows for querying local outlier

对于包含坐标列的数据框(例如 'x'、'y'),我想检查关联值 'val' 是否偏离 'val' 的平均值本地(到坐标的距离 < 半径)邻域。我发现了以下经常使用的方法(例如 or ),构建 KDTree 并查询每一行的局部平均值。但是我想知道是否有更好的解决方案可以防止数据帧迭代导致更快的执行?

import pandas as pd
import numpy as np
from sklearn.neighbors import KDTree

xy = np.mgrid[0:10,0:10]
df = pd.DataFrame({'x':xy[0].ravel(), 'y':xy[1].ravel(), 'val':np.random.rand(100)})

tree = KDTree(df[['x', 'y']].values, metric='euclidean')

radius = 5
for i, row in df.iterrows():
    coords = row[['x', 'y']].values.reshape(1, -1)
    idx = tree.query_radius(coords, r=radius)[0]
    df.loc[i, 'outlier'] = np.abs(row['val'] - df.iloc[idx]['val'].mean()) > df.iloc[idx]['val'].std()
df = df[df["outlier"] == False] #select df without outlier

我还没有想出避免将所有循环在一起的方法,但是您可以应用的一个简单解决方案是将所需的值放入数组中,然后对这些数组执行矢量化操作。我做了一些测试,执行时间平均减少了大约 40%。

coords = df[['x','y']].apply(lambda row: row.values.reshape(1,-1),axis=1)
df.coords = coords
idx = coords.apply(lambda x: tree.query_radius(x,r=radius)[0])
means = idx.apply(lambda x: df.loc[x,'val'].mean())
df.means = means
stds = idx.apply(lambda x: df.loc[x,'val'].std())
df.stds = stds
df['outlier']=np.abs(df['val']-df.means)>df.stds