大数据方法来计算R中的距离集?

Posted

技术标签:

【中文标题】大数据方法来计算R中的距离集?【英文标题】:Big data ways to calculate sets of distances in R? 【发布时间】:2022-01-20 13:24:18 【问题描述】:

问题:我们需要一种大数据方法来计算点之间的距离。我们在下面概述了我们想用一个五观察数据框做的事情。但是,随着行数变大(> 100 万),这种特殊方法是不可行的。过去,我们使用 SAS 进行此类分析,但如果可能,我们更喜欢 R。 (注意:我不打算展示代码,因为虽然我在下面概述了一种在较小数据集上执行此操作的方法,但对于我们规模的数据来说,这基本上是一种不可能的方法。)

我们从商店的数据框开始,每个商店都有纬度和经度(虽然这不是空间文件,我们也不想使用空间文件)。

# you can think of x and y in this example as Cartesian coordinates
stores <- data.frame(id = 1:5,
                     x = c(1, 0, 1, 2, 0),
                     y = c(1, 2, 0, 2, 0))

stores
  id x y
1  1 1 1
2  2 0 2
3  3 1 0
4  4 2 2
5  5 0 0

对于每个商店,我们想知道 x 距离内的商店数量。在一个小的数据框中,这很简单。创建另一个所有坐标的数据框,重新合并,计算距离,如果距离小于 x,则创建一个指标,然后将指标相加(商店本身减去一个,距离为 0)。这将产生一个如下所示的数据集:

   id x y  s1.dist  s2.dist  s3.dist  s4.dist  s5.dist
1:  1 1 1 0.000000 1.414214 1.000000 1.414214 1.414214
2:  2 0 2 1.414214 0.000000 2.236068 2.000000 2.000000
3:  3 1 0 1.000000 2.236068 0.000000 2.236068 1.000000
4:  4 2 2 1.414214 2.000000 2.236068 0.000000 2.828427
5:  5 0 0 1.414214 2.000000 1.000000 2.828427 0.000000

当您(任意)将 1.45 以下视为“收盘价”时,您最终会得到如下所示的指标:

# don't include the store itself in the total
   id x y s1.close s2.close s3.close s4.close s5.close total.close
1:  1 1 1        1        1        1        1        1           4
2:  2 0 2        1        1        0        0        0           1
3:  3 1 0        1        0        1        0        1           2
4:  4 2 2        1        0        0        1        0           1
5:  5 0 0        1        0        1        0        1           2

最终产品应如下所示:

   id total.close
1:  1           4
2:  2           1
3:  3           2
4:  4           1
5:  5           2

感谢所有建议。

非常感谢

【问题讨论】:

数据集到底有多大?数据集是否太大而无法带入 R?有Hadoop 和其他分布式存储系统的数据存储选项。如果数据可以完全导入 R,有很多选择。您可以阅读其中一些选项here。 数据已经在 HPCC 上。问题是,要创建我上面描述的那种矩阵,它就像一个 1,000,000 x 1,000,000 数据帧,即使使用并行化和 HPC 也不理想。不过,如果我误解了你的建议,请纠正我。 我还应该补充一点,我们正在使用机密数据,因此我们可以使用或添加的软件包受到限制。任何连接到互联网的东西都是不允许的,如果我正确理解文档的话,这似乎排除了 Hadoop。 R 可以处理大约 2M 行(或列),因此您将不得不使用 HPC 中的聚类等方法。但是,由于您尝试执行的操作并不是特别复杂,您可能会发现data.table 包是您的最佳选择。我不确定您在坐标(即半正弦、文森蒂、欧几里得等)或比例(即英里、公里等)之间寻找什么度量标准,我只能提供一个包名字! 一百万点?太多了,您必须计算 n(n-1)/2 距离,即约 5000 亿距离 【参考方案1】:

你有什么理由不能循环而不是做一个大计算?

stores <- data.frame(id = 1:5,
                     x = c(1, 0, 1, 2, 0),
                     y = c(1, 2, 0, 2, 0))

# Here's a Euclidean distance metric, but you can drop anything you want in here
distfun <- function(x0, y0, x1, y1)
  sqrt((x1-x0)^2+(y1-y0)^2)


# Loop over each store
t(sapply(seq_len(nrow(stores)), function(i)
  distances <- distfun(x0 = stores$x[i], x1 = stores$x,
                       y0 = stores$y[i], y1 = stores$y)
  # Calculate number less than arbitrary cutoff, subtract one for self
  num_within <- sum(distances<1.45)-1
  c(stores$id[i], num_within)
))

生产:

     [,1] [,2]
[1,]    1    4
[2,]    2    1
[3,]    3    2
[4,]    4    1
[5,]    5    2

这将适用于您可以带入 R 的任何大小的数据集,但随着大小的增加它会变得更慢。这是在我的机器上运行几秒钟的 10,000 个条目的测试:

