Skip to content

Instantly share code, notes, and snippets.

@mluerig
Last active July 26, 2024 00:26
Show Gist options
  • Save mluerig/efa169be9c6580538d9ad48ce7977c8c to your computer and use it in GitHub Desktop.
Save mluerig/efa169be9c6580538d9ad48ce7977c8c to your computer and use it in GitHub Desktop.
Interactive figure in Python - see https://www.luerig.net/posts/interactive-figures/
## load modules
import os
import numpy as np
import pandas as pd
from bokeh.io import save
from bokeh import plotting, models, layouts
## load data (see https://www.luerig.net/posts/interactive-figures/ for how it was collected)
os.chdir(r"D:\workspace\figures\interactive_figures")
## load data (see gist below for how it was collected)
df_data = pd.read_csv(r"data/isopods_traits.csv")
## add url filepath (you can use local files, this is just for my online demo)
url_base = "https://raw.githubusercontent.com/mluerig/website-assets-static/" + \
"main/luerig.net/posts/2024-07-24-interactive-figures/rois_isopods/"
df_data["filepath"] = f"{url_base}/isopod_" + \
df_data["contour_idx"].apply(str).str.zfill(3) + ".png"
## Specify the output file
figure_path = r"figures\figure_isopods_interactive.html"
plotting.output_file(figure_path)
## Convert to ColumnDataSource
ds_points = models.ColumnDataSource(data=dict(df_data))
## Add hover tool with tooltips
hover = models.HoverTool(
tooltips=[
("contour_idx", "@contour_idx"),
("log_length", "@log_length"),
("pigmentation", "@pigmentation"),
]
)
## JavaScript callback for displaying images on hover - it's java script that's wrapping html
## https://docs.bokeh.org/en/latest/docs/user_guide/interaction/js_callbacks.html#customjs-callbacks
div = models.Div(text="")
hover.callback = models.CustomJS(args=dict(div=div, ds=ds_points), code="""
const hit_test_result = cb_data.index;
const indices = hit_test_result.indices;
if (indices.length > 0) {
div.text = `<img
src="${ds.data['filepath'][indices[0]]}"
style="float: left; margin: 0px 15px 15px 0px; min-width: 100%;"
/>`;
}
""")
## Assemble the figure
p = plotting.figure(
tools=["pan", "reset", "wheel_zoom", "box_select", "lasso_select", "tap", hover],
active_scroll="wheel_zoom", output_backend="webgl",
width=500, height=500,
x_axis_label="Length (mm, log)", y_axis_label="Pigmentation (0-1)"
)
## Add scatter plot
p.scatter(
x='log_length', y='pigmentation',
source=ds_points,
size=10
)
## Add regression line
fit = np.polyfit(df_data["log_length"], df_data["pigmentation"], 1)
slope, intercept = fit[0], fit[1]
line_x = np.linspace(df_data["log_length"].min(), df_data["log_length"].max(), 100)
line_y = slope * line_x + intercept
p.line(line_x, line_y, line_width=2, color='red')
## Layout the figure and div side by side
layout = layouts.column(layouts.row(p, div))
## Render and save HTML
save(layout)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment