R Left Outer Присоединиться к 0 Заполнить вместо NA При сохранении действительной NA в левой таблице

Каков самый простой способ сделать левое внешнее соединение на двух таблицах данных (dt1, dt2), причем значение заливки равно 0 (или какое-либо другое значение) вместо NA (по умолчанию) без перезаписывания действительных значений NA в левых данных таблица?

Общим ответом, таким как этот поток, является выполнение левого внешнего соединения с помощью dplyr::left_join или data.table::merge или data.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 все элементы заменяют синтаксис, как указано выше, только значения NA в new_col могут быть заменены:

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

Однако этот подход нецелесообразен для очень больших наборов данных, где сгруппированы десятки или сотни новых столбцов, иногда с динамически создаваемыми именами столбцов. Даже если имена столбцов были известны заранее, очень уродливо отобразить все новые столбцы и заменить их на мутате.

Должен быть лучший способ? Проблема будет просто решена, если синтаксис любого из скобок dplyr::left_join, data.table::merge или data.table легко разрешил пользователю указывать значение fill, отличное от NA. Что-то вроде:

merged_tables <- data.table::merge(dt1, dt2, by="x", all.x=TRUE, fill=0);
Функция

data.table dcast позволяет пользователю указать значение fill, поэтому я считаю, что должен быть более простой способ сделать это, о котором я просто не думаю.

Предложения?

EDIT: @jangorecki указала в комментариях, что на странице data.table GitHug есть запрос функции, чтобы сделать точно что я только что упомянул, обновив синтаксис nomatch=0. Должен быть в следующем выпуске data.table.

Ответ 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

Ответ 2

Самый чистый способ в настоящее время может просто состоять в том, чтобы выровнять промежуточную таблицу со значениями, которые должны быть объединены в левую таблицу (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, потому что его решение также работает и может быть более полезным в других контекстах.

Ответ 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
}