2013-12-08 150 views
13

我是MongoDB聚合的新手,想知道是否有方法使用MongoDB聚合框架計算中位數?在MongoDB聚合框架中計算中位數

乾杯,

劉易斯

+0

AFAIK有作爲'$ median'所以也許你將不得不使用地圖,減少對這個沒有這樣的事情。 – hgoebl

+0

有一個打開的功能請求來添加對'$ median'累加器的支持。請在MongoDB問題跟蹤器中注意/觀看[SERVER-4929](https://jira.mongodb.org/browse/SERVER-4929)。 – Stennie

回答

2

聚集框架不支持位外的開箱。所以你必須自己寫點東西。

我會建議您在應用程序級別上執行此操作。使用正常的find()檢索所有文檔,對結果集進行排序(在數據庫中使用光標的.sort()函數或在應用程序中排序它們 - 您的決定),然後獲取元素size/2

當你真的想在數據庫級別上做到這一點,你可以使用map-reduce來完成。 map函數會發出一個key和一個單一值的數組 - 你想獲得的值的中位數。 reduce函數只是連接它接收的結果數組,所以每個鍵最後都會有一個包含所有值的數組。然後,finalize函數將計算該數組的中值,再次通過對數組進行排序,然後獲取元素編號size/2

+0

好的,這很棒,我會放棄。 – user3080286

20

在一般情況下,計算中位數有點棘手,因爲它涉及對整個數據集進行排序,或者使用深度與數據集大小成正比的遞歸。這也許就是爲什麼很多數據庫沒有開箱即用的中間運算符的原因(MySQL也沒有)。

計算中位數將與這兩個語句最簡單的方法(假設屬性上,我們要計算的位數叫a,我們希望它在集合中的所有文件,coll):

count = db.coll.count(); 
db.coll.find().sort({"a":1}).skip(count/2 - 1).limit(1); 

這相當於什麼人suggest for MySQL

+5

我知道這是不允許發表評論只是說謝謝..但這是美麗的:) – fguillen

1

可以用聚合框架一次性完成。

Sort =>將數組排序後的值=>獲取數組的大小=>將大小除以二=>獲得除法的Int值(中值左側)=>向左側(右側)添加1 = >在左側和右側得到數組元素=>兩種元素的平均

這與彈簧的java mongoTemplate樣品:

該模型書與作者的登錄(「擁有者的列表「),目標是獲得用戶的書的中位數:

 GroupOperation countByBookOwner = group("owner").count().as("nbBooks"); 

    SortOperation sortByCount = sort(Direction.ASC, "nbBooks"); 

    GroupOperation putInArray = group().push("nbBooks").as("nbBooksArray"); 

    ProjectionOperation getSizeOfArray = project("nbBooksArray").and("nbBooksArray").size().as("size"); 

    ProjectionOperation divideSizeByTwo = project("nbBooksArray").and("size").divide(2).as("middleFloat"); 

    ProjectionOperation getIntValueOfDivisionForBornLeft = project("middleFloat", "nbBooksArray").and("middleFloat") 
      .project("trunc").as("beginMiddle"); 

    ProjectionOperation add1ToBornLeftToGetBornRight = project("beginMiddle", "middleFloat", "nbBooksArray") 
      .and("beginMiddle").project("add", 1).as("endMiddle"); 

    ProjectionOperation arrayElementAt = project("beginMiddle", "endMiddle", "middleFloat", "nbBooksArray") 
      .and("nbBooksArray").project("arrayElemAt", "$beginMiddle").as("beginValue").and("nbBooksArray") 
      .project("arrayElemAt", "$endMiddle").as("endValue"); 

    ProjectionOperation averageForMedian = project("beginMiddle", "endMiddle", "middleFloat", "nbBooksArray", 
      "beginValue", "endValue").and("beginValue").project("avg", "$endValue").as("median"); 

    Aggregation aggregation = newAggregation(countByBookOwner, sortByCount, putInArray, getSizeOfArray, 
      divideSizeByTwo, getIntValueOfDivisionForBornLeft, add1ToBornLeftToGetBornRight, arrayElementAt, 
      averageForMedian); 

    long time = System.currentTimeMillis(); 
    AggregationResults<MedianContainer> groupResults = mongoTemplate.aggregate(aggregation, "book", 
      MedianContainer.class); 

和這裏所產生的聚合:

{ 
"aggregate": "book" , 
"pipeline": [ 
    { 
     "$group": { 
      "_id": "$owner" , 
      "nbBooks": { 
       "$sum": 1 
      } 
     } 
    } , { 
     "$sort": { 
      "nbBooks": 1 
     } 
    } , { 
     "$group": { 
      "_id": null , 
      "nbBooksArray": { 
       "$push": "$nbBooks" 
      } 
     } 
    } , { 
     "$project": { 
      "nbBooksArray": 1 , 
      "size": { 
       "$size": ["$nbBooksArray"] 
      } 
     } 
    } , { 
     "$project": { 
      "nbBooksArray": 1 , 
      "middleFloat": { 
       "$divide": ["$size" , 2] 
      } 
     } 
    } , { 
     "$project": { 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginMiddle": { 
       "$trunc": ["$middleFloat"] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "endMiddle": { 
       "$add": ["$beginMiddle" , 1] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "endMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginValue": { 
       "$arrayElemAt": ["$nbBooksArray" , "$beginMiddle"] 
      } , 
      "endValue": { 
       "$arrayElemAt": ["$nbBooksArray" , "$endMiddle"] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "endMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginValue": 1 , 
      "endValue": 1 , 
      "median": { 
       "$avg": ["$beginValue" , "$endValue"] 
      } 
     } 
    } 
] 

}