【问题标题】:is map/reduce appropriate for finding the median and mode of a set of values for many records?map/reduce 是否适合为许多记录查找一组值的中位数和众数?
【发布时间】:2012-01-02 23:02:15
【问题描述】:

我在 Mongodb 中有一组对象,每个对象都嵌入了一组值,例如:

[1.22, 12.87, 1.24, 1.24, 9.87, 1.24, 87.65] // ... up to about 150 values

map/reduce 是在嵌入式数组中查找中值(平均值)和众数(最常见值)的最佳解决方案吗?我问的原因是 map 和 reduce 都必须返回相同的(结构上)值集。在我的情况下,我想获取一组值(数组)并返回一组两个值(中值、众数)。

如果不是,解决此问题的最佳方法是什么?如果相关的话,我希望它在 rake 任务中运行。这将是一夜之间处理数据的事情。

【问题讨论】:

  • “map/reduce 是在嵌入式数组中找到中值(平均值)和众数(最常见值)的最佳解决方案吗?” 中值不是平均值,它是中点的值。那么你想要中位数还是平均值(平均值)?
  • @scotts 你抓住了我,我已经好几年没做统计了。我看不出这会如何改变问题的答案。
  • 计算中位数与均值不同,因为您需要对数字数组进行排序

标签: mongodb mapreduce


【解决方案1】:

这里有一个关于预期输出的关键问题。您的问题不是 100% 清楚您想要哪一个。

你想要(A)

{ _id: "document1", value: { mode: 1.0, median: 10.0 } }
{ _id: "document2", value: { mode: 5.0, median: 150.0 } }
... one for each document

...或者你想要(B),所有数组的所有组合的众数和中位数。

  • 如果答案是 (A),那么 Map/Reduce 将起作用
  • 如果答案是 (B),那么 Map/Reduce 可能不起作用

如果您打算执行(A),请仔细阅读 M/R 文档并了解限制。虽然选项 (A) 可以 是 Map/Reduce,但它也可以只是一个大的 for 循环,在“summary”集合甚至是 upsert回到原来的集合。这可能会更有效率。

【讨论】:

  • 我正在寻找选项 A。我在 map/reduce 中遇到了足够多的神秘错误,因此我决定只使用光标进行循环(find/forEach/update)。我认为您可能是对的,循环将具有更好或至少相同的性能。谢谢你的回答。
  • If the answer is (B), then Map/Reduce will probably not work. - 如果要完成的计算是可交换的和关联的,那么它可以用 MR 完成。 mode(最常见的值)类似于word count example,而计算中值可以通过一个 Reducer 来完成,它从所有映射器中获取中值并再次计算中值。
  • @jcollum:老实说,我认为 Ruby 方法可能更好,因为您可以并行化 Ruby 脚本,而 Map / Reduce 没有这样的选项。
【解决方案2】:

我会先阅读这个http://www.mongovue.com/2010/11/03/yet-another-mongodb-map-reduce-tutorial/

我想你想要

  1. 用于生成键和单个数据元素的映射阶段,
  2. 将所有数据元素放入的归约阶段 每个键的数据数组,
  3. 完成平均值、中位数和众数的最终阶段 对整个集合的操作。


完成函数

一个 finalize 函数可以在归约后运行。 这样的功能是可选的,对于许多 map/reduce 来说不是必需的 案例。 finalize 函数接受一个键和一个值,并返回一个 最终值。

function finalize(key, value) -> final_value

你的 reduce 函数可能 为同一个对象多次调用使用 finalize 时 某事最后只能做一次;例如 计算平均值。

取自http://www.mongodb.org/display/DOCS/MapReduce

【讨论】:

    【解决方案3】:

    我假设您想找到每个文档的众数和中位数,您可以使用 map reduce 来做到这一点。在这种情况下,您在 map 函数中计算中位数和众数,reduce 将返回原样的地图结果

    map = function() {
       var res = 0;
       for (i = 0; i < this.marks.length; i++) {
          res = res + this.marks[i];
       }
       var median = res/this.marks.length;
       emit(this._id,{marks:this.marks,median:median});
    }
    
    
    reduce = function (k, values) {
        values.forEach(function(value) {
          result = value;
        });
        return result;
    }
    

    对于这个系列

    { "_id" : ObjectId("4f02be1f1ae045175f0eb9f1"), "name" : "ram", "marks" : [ 1.22, 12.87, 1.24, 1.24, 9.87, 1.24, 87.65 ] }
    { "_id" : ObjectId("4f02be371ae045175f0eb9f2"), "name" : "sam", "marks" : [ 1.32, 11.87, 12.4, 4.24, 9.37, 3.24, 7.65 ] }
    { "_id" : ObjectId("4f02be4c1ae045175f0eb9f3"), "name" : "pam", "marks" : [ 3.32, 10.17, 11.4, 2.24, 2.37, 3.24, 30.65 ] }
    

    你可以得到中位数

      db.test.mapReduce(map,reduce,{out: { inline : 1}})
    
    {
        "results" : [
            {
                "_id" : ObjectId("4f02be1f1ae045175f0eb9f1"),
                "value" : {
                    "marks" : [
                        1.22,
                        12.87,
                        1.24,
                        1.24,
                        9.87,
                        1.24,
                        87.65
                    ],
                    "median" : 16.475714285714286
                }
            },
            {
                "_id" : ObjectId("4f02be371ae045175f0eb9f2"),
                "value" : {
                    "marks" : [
                        1.32,
                        11.87,
                        12.4,
                        4.24,
                        9.37,
                        3.24,
                        7.65
                    ],
                    "median" : 7.155714285714285
                }
            },
            {
                "_id" : ObjectId("4f02be4c1ae045175f0eb9f3"),
                "value" : {
                    "marks" : [
                        3.32,
                        10.17,
                        11.4,
                        2.24,
                        2.37,
                        3.24,
                        30.65
                    ],
                    "median" : 9.055714285714286
                }
            }
        ],
        "timeMillis" : 1,
        "counts" : {
            "input" : 3,
            "emit" : 3,
            "reduce" : 0,
            "output" : 3
        },
        "ok" : 1,
    }
    

    【讨论】:

    • 这段代码不计算中位数,而是计算平均值。中位数是排序后的数字数组中位于中间位置的数字。
    猜你喜欢
    • 2020-08-16
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-19
    相关资源
    最近更新 更多