web-dev-qa-db-fra.com

Comment ajouter une ligne à un bloc de données avec des totaux?

J'ai un cadre de données où je voudrais ajouter une ligne supplémentaire qui totalise les valeurs pour les colonnes. Par exemple, disons que j'ai ces données:

x <- data.frame(Language=c("C++", "Java", "Python"), 
                Files=c(4009, 210, 35), 
                LOC=c(15328,876, 200), 
                stringsAsFactors=FALSE)    

Les données ressemblent à ceci:

  Language Files   LOC
1      C++  4009 15328
2     Java   210   876
3   Python    35   200

Mon instinct est de faire ceci: 

y <- rbind(x, c("Total", colSums(x[,2:3])))

Et cela fonctionne, il calcule les totaux:

> y
  Language Files   LOC
1      C++  4009 15328
2     Java   210   876
3   Python    35   200
4    Total  4254 16404

Le problème est que les colonnes Files et LOC ont toutes été converties en chaînes:

> y$LOC
[1] "15328" "876"   "200"   "16404"

Je comprends que cela se produit parce que j'ai créé un vecteur c("Total", colSums(x[,2:3]) avec des entrées qui sont à la fois des nombres et des chaînes, et qu'il convertit tous les éléments en un type commun afin que tous les éléments vectoriels soient identiques. Il en va de même pour les colonnes Files et LOC.

Quelle est la meilleure façon de faire cela?

30
Lorin Hochstein

Voir adorn_totals() dans le paquet janitor:

library(janitor)
x %>%
  adorn_totals("row")

#>  Language Files   LOC
#>       C++  4009 15328
#>      Java   210   876
#>    Python    35   200
#>     Total  4254 16404

Les colonnes numériques restent de la classe numeric.

Avertissement: J'ai créé ce package, y compris adorn_totals() qui est destiné à cette tâche

19
Sam Firke

Voici un moyen d'obtenir ce que vous voulez, mais il pourrait très bien exister une solution plus élégante.

rbind(x, data.frame(Language="Total",t(colSums(x[,-1]))))

Pour mémoire, je préfère la réponse de Chase si vous n'avez pas absolument besoin de la colonne Language.

22
Joshua Ulrich

Avez-vous besoin de la colonne Langue dans vos données ou est-il plus approprié de penser à cette colonne en tant que row.names? Cela changerait votre data.frame de 4 observations de 3 variables à 4 observations de 2 variables (Fichiers & LOC). 

x <- data.frame(Files=c(4009, 210, 35), LOC=c(15328,876, 200), row.names=c("C++", "Java", "Python"), stringsAsFactors=F)    
x["Total" ,] <- colSums(x)


> x
       Files   LOC
C++     4009 15328
Java     210   876
Python    35   200
Total   4254 16404
20
Chase

Une façon tidyverse de faire ceci serait d'utiliser bind_rows (ou éventuellement add_rows) et summarise pour calculer les sommes. Ici, le problème est que nous voulons des sommes pour tous sauf un, alors un truc serait:

summarise_all(x, funs(if(is.numeric(.)) sum(.) else "Total"))

En une ligne:

x %>%
  bind_rows(summarise_all(., funs(if(is.numeric(.)) sum(.) else "Total")))
12
Matifou

Essaye ça

y[4,] = c("Total", colSums(y[,2:3]))
9
Prateek Joshi

Si (1) nous n'avons pas besoin du titre "Language" sur la première colonne, nous pouvons le représenter à l'aide de noms de lignes et si (2) il est correct de nommer la dernière ligne comme étant "Sum" plutôt que "Total", nous pouvons utiliser addmargins comme ceci:

rownames(x) <- x$Language
addmargins(as.table(as.matrix(x[-1])), 1)

donnant:

       Files   LOC
C++     4009 15328
Java     210   876
Python    35   200
Sum     4254 16404

Si nous voulons la première colonne intitulée "Language" et la totalité de la ligne intitulée "Total", son un peu plus long:

rownames(x) <- x$Language
Total <- sum
xa <- addmargins(as.table(as.matrix(x[-1])), 1, FUN = Total)
data.frame(Language = rownames(xa), as.matrix(xa[]), row.names = NULL)

donnant:

  Language Files   LOC
1      C++  4009 15328
2     Java   210   876
3   Python    35   200
4    Total  4254 16404
5
G. Grothendieck

Êtes-vous sûr de vouloir vraiment avoir le total des colonnes dans votre bloc de données? Pour moi, l'interprétation du bloc de données dépend maintenant de la ligne. Par exemple, 

  • Rangées 1- (n-1): combien de fichiers sont associés à un particulier language
  • Ligne n: combien de fichiers sont associés à all languages

Cela devient plus confus si vous commencez à sous-définir vos données. Par exemple, supposons que vous souhaitiez savoir quelles langues ont plus de 100 fichiers:

> x = data.frame(Files=c(4009, 210, 35), 
                LOC=c(15328,876, 200), 
                row.names=c("C++", "Java", "Python"), 
                stringsAsFactors=FALSE)    
> x["Total" ,] = colSums(x)
> x[x$Files > 100,]
       Files   LOC
C++    4009 15328
Java    210   876
Total  4254 16404#But this refers to all languages!

La rangée Total est maintenant fausse!

Personnellement, je calculais les sommes de la colonne et les stockais dans un vecteur séparé. 

1
csgillespie

Comme vous mentionnez que ceci est une dernière étape avant d’exporter pour la présentation, vous pouvez avoir des noms de colonne qui incluront des espaces pour plus de clarté (c'est-à-dire "Total général"). Si tel est le cas, les éléments suivants garantiront que le fichier data.frame créé sera lié au jeu de données d'origine sans erreur provoquée par des noms de colonne incohérents:

dfTotals <- data.frame(Language="Total",t(colSums(x[,-1]))))

colnames(dfTotals) <- names(x)  

rbind(x, dfTotals)
1
BobD59

Votre instinct initial fonctionnerait si vous imposiez à vos colonnes le caractère numérique: 

y$LOC <- as.numeric(y$LOC)
y$Files <- as.numeric(y$Files)

Et puis appliquez colSums () et rbind (). 

0
Brandon Bertelsen

Vous pouvez utiliser appliquer pour chaque somme de col

Appliquer (df [-columns que vous ne voulez pas en somme], 2, somme)

Et alors tu peux 

se rapprocher

ces données dans votre df

0
Rohit parihar