【问题标题】:Calculating pairwise Euclidean distance between all the rows of a dataframe计算数据帧所有行之间的成对欧几里得距离
【发布时间】:2020-06-19 19:50:04
【问题描述】:

如何计算数据框所有行之间的欧几里得距离?我正在尝试这段代码,但它不起作用:

zero_data = data
distance = lambda column1, column2: pd.np.linalg.norm(column1 - column2)
result = zero_data.apply(lambda col1: zero_data.apply(lambda col2: distance(col1, col2)))
result.head()

这就是我的(44062 x 278)数据框的样子:

【问题讨论】:

  • 你可以试试zero_data.apply(lambda row: distance(*row.values), axis=1)
  • TypeError: ('() 接受 2 个位置参数,但给出了 44062','发生在索引 0') 这是我得到的错误
  • 欧式距离
  • 请阅读this 文章,了解如何发布可重复的pandas 问题。您没有提供任何数据样本供我们使用。
  • @Quicklearner 您在计算中使用的数据框中的列名是什么?

标签: python pandas numpy dataframe euclidean-distance


【解决方案1】:

计算数据帧 df 的两行 i 和 j 之间的欧几里德距离:

np.linalg.norm(df.loc[i] - df.loc[j])

要在连续行之间计算它,即 0 和 1、1 和 2、2 和 3,...

np.linalg.norm(df.diff(axis=0).drop(0), axis=1)

如果你想在所有行之间计算它,即 0 和 1、0 和 2、...、1 和 1、1 和 2 ...,那么你必须遍历 i 和j(请记住,对于 44062 行,有 970707891 个这样的组合,因此使用 for 循环会非常慢):

import itertools

for i, j in itertools.combinations(df.index, 2):
    d_ij = np.linalg.norm(df.loc[i] - df.loc[j])

编辑:

相反,您可以使用 scipy.spatial.distance.cdist 计算每对两个输入集合之间的距离:

from scipy.spatial.distance import cdist

cdist(df, df, 'euclid')

这将为您返回数据框所有行之间的欧几里德距离的对称(44062 x 44062)矩阵。问题是您需要大量内存才能使其工作(至少 8*44062**2 字节的内存,即 ~16GB)。 所以更好的选择是使用pdist

from scipy.spatial.distance import pdist

pdist(df.values, 'euclid')

这将返回一个数组(大小为 970707891),其中包含 df 行之间的所有成对欧几里得距离。

附:不要忘记在计算距离时忽略“Actual_data”列。例如。您可以执行以下操作:data = df.drop('Actual_Data', axis=1).values,然后是 cdist(data, data, 'euclid')pdist(data, 'euclid')。您还可以创建另一个具有如下距离的数据框:

data = df.drop('Actual_Data', axis=1).values

d = pd.DataFrame(itertools.combinations(df.index, 2), columns=['i','j'])
d['dist'] = pdist(data, 'euclid')


   i  j  dist
0  0  1  ...
1  0  2  ...
2  0  3  ...
3  0  4  ...
...

【讨论】:

  • 计算欧几里得距离后如何得到44062乘44062矩阵
  • 如果我删除实际数据,我将如何从这个数据框中获得 44062 x 44062 的值
【解决方案2】:

处理您的数据子集,例如。

df_data = [[888888, 3, 0, 0],
 [677767, 0, 2, 1],
 [212341212, 0, 0, 0],
 [141414141414, 0, 0, 0],
 [1112224, 0, 0, 0]]

# Creating the data
df = pd.DataFrame(data=data, columns=['Actual_Data', '8,8', '6,6', '7,7'], dtype=np.float64)

# Which looks like
#     Actual_Data  8,8  6,6  7,7
# 0  8.888880e+05  3.0  0.0  0.0
# 1  6.777670e+05  0.0  2.0  1.0
# 2  2.123412e+08  0.0  0.0  0.0
# 3  1.414141e+11  0.0  0.0  0.0
# 4  1.112224e+06  0.0  0.0  0.0

