Add drawing and visualization applications with Marimo framework

- Implement dibujar.py for drawing functionality with base64 and PIL image rendering.
- Create dibujar_retropaint.py for retro painting features using the Paint widget.
- Develop draw_data.py to visualize data with Scatter and Bar widgets, including lazy installation of dependencies.
- Add layout configuration for graphical representations in layouts/Graficos_plotly.grid.json.
- Enhance shell interaction with mejora_shell_mowidget.py, allowing local library imports and script execution.
- Introduce primera_prueba_shell_mowidget.py for testing shell commands and user input handling.
- Create prueba_de_embeddings.py for embedding visualizations using Sentence Transformers and dimensionality reduction techniques.
- Implement pygwalker_visualizaciones.py for interactive data exploration and visualization using Pygwalker.
- Add a sample bash script for user input and ping functionality in scripts/mi_script.sh.
This commit is contained in:
2025-09-02 23:53:01 +02:00
parent 0143c522de
commit 46573ccc8e
35 changed files with 21957 additions and 4 deletions
+132
View File
@@ -0,0 +1,132 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
import os, sys, importlib, subprocess, shutil
def _have(mod: str) -> bool:
try:
importlib.import_module(mod)
return True
except Exception:
return False
def _install_with_uv(spec: str) -> bool:
uv = shutil.which("uv")
if uv:
subprocess.check_call([uv, "pip", "install", spec])
return True
return False
def _install_with_pip(spec: str) -> bool:
# intenta habilitar pip si no existe
try:
import ensurepip # noqa: F401
ensurepip.bootstrap()
except Exception:
pass
pip_exe = shutil.which("pip") or shutil.which("pip3")
if pip_exe:
subprocess.check_call([pip_exe, "install", spec])
return True
# último recurso: python -m pip
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", spec])
return True
except Exception:
return False
def ensure_pkg(spec: str, module: str | None = None):
module = module or spec
if _have(module):
return
if _install_with_uv(spec):
return
if _install_with_pip(spec):
return
raise RuntimeError(
f"No pude instalar {spec}. Instálalo manualmente con `uv pip install {spec}`."
)
# --- instala dependencias ---
# bavisitter desde PyPI; si falla, cae al repo de GitHub
try:
ensure_pkg("bavisitter", "bavisitter")
except Exception:
ensure_pkg("git+https://github.com/jiwnchoi/Bavisitter.git", "bavisitter")
for spec in [("pandas","pandas"), ("altair","altair"), ("vega_datasets","vega_datasets")]:
ensure_pkg(spec[0], spec[1])
import pandas as pd
import altair as alt
from vega_datasets import data
return data, mo, os
@app.cell
def _(mo):
# Cell 2
api_key_input = mo.ui.text(value="", label="OPENAI_API_KEY (opcional aquí; si ya está en el entorno, deja vacío)", full_width=True)
model_dd = mo.ui.dropdown(
options=["gpt-4o", "gpt-4o-mini", "gpt-4.1", "gpt-4o-reasoning"],
value="gpt-4o",
label="Modelo"
)
theme_dd = mo.ui.radio(options=["light", "dark"], value="light", label="Tema de Bavisitter")
launch_btn = mo.ui.run_button(label="Lanzar Bavisitter")
mo.vstack([api_key_input, mo.hstack([model_dd, theme_dd]), launch_btn])
return api_key_input, launch_btn, model_dd, theme_dd
@app.cell
def _(data, mo):
movies_df = data.movies()
# Se muestra automáticamente como tabla si es la última expresión:
mo.ui.dataframe(movies_df)
return (movies_df,)
@app.cell
def _(api_key_input, launch_btn, mo, model_dd, movies_df, os, theme_dd):
# Cell 4
from bavisitter import Bavisitter
# Si se pulsa el botón, configuramos la API key (si el usuario la introdujo)
if api_key_input.value:
os.environ["OPENAI_API_KEY"] = api_key_input.value
# Validaciones mínimas
has_key = bool(os.environ.get("OPENAI_API_KEY", "").strip())
mo.stop(not launch_btn.value, mo.md("Pulsa **Lanzar Bavisitter** para iniciar."))
mo.stop(not has_key, mo.md("Falta **OPENAI_API_KEY**. Escríbela en la celda de arriba o expórtala en el entorno."))
# Instanciar Bavisitter con el dataset de ejemplo y los parámetros elegidos
app = Bavisitter(
movies_df,
model=model_dd.value,
color_mode=theme_dd.value
)
app # Bavisitter renderiza su interfaz; úsala para pedir gráficos, detectar y resolver problemas de diseño
return
@app.cell
def _(os):
os.environ["OPENAI_API_KEY"]
return
if __name__ == "__main__":
app.run()
+101
View File
@@ -0,0 +1,101 @@
import marimo
__generated_with = "0.15.1"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
from d2_widget import Widget
import pathlib
import datetime as dt
return Widget, mo, pathlib
@app.cell(hide_code=True)
def _(mo):
# Definición por defecto (puedes editarla en el textarea)
d2_source_default = """
# Ajustes de layout
direction: right
# Nodos
app: {
label: "Ingesta"
shape: rectangle
}
db: {
label: "PostgreSQL"
shape: cylinder
}
dashboard: {
label: "Metabase"
}
# Enlaces
app -> db: "escribe"
db -> dashboard: "lee"
"""
# Widgets de UI
editor = mo.ui.text_area(value=d2_source_default, label="Definición D2", full_width=True)
theme_id = mo.ui.number(value=200, label="themeID (D2)")
pad_px = mo.ui.number(value=8, label="Padding")
sketch_mode = mo.ui.checkbox(label="Sketch (boceto)", value=False)
animate_edges = mo.ui.checkbox(label="Animar aristas", value=True)
export_name = mo.ui.text(value="diagrama.svg", label="Archivo SVG")
export_button = mo.ui.run_button(label="Exportar SVG")
# Layout
mo.ui.tabs({
"Editor": mo.vstack([
mo.md("## Diagrama D2"),
editor,
]),
"Opciones": mo.vstack([
mo.hstack([theme_id, pad_px, sketch_mode, animate_edges], justify="start", gap="1rem"),
mo.md("Configura opciones del compilador D2."),
]),
"Exportar": mo.vstack([
mo.hstack([export_name, export_button], gap="1rem"),
]),
})
return editor, export_button, export_name, pad_px, sketch_mode, theme_id
@app.cell
def _(pad_px, sketch_mode, theme_id):
compile_options = {
"themeID": int(theme_id.value),
"pad": int(pad_px.value),
"sketch": bool(sketch_mode.value),
}
return (compile_options,)
@app.cell
def _(Widget, compile_options, editor):
diagram_widget = Widget(editor.value, compile_options)
diagram_widget
return (diagram_widget,)
@app.cell
def _(diagram_widget, export_button, export_name, mo, pathlib):
if export_button.value:
svg_text = diagram_widget.svg
out_path = pathlib.Path(export_name.value)
out_path.write_text(svg_text, encoding="utf-8")
mo.md(f"### SVG exportado\n[Descargar SVG]({out_path})")
else:
mo.md("Pulsa **Exportar SVG** para guardar el archivo.")
return
if __name__ == "__main__":
app.run()
+265
View File
@@ -0,0 +1,265 @@
import marimo
__generated_with = "0.15.1"
app = marimo.App(width="columns")
@app.cell(column=0)
def _():
import marimo as mo
from vega_datasets import data
import altair as alt
import pandas as pd
import pyarrow
return alt, data, mo, pd
@app.cell
def _(mo):
mo.md(r"""Visualizacion de barras categóricas Pudiendo seleccionar una""")
return
@app.cell
def _(alt, mo, pd):
dataset = pd.DataFrame([
{"a": "A", "b": 28},
{"a": "B", "b": 55},
{"a": "C", "b": 43},
{"a": "D", "b": 91},
{"a": "E", "b": 81},
{"a": "F", "b": 53},
{"a": "G", "b": 19},
{"a": "H", "b": 87},
{"a": "I", "b": 52},
])
_x = "a:O" # categórica
_y = "b:Q" # numérica
# Selección solo por clic
select1 = alt.selection_point(name="select", on="click")
stroke_width1 = (
alt.when(select1).then(alt.value(2, empty=False)).otherwise(alt.value(0))
)
_bar_chart1 = (
alt.Chart(
dataset,
height=200,
config=alt.Config(scale=alt.ScaleConfig(bandPaddingInner=0.2)),
)
.mark_bar(fill="#4C78A8", stroke="black", cursor="pointer")
.encode(
x= _x,
y= _y,
fillOpacity=alt.when(select1).then(alt.value(1)).otherwise(alt.value(0.3)),
strokeWidth=stroke_width1,
)
.add_params(select1)
)
bar_chart1 = mo.ui.altair_chart(_bar_chart1)
bar_chart1
return bar_chart1, dataset
@app.cell
def _(bar_chart1, dataset, mo):
filtered_data1 = mo.ui.table(bar_chart1.value if len(bar_chart1.value) else dataset)
filtered_data1
return
@app.cell
def _(mo):
mo.md(r"""Visualización de barras categóricas con media pudiéndose seleccionar varias""")
return
@app.cell
def _(alt, data, mo):
dataset_weather = data.seattle_weather()
# Crear columnas explícitas de mes para evitar errores en marimo
dataset_weather2 = dataset_weather.copy()
dataset_weather2["month_date"] = dataset_weather2["date"].dt.month
dataset_weather2["month_name"] = dataset_weather2["date"].dt.strftime("%b") # Ej: Jan, Feb...
# Variables X y Y
_x = "month_name:O" # categórica
_y = "mean(precipitation):Q" # numérica
# Selección por intervalo (brush) en el eje X
brush2 = alt.selection_interval(encodings=["x"])
bars2 = (
alt.Chart(dataset_weather2)
.mark_bar()
.encode(
x=_x,
y=_y,
opacity=alt.when(brush2).then(alt.value(1)).otherwise(alt.value(0.7)),
)
.add_params(brush2)
)
line2 = (
alt.Chart(dataset_weather2)
.mark_rule(color="firebrick")
.encode(
y=_y,
size=alt.SizeValue(3),
)
.transform_filter(brush2)
)
layered_chart2 = alt.layer(bars2, line2)
weather_chart2 = mo.ui.altair_chart(layered_chart2)
weather_chart2
return dataset_weather2, weather_chart2
@app.cell
def _(dataset_weather2, mo, weather_chart2):
filtered_weather2 = mo.ui.table(weather_chart2.value if len(weather_chart2.value) else dataset_weather2)
filtered_weather2
return
@app.cell(column=1)
def _(mo):
mo.md(r"""Gráfico con agrupación en Slider""")
return
@app.cell
def _(alt, data, mo):
# from vega_datasets import data
# Cargar dataset
dataset_movies3 = data.movies()
# Variables X e Y
_x = "IMDB_Rating:Q" # numérica
_y = "Rotten_Tomatoes_Rating:Q" # numérica
# Slider de Altair (param embebido en el gráfico)
slider_binding3 = alt.binding_range(min=0, max=10, step=0.1)
threshold3 = alt.param(name="threshold3", value=5, bind=slider_binding3)
# Capa 1: puntos con IMDB >= threshold
points3 = (
alt.Chart(dataset_movies3)
.mark_circle()
.encode(
x=alt.X(_x, title="IMDB Rating"),
y=alt.Y(_y, title="Rotten Tomatoes Rating")
)
.transform_filter(
alt.datum.IMDB_Rating >= threshold3
)
)
# Capa 2: puntos agregados con IMDB < threshold
binned3 = (
alt.Chart(dataset_movies3)
.mark_circle()
.encode(
x=alt.X(_x, bin=alt.Bin(maxbins=10)),
y=alt.Y(_y, bin=alt.Bin(maxbins=10)),
size=alt.Size("count():Q", scale=alt.Scale(domain=[0,160]))
)
.transform_filter(
alt.datum.IMDB_Rating < threshold3
)
)
# Capa 3: regla vertical del threshold
rule3 = (
alt.Chart()
.mark_rule(color="gray")
.encode(
strokeWidth=alt.StrokeWidth(value=6),
x=alt.X(datum=alt.expr(threshold3.name), type="quantitative")
)
)
# Combinar capas
layered_movies3 = alt.layer(points3, binned3, rule3).add_params(threshold3)
movies_chart3 = mo.ui.altair_chart(layered_movies3)
movies_chart3
return
@app.cell
def _(mo):
mo.md(r"""Altair está trabajando bien en marimor pero aún tiene algunas partes por pulir como por ejemplo la capacidad de tener dos gráficos En una misma celda También tiene problemas a la hora de tener Y filtrar En diferentes celdas además de pasar datos como por ejemplo el Slider""")
return
@app.cell
def _(alt, data, mo):
# Dataset
dataset_movies4 = data.movies()
# Variables
x1 = "IMDB_Rating:Q" # numérica
y1 = "Rotten_Tomatoes_Rating:Q" # numérica
# Selección
pts4 = alt.selection_point(encodings=['x'], name="pts4")
# Heatmap base
rect4 = (
alt.Chart(dataset_movies4)
.mark_rect()
.encode(
x=alt.X(x1, bin=True),
y=alt.Y(y1, bin=True),
color=alt.Color(
"count()",
scale=alt.Scale(scheme="greenblue"),
legend=alt.Legend(title="Total Records")
)
)
.properties(width=550, height=300)
)
movies_rect4 = mo.ui.altair_chart(rect4.add_params(pts4))
movies_rect4
return dataset_movies4, pts4, x1, y1
@app.cell
def _(alt, dataset_movies4, mo, pts4, x1, y1):
circ4 = (
alt.Chart(dataset_movies4)
.mark_point(color="grey")
.encode(
x=alt.X(x1, bin=True),
y=alt.Y(y1, bin=True),
size=alt.Size("count()", legend=alt.Legend(title="Records in Selection"))
)
.transform_filter(pts4)
.properties(width=550, height=300)
)
movies_circ4 = mo.ui.altair_chart(circ4)
movies_circ4
return
if __name__ == "__main__":
app.run()
+144
View File
@@ -0,0 +1,144 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(
width="medium",
layout_file="layouts/Graficos_plotly.grid.json",
)
@app.cell
def _():
# Cell 1: gráfico Plotly (reactivo)
import marimo as mo
import pandas as pd
import plotly.express as px
import numpy as np
df = pd.read_json("https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json")
fig = px.scatter(
df,
x="Horsepower",
y="Miles_per_Gallon",
color="Origin",
hover_data=["Name"],
)
plot = mo.ui.plotly(fig) # Solo scatter/treemap/sunburst soportan selección reactiva
plot
return df, mo, np, pd, plot, px
@app.cell
def _(df, plot):
# Cell 2: dataset filtrado (reactivo e interactivo)
# marimo vuelve a ejecutar esta celda cuando cambia la selección del gráfico
indices = plot.indices # lista de int con las filas seleccionadas
selected_df = df.iloc[indices] if indices else df
# Visor interactivo con búsqueda/ordenación/filtros integrados
selected_df
return
@app.cell
def _(pd):
# Cell 2 • datos base (Vega cars)
cars_df = pd.read_json("https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json")
cars_df["Year"] = pd.to_datetime(cars_df["Year"])
cars_df["Horsepower_num"] = pd.to_numeric(cars_df["Horsepower"], errors="coerce")
numeric_cols = ["Miles_per_Gallon","Horsepower_num","Weight_in_lbs","Acceleration","Displacement"]
cars_df
return cars_df, numeric_cols
@app.cell
def _(cars_df, mo, np):
# Cell 3 • controles UI (reactivos)
origin_dd = mo.ui.dropdown(options=["All"] + sorted(cars_df["Origin"].dropna().unique().tolist()),
value="All", label="Origin")
year_min = int(cars_df["Year"].dt.year.min())
year_max = int(cars_df["Year"].dt.year.max())
year_rg = mo.ui.range_slider(year_min, year_max, value=(year_min, year_max), step=1, label="Year range")
hp_min = int(np.nanmin(cars_df["Horsepower_num"]))
hp_max = int(np.nanmax(cars_df["Horsepower_num"]))
hp_rg = mo.ui.range_slider(hp_min, hp_max, value=(hp_min, hp_max), step=5, label="Horsepower range")
bins_sl = mo.ui.slider(5, 60, value=20, step=1, label="Bins (hist)")
y_metric = mo.ui.radio(options=["Miles_per_Gallon","Horsepower_num","Weight_in_lbs","Acceleration","Displacement"],
value="Miles_per_Gallon", label="Y metric (box)")
mo.hstack([origin_dd, year_rg, hp_rg, bins_sl, y_metric])
return bins_sl, hp_rg, origin_dd, y_metric, year_rg
@app.cell
def _(cars_df, hp_rg, origin_dd, year_rg):
# Cell 4 • dataframe filtrado (base para los 5 gráficos)
df_filtrado = cars_df.copy()
if origin_dd.value != "All":
df_filtrado = df_filtrado[df_filtrado["Origin"] == origin_dd.value]
df_filtrado = df_filtrado[
(df_filtrado["Year"].dt.year >= year_rg.value[0]) &
(df_filtrado["Year"].dt.year <= year_rg.value[1]) &
(df_filtrado["Horsepower_num"].between(hp_rg.value[0], hp_rg.value[1]))
].dropna(subset=["Miles_per_Gallon","Horsepower_num"])
df_filtrado
return (df_filtrado,)
@app.cell
def _(df_filtrado, mo, px):
# Cell 5 • (1) Line chart: media de MPG por año y origen
line_df = (df_filtrado
.assign(YearNum=df_filtrado["Year"].dt.year)
.groupby(["YearNum","Origin"], as_index=False)["Miles_per_Gallon"].mean())
line_fig1 = px.line(line_df, x="YearNum", y="Miles_per_Gallon", color="Origin",
markers=True, title="MPG medio por año")
line_plot1 = mo.ui.plotly(line_fig1)
line_plot1
return
@app.cell
def _(bins_sl, df_filtrado, mo, px):
# Cell 6 • (2) Histogram: distribución de Horsepower (nbins reactivo)
hist_fig1 = px.histogram(df_filtrado, x="Horsepower_num", nbins=bins_sl.value,
color="Origin", title="Distribución de Horsepower")
hist_plot1 = mo.ui.plotly(hist_fig1)
hist_plot1
return
@app.cell
def _(df_filtrado, mo, px, y_metric):
# Cell 7 • (3) Box plot: métrica Y por Origin (métrica reactiva)
box_fig1 = px.box(df_filtrado, x="Origin", y=y_metric.value, points="outliers",
title=f"Distribución de {y_metric.value} por Origin")
box_plot1 = mo.ui.plotly(box_fig1)
box_plot1
return
@app.cell
def _(df_filtrado, mo, px):
# Cell 8 • (4) Density heatmap: Horsepower vs MPG
heat_fig1 = px.density_heatmap(df_filtrado, x="Horsepower_num", y="Miles_per_Gallon",
nbinsx=40, nbinsy=30, title="Densidad HP vs MPG")
heat_plot1 = mo.ui.plotly(heat_fig1)
heat_plot1
return
@app.cell
def _(df_filtrado, mo, numeric_cols, px):
# Cell 9 • (5) Scatter matrix: relación entre variables numéricas
splom_fig1 = px.scatter_matrix(df_filtrado[numeric_cols], dimensions=numeric_cols,
title="Scatter Matrix de variables numéricas")
splom_plot1 = mo.ui.plotly(splom_fig1)
splom_plot1
return
if __name__ == "__main__":
app.run()
+25
View File
@@ -0,0 +1,25 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
import polars as pl
import quak
return mo, pl, quak
@app.cell
def _(mo, pl, quak):
src = "https://github.com/uwdata/mosaic/raw/main/data/athletes.parquet"
df = pl.read_parquet(src) # Cambialo por tu dataframe
q = quak.Widget(df)
mo.ui.anywidget(q)
return
if __name__ == "__main__":
app.run()
+363
View File
@@ -0,0 +1,363 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
return
@app.cell
def _(alt, df, df_base, mo, slider_2d):
chart = (
alt.Chart(df_base).mark_point(color="gray").encode(x="x", y="y") +
alt.Chart(df).mark_point().encode(x="x", y="y")
).properties(width=300, height=300)
mo.vstack([
mo.md("""
## `Slider2D` demo
```python
from wigglystuff import Slider2D
slider_2d = Slider2D(width=300, height=300)
```
This demo contains a two dimensional slider. The thinking is that sometimes you want to be able to make changes to two variables at the same time. The output is always standardized to the range of -1 to 1, but you can always use custom code to adapt this."""),
mo.hstack([slider_2d, chart])
])
return
@app.cell
def _(alt, arr, df_orig, mat, mo, np, pd):
x_sim = np.random.multivariate_normal(
np.array(arr.matrix).reshape(-1),
np.array(mat.matrix),
2500
)
df_sim = pd.DataFrame({"x": x_sim[:, 0], "y": x_sim[:, 1]})
chart_sim = (
alt.Chart(df_sim).mark_point().encode(x="x", y="y") +
alt.Chart(df_orig).mark_point(color="gray").encode(x="x", y="y")
)
mo.vstack([
mo.md("""
## `Matrix` demo
```python
from wigglystuff import Matrix
arr = Matrix(rows=1, cols=2, step=0.1)
mat = Matrix(matrix=np.eye(2), mirror=True, step=0.1)
```
This demo contains a representation of a two dimensional gaussian distribution. You can adapt the center by changing the first array that represents the mean and the variance can be updated by alterering the second one that represents the covariance matrix. Notice how the latter matrix has a triangular constraint."""),
mo.hstack([arr, mat, chart_sim])
])
return
@app.cell
def _(Matrix, mo, np, pd):
pca_mat = mo.ui.anywidget(Matrix(np.random.normal(0, 1, size=(3, 2)), step=0.1))
rgb_mat = np.random.randint(0, 255, size=(1000, 3))
color = ["#{0:02x}{1:02x}{2:02x}".format(r, g, b) for r,g,b in rgb_mat]
rgb_df = pd.DataFrame({
"r": rgb_mat[:, 0], "g": rgb_mat[:, 1], "b": rgb_mat[:, 2], 'color': color
})
return color, pca_mat, rgb_mat
@app.cell
def _(alt, color, mo, pca_mat, pd, rgb_mat):
X_tfm = rgb_mat @ pca_mat.matrix
df_pca = pd.DataFrame({"x": X_tfm[:, 0], "y": X_tfm[:, 1], "c": color})
pca_chart = alt.Chart(df_pca).mark_point().encode(x="x", y="y", color=alt.Color('c:N', scale = None))
mo.vstack([
mo.md("""
### PCA demo with `Matrix`
Ever want to do your own PCA? Try to figure out a mapping from a 3d color map to a 2d representation with the transformation matrix below."""),
mo.hstack([pca_mat, pca_chart])
])
return
@app.cell
def _(c, coffees, mo, price, prob1, prob2, saying, shouting, times, total):
mo.vstack([
mo.md(f"""
## Tangle objects
Very much inspired by [tangle.js](), this library also offers some sliders/choice elements that can natively be combined in markdown.
```python
from wigglystuff import TangleSlider
```
There are some examples below.
### Apples example
Suppose that you have {coffees} and they each cost {price} then in total you would need to spend ${total:.2f}.
### Amdhals law
You cannot always get a speedup by throwing more compute at a problem. Let's compare two scenarios.
- You might have a parallel program that needs to sync up {prob1}.
- Another parallel program needs to sync up {prob2}.
The consequences of these choices are shown below. You might be suprised at the result, but you need to remember that if you throw more cores at the problem then you will also have more cores that will idle when the program needs to sync.
"""),
c,
mo.md(f"""
### Also a choice widget
The slider widget can do numeric values for you, but sometimes you also want to make a choice between discrete choices. For that, you can use the `TangleChoice` widget.
```python
from wigglystuff import TangleChoice
```
As a quick demo, let's repeat {saying} {times}.
{" ".join([saying.choice] * int(times.amount))}
"""
),
mo.md(f"""
### Also a select widget
Like `TangleChoice` but as a drop-down
```python
from wigglystuff import TangleSelect
```
As a quick demo, let's repeat {shouting} {times}.
{" ".join([shouting.choice] * int(times.amount))}
"""
)
])
return
@app.cell
def _(color_picker, mo):
mo.vstack(
[
mo.md(f"""
## Pick colors
Pick colors using a standard browser color input.
```python
from wigglystuff import ColorPicker
ColorPicker(color="#444444")
```
You can use a color picker with marimo's `Html` to affect how things are rendered.
"""),
mo.Html(f'<p style="color: {color_picker.color}">Change my color!</p>'),
mo.hstack([
color_picker,
mo.md(f"You selected {color_picker.value['color']} which is {color_picker.rgb} in RGB values.")
]),
]
)
return
@app.cell
def _(edge_widget, mo):
mo.vstack([
mo.md(f"""
## Drawing Edges
We even have a tool that allows you to connect nodes by drawing edges!
```python
from wigglystuff import EdgeDraw
EdgeDraw(["a", "b", "c", "d"])
```
Try it yourself by drawing below.
"""),
edge_widget,
mo.md(f"""
As you draw more nodes, you will also update the `widget.links` property.
"""),
edge_widget.links
])
return
@app.cell
def _(mo, sortable_list):
mo.vstack([
mo.md("""
## Sortable Lists
```python
from wigglystuff import SortableList
SortableList(["Action", "Comedy", "Drama"], addable=True, removable=True, editable=True)
```
Try dragging items to reorder, adding new items, clicking to edit, or removing with the [x] buttons.
"""),
sortable_list,
mo.md(f"Current value: `{sortable_list.value}`")
])
return
@app.cell
def _(mo):
from wigglystuff import EdgeDraw
edge_widget = mo.ui.anywidget(EdgeDraw(["a", "b", "c", "d"]))
return (edge_widget,)
@app.cell
def _(coffees, price):
# You need to define derivates in other cells.
total = coffees.amount * price.amount
return (total,)
@app.cell
def _(alt, np, pd, prob1, prob2):
cores = np.arange(1, 64 + 1)
p1, p2 = prob1.amount/100, prob2.amount/100
eff1 = 1/(p1 + (1-p1)/cores)
eff2 = 1/(p2 + (1-p2)/cores)
df_amdahl = pd.DataFrame({
'cores': cores,
f'{prob1.amount:.2f}% sync rate': eff1,
f'{prob2.amount:.2f}% sync rate': eff2
}).melt("cores")
c = (
alt.Chart(df_amdahl)
.mark_line()
.encode(
x='cores',
y=alt.Y('value').title("effective cores"),
color="variable"
)
.properties(width=500, title="Comparison between cores and actual speedup.")
)
return (c,)
@app.cell
def _(mo):
from wigglystuff import TangleSlider, TangleChoice, TangleSelect
coffees = mo.ui.anywidget(TangleSlider(amount=10, min_value=0, step=1, suffix=" coffees", digits=0))
price = mo.ui.anywidget(TangleSlider(amount=3.50, min_value=0.01, max_value=10, step=0.01, prefix="$", digits=2))
prob1 = mo.ui.anywidget(TangleSlider(min_value=0, max_value=20, step=0.1, suffix="% of the time", amount=5))
prob2 = mo.ui.anywidget(TangleSlider(min_value=0, max_value=20, step=0.1, suffix="% of the time", amount=0))
saying = mo.ui.anywidget(TangleChoice(["🙂", "🎉", "💥"]))
shouting = mo.ui.anywidget(TangleSelect(["🥔", "🥕", "🍎"]))
times = mo.ui.anywidget(TangleSlider(min_value=1, max_value=20, step=1, suffix=" times", amount=3))
return coffees, price, prob1, prob2, saying, shouting, times
@app.cell
def _():
import altair as alt
import marimo as mo
import micropip
import numpy as np
import pandas as pd
# await micropip.install("wigglystuff==0.1.1")
return alt, mo, np, pd
@app.cell
def _(mo, np):
from wigglystuff import Matrix
mat = mo.ui.anywidget(Matrix(matrix=np.eye(2), mirror=True, step=0.1))
arr = mo.ui.anywidget(Matrix(rows=1, cols=2, step=0.1))
return Matrix, arr, mat
@app.cell
def _(Matrix, mo, np):
x1 = mo.ui.anywidget(Matrix(matrix=np.eye(2), step=0.1))
x2 = mo.ui.anywidget(Matrix(matrix=np.random.random((2, 2)), step=0.1))
return
@app.cell
def _(mo):
from wigglystuff import Slider2D
slider_2d = mo.ui.anywidget(Slider2D(width=300, height=300))
return (slider_2d,)
@app.cell
def _(np, pd, slider_2d):
df = pd.DataFrame({
"x": np.random.normal(slider_2d.x * 10, 1, 2000),
"y": np.random.normal(slider_2d.y * 10, 1, 2000)
})
return (df,)
@app.cell
def _(np, pd):
df_base = pd.DataFrame({
"x": np.random.normal(0, 1, 2000),
"y": np.random.normal(0, 1, 2000)
})
return (df_base,)
@app.cell
def _(np, pd):
x_orig = np.random.multivariate_normal(np.array([0, 0]), np.array([[1, 0], [0, 1]]), 2500)
df_orig = pd.DataFrame({"x": x_orig[:, 0], "y": x_orig[:, 1]})
return (df_orig,)
@app.cell
def _(mo):
from wigglystuff import ColorPicker
color_picker = mo.ui.anywidget(ColorPicker(color="#444444"))
return (color_picker,)
@app.cell
def _(mo):
from wigglystuff import SortableList
sortable_list = mo.ui.anywidget(
SortableList(
["Action", "Comedy", "Drama", "Thriller", "Sci-Fi"],
addable=True,
removable=True,
editable=True,
)
)
return (sortable_list,)
if __name__ == "__main__":
app.run()
@@ -0,0 +1,169 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "aoMP",
"code_hash": "cc4a4b61a8ee606a4542fe01ac4a07df",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": [
{
"type": "stream",
"name": "stderr",
"text": "Resolved 175 packages in 1.52s\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Downloading hf-xet (3.0MiB)\nDownloading google-api-python-client (13.3MiB)\nDownloading semgrep (47.1MiB)\nDownloading fonttools (4.6MiB)\nDownloading tokenizers (3.2MiB)\nDownloading litellm (8.5MiB)\nDownloading selenium (9.2MiB)\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Building html2text==2024.2.26\n Building pyperclip==1.9.0\n Building wget==3.2\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Downloading debugpy (3.4MiB)\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Downloading tiktoken (1.0MiB)\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Built wget==3.2\n Built pyperclip==1.9.0\n Built html2text==2024.2.26\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Downloading bavisitter (5.5MiB)\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading tiktoken\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading hf-xet\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading tokenizers\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading debugpy\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading fonttools\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading litellm\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading bavisitter\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading selenium\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading google-api-python-client\n"
},
{
"type": "stream",
"name": "stderr",
"text": " Downloading semgrep\nPrepared 74 packages in 3.15s\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Uninstalled 7 packages in 17ms\n"
},
{
"type": "stream",
"name": "stderr",
"text": "Installed 124 packages in 92ms\n + aiohappyeyeballs==2.6.1\n + aiohttp==3.12.15\n + aiosignal==1.4.0\n + anthropic==0.37.1\n + async-timeout==5.0.1\n + bavisitter==0.0.4\n + blessed==1.21.0\n + boltons==21.0.0\n + bracex==2.6\n - cachetools==6.2.0\n + cachetools==5.5.2\n - click==8.2.1\n + click==8.1.8\n + click-option-group==0.5.7\n + colorama==0.4.6\n + contourpy==1.3.2\n + cycler==0.12.1\n + debugpy==1.8.16\n + defusedxml==0.7.1\n + deprecated==1.2.18\n + distro==1.9.0\n + editor==1.6.6\n - exceptiongroup==1.3.0\n + exceptiongroup==1.2.2\n + face==24.0.0\n + filelock==3.19.1\n + fonttools==4.59.2\n + frozenlist==1.7.0\n + fsspec==2025.7.0\n + git-python==1.0.3\n + gitdb==4.0.12\n + gitpython==3.1.45\n + glom==22.1.0\n + google-ai-generativelanguage==0.6.6\n + google-api-core==2.25.1\n + google-api-python-client==2.179.0\n + google-auth==2.40.3\n + google-auth-httplib2==0.2.0\n + google-generativeai==0.7.2\n + googleapis-common-protos==1.70.0\n + grpcio==1.74.0\n + grpcio-status==1.62.3\n + hf-xet==1.1.9\n + html2image==2.0.7\n + html2text==2024.2.26\n + httpcore==1.0.9\n + httplib2==0.30.0\n + httpx==0.28.1\n + huggingface-hub==0.34.4\n + importlib-metadata==7.1.0\n + inquirer==3.4.1\n + ipykernel==6.30.1\n + jiter==0.10.0\n + joblib==1.5.2\n + jupyter-client==8.6.3\n + jupyter-core==5.8.1\n + kiwisolver==1.4.9\n + litellm==1.76.0\n + markdown-it-py==4.0.0\n + matplotlib==3.10.5\n + mdurl==0.1.2\n + multidict==6.6.4\n + nest-asyncio==1.6.0\n + nltk==3.9.1\n + open-interpreter==0.4.3\n + openai==1.102.0\n + opentelemetry-api==1.25.0\n + opentelemetry-exporter-otlp-proto-common==1.25.0\n + opentelemetry-exporter-otlp-proto-http==1.25.0\n + opentelemetry-instrumentation==0.46b0\n + opentelemetry-instrumentation-requests==0.46b0\n + opentelemetry-proto==1.25.0\n + opentelemetry-sdk==1.25.0\n + opentelemetry-semantic-conventions==0.46b0\n + opentelemetry-util-http==0.46b0\n + outcome==1.3.0.post0\n + pillow==11.3.0\n + propcache==0.3.2\n + proto-plus==1.26.1\n - protobuf==6.32.0\n + protobuf==4.25.8\n - psutil==7.0.0\n + psutil==5.9.8\n + pyasn1==0.6.1\n + pyasn1-modules==0.4.2\n + pyparsing==3.2.3\n + pyperclip==1.9.0\n + pysocks==1.7.1\n + python-dotenv==1.1.1\n + pyzmq==27.0.2\n + readchar==4.2.1\n + regex==2025.8.29\n + rich==13.5.3\n + rsa==4.9.1\n + ruamel-yaml==0.18.15\n + ruamel-yaml-clib==0.2.12\n + runs==1.2.2\n + selenium==4.35.0\n + semgrep==1.134.0\n + send2trash==1.8.3\n + setuptools==80.9.0\n + shellingham==1.5.4\n + shortuuid==1.0.13\n + smmap==5.0.2\n + sortedcontainers==2.4.0\n - starlette==0.47.3\n + starlette==0.37.2\n + termcolor==2.3.0\n + tiktoken==0.7.0\n + tokenizers==0.22.0\n + tokentrim==0.1.13\n + toml==0.10.2\n + tomli==2.0.2\n + tornado==6.5.2\n + tqdm==4.67.1\n + trio==0.30.0\n + trio-websocket==0.12.2\n + typer==0.12.5\n - typing-extensions==4.15.0\n + typing-extensions==4.14.1\n + uritemplate==4.2.0\n + wcmatch==8.5.2\n + webdriver-manager==4.0.2\n + websocket-client==1.8.0\n + wget==3.2\n + wrapt==1.17.3\n + wsproto==1.2.0\n + xmod==1.8.1\n + yarl==1.20.1\n + yaspin==3.1.0\n + zipp==3.23.0\n"
}
]
},
{
"id": "aCWo",
"code_hash": "3ae165e9e1c0e9cb05ace86a6dc3f262",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<div style='display: flex;flex: 1;flex-direction: column;justify-content: flex-start;align-items: normal;flex-wrap: nowrap;gap: 0.5rem'><marimo-ui-element object-id='aCWo-0' random-id='973dcdc4-688b-bb8b-2945-e809bf38c4e9'><marimo-text data-initial-value='&quot;&quot;' data-label='&quot;&lt;span class=&#92;&quot;markdown prose dark:prose-invert&#92;&quot;&gt;&lt;span class=&#92;&quot;paragraph&#92;&quot;&gt;OPENAI_API_KEY (opcional aqu&#92;u00ed; si ya est&#92;u00e1 en el entorno, deja vac&#92;u00edo)&lt;/span&gt;&lt;/span&gt;&quot;' data-placeholder='&quot;&quot;' data-kind='&quot;text&quot;' data-full-width='true' data-disabled='false' data-debounce='true'></marimo-text></marimo-ui-element><div style='display: flex;flex: 1;flex-direction: row;justify-content: space-between;align-items: normal;flex-wrap: nowrap;gap: 0.5rem'><marimo-ui-element object-id='aCWo-1' random-id='3cb2f00b-da11-1c60-511a-0fb0f40f62bb'><marimo-dropdown data-initial-value='[&quot;gpt-4o&quot;]' data-label='&quot;&lt;span class=&#92;&quot;markdown prose dark:prose-invert&#92;&quot;&gt;&lt;span class=&#92;&quot;paragraph&#92;&quot;&gt;Modelo&lt;/span&gt;&lt;/span&gt;&quot;' data-options='[&quot;gpt-4o&quot;, &quot;gpt-4o-mini&quot;, &quot;gpt-4.1&quot;, &quot;gpt-4o-reasoning&quot;]' data-allow-select-none='false' data-searchable='false' data-full-width='false'></marimo-dropdown></marimo-ui-element><marimo-ui-element object-id='aCWo-2' random-id='1f4a9be7-b346-f30e-90db-bba56e3a0e5d'><marimo-radio data-initial-value='&quot;light&quot;' data-label='&quot;&lt;span class=&#92;&quot;markdown prose dark:prose-invert&#92;&quot;&gt;&lt;span class=&#92;&quot;paragraph&#92;&quot;&gt;Tema de Bavisitter&lt;/span&gt;&lt;/span&gt;&quot;' data-options='[&quot;light&quot;, &quot;dark&quot;]' data-inline='false' data-disabled='false'></marimo-radio></marimo-ui-element></div><marimo-ui-element object-id='aCWo-3' random-id='70c7c342-fe90-38ba-ef7e-40ac99eade64'><marimo-button data-initial-value='0' data-label='&quot;&lt;span class=&#92;&quot;markdown prose dark:prose-invert&#92;&quot;&gt;&lt;span class=&#92;&quot;paragraph&#92;&quot;&gt;Lanzar Bavisitter&lt;/span&gt;&lt;/span&gt;&quot;' data-kind='&quot;neutral&quot;' data-disabled='false' data-full-width='false'></marimo-button></marimo-ui-element></div>"
}
}
],
"console": []
},
{
"id": "ajNN",
"code_hash": "8d688461aee68cf5f12da3ab4e8f4d82",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<marimo-ui-element object-id='ajNN-0' random-id='a2cef7d0-091c-2095-7646-1e56d231f627'><marimo-dataframe data-initial-value='{&quot;transforms&quot;: []}' data-label='null' data-columns='[[&quot;Title&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;US_Gross&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;Worldwide_Gross&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;US_DVD_Sales&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;Production_Budget&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;Release_Date&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;MPAA_Rating&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Running_Time_min&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;Distributor&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Source&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Major_Genre&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Creative_Type&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Director&quot;, &quot;string&quot;, &quot;object&quot;], [&quot;Rotten_Tomatoes_Rating&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;IMDB_Rating&quot;, &quot;number&quot;, &quot;float64&quot;], [&quot;IMDB_Votes&quot;, &quot;number&quot;, &quot;float64&quot;]]' data-dataframe-name='&quot;movies_df&quot;' data-total='3201' data-page-size='5'></marimo-dataframe></marimo-ui-element>"
}
}
],
"console": []
},
{
"id": "pYQS",
"code_hash": "22608c40d28d2013c08e9b6eaf612cd2",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<marimo-ui-element object-id='pYQS-0' random-id='57ca6e34-3f48-9604-fe44-fe037431fd55'><marimo-anywidget data-initial-value='{&quot;color_mode&quot;: &quot;light&quot;, &quot;ipc_queue&quot;: [], &quot;messages&quot;: [], &quot;streaming&quot;: false}' data-label='null' data-js-url='&quot;./@file/20082536-42199-jhi6K66B.js&quot;' data-js-hash='&quot;43fe0f7d5f2958f5c0b3f1f8d8c49a4f&quot;' data-css='&quot;&quot;' data-buffer-paths='[]'></marimo-anywidget></marimo-ui-element>"
}
}
],
"console": []
},
{
"id": "gTaR",
"code_hash": "f452725116c9b87037dbbb8dfc325ec1",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<pre style='font-size: 12px'>&#x27;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&#x27;</pre>"
}
}
],
"console": []
}
]
}
File diff suppressed because one or more lines are too long
+67 -1
View File
@@ -6,7 +6,73 @@
"cells": [
{
"id": "Hbol",
"code_hash": null,
"code_hash": "0914bd1bbeb9727790bfe2b5da7b0eb3",
"outputs": [],
"console": []
},
{
"id": "MJUe",
"code_hash": "d779223af5e58013abde68cbe3a3e517",
"outputs": [],
"console": []
},
{
"id": "vblA",
"code_hash": "16b2686f0a3092171143731bc921c8b9",
"outputs": [],
"console": []
},
{
"id": "bkHC",
"code_hash": "49cb0878d7ec60b899cafc532ba7b203",
"outputs": [],
"console": []
},
{
"id": "lEQa",
"code_hash": "1310ca8f87851c6abfc360ed4ad866c5",
"outputs": [],
"console": []
},
{
"id": "PKri",
"code_hash": "d1adb234ad2ebdd558a8b780168b7504",
"outputs": [],
"console": []
},
{
"id": "Xref",
"code_hash": "501562d98a40f8ec082094108c01df7e",
"outputs": [],
"console": []
},
{
"id": "SFPL",
"code_hash": "c4a8490190c6289529269e729e711bfc",
"outputs": [],
"console": []
},
{
"id": "BYtC",
"code_hash": "fff70582cf75e221534d3bbc4f12a577",
"outputs": [],
"console": []
},
{
"id": "RGSE",
"code_hash": "dd8013e55251f1fccf2dc6b6c0015880",
"outputs": [],
"console": []
},
{
"id": "Kclp",
"code_hash": "05b631c7433a4800962bd9d1451e83c9",
"outputs": [],
"console": []
},
{
"id": "emfo",
"code_hash": "b70e25a38be12dcf8bbcb99cdcecf9d8",
"outputs": [],
"console": []
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "Hbol",
"code_hash": "4fa9f530528a67e370348eb38c6b7024",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "edAi",
"code_hash": "be5e1e397391c61c55f27bf5f6881fd2",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<marimo-ui-element object-id='edAi-0' random-id='973dcdc4-688b-bb8b-2945-e809bf38c4e9'><marimo-anywidget data-initial-value='{&quot;_columns&quot;: [&quot;id&quot;, &quot;name&quot;, &quot;nationality&quot;, &quot;sex&quot;, &quot;date_of_birth&quot;, &quot;height&quot;, &quot;weight&quot;, &quot;sport&quot;, &quot;gold&quot;, &quot;silver&quot;, &quot;bronze&quot;, &quot;info&quot;], &quot;_table_name&quot;: &quot;df&quot;, &quot;sql&quot;: &quot;SELECT * FROM &#92;&quot;df&#92;&quot;&quot;}' data-label='null' data-js-url='&quot;./@file/524489-31880-J4IXjEA0.js&quot;' data-js-hash='&quot;5ec7fd2207d37973a2148c7000e010a6&quot;' data-css='&quot;&quot;' data-buffer-paths='[]'></marimo-anywidget></marimo-ui-element>"
}
}
],
"console": []
}
]
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,53 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "Hbol",
"code_hash": "893c2cde3ff7b0b85a1848b84dc639b1",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "mNiZ",
"code_hash": "fd76317e77b2cdb53ad7dec19180a46d",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<marimo-ui-element object-id='mNiZ-0' random-id='e3a96b2b-fc0f-4574-72e3-dfe94f6627c6'><marimo-text data-initial-value='&quot;echo Hola desde KMD&quot;' data-label='&quot;&lt;span class=&#92;&quot;markdown prose dark:prose-invert&#92;&quot;&gt;&lt;span class=&#92;&quot;paragraph&#92;&quot;&gt;Comando&lt;/span&gt;&lt;/span&gt;&quot;' data-placeholder='&quot;&quot;' data-kind='&quot;text&quot;' data-full-width='false' data-disabled='false' data-debounce='true'></marimo-text></marimo-ui-element>"
}
}
],
"console": []
},
{
"id": "XVTp",
"code_hash": "3df5f5a32e040d8a340e350779b41c10",
"outputs": [
{
"type": "error",
"ename": "interruption",
"evalue": "This cell was interrupted and needs to be re-run",
"traceback": []
}
],
"console": [
{
"type": "stream",
"name": "stderr",
"text": "<span class=\"codehilite\"><div class=\"highlight\"><pre><span></span><span class=\"gt\">Traceback (most recent call last):</span>\n File <span class=\"nb\">&quot;/home/lucas/DataProyects/visualizaciones/.venv/lib/python3.10/site-packages/marimo/_runtime/executor.py&quot;</span>, line <span class=\"m\">138</span>, in <span class=\"n\">execute_cell</span>\n<span class=\"w\"> </span><span class=\"n\">exec</span><span class=\"p\">(</span><span class=\"n\">cell</span><span class=\"o\">.</span><span class=\"n\">body</span><span class=\"p\">,</span> <span class=\"n\">glbls</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/tmp/marimo_75418/__marimo__cell_XVTp_.py&quot;</span>, line <span class=\"m\">3</span>, in <span class=\"n\">&lt;module&gt;</span>\n<span class=\"w\"> </span><span class=\"n\">output</span> <span class=\"o\">=</span> <span class=\"n\">shell</span><span class=\"o\">.</span><span class=\"n\">do_run</span><span class=\"p\">(</span><span class=\"n\">command_input</span><span class=\"o\">.</span><span class=\"n\">value</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/tmp/marimo_75418/__marimo__cell_Hbol_.py&quot;</span>, line <span class=\"m\">13</span>, in <span class=\"n\">do_run</span>\n<span class=\"w\"> </span><span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">subprocess</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">(</span>\n File <span class=\"nb\">&quot;/usr/lib/python3.10/subprocess.py&quot;</span>, line <span class=\"m\">505</span>, in <span class=\"n\">run</span>\n<span class=\"w\"> </span><span class=\"n\">stdout</span><span class=\"p\">,</span> <span class=\"n\">stderr</span> <span class=\"o\">=</span> <span class=\"n\">process</span><span class=\"o\">.</span><span class=\"n\">communicate</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">,</span> <span class=\"n\">timeout</span><span class=\"o\">=</span><span class=\"n\">timeout</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/usr/lib/python3.10/subprocess.py&quot;</span>, line <span class=\"m\">1154</span>, in <span class=\"n\">communicate</span>\n<span class=\"w\"> </span><span class=\"n\">stdout</span><span class=\"p\">,</span> <span class=\"n\">stderr</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">_communicate</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">,</span> <span class=\"n\">endtime</span><span class=\"p\">,</span> <span class=\"n\">timeout</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/usr/lib/python3.10/subprocess.py&quot;</span>, line <span class=\"m\">2021</span>, in <span class=\"n\">_communicate</span>\n<span class=\"w\"> </span><span class=\"n\">ready</span> <span class=\"o\">=</span> <span class=\"n\">selector</span><span class=\"o\">.</span><span class=\"n\">select</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/usr/lib/python3.10/selectors.py&quot;</span>, line <span class=\"m\">416</span>, in <span class=\"n\">select</span>\n<span class=\"w\"> </span><span class=\"n\">fd_event_list</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">_selector</span><span class=\"o\">.</span><span class=\"n\">poll</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">)</span>\n File <span class=\"nb\">&quot;/home/lucas/DataProyects/visualizaciones/.venv/lib/python3.10/site-packages/marimo/_runtime/handlers.py&quot;</span>, line <span class=\"m\">32</span>, in <span class=\"n\">interrupt_handler</span>\n<span class=\"w\"> </span><span class=\"k\">raise</span> <span class=\"n\">MarimoInterrupt</span>\n<span class=\"gr\">KeyboardInterrupt</span>\n</pre></div>\n</span>"
}
]
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,137 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "Hbol",
"code_hash": "1d0db38904205bec4d6f6f6a1f6cec3e",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "MJUe",
"code_hash": "161ebcb820075ae0ed102ae0a00f47c2",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "vblA",
"code_hash": "f7f5821d3695ae3da07f153aa5a1a7a6",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "bkHC",
"code_hash": "dad9563ab733290d13df7d9f3c1f904b",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "XQvs",
"code_hash": null,
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": [
{
"type": "stream",
"name": "stdout",
"text": "Script guardado en scripts/mi_script.sh y listo para ejecutarse.\n"
}
]
},
{
"id": "lEQa",
"code_hash": "eea45b0636482cfa9d88aa3c65db2599",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": [
{
"type": "stream",
"name": "stdout",
"text": "Script guardado en scripts/mi_script.sh y listo para ejecutarse.\n"
}
]
},
{
"id": "PKri",
"code_hash": "de1dd307acb19774134d7d0913854010",
"outputs": [
{
"type": "data",
"data": {
"text/html": "<marimo-ui-element object-id='PKri-0' random-id='eb28ac23-1aa8-4a65-56fa-f187b12e4ce8'><marimo-anywidget data-initial-value='{&quot;command&quot;: &quot;bash -x ./scripts/mi_script.sh&quot;, &quot;working_directory&quot;: &quot;.&quot;}' data-label='null' data-js-url='&quot;./@file/10677-12323-PKp7Iw7j.js&quot;' data-js-hash='&quot;a1582408702671c0b1563e88a1e2c259&quot;' data-css='&quot;&quot;' data-buffer-paths='[]'></marimo-anywidget></marimo-ui-element>"
}
}
],
"console": []
},
{
"id": "Xref",
"code_hash": "9a13086a1230097669e15deae4b7eb72",
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
},
{
"id": "SFPL",
"code_hash": null,
"outputs": [
{
"type": "data",
"data": {
"text/plain": ""
}
}
],
"console": []
}
]
}
@@ -0,0 +1,86 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "Hbol",
"code_hash": "9b50c9b671e7c769d569eb6803bfa319",
"outputs": [],
"console": []
},
{
"id": "MJUe",
"code_hash": "b69c9e22f0e38d50fa6dd4fd0b32197d",
"outputs": [],
"console": []
},
{
"id": "vblA",
"code_hash": "aaa515da69322158d5f7ffc3309edb0b",
"outputs": [],
"console": []
},
{
"id": "bkHC",
"code_hash": "c4c089a66f59ff92da5acf684f4ff257",
"outputs": [],
"console": []
},
{
"id": "lEQa",
"code_hash": "0cecc7bcd800abfcada2f4d2b7494b10",
"outputs": [],
"console": []
},
{
"id": "PKri",
"code_hash": "2e869e6640acd43bb304ec05e69db2b8",
"outputs": [],
"console": []
},
{
"id": "Xref",
"code_hash": "14a4998b87bc95159a178f162e080a72",
"outputs": [],
"console": []
},
{
"id": "SFPL",
"code_hash": "3d94c0bbe21b88e5ce038a4529fd7d43",
"outputs": [],
"console": []
},
{
"id": "BYtC",
"code_hash": "5529c61207cdf6a5357bab61fb2aea56",
"outputs": [],
"console": []
},
{
"id": "RGSE",
"code_hash": "90cae7bc1234a321f4e97934254c0dcb",
"outputs": [],
"console": []
},
{
"id": "Kclp",
"code_hash": "c7351b53182b8fb61ffb6886d05807a6",
"outputs": [],
"console": []
},
{
"id": "emfo",
"code_hash": "1c680ba70a7a65bb39086d250190ed84",
"outputs": [],
"console": []
},
{
"id": "Hstk",
"code_hash": null,
"outputs": [],
"console": []
}
]
}
@@ -0,0 +1,86 @@
{
"version": "1",
"metadata": {
"marimo_version": "0.15.2"
},
"cells": [
{
"id": "Hbol",
"code_hash": "9b50c9b671e7c769d569eb6803bfa319",
"outputs": [],
"console": []
},
{
"id": "MJUe",
"code_hash": "b69c9e22f0e38d50fa6dd4fd0b32197d",
"outputs": [],
"console": []
},
{
"id": "vblA",
"code_hash": "aaa515da69322158d5f7ffc3309edb0b",
"outputs": [],
"console": []
},
{
"id": "bkHC",
"code_hash": "c4c089a66f59ff92da5acf684f4ff257",
"outputs": [],
"console": []
},
{
"id": "lEQa",
"code_hash": "0cecc7bcd800abfcada2f4d2b7494b10",
"outputs": [],
"console": []
},
{
"id": "PKri",
"code_hash": "2e869e6640acd43bb304ec05e69db2b8",
"outputs": [],
"console": []
},
{
"id": "Xref",
"code_hash": "14a4998b87bc95159a178f162e080a72",
"outputs": [],
"console": []
},
{
"id": "SFPL",
"code_hash": "3d94c0bbe21b88e5ce038a4529fd7d43",
"outputs": [],
"console": []
},
{
"id": "BYtC",
"code_hash": "5529c61207cdf6a5357bab61fb2aea56",
"outputs": [],
"console": []
},
{
"id": "RGSE",
"code_hash": "90cae7bc1234a321f4e97934254c0dcb",
"outputs": [],
"console": []
},
{
"id": "Kclp",
"code_hash": "c7351b53182b8fb61ffb6886d05807a6",
"outputs": [],
"console": []
},
{
"id": "emfo",
"code_hash": "1c680ba70a7a65bb39086d250190ed84",
"outputs": [],
"console": []
},
{
"id": "Hstk",
"code_hash": null,
"outputs": [],
"console": []
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
Parte,Porcentaje
P(A),25
P(B),25
P(A|B),25
P(B|A),25
1 Parte Porcentaje
2 P(A) 25
3 P(B) 25
4 P(A|B) 25
5 P(B|A) 25
+3202
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11539
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _():
from modraw import Draw
from mohtml import img
return Draw, img
@app.cell
def _(Draw, mo):
widget = mo.ui.anywidget(Draw(width=1000, height=450))
widget
return (widget,)
@app.cell
def _(img, mo, widget):
def safe_draw_preview(widget, mode="auto"):
"""
Render the Draw content without raising.
mode:
- "auto": prefer base64; fallback to PIL
- "base64": only base64
- "pil": only PIL
- "both": show both (for debugging)
"""
# Read base64 safely
b64 = None
try:
v = getattr(widget, "value", None)
if isinstance(v, dict):
b64 = v.get("base64")
except Exception:
b64 = None
# Get PIL safely
pil_img = None
if hasattr(widget, "get_pil"):
try:
pil_img = widget.get_pil()
except Exception:
pil_img = None
# Decide what to render to avoid duplication
children = []
if mode == "auto":
if b64:
children = [img(src=b64)]
elif pil_img is not None:
children = [pil_img]
elif mode == "base64":
if b64:
children = [img(src=b64)]
elif mode == "pil":
if pil_img is not None:
children = [pil_img]
elif mode == "both":
if b64:
children.append(img(src=b64))
if pil_img is not None:
children.append(pil_img)
return mo.vstack(children) if children else mo.md("Dibuja algo para previsualizar.")
safe_draw_preview(widget, "base64")# fuerza solo base64
return
if __name__ == "__main__":
app.run()
+48
View File
@@ -0,0 +1,48 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _():
from mopaint import Paint
from mohtml import img, div, tailwind_css
return Paint, div, img
@app.cell
def _(Paint, mo):
widget = mo.ui.anywidget(Paint(height=550))
widget
return (widget,)
@app.cell
def _(widget):
widget.value
return
@app.cell
def _(div, img, widget):
div(
img(src=widget.get_base64()), klass="bg-gray-200 p-4"
) # Use base64 representation directly with mohtml
return
@app.cell
def _(widget):
widget.get_pil()
return
if __name__ == "__main__":
app.run()
+214
View File
@@ -0,0 +1,214 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _():
# import marimo as mo
import sys
import subprocess
import importlib
import pandas as pd
import altair as alt
# Instalación perezosa de drawdata si no está disponible
def _ensure(pkg: str):
try:
importlib.import_module(pkg)
except ImportError:
subprocess.check_call([sys.executable, "-m", "pip", "install", pkg, "--quiet"])
_ensure("drawdata")
from drawdata import ScatterWidget, BarWidget # API principal
return BarWidget, ScatterWidget
@app.cell
def _(mo):
mode_selector = mo.ui.radio(
options=["Scatter", "Barras"],
value="Scatter",
label="Modo de dibujo"
)
mode_selector
return (mode_selector,)
@app.cell
def _(BarWidget, ScatterWidget, mo):
# Creamos ambos widgets una sola vez y los reusamos reactivamente
scatter_widget = ScatterWidget()
bar_widget = BarWidget(
collection_names=["Serie A", "Serie B"], # nombres de colecciones (series)
n_bins=20 # nº de bins para eje X
)
mo.md("Widgets inicializados. Usa el selector para mostrar uno u otro.")
return bar_widget, scatter_widget
@app.cell
def _(bar_widget, mode_selector, scatter_widget):
# Importante en marimo: leer .value en celda separada
current_widget = scatter_widget if mode_selector.value == "Scatter" else bar_widget
current_widget
return
@app.cell
def _(bar_widget, mo, mode_selector, scatter_widget):
# Para ScatterWidget y BarWidget existe .data_as_pandas
# En scatter: columnas típicas x, y, color
# En barras: columnas por bin/serie/valor (según widget)
df_drawn = (scatter_widget.data_as_pandas
if mode_selector.value == "Scatter"
else bar_widget.data_as_pandas)
# Validación básica
is_empty = (df_drawn is None) or (len(df_drawn) == 0)
mo.stop(is_empty, mo.md("Dibuja puntos (Scatter) o columnas (Barras) para ver datos."))
# Vista tabular interactiva
mo.ui.dataframe(df_drawn)
return (df_drawn,)
@app.cell
def _(df_drawn, mode_selector):
# Nota: todo está encapsulado en funciones locales para evitar redefinir variables globales.
def _build_chart_c6(df, mode):
import altair as alt
import marimo as mo
# Si no hay datos, mostramos aviso
if df is None or len(df) == 0:
return mo.md("Dibuja datos para visualizar el gráfico.")
# Mapeo robusto (lower -> original) DENTRO de la función
orig_cols = list(df.columns)
lower_map = {str(c).lower(): c for c in orig_cols}
if mode == "Scatter":
x_col = lower_map.get("x", orig_cols[0])
y_col = lower_map.get("y", orig_cols[1 if len(orig_cols) > 1 else 0])
color_col = lower_map.get("color", None)
chart_scatter = (
alt.Chart(df)
.mark_point(filled=True, opacity=0.8, size=80)
.encode(
x=alt.X(x_col, title=str(x_col)),
y=alt.Y(y_col, title=str(y_col)),
color=alt.Color(color_col, title=str(color_col)) if color_col else alt.value("steelblue"),
tooltip=orig_cols
)
.properties(title="Dispersión dibujada (reactivo)")
.interactive()
)
return chart_scatter
else:
# Barras: detectar bin/x, value/y y collection/serie si existe
bin_col = lower_map.get("bin") or lower_map.get("x") or orig_cols[0]
val_col = lower_map.get("value") or lower_map.get("y") or (orig_cols[1] if len(orig_cols) > 1 else orig_cols[0])
serie_col = (
lower_map.get("collection")
or lower_map.get("serie")
or lower_map.get("series")
or lower_map.get("group")
or None
)
base = alt.Chart(df).encode(
x=alt.X(bin_col, title=str(bin_col)),
y=alt.Y(val_col, title=str(val_col)),
tooltip=orig_cols
)
chart_bars = (
base.mark_bar()
.encode(color=alt.Color(serie_col, title=str(serie_col)) if serie_col else alt.value("steelblue"))
.properties(title="Barras dibujadas (reactivo)")
.interactive()
)
return chart_bars
# Construimos el gráfico en una variable y la devolvemos como última expresión
_chart_c6_output = _build_chart_c6(df_drawn, mode_selector.value)
_chart_c6_output
return
@app.cell
def _(df_drawn, mode_selector, scatter_widget):
# También encapsulado para no chocar con nombres de otras celdas.
def _prepare_ml_or_matrix_c7(df, mode):
import pandas as pd
import marimo as mo
if df is None or len(df) == 0:
return mo.md("No hay datos para preparar. Dibuja primero en el lienzo.")
if mode == "Scatter":
xy = getattr(scatter_widget, "data_as_X_y", None)
if (xy is None) or (xy[0] is None) or (xy[1] is None):
return mo.md("Añade puntos en el scatter para obtener X, y.")
X, y = xy
n_feats = X.shape[1] if hasattr(X, "shape") and len(X.shape) > 1 else 1
if n_feats == 1:
x_cols = ["x"]
elif n_feats == 2:
x_cols = ["x", "y"]
else:
x_cols = [f"x{i+1}" for i in range(n_feats)]
preview = pd.DataFrame(X, columns=x_cols)
preview["target"] = y
return mo.vstack([
mo.md(f"Listo para ML — **X**: {getattr(X, 'shape', '')}, **y**: {getattr(y, 'shape', '')}"),
mo.ui.dataframe(preview.head(20))
])
else:
# Barras → matriz (filas=bins, columnas=series) o columna única si no hay series
orig_cols = list(df.columns)
lower_map = {str(c).lower(): c for c in orig_cols}
bin_col = lower_map.get("bin") or lower_map.get("x") or orig_cols[0]
val_col = lower_map.get("value") or lower_map.get("y") or (orig_cols[-1] if len(orig_cols) > 1 else orig_cols[0])
serie_col = (
lower_map.get("collection")
or lower_map.get("serie")
or lower_map.get("series")
or lower_map.get("group")
or None
)
if serie_col:
mat_df = df.pivot_table(index=bin_col, columns=serie_col, values=val_col, fill_value=0, aggfunc="sum").sort_index()
else:
mat_df = df[[bin_col, val_col]].set_index(bin_col).sort_index()
return mo.vstack([
mo.md(f"Matriz lista para modelar/featurear — forma: **{mat_df.shape}**"),
mo.ui.dataframe(mat_df.reset_index().head(50))
])
_ml_matrix_c7_output = _prepare_ml_or_matrix_c7(df_drawn, mode_selector.value)
_ml_matrix_c7_output
return
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
+66
View File
@@ -0,0 +1,66 @@
{
"type": "grid",
"data": {
"columns": 24,
"rowHeight": 20,
"maxWidth": 1400,
"bordered": true,
"cells": [
{
"position": null
},
{
"position": null
},
{
"position": null
},
{
"position": [
0,
0,
24,
9
]
},
{
"position": null
},
{
"position": [
0,
9,
12,
28
]
},
{
"position": [
12,
9,
12,
28
]
},
{
"position": null
},
{
"position": [
0,
37,
12,
28
]
},
{
"position": [
12,
37,
12,
28
]
}
]
}
}
+150
View File
@@ -0,0 +1,150 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return
@app.cell
def _():
import sys
import subprocess
from pathlib import Path
from importlib import import_module
from typing import Dict, Any, Iterable, Tuple, Optional
import shutil
def _prefer_local_path(pkg_name: str, repo_path: Path) -> Optional[str]:
"""Insert repo/src or repo into sys.path (front) if it contains the package folder."""
for cand in (repo_path / "src", repo_path):
if (cand / pkg_name).exists():
sys.path.insert(0, str(cand))
return str(cand)
return None
def _purge_loaded(pkg_name: str) -> None:
"""Remove any already-loaded modules for this package."""
for k in list(sys.modules):
if k == pkg_name or k.startswith(pkg_name + "."):
del sys.modules[k]
def import_local_libs(
libs: Dict[str, str],
install_with_uv: bool = False,
uv_extra_args: Iterable[str] = (),
) -> Dict[str, Any]:
"""
Force-import local libraries from given repos.
- libs: dict { 'package_name': '/abs/path/to/repo' }
- install_with_uv: if True, runs `uv pip install -e <repo>`
(not required for local dev if sys.path injection is enough)
- uv_extra_args: extra args for uv, e.g. ['--constraint', 'constraints.txt']
Returns: dict { 'package_name': imported_module }
"""
imported: Dict[str, Any] = {}
uv_bin = shutil.which("uv")
for name, repo in libs.items():
repo_path = Path(repo).resolve()
if not repo_path.exists():
raise FileNotFoundError(f"[import_local] Repo not found: {repo_path}")
_purge_loaded(name)
added = _prefer_local_path(name, repo_path)
if not added:
raise ModuleNotFoundError(
f"[import_local] Could not find package folder '{name}' in "
f"'{repo_path}/src' or '{repo_path}'. Check your repo layout."
)
if install_with_uv:
if not uv_bin:
raise RuntimeError("uv not found in PATH, but install_with_uv=True.")
# Install editable using uv (no pip required).
# This installs into uv's target environment. For pure local dev,
# you can keep install_with_uv=False and rely on sys.path injection.
subprocess.run(
[uv_bin, "pip", "install", "-e", str(repo_path), *uv_extra_args],
check=True,
)
# Import the top-level package to ensure it resolves from the local path
imported[name] = import_module(name)
return imported
return (import_local_libs,)
@app.cell
def _(import_local_libs):
LIBS = {
"moutils": "/home/lucas/DataProyects/git_proyects/moutils"
}
import_local_libs(LIBS)
# Ahora ya puedes importar normal
from moutils import shell, ShellWidget
return (ShellWidget,)
@app.cell
def _():
bash_code = """#!/bin/bash
# Espera input del usuario
read -p "Write the host to ping" destino
# Ejecuta ping indefinidamente
ping "$destino"
"""
return (bash_code,)
@app.cell
def _(bash_code):
import os
# Crear la carpeta scripts si no existe
os.makedirs("scripts", exist_ok=True)
# Ruta del archivo .sh dentro de scripts
script_path = os.path.join("scripts", "mi_script.sh")
# Guardar el contenido en el archivo
with open(script_path, "w") as f:
f.write(bash_code)
# Dar permisos de ejecución
os.chmod(script_path, 0o755)
print(f"Script guardado en {script_path} y listo para ejecutarse.")
return
@app.cell
def _(ShellWidget):
console = ShellWidget(command="bash -x ./scripts/mi_script.sh", run=True)
console
return
@app.cell
def _():
# console.kill()
return
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
+119
View File
@@ -0,0 +1,119 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
from moutils import shell
return mo, shell
@app.cell
def _(shell):
shell(command="ls")
return
@app.cell
def _(shell):
shell_widget = shell("""
sleep 5 && read -p "Ingresa algo: " variable
""")
shell_widget
return (shell_widget,)
@app.cell
def _(shell_widget):
shell_widget.close()
return
@app.cell
def _():
print("hola")
return
@app.cell
def _(mo):
cmd_input = mo.ui.text(value="echo 'Hola desde ShellWidget!'", label="Comando")
workdir_input = mo.ui.text(value=".", label="Directorio de trabajo (opcional)")
run_btn = mo.ui.run_button(label="Ejecutar comando")
mo.vstack([cmd_input, workdir_input, run_btn])
return cmd_input, run_btn, workdir_input
@app.cell
def _(cmd_input, mo, run_btn, shell, workdir_input):
# Importante: accede a .value en otra celda
if not run_btn.value:
mo.md("Pulsa **Ejecutar comando**")
else:
# Instanciar el widget Shell cuando se pulse el botón
# Shell ejecuta y streamea la salida en la celda
shell(cmd_input.value, working_directory=workdir_input.value)
return
@app.cell
def _(mo):
# import marimo as mo
# from moutils import shell
cmd = mo.ui.text("date", label="Comando (auto)")
cmd
return (cmd,)
@app.cell
def _(cmd, shell):
# Cada cambio en cmd.value re-ejecuta esta celda
shell(cmd.value)
return
@app.cell
def _():
import asyncio
from moutils import ShellWidget
return ShellWidget, asyncio
@app.cell
def _(ShellWidget, asyncio):
def autorun_shell(command: str, working_directory: str = ".") -> ShellWidget:
w = ShellWidget(command, working_directory)
# Programar la ejecución inmediatamente, respetando el event loop actual
try:
loop = asyncio.get_event_loop()
if loop.is_running():
loop.create_task(w._execute_command_async())
else:
loop.run_until_complete(w._execute_command_async())
except RuntimeError:
asyncio.run(w._execute_command_async())
return w
return (autorun_shell,)
@app.cell
def _(autorun_shell):
autorun_shell("date", working_directory=".")
return
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
+479
View File
@@ -0,0 +1,479 @@
import marimo
__generated_with = "0.15.2"
app = marimo.App(width="medium")
@app.cell
def _():
# marimo
import marimo as mo
# Data & math
import pandas as pd
import numpy as np
# Embeddings
from sentence_transformers import SentenceTransformer
# Dimensionality reduction & metrics
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics import pairwise_distances
from sklearn.neighbors import NearestNeighbors
# Viz
import altair as alt
# Misc
from functools import lru_cache
import io
import json
# --- Notes (English): ---
# - All comments are in English as requested.
# - We avoid variable re-declarations across cells (Marimo DAG rule).
# - The last expression in each cell is displayed automatically.
return (
NearestNeighbors,
PCA,
SentenceTransformer,
TSNE,
alt,
io,
lru_cache,
mo,
np,
pairwise_distances,
pd,
)
@app.cell
def _(mo):
# UI elements (defined here; values used in later cells)
sample_toggle = mo.ui.checkbox(label="Use sample dataset (tiny demo)", value=True)
text_area = mo.ui.text_area(
value="Label,Text\ntech,Transformers accelerate NLP research.\ntech,Embeddings capture semantic meaning.\n"
"finance,Markets react to macroeconomic signals.\nfinance,Portfolio optimization reduces risk.\n"
"sports,The team improved defense and strategy.\nsports,Training intensity boosts performance.\n",
label="CSV data (columns: Label,Text)", full_width=True
)
model_a_dropdown = mo.ui.dropdown(
options=[
"sentence-transformers/all-MiniLM-L6-v2",
"thenlper/gte-small",
"BAAI/bge-small-en-v1.5",
"sentence-transformers/paraphrase-MiniLM-L6-v2",
],
value="sentence-transformers/all-MiniLM-L6-v2",
label="Model A"
)
model_b_dropdown = mo.ui.dropdown(
options=[
"sentence-transformers/all-MiniLM-L6-v2",
"thenlper/gte-small",
"BAAI/bge-small-en-v1.5",
"sentence-transformers/paraphrase-MiniLM-L6-v2",
],
value="BAAI/bge-small-en-v1.5",
label="Model B"
)
reducer_dropdown = mo.ui.radio(
options=["PCA", "TSNE"],
value="PCA",
label="Dimensionality reduction"
)
k_slider = mo.ui.slider(3, 20, value=10, label="k for neighborhood agreement")
mo.vstack([
mo.md("### Data & Models"),
sample_toggle,
text_area,
mo.hstack([model_a_dropdown, model_b_dropdown]),
mo.hstack([reducer_dropdown, k_slider]),
])
return (
k_slider,
model_a_dropdown,
model_b_dropdown,
reducer_dropdown,
text_area,
)
@app.cell
def _(io, pd, text_area):
# Build dataframe from UI state.
# If sample_toggle is True, parse the sample CSV from text_area; otherwise expect user-provided CSV.
def _parse_csv_to_df(csv_text: str) -> pd.DataFrame:
# Parse CSV robustly
df = pd.read_csv(io.StringIO(csv_text))
# Basic schema validation
expected_cols = {"Label", "Text"}
if not expected_cols.issubset(set(df.columns)):
raise ValueError("CSV must contain columns: Label, Text")
# Ensure string types
df["Label"] = df["Label"].astype(str)
df["Text"] = df["Text"].astype(str)
return df
dataframe_input = _parse_csv_to_df(text_area.value)
dataframe_input
return (dataframe_input,)
@app.cell
def _(SentenceTransformer, lru_cache, mo, model_a_dropdown, model_b_dropdown):
# Cache model loading to avoid repeated downloads.
@lru_cache(maxsize=4)
def load_model_cached(model_name: str) -> SentenceTransformer:
# English: caches SentenceTransformer for responsiveness
return SentenceTransformer(model_name)
selected_model_a = model_a_dropdown.value
selected_model_b = model_b_dropdown.value
mo.hstack([
mo.md(f"**Model A:** `{selected_model_a}`"),
mo.md(f"**Model B:** `{selected_model_b}`"),
])
return load_model_cached, selected_model_a, selected_model_b
@app.cell
def _(
dataframe_input,
load_model_cached,
np,
selected_model_a,
selected_model_b,
):
# Generate embeddings for both models on the same text order.
texts_for_embedding = dataframe_input["Text"].tolist()
def _embed_texts(model_name: str, texts: list[str]) -> np.ndarray:
model = load_model_cached(model_name)
return model.encode(texts, show_progress_bar=False, normalize_embeddings=True)
embeddings_a = _embed_texts(selected_model_a, texts_for_embedding)
embeddings_b = _embed_texts(selected_model_b, texts_for_embedding)
# Show shapes as a quick check
{"A_shape": embeddings_a.shape, "B_shape": embeddings_b.shape}
return embeddings_a, embeddings_b
@app.cell
def _(
PCA,
TSNE,
dataframe_input,
embeddings_a,
embeddings_b,
np,
pd,
reducer_dropdown,
):
# Reduce to 2D using PCA or TSNE.
reducer_choice = reducer_dropdown.value
def _reduce_2d(X: np.ndarray, method: str) -> np.ndarray:
if method == "PCA":
reducer = PCA(n_components=2, random_state=42)
return reducer.fit_transform(X)
elif method == "TSNE":
# TSNE default perplexity 30 works fine for small demos; set random_state for reproducibility.
reducer = TSNE(n_components=2, random_state=42, init="pca", learning_rate="auto")
return reducer.fit_transform(X)
else:
raise ValueError("Unknown reducer")
coords_a_2d = _reduce_2d(embeddings_a, reducer_choice)
coords_b_2d = _reduce_2d(embeddings_b, reducer_choice)
# Pack into tidy DataFrames for plotting
plot_df_a = pd.DataFrame({
"x": coords_a_2d[:, 0],
"y": coords_a_2d[:, 1],
"Label": dataframe_input["Label"],
"Text": dataframe_input["Text"],
"Model": "A"
})
plot_df_b = pd.DataFrame({
"x": coords_b_2d[:, 0],
"y": coords_b_2d[:, 1],
"Label": dataframe_input["Label"],
"Text": dataframe_input["Text"],
"Model": "B"
})
plot_df_combined = pd.concat([plot_df_a, plot_df_b], ignore_index=True)
plot_df_combined
return (
coords_a_2d,
coords_b_2d,
plot_df_a,
plot_df_b,
plot_df_combined,
reducer_choice,
)
@app.cell
def _(alt, plot_df_combined, reducer_choice):
# Build linked charts: one for Model A and one for Model B.
# English: We use consistent color mapping by Label across both charts.
# Calculate consistent color domain
label_domain = sorted(plot_df_combined["Label"].unique().tolist())
base = alt.Chart(plot_df_combined).mark_circle(size=80).encode(
x=alt.X("x:Q", title=f"{reducer_choice} 1"),
y=alt.Y("y:Q", title=f"{reducer_choice} 2"),
color=alt.Color("Label:N", legend=alt.Legend(title="Label"), scale=alt.Scale(scheme="category10", domain=label_domain)),
tooltip=["Model:N", "Label:N", "Text:N"]
).properties(width=350, height=300)
chart_a = base.transform_filter(alt.datum.Model == "A").properties(title="Model A")
chart_b = base.transform_filter(alt.datum.Model == "B").properties(title="Model B")
alt.hconcat(chart_a, chart_b).resolve_scale(color="shared")
return (label_domain,)
@app.cell
def _(NearestNeighbors, coords_a_2d, coords_b_2d, k_slider, mo, np, pd):
# Compute neighborhood agreement: for each point, overlap between k-NN in A vs B (after 2D reduction).
# English: Robust version that clips k to the valid range based on dataset size.
# from sklearn.neighbors import NearestNeighbors
# Dataset size
n_points = int(coords_a_2d.shape[0])
# Guard: need at least 3 points to compute non-trivial neighbors (exclude self)
mo.stop(n_points < 3, mo.md("Need at least **3** rows to compute neighborhood overlap."))
# Clip k so that kneighbors sees n_neighbors <= n_samples - 1 (exclude self)
requested_k = int(k_slider.value)
max_valid_k = max(1, n_points - 2) # ensures (k+1) <= (n_points - 1)
effective_k = min(requested_k, max_valid_k)
# Informative message if clipping happened
k_info = mo.md(
f"Using **k = {effective_k}** (requested {requested_k}, max valid {max_valid_k} for n={n_points})."
) if effective_k != requested_k else mo.md(f"Using **k = {effective_k}** for n={n_points}.")
def _knn_indices(X2d: np.ndarray, k: int) -> np.ndarray:
# English: use k+1 to include self in the neighbor list, then drop self (index 0)
# n_neighbors must be strictly less than n_samples, so pass (k+1) <= (n_points - 1)
nbrs = NearestNeighbors(n_neighbors=k + 1, metric="euclidean")
nbrs.fit(X2d)
indices = nbrs.kneighbors(return_distance=False)
return indices[:, 1:] # drop self
knn_a = _knn_indices(coords_a_2d, k=effective_k)
knn_b = _knn_indices(coords_b_2d, k=effective_k)
def _rowwise_overlap(idx_a: np.ndarray, idx_b: np.ndarray) -> np.ndarray:
# English: compute per-point overlap fraction between two k-NN index sets
overlaps = []
for a, b in zip(idx_a, idx_b):
inter = len(set(a.tolist()).intersection(set(b.tolist())))
overlaps.append(inter / len(a))
return np.array(overlaps, dtype=float)
overlap_scores = _rowwise_overlap(knn_a, knn_b)
neighborhood_agreement_mean = float(np.mean(overlap_scores))
# Display small summary table (head) + info about effective k
mo.vstack([
k_info,
mo.ui.dataframe(
pd.DataFrame({"Point": np.arange(len(overlap_scores)),
f"Overlap@{effective_k}": overlap_scores})
),
mo.md(f"**Mean Overlap@{effective_k}:** {neighborhood_agreement_mean:.3f}")
])
return neighborhood_agreement_mean, overlap_scores
@app.cell
def _(coords_a_2d, coords_b_2d, dataframe_input, np, pd):
# For each label, compute 2D centroids in A and B, then report distance between centroids.
labels_series = dataframe_input["Label"]
unique_labels = sorted(labels_series.unique().tolist())
def _centroids(coords: np.ndarray, labels: pd.Series) -> dict[str, np.ndarray]:
out = {}
for lab in unique_labels:
pts = coords[labels.values == lab]
out[lab] = np.mean(pts, axis=0)
return out
centroids_a = _centroids(coords_a_2d, labels_series)
centroids_b = _centroids(coords_b_2d, labels_series)
centroid_rows = []
for lab in unique_labels:
ca = centroids_a[lab]
cb = centroids_b[lab]
dist = float(np.linalg.norm(ca - cb))
centroid_rows.append({"Label": lab, "CentroidShift(A_vs_B)": dist})
centroid_shift_df = pd.DataFrame(centroid_rows).sort_values("CentroidShift(A_vs_B)", ascending=False)
centroid_shift_df
return centroid_shift_df, labels_series
@app.cell
def _(embeddings_a, embeddings_b, np, pairwise_distances, pd):
# Compute pairwise distance matrices in the original embedding spaces (A vs B), compare rank correlation (Spearman).
# English: measures whether global structure is preserved similarly by the two models.
from scipy.stats import spearmanr
# Use cosine distances on normalized embeddings (already normalized earlier).
dist_a = pairwise_distances(embeddings_a, metric="cosine")
dist_b = pairwise_distances(embeddings_b, metric="cosine")
# Vectorize upper triangles (avoid diagonal)
triu_idx = np.triu_indices(dist_a.shape[0], k=1)
vec_a = dist_a[triu_idx]
vec_b = dist_b[triu_idx]
rho, p_val = spearmanr(vec_a, vec_b)
pd.DataFrame({"Spearman_rho":[float(rho)], "p_value":[float(p_val)]})
return p_val, rho
@app.cell
def _(
NearestNeighbors,
coords_a_2d,
coords_b_2d,
dataframe_input,
labels_series,
np,
pd,
):
def _label_density(coords: np.ndarray, labels: pd.Series, k: int = 5) -> pd.DataFrame:
# English: need at least 3 points; with n=2, dropping self leaves 0 neighbors
n_points = int(coords.shape[0])
if n_points < 3:
# Return minimal summary with NaNs to signal insufficient neighbors
return pd.DataFrame({"Label": labels.values, "LocalDensity": np.nan}) \
.groupby("Label", as_index=False) \
.agg(Size=("Label","count"), MeanLocalDensity=("LocalDensity","mean"))
# English: choose a valid n_neighbors for sklearn kneighbors
# We query (k_eff + 1) neighbors to include self, then drop self.
# Constraint: n_neighbors <= n_points - 1 to satisfy sklearn's strict inequality.
n_neighbors = min(k + 1, n_points - 1)
k_eff = max(1, n_neighbors - 1) # English: effective neighbors after dropping self, at least 1
nbrs = NearestNeighbors(n_neighbors=n_neighbors, metric="euclidean").fit(coords)
dists, _ = nbrs.kneighbors(return_distance=True)
# English: drop self at index 0 and average first k_eff distances
# If n_neighbors might be > k_eff+1 due to clipping, we still take only k_eff after removing self.
local_density = dists[:, 1:1 + k_eff].mean(axis=1)
df = pd.DataFrame({"Label": labels.values, "LocalDensity": local_density})
out = df.groupby("Label", as_index=False).agg(
Size=("Label","count"),
MeanLocalDensity=("LocalDensity","mean")
)
return out
# English: choose k safely based on dataset size (consistent with above guard)
k_density = min(5, max(1, len(dataframe_input) - 2))
density_a = _label_density(coords_a_2d, labels_series, k=k_density)
density_b = _label_density(coords_b_2d, labels_series, k=k_density)
summary_labels = density_a.merge(density_b, on="Label", suffixes=("_A", "_B"))
summary_labels
return (summary_labels,)
@app.cell
def _(
alt,
centroid_shift_df,
k_slider,
label_domain,
mo,
neighborhood_agreement_mean,
np,
overlap_scores,
p_val,
pd,
plot_df_a,
plot_df_b,
plot_df_combined,
rho,
summary_labels,
):
# UI panel summarizing metrics and allowing CSV export of 2D coords.
export_button = mo.ui.button("Export 2D coordinates (CSV)")
summary_md = mo.md(
f"""
### Summary
- Neighborhood agreement (mean Overlap@{k_slider.value}): **{neighborhood_agreement_mean:.3f}**
- Centroid shift (A vs B): min={centroid_shift_df['CentroidShift(A_vs_B)'].min():.3f}, max={centroid_shift_df['CentroidShift(A_vs_B)'].max():.3f}
- Global structure similarity (Spearman on pairwise distances): **rho={float(rho):.3f}** (p={float(p_val):.2e})
"""
)
# Prepare exported CSV
export_df = plot_df_combined.copy()
export_payload = export_df.to_csv(index=False)
mo.vstack([
summary_md,
mo.ui.tabs({
"Scatter A/B": mo.ui.altair_chart(alt.hconcat(
alt.Chart(plot_df_a).mark_circle(size=80).encode(
x="x:Q", y="y:Q", color=alt.Color("Label:N", scale=alt.Scale(domain=label_domain)),
tooltip=["Label:N","Text:N"]
).properties(title="A", width=360, height=320),
alt.Chart(plot_df_b).mark_circle(size=80).encode(
x="x:Q", y="y:Q", color=alt.Color("Label:N", scale=alt.Scale(domain=label_domain)),
tooltip=["Label:N","Text:N"]
).properties(title="B", width=360, height=320)
)),
"Overlap@k (head)": mo.ui.table(
pd.DataFrame({"Point": np.arange(len(overlap_scores)),
f"Overlap@{k_slider.value}": overlap_scores})
),
"Centroid shift": mo.ui.table(centroid_shift_df),
"Label summary": mo.ui.table(summary_labels),
}),
export_button
])
return
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
+148
View File
@@ -0,0 +1,148 @@
import marimo
__generated_with = "0.15.1"
app = marimo.App(width="medium")
@app.cell
def _():
# Cell 1 — Imports y utilidades
import marimo as mo
import os
import pandas as pd
import numpy as np
import altair as alt
import pygwalker as pyg
from typing import Dict
try:
from vega_datasets import data as vega_data
except Exception:
vega_data = None
DATASET_URLS: Dict[str, str] = {
"Bike Sharing (DC)": "https://kanaries-app.s3.ap-northeast-1.amazonaws.com/public-datasets/bike_sharing_dc.csv",
}
return DATASET_URLS, mo, pd, pyg, vega_data
@app.cell
def _(mo):
options = [
"cars",
"iris",
"seattle-weather",
"stocks",
"Bike Sharing (DC)",
]
dataset_selector = mo.ui.dropdown(options=options, value="cars", label="Dataset")
sample_toggle = mo.ui.checkbox(label="Muestrear filas", value=False)
sample_size = mo.ui.number(value=1000, label="Filas a tomar")
dark_mode = mo.ui.radio(options=["media", "light", "dark"], value="media", label="Tema")
spec_path_input = mo.ui.text(value="", label="Ruta de spec (opcional)", full_width=True)
mo.vstack([
mo.md("## 1) Selecciona un dataset y preferencias"),
mo.hstack([dataset_selector, sample_toggle, sample_size]),
mo.hstack([dark_mode]),
spec_path_input,
])
return (
dark_mode,
dataset_selector,
sample_size,
sample_toggle,
spec_path_input,
)
@app.cell
def _(
dark_mode,
dataset_selector,
sample_size,
sample_toggle,
spec_path_input,
):
selected_name = dataset_selector.value
use_sample = sample_toggle.value
sample_n = sample_size.value
selected_theme = dark_mode.value
spec_path_val = spec_path_input.value.strip()
return sample_n, selected_name, selected_theme, spec_path_val, use_sample
@app.cell
def _(
DATASET_URLS: "Dict[str, str]",
pd,
sample_n,
selected_name,
use_sample,
vega_data,
):
def _load_dataset(name: str) -> pd.DataFrame:
if name == "Bike Sharing (DC)":
return pd.read_csv(DATASET_URLS["Bike Sharing (DC)"], parse_dates=["date"])
if name == "cars":
if vega_data is not None:
return vega_data.cars()
return pd.read_json("https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json")
if name == "iris":
if vega_data is not None:
return vega_data.iris()
return pd.read_csv("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv")
if name == "seattle-weather":
if vega_data is not None:
return vega_data.seattle_weather()
return pd.read_csv(
"https://raw.githubusercontent.com/vega/vega-datasets/master/data/seattle-weather.csv",
parse_dates=["date"],
)
if name == "stocks":
if vega_data is not None:
return vega_data.stocks()
return pd.read_csv(
"https://raw.githubusercontent.com/vega/vega-datasets/master/data/stocks.csv",
parse_dates=["date"],
)
return pd.DataFrame()
_df_raw = _load_dataset(selected_name)
if use_sample and isinstance(sample_n, (int, float)) and sample_n > 0:
_n = int(sample_n)
df = _df_raw.sample(n=min(_n, len(_df_raw)), random_state=42).reset_index(drop=True)
else:
df = _df_raw
df
return (df,)
@app.cell
def _(df, mo):
explorer = mo.ui.data_explorer(df)
explorer
return
@app.cell
def _(df, mo, pyg, selected_theme, spec_path_val):
_gw_gid = "gwalker-main"
_html = pyg.to_html(
df,
gid=_gw_gid,
dark=selected_theme,
spec=spec_path_val if spec_path_val != "" else "",
)
mo.Html(_html)
return
if __name__ == "__main__":
app.run()
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
# Espera input del usuario
read -p "Write the host to ping" destino
# Ejecuta ping indefinidamente
ping "$destino"
Generated
+3 -3
View File
@@ -202,7 +202,7 @@ wheels = [
[[package]]
name = "marimo"
version = "0.15.1"
version = "0.15.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
@@ -223,9 +223,9 @@ dependencies = [
{ name = "uvicorn" },
{ name = "websockets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/aa/6f9ddd2b12220e442e9aa34ab370e449ee4c56f610e6cad6c648075bc59b/marimo-0.15.1.tar.gz", hash = "sha256:ff46f0d61fb2132afee9f1195b81cadbfddc64707451549179c3ae52cdbf17c2", size = 31191348, upload-time = "2025-08-28T18:53:59.222Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/b8/fb55e2943bf2ac958b3c2975ed3d8022edc7763b674f65af75ded2859bd2/marimo-0.15.2.tar.gz", hash = "sha256:726933fd9c9561fa73e323b1821b173c5e0f85196ec2a497a3509cc2fc245c58", size = 31191439, upload-time = "2025-08-29T16:56:46.748Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b6/df/2fb4a9db612bbf70a3ae6a2b223ac1421de2778d4ced3edb388e39287b39/marimo-0.15.1-py3-none-any.whl", hash = "sha256:5bb75bead625c2c5153d1be70748042707469f6f8564e126d4ad4d05856529e0", size = 31427631, upload-time = "2025-08-28T18:54:03.001Z" },
{ url = "https://files.pythonhosted.org/packages/6e/e6/eb732caaea80ca1d0b3a66c862a062dcd031ed1348291b78362aec9dd1c2/marimo-0.15.2-py3-none-any.whl", hash = "sha256:630cbad0217fb6cd8f78e6775b955242c1528a673233095dec5901781588bee9", size = 31427634, upload-time = "2025-08-29T16:56:52.564Z" },
]
[[package]]