{ "cells": [ { "cell_type": "markdown", "id": "5dae00ab", "metadata": {}, "source": [ "# Pie chart on map" ] }, { "cell_type": "markdown", "id": "eaacf775", "metadata": {}, "source": [ "Let's draw pie chart on map.\n", "\n", "Take as an example the results of the Montenegrin independence referendum in 2006.\n", "The dataset was downloaded from Wikipedia article\n", "[\"2006 Montenegrin independence referendum\"](https://en.wikipedia.org/wiki/2006_Montenegrin_independence_referendum)." ] }, { "cell_type": "code", "execution_count": 1, "id": "ade29f8d", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:41.104195Z", "iopub.status.busy": "2024-04-17T07:26:41.104104Z", "iopub.status.idle": "2024-04-17T07:26:41.421696Z", "shell.execute_reply": "2024-04-17T07:26:41.421320Z" } }, "outputs": [], "source": [ "import pandas as pd\n", "\n", "from lets_plot import *\n", "from lets_plot.mapping import *" ] }, { "cell_type": "code", "execution_count": 2, "id": "ec0a99bf", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:41.423270Z", "iopub.status.busy": "2024-04-17T07:26:41.423122Z", "iopub.status.idle": "2024-04-17T07:26:41.425049Z", "shell.execute_reply": "2024-04-17T07:26:41.424858Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "LetsPlot.setup_html() " ] }, { "cell_type": "code", "execution_count": 3, "id": "18e79c96", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:41.426215Z", "iopub.status.busy": "2024-04-17T07:26:41.426065Z", "iopub.status.idle": "2024-04-17T07:26:41.835326Z", "shell.execute_reply": "2024-04-17T07:26:41.835008Z" } }, "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", " \n", " \n", " \n", " \n", " \n", " \n", "
MunicipalityYesYes%NoNo%RegisteredVotedVoted%
0Andrijevica108427.60282471.894369392889.91
1Bar1664063.07949635.99322552638281.79
2Berane1126846.851261852.46283422405184.86
3Bijelo Polje1940555.361543744.04401103505187.39
4Budva590852.75518046.25127971120087.52
\n", "
" ], "text/plain": [ " Municipality Yes Yes% No No% Registered Voted Voted%\n", "0 Andrijevica 1084 27.60 2824 71.89 4369 3928 89.91\n", "1 Bar 16640 63.07 9496 35.99 32255 26382 81.79\n", "2 Berane 11268 46.85 12618 52.46 28342 24051 84.86\n", "3 Bijelo Polje 19405 55.36 15437 44.04 40110 35051 87.39\n", "4 Budva 5908 52.75 5180 46.25 12797 11200 87.52" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "referendum_df = pd.read_csv(\"https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/montenegrin_referendum_2006.csv\")\n", "referendum_df.head()" ] }, { "cell_type": "markdown", "id": "f467b3e6", "metadata": {}, "source": [ "Let's find geographical boundaries of these states using `Lets-Plot` geocoding module. And draw the percentage of those who voted \"for\" on the map by regions." ] }, { "cell_type": "code", "execution_count": 4, "id": "3db40354", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:41.836733Z", "iopub.status.busy": "2024-04-17T07:26:41.836369Z", "iopub.status.idle": "2024-04-17T07:26:42.568409Z", "shell.execute_reply": "2024-04-17T07:26:42.567922Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The geodata is provided by © OpenStreetMap contributors and is made available here under the Open Database License (ODbL).\n" ] } ], "source": [ "from lets_plot.geo_data import *\n", "\n", "country = 'Montenegro'\n", "municipalities = geocode_states(names=referendum_df['Municipality']).scope(country)\n", "\n", "boundaries = municipalities.get_boundaries(resolution=15)" ] }, { "cell_type": "code", "execution_count": 5, "id": "0180ef88", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:42.569936Z", "iopub.status.busy": "2024-04-17T07:26:42.569784Z", "iopub.status.idle": "2024-04-17T07:26:42.588187Z", "shell.execute_reply": "2024-04-17T07:26:42.587999Z" } }, "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", "
statefound namegeometryMunicipalityYes%
0AndrijevicaAndrijevica MunicipalityMULTIPOLYGON (((19.61899 42.80458, 19.63123 42...Andrijevica27.60
1BarBar MunicipalityMULTIPOLYGON (((18.94186 42.29637, 18.94278 42...Bar63.07
2BeraneBerane MunicipalityMULTIPOLYGON (((20.02373 42.76312, 20.02423 42...Berane46.85
3Bijelo PoljeBijelo Polje MunicipalityMULTIPOLYGON (((19.41060 43.07993, 19.42125 43...Bijelo Polje55.36
4BudvaBudva MunicipalityMULTIPOLYGON (((18.79964 42.27958, 18.79945 42...Budva52.75
\n", "
" ], "text/plain": [ " state found name \\\n", "0 Andrijevica Andrijevica Municipality \n", "1 Bar Bar Municipality \n", "2 Berane Berane Municipality \n", "3 Bijelo Polje Bijelo Polje Municipality \n", "4 Budva Budva Municipality \n", "\n", " geometry Municipality Yes% \n", "0 MULTIPOLYGON (((19.61899 42.80458, 19.63123 42... Andrijevica 27.60 \n", "1 MULTIPOLYGON (((18.94186 42.29637, 18.94278 42... Bar 63.07 \n", "2 MULTIPOLYGON (((20.02373 42.76312, 20.02423 42... Berane 46.85 \n", "3 MULTIPOLYGON (((19.41060 43.07993, 19.42125 43... Bijelo Polje 55.36 \n", "4 MULTIPOLYGON (((18.79964 42.27958, 18.79945 42... Budva 52.75 " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "boundaries = pd.merge(boundaries, referendum_df[['Municipality','Yes%']], left_on='state', right_on='Municipality')\n", "boundaries.head()" ] }, { "cell_type": "code", "execution_count": 6, "id": "1e771801", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:42.589383Z", "iopub.status.busy": "2024-04-17T07:26:42.589249Z", "iopub.status.idle": "2024-04-17T07:26:42.642832Z", "shell.execute_reply": "2024-04-17T07:26:42.642563Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plot_title = ggtitle(\"Results of the Montenegrin independence referendum, 2006\")\n", "\n", "fill_colors = ['#a50026', '#d73027', '#66bd63', '#4daf4a','#006837']\n", " \n", "map_layer = geom_map(aes(fill=\"Yes%\"), \n", " data=boundaries,\n", " tooltips=layer_tooltips()\n", " .title('@state')\n", " .line('\\'Yes\\' votes|@{Yes%}')\n", " .format('Yes%', '{} %')) + \\\n", " scale_fill_gradientn(fill_colors)\n", "\n", "ggplot() + map_layer + plot_title + ggsize(800, 700) + theme_void()" ] }, { "cell_type": "markdown", "id": "15d4d3c4", "metadata": {}, "source": [ "Let's prepare the data for visualization and add more information.\n", "The table will contain the following columns:\n", "- 'Municipality' - the name of the municipality\n", "- 'Registered' - registered voters\n", "- 'Vote' - choice:\n", " - \"Yes\" - for the independence option\n", " - \"No\" - against independence\n", " - \"Blank\" - invalid or blank votes\n", "- 'Number' - number of 'Vote'" ] }, { "cell_type": "code", "execution_count": 7, "id": "a474a3b2", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:42.644525Z", "iopub.status.busy": "2024-04-17T07:26:42.644333Z", "iopub.status.idle": "2024-04-17T07:26:42.649500Z", "shell.execute_reply": "2024-04-17T07:26:42.649275Z" } }, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
MunicipalityRegisteredVoteNumber
0Andrijevica4369No2824
1Bar32255No9496
2Berane28342No12618
3Bijelo Polje40110No15437
4Budva12797No5180
...............
58Rožaje19646Blank90
59Šavnik2306Blank20
60Tivat10776Blank91
61Ulcinj17117Blank137
62Žabljak3407Blank24
\n", "

63 rows × 4 columns

\n", "
" ], "text/plain": [ " Municipality Registered Vote Number\n", "0 Andrijevica 4369 No 2824\n", "1 Bar 32255 No 9496\n", "2 Berane 28342 No 12618\n", "3 Bijelo Polje 40110 No 15437\n", "4 Budva 12797 No 5180\n", ".. ... ... ... ...\n", "58 Rožaje 19646 Blank 90\n", "59 Šavnik 2306 Blank 20\n", "60 Tivat 10776 Blank 91\n", "61 Ulcinj 17117 Blank 137\n", "62 Žabljak 3407 Blank 24\n", "\n", "[63 rows x 4 columns]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mref_df = referendum_df\n", "\n", "# Blank or invalid votes:\n", "mref_df[\"Blank\"] = mref_df[\"Voted\"] - mref_df[\"Yes\"] - mref_df[\"No\"]\n", "\n", "mref_df = mref_df[[\"Municipality\", \"Registered\", \"No\", \"Yes\", \"Blank\"]]\n", "\n", "id_vars = [\"Municipality\", \"Registered\"]\n", "mref_df = pd.melt(frame=mref_df, id_vars=id_vars, var_name=\"Vote\", value_name=\"Number\")\n", "mref_df" ] }, { "cell_type": "markdown", "id": "d3d461c3", "metadata": {}, "source": [ "Display the voting results as pie charts. Place them in the center of the corresponding municipalities by connecting `data` and `map` with the parameter `map_join`." ] }, { "cell_type": "code", "execution_count": 8, "id": "c635aec5", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:42.650614Z", "iopub.status.busy": "2024-04-17T07:26:42.650492Z", "iopub.status.idle": "2024-04-17T07:26:42.939963Z", "shell.execute_reply": "2024-04-17T07:26:42.939733Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot() + \\\n", " map_layer + \\\n", " geom_pie(aes(paint_a='Vote', slice='Number'), \n", " data=mref_df, stat='identity', \n", " map=municipalities, map_join=['Municipality','state'],\n", " fill_by='paint_a', size=5, stroke=1.5) + \\\n", " scale_manual('paint_a', values=[\"#e41a1c\",\"#4daf4a\", \"#999999\"]) + \\\n", " plot_title + \\\n", " ggsize(800, 700) + \\\n", " theme_void()" ] }, { "cell_type": "markdown", "id": "1cdaade1", "metadata": {}, "source": [ "In the 'count2d' statistical transformation, we will use the calculated variables: the percentage of votes ('..prop..') and the total number of voters in the municipality ('..sum..'). We will show the details in the tooltips. And let's make the pie area corresponds to the number of voters." ] }, { "cell_type": "code", "execution_count": 9, "id": "953d2389", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:42.941731Z", "iopub.status.busy": "2024-04-17T07:26:42.941615Z", "iopub.status.idle": "2024-04-17T07:26:43.255953Z", "shell.execute_reply": "2024-04-17T07:26:43.255721Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pie_layer = geom_pie(aes('Municipality', paint_a='Vote', weight='Number', size='..sum..', group='Municipality'),\n", " data=mref_df,\n", " map=municipalities, map_join=['Municipality','state'],\n", " fill_by='paint_a', hole=0.2, stroke=1.5, stroke_side='both',\n", " tooltips=layer_tooltips()\n", " .title('@Municipality')\n", " .line('Vote|@Vote')\n", " .line('Number|@{..count..}')\n", " .line('Percent|@{..prop..}')\n", " .line('Total voted|@{..sum..}')\n", " .line('Registered|@Registered')\n", " .format('..prop..', '.2%')\n", " .format('Registered', ',d')) + \\\n", " scale_manual('paint_a', values=['#e41a1c','#4daf4a', '#999999']) + \\\n", " scale_size(name='Total voted', range=[3, 8])\n", "\n", "ggplot() + \\\n", " map_layer + \\\n", " pie_layer + \\\n", " plot_title + \\\n", " plot_title + \\\n", " ggsize(800, 700) + \\\n", " theme_void()" ] }, { "cell_type": "markdown", "id": "201a8ea7", "metadata": {}, "source": [ "Add livemap." ] }, { "cell_type": "code", "execution_count": 10, "id": "8c2eefa3", "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:26:43.257625Z", "iopub.status.busy": "2024-04-17T07:26:43.257501Z", "iopub.status.idle": "2024-04-17T07:26:43.309582Z", "shell.execute_reply": "2024-04-17T07:26:43.309328Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot() + \\\n", " geom_livemap(data_size_zoomin=3) + \\\n", " map_layer + \\\n", " pie_layer + \\\n", " plot_title + \\\n", " theme(legend_position='none')" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.10.13" } }, "nbformat": 4, "nbformat_minor": 5 }