I am trying to crate visualization using Geopandas and Bokeh. Data has column region, geometry and >100 columns (week1, week2, week3,..) that have numeric value of cases.
The task is for the tooltip when hovering over a region to show only the value from column currently selected by the slider.
Things I tried:
This option displays all columns in the tooltip. Too much information
data.plot_bokeh(
slider=[week1,week2,week3],
slider_name="Week:",
figsize=(1600,600))
Not sure what to put as variable next to "Cases", or how to make it dynamic based on slider.
data.plot_bokeh(
slider=[week1,week2,week3],
slider_name="Week:",
hovertool_string="""<h1>@region_name</h1>
<h3>Cases: ?? </h3>""",
figsize=(1600,600))
Bokeh has to have the current value according to slider stored somewhere, as it's correctly coloring the region based on value
CodePudding user response:
- if you inspect the code of plotly_bokeh in geoplot.py/
geoplot()
you will find in slider callback JavaScript an internal column Colormap is set to column corresponding to value in slider (code below) - you did not provide a MWE, so have used OWID COVID data and natural earth lowres polygons. (see data preparation)
- with this it is simple to define hovertool_string argument to work the way you want
solution
import warnings
# bokeh generates a lot of shapely deprecation warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore")
gdf.plot_bokeh(
slider=[c for c in gdf.columns if c[0:4] == "week"],
slider_name="week ",
colorbar_tick_format="0.0a",
colormap="Inferno",
colormap_range=[
gdf.loc[:, [c for c in gdf.columns if c[0:4] == "week"]].quantile(p).quantile(p)
for p in [0.10, 0.90]
],
hovertool_string="<h3>@name</h3><pre>@Colormap{0.0a}</pre>"
)
output
plotly_bokeh callback
# Define Callback for Slider widget:
callback = CustomJS(
args=dict(
slider_widget=slider_widget,
geo_source=geo_source,
value2name=value2name,
),
code="""
//Change selection of field for Colormapper for choropleth plot:
var slider_value = slider_widget.value;
var i;
for(i=0; i<value2name.data["Names"].length; i )
{
if (value2name.data["Values"][i] == slider_value)
{
var name = value2name.data["Names"][i];
}
}
geo_source.data["Colormap"] = geo_source.data[name];
geo_source.change.emit();
""",
)
slider_widget.js_on_change("value", callback)
data preparation
import pandas as pd
import geopandas as gpd
import pandas_bokeh
pandas_bokeh.output_notebook()
df = pd.read_csv(
"https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/owid-covid-data.csv"
).loc[
lambda df: ~df["iso_code"].str.contains("_"),
["iso_code", "date", "total_cases_per_million"],
]
df["date"] = pd.to_datetime(df["date"])
# cases for each week as columns
df2 = (
df.merge(
pd.DataFrame(
{"date": pd.date_range(df["date"].quantile(.1), df["date"].max(), freq="W-MON")}
).assign(week=lambda d: "week" d.index.astype(str).str.zfill(3)),
on="date",
how="inner",
)
.drop(columns=["date"])
.set_index(["iso_code", "week"])
.unstack("week")
.droplevel(0, 1)
.reset_index()
)
# geometry and cases as columns
gdf = (
gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
.loc[:, ["iso_a3", "name", "geometry"]]
.merge(df2, left_on="iso_a3", right_on="iso_code")
)