{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pyhf\n", "import pandas\n", "import numpy as np\n", "import altair as alt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Visualization with Altair\n", "\n", "\n", "[Altair](https://altair-viz.github.io/) is a python API for generating [Vega](https://vega.github.io/) visuazliation specifications. We demonstracte how to use this to build an interactive chart of pyhf results." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Preparing the data\n", "\n", "Altair reads the data as a pandas dataframe, so we create one." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "model = pyhf.simplemodels.uncorrelated_background([7], [20], [5])\n", "data = [25] + model.config.auxdata" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "muscan = np.linspace(0, 5, 31)\n", "results = [\n", " pyhf.infer.hypotest(mu, data, model, return_expected_set=True) for mu in muscan\n", "]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
muobsexp_0exp_1exp_2exp_3exp_4
00.0000001.0000001.0000001.0000001.0000001.0000001.000000
10.1666670.8852080.6708090.7712580.8703220.9492350.989385
20.3333330.7959860.4388380.5815160.7436960.8908810.975022
30.5000000.7264500.2799810.4285000.6234430.8256210.956105
40.6666670.6722160.1742350.3085240.5123830.7546290.931866
\n", "
" ], "text/plain": [ " mu obs exp_0 exp_1 exp_2 exp_3 exp_4\n", "0 0.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000\n", "1 0.166667 0.885208 0.670809 0.771258 0.870322 0.949235 0.989385\n", "2 0.333333 0.795986 0.438838 0.581516 0.743696 0.890881 0.975022\n", "3 0.500000 0.726450 0.279981 0.428500 0.623443 0.825621 0.956105\n", "4 0.666667 0.672216 0.174235 0.308524 0.512383 0.754629 0.931866" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data = np.concatenate(\n", " [\n", " muscan.reshape(-1, 1),\n", " np.asarray([r[0] for r in results]).reshape(-1, 1),\n", " np.asarray([r[1] for r in results]).reshape(-1, 5),\n", " ],\n", " axis=1,\n", ")\n", "df = pandas.DataFrame(data, columns=[\"mu\", \"obs\"] + [f\"exp_{i}\" for i in range(5)])\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining the Chart\n", "\n", "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" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "" ], "text/plain": [ "alt.LayerChart(...)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "band1 = (\n", " alt.Chart(df)\n", " .mark_area(opacity=0.5, color=\"green\")\n", " .encode(x=\"mu\", y=\"exp_1\", y2=\"exp_3\")\n", ")\n", "\n", "band2 = (\n", " alt.Chart(df)\n", " .mark_area(opacity=0.5, color=\"yellow\")\n", " .encode(x=\"mu\", y=\"exp_0\", y2=\"exp_4\")\n", ")\n", "\n", "line1 = alt.Chart(df).mark_line(color=\"black\").encode(x=\"mu\", y=\"obs\")\n", "\n", "line2 = (\n", " alt.Chart(df).mark_line(color=\"black\", strokeDash=[5, 5]).encode(x=\"mu\", y=\"exp_2\")\n", ")\n", "\n", "nearest = alt.selection_single(\n", " nearest=True, on=\"mouseover\", fields=[\"mu\"], empty=\"none\"\n", ")\n", "\n", "\n", "point = (\n", " alt.Chart(df)\n", " .mark_point(color=\"black\")\n", " .encode(x=\"mu\", y=\"obs\", opacity=alt.condition(nearest, alt.value(1), alt.value(0)))\n", " .add_selection(nearest)\n", ")\n", "\n", "text = line1.mark_text(align=\"left\", dx=5, dy=-5).encode(\n", " text=alt.condition(nearest, \"obs\", alt.value(\" \"))\n", ")\n", "\n", "\n", "band2 + band1 + line1 + line2 + point + text" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.5" } }, "nbformat": 4, "nbformat_minor": 4 }