2016-02-02 13 views
8

Desidero sommare in modo efficiente le voci di due frame di dati, sebbene non sia garantito che i frame di dati abbiano le stesse dimensioni o nomi di colonna. Unire non è davvero quello che sto cercando qui. Invece voglio creare un oggetto di output con tutti i nomi di riga e di colonna che appartengono a uno dei frame di dati aggiunti. In ciascuna posizione di tale uscita, voglio usare la logica seguente per il valore calcolato:Aggiungere (non unire!) Due frame di dati con righe e colonne non uguali

  • Se un abbinamento riga/colonna appartiene ad entrambi i frame di dati di ingresso voglio l'uscita di includere la loro somma
  • Se un accoppiamento di righe/colonne appartiene a un solo frame di dati di input Voglio includere quel valore nell'output
  • Se un accoppiamento riga/colonna non appartiene a nessuna matrice di input, voglio avere 0 in quella posizione nell'output.

A titolo di esempio, si considerino i seguenti frame di dati in ingresso:

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 

Voglio che il risultato finale sia

> 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 

Quello che ho fatto finora -

bind_rows/bind_cols in dplyr può lanciare il seguente: "Errore: numero incompatibile di righe (3, in attesa di 2)"

Ho nomi di colonne duplicati, quindi "unione" non funziona per i miei scopi - restituisce un df vuoto per qualche motivo.

risposta

4

Sembra come è possibile unire sulle rownames, quindi si prenderà cura delle somme e la conversione di NA a zero con una certa munging aggiuntivo:

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) 

Oppure, con @ DavidArenburg è molto più elegante ed estensibile soluzione:

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 
+0

Bene, @DavidArenburg merita davvero il merito, dato che lo stavo rendendo molto più complicato di quanto non fosse necessario. – eipi10

2

Innanzitutto, voglio afferrare i nomi di tutte le righe e le colonne della nuova entità:

(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" 

Poi sarebbe costruire una matrice di uscita a tali righe e delle colonne (con dati di matrice inizializzati a tutti 0), aggiungendo df1 e df2 alle relative parti di tale matrice.

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 
+1

Grazie per questo!Preferisco questa soluzione per eipi10 perché non richiede una ridenominazione, che avrei bisogno di fare dinamicamente. Mentre di solito preferisco dplyr, questa soluzione ha molto senso. –

3

Ciò sembra come un certo tipo di una semplice unione su nomi di colonna comune (+ nomi riga) e poi una semplice aggregazione, questo è come vorrei virare le Questa

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 

Sei una base avanzata soluzione piuttosto semplice 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 
+0

Molto elegante. Ho dimenticato l'argomento all = TRUE in "unione". –

+0

'unione 'utilizzerà per impostazione predefinita' intersect'ion dei nomi di ciascun set di dati. Puoi lasciare 'by =' vuoto e ottenere lo stesso risultato. – thelatemail

+0

@thelatemail yeah Ho dimenticato che è il valore predefinito in base. In 'data.table' non funzionerà se non hai impostato le chiavi. –

1

Utilizzando xtabs sul fuso frame di dati/impilati:

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 
Problemi correlati