一个记录和一个整体之间的最小差异 data.frame

Minimum dissimilarity between one record and a whole data.frame

我正在努力使大规模数据集(600,000 条记录)的记录内的差异性计算成为可能。

第一个任务是使用 单个记录 整个 data.frame 之间的欧氏距离计算差异记录.

考虑以下示例:

mydf <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm(5))
one_row <- mydf[1,]

问题分两步:

  1. 使用矢量化操作 到return 长度为 4 的向量,与 mydf[-1,] 的每一行相比具有 one_row 的差异值
  2. 从第 1 点的向量中提取与 one_row
  3. 更相似的行的索引

然后,我可以为 mydf 中的每一行重复这个过程,因此,为每一行找到最相似的行。这将允许我执行凝聚聚类以及计算统计标准,如基于距离矩阵的 Silhoutte。

更新

一种可能的方法是将 one_row 复制到与 mydf 相同的大小,并通过成对执行来向量化相似度计算。

replicated <- [rep(1, 5), 1:ncol(a)]

正确答案

Jesse Tweedle 和 won782 的回答对我的问题都是正确的。

Jesse Tweedle 的积极方面是可以自定义允许使用混合数据类型的距离函数。消极的一面是它不是一个单一的表达式,而是一个函数管道。

won782 的积极方面是它在单个表达式中执行。消极方面是它只适用于矩阵,因此,数字变量。

我选择 won782 答案是因为他的解决方案可以很容易地扩展为用作计算剪影标准的基本组件,而无需存储相异矩阵。

如果我没有正确理解你的问题,你想对给定的向量执行按行运算并计算每行的欧氏距离。

mydf <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm(5))
one_row <- mydf[1,]

result = apply(mydf, 1, function(x) {
  sqrt(sum((x - one_row)^2))
})
result
[1] 0.000000 3.333031 3.737814 1.875482 4.216042

结果是欧氏距离向量。然后,您可以执行 which.min 函数来查找最小值的索引。

使用矩阵运算:

sqrt(rowSums((t(t(as.matrix(mydf)) - as.numeric(one_row)))^2))

在更大的数据集上对两种方法进行基准测试

> mydf <- data.frame(var1 = rnorm(10000), var2 = rnorm(10000), var3 = rnorm(10000))
> one_row <- mydf[1,]
> # Matrix operation method
> system.time({ 
+   sqrt(rowSums((t(t(as.matrix(mydf)) - as.numeric(one_row)))^2))
+   })
   user  system elapsed 
  0.000   0.000   0.001 
> # Apply Method
> system.time({ 
+   apply(mydf, 1, function(x) {
+     sqrt(sum((x - one_row)^2))
+   })
+ })
   user  system elapsed 
  5.186   0.014   5.204 

很明显,矩阵运算是更快的方法。

问题:

您可以在 mydf 上使用 dist,但答案对于您的计算机而言太大(1e11-ish 元素)。因此,挑战在于计算每一行 x 整个数据集的欧氏距离。你不想一遍又一遍地复制整个事情,因为你会重复 600,000 次。但是你可以写一个向量化的函数来计算欧几里德距离,用tidyverse东西来简洁地应用它。

答案:

编写一个函数 euc 并在第二个参数上将其向量化。

library(tidyverse)
euc <- function(x, y) { 
  sqrt(sum((x - y)^2))
}
euc_ <- Vectorize(euc, vectorize.args = "y")
calculate_distances <- function(row, df) {
  dists <- euc_(row, split(df, 1:nrow(df)))
  # gives you name of row and distance that gives minimum distance.
  dists[dists>0 & dists == min(dists[dists>0])] %>% enframe()
}

然后 calculate_distances 函数计算从单行到数据集其余部分的欧几里得距离,然后将参数折叠为具有最小距离的那个的名称和值(不包括它自己,所以我们需要包括 dist>0).

然后将变量合并到一列中(这样可以更轻松地传递给 calculate_distances 之类的函数,而无需指定列名、var1 等)。然后使用 mutatemap 将函数应用于每一行,然后 unnest 解压结果(如果需要,保留原始数据)。

mydf <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm(5))

mydf %>% 
  mutate(n = row_number()) %>% 
  group_by(n) %>% 
  nest(var1, var2, var3) %>% 
  mutate(ans = map(data, calculate_distances, df = mydf)) %>%
  unnest(ans, data)
# A tibble: 5 x 6
      n  name    value         var1       var2       var3
  <int> <chr>    <dbl>        <dbl>      <dbl>      <dbl>
1     1     4 1.027080  0.035684445  0.3152272  1.9001506
2     2     5 1.453509 -0.985996620  0.2650241 -0.2146157
3     3     2 1.645737  0.009665813 -0.8393461  0.4907029
4     4     1 1.027080  0.314943627  0.9910671  1.1789382
5     5     2 1.453509  0.436344415  0.5309611 -0.3521368

祝你好运!希望这会有所帮助。