2012-07-23 12 views
7

Mongoを使っていくつかの要約統計量を生成する方法を探しています。形式のレコードがたくさんあるコレクションがあるとします。Mongoのビニングとテーブル(ユニーク/カウント)

{"name" : "Jeroen", "gender" : "m", "age" :27.53 } 

今、私は性別と年齢の分布を取得したいと思います。性別については、値"m""f"しかないと仮定します。私のコレクションの男性と女性の合計数を得る最も効率的な方法は何ですか?

年齢のために、いくつかの「ビニング」を行い、要約のようなヒストグラムを与える方法があります。すなわち、年齢が間隔内にあるレコードの数:[0, 2), [2, 4), [4, 6) ...など?

答えて

2

コンスタンチンの答えが正しい。 MapReduceは仕事を終わらせる。ここでは、他の人がこの興味深いものを見つけるための完全な解決策があります。

性別をカウントするには、マップファンクションキーが各レコードのthis.gender属性です。 reduce関数は、単純にそれらを追加します。

// count genders 
db.persons.mapReduce(
    function(){ 
     emit(this["gender"], {count: 1}) 
    }, function(key, values){ 
     var result = {count: 0}; 
     values.forEach(function(value) { 
      result.count += value.count; 
     }); 
     return result; 
    }, {out: { inline : 1}} 
); 

がビニングを行うために、我々は2で最寄りの部門に切り捨てするマップ機能でキーを設定します。従って、例えば。 10と11.9999の間の任意の値は、同じキー"10-12"を取得します。そして再び、私たちは、単にそれらを追加します。

db.responses.mapReduce(
    function(){ 
     var x = Math.floor(this["age"]/2)*2; 
     var key = x + "-" + (x+2); 
     emit(key, {count: 1}) 
    }, function(state, values){ 
     var result = {count: 0}; 
     values.forEach(function(value) { 
      result.count += value.count; 
     }); 
     return result; 
    }, {out: { inline : 1}} 
); 
+0

これはクールです。 mapReduce()関数のドキュメントはどこにありますか?私は検索を行いましたが、正式なソースを見つけるようには思われませんでした。 – jimijazz

0

データの量に応じて、男性と女性の量を調べる最も効果的な方法は、 ナイーブなクエリまたはマップ削減ジョブのいずれかです。ビニングは最高減らすマップを経由して行われます。

あなたの鍵はビンであるmapフェーズでは、その値が1で、かつ削減段階で、あなただけの値

+0

これをいくつかのコード例で説明できますか? – Jeroen

+0

あなたはより速く - 私は休暇中で、かなりオフラインです –

1

の合計数を取得するための簡単な方法をまとめます男性はdb.x.find({"gender": "m"}).count()

のいずれかのクエリで男性と女性の両方のカウントが必要な場合、簡単な方法はありません。マップ/リダクションは1つの可能性があります。または、おそらく新しいaggregation frameworkビンビンの場合も同じです要件

Mongoは集約には適していませんが、多くの小さな増分更新では素晴らしいです。 mongoでこの問題を解決する最も良い方法は、別々のコレクションに集約データを収集することです。

あなたはこのような一つの文書で統計情報の収集を続けるのであれば、:その後、毎回あなたが他のコレクションからの人を追加または削除...

stats: [ 
    { 
    "male": 23, 
    "female": 17, 
    "ageDistribution": { 
     "0_2" : 3, 
     "2_4" : 5, 
     "4_6" : 7 
    } 
    } 
] 

、あなたはそれぞれのフィールド上または下でカウント統計情報の収集統計に

db.stats.update({"$inc": {"male": 1, "ageDistribution.2_4": 1}}) 

クエリは雷がこのように高速になり、あなたはほとんど上下統計情報をカウントから任意のパフォーマンスのオーバーヘッドを気づかないだろう。

19

私はちょうどマップよりも高い性能を持っている必要がありMongoDBのバージョン2.2で利用できるようになり、新たな集約フレームワークを(2.2.0-RC0がリリースされている)、試してみましたそれはJavascriptに依存しないので、減らす。

入力データ:性別

{ "_id" : 1, "age" : 22.34, "gender" : "f" } 
{ "_id" : 2, "age" : 23.9, "gender" : "f" } 
{ "_id" : 3, "age" : 27.4, "gender" : "f" } 
{ "_id" : 4, "age" : 26.9, "gender" : "m" } 
{ "_id" : 5, "age" : 26, "gender" : "m" } 

aggregationコマンド:

db.collection.aggregate(
    {$project: {gender:1}}, 
    {$group: { 
     _id: "$gender", 
     count: {$sum: 1} 
    }}) 

結果:

{"result" : 
    [ 
    {"_id" : "m", "count" : 2}, 
    {"_id" : "f", "count" : 3} 
    ], 
    "ok" : 1 
} 

ビンに年齢を取得するには:

db.collection.aggregate(
    {$project: { 
     ageLowerBound: {$subtract:["$age", {$mod:["$age",2]}]}} 
    }, 
    {$group: { 
     _id:"$ageLowerBound", 
     count:{$sum:1} 
    } 
}) 

結果:これがさらに簡単になりましたモンゴ3.4で

{"result" : 
    [ 
     {"_id" : 26, "count" : 3}, 
     {"_id" : 22, "count" : 2} 
    ], 
    "ok" : 1 
} 
+0

集計フレームワークに興味のある人は、集計コマンドで$ matchをできるだけ早く使用することをお勧めします。 – Jenna

+2

任意の数の倍数でない任意のビンの場合、構文は次のとおりですが、[$ cond](http://docs.mongodb.org/manual/reference/operator/aggregation/cond/#exp._S_cond)を使用できます{$ cond:[$ $:$ {$ age:2}}、 "0"、 {$ cond:[{$ lt:[$ age、4]}、 "2"、 "4"]}]}} ...またはそのようなものです。 –

+0

@Jenna GeoJSONのエントリに対して何ができますか? – Pei

0

、新しい$バケットと$bucketAuto集約機能のおかげ。

{ "_id" : { "min" : "f", "max" : "m" }, "count" : 3 } 
{ "_id" : { "min" : "m", "max" : "m" }, "count" : 2 } 

注意、バケットや自動バケット:それは次のような結果になります

{ "_id" : 1, "age" : 22.34, "gender" : "f" } 
{ "_id" : 2, "age" : 23.9, "gender" : "f" } 
{ "_id" : 3, "age" : 27.4, "gender" : "f" } 
{ "_id" : 4, "age" : 26.9, "gender" : "m" } 
{ "_id" : 5, "age" : 26, "gender" : "m" } 

次の入力データと

db.bucket.aggregate([ 
    { 
    $bucketAuto: { 
     groupBy: "$gender", 
     buckets: 2 
    } 
    } 
]) 

:二つのグループに次のクエリの自動バケット通常、連続変数(数値、日付)に使用されますが、この場合、自動バケットはうまく動作します。ヒストグラムのためにビニング@ColinEの回答に基づいて

0

db.persons.aggregate([ 
 { 
    $bucket: { 
    groupBy: "$j.age", 
    boundaries: [0,2,4,6,8,10,12,14,16,18,20], 
    default: "Other", 
    output: { 
     "count": { $sum: 1 } 
    } 
    } 
], 
{allowDiskUse:true}) 

$bucketAutoによって行うことができるバケットは、対数スケールで集めているように見えるので、私のために動作しませんでした。 allowDiskUseは、何百万ものドキュメントがある場合にのみ必要です。