熊猫将公式应用于每一行并找到最小值

时间:2021-07-13 15:39:48

标签: python pandas dataframe function

我正在寻找一种有效的方法来使用来自一个数据帧 (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 的长度不同。如果这个问题看起来冗长,我很抱歉,我只是想尽可能清楚。

1 个答案:

答案 0 :(得分:1)

scipy.spatial.distance.cdist 可用于其默认欧几里得距离度量:

from scipy.spatial.distance import cdist

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

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

得到

>>> 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)

df1的第0行和第2行,选择df2的第0行;对于 df1 的第一行,选择了 df2 的第一行(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]

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