2

Redisで数学を行うための情報をウェブで探しても、実際にはあまり見つかりません。RailsでRedis-RBgemを使用しており、結果のリストをキャッシュしています。

e = [1738738.0, 2019461.0, 1488842.0, 2272588.0, 1506046.0, 2448701.0, 3554207.0, 1659395.0, ...]
$redis.lpush "analytics:math_test", e

現在、私たちの数のリストは、1日あたりリストあたり数千から数万に達し、リストの数は1日あたり数千になる可能性があります。(これは実際にはそれほど多くはありませんが、私たちは成長しており、すぐにはるかに大きなサンプルサイズを期待しています。)

これらのリストのそれぞれについて、統計を実行できるようにしたいと思います。私は現在これをアプリ内で行っています

def basic_stats(arr)
  return nil if arr.nil? or arr.empty?
  min = arr.min.to_f
  max = arr.max.to_f
  total = arr.inject(:+)
  len = arr.length
  mean = total.to_f / len # to_f so we don't get an integer result
  sorted = arr.sort
  median = len % 2 == 1 ? sorted[len/2] : (sorted[len/2 - 1] + sorted[len/2]).to_f / 2
  sum = arr.inject(0){|accum, i| accum +(i-mean)**2 }
  variance = sum/(arr.length - 1).to_f
  std_dev = Math.sqrt(variance).nan? ? 0 : Math.sqrt(variance)

  {min: min, max: max, mean: mean, median: median, std_dev: std_dev, size: len}
end

また、統計を簡単に保存することもできますが、集計リストで統計を実行するには、リストをまとめて集計する必要があります。したがって、すべての可能な集約セットではなく、生の数値を格納することは理にかなっています。このため、私は数学を速くする必要があり、これを行う方法を模索してきました。最も簡単な方法は、リストに15万個のアイテムがあるアプリ内で実行することです。これは、実際にはそれほどひどいことではありません。

$redis_analytics.llen "analytics:math_test", 0, -1
=> 156954
Benchmark.measure do
  basic_stats $redis_analytics.lrange("analytics:math_test", 0, -1).map(&:to_f)
end 
=>   2.650000   0.060000   2.710000 (  2.732993)

1回の計算で3秒押したくないのですが、これは現在のユースケースの約10倍のサンプル数ではない可能性があることを考えると、ひどいことではありません。100万程度のサンプルサイズで作業している場合はどうなりますか?

$redis_analytics.llen("analytics:math_test")
=> 1063454 
Benchmark.measure do
  basic_stats $redis_analytics.lrange("analytics:math_test", 0, -1).map(&:to_f)
end
=>  21.360000   0.340000  21.700000 ( 21.847734) 

オプション

  1. リストのSORTメソッドを使用すると、Redisで最小/最大/長さを即座に取得できます。残念ながら、median、mean、std_devなどのアプリ内に移動する必要があるようです。これらをRedisで計算できない限り。
  2. Luaスクリプトを使用して計算を行います。(私はまだLuaを学んでいないので、これがどのようになるかはわかりません。もっと速いと思われる場合は、試してみたいと思います。)
  3. Rubyを利用するためのより効率的な方法。これは、かなりまともな統計の宝石のように見えるものを利用すると類似した結果が得られるため、少しありそうもないようです。
  4. 別のデータベースを使用してください。

StatsSamplegemの使用例

宝石を使用しても何も得られないようです。Pythonでは、おそらくCモジュールを作成しますが、多くのrubystatsgemがCにあるかどうかはわかりません。

require 'statsample'
def basic_stats(stats)
  return nil if stats.nil? or stats.empty?
  arr = stats.to_scale

  {min: arr.min, max: arr.max, mean: arr.mean, median: arr.median, std_dev: arr.sd, size: stats.length}
end

Benchmark.measure do
  basic_stats $redis_analytics.lrange("analytics:math_test", 0, -1).map(&:to_f)
end
=>  20.860000   0.440000  21.300000 ( 21.436437)

コーダ

もちろん、そのような大規模な統計計算には長い時間がかかる可能性があり、それらをキューにオフロードする必要があります。ただし、この計算の多くは実際にはデータベースではなくRuby / Rails内で行われていることを考えると、他の選択肢があるのではないかと思いました。

4

3 に答える 3

4

他の人が同じことをするのを助けることができる入力が誰かにある場合に備えて、これを開いたままにしておきたいと思います。しかし、私にとっては、SQLが非常にうまく機能することをRedisに強制しようとすると、あまりにも多くの時間を費やしていることに気づきました。これをPostgresにダンプするだけで、データベースで直接効率的な集計と計算を行うことができます。Redisを使用し始めたときは良いアイデアでしたが、スケールアウトして悪いものにしたので、行き詰まっていたと思います。

于 2012-09-11T19:10:28.707 に答える
3

Redis 2.6に切り替えることができれば、Luaスクリプトがこの問題を解決するためのおそらく最良の方法です。ところで、速度のテストは非常に簡単なはずなので、必要な時間の投資が少ないことを考えると、Luaスクリプトを試して、どのような結果が得られるかを確認することを強くお勧めします。

もう1つの方法は、Luaを使用してデータを設定し、リストごとに関連するハッシュタイプを更新して、最小/最大/平均の統計を直接保持することです。これにより、毎回これらの統計を計算する必要がなくなります。 、段階的に更新されるため。常に可能であるとは限りませんが、特定のユースケースによって異なります。

于 2012-09-24T00:02:07.860 に答える
0

NArrayを見てみましょう。彼らのホームページから:

この拡張ライブラリは、Ruby言語に大きな数値配列の高速計算と簡単な操作を組み込んでいます。

それらの配列クラスには、必要なほとんどすべての関数が組み込まれているようです。そのページのCmd-F「統計」。

于 2012-09-11T19:12:05.793 に答える