我有一个包含以下列的数据表:
name, x, y
a, 1, 2
b, 2, 3
c, 3, 1
我希望自己加入此表,将每一行保持在name != name
,并在每一侧的x
和y
值上运行距离函数。结果应采用以下格式:
name1, name2, distance
我写了这样的距离函数:
dist <- function(a, b) sqrt((a$x-b$x)^2 + (a$y-b$y)^2)
我尝试使用outer
函数,但它只需要向量,而不是数据表,我尝试在dplyr中使用各种连接但是不成功。
答案 0 :(得分:7)
假设您的数据集如下所示:
d <- data_frame(name = rownames(mtcars), x = mtcars$mpg, y = mtcars$cyl)
尝试两个数据框的所有组合(或与其自身的所有组合)的一般方法是tidyr的crossing
函数(尽管您需要注意重命名列)。之后,您将能够计算距离并进行过滤:
library(dplyr)
library(tidyr)
d %>%
rename(name1 = name, x1 = x, y1 = y) %>%
crossing(d) %>%
rename(name2 = name, x2 = x, y2 = y) %>%
mutate(distance = sqrt((x1 - x2) ^ 2 + (y1 - y2) ^ 2)) %>%
filter(name1 != name2)
在这种特殊情况下,您可以使用我的fuzzyjoin包,特别是distance_join
(您需要GitHub的最新开发版本)。这将根据距离阈值连接两个数据框(在本例中为自连接),并附加一个距离为的附加列:
library(fuzzyjoin)
d %>%
rename(name1 = name) %>%
distance_inner_join(d, max_dist = Inf, distance_col = "distance") %>%
rename(name2 = name) %>%
filter(name1 != name2)
这将给出:
# A tibble: 992 x 7
name1 x.x y.x name2 x.y y.y distance
<chr> <dbl> <dbl> <chr> <dbl> <dbl> <dbl>
1 Mazda RX4 21 6 Mazda RX4 Wag 21.0 6 0.000000
2 Mazda RX4 21 6 Datsun 710 22.8 4 2.690725
3 Mazda RX4 21 6 Hornet 4 Drive 21.4 6 0.400000
4 Mazda RX4 21 6 Hornet Sportabout 18.7 8 3.047950
5 Mazda RX4 21 6 Valiant 18.1 6 2.900000
6 Mazda RX4 21 6 Duster 360 14.3 8 6.992138
7 Mazda RX4 21 6 Merc 240D 24.4 4 3.944617
8 Mazda RX4 21 6 Merc 230 22.8 4 2.690725
9 Mazda RX4 21 6 Merc 280 19.2 6 1.800000
10 Mazda RX4 21 6 Merc 280C 17.8 6 3.200000
# ... with 982 more rows
如果您知道自己并不关心远距离比赛,可以将max_dist
设置为另一个非无限阈值。
答案 1 :(得分:3)
这是一个使用cbind
和dist
(@mrflick提到的函数)的基本R方法。我们在这篇文章的底部创建了一个名为df的data.frame。
请注意,dist
会返回一个下三角矩阵:
dist(df[,-1])
1 2
2 1.414214
3 2.236068 2.236068
我们可以使用combn
创建名称变量的成对比较,然后将结果合并到data.frame中,并为列名称提供setNames
。
dfNew <- setNames(data.frame(t(combn(df$name, 2)),
combn(df$name, 2, function(i) {
dist(df[df$name %in% i, -1])})),
c("var1", "var2", "distance"))
返回
dfNew
var1 var2 distance
1 a b 1.414214
2 a c 2.236068
3 b c 2.236068
请注意,names变量必须是字符,或者您必须将其包装在as.character
函数中才能使其生效。
数据强>
df <- read.table(header=TRUE, text="name, x, y
a, 1, 2
b, 2, 3
c, 3, 1", sep=",", stringsAsFactors=F)