Interessante Ideen im Observable Framework
(simonwillison.net)- 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 wienowä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,d3undPlotwerden 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 deploykö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
jsmarkierte 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 nowist 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
- Wenn sich
- 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 Funktiondisplay(); 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 ArraypackagesausInputs.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
packageNamewird alsconstdefiniert und kann in anderenjs-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
.jsonan 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
/contentvon Datasette abgerufen- Die Abfrage lautet
select package from stats group by package order by max(downloads) desc _shape=arrayfirstist eine Abkürzung, um die erste Spalte der Ergebniszeilen als JSON-Array zu erhalten
- Die Abfrage lautet
Nur verwendeten Code einbinden
- Das Beispiel-Dashboard verwendet zusätzliche Bibliotheken wie
Inputs,d3undPlot - 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, diecurl https://earthquake.usgs.gov/earthquakes/feed/…enthält- Beim Build teilt der Dateiname dem Framework mit, dass die Zieldatei
quakes.jsonheißt und der auszuführende Loader.shist
- Beim Build teilt der Dateiname dem Framework mit, dass die Zieldatei
- 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
viewofeingefü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
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.
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.
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
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.
Meist geht es darum, Top-Level-Zellendefinitionen umzuschreiben.
[0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
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.
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.
Nachdem man ein Python-Projekt erstellt hat, startet man den Development Server nicht mit
yarn run dev, sondern mitpoetry 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.Werkzeuge wie node und npm/yarn sowie JavaScript selbst bleiben dann gemeinsam in der venv.
.sh-Data-Loader setzen und auf den vollständigen Pfad zubin/pythonim 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.
[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?
Anders gesagt: Alles innerhalb eines Codeblocks mit dem Content-Hint
jswird sofort im Browser des Nutzers ausgeführt.Um Code anzuzeigen, muss man den Hint
js echoverwenden. 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 wiejs execzu markieren und den weit verbreiteten Hintjsunverändert zu lassen. Mit der aktuellen Struktur muss man beim Integrieren dieses Renderers in bestehende Apps separat verwalten, wo Ausführung erlaubt ist.Das soll pro Seite über Front Matter oder projektweit über die Projekteinstellungen möglich sein. Dann kann man
js run=falsezum Standard machen und Live-Code nur bei Bedarf mitjs runaktivieren. Unser primärer Use Case ist allerdings Live-Code, daher haben wir das als Standard gewählt.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.ktsheißen, Observable erwartet bei ausführbaren Shebang-Loadern dagegen die Erweiterungfoo.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.
.shoder.exezu nehmen.Damit soll man einen Interpreter angeben können, der mit einer bestimmten Dateiendung verknüpft ist, zum Beispiel
.ktsfür Kotlin. https://github.com/observablehq/framework/pull/935Data 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.
Diese Demo lädt Daten zur Laufzeit mit
fetch(): https://simonw.github.io/observable-framework-experiments/pa...