Last active
July 26, 2024 00:26
-
-
Save mluerig/efa169be9c6580538d9ad48ce7977c8c to your computer and use it in GitHub Desktop.
Interactive figure in Python - see https://www.luerig.net/posts/interactive-figures/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 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