r - 一条记录和整个数据帧之间的最小差异



我试图使计算海量数据集(600,000条记录)记录中的不相异性变得可行。

第一个任务是使用单个记录整个 data.frame(不包括该记录)之间的欧氏距离来计算差异性。

考虑以下示例:

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

这个问题分两步阐明:

  1. 使用矢量化操作返回长度为 4 的向量,其中one_row的相异性值与每行mydf[-1,]的比较
  2. 从点 1.的向量中提取与one_row更相似的行的索引

然后,我可以为 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整个数据集的欧几里得距离。你不想一遍又一遍地复制整个事情,因为你要做60万次。但是你可以写一个矢量化函数来计算欧几里得距离,并使用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)。

然后,您将 var 组合到一列中(这样可以更轻松地传递给像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

👍 祝你好运! 🤞 希望这有帮助。

最新更新