Adding notebook for Highlight_Lines_on_Grey_Background article

pull/4/head
vaclavdekanovsky 2020-09-26 01:05:23 +02:00
parent 15e4e73eb1
commit eb20c35d6e
1 changed files with 954 additions and 0 deletions

View File

@ -0,0 +1,954 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import plotly.express as px\n",
"import pandas as pd\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# load preprocessed data\n",
"We will work with a long data frame - categorical columns has one line per combination of `Country Name`, `Country Code`, `years` and `Region` and each row contain 2 values:\n",
"\n",
"* `visitors` - the number of tourist which visited this country in the year\n",
"* `receipts` - how much these tourist have spent in the country in that year\n",
"\n",
"We have values for 215 countries for years 1995-2018\n",
"\n",
"Data were preprocessed using - https://github.com/vaclavdekanovsky/data-analysis-in-examples/blob/master/Vizualizations/Plotly/Preprocess/Preprocessing.ipynb"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# First run the preprocessing notebook\n",
"# https://github.com/vaclavdekanovsky/data-analysis-in-examples/blob/master/Vizualizations/Plotly/Preprocess/Preprocessing.ipynb\n",
"# then load the preprocessed pickles\n",
"long_df = pd.read_pickle(\"../Preprocess/long.plk\")\n",
"yr2018 = long_df[long_df[\"years\"]==\"2018\"]\n",
"spfrit = long_df[long_df[\"Country Name\"].isin([\"Spain\",\"France\",\"Italy\"])]\n",
"sptuar = long_df[long_df[\"Country Name\"].isin([\"Spain\",\"Turkey\",\"Aruba\"])]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simple line chart"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# simple line chart with Plotly Express\n",
"px.line(long_df, \n",
" x=\"years\", \n",
" y=\"visitors\", \n",
" color=\"Country Name\", \n",
" title=\"Growth of tourism 1995-2018\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can apply the grey color to all traces using `fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})` and then specify the color of some traces using `.update_traces(patch={})` and `selector={\"legendgroup\":\"Country Name\"}`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(long_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\")\n",
"\n",
"# set color of all traces to lightgrey\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"# color Turkish line to blue\n",
"fig.update_traces(patch={\"line\":{\"color\":\"blue\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Turkey\"})\n",
"\n",
"# color Japanese line to red\n",
"fig.update_traces(patch={\"line\":{\"color\":\"red\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Japan\"})\n",
"\n",
"# remove the legend, y-axis and add a title\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan\",\n",
" showlegend=False,\n",
" yaxis={\"visible\":False})\n",
" \n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you wonder how I know which values to supply to the dictionary, that `{\"line\":{\"color\":\"blue\", \"width\":5}` changes the properties of the line and that `legendgroup` is the right parameter to identify the line by `Country name` the easiest way is to read `fig[\"data\"]`. Each Plotly chart is a dictionary and all parameters can be changed when you update this dictionary. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Scattergl({\n",
" 'hovertemplate': 'Country Name=Aruba<br>years=%{x}<br>visitors=%{y}<extra></extra>',\n",
" 'legendgroup': 'Aruba',\n",
" 'line': {'color': 'lightgrey', 'dash': 'solid'},\n",
" 'mode': 'lines',\n",
" 'name': 'Aruba',\n",
" 'showlegend': True,\n",
" 'x': array(['1995', '1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003',\n",
" '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012',\n",
" '2013', '2014', '2015', '2016', '2017', '2018'], dtype=object),\n",
" 'xaxis': 'x',\n",
" 'y': array([ 619000., 641000., 650000., 647000., 683000., 721000., 691000.,\n",
" 643000., 642000., 728000., 733000., 694000., 772000., 827000.,\n",
" 813000., 824000., 869000., 904000., 979000., 1072000., 1225000.,\n",
" 1102000., 1070500., 1082000.]),\n",
" 'yaxis': 'y'\n",
"})"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fig[\"data\"][0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ordering the lines\n",
"But the order of the lines is that Turkey and Japan lie somewhere in the middle, so some grey lines are below it and some are above. \n",
"\n",
"We need to sort the dataframe (or the traces), so that the highlightted bars are on the top. One option is to use `category_orders` parameter of Plotly. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(long_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\",\n",
" category_orders={\"Country Name\":[\"Japan\",\"Turkey\"]})\n",
"\n",
"# set color of all traces to lightgrey\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"# color Turkish line to blue\n",
"fig.update_traces(patch={\"line\":{\"color\":\"blue\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Turkey\"})\n",
"\n",
"# color Japanese line to red\n",
"fig.update_traces(patch={\"line\":{\"color\":\"red\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Japan\"})\n",
"\n",
"# remove the legend, y-axis and add a title\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan with category_order\",\n",
" showlegend=False,\n",
" yaxis={\"visible\":False})\n",
" \n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"But that doesn't lead to the desired output. Japan and Turkey are now on the complete bottom. Other option is to sort the data frame itself by adding a sort column, map values 1,2 to our desired countries and 3 to all the others."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Country Name</th>\n",
" <th>Country Code</th>\n",
" <th>Region</th>\n",
" <th>years</th>\n",
" <th>visitors</th>\n",
" <th>receipts</th>\n",
" <th>order</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>4945</th>\n",
" <td>Aruba</td>\n",
" <td>ABW</td>\n",
" <td>Latin America &amp; Caribbean</td>\n",
" <td>2018</td>\n",
" <td>1082000.0</td>\n",
" <td>2.024000e+09</td>\n",
" <td>3.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4946</th>\n",
" <td>Afghanistan</td>\n",
" <td>AFG</td>\n",
" <td>South Asia</td>\n",
" <td>2018</td>\n",
" <td>0.0</td>\n",
" <td>5.000000e+07</td>\n",
" <td>3.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4947</th>\n",
" <td>Angola</td>\n",
" <td>AGO</td>\n",
" <td>Sub-Saharan Africa</td>\n",
" <td>2018</td>\n",
" <td>218000.0</td>\n",
" <td>5.570000e+08</td>\n",
" <td>3.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Country Name Country Code Region years visitors \\\n",
"4945 Aruba ABW Latin America & Caribbean 2018 1082000.0 \n",
"4946 Afghanistan AFG South Asia 2018 0.0 \n",
"4947 Angola AGO Sub-Saharan Africa 2018 218000.0 \n",
"\n",
" receipts order \n",
"4945 2.024000e+09 3.0 \n",
"4946 5.000000e+07 3.0 \n",
"4947 5.570000e+08 3.0 "
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sort the dataframe\n",
"sorted_df = long_df.copy()\n",
"\n",
"# map the value order\n",
"sorted_df[\"order\"] = sorted_df[\"Country Name\"].map({\"Japan\": 1, \"Turkey\": 2}).fillna(3)\n",
"\n",
"# sort by this order\n",
"sorted_df.sort_values(by=[\"order\",\"years\"], ascending=False, inplace=True)\n",
"sorted_df.head(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(sorted_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\")\n",
"\n",
"# set color of all traces to lightgrey\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"# color Turkish line to blue\n",
"fig.update_traces(patch={\"line\":{\"color\":\"blue\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Turkey\"})\n",
"\n",
"# color Japanese line to red\n",
"fig.update_traces(patch={\"line\":{\"color\":\"red\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Japan\"})\n",
"\n",
"# remove the legend, y-axis and add a title\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan (sorted dataset)\",\n",
" showlegend=False,\n",
" yaxis={\"visible\":False})\n",
" \n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Other option how to update the colors of the chart is to provide a dictionary in the format `{trace_name: color}` as an input to `color_discrete_map`. "
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'Aruba': 'lightgrey',\n",
" 'Zimbabwe': 'lightgrey',\n",
" 'Turkey': 'red',\n",
" 'Japan': 'blue'}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sorted_df\n",
"\n",
"# crete a dict with colors:\n",
"colors = pd.DataFrame(sorted_df[\"Country Name\"].unique(), columns=[\"Country Name\"])\n",
"colors[\"color\"] = colors[\"Country Name\"].map({\"Japan\": \"blue\", \"Turkey\": \"red\"}).fillna(\"lightgrey\")\n",
"\n",
"# color map is a dict with colors, lightgrey for most, {\"Aruba\": \"lightgrey\", ... \"Japan: \"blue\", ...}\n",
"color_map = {v[\"Country Name\"]: v[\"color\"] for k,v in colors.iterrows()}\n",
"\n",
"# show sample from the dictionary\n",
"{k:color_map[k] for k in color_map if k in [\"Aruba\",\"Japan\",\"Turkey\",\"Zimbabwe\"]}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# but still my lines are somewhere in the middle\n",
"fig = px.line(sorted_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\", \n",
" line_group=\"Country Name\",\n",
" color_discrete_map=color_map)\n",
"\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan\",\n",
" # remove the legent\n",
" showlegend=False,\n",
" \n",
" # make y-axis invisible\n",
" yaxis={\"visible\":False})\n",
" \n",
"fig.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for i,d in enumerate(fig[\"data\"]):\n",
" if d[\"legendgroup\"] in [\"Japan\",\"Turkey\"]:\n",
" fig[\"data\"][i][\"line\"][\"width\"] = 5\n",
" \n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Anotations\n",
"The chart still looks quite ugly. We have removed the legend and the axis so we don't even know how many visitors came to these countries. Let's add some annotations. \n",
"\n",
"Annotations are added using `fig.update_layoute(annotations=[])` where you supply the list of dictionaries describing the position and the style of the text. You can refer the position to the plot area or to the plot image canvas when you use `xref=\"paper\"`. In such a case (x:0, y:0 is the bottom left corner of the plot area and (x:1, y:1) is the top right one). \n",
"\n",
"Let's see what you can do with plotly annotations. I think the most interesting is that you refer `x` to canvas and `y` to the coordinate on the chart. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import plotly.graph_objects as go\n",
"fig = go.Figure()\n",
"fig.update_layout(\n",
" title=\"Annotations' position\",\n",
" annotations=\\\n",
" [\n",
" {\"x\":4, \"y\":0, \"text\": \"<i>(4,0)</i>\", \"showarrow\":False},\n",
" {\"x\":4, \"y\":2, \"text\": \"(4,2)\", \"showarrow\":True, \n",
" \"font\":{\"color\":\"blue\", \"size\":15}, \"arrowcolor\":\"blue\"},\n",
" {\"x\":0, \"y\":2, \"text\": \"<b>(0,2) with ax and arrowhead</b>\", \n",
" \"showarrow\":True, \"arrowhead\": 3, \"ax\":40},\n",
" {\"xref\": \"paper\", \"x\":0, \"y\":0, \"text\": \"<b>paper (0,0)</b>\", \"showarrow\":False},\n",
" {\"xref\": \"paper\", \"xanchor\": \"right\",\"x\":0, \"y\":-.5, \n",
" \"text\": \"<b>(paper: 0,plot: -.5), mix paper <br>and plot positioning</b>\", \"showarrow\":False},\n",
" {\"xref\": \"paper\", \"yref\": \"paper\", \"xanchor\": \"left\", \"yanchor\":\"bottom\",\"x\":1, \"y\":1, \n",
" \"text\": \"<b>paper (1,1), leftbottom anchor</b>\", \"showarrow\":False},\n",
" {\"xref\": \"paper\", \"yref\": \"paper\", \"xanchor\": \"right\", \"yanchor\":\"top\",\"x\":1, \"y\":1, \n",
" \"text\": \"<b>paper (1,1), righttop anchor</b>\", \"showarrow\":False},\n",
" {\"xref\": \"paper\", \"xanchor\": \"center\", \"yanchor\":\"middle\",\"x\":1, \"y\":0, \n",
" \"text\": \"(paper: 1, plot: 0) <br> centermiddle anchor\", \"showarrow\":False},\n",
" ],\n",
" margin={\"l\":300, \"r\": 250},)\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create the list of dictionaries with the annotations at the beginning and the end of the lines."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"# annotation at the beginning of the line \n",
"# use the \"xanchor\": \"right\" so that the labels stick to the right side of the plot area\n",
"turkey_annotation = \\\n",
"[{\"xref\":\"paper\", \"yref\":\"paper\", \"x\":0, \"y\":0.15,\n",
" \"xanchor\":'right', \"yanchor\":\"top\",\n",
" \"text\":'7M',\n",
" \"font\":dict(family='Arial', size=15, color=\"red\"),\n",
" \"showarrow\":False},\n",
" # end of the line legend\n",
" # use the \"xanchor\": \"left\" so that the labels stick to the right side of the plot area\n",
" {\"xref\":\"paper\", \"yref\":\"paper\", \"x\":1, \"y\":0.53,\n",
" \"xanchor\":\"left\", \"yanchor\":\"top\",\n",
" \"text\":'Turkey (45M)',\n",
" \"font\":dict(family='Arial', size=15, color=\"red\"),\n",
" \"showarrow\":False},]\n",
"\n",
"japan_annotation = [{\"xref\":\"paper\", \"yref\":\"paper\", \"x\":0, \"y\":0.1,\n",
" \"xanchor\":'right', \"yanchor\":'top',\n",
" \"text\":'3.3M',\n",
" \"font\":dict(family='Arial',\n",
" size=15,\n",
" color=\"blue\"),\n",
" \"showarrow\":False}, \n",
" {\"xref\":\"paper\", \"yref\":\"paper\", \"x\":1, \"y\":0.39,\n",
" \"xanchor\":'left', \"yanchor\":'top',\n",
" \"text\":'Japan (31M)',\n",
" \"font\":dict(family='Arial',\n",
" size=15,\n",
" color=\"blue\"),\n",
" \"showarrow\":False}]\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(sorted_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\", \n",
" line_group=\"Country Name\",\n",
" color_discrete_map=color_map)\n",
"\n",
"for legendgroup in [\"Turkey\",\"Japan\"]:\n",
" fig.update_traces(patch={\"line\":{\"width\":5}}, selector={\"legendgroup\":legendgroup})\n",
"\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan\",\n",
" # remove the legent\n",
" showlegend=False,\n",
" \n",
" # make y-axis invisible\n",
" yaxis={\"visible\":False},\n",
" \n",
" # create the annoations\n",
" # point annotattion\n",
" annotations=turkey_annotation+japan_annotation,\n",
" \n",
" # adjust the margin, so that annotations fit into the canvas\n",
" margin={\"l\":50, \"r\": 100})\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cool. We can also refer the `y` using `(x: 0 on canvas and y: 7_000_000 on the plot)`. Also using `xnachor` and `yanchor` let us position the text quite quickly and precisely. Don't forget to style the texts with color and size. "
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"# annotation at the beginning of the line \n",
"# use the \"xanchor\": \"right\" so that the labels stick to the right side of the plot area\n",
"annotation_turkey = \\\n",
"[{\"xref\": \"paper\", \"x\":0, \"y\":7_000_000,\n",
" \"xanchor\":'right', \"yanchor\":\"middle\",\n",
" \"text\":'7M',\n",
" \"font\":dict(family='Arial', size=15, color=\"red\"),\n",
" \"showarrow\":False},\n",
" # end of the line legend\n",
" # use the \"xanchor\": \"left\" so that the labels stick to the right side of the plot area\n",
" {\"xref\":\"paper\", \"x\":1, \"y\":45_000_000,\n",
" \"xanchor\":\"left\", \"yanchor\":\"middle\",\n",
" \"text\":'Turkey (45M)',\n",
" \"font\":dict(family='Arial', size=15, color=\"red\"),\n",
" \"showarrow\":False},]\n",
"\n",
"annotation_japan = [{\"xref\": \"paper\", \"x\":0, \"y\":3_300_000,\n",
" \"xanchor\":'right', \"yanchor\":'middle',\n",
" \"text\":'3.3M',\n",
" \"font\":dict(family='Arial',\n",
" size=15,\n",
" color=\"blue\"),\n",
" \"showarrow\":False}, \n",
" {\"xref\":\"paper\", \"x\":1, \"y\":31_000_000,\n",
" \"xanchor\":'left', \"yanchor\":'middle',\n",
" \"text\":'Japan (31M)',\n",
" \"font\":dict(family='Arial',\n",
" size=15,\n",
" color=\"blue\"),\n",
" \"showarrow\":False}]\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Annotations can be used for at least 4 puposes:\n",
" \n",
"* To highlight point(s)\n",
"* To describe/highlight an area\n",
"* To label a desired point (usually outside of the chart)\n",
"* Instead of a legend\n",
"\n",
"We used a label on the begining of the lines and the legend at the end of the lines. Let's add the point highlight and area description. "
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"tourism_boom_in_japan_annotation = [{\"x\":2011, \"y\":6220000, \"ay\": -40, \n",
" \"text\": \"<b>Tourism Boom<br> in Japan 2011</b>\",\n",
" \"arrowhead\": 3, \"showarrow\":True,\n",
" \"font\": {\"size\": 15}, \"bgcolor\":None, \"textangle\": 0}]\n",
"\n",
"area_annotation = [{\"x\":2007, \"y\":40000000, \n",
" \"text\": \"<b>Number of tourist is growing</b>\",\n",
" \"textangle\": -25,\n",
" \"showarrow\":False,\n",
" \"bgcolor\":\"lightblue\",\n",
" \"font\": {\"size\": 15}}]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(sorted_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\", \n",
" line_group=\"Country Name\",\n",
" color_discrete_map=color_map)\n",
"\n",
"for legendgroup in [\"Turkey\",\"Japan\"]:\n",
" fig.update_traces(patch={\"line\":{\"width\":5}}, selector={\"legendgroup\":legendgroup})\n",
"\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan\",\n",
" # remove the legent\n",
" showlegend=False,\n",
" \n",
" # make y-axis invisible\n",
" yaxis={\"visible\":False},\n",
" \n",
" # create the annoations\n",
" # point annotattion\n",
" annotations=annotation_turkey+annotation_japan+tourism_boom_in_japan_annotation+area_annotation,\n",
" \n",
" # adjust the margin, so that annotations fit into the canvas\n",
" margin={\"l\":50, \"r\": 100})\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Buttons\n",
"Plotly contains many interactive features and button which let you control the drawn elements are one of the them. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
" \n",
"# but still my lines are somewhere in the middle\n",
"fig = px.line(sorted_df,\n",
" x=\"years\",\n",
" y=\"visitors\", \n",
" color=\"Country Name\", \n",
" line_group=\"Country Name\")\n",
"\n",
"# for the interactivity we need to know which traces belong to each country\n",
"traces = {}\n",
"for i,d in enumerate(fig.to_dict()[\"data\"]):\n",
" traces[d[\"legendgroup\"]] = i\n",
" \n",
"\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"fig.update_layout(title=\"Tourism Growth in Turkey and Japan\",\n",
" # remove the legent\n",
" showlegend=False,\n",
" \n",
" # make y-axis invisible\n",
" yaxis={\"visible\":False}, \n",
" \n",
" \n",
" # create big enough margins for our annotations\n",
" margin={\"l\":50, \"r\": 100},\n",
" \n",
"\n",
" # button\n",
" updatemenus=[\n",
" # a dropdown `direction=\"down\"`\n",
" # changing the color of the bars\n",
" dict(\n",
" buttons=list([\n",
" dict(\n",
" args=[\n",
" {\"line.color\":[\"blue\"],\"line.width\":5}, \n",
" {\"title\":\"Tourism growth in Japan\",\n",
" \"annotations\": annotation_japan}, [traces[\"Japan\"]]], \n",
" # args2 used as toggle button, applies when clicked on active button\n",
" args2=[{\"line.color\":[\"lightgrey\"],\"line.width\":2},\n",
" {\"title\":\"Tourism growth in Japan and Turkey\",\n",
" \"annotations\": None}],\n",
" label=\"Japan\",\n",
" method=\"update\",)\n",
" ,dict(\n",
" args=[{\"line.color\":[\"red\"],\"line.width\":5}, \n",
" {\"title\":\"Tourism growth in Turkey\",\n",
" \"annotations\": annotation_turkey}, [traces[\"Turkey\"]]], \n",
" args2=[{\"line.color\":[\"lightgrey\"],\"line.width\":2},\n",
" {\"title\":\"Tourism growth in Japan and Turkey\",\n",
" \"annotations\": None}],\n",
" label=\"Turkey\",\n",
" method=\"update\")\n",
" \n",
" ]),\n",
" type = \"buttons\",\n",
" direction=\"left\",\n",
" pad={\"r\": 10, \"t\": 10},\n",
" showactive=False,\n",
" x=0.5,\n",
" xanchor=\"left\",\n",
" y=1.25,\n",
" yanchor=\"top\"\n",
" )]\n",
" )\n",
"\n",
"\n",
" \n",
"\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you click on both Japan and Turkey buttons, without triggering the toggle-clean action, you will see both red and blue line. I did not achive it anyhow, even when I supplied a list of 215 style dictionaries for each country. You have to use Plotly's dashboard library with callback actions which allow almost every imaginable output."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"reset = {\"line.color\":[\"lightgrey\"],\"line.width\":2}\n",
"japan = {\"line.color\":[\"blue\"],\"line.width\":5}\n",
"turkey = {\"line.color\":[\"red\"],\"line.width\":5}\n",
"jp_list = []\n",
"tr_list = []\n",
"for i,d in enumerate(fig.to_dict()[\"data\"]):\n",
" if d[\"legendgroup\"] == \"Japan\":\n",
" jp_list.append(japan)\n",
" else: \n",
" jp_list.append(reset)\n",
" \n",
" if d[\"legendgroup\"] == \"Turkey\":\n",
" tr_list.append(turkey)\n",
" else:\n",
"\n",
" tr_list.append(reset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# IIHF standings\n",
"This type of chart is useful for displaying changes in the ranks. Let's explore the best Ice-Hockey national teams over last 6 years.\n",
"I have switched to Linux and manually copied these rankings from the wiki - https://en.wikipedia.org/wiki/IIHF_World_Ranking into ods spredsheet format. In order to read it in pandas, I have to install `pip install odfpy`"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Country</th>\n",
" <th>Year</th>\n",
" <th>Ranking</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>31</th>\n",
" <td>Czech Rep.</td>\n",
" <td>2019</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>20</th>\n",
" <td>Finland</td>\n",
" <td>2018</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Canada</td>\n",
" <td>2019</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Country Year Ranking\n",
"31 Czech Rep. 2019 5\n",
"20 Finland 2018 5\n",
"1 Canada 2019 1"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"iihf = pd.read_excel(\"IIHF_Standings.ods\", engine=\"odf\")\n",
"sweden = iihf[iihf[\"Country\"]==\"Sweden\"]\n",
"iihf.sample(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(iihf, x=\"Year\", y=\"Ranking\", color=\"Country\", \n",
" category_orders={\"Country\":[\"Sweden\"]})\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"fig.update_traces(patch={\"line\":{\"color\":\"blue\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Sweden\"})\n",
"fig.update_yaxes(autorange=\"reversed\")\n",
"fig.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(iihf, x=\"Year\", y=\"Ranking\", color=\"Country\",\n",
" category_orders={\"Country\":[\"Canada\",\"Russia\",\"Finland\",\"Czech Rep.\", \"USA\"]},\n",
" title=\"IIHF Ranking of Sweden (using update_traces)\")\n",
"# set all traces to grey\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"# set one trace to blue\n",
"fig.update_traces(patch={\"line\":{\"color\":\"yellow\", \"width\":5}, \n",
" \"text\":sweden[\"Ranking\"].values,\n",
" \"textposition\":\"top center\",\n",
" \"mode\":\"lines+text\",\n",
" \"textfont\":{\"size\":15, \"color\":\"blue\"},\n",
" \n",
" }, \n",
" selector={\"legendgroup\":\"Sweden\"})\n",
"\n",
"# reverse the axis so that 1 is on the top\n",
"fig.update_yaxes(autorange=\"reversed\")\n",
"\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"or you can do the same when you prepare the dictionary with the annotations"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"annotations = []\n",
"for index, row in sweden.reset_index().iterrows():\n",
" d = {\"x\": row[\"Year\"], \n",
" \"y\": row[\"Ranking\"], \n",
" \"yshift\": 6,\n",
" \"text\": row[\"Ranking\"],\n",
" \"xanchor\": \"center\", \n",
" \"yanchor\": \"bottom\", \n",
" \"showarrow\":False,\n",
" \"bgcolor\": \"rgba(0,0,255,.6)\",\n",
" \"font\":{\"color\":\"yellow\", \"size\": 15}}\n",
" if index == 0: d[\"xshift\"] = -10\n",
" if index == len(sweden)-1: d[\"xshift\"] = 10\n",
" \n",
" annotations.append(d)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = px.line(iihf, x=\"Year\", y=\"Ranking\", color=\"Country\",\n",
" category_orders={\"Country\":[\"Canada\",\"Russia\",\"Finland\",\"Czech Rep.\", \"USA\"]},\n",
" title=\"IIHF Ranking of Sweden (using annotations)\")\n",
"\n",
"# reverse the axis so that 1 is on the top\n",
"fig.update_yaxes(autorange=\"reversed\")\n",
"\n",
"# set all traces to grey\n",
"fig.update_traces({\"line\":{\"color\":\"lightgrey\"}})\n",
"\n",
"# set one trace to blue\n",
"fig.update_traces(patch={\"line\":{\"color\":\"blue\", \"width\":5}}, \n",
" selector={\"legendgroup\":\"Sweden\"})\n",
"\n",
"fig.update_layout(annotations=annotations)\n",
"\n",
"\n",
"\n",
"fig.show()"
]
}
],
"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.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}