【发布时间】:2019-05-06 15:54:16
【问题描述】:
我有这个数据框,我想交叉比较这个数据框内的所有值。
dat <- tibble::tibble(
name = c("a","b","c"),
value = c(1,2,3))
我想比较这个数据框中的所有行对,在这种情况下,我想用较小的数字除以较大的数字。
最终的数据框应如下所示:
a,b,0.5
a,c,0.33
b,c,0.66
有没有办法做到这一点?
【问题讨论】:
我有这个数据框,我想交叉比较这个数据框内的所有值。
dat <- tibble::tibble(
name = c("a","b","c"),
value = c(1,2,3))
我想比较这个数据框中的所有行对,在这种情况下,我想用较小的数字除以较大的数字。
最终的数据框应如下所示:
a,b,0.5
a,c,0.33
b,c,0.66
有没有办法做到这一点?
【问题讨论】:
使用data.table包,我们可以将dat与自己连接,条件是一个值小于另一个值,并计算与连接表的列的比率。
library(data.table)
setDT(dat)
out <-
dat[dat, on = .(value < value),
.(name1 = x.name,
name2 = i.name,
ratio = x.value/i.value)]
out <- out[!is.na(ratio)]
out
# name1 name2 ratio
# 1: a b 0.5000000
# 2: a c 0.3333333
# 3: b c 0.6666667
【讨论】:
as_tibble(setDT(dat)[dat, on = .(value < value), .(name1 = x.name, name2 = i.name, ratio = x.value/i.value)][!is.na(ratio)]) cc:@MKR
一种选择是
v1 <- setNames(dat$value, dat$name)
do.call(rbind, combn(v1, 2, FUN = function(x)
setNames(data.frame(as.list(names(x)), round(Reduce(`/`, x[order(x)]), 2)),
c("col1", "col2", "val")), simplify = FALSE))
# col1 col2 val
#1 a b 0.50
#2 a c 0.33
#3 b c 0.67
或者fuzzyjoin 的选项(灵感来自@IceCreamToucan 的帖子)
library(fuzzyjoin)
fuzzy_inner_join(dat, dat, by = "name", match_fun = list(`<`)) %>%
transmute(col1 = name.x, col2 = name.y, val = value.x/value.y)
# A tibble: 3 x 3
# col1 col2 val
# <chr> <chr> <dbl>
#1 a b 0.5
#2 a c 0.333
#3 b c 0.667
【讨论】:
我们可以使用tidyverse:
library(tidyverse)
dat %>% expand(name, name) %>% cbind(expand(dat, value,value)) %>%
filter(value1>value) %>%
mutate(ratio=value/value1)
#> name name1 value value1 ratio
#> 1 a b 1 2 0.5000000
#> 2 a c 1 3 0.3333333
#> 3 b c 2 3 0.6666667
或者只是在baser中涂鸦:
df <- cbind(expand.grid(dat$name,dat$name), expand.grid(dat$value, dat$value))
df <- df[order(df[,3], -df[,4]),]
df <- df[df[,3] < df[,4],]
df$ratio <- df[,3] / df[,4]
df[,-c(3,4)] -> df
df
#> Var1 Var2 ratio
#> 7 a c 0.3333333
#> 4 a b 0.5000000
#> 8 b c 0.6666667
【讨论】: