기하 평균 : 내장 기능이 있습니까?


106

기하 평균에 대한 내장을 찾으려고했지만 찾을 수 없었습니다.

(분명히 빌트인은 쉘에서 작업하는 동안 저를 절약 할 수 없으며 정확도에 차이가 있다고 생각하지 않습니다. 스크립트의 경우 빌트인을 가능한 한 자주 사용하려고합니다. 성능 향상은 종종 눈에 띄게 나타납니다.

(내가 의심하는) 하나가 없다면 여기 내 것입니다.

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

11
음수 및 오버플로에주의하십시오. prod (a)는 매우 빠르게 밑돌거나 넘칠 것입니다. 나는 큰 목록을 사용하여 시간을 측정하고 exp (mean (log (x)))와 함께 1.4 대 1.4를 사용하여 Inf를 빠르게 얻었습니다. 반올림 문제는 매우 심각 할 수 있습니다.
Tristan

이 Q를 게시 한 후 5 분 후에 누군가가 gm에 대한 R의 내장 기능을 알려줄 것이라고 확신했기 때문에 위의 함수를 빠르게 작성했습니다. 따라서 내장 기능이 없으므로 귀하의 발언에 비추어 다시 코딩하는 데 시간을 할애 할 가치가 있습니다. 나에게서 + 1.
doug

1
저는 9 년 후이 기하학적 평균내장 이라는 태그를 지정했습니다 .
smci

답변:


77

다음은 R에서 기하 평균을 계산하기위한 벡터화 된 0 및 NA 허용 함수입니다. 포함 된 자세한 mean계산 length(x)x양수가 아닌 값을 포함 하는 경우에 필요 합니다.

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

na.rm통과 에 주목 해 주신 @ ben-bolker 와 올바르게 작동하는지 확인한 @Gregor에게 감사드립니다 .

의견 중 일부 NA는 데이터 및 0 값 의 잘못된 동등성과 관련이 있다고 생각 합니다. 응용 프로그램에서 나는 그것들이 동일하다는 것을 염두에 두었지만 물론 이것은 일반적으로 사실이 아닙니다. 따라서 0의 선택적 전파를 포함하고 제거 length(x)의 경우 다르게 처리 NA하려면 다음이 위의 함수에 대한 약간 더 긴 대안입니다.

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

또한 음수 값을 확인 NaN하고 기하 평균이 음수 값에 대해 정의되지 않았다는 점을 고려 하여 더 유익하고 적절한 결과를 반환 합니다 (하지만 0에 대한 것임). 이것에 대해 내 사건에 남아있는 논평자들에게 감사드립니다.


2
na.rm인수 로 전달하는 것이 더 낫지 않습니까 (즉, 사용자가 다른 R 요약 함수와의 일관성을 위해 NA 허용 여부를 결정할 수 있도록 함)? 자동으로 0을 제외하는 것에 대해 불안합니다. 옵션으로도 만들 것입니다.
Ben Bolker 2014-08-28

1
na.rm옵션으로 통과 하는 것이 맞을 것 입니다. 내 대답을 업데이트하겠습니다. 0 제외와 관련하여 기하 평균은 0을 포함하여 양수가 아닌 값에 대해 정의되지 않습니다. 위의 내용은 기하 평균에 대한 일반적인 수정 사항으로, 0 (또는이 경우 모든 0이 아닌 값)에 더미 값 1이 지정되어 제품에 영향을주지 않습니다 (또는 로그 합계에서 0).
Paul McMurdie 2014-08-28

* 나는 양수가 아닌 값에 대한 일반적인 수정을 의미했으며, 기하 평균을 사용할 때 0이 가장 일반적입니다.
Paul McMurdie 2014-08-28

1
귀하의 na.rm코드로 통과가 작동하지 않습니다 ... 참조 gm_mean(c(1:3, NA), na.rm = T). 당신은 제거 할 필요 & !is.na(x)벡터 집합에서, 그리고 최초의 인수 이후 sum이다 ..., 당신은 통과해야 na.rm = na.rm이름으로, 당신은 또한 제외해야 0's와 NA의 벡터에서의 length호출.
Gregor Thomas

2
주의 : 대한 x유일한 제로 (들)을 포함, 같은 x <- 0, exp(sum(log(x[x>0]), na.rm = TRUE)/length(x))제공 1이해가되지 않습니다 기하 평균을 위해.
adatum

88

아니요,하지만 여기 와 같이 글을 쓴 사람이 몇 명 있습니다 .

또 다른 가능성은 이것을 사용하는 것입니다.

exp(mean(log(x)))

exp (mean (log (x))) 사용의 또 다른 장점은 prod ()를 사용하여보다 명확한 공식을 사용할 때 문제가되는 큰 숫자의 긴 목록으로 작업 할 수 있다는 것입니다. prod (a) ^ (1 / length (a)) 및 exp (mean (log (a)))는 동일한 답을 제공합니다.
lukeholman

링크가 수정되었습니다
PatrickT


12

그만큼

exp(mean(log(x)))

x에 0이 없으면 작동합니다. 그렇다면 로그는 항상 기하 평균이 0 인 -Inf (-Infinite)를 생성합니다.

한 가지 해결책은 평균을 계산하기 전에 -Inf 값을 제거하는 것입니다.

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

한 줄짜리를 사용하여이를 수행 할 수 있지만 이는 비효율적 인 로그를 두 번 계산하는 것을 의미합니다.

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

할 수있을 때 로그를 두 번 계산하는 이유 : exp (mean (x [x! = 0]))
zzk

sum(x) / length(x)x를 필터링 한 다음에 전달하면 평균의 분모 가 잘못 되었기 때문에 두 방법 모두 평균 이 잘못되었습니다 mean.
Paul McMurdie 2014-08-28

명시 적으로 의도하지 않는 한 필터링은 나쁜 생각이라고 생각합니다 (예 : 범용 함수를 작성하는 경우 필터링을 기본값으로 설정하지 않음). (!) 문제의 맥락에서 무엇을 필터링 실제로 제로 밖으로 수단에 대해 매우 신중하게 생각
벤 Bolker

정의에 따라 0을 포함하는 숫자 집합의 기하학적 평균은 0이어야합니다! math.stackexchange.com/a/91445/221143
Chris

6

마크가 말한 그대로 사용합니다. 이렇게하면 tapply를 사용해도 내장 mean기능을 사용할 수 있으며, 자신의 기능을 정의 할 필요가 없습니다! 예를 들어, data $ value의 그룹 별 기하학적 평균을 계산하려면 다음을 수행하십시오.

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

3

이 버전은 다른 답변보다 더 많은 옵션을 제공합니다.

  • 이를 통해 사용자는 (실제) 숫자가 아닌 결과와 사용할 수없는 결과를 구분할 수 있습니다. 음수가 있으면 답은 실수 NaN가 아니므 로 반환됩니다. 모든 NA값이 있으면 함수는 NA_real_대신 실제 값을 사용할 수 없음을 반영하여 반환 합니다. 이것은 미묘한 차이이지만 (약간) 더 강력한 결과를 얻을 수 있습니다.

  • 첫 번째 선택적 매개 변수 zero.rm는 사용자가 0을 0으로 만들지 않고 출력에 영향을주는 0을 갖도록하기위한 것입니다. 경우 zero.rm로 설정 FALSE하고 eta로 설정 NA_real_(기본값), 제로 한 방향으로 결과를 축소의 효과가 있습니다. 나는 이것에 대한 이론적 정당성을 가지고 있지 않다. 단지 0을 무시하지 않고 자동으로 결과를 0으로 만드는 것을 포함하지 않는 "무언가"하는 것이 더 합리적이다.

  • etahttps://support.bioconductor.org/p/64014/ 토론에서 영감을 얻은 0을 처리하는 방법입니다.

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

1
이것이 기존 솔루션과 어떻게 다른지 / 개선되는지에 대한 세부 정보를 추가 할 수 있습니까? (필자는 개인적으로 dplyr필요한 경우가 아니면 그러한 유틸리티와 같은 무거운 종속성을 추가하고 싶지 않습니다 ...)
Ben Bolker

동의합니다. case_whens가 약간 어리 석었으므로 ifs 에 찬성하여 종속성과 종속성을 제거했습니다 . 나는 또한 약간의 정교함을 제공했다.
Chris Coffee

1
나는 후자의 아이디어로 가서 세 개의```.rm`` 매개 변수를 모두 정렬하도록 기본값을 nan.rm로 변경했습니다 TRUE.
Chris Coffee

1
또 하나의 문체 nitpick. ifelse벡터화를 위해 설계되었습니다. 확인해야 할 단일 조건으로 사용하는 것이 더 관용적입니다value.count <- if(zero.rm) sum(x[!is.na(x)] > 0) else sum(!is.na(x))
Gregor Thomas

그것보다 더 좋아 보인다 ifelse. 변경되었습니다. 감사!
Chris Coffee


3

데이터에 결 측값이있는 경우 드문 경우가 아닙니다. 인수를 하나 더 추가해야합니다.

다음 코드를 시도 할 수 있습니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.