3 Punkte von GN⁺ 2024-03-04 | 1 Kommentare | Auf WhatsApp teilen
  • Observable Framework ist ein Open-Source-Tool, das über das Notebook-Modell für schnelle, explorative Datenanalysen hinausgeht und schnell ladende Daten-Apps, Dashboards und Reports als statische Websites bereitstellen soll
  • js-Codeblöcke und Inline-Ausdrücke in Markdown werden im Browser ausgeführt, und wenn sich reaktive Werte wie now ändern, werden die zugehörigen Anzeigen automatisch aktualisiert
  • Das Framework behält die Reaktivität von Observable Notebooks bei, bietet aber zugleich eine einzelne Markdown-Datei, Standard-JavaScript und einen Git-freundlichen Workflow
  • Bibliotheken wie Inputs, d3 und Plot werden während der Entwicklung lazy geladen; bei Build und Deployment wird nur der referenzierte Code automatisch vom jsdelivr CDN geladen
  • Mit einem Data Loader lassen sich Daten zur Build-Zeit in beliebigen Sprachen vorbereiten und als statische Dateien wie JSON oder CSV bündeln, wodurch Dashboards mit weniger Backend-Abhängigkeiten bereitgestellt werden können

Statischer Site-Generator für Daten-Apps

  • Observable Framework ist ein statischer Site-Generator, der Markdown, JavaScript und bei Bedarf auch andere Sprachen zu interaktiven Seiten kompiliert
  • Enthalten ist ein voll ausgestatteter Hot-Reloading-Server, sodass Änderungen nach dem Bearbeiten und Speichern von Dateien im Editor sofort im Browser erscheinen
  • Wenn die Arbeit abgeschlossen ist, kann per Build-Befehl ein Satz statischer Dateien erzeugt werden
    • Diese Dateien können auf einem Server bereitgestellt werden
    • Mit npm run deploy können sie auch direkt auf Observables authentifizierte Sharing-Plattform deployt werden

JavaScript, das in Markdown ausgeführt wird

  • Das Kerndesign des Frameworks besteht darin, JavaScript in Markdown-Dokumente einzubetten, um interaktive Dokumente zu erstellen
  • Mit js markierte Markdown-Codeblöcke werden im Browser des Nutzers als JavaScript ausgeführt
  • Auch Inline-Ausdrücke können verwendet werden; etwa kann ${new Date(now)} die aktuelle Zeit als Zeichenkette anzeigen
  • now ist eine spezielle Variable, die die aktuelle Zeit in Millisekunden seit der Epoch liefert und laufend aktualisiert wird
    • Wenn sich now ändert, werden auch die Zellen und Inline-Ausdrücke aktualisiert, die darauf verweisen
  • In Observable Notebooks werden Code und Markdown in getrennten Zellen geschrieben; im Framework stehen beide in einem einzigen Textdokument
  • Inline-Ausdrücke und js-Blöcke können sich in der Darstellung unterscheiden
    • Inline-Ausdrücke verwenden die Standard-Stringdarstellung von JavaScript-Objekten
    • js-Blöcke verwenden Observables Funktion display(); die Darstellungsregeln stehen in inspect/src/inspect.js

Reaktives Ausführungsmodell bleibt erhalten

  • Die Reaktivität, eine Kernfunktion von Observable Notebooks, bleibt auch in den JavaScript-Markdown-Dokumenten des Frameworks erhalten
  • Wenn sich eine Zelle ändert, werden andere Zellen, die von ihr abhängen, automatisch neu ausgewertet
  • Das ist ein großer Unterschied zu Jupyter Notebooks und außerdem eine zentrale Funktion des Python-Notebook-Tools marimo
  • Besonders wirkungsvoll ist dies zusammen mit Formulareingaben
    • Fügt man einer Seite eine Eingabe hinzu und verweist an anderer Stelle im Dokument auf ihren Wert, lassen sich Echtzeit-Interaktionen leicht erstellen

