El taller de componentes: los 100 componentes de Reflex
·
Resumen del curso
Diseño
·
Box
·
Flex
·
Stack
·
Grid
·
Container
·
Section
·
Center
·
Spacer
·
Separator
·
Card
·
Inset
·
Aspect Ratio
·
Fragment
Tipografía
·
Text
·
Heading
·
Link
·
Code
·
Markdown
·
Blockquote
·
Quote
·
Strong
·
Em
·
Kbd
Formularios
·
Button
·
Input
·
Text Area
·
Checkbox
·
Switch
·
Radio Group
·
Select
·
Slider
·
Form
·
Upload
Visualización de datos
·
Badge
·
Avatar
·
Icon
·
Callout
·
List
·
Data List
·
Code Block
·
Progress
·
Spinner
·
Scroll Area
·
Moment
Mostrar y ocultar
·
Tabs
·
Accordion
·
Segmented Control
Superposiciones
·
Tooltip
·
Popover
·
Hover Card
·
Dialog
·
Alert Dialog
·
Drawer
·
Dropdown Menu
·
Context Menu
·
Toast
Renderizado dinámico
·
Cond
·
Match
·
Foreach
·
Auto Scroll
Multimedia
·
Image
·
Audio
·
Video
Tablas y cuadrículas de datos
·
Table
·
Data Table
·
Data Editor
Gráficos
·
Area Chart
·
Bar Chart
·
Line Chart
·
Composed Chart
·
Pie Chart
·
Radar Chart
·
Radial Bar Chart
·
Scatter Chart
·
Funnel Chart
·
Treemap
·
Sankey Chart
·
Error Bar
Piezas de gráficos
·
Axis
·
Cartesian Grid
·
Chart Tooltip
·
Legend
·
Label
·
Reference
·
Brush
Otras bibliotecas de gráficos
·
Plotly
·
Pyplot
Elementos HTML
·
HTML
·
HTML Text
·
HTML Layout
·
HTML Forms
·
HTML Media
·
HTML Tables
·
SVG
Otras herramientas
·
Theme
·
Skeleton
·
Clipboard
·
Html Embed
·
Script
·
Memo
rx.plotlyDocumentación oficial
Pedido del taller
Convierte las ventas anuales de cada producto en una gráfica interactiva que se pueda ampliar y reducir, y haz que cambie al elegir un producto.
Plotly es una librería de Python para crear gráficas interactivas que se pueden ampliar, desplazar y explorar pasando el ratón. rx.plotly recibe tal cual un Figure de Plotly y lo dibuja en el navegador. Desde gráficas básicas como líneas y barras hasta mapas de calor, 3D y gráficas financieras, todo sin JavaScript.
En lugar de llamar a fig.show() como en un script, en el código de la interfaz lo pasas con rx.plotly(data=fig). El paquete necesario es plotly, y en esta lección creamos las figuras con plotly.graph_objects (go).
import plotly.graph_objects as go
plotly_years = [1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007]
plotly_canada = [68.75, 69.96, 71.3, 72.13, 72.88, 74.21, 75.76, 76.86, 77.95, 78.61, 79.77, 80.65]
plotly_line_fig = go.Figure(go.Scatter(x=plotly_years, y=plotly_canada, mode="lines", name="Canada"))
plotly_line_fig.update_layout(title="Life expectancy in Canada")
rx.plotly(data=plotly_line_fig, width="100%")La mayoría de los ejemplos de la documentación oficial usan plotly.express (px). px necesita pandas, así que en esta página creamos las mismas figuras con go y escribimos los datos como listas de Python. Una figura creada con px también es un Figure, así que se pasa a rx.plotly del mismo modo.
Si das un dict a layout, se sobrescribe encima del layout de la figura. Es cómodo para cambiar solo desde la pantalla el título, la altura o el fondo de una misma figura. El banco de pruebas de abajo y algunos ejemplos reutilizan tal cual el plotly_line_fig de arriba.
Prueba las propiedades
title
height
400
showlegend
plot_bgcolor
locale
rx.plotly(
data=plotly_line_fig,
layout={"title": {"text": "Life expectancy"}, "height": 400, "showlegend": False, "plot_bgcolor": "rgba(0,0,0,0)"},
locale="en",
width="100%",
)| Propiedad | Valores | Por defecto | Uso |
|---|---|---|---|
data | go.Figure o su dict | - | Figura que se dibuja. También admite un valor del State |
layout | dict | - | Valores que se sobrescriben en el layout de la figura |
template | Template de Plotly | modo claro plotly, modo oscuro plotly_dark | Tema general de colores y fuentes |
config | dict | - | Ajustes de Plotly, como la barra de herramientas o el zoom con la rueda |
locale | "de" "ko" … | - | Idioma del formato de números y fechas y de los textos de la barra de herramientas |
use_resize_handler | True / False | True | Reajustar cuando cambia el tamaño de la ventana |
on_click · on_hover · on_unhover · on_selected · on_selecting | eventos | - | Pasan la lista de puntos (list[dict]) |
on_relayout · on_double_click · on_deselect · on_after_plot, etc. | eventos | - | Zoom y desplazamiento, doble clic, deselección, tras redibujar (sin argumentos) |
Si no indicas template, Reflex cambia el tema según el modo claro u oscuro. El rx.plotly de Reflex 0.10 tiene además on_click y on_double_click, que no aparecen en la tabla de la documentación oficial.
Son los ejemplos de Plotly Express de la documentación oficial pasados a go. Sea cual sea la figura, se pasa igual: rx.plotly(data=figura). Los datos son valores inventados para el ejemplo.
Ponemos varios go.Bar y con barmode="group" quedan uno al lado de otro. Equivale a px.bar(..., color="country").
import plotly.graph_objects as go
plotly_bar_fig = go.Figure([
go.Bar(name="Chairs", x=["Q1", "Q2", "Q3", "Q4"], y=[120, 150, 170, 210]),
go.Bar(name="Tables", x=["Q1", "Q2", "Q3", "Q4"], y=[80, 95, 130, 120]),
])
plotly_bar_fig.update_layout(barmode="group", title="Orders by quarter")
rx.plotly(data=plotly_bar_fig, width="100%")Si das mode="markers" a go.Scatter, solo se dibujan puntos. Si creas un trace por grupo, cada uno recibe su color y su entrada en la leyenda.
import random
import plotly.graph_objects as go
plotly_scatter_rng = random.Random(7)
plotly_scatter_fig = go.Figure([
go.Scatter(
x=[plotly_scatter_rng.gauss(cx, 0.3) for _ in range(30)],
y=[plotly_scatter_rng.gauss(cy, 0.4) for _ in range(30)],
mode="markers",
name=name,
)
for name, cx, cy in [("A", 3.4, 5.0), ("B", 2.8, 5.9), ("C", 3.0, 6.6)]
])
plotly_scatter_fig.update_layout(title="Sample clusters", xaxis_title="width", yaxis_title="length")
rx.plotly(data=plotly_scatter_fig, width="100%")A go.Pie se le dan nombres (labels) y valores (values). Los porcentajes los calcula Plotly.
import plotly.graph_objects as go
plotly_pie_fig = go.Figure(go.Pie(labels=["Thur", "Fri", "Sat", "Sun"], values=[171.83, 51.96, 260.40, 247.39]))
plotly_pie_fig.update_layout(title="Tips by day")
rx.plotly(data=plotly_pie_fig, width="100%")go.Histogram2d colorea con más intensidad las celdas donde se acumulan más pares de valores (px.density_heatmap). Si ya tienes la tabla calculada, usa go.Heatmap(z=...).
import random
import plotly.graph_objects as go
plotly_heat_rng = random.Random(3)
plotly_bills = [plotly_heat_rng.gauss(20, 8) for _ in range(300)]
plotly_tips = [b * 0.15 + plotly_heat_rng.gauss(0, 0.8) for b in plotly_bills]
plotly_heatmap_fig = go.Figure(go.Histogram2d(x=plotly_bills, y=plotly_tips, nbinsx=20, nbinsy=15))
plotly_heatmap_fig.update_layout(title="Bill vs tip density heatmap")
rx.plotly(data=plotly_heatmap_fig, width="100%")go.Histogram divide los valores en intervalos y cuenta cuántos hay en cada uno. nbinsx es el número máximo de intervalos.
import random
import plotly.graph_objects as go
plotly_hist_rng = random.Random(5)
plotly_hist_fig = go.Figure(go.Histogram(x=[plotly_hist_rng.gauss(20, 8) for _ in range(250)], nbinsx=20))
plotly_hist_fig.update_layout(title="Distribution of total bills")
rx.plotly(data=plotly_hist_fig, width="100%")go.Box muestra en una caja la mediana, los cuartiles y los valores atípicos. Se pone un trace por grupo.
import random
import plotly.graph_objects as go
plotly_box_rng = random.Random(11)
plotly_box_fig = go.Figure([
go.Box(y=[plotly_box_rng.gauss(mean, 6) for _ in range(40)], name=day)
for day, mean in [("Thur", 17), ("Fri", 17), ("Sat", 20), ("Sun", 21)]
])
plotly_box_fig.update_layout(title="Total bill by day")
rx.plotly(data=plotly_box_fig, width="100%")En un diagrama de dispersión, el tamaño de los puntos (marker.size) muestra un tercer valor. Si cambias el eje x a type="log", equivale a px.scatter(..., log_x=True).
import plotly.graph_objects as go
plotly_bubble_fig = go.Figure(go.Scatter(
x=[800, 2500, 6000, 15000, 32000, 45000],
y=[52, 61, 68, 74, 79, 82],
text=["A", "B", "C", "D", "E", "F"],
mode="markers",
marker={"size": [20, 45, 30, 60, 25, 40], "color": [1, 2, 3, 4, 5, 6], "colorscale": "Viridis"},
))
plotly_bubble_fig.update_xaxes(type="log", title="income")
plotly_bubble_fig.update_layout(title="Income vs life expectancy", yaxis_title="years")
rx.plotly(data=plotly_bubble_fig, width="100%")Un calendario se dibuja con barras horizontales (orientation="h"). El inicio de la barra es base, la longitud es la duración en milisegundos, y el eje x es un eje de fechas. Es lo mismo que hace px.timeline por dentro. Invertimos el eje y para leerlo de arriba abajo.
from datetime import date
import plotly.graph_objects as go
plotly_jobs = [("Job A", "2009-01-01", "2009-02-28"), ("Job B", "2009-03-05", "2009-04-15"), ("Job C", "2009-02-20", "2009-05-30")]
plotly_gantt_fig = go.Figure(go.Bar(
y=[job for job, start, end in plotly_jobs],
base=[start for job, start, end in plotly_jobs],
x=[(date.fromisoformat(end) - date.fromisoformat(start)).days * 86_400_000 for job, start, end in plotly_jobs],
orientation="h",
))
plotly_gantt_fig.update_xaxes(type="date")
plotly_gantt_fig.update_yaxes(autorange="reversed")
rx.plotly(data=plotly_gantt_fig, width="100%")Despliega datos jerárquicos desde la raíz del centro hacia anillos exteriores. La relación padre-hijo se escribe con labels y parents.
import plotly.graph_objects as go
plotly_sunburst_fig = go.Figure(go.Sunburst(
labels=["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
parents=["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve"],
values=[10, 14, 12, 10, 2, 6, 6, 4, 4],
))
rx.plotly(data=plotly_sunburst_fig, width="100%")Muestra cuánto se reduce cada etapa, desde la visita hasta la compra. Sirve para encontrar en qué etapa se pierde más gente.
import plotly.graph_objects as go
plotly_funnel_fig = go.Figure(go.Funnel(
x=[39, 27.4, 20.6, 11, 2],
y=["Website visit", "Downloads", "Potential customers", "Requested price", "Invoice sent"],
))
rx.plotly(data=plotly_funnel_fig, width="100%")locale pone en ese idioma el formato de números y fechas y los textos de ayuda de la barra de herramientas. Abajo, en alemán. Puedes dar también config; si están los dos, el valor de locale se aplica al final.
rx.plotly(data=plotly_line_fig, locale="de", width="100%")Si das a go.Surface una lista bidimensional de alturas (z), obtienes una superficie. La documentación oficial lee datos de altura de una montaña desde un archivo CSV; aquí usamos valores generados con una fórmula. Puedes arrastrar para girarla.
import math
import plotly.graph_objects as go
plotly_surface_z = [[60 + 40 * math.sin(x / 4) * math.cos(y / 5) + y for x in range(30)] for y in range(30)]
plotly_surface_fig = go.Figure(data=[go.Surface(z=plotly_surface_z)])
plotly_surface_fig.update_traces(contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True))
plotly_surface_fig.update_layout(scene_camera_eye=dict(x=1.87, y=0.88, z=-0.64), margin=dict(l=65, r=50, b=65, t=90))
rx.plotly(data=plotly_surface_fig, width="100%")Para cada fecha, dibuja en una vela la apertura, el máximo, el mínimo y el cierre. El cuerpo va entre la apertura y el cierre; la mecha, entre el mínimo y el máximo.
import plotly.graph_objects as go
plotly_candle_fig = go.Figure(go.Candlestick(
x=["2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05", "2024-01-08", "2024-01-09"],
open=[187.15, 184.22, 182.15, 181.99, 182.09, 183.92],
high=[188.44, 185.88, 183.09, 182.76, 185.60, 185.15],
low=[183.89, 183.43, 180.88, 180.17, 181.50, 182.73],
close=[185.64, 184.25, 181.91, 181.18, 185.56, 185.14],
))
plotly_candle_fig.update_layout(title=dict(text="Stock price"), yaxis=dict(title=dict(text="USD")))
rx.plotly(data=plotly_candle_fig, width="100%")Muestra cómo cambia un valor inicial al pasar por partidas que suman y restan. Con measure indicas si cada partida es una variación ("relative") o un total ("total").
import plotly.graph_objects as go
plotly_waterfall_fig = go.Figure(go.Waterfall(
name="2018",
orientation="v",
measure=["relative", "relative", "total", "relative", "relative", "total"],
x=["Sales", "Consulting", "Net revenue", "Purchases", "Other expenses", "Profit before tax"],
textposition="outside",
text=["+60", "+80", "", "-40", "-20", "Total"],
y=[60, 80, 0, -40, -20, 0],
connector={"line": {"color": "rgb(63, 63, 63)"}},
))
plotly_waterfall_fig.update_layout(title="Profit and loss statement 2018", showlegend=True)
rx.plotly(data=plotly_waterfall_fig, width="100%")Es una barra pequeña que se usa en lugar de un indicador de aguja. Se da gauge.shape="bullet" a go.Indicator y en una sola línea caben la barra del resultado, los tramos (steps) y la línea de objetivo (threshold).
import plotly.graph_objects as go
plotly_bullet_fig = go.Figure(go.Indicator(
mode="number+gauge+delta",
value=180,
delta={"reference": 200},
domain={"x": [0.25, 1], "y": [0.4, 0.6]},
title={"text": "Revenue"},
gauge={
"shape": "bullet",
"axis": {"range": [None, 300]},
"threshold": {"line": {"color": "black", "width": 2}, "thickness": 0.75, "value": 170},
"steps": [{"range": [0, 150], "color": "gray"}, {"range": [150, 250], "color": "lightgray"}],
"bar": {"color": "black"},
},
))
plotly_bullet_fig.update_layout(height=250)
rx.plotly(data=plotly_bullet_fig, width="100%")El error se muestra como una banda suave alrededor de la línea en lugar de con barras. Se une el borde superior hacia delante y el inferior hacia atrás para formar una línea cerrada, y con fill="toself" se rellena el interior.
import plotly.graph_objects as go
plotly_band_x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plotly_band_y = [1, 2, 7, 4, 5, 6, 7, 8, 9, 10]
plotly_band_upper = [2, 3, 8, 5, 6, 7, 8, 9, 10, 11]
plotly_band_lower = [0, 1, 5, 3, 4, 5, 6, 7, 8, 9]
plotly_band_fig = go.Figure([
go.Scatter(x=plotly_band_x, y=plotly_band_y, line=dict(color="rgb(0,100,80)"), mode="lines"),
go.Scatter(
x=plotly_band_x + plotly_band_x[::-1],
y=plotly_band_upper + plotly_band_lower[::-1],
fill="toself",
fillcolor="rgba(0,100,80,0.2)",
line=dict(color="rgba(255,255,255,0)"),
hoverinfo="skip",
showlegend=False,
),
])
rx.plotly(data=plotly_band_fig, width="100%")Los mapas de Plotly descargan datos cartográficos de un servidor externo al dibujarse. Los mapas de contorno (Scattergeo, Choropleth) usan datos geográficos del CDN de Plotly, y los mapas de teselas (Scattermap), un servidor de teselas. Esta lección no usa recursos externos, así que solo se muestra el código. En tu app funciona tal cual.
Los ajustes de un mapa de contorno están en el layout.geo de la figura, y con update_geos cambias elementos como lagos o ríos.
import plotly.graph_objects as go
geo_fig = go.Figure(go.Scattergeo())
geo_fig.update_geos(
visible=False,
resolution=50,
showlakes=True,
lakecolor="Blue",
showrivers=True,
rivercolor="Blue",
)
geo_fig.update_layout(height=300, margin={"r": 0, "t": 0, "l": 0, "b": 0})
def geo_map_chart():
return rx.plotly(data=geo_fig, width="100%")Dibuja puntos sobre un mapa de teselas. La documentación oficial usa px.scatter_map; con go, es go.Scattermap. El aspecto del mapa se fija en layout.map.
import plotly.graph_objects as go
map_fig = go.Figure(go.Scattermap(
lat=[37.57, 35.18, 35.87],
lon=[126.98, 129.08, 128.60],
text=["Seoul", "Busan", "Daegu"],
mode="markers",
marker={"size": [18, 12, 10]},
))
map_fig.update_layout(
map={"style": "open-street-map", "center": {"lat": 36.4, "lon": 127.8}, "zoom": 5},
margin={"r": 0, "t": 0, "l": 0, "b": 0},
)
def scatter_map_chart():
return rx.plotly(data=map_fig, width="100%")go.Table recibe los nombres de columna en header y, en cells, una lista de valores por columna. Permite desplazarse y reordenar columnas.
import plotly.graph_objects as go
plotly_table_fig = go.Figure(go.Table(
header=dict(values=["A Scores", "B Scores"]),
cells=dict(values=[[100, 90, 80, 90], [95, 85, 75, 95]]),
))
rx.plotly(data=plotly_table_fig, width="100%")Muestra la magnitud de un flujo con el grosor de las bandas. Los nombres de los nodos van en node.label, y en link se une source→target con los índices de los nodos.
import plotly.graph_objects as go
plotly_sankey_fig = go.Figure(go.Sankey(
node=dict(pad=15, thickness=20, line=dict(color="black", width=0.5), label=["A1", "A2", "B1", "B2", "C1", "C2"], color="blue"),
link=dict(source=[0, 1, 0, 2, 3, 3], target=[2, 3, 3, 4, 4, 5], value=[8, 4, 2, 8, 4, 2]),
))
plotly_sankey_fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
rx.plotly(data=plotly_sankey_fig, width="100%")go.Scatter3d coloca en el espacio la relación entre tres valores (px.scatter_3d).
import random
import plotly.graph_objects as go
plotly_s3d_rng = random.Random(2)
plotly_s3d_fig = go.Figure([
go.Scatter3d(
x=[plotly_s3d_rng.gauss(cx, 0.4) for _ in range(30)],
y=[plotly_s3d_rng.gauss(cy, 0.3) for _ in range(30)],
z=[plotly_s3d_rng.gauss(cz, 0.3) for _ in range(30)],
mode="markers",
marker={"size": 4},
name=name,
)
for name, cx, cy, cz in [("A", 5.0, 3.4, 0.2), ("B", 5.9, 2.8, 1.3), ("C", 6.6, 3.0, 2.0)]
])
rx.plotly(data=plotly_s3d_fig, width="100%")En una figura 3D, los ejes están dentro de scene. Para cada eje, como scene.xaxis, se fijan nticks (número de marcas) y range (rango). La documentación oficial usa números aleatorios de numpy; aquí hacemos lo mismo con random.
import random
import plotly.graph_objects as go
plotly_mesh_rng = random.Random(1)
plotly_mesh_fig = go.Figure(go.Mesh3d(
x=[70 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
y=[55 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
z=[40 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
opacity=0.5,
color="rgba(244,22,100,0.6)",
))
plotly_mesh_fig.update_layout(
scene=dict(
xaxis=dict(nticks=4, range=[-100, 100]),
yaxis=dict(nticks=4, range=[-50, 100]),
zaxis=dict(nticks=4, range=[-100, 100]),
),
margin=dict(r=20, l=10, b=10, t=10),
)
rx.plotly(data=plotly_mesh_fig, width="100%")Si la figura está en un valor del State, puedes cambiarla durante la ejecución. La documentación oficial guarda un go.Figure en una variable del State; aquí el State solo guarda números, y un valor calculado con @rx.var crea la figura. Al mover el deslizador, el valor calculado se vuelve a crear y la gráfica cambia.
import math
import plotly.graph_objects as go
class PlotlyWaveState(rx.State):
cycles: int = 2
@rx.event
def set_cycles(self, value: list[float]):
self.cycles = int(value[0])
@rx.var
def figure(self) -> go.Figure:
xs = [i / 50 for i in range(101)]
fig = go.Figure(go.Scatter(x=xs, y=[math.sin(2 * math.pi * self.cycles * x) for x in xs], mode="lines"))
fig.update_layout(title=f"{self.cycles} cycles", height=300)
return fig
rx.vstack(
rx.slider(value=[PlotlyWaveState.cycles], min=1, max=8, step=1, on_change=PlotlyWaveState.set_cycles, width="240px"),
rx.plotly(data=PlotlyWaveState.figure, width="100%"),
width="100%",
)on_click, on_hover y on_selected pasan la lista de puntos en los que ocurrió el evento. Cada punto trae valores como x, y, curveNumber (qué trace) y pointNumber (qué punto).
import plotly.graph_objects as go
plotly_click_fig = go.Figure(go.Bar(x=["Mon", "Tue", "Wed", "Thu", "Fri"], y=[12, 19, 7, 15, 22]))
plotly_click_fig.update_layout(height=280)
class PlotlyClickState(rx.State):
picked: str = "Click a bar"
@rx.event
def pick(self, points: list[dict]):
if points:
point = points[0]
self.picked = f"{point['x']}: {point['y']}"
rx.vstack(
rx.plotly(data=plotly_click_fig, on_click=PlotlyClickState.pick, width="100%"),
rx.badge(PlotlyClickState.picked, size="2"),
width="100%",
)El aspecto se fija con update_layout() al crear la figura. Todas las propiedades de layout, como la posición del título (title_x), los colores de fondo de la figura y del exterior o la fuente, están en la documentación de Layout de Plotly.
En lugar de dar a rx.plotly un ancho y un alto fijos, conviene dejar que siga el tamaño del contenedor exterior. Así crece y encoge según el tamaño de la pantalla.
import plotly.graph_objects as go
plotly_styled_fig = go.Figure(go.Scatter(
x=[1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007],
y=[68.75, 69.96, 71.3, 72.13, 72.88, 74.21, 75.76, 76.86, 77.95, 78.61, 79.77, 80.65],
mode="lines",
name="Canada",
))
plotly_styled_fig.update_layout(
title="Life expectancy in Canada",
title_x=0.5,
plot_bgcolor="#c3d7f7",
paper_bgcolor="rgba(128, 128, 128, 0.1)",
showlegend=True,
title_font_family="Open Sans",
title_font_size=25,
)
rx.plotly(data=plotly_styled_fig, width="100%")Reflex 0.10 también tiene paquetes ligeros que solo incluyen los traces necesarios. rx.plotly.basic, rx.plotly.cartesian, rx.plotly.finance, rx.plotly.geo, rx.plotly.gl2d, rx.plotly.gl3d, rx.plotly.mapbox y rx.plotly.strict aceptan las mismas propiedades que rx.plotly y reducen lo que descarga el navegador (todavía no figuran en la documentación oficial).
En el State solo guardamos el nombre del producto elegido, y un valor calculado dibuja a la vez las barras mensuales y la línea acumulada. La línea acumulada va en el eje derecho (yaxis2), y con hovermode="x unified" los valores de un mes se reúnen en un solo recuadro.
import plotly.graph_objects as go
plotly_months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
plotly_sales = {
"Chairs": [32, 41, 38, 52, 61, 58, 70, 66, 59, 73, 88, 95],
"Tables": [12, 15, 21, 19, 24, 30, 28, 33, 35, 31, 40, 46],
"Lamps": [55, 50, 47, 44, 49, 53, 60, 58, 62, 67, 80, 102],
}
class PlotlyOrderState(rx.State):
product: str = "Chairs"
@rx.event
def set_product(self, value: str):
self.product = value
@rx.var
def figure(self) -> go.Figure:
monthly = plotly_sales[self.product]
running = [sum(monthly[: i + 1]) for i in range(len(monthly))]
fig = go.Figure([
go.Bar(x=plotly_months, y=monthly, name="Monthly"),
go.Scatter(x=plotly_months, y=running, name="Running total", yaxis="y2", mode="lines+markers"),
])
fig.update_layout(
title=f"{self.product} sales",
hovermode="x unified",
yaxis2={"overlaying": "y", "side": "right"},
legend={"orientation": "h", "y": -0.2},
height=360,
)
return fig
rx.vstack(
rx.select(["Chairs", "Tables", "Lamps"], value=PlotlyOrderState.product, on_change=PlotlyOrderState.set_product),
rx.plotly(data=PlotlyOrderState.figure, width="100%"),
width="100%",
)Notas del taller
En lugar de fig.show(), rx.plotly(data=fig). Da igual que la figura venga de go o de px.
layout=dict sobrescribe el layout, y locale cambia el formato de números y el idioma de la barra de herramientas.
Guarda valores simples en el State; si un valor calculado con @rx.var devuelve un go.Figure, la gráfica lo sigue.
on_click, on_hover y on_selected pasan la lista de puntos (x, y, curveNumber, pointNumber …).
Los mapas descargan datos cartográficos externos. El tema cambia solo según el modo claro u oscuro.
Recibe avisos de nuevas lecciones
Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.
R
ReflexKR
Aprende Python y Reflex haciendo. Todos los ejemplos se ejecutaron y verificaron en Reflex 0.10.0.
ReflexKR es gratis y sin banners publicitarios. Si te ayudó, puedes apoyarlo con un café. ☕ Invítanos un café
Aviso de afiliados© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Preguntas y respuestas
Pregunta lo que quieras sobre esta lección. Aquí se reúnen las preguntas de todas las versiones de idioma de esta página.