inline-python

Für jeden Zweck das passende Werkzeug: In meinem Alltag bedeutet das, dass ich Simulationen in Rust schreibe und in Python visualisiere. Dank inline-python geht das sogar sehr reibungslos.

use inline_python::python;

fn main() {
    let x: Vec<f32> = (0..628).map(|i| i as f32 / 100.).collect();
    let y: Vec<f32> = x.iter().map(|x| x.sin()).collect();

    python! {
        import numpy as np
        from matplotlib import pyplot as plt

        plt.plot('x, 'y)
        plt.show()
    }
}

Dieses Minimalbeispiel ist natürlich nicht nützlich, aber ich habe es bereits produktiv genutzt, um Dynamik auf petgraph Graphen zu simulieren und ihren Zustand per graph-tool zu visualisieren.

Graph state visualized with graph-tool

Platzhalterbilder

Große Bilder können die Ladezeit von Webseiten dramatisch verschlechtern. Schlimmer als weiße Flächen ist das sprungartige Verschieben des Textes, wenn weiter oben gerade ein Bild fertig geladen wurde. Allerdings müssen Bilder bei immer weiter steigenden Pixeldichten der Anzeigegeräte auch immer hochaufgelöster werden und gleichzeitig über langsame 3G-Verbindungen geladen werden.

Da der Eintrag über Fraktale einige recht große Bilder enthält, habe ich ein Pelican-Plugin geschrieben, das

  1. Vorschau-.jpg erzeugt, die in der Regel kleiner als 1 kB sind,
  2. jedes Bild durch die data-uri des Previews ersetzt und dies verschwommen anzeigt, bis das Originalbild per JavaScript nachgeladen ist.

Das sieht dann etwa so aus:

Glücklicherweise ist es recht einfach mit Python html zu parsen und data-uri zu erzeugen, sodass mein Plugin im Wesentlichen fertig generiertes html nimmt und folgendes tut:

import base64
from bs4 import BeautifulSoup

with open("file.html") as f:
    soup = BeautifulSoup(f, "html.parser")

    for img in soup.find_all("img"):
        thumbnail = create_thumbnail(img)
        b64 = base64.b64encode(open(thumbnail, "rb").read()).decode("utf-8")
        data_uri = f"data:image/jpeg;base64,{b64}"
        # TODO: replace img source by the data-uri

Nachdem alles vorbereitet ist, ist die clientseitige Logik mit ein paar Zeilen JavaScript und CSS recht simpel.

Die Idee ist, dynamisch die voll aufgelösten Bilder per JavaScript zu laden und mit dem onLoad Event sichtbar zu machen.

A Graph a Day

Vor einiger Zeit habe ich @randomGraphs geschrieben: Ein Twitterbot, der einen Zufallsgraphen pro Tag tweetet.

Die meisten Graphtypen, die er darstellen kann stammen aus der NetworkX Bibliothek oder sind reale Netzwerke. Ein paar Proximity Graphs habe ich selbst geschrieben. Die Darstellung und gegebenenfalls das Layout übernimmt Cytoscape oder graph-tool (dessen Autor diesem Bot folgt).

Bei diesem Projekt habe ich exzessiv Gebrauch von Pythons Decorator und Introspection gemacht, sodass man, um einen neuen Graphtyp einzuführen nur eine Methode schreiben muss, die eine Graph-Datenstruktur zurück gibt. Einstellungen, welche Darstellungen erlaubt sind, werden per decorator getätigt und alle Methoden werden per Introspection automatisch zum Pool hinzugefügt, aus dem der Zufallsgenerator zieht.

Eine typische Methode sieht etwa so aus.

@synonym("Barabasi Albert")
@synonym("preferential attachment")
@style(styles_all)
@layout(["kamada-kawai", "force-directed", "sfdp", "fruchterman_reingold", "arf", "radial_tree"])
def generateBarabasiAlbert(self, N=None, m=None, **kwargs):
    if N is None: N = random.randint(4, 400)
    if m is None: m = random.randint(1, 5)

    G = gen.barabasi_albert_graph(N, m)  # gen is networkx Generator
    details = dict(name="Barabási-Albert Graph", N=N, m=m, seed=self.seed,
                   template="{name}, N = {N}, m = {m}")

    return G, details

Und liefert für \(N=226, m=1\) und das radial_tree Layout beispielsweise diesen Graph. Die Größe der Knoten wird hier von der Betweenness Centrality bestimmt.

Graph

Die @synonym Decorators ermöglichen die zweite Funktion des Bots, denn er tweetet nicht nur einmal am Tag einen zufälligen Graphen, sondern reagiert auch auf Mentions. Falls in der Mention der Name der Methode oder eines der per @synonym registrierten Worte auftaucht, antwortet er mit einem Bild des entsprechenden Graphen. Dank fuzzywuzzy ist es sogar resistent gegen Tippfehler.

Twitter unterstützt leider keine Vektorgrafiken und wandelt Bilder gerne in stark komprimierte .jpg, was gerade bei diesen Graphen zu störenden Artefakten führt. Dagegen hilft es, wenn ich einen Rand aus transparenten Pixeln dem Bild hinzufüge. Das führt dazu, dass Twitter .jpg nicht als geeignetes Format ansieht und die Bilder im verlustfreien .png ausliefert.

convert -alpha on -channel RGBA -bordercolor "rgba(0,0,0,0)" -border "1x1" input.png output.png

Graph

Der komplette Quellcode ist auf Github.