【问题标题】:Pandas apply formula to each row and find minimum熊猫将公式应用于每一行并找到最小值
【发布时间】:2021-07-13 15:39:48
【问题描述】:

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

【问题讨论】:

    标签: python pandas dataframe function


    【解决方案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]
    

    结果相同,但内存效率更高。

    【讨论】:

    • Mustafa Aydin,只是一个旁注。这很好用,但是对于大型数据帧,需要大量内存,从而导致内存错误:无法分配具有形状 (x, x) 和数据类型的数组。虽然很慢,但似乎 for 循环可能更适合这些情况。
    • @SasCom 感谢时间-记忆权衡评论。
    • 艾丁,对不起,我是新手!我想我是想礼貌地问我如何实现这样的 for 循环。
    • 嘿@SasCom,我尝试用 cmets 编写一个 for 循环实现。它给出了相同的结果,但正如您所说,它可能对内存更友好。希望这会有所帮助。
    • Aydin,非常感谢您的帮助!
    猜你喜欢
    • 2017-02-03
    • 2018-03-24
    • 2018-06-09
    • 2019-11-24
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 2017-04-19
    • 1970-01-01
    相关资源
    最近更新 更多