Visualization¶
Visualization with Altair¶
Altair is a python API for generating Vega visuazliation specifications. We demonstracte how to use this to build an interactive chart of pyhf results.
import pyhf
import json
import numpy as np
import pandas
import altair as alt
Preparing the data¶
Altair reads the data as a pandas dataframe, so we create one.
with open("data/2-bin_1-channel.json") as serialized:
spec = json.load(serialized)
workspace = pyhf.Workspace(spec)
model = workspace.model(poi_name="mu")
pars = model.config.suggested_init()
data = workspace.data(model)
muscan = np.linspace(0, 5, 31)
results = [
pyhf.infer.hypotest(mu, data, model, return_expected_set=True) for mu in muscan
]
data = np.concatenate(
[
muscan.reshape(-1, 1),
np.asarray([r[0] for r in results]).reshape(-1, 1),
np.asarray([r[1] for r in results]).reshape(-1, 5),
],
axis=1,
)
df = pandas.DataFrame(data, columns=["mu", "obs"] + [f"exp_{i}" for i in range(5)])
df.head()
| mu | obs | exp_0 | exp_1 | exp_2 | exp_3 | exp_4 | |
|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
| 1 | 0.166667 | 0.879600 | 0.690956 | 0.786411 | 0.879601 | 0.953149 | 0.990265 |
| 2 | 0.333333 | 0.761583 | 0.467167 | 0.606464 | 0.761583 | 0.899718 | 0.977348 |
| 3 | 0.500000 | 0.648555 | 0.308972 | 0.458403 | 0.648555 | 0.840153 | 0.960587 |
| 4 | 0.666667 | 0.542811 | 0.199828 | 0.339450 | 0.542811 | 0.775260 | 0.939323 |
Defining the Chart¶
We need to filled areas for the 1,2 sigma bands and two lines for the expected and observed CLs value. For interactivity we add a hovering label of the observed result
band1 = (
alt.Chart(df)
.mark_area(opacity=0.5, color="green")
.encode(x="mu", y="exp_1", y2="exp_3")
)
band2 = (
alt.Chart(df)
.mark_area(opacity=0.5, color="yellow")
.encode(x="mu", y="exp_0", y2="exp_4")
)
line1 = alt.Chart(df).mark_line(color="black").encode(x="mu", y="obs")
line2 = (
alt.Chart(df).mark_line(color="black", strokeDash=[5, 5]).encode(x="mu", y="exp_2")
)
nearest = alt.selection_single(
nearest=True, on="mouseover", fields=["mu"], empty="none"
)
point = (
alt.Chart(df)
.mark_point(color="black")
.encode(x="mu", y="obs", opacity=alt.condition(nearest, alt.value(1), alt.value(0)))
.add_selection(nearest)
)
text = line1.mark_text(align="left", dx=5, dy=-5).encode(
text=alt.condition(nearest, "obs", alt.value(" "))
)
band2 + band1 + line1 + line2 + point + text