Skip to content

Instantly share code, notes, and snippets.

@edasmalchi
Last active August 19, 2020 20:19
Show Gist options
  • Save edasmalchi/e24cd4ffa402fc88047fc94cd9733592 to your computer and use it in GitHub Desktop.
Save edasmalchi/e24cd4ffa402fc88047fc94cd9733592 to your computer and use it in GitHub Desktop.
transit-supply-viz
name: pd1
channels:
- conda-forge
- defaults
dependencies:
- pandas
- jupyter
- matplotlib
- appmode
- xlrd
- geopandas
- descartes
- folium
- ipyleaflet
- pysal
prefix: /Users/edasmalchi/anaconda3/envs/pd1
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import geopandas as gpd\n",
"import pandas as pd\n",
"idx = pd.IndexSlice\n",
"import numpy as np\n",
"\n",
"# import folium\n",
"from folium.plugins import MarkerCluster\n",
"import pysal as ps\n",
"from pysal.viz import mapclassify\n",
"\n",
"import ipywidgets as widgets"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"#Patch for Chrome courtesy of : https://github.com/python-visualization/folium/issues/812#issuecomment-555238062\n",
"\n",
"import base64\n",
"\n",
"import folium\n",
"\n",
"def _repr_html_(self, **kwargs):\n",
" html = base64.b64encode(self.render(**kwargs).encode('utf8')).decode('utf8')\n",
" onload = (\n",
" 'this.contentDocument.open();'\n",
" 'this.contentDocument.write(atob(this.getAttribute(\\'data-html\\')));'\n",
" 'this.contentDocument.close();'\n",
" )\n",
" if self.height is None:\n",
" iframe = (\n",
" '<div style=\"width:{width};\">'\n",
" '<div style=\"position:relative;width:100%;height:0;padding-bottom:{ratio};\">'\n",
" '<iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;'\n",
" 'border:none !important;\" '\n",
" 'data-html={html} onload=\"{onload}\" '\n",
" 'allowfullscreen webkitallowfullscreen mozallowfullscreen>'\n",
" '</iframe>'\n",
" '</div></div>').format\n",
" iframe = iframe(html=html, onload=onload, width=self.width, ratio=self.ratio)\n",
" else:\n",
" iframe = ('<iframe src=\"about:blank\" width=\"{width}\" height=\"{height}\"'\n",
" 'style=\"border:none !important;\" '\n",
" 'data-html={html} onload=\"{onload}\" '\n",
" '\"allowfullscreen\" \"webkitallowfullscreen\" \"mozallowfullscreen\">'\n",
" '</iframe>').format\n",
" iframe = iframe(html=html, onload=onload, width=self.width, height=self.height)\n",
" return iframe\n",
"\n",
"folium.branca.element.Figure._repr_html_ = _repr_html_"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"la_county = gpd.read_file(\"la_county.geojson\", driver='GeoJSON')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# service_data = pd.read_parquet('service_data_mdf.parquet')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"service_data = pd.read_json('with_new_dot_mdf.json', orient='table')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def select_view(df_all, agencies, service_type, geo):\n",
" serv_types = ['am_peak', 'midday', 'pm_peak', 'evening', 'early_am', 'total']\n",
" \n",
" assert service_type in serv_types\n",
" \n",
" agency_filtered = df_all\n",
" if 'all_agencies' not in agencies:\n",
" agency_filtered = df_all.loc[idx[:, :, agencies], :]\n",
" \n",
" agency_summed = agency_filtered.groupby(level=['tract', 'covid']).sum()\n",
" agency_summed = agency_summed[[f'{service_type}_vrh']]\n",
" \n",
" pre_covid = agency_summed.loc[idx[:, 0], :].reset_index(level='covid', drop=True)\n",
" covid = agency_summed.loc[idx[:, 1], :].reset_index(level='covid', drop=True)\n",
" difference = covid - pre_covid\n",
" \n",
" pre_covid = pre_covid.rename(\n",
" columns={pre_covid.columns[0]:f'{pre_covid.columns[0]}_pre_covid'})\n",
" covid = covid.rename(\n",
" columns={covid.columns[0]:f'{covid.columns[0]}_covid'})\n",
" difference = difference.rename(\n",
" columns={difference.columns[0]:f'{difference.columns[0]}_difference'})\n",
" joined = pre_covid.join(covid).join(difference)\n",
" joined['pct_maintained'] = joined.iloc[:, 2] / joined.iloc[:, 0] + 1\n",
" return geo.set_index('tract').join(joined.dropna(), how='inner')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def add_choropleth(vrh_gdf, m, classifier):\n",
" \n",
" vrh_gdf = vrh_gdf[vrh_gdf['pct_maintained'] != np.inf]\n",
" vrh_gdf = vrh_gdf[vrh_gdf['pct_maintained'] <= 2.0]\n",
" vrh_gdf['pct_maintained'] = vrh_gdf['pct_maintained'] * 100\n",
" \n",
" if classifier == 'Fixed':\n",
"# threshold_scale = [0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 2.0]\n",
" threshold_scale = [0, 20, 40, 60, 80, 100, 120, 200]\n",
" elif classifier == 'Quantiles':\n",
" threshold_scale = mapclassify.Quantiles(\n",
" vrh_gdf['pct_maintained'], k = 5).bins.tolist()\n",
" threshold_scale = [vrh_gdf['pct_maintained'].min()] + threshold_scale\n",
" elif classifier == 'Natural Breaks':\n",
" threshold_scale = mapclassify.NaturalBreaks(\n",
" vrh_gdf['pct_maintained'], k = 5).bins.tolist()\n",
" threshold_scale = [vrh_gdf['pct_maintained'].min()] + threshold_scale\n",
"# print(threshold_scale)\n",
" choropleth = folium.Choropleth(geo_data = vrh_gdf.reset_index().to_json(),\n",
" data = vrh_gdf.reset_index(),\n",
" columns = ('tract', 'pct_maintained'), key_on = 'feature.properties.tract',\n",
" nan_fill_color = 'red', fill_color = 'YlGnBu', fill_opacity = 0.6, line_opacity = 0.2, \n",
" threshold_scale = threshold_scale, legend_name='Service Maintained Post-COVID (Percentage)'\n",
" )\n",
" choropleth.add_to(m)\n",
" return"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"serv_list = [x[:-4] for x in list(service_data.columns)] \n",
"\n",
"disp_serv = [i.replace('_', ' ').capitalize() for i in serv_list]\n",
"\n",
"disp_serv = [i.replace('Am', 'AM').replace('Pm', 'PM').replace('am', 'AM').replace('peak', 'Peak') for i in disp_serv]\n",
"\n",
"disp_to_serv = dict(zip(disp_serv, serv_list))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"agency_list = ['all_agencies'] + list(service_data.droplevel(['tract', 'covid']).index.unique())\n",
"\n",
"disp_agency_la = ['All Agencies', 'LA Metro (bus)', 'LADOT Transit', 'LA Metro (rail)', 'Torrance Transit',\n",
" 'Santa Monica Big Blue Bus', 'Culver CityBus', 'Long Beach Transit',\n",
" 'Palos Verdes Peninsula Transit Authority', 'Norwalk Transit', 'Pasadena Transit']\n",
"\n",
"disp_to_agency = dict(zip(disp_agency_la, agency_list))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"region_widget = widgets.RadioButtons(\n",
" options=['pepperoni', 'pineapple', 'anchovies'],\n",
"# value='pineapple', # Defaults to 'pineapple'\n",
"# layout={'width': 'max-content'}, # If the items' names are long\n",
" description='Pizza topping:',\n",
" disabled=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"agency_widget = widgets.SelectMultiple(\n",
" options=disp_agency_la,\n",
" value=[disp_agency_la[0]],\n",
" #rows=10,\n",
" description='Agencies',\n",
" disabled=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"service_widget = widgets.Select(\n",
" options=disp_serv,\n",
" value=disp_serv[0],\n",
" #rows=10,\n",
" description='Service Type',\n",
" disabled=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"classify_widget = widgets.RadioButtons(\n",
" options=['Quantiles', 'Natural Breaks', 'Fixed'],\n",
" value='Fixed', # Defaults to 'Fixed'\n",
"# layout={'width': 'max-content'}, # If the items' names are long\n",
" description='Classifier:',\n",
" disabled=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def regional_map(region, agencies, service_type):\n",
" return"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"def interactive_map(agencies, service_type, classifier):\n",
" print('Running data query...', end='')\n",
"# print(service_type)\n",
" #convert displayed names to short names\n",
" agencies = [disp_to_agency[agency] for agency in agencies]\n",
" service_type = disp_to_serv[service_type]\n",
" view = select_view(service_data, agencies, service_type, la_county)\n",
" m = folium.Map([33.9556, -118.4399], zoom_start = 10)\n",
" add_choropleth(view, m, classifier)\n",
" print(' Done!')\n",
" print('Drawing map...')\n",
" display(m)\n",
" return m"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"w = widgets.interactive_output(\n",
" interactive_map,\n",
" {'agencies': agency_widget, 'service_type': service_widget,\n",
" 'classifier': classify_widget})\n",
"ui = widgets.VBox([\n",
" widgets.HBox([agency_widget, service_widget]), \n",
" classify_widget])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualizing Transit Service Supply During the Pandemic Response\n",
"\n",
"This tool visualizes an estimate of how the supply of transit service (measured as service hours) in each Census tract has changed since the start of the COVID-19 pandemic. Using GTFS data for each operator, it compares service levels from the most recent data available before March 2020 to the most recent data available from after mid-March 2020.\n",
"\n",
"By default, it shows an aggregation of all transit agencies in a region for which suitable data were available, but the selection boxes allow a custom subset of operators.\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "a96792a5ddab411aa2027f0ea028e8e2",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(HBox(children=(SelectMultiple(description='Agencies', index=(0,), options=('All Agencies', 'LA …"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "fbafc70cb89e43b686b857dab31e60bf",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Output(outputs=({'output_type': 'stream', 'text': 'Running data query...', 'name': 'stdout'},))"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"display(ui, w)"
]
},
{
"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.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment