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?
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
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
.
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
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")))
Essaye ça
y[4,] = c("Total", colSums(y[,2:3]))
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
Ê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,
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é.
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)
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 ().
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