2013-12-08 19 views
13

Sono nuovo nell'aggregazione MongoDB e mi chiedevo se esiste un modo per calcolare la mediana utilizzando il framework di aggregazione MongoDB?Calcola la mediana nel framework di aggregazione MongoDB

Cheers,

Lewis

+0

per quanto ne so non c'è alcuna cosa come '$ median' quindi probabilmente dovrete utilizzare map-ridurre per questo. – hgoebl

+0

C'è una richiesta di funzione aperta per aggiungere il supporto per un accumulatore '$ median'. Si prega di ingrandire/guardare [SERVER-4929] (https://jira.mongodb.org/browse/SERVER-4929) nel tracker dei problemi di MongoDB. – Stennie

risposta

2

Il quadro di aggregazione non supporta mediana out-of-the-box. Quindi dovrai scrivere qualcosa per conto tuo.

Consiglierei di farlo a livello di applicazione. Recupera tutti i tuoi documenti con un normale find(), ordina i set di risultati (sulla base dati usando la funzione .sort() del cursore o ordinandoli nell'applicazione - tua decisione) e poi prendi l'elemento size/2.

Quando si desidera veramente farlo a livello di database, è possibile farlo con map-reduce. La funzione mappa emetterebbe una chiave e una matrice con un singolo valore, il valore che si desidera ottenere dalla mediana. La funzione di riduzione semplicemente concatenerebbe le matrici dei risultati che riceve, quindi ogni chiave finisce con una matrice con tutti i valori. La funzione finalize calcolerebbe quindi la mediana di quell'array, di nuovo ordinando l'array e quindi ottenendo il numero di elemento size/2.

+0

Ok questo è fantastico, darò un via. – user3080286

20

La mediana è alquanto complicata da calcolare nel caso generale, poiché comporta l'ordinamento dell'intero set di dati o l'utilizzo di una ricorsione con una profondità che è anche proporzionale alla dimensione del set di dati. Questo è forse il motivo per cui molti database non hanno un operatore mediano immediato (anche MySQL non ne ha uno).

Il modo più semplice per calcolare la mediana sarebbe stato con queste due affermazioni (supponendo che l'attributo su cui vogliamo calcolare la mediana si chiama a e vogliamo sopra tutti i documenti della collezione, coll):

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

Questo è l'equivalente di ciò che le persone suggest for MySQL.

+5

So che non è permesso inserire un commento solo per dire grazie .. ma questo è bello :) – fguillen

1

E 'possibile farlo in un colpo solo con il quadro generale.

Sort => inserisci valori di array Array => ottieni Dimensioni di array => divide dimensione per due => ottiene il valore Int della divisione (lato sinistro della mediana) => aggiungi 1 a lato sinistro (lato destro) = > ottenere elemento della matrice al lato sinistro e lato destro => media dei due elementi

Questo è un esempio con la primavera java mongoTemplate:

il modello è un elenco di libro con il login dell'autore ("proprietario "), l'obiettivo è ottenere la mediana del libro dagli utenti:

 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); 

E qui l'aggregazione risultante:

{ 
"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"] 
      } 
     } 
    } 
] 

}

Problemi correlati