3 puntos por GN⁺ 2024-03-04 | 1 comentarios | Compartir por WhatsApp
  • Observable Framework es una herramienta de código abierto que va más allá del modelo de notebooks fuerte para exploración temporal de datos, y busca desplegar como sitios estáticos apps de datos, dashboards y reportes que cargan rápido
  • Los bloques de código js y las expresiones inline dentro de Markdown se ejecutan en el navegador, y cuando cambian valores reactivos como now, también se actualizan automáticamente las visualizaciones relacionadas
  • Framework mantiene la reactividad de Observable Notebooks, pero además ofrece un solo archivo Markdown, JavaScript estándar y un flujo de trabajo amigable con Git
  • Bibliotecas como Inputs, d3 y Plot se cargan de forma diferida durante el desarrollo, y al compilar y desplegar solo se carga automáticamente desde jsdelivr CDN el código realmente referenciado
  • Con Data loader se pueden preparar datos en tiempo de compilación usando cualquier lenguaje y empaquetarlos como archivos estáticos como JSON o CSV, lo que permite desplegar dashboards con menos dependencia de backend

Generador de sitios estáticos para apps de datos

  • Observable Framework es un generador de sitios estáticos que mezcla Markdown, JavaScript y, si hace falta, otros lenguajes para compilar páginas interactivas
  • Incluye un servidor con hot reloading completo, así que al modificar y guardar archivos en el editor, los cambios se reflejan de inmediato en el navegador
  • Cuando se termina el trabajo, se puede crear un conjunto de archivos estáticos con el comando de compilación
    • Estos archivos se pueden desplegar en un servidor
    • También se pueden desplegar directamente en la plataforma autenticada de uso compartido de Observable con npm run deploy

JavaScript que se ejecuta dentro de Markdown

  • El diseño central de Framework consiste en insertar JavaScript dentro de documentos Markdown para crear documentos interactivos
  • Los bloques de código Markdown etiquetados como js se ejecutan como JavaScript en el navegador del usuario
  • También se pueden usar expresiones inline, y por ejemplo ${new Date(now)} puede mostrar la hora actual como cadena
  • now es una variable especial que entrega la hora actual en milisegundos desde epoch y se actualiza continuamente
    • Cuando now cambia, también se actualizan las celdas y expresiones inline que la referencian
  • En Observable Notebooks, el código y el Markdown se escriben en celdas separadas, pero en Framework ambos viven dentro de un solo documento de texto
  • Las expresiones inline y los bloques js pueden mostrarse de forma distinta
    • Las expresiones inline usan la representación de cadena predeterminada de los objetos JavaScript
    • Los bloques js usan la función display() de Observable, y las reglas de visualización están en inspect/src/inspect.js

Mantener el modelo de ejecución reactivo

  • La reactividad, una función central de Observable Notebooks, también se mantiene en los documentos Markdown con JavaScript de Framework
  • Si una celda cambia, otras celdas que dependen de ella se vuelven a evaluar automáticamente
  • Esta forma de trabajar es una gran diferencia frente a Jupyter notebooks, y también es una característica emblemática de marimo, una herramienta de notebooks para Python
  • Funciona especialmente bien junto con entradas de formulario
    • Si se agrega una entrada en la página y su valor se referencia en otras partes del documento, se puede crear interacción en tiempo real fácilmente

