Arrondir à la deuxième décimale en utilisant le cadre D'agrégation MongoDB
j'utilise le cadre d'agrégation de mongodb et je fais quelques calculs comme indiqué ci-dessous
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]}
}
}
);
j'essaie de comprendre ce qui est le meilleur et le plus facile pour arrondir mes résultats à 2 décimales. Voici mon résultat
{
"result" : [
{
"_id" : {
"Region" : "East",
"Mth" : 201301
},
"ActSls" : 72,
"PlnSls" : 102,
"ActToPln" : 70.58823529411765
}
],
"ok" : 1
}
je veux "ActToPln" pour montrer 70.59 au lieu de "ActToPln": 70.58823529411765, dans les résultats du cadre d'aggégation lui-même. Je veux éviter de faire l'arrondi dans mon application
Pouvez-vous s'il vous plaît aider avec le même.
Voici l'ensemble de données que j'ai utilisé.
{
"_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
}
Merci d'avance. Nandu
8 réponses
Il n'y a pas de $round
opérateur mais vous pouvez le faire dans le cadre d'agrégation - le faire dans un ordre spécifique évitera généralement les problèmes de précision à virgule flottante.
> 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 }
compte tenu du pipeline existant dans le problème, remplacer:
{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]}
{$divide:[
{$subtract:[
{$multiply:[
{$divide: ['$ActSls','$PlnSls']},
10000
]},
{$mod:[
{$multiply:[{$divide: ['$ActSls','$PlnSls']}, 10000 ]},
1]}
]},
100
]}
avec vos points de données sample ceci est le résultat:
{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 }
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 }
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 }
mongo-ronde œuvres de nice. Le plus propre que j'ai trouvé.
Dire le nombre est 3.3333333
var round = require('mongo-round');
db.myCollection.aggregate([
{ $project: {
roundAmount: round('$amount', 2) // it will become 3.33
} }
]);
il n'y a pas d'opérateur rond dans la version actuelle du cadre D'agrégation. Vous pouvez essayer ce morceau de code:
> 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
}
mais comme vous le voyez, cette solution ne fonctionne pas bien à cause de problèmes de précision. La façon la plus simple dans ce cas est de suivre @wiredprairie
's des conseils et faire round
s dans votre application.
cette solution arrondit correctement vers le haut ou vers le bas à 2dp:
"rounded" : {
$subtract:[
{$add:['$absolute',0.0049999999999999999]},
{$mod:[{$add:['$absolute',0.0049999999999999999]}, 0.01]}
]
}
par exemple il tourne 1.2499 vers le haut à 1.25, mais 1.2501 vers le bas à 1.25.
Notes:
- Cette solution est basée sur les exemples donnés au http://www.kamsky.org/stupid-tricks-with-mongodb/rounding-numbers-in-aggregation-framework
- il résout le problème dans la réponse D'Asya Kamsky, qu'il ne tronque et ne fait pas arrondir Haut/Bas correctement; même après le changement suggéré dans les commentaires.
- le nombre de 9 de queue dans le facteur d'addition est grand, pour accommoder des nombres d'entrée de haute précision. En fonction de la précision des chiffres à arrondir, le facteur d'addition peut devoir être encore plus précis que celui-ci.
{$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]}
espérons que celui-ci pourrait aider à compléter.
je ne sais pas pourquoi, mais toutes les réponses (à cette page) donnez-moi 12.34
12.345
. J'ai donc écrit ma propre étape de projet:
x = 12.345
{'$project': {
y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]},
}},
donne 12.35
.
Voici simple arithmétique, pas de trucs:
- 12.345 * 100 = 1234.5 # cette étape nous amène à la position d'arrondi: 100 = 10^2 (deux signes après le point). Le pas sera équilibré par l'étape 4.
- 1234.5 + 0.5 = 1235.0 # Ici, je reçois mon
round half up
- tronqué (1235.0) = 1235 # Simplement gouttes partie fractionnaire
- 1235 / 100 = 12.35
cependant, cela ne fonctionne pas correctement pour les négatifs (c'était suffisant pour mon agrégation). Pour les deux cas (positif et négatif), vous devez l'utiliser avec abs
:
{'$project': {
z: {'$multiply': [
{'$divide': ['$x', {'$abs': '$x'}]},
{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, 100]}, 0.5]}}, 100]}
]},
}}
ici je reçois le signe du nombre, envelopper le nombre original par abs et ensuite multiplier le signe en arrondissant la sortie.
rounded:{'$multiply': [{ "$cond": [{ "$gte": [ "$x", 0 ] }, 1,-1 ]},{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, {$pow:[10,2]}]}, 0.5]}}, {$pow:[10,2]}]}]}
la solution d'egvo est cool mais donne la division par zéro si elle est zéro. Pour éviter $cond peut être utilisé pour détecter le signe
(remplacez x par field_name et le numéro 2 par le nombre décimal désiré)
laissez-moi vous dire qu'il est dommage que MongoDB manque cette fonction. Je suis hopping ils l'ajouteront bientôt.
cependant, je suis arrivé avec un long pipeline d'agrégation. En admettant, il peut ne pas être efficace, mais il honore les règles de l'arrondissement.
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"]}}
]
}
}
}
}
}
}
}
}])
supposons que j'ai les documents suivants dans ma collection nommés t
{ number" : 2.341567 }
{ number" : 2.0012 }
{ number" : 2.0012223 }
Après les requêtes ci-dessus, j'ai:
{ "number" : 2.342 }
{ "number" : 2.001 }
{ "number" : 2.001 }