2013-06-07 16 views
5

Ho un datatable, che contiene diverse colonne. voglio calcolare la media di questi con LINQ, senza raggruppamento: ho provato questo:Calcolare la media con linq senza raggruppamento

dtPointCollFb.GroupBy(r => 1).Select(r => new 
     { 
      Plus100 = r.Average(item => item.PLUS100), 
      Plus50 = r.Average(item => item.PLUS50), 
      Plus10 = r.Average(item => item.PLUS10), 
      Plus5 = r.Average(item => item.PLUS5), 
      Plus1 = r.Average(item => item.PLUS1), 
      NULLA = r.Average(item => item.NULLA) 
     }).ToList() 

Questo funziona perfettamente, quando ho calcolare somma, ma con media producono i primi valori record, non media tutti i record . Penso che questo non sia necessario: .GroupBy (r => 1) perché sto raggruppando con costante.

Ma senza questo non posso usare media solo per l'intera query, e solo per una colonna:

dtPointCollFb.Average(r => r.PLUS100) 

Così qualcuno può produrre una soluzione semplice best practice? Se è possibile con la sintassi del metodo, non la query.

voglio qualcosa di simile:

dtPointCollFb.GroupBy(r => 0).Select(r => new 
     { 
      Plus100 = r.Sum(item => item.PLUS100)/dtPointCollFb.Rows.Count, 
      Plus50 = r.Sum(item => item.PLUS50)/dtPointCollFb.Rows.Count, 
      Plus10 = r.Sum(item => item.PLUS10)/dtPointCollFb.Rows.Count, 
      Plus5 = r.Sum(item => item.PLUS5)/dtPointCollFb.Rows.Count, 
      Plus1 = r.Sum(item => item.PLUS1)/dtPointCollFb.Rows.Count, 
      NULLA = r.Sum(item => item.NULLA)/dtPointCollFb.Rows.Count 
     }).ToList() 

ma con modo più semplice e più pulito. Questo produce la media correttamente.

+0

Non lo stesso, ma esiste un suggerimento per calcolare le medie separatamente. – speti43

+0

Solo per la cronaca, il costrutto 'GroupBy' è, credo, destinato a forzare tutte le medie da calcolare su un singolo round-trip nel database. – Rawling

+0

Sì, sei corretto, sarebbe il modo migliore, perché trasporto molti dati dal server sql. Questo modello è stato progettato per rendere più aggregazione sul server web con linq. Calcolo cose diverse dalla stessa raccolta di dati.Potrei farlo in 5 query sql diverse. Non lo so, forse sarebbe meglio. – speti43

risposta

6

Enumerable.Average calcola una media per una sequenza di numeri. Quindi devi proiettare (ovvero Select) una colonna per ogni media di cui hai bisogno.

dtPointCollFb.Select(r => r.PLUS100).Average() 

o

dtPointCollFb.Average(r => r.PLUS100)  

GroupBy costruisce una lista di liste. In questo caso l'elenco (esterno) ha un elemento. (Dato che si sta arbitrariamente utilizzando la stessa chiave per tutti gli elementi della lista originale)

Quindi quello che hai sopra potrebbe facilmente essere scritte come:

var averages = new 
{ 
    Plus100 = dtPointCollFb.Average(item => item.PLUS100), 
    Plus50 = dtPointCollFb.Average(item => item.PLUS50), 
    Plus10 = dtPointCollFb.Average(item => item.PLUS10), 
    Plus5 = dtPointCollFb.Average(item => item.PLUS5), 
    Plus1 = dtPointCollFb.Average(item => item.PLUS1), 
    NULLA = dtPointCollFb.Average(item => item.NULLA) 
}; 

Senza fare niente di più richiederebbe personalizzato funzioni di estensione (estensione IEnumerable<DataTableClass>).

+0

Bella soluzione :) – speti43

2

Se, come suggerito da Christopher, si desidera utilizzare le estensioni, è possibile utilizzare il metodo Aggregate.

Data la classe di

public class DataPoint 
{ 
    public double Plus100 { get; set; } 
    public double Plus50 { get; set; } 
    public double Plus10 { get; set; } 
    public double Plus5 { get; set; } 
    public double Plus1 { get; set; } 
    public double NULLA { get; set; } 
} 

la funzione di media sarebbe simile a questa (il conteggio manuale evita più enumerazioni attraverso la raccolta):

public static DataPoint Average(this IEnumerable<DataPoint> dataPoints) 
{ 
    var count = 0; 
    var totals = dataPoints.Aggregate((lhs, rhs) => 
     { 
      ++count; 
      return new DataPoint 
       { 
        Plus100 = lhs.Plus100 + rhs.Plus100, 
        Plus50 = lhs.Plus50 + rhs.Plus50, 
        Plus10 = lhs.Plus10 + rhs.Plus10, 
        Plus5 = lhs.Plus5 + rhs.Plus5, 
        Plus1 = lhs.Plus1 + rhs.Plus1, 
        NULLA = lhs.NULLA + rhs.NULLA 
       }; 
     }); 

    return new DataPoint 
     { 
      Plus100 = totals.Plus100/count, 
      Plus50 = totals.Plus50/count, 
      Plus10 = totals.Plus10/count, 
      Plus5 = totals.Plus5/count, 
      Plus1 = totals.Plus1/count, 
      NULLA = totals.NULLA/count 
     }; 
} 

Il vantaggio di questo metodo è che si va solo attraverso la raccolta una volta. Se si dispone di un set di dati di grandi dimensioni, si risparmia energia di calcolo. Se hai meno punti dati, utilizzerei il metodo di Christopher.

+0

Buon consiglio, l'ho fatto come hai fatto tu, ma non ho fatto un metodo di estensione linq, perché uso questo calcolo solo una volta. Ma ho anche fatto un corso annidato, come hai fatto anche tu. Grazie! – speti43

Problemi correlati