首页 > 解决方案 > 熊猫将公式应用于每一行并找到最小值

问题描述

我正在寻找一种有效的方法来应用公式,使用来自一个数据帧 (df1) 的单行的变量对另一个数据帧 (df2) 中的每一行,然后找到此操作的最小值并存储 df2 中的行这个最小值作为一个新的数据框(df3)出现。给出了示例输入/输出。

df1                
Index   X1  Y1  Z1      
1       3   6   4        
2       7   2   1       
3       4   7   3       

df2
Index   X2  Y2  Z2
1       2   4   1
2       5   3   2
3       7   1   5

申请公式:

d = math.sqrt((X2-X1)**2 + (Y2-Y1)**2 + (Z2-Z1)**2)

如果要将此公式迭代地应用于 df2,其中 (X1, Y1, Z1) 来自 df1 row1 并且 (X2, Y2, Z2) 来自 df2 中的每一行以给出。

[out]
Index  d
1      3.741
2      4.123
3      6.481

由于 df2 第 1 行中的 (X2, Y2, Z2) 提供了最低的 d 值,因此该行将保存到 df3 中,然后对 df1 中的每一行重复该过程。

df3
Index  X2  Y2  Z2  
1      2   4   1 

*注意,df1 和 df2 的长度不同。如果这个问题看起来冗长,我很抱歉,我只是想尽可能清楚。

标签: pythonpandasdataframefunction

解决方案


scipy.spatial.distance.cdist可以使用其默认的欧几里德距离度量来实现这一点:

from scipy.spatial.distance import cdist

df3 = df2.iloc[cdist(df1, df2).argmin(axis=1)]

cdist返回一个(n1, n2)整形数组,其中n1和分别是和n2中的行数。然后我们查看每行的最小距离的索引,看看是哪一行引起了它。然后从中选择这些,df1df2df2ilocdf2

要得到

>>> df3

       X2  Y2  Z2
Index
1       2   4   1
2       5   3   2
1       2   4   1

中间结果:

>>> cdist(df1, df2)

# first row is your calculations in the question, for example
array([[3.74165739, 4.12310563, 6.4807407 ],
       [5.38516481, 2.44948974, 4.12310563],
       [4.12310563, 4.24264069, 7.        ]])

>>> cdist(df1, df2).argmin(axis=1)

array([0, 1, 0], dtype=int64)

即,对于 的第 0 行和第 2 行df1,选择第 0 行df2;对于 的第一行df1df2选择 的第一行(0 索引)。


当您提醒时间-内存权衡时,这是一个 for 循环实现:
# will keep the minimum distance rows' indices
min_inds = []

# foreach row of `df1`...
for row1 in df1.values:
    # these will keep track of min so-far
    min_dist = np.inf
    min_ind = None
    # foreach row of `df2`...
    for j, row2 in enumerate(df2.values):
        # squared distance
        dist = ((row1 - row2) ** 2).sum()
        # is less than minimum so far?
        if dist < min_dist:
            # then update min distance and index
            min_dist = dist
            min_ind = j
    # one row of `df1` finished; save its corresponding row's index
    min_inds.append(min_ind)

# Now we form `df3` with `iloc` as before
df3 = df2.iloc[min_inds]

这给出了相同的结果,但可能更节省内存。


推荐阅读