{ "cells": [ { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "from branca.colormap import linear\n", "import folium\n", "from folium import Map, Marker, GeoJson, LayerControl\n", "import pandas as pd\n", "import geopandas as gpd\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load and explore geojson" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "gj_path = \"geojson/anc.geojson\"" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "anc_df = gpd.read_file(gj_path)" ] }, { "cell_type": "code", "execution_count": 5, "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", "
OBJECTIDANC_IDWEB_URLNAMEShape_LengShape_Areageometry
011Chttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 1C5218.9543611.285112e+06POLYGON ((-77.0464219248981 38.92597950466725,...
121Dhttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 1D4224.0100689.475922e+05POLYGON ((-77.03645123520528 38.93638367371454...
232Ahttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2A12477.9432047.065358e+06POLYGON ((-77.05445304334567 38.90725341205063...
342Bhttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2B7712.5047852.160620e+06POLYGON ((-77.0412259402753 38.91701561959232,...
452Chttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2C7811.0846272.861750e+06POLYGON ((-77.02404971019487 38.90293630338282...
\n", "
" ], "text/plain": [ " OBJECTID ANC_ID WEB_URL NAME \\\n", "0 1 1C http://anc.dc.gov/page/advisory-neighborhood-c... ANC 1C \n", "1 2 1D http://anc.dc.gov/page/advisory-neighborhood-c... ANC 1D \n", "2 3 2A http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2A \n", "3 4 2B http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2B \n", "4 5 2C http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2C \n", "\n", " Shape_Leng Shape_Area \\\n", "0 5218.954361 1.285112e+06 \n", "1 4224.010068 9.475922e+05 \n", "2 12477.943204 7.065358e+06 \n", "3 7712.504785 2.160620e+06 \n", "4 7811.084627 2.861750e+06 \n", "\n", " geometry \n", "0 POLYGON ((-77.0464219248981 38.92597950466725,... \n", "1 POLYGON ((-77.03645123520528 38.93638367371454... \n", "2 POLYGON ((-77.05445304334567 38.90725341205063... \n", "3 POLYGON ((-77.0412259402753 38.91701561959232,... \n", "4 POLYGON ((-77.02404971019487 38.90293630338282... " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "anc_df.head()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# Tried this to reduce the map complexity for Chrome, didn't work\n", "# anc_df.geometry = anc_df.geometry.simplify(500, preserve_topology=False)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def embed_map(m):\n", " from IPython.display import IFrame\n", "\n", " m.save('inline_map.html')\n", " return IFrame('inline_map.html', width='100%', height='750px')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Preprocess data and join with election CSV using pandas" ] }, { "cell_type": "code", "execution_count": 7, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
wardyearancnum_candidatesvotesvote_normengagementprop_uncontestedprop_empty
012012A1.583333481.500000NaNNaN0.5000000.0
112012B1.250000544.333333NaNNaN0.9166670.0
212012C1.500000641.875000NaNNaN0.6250000.0
312012D1.400000559.800000NaNNaN0.6000000.0
412014A1.333333296.2500000.5292460.636180.6666670.0
\n", "
" ], "text/plain": [ " ward year anc num_candidates votes vote_norm engagement \\\n", "0 1 2012 A 1.583333 481.500000 NaN NaN \n", "1 1 2012 B 1.250000 544.333333 NaN NaN \n", "2 1 2012 C 1.500000 641.875000 NaN NaN \n", "3 1 2012 D 1.400000 559.800000 NaN NaN \n", "4 1 2014 A 1.333333 296.250000 0.529246 0.63618 \n", "\n", " prop_uncontested prop_empty \n", "0 0.500000 0.0 \n", "1 0.916667 0.0 \n", "2 0.625000 0.0 \n", "3 0.600000 0.0 \n", "4 0.666667 0.0 " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "election_df = pd.read_csv(\"election_data_for_anc_map.csv\")\n", "election_df.head()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(160, 8)\n" ] }, { "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", "
yearnum_candidatesvotesvote_normengagementprop_uncontestedprop_emptyANC_ID
020121.583333481.500000NaNNaN0.5000000.01A
120121.250000544.333333NaNNaN0.9166670.01B
220121.500000641.875000NaNNaN0.6250000.01C
320121.400000559.800000NaNNaN0.6000000.01D
420141.333333296.2500000.5292460.636180.6666670.01A
\n", "
" ], "text/plain": [ " year num_candidates votes vote_norm engagement prop_uncontested \\\n", "0 2012 1.583333 481.500000 NaN NaN 0.500000 \n", "1 2012 1.250000 544.333333 NaN NaN 0.916667 \n", "2 2012 1.500000 641.875000 NaN NaN 0.625000 \n", "3 2012 1.400000 559.800000 NaN NaN 0.600000 \n", "4 2014 1.333333 296.250000 0.529246 0.63618 0.666667 \n", "\n", " prop_empty ANC_ID \n", "0 0.0 1A \n", "1 0.0 1B \n", "2 0.0 1C \n", "3 0.0 1D \n", "4 0.0 1A " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "election_df[\"ANC_ID\"] = election_df[\"ward\"].map(str) + election_df[\"anc\"]\n", "election_df.drop(columns = [\"ward\", \"anc\"], inplace=True)\n", "print(election_df.shape)\n", "election_df.head()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(40, 8)\n" ] } ], "source": [ "election_df_2018 = election_df[election_df.year == 2018]\n", "print(election_df_2018.shape)" ] }, { "cell_type": "code", "execution_count": 10, "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", " \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", "
OBJECTIDANC_IDWEB_URLNAMEShape_LengShape_Areageometryyearnum_candidatesvotesvote_normengagementprop_uncontestedprop_empty
011Chttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 1C5218.9543611.285112e+06POLYGON EMPTY20181.375000690.5000000.6951440.8188040.6250000.0
121Dhttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 1D4224.0100689.475922e+05POLYGON EMPTY20181.600000513.2000000.6081000.8189870.4000000.0
232Ahttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2A12477.9432047.065358e+06POLYGON EMPTY20181.125000254.3750000.6880370.7701380.8750000.0
342Bhttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2B7712.5047852.160620e+06POLYGON EMPTY20181.222222574.6666670.6983490.8408830.7777780.0
452Chttp://anc.dc.gov/page/advisory-neighborhood-c...ANC 2C7811.0846272.861750e+06POLYGON EMPTY20181.333333417.0000000.6864300.8068620.6666670.0
\n", "
" ], "text/plain": [ " OBJECTID ANC_ID WEB_URL NAME \\\n", "0 1 1C http://anc.dc.gov/page/advisory-neighborhood-c... ANC 1C \n", "1 2 1D http://anc.dc.gov/page/advisory-neighborhood-c... ANC 1D \n", "2 3 2A http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2A \n", "3 4 2B http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2B \n", "4 5 2C http://anc.dc.gov/page/advisory-neighborhood-c... ANC 2C \n", "\n", " Shape_Leng Shape_Area geometry year num_candidates \\\n", "0 5218.954361 1.285112e+06 POLYGON EMPTY 2018 1.375000 \n", "1 4224.010068 9.475922e+05 POLYGON EMPTY 2018 1.600000 \n", "2 12477.943204 7.065358e+06 POLYGON EMPTY 2018 1.125000 \n", "3 7712.504785 2.160620e+06 POLYGON EMPTY 2018 1.222222 \n", "4 7811.084627 2.861750e+06 POLYGON EMPTY 2018 1.333333 \n", "\n", " votes vote_norm engagement prop_uncontested prop_empty \n", "0 690.500000 0.695144 0.818804 0.625000 0.0 \n", "1 513.200000 0.608100 0.818987 0.400000 0.0 \n", "2 254.375000 0.688037 0.770138 0.875000 0.0 \n", "3 574.666667 0.698349 0.840883 0.777778 0.0 \n", "4 417.000000 0.686430 0.806862 0.666667 0.0 " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "joined_df = anc_df.merge(election_df_2018, how=\"left\", on=\"ANC_ID\")\n", "joined_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Update geojson features from dataframe values" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "' No longer necessary\\nfor anc in gjdata[\\'features\\']:\\n anc_id = anc[\\'properties\\'][\\'ANC_ID\\']\\n features = election_df.columns.tolist()\\n features.remove(\"ANC_ID\")\\n for feature in features:\\n anc[\\'properties\\'][feature] = joined_df.loc[joined_df[\\'ANC_ID\\'] == anc_id, feature].item()\\n'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\" No longer necessary\n", "for anc in gjdata['features']:\n", " anc_id = anc['properties']['ANC_ID']\n", " features = election_df.columns.tolist()\n", " features.remove(\"ANC_ID\")\n", " for feature in features:\n", " anc['properties'][feature] = joined_df.loc[joined_df['ANC_ID'] == anc_id, feature].item()\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Construct map" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "anc_map = Map(location = (38.8899, -77.0091),\n", " zoom_start = 12,\n", " tiles = 'Stamen Toner')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "folium.Choropleth(\n", " geo_data=gj_path,\n", " data=joined_df,\n", " columns=[\"ANC_ID\", \"votes\"],\n", " key_on='feature.properties.ANC_ID',\n", " fill_color='GnBu',\n", " fill_opacity=0.5,\n", " line_weight=1, \n", " highlight=True,\n", " overlay=True,\n", " name=\"average votes\",\n", " legend_name=\"average # votes for winning candidates\",\n", ").add_to(anc_map)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "folium.Choropleth(\n", " geo_data=gj_path,\n", " data=joined_df,\n", " columns=[\"ANC_ID\", \"engagement\"],\n", " key_on='feature.properties.ANC_ID',\n", " fill_color='PuRd',\n", " fill_opacity=0.5,\n", " line_weight=1, \n", " highlight=True,\n", " overlay=True,\n", " name=\"engagement\",\n", " legend_name=\"percentage of ballots where ANC candidate was marked\",\n", ").add_to(anc_map)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LayerControl().add_to(anc_map)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "embed_map(anc_map)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "anc_map.save(\"anc_map.html\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 2 }