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
}