Geometrik Ortalama: yerleşik var mı?


106

Geometrik ortalama için bir yerleşik bulmaya çalıştım ama bulamadım.

(Açıkçası bir yerleşik, kabukta çalışırken bana hiç zaman kazandırmayacaktır ve doğrulukta herhangi bir fark olduğundan şüphelenmiyorum; komut dosyaları için yerleşikleri olabildiğince sık kullanmaya çalışıyorum, burada (kümülatif) performans kazancı genellikle fark edilir.

Bir tane olmaması durumunda (ki durumdan şüpheliyim) işte benim.

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

11
Negatif sayılara ve taşmalara dikkat edin. prod (a) çok hızlı bir şekilde altından veya taşar. Bunu büyük bir liste kullanarak zamanlamaya çalıştım ve hızlı bir şekilde Inf'a karşı exp (ortalama (log (x))); yuvarlama sorunu oldukça şiddetli olabilir.
Tristan

Yukarıdaki işlevi hızlı bir şekilde yazdım çünkü bu Q'yu gönderdikten 5 dakika sonra birinin bana R'nin gm için yerleşik olduğunu söyleyeceğinden emindim. Yerleşik olmadığı için, açıklamalarınızın ışığında yeniden kodlama yapmaya zaman ayırmaya değer. Benden + 1.
doug

1
Bu geometrik ortalama ve yerleşik olanı 9 yıl sonra etiketledim .
smci

Yanıtlar:


79

İşte R'de geometrik ortalamayı hesaplamak için vektörleştirilmiş, sıfır ve NA toleranslı bir fonksiyon . Pozitif olmayan değerler içeren durumlar için ayrıntılı meanhesaplama length(x)gereklidir x.

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

@ Ben-bolker'a teşekkürler na.rm ve doğru çalıştığından emin olduğu .

Bazı yorumların NAverilerdeki değerlerin yanlış eşdeğerliği ve sıfırlarla ilgili olduğunu düşünüyorum. Aklımdaki uygulamada bunlar aynı, ama elbette bu genel olarak doğru değil. Bu nedenle, isteğe bağlı sıfır yayılımını dahil etmek ve kaldırma length(x)durumunda farklı şekilde davranmak istiyorsanız NA, aşağıdaki işleve biraz daha uzun bir alternatiftir.

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

Ayrıca negatif değerleri kontrol NaNettiğini ve geometrik ortalamanın negatif değerler için tanımlanmadığını (ancak sıfırlar için olduğunu) dikkate alarak daha bilgilendirici ve uygun bir döndürdüğünü unutmayın. Bu konuda davamda kalan yorumculara teşekkürler.


2
na.rmBir argüman olarak geçmek daha iyi olmaz mıydı (yani, diğer R özet işlevleriyle tutarlılık için NA'ya toleranslı olup olmayacağına kullanıcının karar vermesine izin verin)? Sıfırları otomatik olarak dışlamak konusunda endişeliyim - bunu da bir seçenek haline getirirdim.
Ben Bolker

1
Belki de na.rmbir seçenek olarak pas geçme konusunda haklısınız . Cevabımı güncelleyeceğim. Sıfırları hariç tutmaya gelince, sıfırlar dahil pozitif olmayan değerler için geometrik ortalama tanımsızdır. Yukarıdakiler, sıfırların (veya bu durumda tüm sıfır olmayanların) ürün üzerinde hiçbir etkisi olmayan (veya eşdeğer olarak, logaritmik toplamda sıfır) 1'lik bir kukla değer verildiği geometrik ortalama için ortak bir düzeltmedir.
Paul McMurdie

* Pozitif olmayan değerler için ortak bir düzeltmeyi kastetmiştim, geometrik ortalama kullanıldığında en yaygın olan sıfırdır.
Paul McMurdie

1
Kişisel na.rmkodlu olarak geçiş çalışmaları değil ... bkz gm_mean(c(1:3, NA), na.rm = T). Sen kaldırmak için gereken & !is.na(x)vektör alt kümeden ve ilk arg beri sumolduğu ..., sen geçmesi gerekiyor na.rm = na.rmadıyla ve ayrıca dışlamak gerekir 0'ler ve NAde vektör gelen' ın lengthçağrısı.
Gregor Thomas

2
Dikkat: için xsadece sıfır (s) içeren, gibi x <- 0, exp(sum(log(x[x>0]), na.rm = TRUE)/length(x))verir 1mantıklı değil geometrik ortalama, için.
adatum

88

Hayır, ama burada olduğu gibi bir tane yazmış birkaç kişi var .

Başka bir olasılık da bunu kullanmaktır:

exp(mean(log(x)))

Exp (mean (log (x))) kullanmanın bir başka avantajı da, büyük sayılardan oluşan uzun listelerle çalışabilmenizdir; bu, prod () kullanarak daha belirgin formülü kullanırken sorunludur. Prod (a) ^ (1 / uzunluk (a)) ve exp (ortalama (log (a))) aynı cevabı verir.
lukeholman

bağlantı düzeltildi
PatrickT


12

exp(mean(log(x)))

x'te 0 olmadığı sürece çalışacaktır. Eğer öyleyse, günlük her zaman 0 geometrik ortalama ile sonuçlanan -Inf (-Infinite) üretecektir.

Çözümlerden biri, ortalamayı hesaplamadan önce -Inf değerini kaldırmaktır:

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

Bunu yapmak için tek satırlık kullanabilirsiniz, ancak bu, günlüğü iki kez hesaplamak anlamına gelir ki bu verimsizdir.

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

yapabildiğiniz zaman günlüğü neden iki kez hesaplayın: exp (ortalama (x [x! = 0]))
zzk

1
her iki yaklaşım da ortalamayı yanlış anlıyor, çünkü ortalamanın paydası, sum(x) / length(x)x'i filtreler ve sonra ona geçirirseniz yanlıştır mean.
Paul McMurdie

Açıkça yapmak istemediğiniz sürece filtrelemenin kötü bir fikir olduğunu düşünüyorum (örneğin, genel amaçlı bir işlev yazıyor olsaydım, filtrelemeyi varsayılan yapmazdım) - Tamam bu tek seferlik bir kod parçasıysa ve siz sıfırları filtrelemenin sorunun bağlamında aslında ne anlama geldiğini çok dikkatlice düşündüm (!)
Ben Bolker

Tanım gereği, sıfır içeren bir sayı kümesinin geometrik ortalaması sıfır olmalıdır! math.stackexchange.com/a/91445/221143
Chris

6

Tam olarak Mark'ın söylediğini kullanıyorum. Bu şekilde, kılavuz çekme ile bile yerleşik meanişlevi kullanabilirsiniz, sizinkini tanımlamanıza gerek kalmaz! Örneğin, $ değer verisinin grup başına geometrik ortalamasını hesaplamak için:

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

3

Bu sürüm diğer cevaplardan daha fazla seçenek sunar.

  • Kullanıcının (gerçek) sayı olmayan sonuçları ve mevcut olmayanları ayırt etmesine olanak tanır. Negatif sayılar varsa, cevap gerçek bir sayı olmayacağından NaNdöndürülür. Tüm NAdeğerler buysa , işlev, NA_real_gerçek bir değerin tam anlamıyla mevcut olmadığını yansıtmak için geri dönecektir . Bu ince bir farktır, ancak (biraz) daha sağlam sonuçlar verebilir.

  • İlk isteğe bağlı parametrenin zero.rm, kullanıcının çıktıyı sıfır yapmadan sıfırları etkilemesine izin vermesi amaçlanmıştır. Eğer zero.rmayarlandığında FALSEve etaayarlanırsa NA_real_(varsayılan değerine), sıfırlar birine doğru sonucu küçülme etkisi vardır. Bunun için herhangi bir teorik gerekçem yok - sadece sıfırları görmezden gelmek değil, sonucu otomatik olarak sıfırlamayı içermeyen "bir şeyler yapmak" daha mantıklı görünüyor.

  • etaaşağıdaki tartışmadan esinlenerek sıfırları ele almanın bir yoludur: https://support.bioconductor.org/p/64014/

geomean <- function(x,
                    zero.rm = TRUE,
                    na.rm = TRUE,
                    nan.rm = TRUE,
                    eta = NA_real_) {
    nan.count <- sum(is.nan(x))
     na.count <- sum(is.na(x))
  value.count <- if(zero.rm) sum(x[!is.na(x)] > 0) else sum(!is.na(x))

  #Handle cases when there are negative values, all values are missing, or
  #missing values are not tolerated.
  if ((nan.count > 0 & !nan.rm) | any(x < 0, na.rm = TRUE)) {
    return(NaN)
  }
  if ((na.count > 0 & !na.rm) | value.count == 0) {
    return(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.
  if (all(x > 0, na.rm = TRUE)) {
    return(exp(mean(log(x), na.rm = TRUE)))
  }
  if (all(x == 0, na.rm = TRUE)) {
    return(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.
  if (is.na(eta)) {
    return(exp(sum(log(x[x > 0]), na.rm = TRUE) / value.count))
  }
  if (eta > 0) {
    return(exp(mean(log(x + eta), na.rm = TRUE)) - eta)
  }
  return(0) #only propagate zeroes when eta is set to 0 (or less than 0)
}

2
Bunun mevcut çözümlerden nasıl farklılaştığını / geliştiğini açıklayan bazı ayrıntılar ekleyebilir misiniz? (Kişisel dplyrolarak, gerekli olmadıkça böyle bir yardımcı program gibi ağır bir bağımlılık eklemek istemem ...)
Ben Bolker

Katılıyorum, case_whens'ler biraz aptalca, bu yüzden onları ve ifs lehine bağımlılığı kaldırdım . Ayrıca biraz detay verdim.
Chris Coffee

1
İkinci fikrinizle gittim ve varsayılanı üç ".rm '' parametresini hizalamak nan.rmiçin değiştirdim TRUE.
Chris Coffee

1
Bir başka stilistik nitpick. ifelsevektörleştirme için tasarlanmıştır. Kontrol edilecek tek bir koşulla, kullanımı daha deyimsel olacaktırvalue.count <- if(zero.rm) sum(x[!is.na(x)] > 0) else sum(!is.na(x))
Gregor Thomas

Bundan daha güzel görünüyor ifelse. Değişti. Teşekkürler!
Chris Coffee


3

Verilerinizde eksik değerler olması durumunda, bu nadir görülen bir durum değildir. bir argüman daha eklemeniz gerekiyor.

Aşağıdaki kodu deneyebilirsiniz:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.