Data Sci Abenteuer - teil 3, fischiges Histogramm

Veröffentlicht am 2021-05-27 18:18

Verfügbare Sprachen:

Dieser Artikel ist der nächste in der Data Sci Serie und folgt den vorhergehenden Artikel der erforcht den Datensatz mit Python und R. In diesem Artikel ist ein Histogramm der fishigen Art.

Deutsch ist meine dritte Sprache und ich bin nicht gewöhnt auf Deutsch zu schreiben. Entschuldigen Sie, bitte, meine Fehler.

Leider das Buch von Scott Murray behandelt die Histogramme nicht. So ich muss ObservableHQ ansehen, ob da ein Beispiel des d3.bin Funktion ist. Leider, finde ich die Beispiele bei ObservableHQ ganz schwer zu lesen, so dieser Artikel ist meisten meine Notizen für zukünftige Projekte.

Als der erste Schritt werden wir die Daten filtern und als JSON in Python speichern.

for i in range(len(group)):
    partial = {}
    for column in list(group.columns):
        partial[column] = group.iloc[i][column]
    result.append(partial)

with open(os.path.join(os.getcwd(), 'grouped', f"{specie}.json"), 'w') as f:
    f.write(json.dumps(result))

Der zweite Schritt ist die Einladung der Daten in d3.

d3.json("url to data", (data) => {
        return data
    }).then((data) => {
      // Code wird hier sein
    })

Der wichtigste Teil des Histogramm ist die Erstellung der Kategorien. In d3 gibt es eine Funktion bin() die erstellt die Kategorien. Im nächsten Beispiel ist meine Implementierung:

const buckets = d3.bin()(data.map((item) => item["Weight"]));

Dies wird gefolgt von der Einstellung des Diagramms.

const width = 350,
      height = 300,
      margin = { top: 60, right: 20, bottom: 40, left: 40},
      maxBins = d3.max(buckets, d => d.length),
      max = buckets[buckets.length - 1].x1,
      min = buckets[0].x0,
      svg = d3.select("#hist")
              .append("svg")
              .attr("height", height)
              .attr("width", width),

Als nächste sind die Achsen:

x = d3.scaleLinear()
       .domain([min, max])
       .range([30, width - 30])
       .clamp(false),
y = d3.scaleLinear()
      .domain([0, maxBins])
      .nice() // nice() gibt us ein neues Intervall [niceStart, niceEnde]
      .range([height - margin.bottom, margin.top]), // änhlich wie range in Python
xAxis = g => g.attr("transform", `translate(0,${height - margin.bottom})`)
              .call(d3.axisBottom(x).tickSizeOuter(0))
              .call(g => g.append("text")
                          .attr("x", (width - margin.right)/2)
                          .attr("y", 35)
                          .attr("fill", "#000")
                          .attr("text-anchor", "middle")
                          .text("Weight [g]")
                    );

So jetzt werden wir alles zusammenstellen. Im ersten Block haben wir svg Element herstellen. Der Histogramm benutzt die Rechtecke, rect Element. Nachdem ist die data-Funktion, die die Daten akzeptiert. Als letzter Schritt stellen wir die Parameter der Rechtecke (Farbe, Breite, usw.).

Der zweite Befehl stellt die Achse im svg Element ein.

svg.append("g")
   .selectAll("rect")
   .data(buckets)
   .join("rect")
   .attr("fill", (d => binColor(d.x0)))
   .attr("x", d => x(d.x0) + 1)
   .attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1))
   .attr("y", d => y(d.length))
   .attr("height", d => y(0) - y(d.length));

svg.append("g").call(xAxis);

Kein gutes Diagramm ist ohne den Titel und die Beschriftungen.

const labels = svg.append("g")
                  .selectAll("text")
                  .data(buckets.filter(d => d.length > 0))
                  .join("text")
                  .attr("x", d => ((x(d.x0) + x(d.x1)) / 2) | 0)
                  .attr("y", d => y(d.length) - 2)
                  .style("fill", "black")
                  .style("font-size", 10)
                  .style("text-anchor", "middle");
      labels.text(d => {
            if (x(d.x1) - x(d.x0) < 50) {
                return d.length
            } else if (d.length > 1) {
                return `${d.length} items`
            } else if (d.length === 1) {
                return "1 item"
            } else {
                return "empty bucket"
            }
        });

svg.append("g")
   .append("text")
   .text("Bream weight distribution")
   .style("fill", "#000")
   .attr("font-weight", "bold")
   .style("font-size", 14)
   .style("text-anchor", "end")
   .attr("x", 250)
   .attr("y", 30);

Alles zusammen sieht das aus:

Link zu den Fischdaten