# Computing the distance matrix
dist_matrix = df.apply(lambda row: [np.linalg.norm(row.values - df.loc[[_id], :].values, 2) for _id in df.index.values], axis=1)

# Which looks like
# 0     [0.0, 211121.00003315636, 211452324.0, 141413252526.0, 223336.000020149]
# 1    [211121.00003315636, 0.0, 211663445.0, 141413463647.0, 434457.0000057543]
# 2                 [211452324.0, 211663445.0, 0.0, 141201800202.0, 211228988.0]
# 3        [141413252526.0, 141413463647.0, 141201800202.0, 0.0, 141413029190.0]
# 4      [223336.000020149, 434457.0000057543, 211228988.0, 141413029190.0, 0.0]

# Reformatting the above into readable format
dist_matrix = pd.DataFrame(
  data=dist_matrix.values.tolist(), 
  columns=df.index.tolist(), 
  index=df.index.tolist())

# Which gives you
#               0             1             2             3             4
# 0  0.000000e+00  2.111210e+05  2.114523e+08  1.414133e+11  2.233360e+05
# 1  2.111210e+05  0.000000e+00  2.116634e+08  1.414135e+11  4.344570e+05
# 2  2.114523e+08  2.116634e+08  0.000000e+00  1.412018e+11  2.112290e+08
# 3  1.414133e+11  1.414135e+11  1.412018e+11  0.000000e+00  1.414130e+11
# 4  2.233360e+05  4.344570e+05  2.112290e+08  1.414130e+11  0.000000e+00

更新

正如cmets中指出的问题是memory overflow,所以我们必须批量操作问题。

# Collecting the data
# df = ....

# Set this number to a lower value if you get the same `memory` errors.
batch = 200 # #'s of row's / user's used to compute the matrix

# To be conservative, let's write the intermediate results to file type.
dffname = []

for ifile,_slice in enumerate(np.array_split(range(df.shape[0]), batch)):

  # Let's compute distance for `batch` #'s of points in data frame
  tmp_df = df.iloc[_slice, :].apply(lambda row: [np.linalg.norm(row.values - df.loc[[_id], :].values, 2) for _id in df.index.values], axis=1)

  tmp_df = pd.DataFrame(tmp_df.values.tolist(), index=df.index.values[_slice], columns=df.index.values)

  # You can change it from csv to any other files
  tmp_df.to_csv(f"{ifile+1}.csv")
  dffname.append(f"{ifile+1}.csv")

# Reading back the dataFrames
dflist = []
for f in dffname:
  dflist.append(pd.read_csv(f, dtype=np.float64, index_col=0))

res = pd.concat(dflist)

【讨论】:

  • 我认为在计算中应该忽略列“Actual_data”。另外你应该考虑到df中有44062行,你的解决方案会很慢。
  • MemoryError Traceback (最近一次调用最后一次) in 1 from scipy.spatial.distance import cdist 2 ----> 3 cdist(a, a , 'euclid') ~\Anaconda3\lib\site-packages\scipy\spatial\distance.py in cdist(XA, XB, metric, *args, **kwargs) 2727 out = kwargs.pop("out", None ) 2728 if out is None: -> 2729 dm = np.empty((mA, mB), dtype=np.double) 2730 else: 2731 if out.shape != (mA, mB): 应用后我得到上述错误 cdist(a, a, 'euclid')
  • @Quicklearner 您内存不足,正如我在回答中所写的那样...... 44062 x 44062 的浮点数组至少需要大约 16GB 的内存。使用 pdist 代替它需要一半的内存。
猜你喜欢
  • 2018-05-26
  • 2020-01-23
  • 2017-09-08
  • 1970-01-01
  • 2019-02-24
  • 2014-04-09
  • 2016-02-06
  • 2019-07-10
  • 1970-01-01
相关资源
最近更新 更多