J'ai essayé de trouver une moyenne géométrique intégrée, mais je n'ai pas réussi.
(Évidemment, un intégré ne me fera pas gagner de temps tout en travaillant dans le shell, et je ne soupçonne pas qu'il y ait une différence de précision; pour les scripts, j'essaie d'utiliser les intégrés aussi souvent que possible, où le (cumulatif) le gain de performance est souvent perceptible.
Au cas où il n'y en aurait pas (ce dont je doute), voici le mien.
gm_mean = function(a){prod(a)^(1/length(a))}
Voici une fonction vectorisée, tolérante à zéro et NA pour le calcul de la moyenne géométrique dans R. Le calcul détaillé mean
impliquant length(x)
est nécessaire pour les cas où x
contient non -valeurs positives.
gm_mean = function(x, na.rm=TRUE){
exp(sum(log(x[x > 0]), na.rm=na.rm) / length(x))
}
Merci à @ ben-bolker d'avoir noté le pass-through na.rm
Et à @Gregor de s'assurer qu'il fonctionne correctement.
Je pense que certains des commentaires sont liés à une fausse équivalence des valeurs de NA
dans les données et les zéros. Dans l'application que j'avais en tête, ce sont les mêmes, mais bien sûr ce n'est généralement pas vrai. Ainsi, si vous souhaitez inclure la propagation facultative des zéros et traiter la length(x)
différemment dans le cas de la suppression de NA
, ce qui suit est une alternative légèrement plus longue à la fonction ci-dessus.
gm_mean = function(x, na.rm=TRUE, zero.propagate = FALSE){
if(any(x < 0, na.rm = TRUE)){
return(NaN)
}
if(zero.propagate){
if(any(x == 0, na.rm = TRUE)){
return(0)
}
exp(mean(log(x), na.rm = na.rm))
} else {
exp(sum(log(x[x > 0]), na.rm=na.rm) / length(x))
}
}
Notez qu'il vérifie également les valeurs négatives et renvoie un NaN
plus informatif et approprié en respectant que la moyenne géométrique n'est pas définie pour les valeurs négatives (mais pour les zéros). Merci aux commentateurs qui sont restés sur mon cas à ce sujet.
Non, mais il y a quelques personnes qui en ont écrit un, comme ici .
Une autre possibilité consiste à utiliser ceci:
exp(mean(log(x)))
Le
exp(mean(log(x)))
fonctionnera sauf s'il y a un 0 dans x. Si c'est le cas, le journal produira -Inf (-Infinite), ce qui donne toujours une moyenne géométrique de 0.
Une solution consiste à supprimer la valeur -Inf avant de calculer la moyenne:
geo_mean <- function(data) {
log_data <- log(data)
gm <- exp(mean(log_data[is.finite(log_data)]))
return(gm)
}
Vous pouvez utiliser une ligne unique pour ce faire, mais cela signifie calculer le journal deux fois, ce qui est inefficace.
exp(mean(log(i[is.finite(log(i))])))
vous pouvez utiliser le package psych
et appeler geometric.mean
fonction en cela.
J'utilise exactement ce que dit Mark. De cette façon, même avec tapply, vous pouvez utiliser la fonction intégrée mean
, pas besoin de définir la vôtre! Par exemple, pour calculer des moyennes géométriques par groupe de données $ value:
exp(tapply(log(data$value), data$group, mean))
Dans le cas où il manque des valeurs dans vos données, ce n'est pas un cas rare. vous devez ajouter un argument supplémentaire. Vous pouvez essayer les codes suivants.
exp(mean(log(i[is.finite(log(i))]),na.rm=T))
Le package EnvStats a une fonction pour geoMean et geoSd
Voici ma version. Il présente les caractéristiques suivantes qui le distinguent de la réponse actuellement acceptée par Paul McMurdie:
na.rm == TRUE
, Les valeurs NA
sont ignorées dans le dénominateur - d'où l'utilisation de valeurs non manquantes comptent la variable values.count
Dans le dénominateur au lieu de length(x)
.NaN
et génériques NA
, avec un paramètre .rm
Pour chacune. Par défaut, NaN
s sont "mauvais", tout comme les nombres négatifs sont mauvais, donc NaN
est retourné. Avoir deux paramètres pour gérer les valeurs manquantes n'est évidemment pas idéal, mais la façon dont j'ai défini les valeurs par défaut pour ces paramètres et organisé les cas dans l'instruction case_when
Devrait (espérons-le) éviter la possibilité d'un comportement inattendu.eta
qui gère les zéros. eta
par défaut à NA_real_
, auquel cas les zéros sont comptés dans le dénominateur mais ne sont pas propagés (analogue au paramètre facultatif zero.propagate = FALSE
dans la réponse acceptée). Lorsqu'un nombre positif est passé, eta
fonctionne comme une constante artificielle à ajouter à x
(mais uniquement dans le cas où x
contient des zéros). Lorsqu'un autre nombre est passé (vraisemblablement 0), les zéros sont propagés, tout comme lorsque zero.propagate
Est égal à TRUE
dans la réponse acceptée.Je suis sûr que des ajustements peuvent être nécessaires (par exemple, il peut être préférable d'ajouter eta
(étant donné que eta
est un nombre positif), qu'il y ait ou non des zéros). J'ai pensé même à la fonction de choisir dynamiquement une valeur pour eta
basée sur x
mais j'ai opté pour ne pas ajouter de complexité supplémentaire.
suppressMessages(library(dplyr))
geomean <- function(x, na.rm = TRUE, nan.rm = FALSE, eta = NA_real_) {
nan.count <- is.nan(x) %>%
sum()
na.count <- is.na(x) %>%
sum()
value.count <- !is.na(x) %>%
sum()
case_when(
#Handle cases when there are negative values, all values are missing, or
#missing values are not tolerated.
(nan.count > 0 & !nan.rm) | any(x < 0, na.rm = TRUE) ~ NaN,
(na.count > 0 & !na.rm) | value.count == 0 ~ NA_real_,
#Handle cases when non-missing values are either all positive or all zero.
#In these cases the eta parameter is irrelevant and therefore ignored.
all(x > 0, na.rm = TRUE) ~ exp(mean(log(x), na.rm = TRUE)),
all(x == 0, na.rm = TRUE) ~ 0,
#All remaining cases are cases when there are a mix of positive and zero values.
#By default, we do not use an artificial constant or propagate zeros.
is.na(eta) ~ exp(sum(log(x[x > 0]), na.rm = TRUE) / value.count),
eta > 0 ~ exp(mean(log(x + eta), na.rm = TRUE)) - eta,
TRUE ~ 0 #only propagate zeroes when eta is set to 0 (or less than 0)
)
}