stores <- data.frame(id=1:10000, 
                     x=runif(10000, max = 10), 
                     y=runif(10000, max = 10))
          [,1] [,2]
    [1,]     1  679
    [2,]     2  698
    [3,]     3  618
    [4,]     4  434
    [5,]     5  402
...
 [9995,]  9995  529
 [9996,]  9996  626
 [9997,]  9997  649
 [9998,]  9998  514
 [9999,]  9999  667
[10000,] 10000  603

计算越多越慢(因为它必须在每对点之间运行,这总是 O(n^2))但不知道您要计算的实际距离度量,我们无法优化再慢一点。

【讨论】:

这与以矢量化格式进行整个计算相同。您仍在重复计算。例如,一旦你计算了 1 和 2 之间的距离,你就再次计算 2 和 1 之间的距离,这使得这个函数的时间复杂度在 O(n^2) 中。而且我的朋友不会在超过 100 万行中工作 @Onyambu 是的,同意 - 但至少在 O(n^2) 的时间复杂度下它是可行的(可能一次创建数据库,而不是交互的东西?),而内存复杂度为O(n^2) 将需要 hardware 根本不存在 - 请参阅我对 jay 的回答的评论,估计 1M 行需要 ~4TB 的 RAM 此外,距离矩阵不能保证是对称的 - 在欧几里得空间中是对称的,但在许多研究领域中,A 和 B 之间的距离并不总是与B 和 A,如果是这种情况,就无法避免“重复”计算。【参考方案2】:

您真的已经尝试过经典的dist() 功能了吗?核心是在C 中实现的,因此应该很快。

可能对矩阵的强制转换(无论如何都发生在 dist 中)已经花费了大量时间,也许可以立即将其作为矩阵而不是首先作为数据框读取。

M <- as.matrix(stores[-1])

dist(M, diag=TRUE, upper=TRUE)
#          1        2        3        4        5
# 1 0.000000 1.414214 1.000000 1.414214 1.414214
# 2 1.414214 0.000000 2.236068 2.000000 2.000000
# 3 1.000000 2.236068 0.000000 2.236068 1.000000
# 4 1.414214 2.000000 2.236068 0.000000 2.828427
# 5 1.414214 2.000000 1.000000 2.828427 0.000000

否则你可以试试这个C++ 实现,它基本上是@coatless's code 的副本。但是,我在 R 脚本中使用了 Rcpp 包。

library(Rcpp)
cppFunction('Rcpp::NumericMatrix calcPWD1 (const Rcpp::NumericMatrix & x)
  unsigned int outrows = x.nrow(), i = 0, j = 0;
  double d;
  Rcpp::NumericMatrix out(outrows,outrows);

  for (i = 0; i < outrows - 1; i++)
    Rcpp::NumericVector v1 = x.row(i);
    for (j = i + 1; j < outrows ; j ++)
      d = sqrt(sum(pow(v1-x.row(j), 2.0)));
      out(j,i)=d;
      out(i,j)=d;
    
  

  return out;
')

calcPWD1(M)
#          [,1]     [,2]     [,3]     [,4]     [,5]
# [1,] 0.000000 1.414214 1.000000 1.414214 1.414214
# [2,] 1.414214 0.000000 2.236068 2.000000 2.000000
# [3,] 1.000000 2.236068 0.000000 2.236068 1.000000
# [4,] 1.414214 2.000000 2.236068 0.000000 2.828427
# [5,] 1.414214 2.000000 1.000000 2.828427 0.000000

但是,基准测试显然支持dist,所以你应该试一试:

M_big <- M[sample(nrow(M), 1e4, replace=TRUE), ]  ## inflate to 10k rows
microbenchmark::microbenchmark(
  dist=dist(M_big, diag=TRUE, upper=TRUE),
  calcPWD1=calcPWD1(M_big),
  control=list(warmup=10L),
  times=3L
)
# Unit: milliseconds
#     expr       min        lq     mean   median        uq       max neval cld
#     dist  640.1861  660.1396  765.881  680.093  828.7284  977.3638     3  a 
# calcPWD1 1419.4106 1439.1353 1505.253 1458.860 1548.1736 1637.4873     3   b

请务必阅读@coatless's and Dirk Eddelbuettel's answers,他们在其中写了更多关于CC++R 的内容,并且还有其他版本的函数。

【讨论】:

dist 肯定会突破一百万个条目!在您的示例中使用 10k 条目运行它已经占用了大约 400MB 的内存,预计在 100k 时会增加到 40GB,并且在 OP 的 1M 行处需要 4TB 的内存。 我认为问题在于dist 是否会损坏或内存不足。

以上是关于大数据方法来计算R中的距离集?的主要内容,如果未能解决你的问题,请参考以下文章

在 R 中处理大型数据集

大数据学习笔记:距离度量和相似度度量

python - 如何使用python中的haversine库计算大距离矩阵?

R中的高效方法是将新列添加到具有大数据集的数据框中

使用非常大的数据集分析 R 中两点之间的空间数据

大数据学习笔记:聚类分析