Cree una aplicación web interactiva con PyScript y Pandas
PyScript nos permite crear una aplicación web sin servidor con HTML y Python como lenguaje de script
¿Es PyScript el futuro de las aplicaciones web? Tal vez, pero probablemente todavía no. Todavía es solo una versión alfa y todavía hay mucho trabajo por hacer, estoy seguro.
Pero en este momento lo útil que? Vamos a crear una aplicación web interactiva donde la lógica está completamente escrita en Python.
Probablemente haya oído hablar de PyScript, fue anunciado en la conferencia PyCon en 2022 por el CEO de Anaconda, Peter Wang. Y, según sus propias palabras, es «una tecnología nueva y brillante… que permite a los usuarios escribir Python y, de hecho, muchos idiomas en el navegador».
La gran ventaja de PyScript es que puedes escribir aplicaciones web en Python sin necesidad de un servidor. Esto se logra mediante el uso del intérprete Pyodide Python que está escrito en WebAssembly, el lenguaje de nivel inferior para la web que es compatible con los navegadores modernos.
Hasta ahora, las aplicaciones web de Python se han basado en servidores y utilizan marcos como Django o Flask, donde el front-end se crea en HTML y Javascript, mientras que el back-end es Python que se ejecuta en un servidor remoto. Más recientemente, Dash y Streamlit han intentado facilitar la creación de tales aplicaciones al proporcionar marcos solo de Python, evitando así la necesidad de aprender HTML y Javascript. Pero estas siguen siendo aplicaciones basadas en servidor. PyScript es un enfoque diferente.
En PyScript, la interfaz de usuario todavía se construye con HTML, pero Javascript se reemplaza con Python (aunque aún puede usar Javascript, si lo desea, y las funciones escritas en dos lenguajes de secuencias de comandos pueden comunicarse entre sí).
La aplicación web
Voy a ejecutar una aplicación simple escrita en PyScript y HTML que lee datos de una fuente remota y muestra un tablero simple donde el usuario puede seleccionar los datos que se mostrarán en un gráfico de Pandas. La forma básica de la aplicación se ve así:
<html>
<head>
<link rel="stylesheet"
href="https://pyscript.net/alpha/pyscript.css" />
<script defer
src="https://pyscript.net/alpha/pyscript.js">
</script>
<py-env>
- libraries-that-will-be-used
</py-env>
</head>
<body> <!-- HTML layout code goes here --> <py-script>
# PyScript code goes here
</py-script>
</body>
</html>
Como puede ver, se parece a un archivo HTML estándar, de hecho, lo es. Pero para la aplicación PyScript, debemos incluir los enlaces a la hoja de estilo PyScript y la biblioteca Javascript en la sección <head>, además de incluir el bloque <py-env> que veremos más adelante. A continuación, en el <body> tenemos el contenido del diseño HTML seguido de las etiquetas <py-script>…</py-script> que contendrán el código de Python. Ahora desarrollaremos esas secciones. Primero, usaré el marco Bootstrap para que toda la aplicación se vea bien. Esto significa que debemos incluir el enlace al archivo CSS de Bootstrap. Y usaremos las bibliotecas Matplotlib y Pandas, y esas deben declararse en la sección <py-env>…</pt-env>. El <head>…</head> ahora se ve así:
<head>
<link rel="stylesheet"
href="https://pyscript.net/alpha/pyscript.css" />
<script defer
src="https://pyscript.net/alpha/pyscript.js">
</script>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<py-env>
- matplotlib
- pandas
</py-env>
</head>
Lo siguiente a mirar es el código HTML para la página web. La primera sección es básicamente un encabezado de la página. Utilizamos el contenedor Jumbotron de Bootstrap. Esto nos da un cuadro agradable con un fondo gris y un texto introductorio en el estilo clásico de Bootstrap.
<div class="jumbotron">
<h1>Weather Data</h1>
<p class="lead">
Some graphs about the weather in London in 2020
</p>
</div>
A continuación hay dos filas: la primera tiene un botón y un menú desplegable y la segunda contendrá el gráfico que se mostrará. Aquí está la primera fila:
<div class="row">
<div class="col-sm-2 p-2 ml-4 mb-1">
<button type="button" class="btn btn-secondary">Select chart from list:</button>
</div> <div class="col-sm-4 p-2 mr-4 mb-1">
<select class="form-control" id="select">
<option value="Tmax">Maximum Temperature</option>
<option value="Tmin">Minimum Temperature</option>
<option value="Sun">Sun</option>
<option value="Rain">Rain</option>
</select>
</div>
</div>
Consta de dos columnas, una para el botón y la segunda para el menú (uso un botón aquí solo por motivos estéticos, en realidad no funciona como un botón). Las opciones en el menú desplegable se utilizarán para mostrar uno de los cuatro gráficos diferentes. Los datos que se utilizarán son una tabla de condiciones climáticas en Londres[1] para el año 2020. Hay 12 filas en la tabla que representan cada mes y las columnas en la tabla representan la temperatura máxima para ese mes, la temperatura mínima, el número de horas de sol y la cantidad de lluvia en milímetros. Entonces, los elementos del menú representan estas opciones y tomarán un valor de ‘Tmax’, ‘Tmin’, ‘Sun’ o ‘Rain’. Hasta ahora hemos codificado la página web y ahora necesitamos definir la lógica que reaccionará a la entrada del usuario y dibujará el gráfico. Esto se define en la sección <py-script>. El código que trataremos a continuación va dentro de esta sección.
Primero importe algunas bibliotecas.
# Import libraries
import pandas as pd
import matplotlib.pyplot as plt
Pandas y Matplotlib, por supuesto, pero también necesitamos lo siguiente:
from pyodide.http import open_url
This is a library provided by PyScript that allows us to read from a source on the web and we use it like this:
url = 'https://raw.githubusercontent.com/alanjones2/uk-historical-weather/main/data/Heathrow.csv'
url_content = open_url(url)df = pd.read_csv(url_content)
La implementación de PyScript, la función read_csv de Pandas, no puede abrir la URL directamente, por lo que debemos usar la técnica anterior. El archivo que se está descargando contiene varias décadas de datos, pero para simplificar las cosas, lo filtraremos para que solo contenga los datos de 2020.
# filter the data for the year 2020
df = df[df['Year']==2020]
Ahora la función para trazar el gráfico en el HTML <div> que vimos anteriormente.
# Function to plot the chart
def plot(chart):
fig, ax = plt.subplots()
df.plot(y=chart, x='Month', figsize=(8,4),ax=ax)
pyscript.write("chart1",fig)
Esto es algo bastante estándar para trazar con Pandas. La principal diferencia con un programa de Python «normal» es la forma en que se representa.
La función pyscript.write toma la identificación de un elemento HTML y escribe el contenido del segundo parámetro en él. Esto se basa en que la implementación de PyScript incluye una forma de representar el objeto en particular, en este caso, un gráfico matplotlib, y esto no necesariamente funcionará con ningún tipo de objeto.
Por ejemplo, si quisiéramos mostrar un gráfico de Plotly, tendríamos que usar una técnica diferente, ya que la representación de una figura de Plotly no está implementada directamente en PyScript (todavía).
Lo siguiente que debe hacer es definir una forma de invocar la función de trazado cuando el usuario selecciona un nuevo gráfico del menú desplegable. Primero, algunas bibliotecas más: se proporcionan como parte de PyScript.
from js import document
from pyodide import create_proxy
La biblioteca js permite que PyScript acceda a las funciones de Javascript, en este caso particular estamos importando la capacidad de acceder al DOM. Y create_proxy de Pyodide hace lo contrario, lo que permite que Javascript llame directamente a las funciones de Pyodide.
def selectChange(event):
choice = document.getElementById("select").value
plot(choice)
Esta función se llama cuando ocurre un evento de cambio. Lee el valor de la selección y luego llama a la función de trazado previamente definida con ese valor. A continuación, definimos un proxy que permitirá que el evento de cambio llame a la función de PyScript selectChange.
# set the proxy
def setup():
# Create a JsProxy for the callback function
change_proxy = create_proxy(selectChange) e = document.getElementById("select")
e.addEventListener("change", change_proxy)
Finally, when the page first loads we need to call the setup
function and run the plot
function with a default value (‘Tmax’) in order that a chart is displayed on start up.
And that is the entire code that implements a simple dashboard app like the one in the screenshot at the beginning of the article. The code for this and a link to a demo app will be in a link at the end.
¿Cómo se compara PyScript con las aplicaciones basadas en servidor existentes?
En términos de dificultad, siempre que esté familiarizado con Python, construir una aplicación web es relativamente sencillo, probablemente más fácil que construir una aplicación Django o Flask, más al nivel de Dash, sugeriría. Sin embargo, Streamlit es una historia diferente. Streamlit es más limitado en el lado del diseño visual pero tiene la ventaja de que no tienes que aprender HTML.
Las aplicaciones Streamlit son muy fáciles de crear, más fáciles que PyScript, diría yo. Sin embargo, la ventaja que tiene PyScript sobre todos sus rivales es que no depende de un servidor. Eso hace que la implementación sea muy fácil: simplemente cargué la aplicación que ves aquí en un sitio de páginas de Github y simplemente funciona. Actualmente, PyScript es un poco lento.
Dado que todo el trabajo se realiza en el navegador, el rendimiento lo determina la máquina que ejecuta el navegador; si tiene un hardware débil, puede llevar mucho tiempo cargar su aplicación, aunque una vez que está en funcionamiento, el rendimiento parece Estar bien. Sin embargo, el rendimiento solo mejorará a medida que el hardware se vuelve más poderoso y el PyScript madura y (con suerte) se vuelve más eficiente. Y un día, quién sabe, tal vez veamos PyScript integrado en los navegadores de la misma manera que Javascript en la actualidad.
Actualización:
puede encontrar una versión de esta aplicación que usa Plotly en lugar de Pandas, aquí. Como siempre, gracias por leer. Puede encontrar un enlace al código completo y la página web de demostración en mi sitio web, donde también encontrará enlaces a muchos otros artículos.
Si desea mantenerse al día con lo que escribo, considere suscribirse a mi boletín informativo ocasional o haga clic en el enlace a continuación para recibir un correo electrónico cuando publique aquí.
Notas
Los datos meteorológicos provienen de mi repositorio uk-historical-weather y se derivan de los datos de la estación histórica de la Oficina Meteorológica del Reino Unido.
Se distribuye de acuerdo con la Licencia de Gobierno Abierto del Reino Unido y se puede utilizar en las mismas condiciones.
Nacido en el mar
En respuesta a la pregunta «¿funcionará con Seaborn?»:
<html><head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"><py-env>
- matplotlib
- pandas
- seaborn
</py-env></head>
<body><div class="jumbotron"> <h1>Weather Data</h1>
<p class="lead">
Some graphs about the weather in London in 2020
</p></div><div class="row">
<div class="col-sm-2 p-2 ml-4 mb-1">
<button type="button" class="btn btn-secondary">Select chart from list:</button>
</div>
<div class="col-sm-4 p-2 mr-4 mb-1">
<select class="form-control" id="select">
<option value="Tmax">Maximum Temperature</option>
<option value="Tmin">Minimum Temperature</option>
<option value="Sun">Sun</option>
<option value="Rain">Rain</option>
</select>
</div>
</div><div class="row">
<div class="col-sm-6 p-2 shadow ml-4 mr-4 mb-4 bg-white rounded">
<div id="chart1"></div>
</div>
</div><py-script>
# Import libraries
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns# Get the datafrom pyodide.http import open_urlurl = 'https://raw.githubusercontent.com/alanjones2/uk-historical-weather/main/data/Heathrow.csv'url_content = open_url(url)df = pd.read_csv(url_content)# filter the data for the year 2020df = df[df['Year']==2020]# Function to plot the chartdef plot(chart):
fig, ax = plt.subplots()
sns.lineplot(y=chart, x="Month", data=df, ax=ax)
pyscript.write("chart1",fig)# Set up a proxy to be called when a 'change'# event occurs in the select controlfrom js import document
from pyodide import create_proxy# Read the value of the select control# and call 'plot'def selectChange(event):
choice = document.getElementById("select").value
plot(choice)# set the proxydef setup():
# Create a JsProxy for the callback function
change_proxy = create_proxy(selectChange)
e = document.getElementById("select")
e.addEventListener("change", change_proxy)setup()# Intitially call plot with 'Tmax'plot('Tmax')</py-script></body></html>