2010-04-08 8 views
74

Ich habe versucht, eine eingebaute für geometrische Mittel zu finden, konnte aber nicht.Geometric Mean: Gibt es eine eingebaute?

(Offensichtlich spart mir ein eingebautes Programm keine Zeit während der Arbeit in der Shell, noch vermute ich, dass es einen Unterschied in der Genauigkeit gibt; bei Skripten versuche ich so oft wie möglich eingebaute zu verwenden, wo der (kumulativ) Performance-Gewinn ist oft spürbar.

Falls es nicht ein (was ich bezweifle, dass der Fall ist) hier ist meins.

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

Vorsicht bei negativen Zahlen und Überläufen. prod (a) wird sehr schnell unter oder überlaufen. Ich habe versucht, das mit einer großen Liste zu takten und schnell Inf mit der Methode vs 1.4 mit exp (Mittelwert (log (x))); das Rundungsproblem kann ziemlich schwerwiegend sein. – Tristan

+0

Ich schrieb die obige Funktion schnell, weil ich mir sicher war, dass 5 Minuten nach der Veröffentlichung dieses Q jemand mir sagen würde, dass R für GM integriert ist. Also kein eingebautes, also ist es sicher wert, die Zeit zu nehmen, um im Lichte Ihrer Anmerkungen neu zu kodieren. + 1 von mir. – doug

Antwort

42

Hier ist ein vektorisiert, nullter und NA tolerante Funktion für die Einbeziehung length(x) geometrisches Mittel in R. Die ausführliche mean Berechnung der Berechnung für die Fälle erforderlich ist, wo x kraft- Werte enthält.

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

Dank @ ben-Bolker für unter Hinweis auf die na.rm Passthrough und @Gregor für dafür, dass es korrekt funktioniert.

Ich denke, einige der Kommentare beziehen sich auf eine falsche Äquivalenz von NA Werte in den Daten und Nullen. In der Anwendung, die ich mir vorgenommen habe, sind sie gleich, aber das stimmt natürlich nicht. Wenn Sie also die optionale Weitergabe von Nullen einbeziehen und die length(x) beim Entfernen von NA anders behandeln möchten, ist die folgende etwas längere Alternative zur obigen Funktion.

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

Beachten Sie, dass es auch für etwaige negative Werte überprüft und gibt ein informativer und entsprechende NaN respektieren, dass geometrische Mittel nicht für negative Werte definiert ist (aber ist für Nullen). Danke an die Kommentatoren, die über meinen Fall geblieben sind.

+1

wäre es nicht besser, 'na.rm' als Argument zu übergeben (dh den Benutzer entscheiden zu lassen, ob er NA-tolerant sein will oder nicht, auf Konsistenz mit anderen R-Zusammenfassungsfunktionen)? Ich bin nervös, wenn ich Nullen automatisch ausschließe - das würde ich auch machen. –

+1

Vielleicht hast du recht damit, 'na.rm' als Option zu übergeben. Ich werde meine Antwort aktualisieren. Was das Ausschließen von Nullen betrifft, ist das geometrische Mittel für nichtpositive Werte, einschließlich Nullen, nicht definiert. Das Obige ist eine allgemeine Festlegung für das geometrische Mittel, bei dem Nullen (oder in diesem Fall alle Nicht-Nullen) einen Dummy-Wert von 1 erhalten, der keine Auswirkung auf das Produkt hat (oder äquivalent null in der logarithmischen Summe). –

+0

* Ich meinte eine allgemeine Korrektur für nicht positive Werte, wobei Null am häufigsten verwendet wird, wenn das geometrische Mittel verwendet wird. –

70

Nein, aber es gibt ein paar Leute, die geschrieben haben, B. here.

Eine andere Möglichkeit lity ist dies zu verwenden:

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

dieser Link ist tot – eddi

+0

Ein anderer Vorteil der Verwendung von exp (Mittelwert (log (x))) ist, dass Sie mit langen Listen von großen Zahlen arbeiten können, was problematisch ist, wenn die offensichtlicher Formel mit prod() verwendet wird. Beachten Sie, dass prod (a)^(1/Länge (a)) und exp (Mittelwert (log (a))) die gleiche Antwort geben. – lukeholman

5

Ich benutze genau was Mark sagt. Auf diese Weise können Sie sogar mit tapply die eingebaute mean Funktion verwenden, die Sie nicht definieren müssen! Zum Beispiel pro-Gruppe geometrische Mittel der Daten $ Wert zu berechnen:

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

können Sie psych Paket verwenden und geometric.mean Funktion in das nennen.

+0

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

+0

Diese Funktionen sollten die Serie und nicht ihr Wachstum nehmen, zumindest als eine Option, würde ich sagen. –

10

Die

exp(mean(log(x))) 

wird funktionieren, wenn es eine 0 in x ist. Wenn ja, wird das Protokoll erzeugen -Inf (Unbegrenzte), die immer in einem geometrischen Mittelwert ergibt 0.

Eine Lösung ist die -Inf Wert zu entfernen, bevor der Mittelwert Berechnung:

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

Sie können Verwenden Sie einen Einzeiler, um dies zu tun, aber es bedeutet, das Protokoll zweimal zu berechnen, was ineffizient ist.

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

warum berechnen Sie das Protokoll zweimal, wenn Sie tun können: exp (Mittelwert (x [x! = 0])) – zzk

+0

beide Ansätze bekommen den Mittelwert falsch, weil der Nenner für den Mittelwert, Summe (x)/Länge (x) 'ist falsch, wenn Sie x filtern und dann an' mean' übergeben. –

+0

Ich denke, Filterung ist eine schlechte Idee, es sei denn, Sie wollen es explizit (zB wenn ich eine * Allzweck * -Funktion schreibe, würde ich nicht die Standard-Filterung machen) - OK, wenn dies ein einmaliger Code ist und Sie haben sehr genau darüber nachgedacht, was das Filtern von Nullen im Kontext Ihres Problems bedeutet (!) –

3

Falls in Ihren Daten Werte fehlen, ist dies kein seltener Fall. müssen Sie ein weiteres Argument hinzufügen. Sie können folgende Codes versuchen.

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