web-dev-qa-db-ja.com

MongoDB集計フレームワークを使用して小数点以下2桁に丸める

私はmongodb集計フレームワークを使用しており、以下に示すようにいくつかの計算を行っています

db.RptAgg.aggregate( 
{ $group :
 { _id : {Region:"$RegionTxt",Mth:"$Month"},           
   ActSls:{$sum:"$ActSls"},
   PlnSls:{$sum:"$PlnSls"}
 } 
},
{ $project : 
 {
   ActSls:1,
   PlnSls:1,
   ActToPln:{$cond:[{ $ne: ["$PlnSls", 0] },{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]},0]}
  }

}

); 

結果を小数点第2位で四捨五入するための最良かつ最も簡単な方法を見つけようとしています。以下は私の結果です

{
    "result" : [
            {
                    "_id" : {
                            "Region" : "East",
                            "Mth" : 201301
                    },
                    "ActSls" : 72,
                    "PlnSls" : 102,
                    "ActToPln" : 70.58823529411765
            }
    ],
    "ok" : 1

}

「ActToPln」に70.59を表示してほしい"ActToPln":70.58823529411765の代わりに、集約フレームワーク自体からの結果。アプリケーションで丸めを行わないようにしたい

同じことを手伝ってくれませんか。

以下は私が使用したデータセットです。

{
    "_id" : ObjectId("51d67ef69557c507cb172572"),
    "RegionTxt" : "East",
    "Month" : 201301,
    "Date" : "2013-01-01",
    "ActSls" : 31,
    "PlnSls" : 51
}
{
    "_id" : ObjectId("51d67ef69557c507cb172573"),
    "RegionTxt" : "East",
    "Month" : 201301,
    "Date" : "2013-01-02",
    "ActSls" : 41,
    "PlnSls" : 51
}

前もって感謝します。南都

24
user2552537

$round演算子はありませんが、これは集計フレームワークで実行できます。特定の順序で実行すると、通常、浮動小数点の精度の問題を回避できます。

> db.a.save({x:1.23456789})
> db.a.save({x:9.87654321})
> db.a.aggregate([{$project:{ _id:0, 
         y:{$divide:[
              {$subtract:[
                      {$multiply:['$x',100]},
                      {$mod:[{$multiply:['$x',100]}, 1]}
              ]},
              100]}
}}])
{ "y" : 1.23 }
{ "y" : 9.87 }

問題の既存のパイプラインを考慮して、以下を置き換えます。

{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]}

{$divide:[
     {$subtract:[ 
          {$multiply:[
             {$divide: ['$ActSls','$PlnSls']},
             10000
          ]}, 
          {$mod:[
             {$multiply:[{$divide: ['$ActSls','$PlnSls']}, 10000 ]},
             1]}
          ]}, 
     100
]}

あなたのサンプルデータポイントでこれは結果です:

{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 }
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 }
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 }
17
Asya Kamsky

理由はわかりませんが、(このページの)すべての回答で12.34 ために 12.345。だから私は自分のプロジェクトステージを書きました:

x = 12.345

{'$project': {
    y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]},
}},

それは与えます 12.35


これは簡単な算術であり、トリックはありません:

  1. 12.345 * 100 = 1234.5#この手順により、丸め位置に到達します:100 = 10 ^ 2(ドットの後に2つの記号)。ステップは、ステップ4でバランス調整されます。
  2. 1234.5 + 0.5 = 1235.0#ここでround half up
  3. truncate(1235.0)= 1235#小数部分を単に削除する
  4. 1235/100 = 12.35

ただし、ネガティブの場合は正しく機能しません(私の集計ではそれで十分でした)。両方の(ポジティブとネガティブ)ケースでは、absと一緒に使用する必要があります。

