幾何平均:ビルトインはありますか?


106

組み込みの幾何平均を見つけようとしましたが、見つかりませんでした。

(明らかに、ビルトインはシェルでの作業中に時間を節約するつもりはありません。また、精度に違いがあるとは思わないでしょう。スクリプトでは、ビルトインを可能な限り頻繁に使用しようとします(累積)多くの場合、パフォーマンスの向上が顕著です。

ない場合(私はそうではありません)は、ここにあります。

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

11
負の数とオーバーフローに注意してください。prod(a)は非常に速くアンダーフローまたはオーバーフローします。私は大きなリストを使用してこれを計時しようとしましたが、すぐにあなたの方法とExp(mean(log(x)))で1.4を使用してInfを取得しました。丸めの問題はかなり深刻になる可能性があります。
トリスタン

このQを投稿してから5分後に、誰かがgのRの組み込みを教えてくれると確信していたので、上記の関数をすばやく書きました。だから組み込みではないので、あなたの発言に照らして時間をかけて再コーディングする価値があるのは確かです。+ 1私から。
ダグ

1
9年後、この幾何平均組み込みのタグを付けました。
smci

回答:


77

これは、Rの幾何平均を計算するためのベクトル化されたゼロ許容関数とNA許容関数です。非正値が含まれている場合は、詳細なmean計算length(x)が必要xです。

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

パススルーに注意してくれた@ ben-bolkerと、na.rm正しく動作することを確認してくれた@Gregorに感謝します。

コメントのいくつかはNA、データとゼロの値の偽の同等性に関連していると思います。念頭に置いたアプリケーションでは同じですが、もちろんこれは一般的には当てはまりません。したがって、オプションのゼロの伝播を含め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、幾何平均が負の値に対して定義されていない(ただし、ゼロに対する)ことを考慮して、より有益で適切な値を返します。これについて私の事件に留まったコメント者に感謝します。


2
na.rm引数として渡す方が良いのではないでしょうか(つまり、他のRサマリー関数との一貫性を保つために、NAを許容するかどうかをユーザーに決定させます)。私はゼロを自動的に除外することに不安を感じています-私もそれをオプションにします。
ベンボルカー2014

1
おそらくあなたはna.rmオプションとして渡すことについて正しいです。回答を更新します。ゼロの除外に関しては、ゼロを含む非正の値の幾何平均は定義されていません。上記は幾何平均の一般的な修正で、ゼロ(またはこの場合はすべての非ゼロ)に1のダミー値が与えられます。これは、積(または、対数合計のゼロ)に影響を与えません。
ポールマクマーディ2014

*正でない値の一般的な修正を意味しました。幾何平均が使用されている場合、ゼロが最も一般的です。
ポールマクマーディ2014

1
あなたのna.rmパススルーコード化されたように動作しません...参照gm_mean(c(1:3, NA), na.rm = T)& !is.na(x)ベクトルのサブセットからを削除する必要があり、sumis の最初の引数は名前で...渡すna.rm = na.rm必要があるため、呼び出しでベクトルから0とを除外する必要もあります。NAlength
グレゴールトーマス

2
注意してください:のためxだけにゼロ(複数可)を含む、のようなx <- 0exp(sum(log(x[x>0]), na.rm = TRUE)/length(x))与え1意味をなさない幾何平均、のために。
adatum 2017年

88

いいえ、ですが、こちらのように書いた人もいます

別の可能性はこれを使用することです:

exp(mean(log(x)))

exp(mean(log(x)))を使用するもう1つの利点は、大きな数値の長いリストを操作できることです。これは、prod()を使用したより明確な式を使用する場合に問題になります。prod(a)^(1 / length(a))とexp(mean(log(a)))は同じ答えを返すことに注意してください。
lukeholman 2015

リンクが修正されました
PatrickT 2018


12

exp(mean(log(x)))

xに0がなければ機能します。その場合、ログは-Inf(-Infinite)を生成し、常に幾何平均が0になります。

1つの解決策は、平均を計算する前に-Inf値を削除することです。

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

これを行うにはワンライナーを使用できますが、これはログを2回計算することを意味し、非効率的です。

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

できるときにログを2回計算する理由:exp(mean(x [x!= 0]))
zzk '25

どちらのアプローチも、平均値の分母がsum(x) / length(x)xをフィルタリングしてに渡すと、平均値が間違っているため、平均値が間違っていますmean
ポールマクマーディ2014

あなたが明示的にそうするつもりでない限り、フィルタリングは悪い考えだと思います(たとえば、私が汎用関数を書いている場合、フィルタリングをデフォルトにしません)-これが1回限りのコードであり、問題のコンテキストで実際にゼロをフィルタリングすることの意味について非常に慎重に考えました(!)
Ben Bolker '28

定義上、ゼロを含む一連の数値の幾何平均はゼロでなければなりません!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は、ユーザーがゼロにすることなく、出力に影響を与えることができるようにすることを目的としています。場合zero.rmに設定されているFALSEetaに設定されているNA_real_(デフォルト値)、ゼロが1に向かって結果を縮小する効果を有します。私はこれに対する理論的な正当化はありません-ゼロを無視するのではなく、結果を自動的にゼロにすることを含まない「何かをする」ことの方が理にかなっているようです。

  • eta次のディスカッションに触発されてゼロを処理する方法です: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)
}

1
これが既存のソリューションとどのように異なるか、または改善されているかを説明する詳細を追加できますか?(私は個人的に、dplyr必要でない限り、そのようなユーティリティのような重い依存関係を追加したくありません...)
Ben Bolker

私は同意します、case_whensは少しばかげたので、それらを削除してifs を優先しました。詳細についても説明しました。
クリスコーヒー

1
私はあなたの後者のアイデアを取り入れ、デフォルトのnan.rmTRUEに変更して、3つの `` `.rm``パラメータすべてを揃えます。
クリスコーヒー

1
もう1つの文体的なヒント。ifelseベクトル化のために設計されています。単一の条件をチェックするので、使用する方が慣用的ですvalue.count <- if(zero.rm) sum(x[!is.na(x)] > 0) else sum(!is.na(x))
Gregor Thomas

も見栄えifelseがいいです。かわった。ありがとう!
クリスコーヒー


3

データに欠損値がある場合、これはまれなケースではありません。引数をもう1つ追加する必要があります。

次のコードを試すことができます:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.