【问题标题】:Calculate euclidean distance between groups in a data frame计算数据框中组之间的欧几里得距离
【发布时间】:2020-01-23 17:14:47
【问题描述】:

我有各种商店的每周数据,格式如下:

pd.DataFrame({'Store':['S1', 'S1', 'S1', 'S2','S2','S2','S3','S3','S3'], 'Week':[1, 2, 3,1,2,3,1,2,3],
                           'Sales' : [20,30,40,21,31,41,22,32,42],'Cust_count' : [2,4,6,3,5,7,4,6,8]})

   Store Week Sales Cust_count
0   S1   1    20    2
1   S1   2    30    4
2   S1   3    40    6
3   S2   1    21    3
4   S2   2    31    5
5   S2   3    41    7
6   S3   1    22    4
7   S3   2    32    6
8   S3   3    42    8

如您所见,数据处于商店周级别,我想计算同一周内每个商店之间的欧几里得距离,然后取计算距离的平均值。因此,例如 Store S1 和 S2 的计算如下所示:

    For week 1: sqrt((20-21)^2 + (2-3)^2) = sqrt(2)
    For week 2: sqrt((30-31)^2 + (4-5)^2) = sqrt(2)
    For week 3: sqrt((40-41)^2 + (6-7)^2) = sqrt(2)
    The final value for distance between S1 and S2 = sqrt(2) which is calculated as 
average distance of the 3 weeks i.e. (3 * sqrt(2)) / 3 

最后输出应该如下:

   S1    S2      S3
S1 0     1.414   2.818
S2 1.414 0       some val
S3 2.818 some val 0

我对分组数据帧中的列和 scipy.spatial.distance.cdist 计算欧几里得距离有一些想法,但我无法将这些概念联系起来并提出解决方案。

【问题讨论】:

    标签: python pandas dataframe scipy euclidean-distance


    【解决方案1】:

    我们可以pivot 然后使用numpy 进行这些计算

    df1  = (df.pivot(index='Store', columns='Week', values=['Sales', 'Cust_count'])
           #  .fillna(0)  # Uncomment if you want to treat missing store-weeks as 0s
           )
    arr1 = df1['Sales'].to_numpy()
    arr2 = df1['Cust_count'].to_numpy()
    
    data = np.nanmean(np.sqrt(((arr1[None, :] - arr1[:, None])**2 
                             + (arr2[None, :] - arr2[:, None])**2)), 
                      axis=2)
    
    pd.DataFrame(data, index=df1.index, columns = df1.index)
    

    Store        S1        S2        S3
    Store                              
    S1     0.000000  1.414214  2.828427
    S2     1.414214  0.000000  1.414214
    S3     2.828427  1.414214  0.000000
    

    【讨论】:

    • 这很聪明:-)
    • 这是一个很好的解决方案。我确实有一些观察,枢轴功能对我不起作用,说“数据必须是一维的”。我猜pivot_table 函数应该在这里工作。其次,枢轴方法的一个潜在缺点是,由于我有 3 个月的数据和 100 家商店的数据,所以基本上在枢轴之后将创建至少 1200 个列,我觉得计算距离可能很难计算。你的想法?
    • 在一种情况下,此解决方案无法正常工作,即当两个商店的周数不同时,例如,如果我从 S2 商店中删除第 3 周的数据,那么上述解决方案也会给出S1 和 S2 之间的值相同(1.414),但在这种情况下,该值应该是 1.414(第 1 周)、1.414(第 2 周)和 sqrt(40-0)^2 + (6-0)^2 的平均值3
    • @bakas 我不会说它工作不正常。您只是没有指定对不存在的几周进行什么比较,这更加谨慎,只取两个商店都存在数据的几周的平均值(我认为这是合理的:D)。幸运的是,修复非常简单;在枢轴之后添加.fillna(0),它可以按您的意愿工作。在您描述的情况下,您现在应该为 S1 和 S2 获得 14.42530798568652
    • 也许我说得太严厉了,抱歉。你说得对,我也应该提到这种情况。谢谢:)
    【解决方案2】:

    permutations 的 For 循环

    import itertools
    s=list(itertools.permutations(df.Store.unique(), 2))
    from scipy import spatial
    l=[]
    for x in s:
         l.append(np.sqrt(np.mean(np.sum((df[df.Store == x[0]].iloc[:, 2:].values - df[df.Store == x[1]].iloc[:, 2:].values)**2,axis=1),axis=0)))
    
    s=pd.Series(l,index=pd.MultiIndex.from_tuples(s)).unstack()
    s
    Out[216]: 
              S1        S2        S3
    S1       NaN  1.414214  2.828427
    S2  1.414214       NaN  1.414214
    S3  2.828427  1.414214       NaN
    

    【讨论】:

    • 它适用于示例数据集,但是当我在完整数据集上运行时,我在 l.append 步骤中出现错误,提示“操作数无法与形状一起广播 (193,2) ( 97,2)"。是因为两个商店可能没有相同数量的数据点吗?如果是,我们如何解决这个问题
    【解决方案3】:

    你可以先在 Week 上merge 得到所有店铺组合,然后用欧式距离计算列dist,最后用aggfunc='mean' 计算pivot_table

    df.merge(df, on='Week', how='left', suffixes=('','_'))\
      .assign(dist = lambda x: np.sqrt((x.Sales - x.Sales_)**2 + (x.Cust_count - x.Cust_count_)**2))\
      .pivot_table(index='Store', columns='Store_', values='dist', aggfunc='mean')
    
    Store_        S1        S2        S3
    Store                               
    S1      0.000000  1.414214  2.828427
    S2      1.414214  0.000000  1.414214
    S3      2.828427  1.414214  0.000000
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-24
      • 2020-06-10
      • 2017-09-15
      • 1970-01-01
      • 2016-02-15
      • 1970-01-01
      • 2021-03-30
      • 2020-11-29
      相关资源
      最近更新 更多