4

リストを引数として取る累積加重平均関数を実装しようとしています

[[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]]

そして戻ります(ここでは小数点以下2桁に丸められます)

[3.1, 2.47, 4.08, 5.81]

例: 2.47 = (1000 * 3.1 + 500 * 1.2) / 1500。

現在、次のコードを使用してこれを解決しています。

def cumulative_weighted_average(list)
  cs = 0
  qu = 0
  res = list.inject([0]) do |s, el|
    cs += el[0] * el[1]
    qu += el[0]
    s + [cs.to_f / qu]
  end
  res.shift
  res
end

これを行うためのより短い(よりコンパクトな)方法はありますか?

編集: 以下の回答をありがとう!リストには平均して約 1000 のエントリが含まれるため、速度要件については不明です。基本的にブロック内の 2 つの値を追跡できるようにする必要があるため、次のように記述できる inject の拡張機能はありますか?

list.inject([0,0]){ |s1, s2, el| ...}

s1 と s2 は 0 に初期化されますか?

4

3 に答える 3

5

これがあなたが望むものだと思います:

def cumulative_weighted_average list
  cs, qu = 0.0, 0.0
  list
  .map{|x, w| [cs += x * w, qu += x]}
  .map{|cs, qu| cs / qu}
end

cumulative_weighted_average([[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]])
# => [3.1, 2.466666666666667, 4.078260869565217, 5.812222222222222]


追加の質問については、次のようなことが考えられます。

list.inject([0,0]){|(s1, s2), el| ...}
于 2013-07-19T17:00:53.253 に答える
0

2段階の計算を許可すれば、「外側の」一時変数を避けて、見た目を少しすっきりさせて慣用的なRubyにすることができます(これは必ずしも遅くはなく、同じ量の数学が必要です):

def cumulative_weighted_average list
  cumulative_totals = list.inject( [] ) do |cumulative,item|
    tot_count, tot_sum = cumulative.last || [0, 0.0]
    next_count, next_value = item
    cumulative << [ tot_count + next_count,  tot_sum + next_count * next_value ]
  end
  cumulative_totals.map { |count,sum| sum/count }
end

p cumulative_weighted_average( 
    [[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]] )

=> [3.1, 2.46666666666667, 4.07826086956522, 5.81222222222222]
于 2013-07-19T16:10:52.480 に答える