Ejemplo de dashboard de descargas de PyPI

  • El dashboard de ejemplo muestra estadísticas de descargas de PyPI por paquete de Python, y la versión en Observable Framework está compuesta por un documento Markdown de 57 líneas
  • El usuario elige un paquete del arreglo packages con Inputs.select()
    • Inputs.select() es un método incluido en Framework y se puede consultar en la documentación de Observable Inputs
    • La función view() es una capacidad nueva agregada en Framework, y permite que los cambios en la selección de entrada se reflejen en otros bloques de código del documento
  • packageName se define con const y puede usarse en otros bloques js de la página
  • Los datos se obtienen con d3.json()
    • En Framework se puede usar D3 completo
    • La URL incluye el nombre del paquete seleccionado
    • La fuente de datos es la API JSON de Datasette
  • La tabla SQLite está en datasette.io/content/stats y se actualiza una vez al día con estadísticas recientes de paquetes de PyPI
    • El flujo de trabajo relacionado de GitHub Actions se trató en una publicación anterior sobre baked data
  • Al agregar .json a la URL se devuelve JSON
    • Solo se solicitan las filas del paquete específico
    • Se ordena por fecha en orden descendente
    • Se reciben hasta 1,000 filas como arreglo de objetos
  • Las fechas en cadena de SQLite se convierten en objetos Date de JavaScript con d3.timeParse("%Y-%m-%d")
  • El gráfico se renderiza con Observable Plot, que viene empaquetado con Framework
  • La lista de paquetes se obtiene ejecutando una consulta SQL directamente contra la base de datos /content de Datasette
    • La consulta es select package from stats group by package order by max(downloads) desc
    • _shape=arrayfirst es una forma abreviada de recibir en un arreglo JSON la primera columna de las filas del resultado

Incluir solo el código que se usa

  • El dashboard de ejemplo usa bibliotecas adicionales como Inputs, d3 y Plot
  • En modo de desarrollo se aplica carga diferida
    • El código solo se carga cuando una celda intenta usarlo por primera vez
  • Cuando se compila y despliega la aplicación, Framework carga automáticamente desde jsdelivr CDN solo el código de las bibliotecas referenciadas

Caché de datos en tiempo de compilación

  • El Data loader de Framework sirve para preparar datos del dashboard en tiempo de compilación
  • Los dashboards de Framework pueden obtener datos en tiempo de ejecución desde cualquier lugar usando fetch() o sus wrappers
    • Observable Notebooks también funciona de la misma manera
    • Con este enfoque, el rendimiento del dashboard depende del backend al que esté conectado
  • Framework recomienda un patrón en el que los datos del dashboard se generan al momento del despliegue y solo se empaquetan como archivos estáticos los subconjuntos de datos necesarios
    • Los archivos de datos estáticos pueden servirse rápidamente desde el mismo hosting estático que el código del dashboard
  • Data loader es un script escrito en cualquier lenguaje de programación
    • Durante la compilación, Framework ejecuta el script
    • El resultado de la salida estándar del script se guarda como archivo
  • El ejemplo usa un archivo quakes.json.sh con curl https://earthquake.usgs.gov/earthquakes/feed/…
    • Durante la compilación, el nombre del archivo le indica a Framework que el archivo de destino es quakes.json y que el loader a ejecutar es .sh
  • Si se puede emitir JSON, CSV u otro formato útil por la salida estándar, entonces se pueden obtener datos con cualquier tecnología

Diferencias con Observable Notebooks

  • Observable Framework reutiliza muchas ideas y código de Observable Notebooks, pero tiene diferencias importantes en el formato de archivos y el entorno de ejecución
  • Los Observable Notebooks existentes tienen las siguientes características frente a Jupyter Notebooks
    • Usan JavaScript en lugar de Python
    • El editor de notebooks en sí no es de código abierto, sino un producto alojado ofrecido en observablehq.com
    • Los notebooks pueden exportarse como archivos estáticos y ejecutarse en cualquier lugar, pero el editor es un producto propietario
    • Las celdas son reactivas, y si una celda cambia, otras celdas que dependen de ella se reevalúan automáticamente como en Excel
    • Para soportar el modelo de reactividad, crearon una palabra clave personalizada llamada viewof, por lo que la sintaxis de JavaScript no es completamente estándar
    • Los notebooks editables usan un formato de archivo propietario y complejo, poco compatible con herramientas como Git, así que Observable implementó su propio sistema de control de versiones y colaboración
  • Observable Framework traslada este modelo a un formato de archivo más simple y a un entorno de ejecución de código abierto
    • Los documentos son un solo archivo Markdown que incluye bloques de JavaScript
    • Sigue siendo reactivo, pero puede editarse con cualquier editor de texto y guardarse en Git
    • Todo es código abierto bajo licencia ISC, y toda la pila de edición puede ejecutarse en una máquina local
    • Solo usa JavaScript estándar, sin sintaxis personalizada

