У меня есть две таблицы данных в R, которые имеют одинаковые столбцы (номер, имя и порядок) и идентификатор следующим образом:
library(data.table)
dt1 <- data.table(ids = c(1, 2, 5), col1 = c("A", "B", "F"), col2 = c("B", "F", "G"))
dt2 <- data.table(ids = c(2, 1, 6, 5), col1 = c("B", "A", "K", "L"), col2 = c("F", "G", "M", "G"))
> dt1
ids col1 col2
1: 1 A B
2: 2 B F
3: 5 F G
> dt2
ids col1 col2
1: 2 B F
2: 1 A G
3: 6 K M
4: 5 L G
Я хотел бы знать для каждого столбца, сколько (общих) идентификаторов имеют одинаковое значение. Например, для col1 у нас есть: для ID1 оба значения равны A, для ID2 оба значения равны B, а для ID5 значения различаются, поэтому конечный результат для этого столбца равен 2. У меня есть следующее решение:
joint_dt <- merge(dt1, dt2, by = "ids", suffixes = c("", "_old"))
comp_res <- mapply(function(x, y) sum(x == y), joint_dt[, 2:ncol(dt1)], joint_dt[, (ncol(dt1) + 1):ncol(joint_dt)])
> comp_res
col1 col2
2 2
Это лучший способ сделать то, что я хочу, или мне не хватает какого-то пакета или функции, более предназначенной для этого?
Другой метод заключается в использовании внутреннего соединения для достижения результата:
sapply(c(col1 = "col1",col2 = "col2"), function(x) dt1[dt2, on=c("ids", x), nomatch=0L, .N])
выход:
col1 col2
2 2
вот пример данных, если кому-то интересно время кодов (нет tidyverse
здесь, чтобы время)
library(data.table)
set.seed(0L)
nr <- 1e6L
nc <- 2L
nids <- nr/100
dt1 <- as.data.table(matrix(sample(nids, nr*nc, replace=TRUE), ncol=nc))[, ids := 1:nr]
setnames(dt1, names(dt1), gsub("^V", "col", names(dt1)))
dt2 <- as.data.table(matrix(sample(nids, nr*nc, replace=TRUE), ncol=nc))[, ids := 1:nr]
setnames(dt2, names(dt2), gsub("^V", "col", names(dt2)))
некоторые тайминги для data.table
решений:
временной код:
library(microbenchmark)
microbenchmark(
mtd0 = {
cols <- structure(paste0("col", seq_len(nc)), names=paste0("col", seq_len(nc)))
sapply(cols, function(x) dt1[dt2, on=c("ids", x), nomatch=0L, .N])
},
mtd1=melt(dt1, id.vars = "ids")[ melt(dt2, id.vars = "ids"), ids2 := i.ids, on = .(variable, value)][
!is.na(ids2), .N, by = variable],
times=3L)
тайминги:
Unit: milliseconds
expr min lq mean median uq max neval cld
mtd0 179.4386 186.3906 195.6833 193.3425 203.8057 214.2689 3 a
mtd1 8306.7968 8373.2351 8467.4561 8439.6734 8547.7858 8655.8982 3 b
Подход, использующий объединение расплавленных таблиц данных.
melt(dt1, id.vars = "ids")[ melt(dt2, id.vars = "ids"), ids2 := i.ids, on = .(variable, value)][!is.na(ids2), .N, by = variable][]
variable N
1: col1 2
2: col2 2
Одна tidyverse
возможность может быть:
dt2 %>%
inner_join(dt1, by = c("ids" = "ids")) %>%
gather(var, val, -ids) %>%
separate(var, c("var", "temp")) %>%
count(ids, var, val) %>%
group_by(var) %>%
summarise(n = length(n[n > 1])) %>%
ungroup()
var n
<chr> <int>
1 col1 2
2 col2 2
Я думаю, что map
из purrr
идеально подходит для этого в сочетании с фильтрующим соединением semi_join
из dplyr
, которое возвращает строки, которые существуют в обоих df.
library(purrr)
library(dplyr)
map_dfc(c("col1", "col2"),
~dt1 %>%
semi_join(dt2 %>% select("ids", .x)) %>%
summarise(!!.x := n()))
Результат
col1 col2
1 2 2
Другой tidyverse
подход:
library(tidyverse)
library(data.table)
dt1 <- data.table(ids = c(1, 2, 5), col1 = c("A", "B", "F"), col2 = c("B", "F", "G"))
dt2 <- data.table(ids = c(2, 1, 6, 5), col1 = c("B", "A", "K", "L"), col2 = c("F", "G", "M", "G"))
dt1 %>% gather(col,value1,-ids) %>% # reshape dt1
inner_join(dt2 %>% gather(col,value2,-ids), by=c("ids","col")) %>% # reshape dt2 and join
group_by(col) %>% # for each col value
summarise(res = sum(value1 == value2)) # count matches
# # A tibble: 2 x 2
# col res
# <chr> <int>
# 1 col1 2
# 2 col2 2
Единственная проблема, с которой я столкнулся при объединении, заключается в том, что если типы столбцов отличаются, это вызывает ошибку. Это происходит, когда, например, столбец символов пуст в одном из моих файлов, и fread по умолчанию использует его как логический, а в другом он имеет некоторые значения и является символом. Любые обходные пути в виду? Потенциально, читая все столбцы как символы в fread, я думаю...