{'$project': {
    z: {'$multiply': [
        {'$divide': ['$x', {'$abs': '$x'}]}, 
        {'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, 100]}, 0.5]}}, 100]}
    ]},
}}

ここでは、数値の符号を取得し、元の数値をabsでラップしてから、出力を丸めて符号を乗算しています。

7
egvo

Mongo 4.2以降、新しい$round 集計演算子が追加されましたを丸めるために使用できますnumber指定された10進数に一定の精度でplace

{$ round:[<number>、<place>]}

集計パイプライン内でそのまま使用できます(ここでは、xsを2の小数点以下の桁数に丸めます)。

// db.collection.insert([{x: 1.23456}, {x: 9.87654}, {x: 0.055543}, {x: 12.345}])
db.collection.aggregate([{ $project: { "rounded_x": { $round: ["$x", 2] }}}])
// [{"rounded_x": 1.23}, {"rounded_x": 9.88}, {"rounded_x": 0.06}, {"rounded_x": 12.35}]

placeパラメーターはオプションであり、省略した場合は整数全体に丸められます(つまり、小数点以下0桁で丸められます)。

5
Xavier Guihot

mongo-round はうまく機能します。私が見つけた最もクリーンな方法。

数が3.3333333

var round = require('mongo-round');

db.myCollection.aggregate([
    { $project: {
        roundAmount: round('$amount', 2)  // it will become 3.33
    } }
]);
4
Hongbo Miao

このソリューションでは、2 dpに正しく切り上げまたは切り下げます。

"rounded" : {
  $subtract:[
    {$add:['$absolute',0.0049999999999999999]},
    {$mod:[{$add:['$absolute',0.0049999999999999999]}, 0.01]}
  ]
}

たとえば、1.2499は1.25に切り上げられますが、1.2501は1.25に切り下げられます。

ノート:

  1. このソリューションは、 http://www.kamsky.org/stupid-tricks-with-mongodb/rounding-numbers-in-aggregation-framework
  2. Asya Kamskyの回答の問題を解決します。切り捨てるだけで、 切り上げ/切り下げを正しく行わない ;コメントで提案された変更後も。
  3. 高精度の入力値に対応するために、加算係数の後続9の数は多くなっています。丸められる数値の精度によっては、加算係数をこれよりもさらに正確にする必要がある場合があります。
3
Vince Bowdren

現在のバージョンのAggregation Frameworkには、ラウンド演算子はありません。あなたはこのスニペットを試すことができます:

> db.a.save({x:1.23456789})
> db.a.save({x:9.87654321})
> db.a.aggregate([{$project:{y:{$subtract:['$x',{$mod:['$x', 0.01]}]}}}])
{
    "result" : [
        {
            "_id" : ObjectId("51d72eab32549f94da161448"),
            "y" : 1.23
        },
        {
            "_id" : ObjectId("51d72ebe32549f94da161449"),
            "y" : 9.870000000000001
        }
    ],
    "ok" : 1
}

しかし、ご覧のとおり、精度の問題のため、このソリューションはうまく機能しません。この場合の最も簡単な方法は、@wiredprairieのアドバイスに従い、アプリケーションでroundsを作成することです。

2
Artem Mezhenin

MongoDBにこの関数が欠けているのは残念です。私は彼らがすぐにそれを追加することを望んでいます。

しかし、長い集計パイプラインを思いつきました。認めると、それは効率的ではないかもしれませんが、丸めのルールを尊重します。

db.t.aggregate([{
    $project: {
        _id: 0,
        number: {
            $let: {
                vars: {
                    factor: {
                        $pow: [10, 3]
                    },
                },
                in: {
                    $let: {
                        vars: {
                            num: {$multiply: ["$$factor", "$number"]},
                        },
                        in: {
                            $switch: {
                                branches: [
                                    {case: {$gte: ["$$num", {$add: [{$floor: "$$num"}, 0.5]}]}, then: {$divide:[ {$add: [{$floor: "$$num"}, 1.0]},"$$factor"]}},
                                    {case: {$lt: ["$$num", {$add: [{$floor: "$$num"}, 0.5]}]}, then: {$divide:[{$floor: "$$num"}, "$$factor"]}}                                    
                                ]
                            }
                        }
                    }
                }
            }
        }
    }
}])

tという名前のコレクションに次のドキュメントがあるとします。

{ number" : 2.341567 }
{ number" : 2.0012 }
{ number" : 2.0012223 }

上記のクエリを実行した後、私は得ました:

{ "number" : 2.342 }
{ "number" : 2.001 }
{ "number" : 2.001 }
1
Saleem
rounded:{'$multiply': [{ "$cond": [{ "$gte": [ "$x", 0 ] }, 1,-1 ]},{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, {$pow:[10,2]}]}, 0.5]}}, {$pow:[10,2]}]}]}

egvoの解はクールですが、ゼロの場合はゼロによる除算を提供します。 $ condを回避するために、サインを検出するために使用できます

(xをfield_nameで置き換え、数値2を目的の10進数で置き換えます)

1
Brcn
{$divide:[
            {$cond: { if: { $gte: [ {$mod:[{$multiply:['$dollarAmount',100]}, 1]}, 0.5 ] }, then: {$add: [{$subtract:[
                  {$multiply:['$dollarAmount',100]},
                  {$mod:[{$multiply:['$dollarAmount',100]}, 1]}
          ]}
                ,1]}, else: {$subtract:[
                  {$multiply:['$dollarAmount',100]},
                  {$mod:[{$multiply:['$dollarAmount',100]}, 1]}
          ]} }}
          , 
          100]}

うまくいけば、これらは四捨五入に役立ちます。

0
edoms06