Beispiel: PyPI-Download-Dashboard

  • Das Beispiel-Dashboard zeigt PyPI-Download-Statistiken nach Python-Paket; die Observable-Framework-Version besteht aus einem 57 Zeilen langen Markdown-Dokument
  • Nutzer wählen mit Inputs.select() ein Paket aus dem Array packages aus
    • Inputs.select() ist eine im Framework enthaltene Methode und in der Dokumentation zu Observable Inputs beschrieben
    • Die Funktion view() ist eine neu im Framework hinzugekommene Funktion und sorgt dafür, dass Änderungen der Eingabeauswahl in anderen Codeblöcken des Dokuments sichtbar werden
  • packageName wird als const definiert und kann in anderen js-Blöcken der Seite verwendet werden
  • Die Daten werden mit d3.json() abgerufen
    • Im Framework kann D3 vollständig genutzt werden
    • Die URL enthält den Namen des ausgewählten Pakets
    • Die Datenquelle ist die JSON-API von Datasette
  • Die SQLite-Tabelle befindet sich unter datasette.io/content/stats und wird einmal täglich mit aktuellen PyPI-Paketstatistiken aktualisiert
    • Der zugehörige GitHub-Actions-Workflow wurde in einem früheren Beitrag zu baked data behandelt
  • Hängt man .json an die URL an, wird JSON zurückgegeben
    • Es werden nur Zeilen für ein bestimmtes Paket angefordert
    • Sortiert wird absteigend nach Datum
    • Es werden maximal 1.000 Zeilen als Array von Objekten empfangen
  • SQLite-Datumsstrings werden mit d3.timeParse("%Y-%m-%d") in JavaScript-Date-Objekte umgewandelt
  • Das Diagramm wird mit Observable Plot gerendert, das zusammen mit dem Framework paketiert ist
  • Die Paketliste wird über eine direkte SQL-Abfrage an die Datenbank /content von Datasette abgerufen
    • Die Abfrage lautet select package from stats group by package order by max(downloads) desc
    • _shape=arrayfirst ist eine Abkürzung, um die erste Spalte der Ergebniszeilen als JSON-Array zu erhalten

Nur verwendeten Code einbinden

  • Das Beispiel-Dashboard verwendet zusätzliche Bibliotheken wie Inputs, d3 und Plot
  • Im Entwicklungsmodus kommt Lazy Loading zum Einsatz
    • Code wird erst geladen, wenn er in einer Zelle zum ersten Mal verwendet werden soll
  • Wenn die Anwendung gebaut und deployt wird, lädt das Framework automatisch nur den referenzierten Bibliothekscode vom jsdelivr CDN

Daten-Caching zur Build-Zeit

  • Der Data Loader des Frameworks ist eine Funktion, um Dashboard-Daten zur Build-Zeit vorzubereiten
  • Framework-Dashboards können zur Laufzeit fetch() oder Wrapper darum verwenden, um Daten von überall abzurufen
    • Observable Notebooks funktionieren auf dieselbe Weise
    • Bei diesem Ansatz hängt die Dashboard-Performance vom angebundenen Backend ab
  • Das Framework empfiehlt ein Muster, bei dem beim Deployment Daten für das Dashboard erzeugt und nur die benötigten Teilmengen als statische Dateien gebündelt werden
    • Statische Datendateien können über dasselbe statische Hosting wie der Dashboard-Code schnell ausgeliefert werden
  • Ein Data Loader ist ein Skript, das in einer beliebigen Programmiersprache geschrieben ist
    • Beim Build führt das Framework das Skript aus
    • Die Standardausgabe des Skripts wird als Datei gespeichert
  • Das Beispiel verwendet eine Datei quakes.json.sh, die curl https://earthquake.usgs.gov/earthquakes/feed/… enthält
    • Beim Build teilt der Dateiname dem Framework mit, dass die Zieldatei quakes.json heißt und der auszuführende Loader .sh ist
  • Solange JSON, CSV oder ein anderes nützliches Format auf die Standardausgabe geschrieben werden kann, lassen sich Daten mit jeder Technologie abrufen