Cambio de dirección en Observable

  • Observable Framework parece reflejar un cambio en Observable, alejándose de una herramienta colaborativa centrada en el editor propietario de Observable Notebook y acercándose más a las herramientas para desarrolladores
  • La descripción de Observable en Twitter dice: “The end-to-end solution for developers who want to build and host dashboards that don’t suck”
    • En una copia de Internet Archive del 3 de octubre de 2023 decía: “Build data visualizations, dashboards, and data apps that impact your business — faster.”
  • El uso de Observable Notebooks puede verse algo limitado por lo propietario de la plataforma y por las restricciones de las cuentas gratuitas, especialmente la ausencia de notebooks privados gratis
  • Bibliotecas de código abierto como Observable Plot ya se consideran tecnologías que se pueden usar activamente
  • Observable Framework vuelve a implementar, con código abierto, JavaScript estándar, un solo archivo de texto y un modelo de despliegue estático, las ideas que hacían atractivos a Observable Notebooks

1 comentarios

 
GN⁺ 2024-03-04
Opiniones en Hacker News
  • En cierto sentido, Observable Framework es como Avengers: Endgame del universo cinematográfico de Mike Bostock.
    Reúne d3, Observable, Observable Plot y HTL, y además le agrega bastantes ideas nuevas.

    • Personalmente, Polymaps sigue siendo mi creación favorita de él.
    • Da la sensación de estar construyendo algo para agentes desarrolladores de IA “aumentados por humanos”.
      Observable ya tiene integración con IA, y esto parece un wrapper que permite que la IA lo combine y aproveche más fácilmente. La parte donde evalúa la estrategia sin IA se sintió un poco rara.
    • Hace tiempo había marcado Observable y Observable Framework en favoritos, pero no los había revisado en detalle.
      Hoy empecé a ver formas de alojar Jupyter Notebooks estáticos o levantarlos de forma interactiva con WASM, y creo que para la mayoría de los usos Observable Framework encajaría mejor.
  • El problema de Observable es que, aunque parece una galería de ejemplos de d3, el código está diseñado para ejecutarse dentro de ese framework, así que no se puede simplemente copiar y pegar.
    d3 tampoco es fácil de usar sin ejemplos, sobre todo porque muchas veces hay cambios incompatibles entre versiones. Aun así, el sitio tiene muchísimos gráficos increíbles.
    [0] https://observablehq.com/@d3/gallery

    • 100% de acuerdo. Nunca pude superar del todo el hecho de que sea ligeramente distinto del JavaScript real.
      Está lo bastante cerca del lenguaje base como para que quizá podrían haber usado simplemente JavaScript, agregando algunas APIs para mostrar gráficos.
    • En algún momento la comunidad publicó recursos para convertir JavaScript estilo Observable a JavaScript normal.
      En su mayoría consiste en reescribir las definiciones de celdas de nivel superior.
      [0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
    • Eso es tremendamente frustrante. Se siente como un lock-in de plataforma del que ninguna empresa debería estar orgullosa.
      Esa queja me pesaba especialmente con los notebooks de ObservableHQ. Son ejemplos excelentes y, al mismo tiempo, terminan siendo material inútil. Dicho eso, este Framework parece algo más abierto y, como mínimo, permite autohospedaje, así que lo sigo observando.
    • Esto parece más bien un problema de que d3 no tenga ejemplos que se puedan copiar y pegar en otros lugares, no tanto un problema de Observable.
      Además, este artículo trata sobre que el nuevo Observable Framework eliminó algunos de los problemas de los antiguos notebooks de Observable, así que este comentario queda un poco desalineado con el texto. Ahora la idea es que “todo es JavaScript estándar y no hay sintaxis personalizada”.
  • Framework también es muy fácil de desplegar en GitHub Pages.
    Dejé documentados los pasos y un GitHub Action de ejemplo: https://notes.billmill.org/programming/observable_framework/...

  • El autor dio en el clavo sobre Framework.
    Probé Observable Framework creando un pequeño gráfico interactivo (https://github.com/willmeyers/observable-ssta), y fue increíblemente fácil configurarlo y graficar los datos. Mi única queja es que me gustaría poder configurar los cargadores de datos en Python para que usen virtualenv.

    • Estoy usando una configuración con poetry para ejecutar los cargadores de datos en Python dentro de un virtualenv administrado por poetry.
      Después de crear el proyecto de Python, al iniciar el servidor de desarrollo ejecuto poetry run yarn run dev en lugar de yarn run dev, y así Python corre dentro del virtualenv. Esta configuración permite definir como un paquete Python personalizado el código reutilizable y testeable unitariamente para los cargadores de datos, y luego importarlo desde archivos *.json.py para mantenerlos muy simples.
    • ¿No se puede resolver con nodeenv de Python? Normalmente hago eso cuando agrego JS a un proyecto.
      node, herramientas como npm/yarn e incluso JavaScript quedan mantenidos dentro del venv.
    • ¿No se podría poner un shebang en el cargador de datos .sh y apuntar a la ruta completa de bin/python dentro del directorio del entorno virtual?
  • Hace poco terminé mi primer proyecto “real” con un notebook de Observable.
    Incluyó aprender Observable Plot y Arquero, reaprender algo de JavaScript e integrarlo con el proceso de generación de datos, un simulador basado en Rust. Sinceramente, fue excelente. Me tomó bastante energía aprender las herramientas y la capacidad de parametrizar el generador de datos todavía deja que desear, pero el notebook final es hermoso y funciona bien.
    Con Markdown y reactividad, sentí que este tipo de notebook realmente puede ser útil. El formato personalizado de Jupyter dificulta mucho el control de versiones, y sin reactividad, un notebook diseñado de manera iterativa fácilmente termina convertido en un desastre con estado: fácil de escribir, pero difícil de leer. También lo intenté con Quarto y su integración con Observable, pero se sintió como una solución improvisada hecha de piezas sueltas.
    De verdad, fue la primera vez que escribir un notebook y compartirlo con otros me resultó agradable y hasta emocionante. Seguramente seguirá habiendo aristas filosas, pero después de este proyecto se convirtió en mi primera opción entre las herramientas de notebooks.

    • Si estás buscando una alternativa a Quarto, también vale la pena mirar Living Papers, publicado hace poco, que permite crear documentos reactivos/estáticos desde una sola fuente.
      [0]: https://living-papers.vercel.app/
  • Si quieres probar y trastear rápido con Framework en el navegador, preparé un Codespace devcontainer que configura automáticamente los entornos de Node y Python
    [0]: https://github.com/dleeftink/observable-codespace

  • ¿Debería pasarme de Jupyter Notebook a Observable? ¿O plantearlo como una división entre ambos ya es un enfoque equivocado?

    • Al final, creo que la pregunta central es si eres más productivo en Python y su ecosistema, o en JavaScript y sus paquetes, especialmente cosas como D3
  • Dicho de otra forma, todo lo que esté dentro de un bloque de código con la pista de contenido js se ejecuta de inmediato en el navegador del usuario
    Para mostrar el código hay que usar la pista js echo. Pensando en la compatibilidad hacia atrás, ¿no habría sido mejor al revés? Por ejemplo, dejar el código que se ejecuta en el navegador del usuario como una pista opt-in tipo js exec, y mantener intacta la pista js, que se usa ampliamente. Con la estructura actual, al integrar ese renderer en una app existente hay que administrar por separado dónde se permite la ejecución

    • Tenemos previsto permitir cambiar las opciones predeterminadas de los bloques
      Se podrá hacer por página en el front matter, o aplicarlo a todo el proyecto desde la configuración del proyecto. Así podrías hacer que js run=false sea el valor predeterminado y activar el código en vivo con js run solo cuando quieras. De todos modos, como nuestro caso de uso principal es el código en vivo, elegimos que ese sea el valor predeterminado
    • Exacto. Es el mismo problema que el renderizado automático de diagramas Mermaid en GitHub
      Ahora ya no se puede simplemente mostrar un bloque de código Mermaid sin quitar la anotación de lenguaje
  • Pasé una noche metido en Observable Framework y me pareció muy bueno
    Casi no estorbó, y pude visualizar y explorar en detalle mi historial de Google Maps. La parte del entorno de los data loaders no fue del todo clara, pero se resolvió ejecutando Python dentro del entorno de poetry
    Como me gusta Kotlin, también intenté crear un data loader para scripts de Kotlin, pero hubo algunas asperezas. Kotlin espera que el nombre del archivo de script sea foo.main.kts, mientras que Observable espera la extensión foo.exe para un loader ejecutable con shebang. Así que hice un script exe proxy que invoca el script de Kotlin, pero entonces no se dispara la recarga automática de datos
    Algo que resulta un poco incómodo frente a marimo o Jupyter es el uso de variables entre el data loader y el notebook. Por ejemplo, quisiera cambiar el rango de datos que trae el loader usando un componente de vista para seleccionar fechas, pero no queda claro cómo hacerlo. Eso vuelve un poco más lento el análisis exploratorio. Sé que esto va contra el paradigma, pero quería señalarlo. Al final, mientras exploras, puede que termines moviendo una buena parte del procesamiento de datos al notebook, lo cual no es ideal desde el punto de vista del rendimiento
    Por último, me gustaría poder definir data loaders en línea. Me gustan los archivos únicos, así que si al agregar un bloque de código Python Framework pudiera extraerlo a un archivo, sería una pequeña mejora de calidad de vida. Todavía es temprano, pero Framework parece prometedor. Me gustaría poder subir aquí todas mis notas en Markdown y crear un entorno tipo org-mode sin llegar a un Emacs completo

    • Gracias por el feedback. Hay un PR abierto para que sea más fácil registrar nuevos intérpretes sin pasar por .sh o .exe
      Permitirá especificar un intérprete asociado a una extensión de archivo concreta. Por ejemplo, .kts para Kotlin. https://github.com/observablehq/framework/pull/935
      La forma de impulsar data loaders mediante valores de entrada va un poco en otra dirección, dado que Framework prefiere snapshots de datos estáticos. El objetivo es que el sitio compilado sea autocontenido y tenga buen rendimiento. Aun así, una técnica que suele funcionar bien es generar desde el data loader un archivo Parquet con un superconjunto de los datos con los que quieres interactuar, y luego en el cliente extraer con DuckDB/SQL el subconjunto que se va a visualizar. Por lo general rinde bien, aunque por supuesto depende del tamaño del superconjunto que quieras manejar
  • Observable se integra muy bien con ClickHouse a través de una API REST
    Hay un ejemplo aquí: https://observablehq.com/@stas-sl/github-issues-survival-ana...
    Todavía no he probado el nuevo Observable Framework, pero sería interesante ver ejemplos similares que consulten una base de datos en tiempo real. Espero que no sea una arquitectura donde solo sea posible precargar y cachear todos los datos. Como estas apps deben ser interactivas, idealmente deberían exponer la posibilidad de editar SQL en vivo