Добавить (не объединить!) два фрейма данных с неравными строками и столбцами
Я хочу эффективно суммировать записи двух фреймов данных, хотя фреймы данных не гарантированно имеют одинаковые размеры или имена столбцов. Слияние-это не то, что мне нужно. Вместо этого я хочу создать выходной объект со всеми именами строк и столбцов, которые принадлежат любому из добавленных фреймов данных. В каждой позиции этого вывода я хочу использовать следующую логику для вычисляемого значения:
- если сопряжение строк / столбцов принадлежит обоим входным кадрам данных Я хочу, чтобы результат включал их сумму
- если спаривание строки / столбца принадлежит только одному входному фрейму данных, я хочу включить это значение в выход
- если спаривание строки / столбца не принадлежит ни одной входной матрице, я хочу иметь 0 в этой позиции на выходе.
в качестве примера можно рассмотреть следующие входные данные:
df1 = data.frame(x = c(1,2,3), y = c(4,5,6))
rownames(df1) = c("a", "b", "c")
df2 = data.frame(x = c(7,8), z = c(9,10), w = c(2, 3))
rownames(df2) = c("a", "d")
> df1
x y
a 1 4
b 2 5
c 3 6
> df2
x z w
a 7 9 2
d 8 10 3
Я хочу, чтобы конечный результат был
> df2
x y z w
a 8 4 9 2
b 2 5 0 0
c 3 6 0 0
d 8 0 10 3
что я сделал до сих пор -
bind_rows / bind_cols в dplyr может бросить следующее: "Ошибка: несовместимое количество строк (3, ожидание 2)"
у меня есть дублированные имена столбцов, поэтому "merge" также не работает для моих целей - по какой-то причине возвращает пустой df.
5 ответов
кажется, что вы можете объединить имена строк, а затем позаботиться о суммах и преобразовании NA
до нуля с некоторым дополнительным munging:
library(dplyr)
df.new = df1 %>% add_rownames %>%
full_join(df2 %>% add_rownames, by="rowname") %>%
mutate_each(funs(replace(., which(is.na(.)), 0))) %>%
mutate(x = x.x + x.y) %>%
select(rowname,x,y,z,w)
или с гораздо более элегантным и расширяемым решением @DavidArenburg:
df.new = df1 %>% add_rownames %>%
full_join(df2 %>% add_rownames) %>%
group_by(rowname) %>%
summarise_each(funs(sum(., na.rm = TRUE)))
df.new
rowname x y z w
1 a 8 4 9 2
2 b 2 5 0 0
3 c 3 6 0 0
4 d 8 0 10 3
это похоже на какое-то простое слияние общих имен столбцов (+ имена строк), а затем простое агрегирование, вот как я бы справился с этим
library(data.table)
merge(setDT(df1, keep.rownames = TRUE), # Convert to data.table + keep rows
setDT(df2, keep.rownames = TRUE), # Convert to data.table + keep rows
by = intersect(names(df1), names(df2)), # merge on common column names
all = TRUE)[, lapply(.SD, sum, na.rm = TRUE), by = rn] # Sum all columns by group
# rn x y z w
# 1: a 8 4 9 2
# 2: b 2 5 0 0
# 3: c 3 6 0 0
# 4: d 8 0 10 3
являются довольно прямым базовым решением R
df1$rn <- row.names(df1)
df2$rn <- row.names(df2)
res <- merge(df1, df2, all = TRUE)
rowsum(res[setdiff(names(res), "rn")], res[, "rn"], na.rm = TRUE)
# x y z w
# a 8 4 9 2
# b 2 5 0 0
# c 3 6 0 0
# d 8 0 10 3
во-первых, я бы схватил имена всех строк и столбцов нового объекта:
(all.rows <- unique(c(row.names(df1), row.names(df2))))
# [1] "a" "b" "c" "d"
(all.cols <- unique(c(names(df1), names(df2))))
# [1] "x" "y" "z" "w"
затем я бы построил выходную матрицу с этими строками и именами столбцов (с матричными данными, инициализированными для всех 0s), добавив df1
и df2
к соответствующим частям этой матрицы.
out <- matrix(0, nrow=length(all.rows), ncol=length(all.cols))
rownames(out) <- all.rows
colnames(out) <- all.cols
out[row.names(df1),names(df1)] <- unlist(df1)
out[row.names(df2),names(df2)] <- out[row.names(df2),names(df2)] + unlist(df2)
out
# x y z w
# a 8 4 9 2
# b 2 5 0 0
# c 3 6 0 0
# d 8 0 10 3
используя xtabs
на расплавленных / сложенных фреймах данных:
out <- rbind(cbind(rn=rownames(df1),stack(df1)), cbind(rn=rownames(df2),stack(df2)))
as.data.frame.matrix(xtabs(values ~ rn + ind, data=out))
# x y w z
#a 8 4 2 9
#b 2 5 0 0
#c 3 6 0 0
#d 8 0 3 10
Я не уверен, что принятый (или альтернативный метод слияния) является лучшим. Это даст неправильные результаты, если у вас есть общие строки, они будут объединены, а не суммированы.
Это может быть показано trivialy путем изменения df2 на:
df2 = data.frame(x = c(1,2), y = c(4,5), z = c(9,10), w = c(2, 3))
rownames(df2) = c("a", "d")
ожидаемые результаты:
rn x y z w
1: a 2 8 9 2
2: b 2 5 0 0
3: c 3 6 0 0
4: d 2 5 10 3
фактические результаты
merge(setDT(df1, keep.rownames = TRUE),
setDT(df2, keep.rownames = TRUE),
by = intersect(names(df1), names(df2)),
all = TRUE)[, lapply(.SD, sum, na.rm = TRUE), by = rn]
rn x y z w
1: a 1 4 9 2
2: b 2 5 0 0
3: c 3 6 0 0
4: d 2 5 10 3
вам нужно объединить как внешнее соединение с внутренним соединением (или левое/правое соединение, объединить все=T/all=F). Или альтернативно используя rbind plyr.заполнить :
базовое решение R
res <- rbind.fill(df1,df2)
rowsum(res[setdiff(names(res), "rn")], res[, "rn"], na.rm = TRUE)
таблица данных
as.data.table(rbind.fill(
setDT(df1, keep.rownames = TRUE),
setDT(df2, keep.rownames = TRUE)
))[, lapply(.SD, sum, na.rm = TRUE), by = rn]
Я предпочитаю rbind.заполните метод, как вы можете "объединить" > 2 фрейма данных, используя тот же синтаксис.