2017-02-28 7 views
0

Ich arbeite an einem D3-Diagramm (Multiple Liniendiagramm).
Ich versuche, eine Aktienprognose darzustellen, also enthält das Diagramm im Grunde zwei Zeilen: Stockwerte und eine andere für meine Vorhersage.d3.js - Mehrere Liniendiagramme aktualisieren keine Kreise

Die Vorhersage ist monatlich, alle Tage des Monats sind im Diagramm dargestellt.
Um den Monat zu wählen, habe ich ein Dropdown-Menü hinzugefügt.

Ich habe einen Kreis an jeden Tag Daten angehängt und funktioniert gut zum ersten Mal. Wenn der Benutzer versucht, den Monat zu ändern, werden die alten Kreise nicht aktualisiert, sondern die neuen hinzugefügt.

Folgen Sie den Code über Kreise:

topicEnter.append("g").selectAll("circle") 
    .data(function(d){return d.values}) 
    .enter() 
    .append("circle") 
    .attr("r", 5) 
    .attr("cx", function(dd){return x(dd.date)}) 
    .attr("cy", function(dd){return y(dd.probability)}) 
    .attr("fill", "none") 
    .attr("stroke", "black"); 

Ich habe eine fiddle getan die Situation besser zu verstehen und um Code zu erhalten.

Was fehlt mir hier? Warum aktualisieren sich die Kreise nicht selbst mit den Linien?

Antwort

1

Um das Problem zu Kreisen lösen nicht aktualisieren Sie folgendes tun:

function update(topics) { 
    // Calculate min and max values with arrow functions 
    const minValue = d3.min(topics, t => d3.min(t.values, v => v.probability)); 
    const maxValue = d3.max(topics, t => d3.max(t.values, v => v.probability)); 
    y.domain([minValue, maxValue]); 
    x2.domain(x.domain()); 
    y2.domain(y.domain()); 
    // update axes 
    d3.transition(svg).select('.y.axis').call(yAxis); 
    d3.transition(svg).select('.x.axis').call(xAxis); 
    // Update context 
    var contextUpdate = context.selectAll(".topic").data(topics); 
    contextUpdate.exit().remove(); 
    contextUpdate.select('path') 
    .transition().duration(600) 
    .call(drawCtxPath); 
    contextUpdate.enter().append('g') // append new topics 
    .attr('class', 'topic') 
    .append('path').call(drawCtxPath); 
    // New data join 
    var focusUpdate = focus.selectAll('.topic').data(topics); 
    // Remove extra topics not found in data 
    focusUpdate.exit().remove(); //remove topics 
    // Update paths 
    focusUpdate.select('path') 
    .transition().duration(600) 
    .call(drawPath) 
    // Update circles 
    var circlesUpdate = focusUpdate 
    .selectAll('.topic-circle') 
    .data(d => d.values); 
    circlesUpdate.exit().remove(); 
    circlesUpdate.transition().duration(600).call(drawCircle); 
    circlesUpdate.enter().append('circle').call(drawCircle); 
    // Add new topics 
    var newTopics = focusUpdate.enter().append('g') // append new topics 
    .attr('class', 'topic'); 
    // Add new paths 
    newTopics.append('path').call(drawPath) 
    // Add new circles 
    newTopics.selectAll('.topic-circle') 
    .data(d => d.values) 
    .enter() 
    .append('circle') 
    .call(drawCircle); 
} 

Mit diesen Hilfsfunktionen Code-Duplizierung zu reduzieren:

function drawCtxPath(path) { 
    path.attr("d", d => line2(d.values)) 
    .style("stroke", d => color(d.name)); 
} 
function drawPath(path) { 
    path.attr("d", d => line(d.values)) 
    .attr('clip-path', 'url(#clip)') 
    .style("stroke", d => color(d.name)); 
} 
function drawCircle(circle) { 
    circle.attr('class', 'topic-circle') 
    .attr('clip-path', 'url(#clip)') 
    .attr("r", d => 5) 
    .attr("cx", d => x(d.date)) 
    .attr("cy", d => y(d.probability)) 
    .attr("fill", "none") 
    .attr("stroke", "black"); 
} 

