8. Vizualizace dat - grafy

Cíle cvičení

  • Vytvoření obrázku (grafu) pomocí knihovny Matplotlib
  • Kreslení dat v reálném čase
  • Kontrola průběžné domácí práce
  • Definitivní stanovení témat semestrálních prací

Podklady

Kreslení grafů pomocí Matplotlib

Matplotlib renderuje data na displej nebo do souboru prostřednictvím backendu, což je vykreslovací zařízení poskytované operačním systémem prostřednictvím vhodné knihovny (např. Qt). Pro naše účely použijeme neinteraktivní statické backendy (tj. nebude se vytvářet vykreslovací okno) a graf budeme transformovanat na proud binárních dat. Tato data pak pošleme společně s vhodným mime-type jako odpověď na dotaz GET z prohlížeče, který renderuje HTML kód obsahující tag <img/>.

Rastrový formát PNG

import io
import random
 
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.backends.backend_svg import FigureCanvasSVG
 
@app.route("/matplot-as-image-<int:num_x_points>.png")
def plot_png(num_x_points=50):
    fig = Figure()
    axis = fig.add_subplot(1, 1, 1)
    x_points = range(num_x_points)
    axis.plot(x_points, [random.randint(1, 30) for x in x_points])
 
    output = io.BytesIO()
    FigureCanvasAgg(fig).print_png(output)
    return Response(output.getvalue(), mimetype="image/png")

HTML kód, který požádá server o graf 200 náhodných bodů ve formátu PNG pak vypadá následovně.

<img src="/matplot-as-image-200.png" alt="random points as png" height="200"/>

Vektorový formát SVG

@app.route("/matplot-as-image-<int:num_x_points>.svg")
def plot_svg(num_x_points=50):
    fig = Figure()
    axis = fig.add_subplot(1, 1, 1)
    x_points = range(num_x_points)
    axis.plot(x_points, [random.randint(1, 30) for x in x_points])
 
    output = io.BytesIO()
    FigureCanvasSVG(fig).print_svg(output)
    return Response(output.getvalue(), mimetype="image/svg+xml")

Využití data URI

Data URI je způsob, jak obsah externího souboru zapsat přímo do HTML/CSS. Pokud není použit protokol HTTP/2, režie navázání spojení pro získáním jednotlivého souboru může být mnohem větší než samotné stažení dat. Při velkém počtu souborů hraje navíc roli maximální počet souběžných spojení, kvůli kterému musí soubory čekat ve frontě.

Následující příklad produkuje datový proud v kódování base64, navíc se využívá toho, že od verze 3.1 knihovny Matplotlib není třeba instacionovat CanvasAgg. Celý příklad je zde.

fig = Figure()
# kod pro vytvoreni grafu
output = io.BytesIO()
fig.savefig(output, format="png")
data = base64.b64encode(buf.getbuffer()).decode("ascii")
return f"<img src='data:image/png;base64,{data}'/>"

Aktualizace grafu v reálném čase

Pro kreslení grafů lze samozřejmě využít render na straně klienta, např. pomocí vhodné knihovny v JavaScriptu. Takovou knihovnou je např. chart.js.

Knihovna umožňuje vykreslovat datové řady do HTML elementu canvas. Nejprve je třeba vytvořit instanci třídy Chart, které se předá handler na element.

var ctx = document.getElementById('tempChart').getContext('2d');
var tempChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: [],
    datasets: [{
      label: 'temperature',
      data: []
    },{
      label: 'humidity',
      data: []
    }]
  }
});

Předpokládejme, že data přichází do prohlížeče asynchronně např. prostřednictvím SSE a jsou tvořena třemi hodnotami (čas, teplota, vlhkost) oddělenými čárkou. Funkce pro zpracování takových dat může vypadat třeba takto:

var eventSource = new EventSource('/stats');
 
eventSource.onmessage = function (event) {
 
  var data = event.data.split(',');
  console.log(data);
  var currentTime = data[0];
  var temp = parseInt(data[1]);
  var humi = parseInt(data[2]);
 
  tempChart.data.labels.push(currentTime);
  tempChart.data.datasets[0].data.push(temp);
  tempChart.data.datasets[1].data.push(humi);
  if (tempChart.data.labels.length > 30) {
    tempChart.data.labels.shift();
    tempChart.data.datasets[0].data.shift();
    tempChart.data.datasets[1].data.shift();
  }
  tempChart.update();
};

Element pro vykreslování pak může vypadat třeba takto:

<canvas id="tempChart" width="800" height="400"></canvas>

Celý příklad ke stažení zde

courses/b0b37nsi/tutorials/08.txt · Last modified: 2024/04/11 08:50 by viteks