2010-04-08 13 views
74

Intenté encontrar un built-in para la media geométrica pero no pude.Media geométrica: ¿hay un built-in?

(Obviamente, un built-in no me va a ahorrar nada mientras trabajaba en el shell, ni sospecho que haya ninguna diferencia en la precisión, para los scripts trato de usar complementos tan a menudo como sea posible, donde la ganancia de rendimiento (acumulativo) es a menudo notable.

En caso de que no es uno (cosa que dudo es el caso) Esta es la mía.

gm_mean = function(a){prod(a)^(1/length(a))} 
+9

Tenga cuidado con los números negativos y los desbordamientos. prod (a) se subutilizará o desbordará muy rápidamente. Traté de cronometrar esto usando una gran lista y obtuve Inf rápidamente usando su método vs 1.4 con exp (mean (log (x))); el problema de redondeo puede ser bastante severo. – Tristan

+0

acabo de escribir la función de arriba rápidamente porque estaba seguro de que 5 minutos después de publicar esta Q, alguien me diría que R está incorporado para gm. Entonces no tiene incorporado, por lo que vale la pena tomarse el tiempo para volver a codificar a la luz de sus comentarios. + 1 de mi parte – doug

Respuesta

42

Aquí es una vectorizado, la función cero y NA-tolerante para el cálculo de la media geométrica en R. El verbosa mean cálculo que implica length(x) es necesaria para los casos en los que x contiene valores no positivos.

gm_mean = function(x, na.rm=TRUE){ 
    exp(sum(log(x[x > 0]), na.rm=na.rm)/length(x)) 
} 

Gracias a @ ben-Bolker para destacar la na.rm de paso y @Gregor de asegurarse de que funciona correctamente.

Creo que algunos de los comentarios están relacionados con una falsa equivalencia de NA valores en los datos y ceros. En la aplicación que tenía en mente, son los mismos, pero por supuesto esto no es verdad en general. Por lo tanto, si desea incluir la propagación opcional de ceros y tratar el length(x) de manera diferente en el caso de la eliminación NA, la siguiente es una alternativa ligeramente más larga que la función anterior.

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)) 
    } 
} 

Nota que también comprueba para cualquier valores negativos, y devuelve una más informativo y apropiado NaN respetando que media geométrica no está definida para valores negativos (pero es para ceros). Gracias a los comentaristas que se quedaron en mi caso sobre esto.

+1

¿No sería mejor pasar 'na.rm' como argumento (es decir, dejar que el usuario decida si quiere ser tolerante a NA o no, para coherencia con otras funciones de resumen R)? Estoy nervioso por la exclusión automática de ceros; también lo haría una opción. –

+1

Quizás tenga razón acerca de pasar 'na.rm' como una opción. Actualizaré mi respuesta. En cuanto a la exclusión de ceros, la media geométrica no está definida para los valores no positivos, incluidos los ceros. Lo anterior es una solución común para la media geométrica, en la cual ceros (o en este caso todos los no ceros) reciben un valor ficticio de 1, que no tiene ningún efecto sobre el producto (o equivalentemente, cero en la suma logarítmica). –

+0

* Quise decir una solución común para valores no positivos, siendo el cero el más común cuando se usa la media geométrica. –

70

no, pero hay algunas personas que han escrito uno, como here.

Otro posible lity es usar esto:

exp(mean(log(x))) 
+4

ese enlace está muerto – eddi

+0

Otra ventaja de usar exp (mean (log (x))) es que puede trabajar con listas largas de números grandes, lo cual es problemático cuando se usa la fórmula más obvia usando prod(). Tenga en cuenta que prod (a)^(1/length (a)) y exp (mean (log (a))) dan la misma respuesta. – lukeholman

5

Uso exactamente lo que dice Mark. De esta forma, incluso con tapply, puede usar la función incorporada mean, ¡no necesita definir la suya! Por ejemplo, para calcular por grupos medias geométricas de valor $ datos:

exp(tapply(log(data$value), data$group, mean)) 
10

puede utilizar psych paquete y llame geometric.mean en función de eso.

+0

'psych :: geometric.mean()' – smci

+0

Estas funciones deberían tomar la serie y no su crecimiento, al menos como una opción, diría yo. –

10

El

exp(mean(log(x))) 

funcionará a menos que haya un 0 en x. Si es así, el registro producirá -Inf (-infinito), que siempre resulta en una media geométrica de 0.

Una solución es quitar el valor -Inf antes de calcular la media:

geo_mean <- function(data) { 
    log_data <- log(data) 
    gm <- exp(mean(log_data[is.finite(log_data)])) 
    return(gm) 
} 

Puede use un trazador de líneas para hacer esto, pero significa calcular el registro dos veces, lo que es ineficiente.

exp(mean(log(i[is.finite(log(i))]))) 
+0

por qué calcular el registro dos veces cuando puede: exp (mean (x [x! = 0])) – zzk

+0

ambos enfoques obtienen la media incorrecta, porque el denominador de la media, 'suma (x)/longitud (x) 'es incorrecto si filtra x y luego lo pasa a' mean'. –

+0

Creo que filtrar es una mala idea a menos que explícitamente quieras hacerlo (p. Ej., Si estuviera escribiendo una función * de propósito general no convertiría el filtrado en el predeterminado) - Aceptar si se trata de una pieza única de código y has pensado con mucho cuidado en qué significa realmente filtrar ceros en el contexto de tu problema (!) –

3

En caso de que le falten valores en sus datos, este no es un caso raro. necesita agregar un argumento más. Puede probar los siguientes códigos.

exp(mean(log(i[is.finite(log(i))]),na.rm=T))