Ich denke, es gibt einige weitere Probleme sind in Ihrem Wenn Sie denselben Monat zweimal auswählen, erhalten Sie einen Fehler, den wir folgendermaßen beheben können:

d3.select('#month_chart').on("change", function() { 
    // Get selected value of the select 
    var month = this.options[this.selectedIndex].value; 
    // Since you have hardcoded data we need to return a new array 
    // This is why if you select the same month twice your code breaks 
    // since parseDate will fail since the data will be already parsed 
    // the second time 
    var monthData = get_monthly_data(month).map(d => { 
    return { 
     date: parseDate(d.date), 
     predicted_bool: d.predicted_bool, 
     target: d.target 
    }; 
    }); 
    // Lets use arrow functions! 
    var keys = d3.keys(monthData[0]).filter(k => k !== 'date'); 
    color.domain(keys); 
    // More arrow functions! 
    var topics = keys.map(key => { 
    return { 
     name: key, 
     values: monthData.map(d => { 
     return { 
      date: d.date, 
      probability: +d[key] 
     }; 
     }) 
    }; 
    }); 
    x.domain(d3.extent(monthData, d => d.date)); 
    update(topics); 
}); 

// A good ol' switch 
function get_monthly_data(month) { 
    switch (month) { 
    case 'gennaio': 
     return data_1; 
    case 'febbraio': 
     return data_2; 
    case 'marzo': 
     return data_3; 
    default: 
     return data_1; 
    } 
} 
jsfiddle

Arbeits:

https://jsfiddle.net/g699scgt/37/

+0

Vielen Dank, eine sehr detaillierte Antwort! Danke nochmal! – Giordano

1

Das Problem ist Ihr Aktualisierungszyklus, aber es gibt eine gute Anzahl von Beispielen für den Prozess zum Eingeben, Aktualisieren und Beenden in d3.

Aber im Wesentlichen:

  1. anhängen Sie ein neues g Element für jede Charge von Kreisen, was bedeutet, dass Sie eine leere Auswahl haben (keine Kreise sind in diesem g noch) nicht jedes Mal, und jeder Datenpunkt angehängt wird (und keine werden entfernt). Sie benötigen diesen zusätzlichen Anhang nicht. Sehen Sie sich die DOM-Struktur jedes Anhangs in Ihrem vorhandenen Code an.

  2. Ihre enter() - Auswahl gibt neue Elemente zurück - nicht geänderte Elemente. Wenn Ihre Gesamtzahl der Elemente gleich bleibt, haben Sie eine leere Eingabe() Auswahl. Sie sollten vorhandene Elemente separat aktualisieren (oder sie alle entfernen und jedes Mal anfügen).

Sie werden etwas näher an this wollen:

// set the data 
    circles = topic.selectAll("circle") 
     .data(function(d){return d.values}); 
// update existing circles  
    circles.attr("cx", function(dd){return x(dd.date)}) 
     .attr("cy", function(dd){return y(dd.probability)}); 
// add new circles  
    circles.enter() 
     .append("circle") 
     .attr("r", 5) 
     .attr("cx", function(dd){return x(dd.date)}) 
     .attr("cy", function(dd){return y(dd.probability)}) 
     .attr("fill", "none") 
     .attr("stroke", "black"); 
    // remove excess circles 
    circles.exit().remove(); 

Sie werden wahrscheinlich wollen auch die Linien zu überarbeiten, die die Linien fügen Sie die Eingabe, zu reflektieren Aktualisierung, Ausgang Zyklus in d3.

+0

Vielen Dank!Du hast recht, ich habe viele Beispiele gefunden, aber ich habe sie nicht gut verstanden – Giordano