【问题标题】:R Left Outer Join with 0 Fill Instead of NA While Preserving Valid NA's in Left TableR用0填充而不是NA的左外连接,同时在左表中保留有效的NA
【发布时间】:2016-05-13 05:15:47
【问题描述】:

在两个数据表(dt1,dt2)上进行左外连接的最简单方法是什么桌子?

一个常见的答案,例如在 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

在这种微不足道的情况下,可以替换new_col 中的所有元素替换语法,而不是使用data.table 的所有元素替换语法:

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.tabledcast 函数允许用户指定fill 值,所以我认为必须有一种更简单的方法来做到这一点,而我只是没有想到。

建议?

编辑:@jangorecki 在 cmets 中指出,当前在 data.table GitHug page 上打开了一个功能请求,以执行我刚才提到的操作,更新 nomatch=0 语法。应该在data.table的下一个版本中。

【问题讨论】:

  • merge 的末尾或使用[..., nomatch=NA] 链式data.table 查询merge()[is.na(col), col := 0] 进行外部连接。有一个开放的FR,所以nomatch arg 可以处理任意值,目前对于外连接它只能使用NA
  • 很抱歉,我无法理解您的回答。 col 来自哪里?很高兴听到有一个开放的功能请求。我会为它添加我的 +1。
  • col 只是一个正在连接的列
  • 在哪个连接上进行?在这种情况下,x 不是关键吗?如果将col 指定为添加的新列的名称(在本例中为new_col),则您的建议有效,但除非我误解,否则您仍然必须为手动添加的每个新列链接此替换,很多就像我的示例中的 dplyr::mutate 一样,这对于合并的数百个新列是不切实际的。我是不是误会了?

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


【解决方案1】:

您能否使用列索引来仅引用新列,就像 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

【讨论】:

  • 当使用dplyr::left_join 时,如果它是一个data.table,是否保证它们会在结果data.frame 的右边?请注意,在 data.table 示例中,列被插入到键列的右侧,而不是所有现有 x 列的右侧。
  • left_join 可靠地将右侧/第二个表中的列放在右侧。我不知道data.table 在使用它的合并功能时如何对列进行排序。我用data.frame 对象做了这个例子,因为data.table 包在我的最后一行重载了[,但是当我left_join 两个data.tables 时,我得到了相同的列顺序。除了在我的示例中卸载 data.table 以使最后一行运行之外,还可以在加入之前使用 as_data_frame 强制对象 - 或者知道data.table 的人可以使最后一行像在基础 R 中一样工作。
  • 感谢您的回答。我正在处理非常大的数据集,所以我有点担心每次合并时在 data.table 和 data.frame 之间切换(我做了很多)。我会做一些性能测试,看看它是否可行。在data.table v1.9.8 与nomatch 的新选项一起发布之前,您的答案可能是最好的。
【解决方案2】:

目前最干净的方法可能只是在左表 (dt1) 中使用要连接的值为中间表播种,链接 dt2 的合并,将 NA 值设置为 0,将中间表与 dt1 合并。完全可以用data.table完成,不依赖data.frame语法,中间步骤保证第二次合并不会有nomatchNA结果:

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,因为他的解决方案也有效,并且在其他情况下可能更有用。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 这是一个非常简单的答案,正是我所需要的。谢谢!
    • 谢谢,这是一个很大的帮助 :) 一个小改进 - 如果您将 ... 作为函数定义和 left_join 调用的附加参数,那么调用者将能够使用其他参数如by=.
    猜你喜欢
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    • 2017-02-28
    • 2020-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多