我试图使计算海量数据集(600,000条记录)记录中的不相异性变得可行。
第一个任务是使用单个记录与整个 data.frame(不包括该记录)之间的欧氏距离来计算差异性。
考虑以下示例:
mydf <- data.frame(var1 = rnorm(5), var2 = rnorm(5), var3 = rnorm(5))
one_row <- mydf[1,]
这个问题分两步阐明:
- 使用矢量化操作返回长度为 4 的向量,其中
one_row
的相异性值与每行mydf[-1,]
的比较 - 从点 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
等)。然后使用mutate
和map
将函数应用于每一行,然后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
👍 祝你好运! 🤞 希望这有帮助。