2015-08-17 1 views
4

Je suis assez nouveau à R et j'essaye de résumer des colonnes par des groupes basés sur leurs noms. J'ai une trame de données comme celui-ci:Sum toutes les colonnes dont les noms commencent par un modèle, par le groupe

DT <- data.frame(a011=c(0,10,20,0),a012=c(010,10,0,0),a013=c(10,30,0,10), 
a021=c(10,20,20,10),a022=c(0,0,0,10),a023=c(20,0,0,0),a031=c(30,0,10,0), 
a032=c(0,0,10,0),a033=c(20,0,0,0)) 

je voudrais obtenir la somme de toutes les colonnes commençant par « A01 », de toutes les colonnes commençant par « A02 » et toutes les colonnes commençant par « A03 « :

a01tot a02tot a03tot 
    20  30  50 
    50  20  0 
    20  20  20 
    10  20  0 

jusqu'à présent, je l'ai utilisé

DT$a01tot <- rowSums(DT[,grep("a01", names(DT))]) 

et ainsi de suite, mais mon vrai cadre de données a beaucoup plus de groupes et je voudrais éviter d'avoir à écrire une ligne de code pour chaque groupe . Je me demandais s'il est possible d'inclure "a01", "a02", "a03" ... dans un vecteur ou une liste et avoir quelque chose qui ajoute les colonnes "a01tot", "a02tot", "a03tot" ... à le cadre de données automatiquement.

Je sais que ma question est très semblable à celui-ci: R sum of rows for different group of columns that start with similar string, mais la solution fait remarquer là-bas,

cbind(df, t(rowsum(t(df), sub("_.*", "_t", names(df))))) 

ne fonctionne pas dans mon cas parce qu'il n'y a pas un élément commun (comme « _ ") à remplacer (je ne peux pas changer le nom des variables en a01_1, a02_2 etc.).

Passer au format "long" n'est pas non plus une solution viable dans mon cas.

Toute aide sera grandement appréciée.

Répondre

3

Vous pouvez stocker les motifs dans un vecteur et les parcourir en boucle. Avec votre exemple, vous pouvez utiliser quelque chose comme ceci:

patterns <- unique(substr(names(DT), 1, 3)) # store patterns in a vector 
new <- sapply(patterns, function(xx) rowSums(DT[,grep(xx, names(DT)), drop=FALSE])) # loop through 
#  a01 a02 a03 
#[1,] 20 30 50 
#[2,] 50 20 0 
#[3,] 20 20 20 
#[4,] 10 20 0 

Vous pouvez régler les noms comme ceci:

colnames(new) <- paste0(colnames(new), "tot") # rename 
+0

Merci, cela fonctionne très bien avec mon exemple. Lorsque j'essaie de l'appliquer aux données réelles, cependant, j'obtiens une erreur dans rowSums: "'x' doit être un tableau d'au moins deux dimensions". Cela semble être dû au fait que pour l'un des "groupes" il n'y a qu'une seule colonne, donc rowSums obtient un vecteur au lieu d'un cadre de données. Comment pourrais-je résoudre ceci? – Astarte

+0

@Astarte vous pouvez contourner ce problème en ajoutant 'drop = FALSE'. J'ai édité la solution ci-dessus. – Jota

1

Une autre solution possible

library(dplyr) 
library(reshape2) 
library(tidyr) 

DT %>% 
    mutate(id = 1:n()) %>% 
    melt(id.vars = c('id')) %>% 
    mutate(Group = substr(variable, 1, 3)) %>% 
    group_by(id, Group) %>% 
    summarise(tot = sum(value)) %>% 
    spread(Group, tot) %>% 
    select(-id) 

Résultats

Source: local data frame [4 x 3] 

    a01 a02 a03 
1 20 30 50 
2 50 20 0 
3 20 20 20 
4 10 20 0 

Puis comme le suggère @Jota colnames(new) <- paste0(colnames(new), "tot")