2015-04-12 24 views
8

Ho un gruppo di grafici che visualizza un gruppo di dati per me (here), basato su un csv con circa 25.000 righe di dati, ciascuna con 12 parametri. Tuttavia, qualsiasi interazione (come la selezione di un intervallo con il pennello su uno dei grafici) è lenta e poco pratica, completamente diversa da the dc.js demo found here, che gestisce anche migliaia di record ma mantiene animazioni fluide, oppure crossfilter's demo here che ha 10 volte di più record (voli) come faccio io.Ottimizzazione di un gruppo di grafici a linee dc.js

So che i maiali delle risorse principali sono i due grafici a linee, poiché hanno punti dati ogni 15 minuti per circa 8 mesi solidi. La rimozione di uno di essi rende i grafici di nuovo reattivi, ma sono la caratteristica principale delle visualizzazioni, quindi c'è un modo in cui posso farli mostrare dati meno dettagliati?

Il codice per i due grafici linea specificamente è qui sotto:

 var lineZoomGraph = dc.lineChart("#chart-line-zoom") 
      .width(1100) 
      .height(60) 
      .margins({top: 0, right: 50, bottom: 20, left: 40}) 
      .dimension(dateDim) 
      .group(tempGroup) 
      .x(d3.time.scale().domain([minDate,maxDate])); 

     var tempLineGraph = dc.lineChart("#chart-line-tempPer15Min") 
      .width(1100).height(240) 
      .dimension(dateDim) 
      .group(tempGroup) 
      .mouseZoomable(true) 
      .rangeChart(lineZoomGraph) 
      .brushOn(false) 
      .x(d3.time.scale().domain([minDate,maxDate])); 

indipendente ma questione rilevante; come posso modificare l'asse y sui grafici a linee? Di default non comprendono i valori più alti e più bassi trovati nel set di dati, il che sembra strano.

Edit: un po 'di codice che ho scritto per cercare di risolvere il problema:

var graphWidth = 1100; 
var dataPerPixel = data.length/graphWidth; 

var tempGroup = dateDim.group().reduceSum(function(d) { 
    if (d.pointNumber % Math.ceil(dataPerPixel) === 0) { 
     return d.warmth; 
    } 
}); 

d.pointNumber è un ID unico punto per ogni punto di dati, cumulativo 0-22.000 ish. Ora tuttavia il grafico a linee appare vuoto. Ho controllato i dati del gruppo utilizzando tempGroup.all() e ora ogni 21 ° punto dati ha un valore di temperatura, ma tutti gli altri hanno NaN. Non sono riuscito a ridurre le dimensioni del gruppo; è ancora a 22 mila circa. Mi chiedo se questo sia l'approccio giusto ...

Modifica 2: trovato un approccio diverso. Creo normalmente il tempGroup ma poi creo un altro gruppo che filtra ancora il tempGroup esistente.

var tempGroup = dateDim.group().reduceSum(function(d) { return d.warmth; }); 
    var filteredTempGroup = { 
     all: function() { 
      return tempGroup.top(Infinity).filter(function (d) { 
       if (d.pointNumber % Math.ceil(dataPerPixel) === 0) return d.value; 
      }); 
     } 
    }; 

Il problema che ho è che non è accessibile d.pointNumber quindi non posso dire se è il punto di dati ennesimo (o un multiplo di quello). Se lo assegno ad un var sarà comunque un valore fisso, quindi non sono sicuro di come spostarlo ...

+1

Il "falso gruppo "approccio nella tua seconda modifica sembra ragionevole. Poiché i tuoi dati sono probabilmente in ordine cronologico comunque (?), l'indice dovrebbe essere praticamente uguale al 'pointNumber', quindi aggiungere un parametro alla funzione di callback del filtro dovrebbe darti un indice che puoi usare:' .filter (function (d, i) {return (i% Math) .ceil (dataPerPixel) === 0);}) '. Si noti inoltre che la funzione di callback del filtro dovrebbe restituire un valore booleano e non un valore. – Gordon

+0

OK, quindi funziona, un po '. Ottengo risultati 1071 molto più gestibili, ma i risultati sono anche fuori uso, il che mi confonde. Se guardi il sito web in diretta ora vedrai cosa intendo. Gli oggetti del gruppo iniziano correttamente nei primi punti di dati, poi saltano avanti di alcuni giorni e poi tornano indietro ... quindi i punti vanno bene, solo in qualche modo disordinati. – IronWaffleMan

+1

Um, si, probabilmente si vorrebbe usare '.all()' invece di '.top (Infinity)', per ovvi motivi. Mi è mancato. – Gordon

risposta

4

Quando si affrontano problemi di prestazioni con i grafici basati su d3, il solito colpevole è il numero di elementi DOM, non la dimensione dei dati. Si noti che la demo di crossfilter ha molte righe di dati, ma solo un paio di centinaia di barre.

Sembra che si stia tentando di tracciare tutti i punti invece di aggregarli. Immagino che, dal momento che stai facendo una serie temporale, potrebbe non essere intuitivo aggregare i punti, ma considera che la tua trama può mostrare solo 1100 punti (la larghezza), quindi è inutile sovraffaticare il motore SVG che traccia 25.000.

Suggerirei di portarlo in un posto tra 100-1000 bidoni, ad es. facendo una media ogni giorno:

var daysDim = data.dimension(function(d) { return d3.time.day(d.time); }); 

function reduceAddAvg(attr) { 
    return function(p,v) { 
    if (_.isLegitNumber(v[attr])) { 
     ++p.count 
     p.sums += v[attr]; 
     p.averages = (p.count === 0) ? 0 : p.sums/p.count; // gaurd against dividing by zero 
    } 
    return p; 
    }; 
} 
function reduceRemoveAvg(attr) { 
    return function(p,v) { 
    if (_.isLegitNumber(v[attr])) { 
     --p.count 
     p.sums -= v[attr]; 
     p.averages = (p.count === 0) ? 0 : p.sums/p.count; 
    } 
    return p; 
    }; 
} 
function reduceInitAvg() { 
    return {count:0, sums:0, averages:0}; 
} 
... 
// average a parameter (column) named "param" 
var daysGroup = dim.group().reduce(reduceAddAvg('param'), reduceRemoveAvg('param'), reduceInitAvg); 

(media riutilizzabile ridurre funzioni from the FAQ)

Poi specificare l'xUnits per abbinare, e utilizzare elasticY per auto-calcola l'asse y:

chart.xUnits(d3.time.days) 
    .elasticY(true) 
+0

Grazie per il suggerimento ... Sono andato in cerca di altre persone usando le funzioni di ricampionamento sui grafici a linee in d3 ma non ci sono esempi concreti là fuori, che trovo strano dal momento che qualcuno deve aver avuto il mio problema ad un certo punto ... Ho tentato di risolverlo da solo con una funzione che ho inserito in una modifica nella mia risposta originale, ma il problema è che non si sbarazza dei dati originali, ma rende ogni punto di dati N un valore e il resto è NaN. – IronWaffleMan

+0

hai usato le funzioni di riduzione suggerite da Gordon? – Xavier

+0

Il campionamento è in realtà una tecnica di dati piuttosto che una tecnica di creazione di grafici, se questo è utile per Google. Il tuo tentativo mi è sembrato per lo più okay ma non l'ho provato. Personalmente, continuerei con le medie per non perdere dati, dal momento che questa quantità di dati dovrebbe andare bene in JavaScript – Gordon