R左外连接0填充而不是NA,同时保留左表中的有效NA

时间:2016-02-03 20:08:01

标签: r merge left-join data.table dplyr

在两个数据表(dt1,dt2)上进行左外连接的最简单方法是使用填充值为0(或其他值)而不是NA(默认值)而不覆盖左数据中的有效NA值表

一个常见的答案,例如在this thread中,是使用dplyr::left_joindata.table::mergedata.table的dt2 [dt1]键控列括号进行左外连接语法,然后是第二步,只需将所有NA值替换为联接数据表中的0。例如:

library(data.table);
dt1 <- data.table(x=c('a', 'b', 'c', 'd', 'e'), y=c(NA, 'w', NA, 'y', 'z'));
dt2 <- data.table(x=c('a', 'b', 'c'), new_col=c(1,2,3));
setkey(dt1, x);
setkey(dt2, x);
merged_tables <- dt2[dt1];
merged_tables[is.na(merged_tables)] <- 0;

此方法必然假设dt1中没有需要保留的有效NA值。然而,正如您在上面的示例中所看到的,结果是:

   x new_col y
1: a       1 0
2: b       2 w
3: c       3 0
4: d       0 y
5: e       0 z

但是期望的结果是:

   x new_col y
1: a       1 NA
2: b       2 w
3: c       3 NA
4: d       0 y
5: e       0 z

在这种微不足道的情况下,不是使用data.table所有元素替换上面的语法,只需替换new_col中的NA值:

library(dplyr);
merged_tables <- mutate(merged_tables, new_col = ifelse(is.na(new_col), 0, new_col));

但是,这种方法对于合并了数十个或数百个新列的非常大的数据集并不实用,有时会使用动态创建的列名。即使列名都是提前知道的,列出所有新列并在每个列上进行mutate-style替换也是非常难看的。

必须有更好的方法吗?如果任何dplyr::left_joindata.table::mergedata.table括号的语法都允许用户指定除NA之外的fill值,则可以简单地解决该问题。类似的东西:

merged_tables <- data.table::merge(dt1, dt2, by="x", all.x=TRUE, fill=0);

data.table的{​​{1}}函数允许用户指定dcast值,因此我认为必须有一种更简单的方法来执行此操作,而我只是没想到。< / p>

建议?

编辑:@jangorecki在评论中指出,目前正在data.table GitHug page上打开一个功能请求来完成我刚刚提到的更新fill语法。应该在nomatch=0的下一个版本中。

3 个答案:

答案 0 :(得分:3)

我偶然发现了与dplyr相同的问题并编写了一个解决我问题的小函数。 (解决方案需要tidyr和dplyr)

left_join0 <- function(x, y, fill = 0L){
  z <- left_join(x, y)
  tmp <- setdiff(names(z), names(x))
  z <- replace_na(z, setNames(as.list(rep(fill, length(tmp))), tmp))
  z
}

答案 1 :(得分:2)

您是否可以使用列索引仅引用新列,与left_join一样,它们都在结果data.frame的右侧?这里将是dplyr:

dt1 <- data.frame(x = c('a', 'b', 'c', 'd', 'e'),
                  y = c(NA, 'w', NA, 'y', 'z'),
                  stringsAsFactors = FALSE)
dt2 <- data.frame(x = c('a', 'b', 'c'),
                  new_col = c(1,2,3),
                  stringsAsFactors = FALSE)

merged <- left_join(dt1, dt2)
index_new_col <- (ncol(dt1) + 1):ncol(merged)
merged[, index_new_col][is.na(merged[, index_new_col])] <- 0

> merged
  x    y new_col
1 a <NA>       1
2 b    w       2
3 c <NA>       3
4 d    y       0
5 e    z       0

答案 2 :(得分:1)

目前最干净的方法可能只是种植一个中间表,其中包含要在左表(dt1)中连接的值,链接dt2的合并,将NA值设置为0,将中间表与dt1合并。可以完全使用data.table完成,并且不依赖于data.frame语法,并且中间步骤可确保第二次合并中不会出现nomatch NA结果:

library(data.table);
dt1 <- data.table(x=c('a', 'b', 'c', 'd', 'e'), y=c(NA, 'w', NA, 'y', 'z'));
dt2 <- data.table(x=c('a', 'b', 'c'), new_col=c(1,2,3));
setkey(dt1, x);
setkey(dt2, x);
inter_table <- dt2[dt1[, list(x)]];
inter_table[is.na(inter_table)] <- 0;
setkey(inter_table, x);
merged <- inter_table[dt1];

> merged;
   x new_col  y
1: a       1 NA
2: b       2  w
3: c       3 NA
4: d       0  y
5: e       0  z

这种方法的好处是它不依赖于在右侧添加的新列,而是保留在data.table键控速度优化中。将答案归功于@SamFirke,因为他的解决方案也有效,在其他情况下可能更有用。