Unterschiede zu Observable Notebooks

  • Observable Framework nutzt viele Ideen und viel Code aus Observable Notebooks wieder, unterscheidet sich aber deutlich bei Dateiformat und Ausführungsumgebung
  • Bestehende Observable Notebooks haben im Vergleich zu Jupyter Notebooks folgende Eigenschaften
    • Sie verwenden JavaScript statt Python
    • Der Notebook-Editor selbst ist nicht Open Source, sondern ein gehostetes Produkt auf observablehq.com
    • Notebooks lassen sich als statische Dateien exportieren und überall ausführen, der Editor ist jedoch proprietär
    • Zellen sind reaktiv; wenn sich eine Zelle ändert, werden andere Zellen, die von ihr abhängen, ähnlich wie in Excel automatisch neu ausgewertet
    • Um das Reaktivitätsmodell zu unterstützen, wurde ein eigenes Schlüsselwort namens viewof eingeführt, weshalb die JavaScript-Syntax nicht vollständig Standard-JavaScript ist
    • Editierbare Notebooks haben ein komplexes proprietäres Dateiformat und passen nicht gut zu Tools wie Git; daher hat Observable ein eigenes System für Versionsverwaltung und Zusammenarbeit implementiert
  • Observable Framework überträgt dieses Modell in ein einfacheres Dateiformat und eine Open-Source-Ausführungsumgebung
    • Ein Dokument ist eine einzelne Markdown-Datei mit JavaScript-Blöcken
    • Es ist weiterhin reaktiv, lässt sich aber mit jedem Texteditor bearbeiten und in Git verwalten
    • Das Ganze ist Open Source unter der ISC-Lizenz, und der gesamte Editor-Stack kann auf dem lokalen Rechner ausgeführt werden
    • Es verwendet ausschließlich Standard-JavaScript ohne eigene Syntax

Richtungswechsel bei Observable

  • Observable Framework wirkt wie eine Veränderung, bei der Observable sich vom bisherigen Fokus auf kollaborative Tools rund um den proprietären Observable-Notebook-Editor stärker in Richtung Developer Tools bewegt
  • Observables Twitter-Beschreibung lautet: „The end-to-end solution for developers who want to build and host dashboards that don’t suck“
    • In einer Kopie des Internet Archive vom 3. Oktober 2023 stand: „Build data visualizations, dashboards, and data apps that impact your business — faster.“
  • Die Nutzung von Observable Notebooks kann aufgrund der Proprietarität der Plattform und der Einschränkungen kostenloser Konten teilweise begrenzt sein, insbesondere wegen fehlender kostenloser privater Notebooks
  • Open-Source-Bibliotheken wie Observable Plot gelten bereits als Technologien, die sich aktiv nutzen lassen
  • Observable Framework setzt die Ideen, die Observable Notebooks attraktiv gemacht haben, mit Open Source, Standard-JavaScript, einer einzelnen Textdatei und einem statischen Deployment-Modell neu um

1 Kommentare

 
GN⁺ 2024-03-04
Meinungen auf Hacker News
  • In gewisser Weise wirkt Observable Framework wie Avengers: Endgame im Mike-Bostock-Cinematic-Universe.
    Es bringt d3, Observable, Observable Plot und HTL zusammen und legt dazu noch einige neue Ideen obendrauf.

    • Persönlich mag ich Polymaps von seinen Werken immer noch am liebsten.
    • Es fühlt sich an, als würde hier etwas für „menschlich verstärkte“ AI Developer Agents gebaut.
      Observable hat bereits AI-Integration, und das hier wirkt wie ein Wrapper, der es AI erleichtert, Dinge zusammenzusetzen und zu nutzen. Die Stelle, an der die Strategie ohne AI bewertet wurde, fühlte sich etwas seltsam an.
    • Ich hatte Observable und Observable Framework früher gebookmarkt, sie mir aber nicht im Detail angesehen.
      Heute habe ich angefangen, mir anzuschauen, wie man statische Jupyter Notebooks hostet oder sie mit WASM interaktiv bereitstellt; für die meisten Anwendungsfälle scheint Observable Framework besser zu passen.
  • Das Problem mit Observable ist: Es sieht zwar wie eine Sammlung von d3-Beispielen aus, aber der Code ist dafür entworfen, innerhalb dieses Frameworks zu laufen, sodass man ihn nicht einfach kopieren und einfügen kann.
    d3 ist auch mit Beispielen nicht gerade leicht zu benutzen, besonders weil Änderungen zwischen Versionen oft nicht kompatibel sind. Trotzdem gibt es auf der Website viele erstaunliche Grafiken.
    [0] https://observablehq.com/@d3/gallery

    • Stimme zu 100 % zu. Ich konnte mich letztlich nie damit anfreunden, dass es sich leicht von echtem JavaScript unterscheidet.
      Es ist nah genug an der Basissprache, dass man wohl einfach JavaScript hätte verwenden und nur ein paar APIs zur Anzeige von Grafiken hinzufügen können.
    • Die Community hatte einmal Ressourcen veröffentlicht, um Observable-JavaScript in normales JavaScript umzuwandeln.
      Meist geht es darum, Top-Level-Zellendefinitionen umzuschreiben.
      [0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
    • Genau das ist extrem frustrierend. Es fühlt sich wie Plattformabhängigkeit an, auf die kein Unternehmen stolz sein sollte.
      Bei ObservableHQ-Notebooks war diese Frustration besonders groß. Sie sind großartige Beispiele und zugleich als Material unbrauchbar. Dieses Framework wirkt allerdings etwas offener, und zumindest Self-Hosting ist möglich, also beobachte ich es weiter.
    • Das scheint mir weniger ein Problem von Observable zu sein als vielmehr eines von d3, weil d3 keine Beispiele bereitstellt, die man anderswo kopieren und einfügen kann.
      Außerdem geht es in diesem Artikel darum, dass das neue Observable Framework einige Probleme der alten Observable-Notebooks beseitigt hat; daher geht dieser Kommentar ein wenig am Artikel vorbei. Jetzt heißt es im Grunde: „Alles ist Standard-JavaScript, es gibt keine Custom-Syntax.“
  • Das Framework lässt sich auch sehr einfach auf GitHub Pages deployen.
    Ich habe die Schritte und eine Beispiel-GitHub-Action zusammengestellt: https://notes.billmill.org/programming/observable_framework/...

  • Der Autor hat das Framework genau richtig eingeordnet.
    Ich habe mit Observable Framework einen kleinen interaktiven Plot gebaut (https://github.com/willmeyers/observable-ssta), und das Setup sowie das Plotten der Daten waren unglaublich einfach. Meine einzige Beschwerde ist, dass man Python Data Loader so konfigurieren können sollte, dass sie eine virtualenv verwenden.

    • Ich nutze eine Konfiguration mit poetry, bei der der Python Data Loader innerhalb der von poetry verwalteten virtualenv ausgeführt wird.
      Nachdem man ein Python-Projekt erstellt hat, startet man den Development Server nicht mit yarn run dev, sondern mit poetry run yarn run dev; dadurch läuft Python innerhalb der virtualenv. Diese Konfiguration erlaubt es, wiederverwendbaren und unit-testbaren Code für Data Loader als eigenes Python-Package zu definieren und ihn dann in *.json.py-Dateien zu importieren, sodass diese sehr einfach bleiben.
    • Lässt sich das nicht mit Pythons nodeenv lösen? So mache ich es normalerweise, wenn ich einem Projekt JS hinzufüge.
      Werkzeuge wie node und npm/yarn sowie JavaScript selbst bleiben dann gemeinsam in der venv.
    • Könnte man nicht einen Shebang in einen .sh-Data-Loader setzen und auf den vollständigen Pfad zu bin/python im Verzeichnis der virtuellen Umgebung zeigen lassen?
  • Ich habe kürzlich mein erstes „echtes“ Projekt mit einem Observable Notebook abgeschlossen.
    Dazu gehörten das Lernen von Observable Plot und Arquero, das Wiederauffrischen einiger JavaScript-Kenntnisse und die Integration mit dem Datengenerierungsprozess, einem Rust-basierten Simulator. Ehrlich gesagt war es wirklich großartig. Das Erlernen der Tools hat einiges an Energie gekostet, und die Parametrisierung des Datengenerators ist noch nicht ganz zufriedenstellend, aber das fertige Notebook ist schön und funktioniert gut.
    Mit Markdown und Reaktivität fühlen sich solche Notebooks tatsächlich brauchbar an. Jupyters Custom-Format erschwert Versionskontrolle erheblich, und ohne Reaktivität werden iterativ entwickelte Notebooks leicht zu zustandsbasierten Durcheinandern, die zwar einfach zu schreiben, aber schwer zu lesen sind. Ich habe es auch mit Quarto und dessen Observable-Integration versucht, aber das wirkte wie ein provisorisches Zusammenstückeln.
    Ganz ehrlich: Es war das erste Mal, dass es Spaß gemacht hat und ich mich darauf gefreut habe, ein Notebook zu schreiben und mit anderen zu teilen. Es wird sicher noch weitere scharfe Kanten geben, aber seit diesem Projekt ist es meine erste Wahl unter den Notebook-Tools.

    • Wenn du nach einer Alternative zu Quarto suchst, lohnt sich ein Blick auf das kürzlich veröffentlichte Living Papers, mit dem man reaktive/statische Dokumente aus einer einzigen Quelle erstellen kann.
      [0]: https://living-papers.vercel.app/
  • Wenn man das Framework schnell im Browser ausprobieren und damit herumspielen möchte, wurde ein Codespace devcontainer erstellt, der die Node- und Python-Umgebung automatisch einrichtet.
    [0]: https://github.com/dleeftink/observable-codespace

  • Sollte man von Jupyter Notebook zu Observable wechseln? Oder ist schon die Aufteilung der beiden auf diese Weise der falsche Ansatz?

    • Letztlich ist die Kernfrage wohl, ob man in Python und dessen Ökosystem produktiver ist oder in JavaScript und dessen Paketen, insbesondere Dingen wie D3.
  • Anders gesagt: Alles innerhalb eines Codeblocks mit dem Content-Hint js wird sofort im Browser des Nutzers ausgeführt.
    Um Code anzuzeigen, muss man den Hint js echo verwenden. Mit Blick auf Abwärtskompatibilität wäre es nicht besser gewesen, es umgekehrt zu machen? Also etwa Code, der im Browser des Nutzers ausgeführt wird, per Opt-in-Hint wie js exec zu markieren und den weit verbreiteten Hint js unverändert zu lassen. Mit der aktuellen Struktur muss man beim Integrieren dieses Renderers in bestehende Apps separat verwalten, wo Ausführung erlaubt ist.

    • Es ist geplant, die Standardoptionen für Blöcke änderbar zu machen.
      Das soll pro Seite über Front Matter oder projektweit über die Projekteinstellungen möglich sein. Dann kann man js run=false zum Standard machen und Live-Code nur bei Bedarf mit js run aktivieren. Unser primärer Use Case ist allerdings Live-Code, daher haben wir das als Standard gewählt.
    • Stimmt. Das ist dasselbe Problem wie beim automatischen Rendern von Mermaid-Diagrammen auf GitHub.
      Jetzt kann man Mermaid-Codeblöcke nicht mehr einfach anzeigen, ohne die Sprachannotation zu entfernen.
  • Ich habe eine Nacht lang intensiv mit Observable Framework verbracht, und es war großartig.
    Es stand kaum im Weg, und ich konnte meinen Google-Maps-Verlauf detailliert visualisieren und erkunden. Der Teil zur Data-Loader-Umgebung war nicht ganz klar, aber es funktionierte, nachdem Python in einer poetry-Umgebung lief.
    Da ich Kotlin mag, wollte ich auch einen Data Loader für Kotlin-Skripte bauen, aber dabei gab es raue Kanten. Kotlin erwartet, dass Skriptdateien foo.main.kts heißen, Observable erwartet bei ausführbaren Shebang-Loadern dagegen die Erweiterung foo.exe. Also habe ich ein Proxy-exe-Skript gebaut, das das Kotlin-Skript aufruft, aber dann wird das automatische Neuladen der Daten nicht ausgelöst.
    Ein kleiner Nachteil gegenüber marimo oder Jupyter ist die Verwendung von Variablen zwischen Data Loader und Notebook. Wenn ich zum Beispiel über eine Datumsauswahl-View-Komponente den Datenbereich ändern möchte, den der Loader abruft, ist nicht klar, wie das gehen soll. Dadurch wird explorative Analyse etwas langsamer. Mir ist klar, dass das nicht ganz zum Paradigma passt, aber ich wollte es erwähnen. Am Ende kann es passieren, dass man beim Explorieren einen erheblichen Teil der Datenaufbereitung ins Notebook verlagert, was aus Performance-Sicht nicht ideal ist.
    Schließlich wäre es schön, Data Loader inline definieren zu können. Ich mag einzelne Dateien; wenn man also einen Python-Codeblock hinzufügen könnte und das Framework ihn in eine Datei extrahiert, wäre das eine kleine Verbesserung der Lebensqualität. Es ist noch früh, aber Framework wirkt vielversprechend. Ich würde gern alle meine Markdown-Notizen dort ablegen und mir eine org-mode-ähnliche Umgebung schaffen, ohne gleich bis zu einem vollständigen Emacs gehen zu müssen.

    • Danke für das Feedback. Es gibt einen offenen PR, der es erleichtert, neue Interpreter zu registrieren, ohne den Umweg über .sh oder .exe zu nehmen.
      Damit soll man einen Interpreter angeben können, der mit einer bestimmten Dateiendung verknüpft ist, zum Beispiel .kts für Kotlin. https://github.com/observablehq/framework/pull/935
      Data Loader über Eingabewerte zu steuern, passt insofern nicht ganz zur Ausrichtung des Frameworks, als es statische Datensnapshots bevorzugt. Ziel ist, dass die gebaute Site in sich abgeschlossen und performant ist. Eine Technik, die dennoch gut funktioniert, ist, im Data Loader eine Obermenge der interaktiv zu nutzenden Daten als Parquet-Datei zu erzeugen und clientseitig mit DuckDB/SQL die Teilmenge für die Visualisierung herauszufiltern. Das ist in der Regel performant, hängt aber natürlich von der Größe der Obermenge ab, mit der man arbeiten möchte.
  • Observable lässt sich über die REST API sehr gut mit ClickHouse integrieren.
    Ein Beispiel gibt es hier: https://observablehq.com/@stas-sl/github-issues-survival-ana...
    Das neue Observable Framework habe ich noch nicht ausprobiert, aber ein ähnliches Beispiel, das eine Datenbank in Echtzeit abfragt, wäre interessant. Ich hoffe, dass es nicht so angelegt ist, dass man nur alle Daten vorab laden und cachen kann. Solche Apps sollten interaktiv sein; idealerweise sollte man SQL live editierbar freigeben können.