How to deal with extreme values on a Folium map - python

I have very little experience with folium maps.
I need to make a map with the number of establishments in each department, the problem is that the capital has far more establishments than the interior, so when I create the color layer I get the capital as dark blue and all the rest with the same lighter color. This way the map is not so useful...
How could I solve that? I thought of maybe dividing the value by the population but it would be better to use the original value.
In the documentation, I did not find a way to parameterize the color.
df1 = pd.DataFrame({'code':['75','77','78','91','92','93','94','95'],'value':['13000','2000','2500','2300','2150','2600','1630','1300']})
dep_geo = geopandas.read_file('./dep.json', driver="JSON") #geodata taken from https://github.com/gregoiredavid/france-geojson/blob/master/departements.geojson
departments = {'75', '77', '78', '91', '92', '93', '94', '95'}
dep_geo = dep_geo[dep_geo['code'].isin(departments)]
df_map = dep_geo.merge(df1, how="left", left_on=['code'], right_on=['code'])
my_map = folium.Map(location=[48.856614, 2.3522219],
zoom_start = 9, tiles='cartodbpositron')
folium.Choropleth(
geo_data=df_map,
data=df_map,
columns=['code',"value"],
key_on="feature.properties.code",
fill_color='YlGnBu',
fill_opacity=0.5,
line_opacity=0.2,
legend_name="value ",
smooth_factor=0,
Highlight= True,
line_color = "black",
name = "value",
show=False,
overlay=True,
nan_fill_color = "White"
).add_to(my_map)
Result:
Thank you for your help!

it's as simple as using vmax argument. I've set to 85th percentile
also used geopandas explore() to generate the folium map
import geopandas as gpd
import pandas as pd
import folium
df1 = pd.DataFrame(
{
"code": ["75", "77", "78", "91", "92", "93", "94", "95"],
"value": ["13000", "2000", "2500", "2300", "2150", "2600", "1630", "1300"],
}
)
# dep_geo = geopandas.read_file('./dep.json', driver="JSON") #geodata taken from https://github.com/gregoiredavid/france-geojson/blob/master/departements.geojson
dep_geo = gpd.read_file(
"https://github.com/gregoiredavid/france-geojson/raw/master/departements.geojson"
) # geodata taken from https://github.com/gregoiredavid/france-geojson/blob/master/departements.geojson
departments = {"75", "77", "78", "91", "92", "93", "94", "95"}
dep_geo = dep_geo[dep_geo["code"].isin(departments)]
df_map = dep_geo.merge(df1, how="left", left_on=["code"], right_on=["code"])
df_map["value"] = pd.to_numeric(df_map["value"])
df_map.explore(
column="value",
cmap="YlGnBu",
vmax=df_map["value"].quantile(0.85),
style_kwds=dict(
color="rgba(0,0,0,.2)",
),
location=[48.856614, 2.3522219],
zoom_start=9,
tiles="cartodbpositron",
)

Related

Center align all dataframe headers except two that one of them must align left and the other right

The code below creates a table with the values of all columns centered and the column titles also centered. I align the values in the local_team column to the right and the values in the visitor_team column to the left:
ef dfi_image(list_dict,name_file):
df = pd.DataFrame(list_dict)
df = df[['time','competition','local_team','score','visitor_team','channels']]
df = df.style.set_table_styles([dict(selector='th', props=[('text-align', 'center'),('background-color', '#40466e'),('color', 'white')])])
df.set_properties(**{'text-align': 'center'}).hide(axis='index')
df.set_properties(subset=['local_team'], **{'text-align': 'right'}).hide(axis='index')
df.set_properties(subset=['visitor_team'], **{'text-align': 'left'}).hide(axis='index')
dfi.export(df, name_file + ".png")
dfi_image(games,'table_dfi.png')
But the title of local_team doesn't align to the right and neither the title of visitor_team aligns to the left and I need them with equal alignment to the values of these columns.
So i try:
def dfi_image(list_dict,name_file):
df = pd.DataFrame(list_dict)
df = df[['time','competition','local_team','score','visitor_team','channels']]
df = df.style.set_table_styles([
dict(selector='th', props=[('text-align', 'center'), ('background-color', '#40466e'), ('color', 'white')]),
dict(selector='th.col_heading.local_team', props=[('text-align', 'right')]),
dict(selector='th.col_heading.visitor_team', props=[('text-align', 'left')])
])
df.set_properties(**{'text-align': 'center'}).hide(axis='index')
df.set_properties(subset=['local_team'], **{'text-align': 'right'}).hide(axis='index')
df.set_properties(subset=['visitor_team'], **{'text-align': 'left'}).hide(axis='index')
dfi.export(df, name_file + ".png")
dfi_image(games,'table_dfi.png')
how to proceed for the result is equal this image:
Here is one way to do it with Pandas Styler.set_table_styles:
import pandas as pd
df = pd.DataFrame(
{
"col1": [1000000000, 2000000000],
"col2": [4000000000, 5000000000],
"col3": [7000000000, 8000000000],
"col4": [10000000000, 11000000000],
"col5": [13000000000, 14000000000],
}
)
To center align all column headers except col2 (right align) and col3 (left align):
styles = (
{
key: [{"selector": "th", "props": [("text-align", "center")]}]
for key in df.columns
if key not in ["col2", "col3"]
}
| {"col2": [{"selector": "th", "props": [("text-align", "right")]}]}
| {"col3": [{"selector": "th", "props": [("text-align", "left")]}]}
)
df.style.set_table_styles(styles)
Output in a Jupyter notebook cell:

Is there a way to control which vertices connect in a plotly.express.line_geo map?

I'm trying to make a connection map that has the option to use an animation_frame to show different months/years. Plotly.express has this option, but the plotly.express.line_geo maps seem to just attach the vertices of the network at random. I was looking at these examples from https://plotly.com/python/lines-on-maps/.
import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.line_geo(df, locations="iso_alpha",
color="continent", # "continent" is one of the columns of gapminder
projection="orthographic")
fig.show()
Plotly.graph_objects allows you to map actual connections between vertices, but doesn't seem to have an animation option.
import plotly.graph_objects as go
import pandas as pd
df_airports = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
df_airports.head()
df_flight_paths = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv')
df_flight_paths.head()
fig = go.Figure()
flight_paths = []
for i in range(len(df_flight_paths)):
fig.add_trace(
go.Scattergeo(
locationmode = 'USA-states',
lon = [df_flight_paths['start_lon'][i], df_flight_paths['end_lon'][i]],
lat = [df_flight_paths['start_lat'][i], df_flight_paths['end_lat'][i]],
mode = 'lines',
line = dict(width = 1,color = 'red'),
opacity = float(df_flight_paths['cnt'][i]) / float(df_flight_paths['cnt'].max()),
)
)
fig.show()
Does anyone know of a way that i could make a map like the flight path map, but allow an animation option to look at the flight maps for different months/years?
you can animate any trace type using frames
taking sample flight path data used in question, have split it into groups based on first letter of start airport
there is no need to create a trace per flight, instead create pairs of start end locations in arrays separated by None
with this it is simple to create a frame with a trace for each group
then just create the figure from the frames, plus a default trace
add play button and slider
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
df_flight_paths = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv"
)
frames = []
# lets split data based on first letter of start airport
# create a frame for each grouping
bins = 6
for color, df in df_flight_paths.groupby(
pd.qcut(
df_flight_paths["airport1"].str[0].apply(ord),
q=bins,
labels=px.colors.qualitative.Plotly[:bins],
)
):
name = f'{df["airport1"].str[0].min()}-{df["airport1"].str[0].max()}'
frames.append(
go.Frame(
name=name,
data=go.Scattergeo(
lon=df.assign(nan=None)[["start_lon", "end_lon", "nan"]].values.ravel(),
lat=df.assign(nan=None)[["start_lat", "end_lat", "nan"]].values.ravel(),
mode="lines",
line=dict(width=1, color=color),
),
)
)
# now create figure and add play button and slider
go.Figure(
data=frames[0].data,
frames=frames,
layout={
"updatemenus": [
{
"type": "buttons",
"buttons": [{"label": "Play", "method": "animate", "args": [None]}],
}
],
"sliders": [
{
"active": 0,
"steps": [
{
"label": f.name,
"method": "animate",
"args": [[f.name]],
}
for f in frames
],
}
],
},
).update_geos(
scope="north america",
)

plotly graph_objects (go) selecting two lines from two dropdowns menu to compare in the same figure

i am trying to compare two lines in the same fig in plotly go, the goal is to let the user to select two entities (i.e. countries) from two dropdown menu in the chart
this is the chart with all entities active
when i select different country in the two dropdown:
i.e. dropdown1 = Italy, dropdown2= France
the chart shows just one line (the last selected from the dropdown, so it update)but it doesn't plot the two lines in the same figure
Plot with just one line
this is the dataframe:
dataframe head
this is the code i am working on:
fig1 = go.Figure()
for column in df.columns.to_list():
fig1.add_trace(
go.Scatter(
x = df.index,
y = df[column],
name = column
)
)
button_all = dict(label = 'All',
method = 'restyle',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
button_none = dict(label = 'None',
method = 'update',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
def create_layout_button(column):
return dict(label = column,
method = 'restyle',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
def create_layout_buttonE(column):
return dict(label = column,
method = 'update',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
addAll = True
# Update plot sizing
fig1.update_layout(
width=700,
height=500,
autosize=False,
margin=dict(t=120, b=0, l=0, r=0),
)
# Add dropdowns
button_layer_1_height = 1.08
fig1.update_layout(
updatemenus=[
dict(
buttons=([button_all] * addAll) + list(df.columns.map(lambda column: create_layout_button(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.1,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
dict(
buttons=([button_none] * addAll) + list(df.columns.map(lambda column: create_layout_buttonE(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.37,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
]
)
fig1.show()
synthesized dataframe in same structure as you defined
taken a different approach. Rather than creating all traces and controlling visibility in updatemenus. Create two traces and control name and contents of y in updatemenus
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# get some countries
countries = (
pd.read_csv(
"https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/locations.csv"
)
.loc[lambda d: d["iso_code"].str.len() == 3, "location"]
.sample(20)
.sort_values()
.values
)
# build data frame of same struct
df = pd.DataFrame(
np.random.randint(200, 1500, [22, len(countries)]),
columns=countries,
index=range(2000, 2022),
)
# create a figure with two place holder traces
fig = go.Figure(
[
go.Scatter(x=df.index, y=np.full(len(df), np.nan), meta=i, name=i)
for i in range(2)
]
)
# but data for y and name in when country is selected
fig.update_layout(
updatemenus=[
{
"x": b / 3,
"y": 1.4,
"active": None,
"buttons": [
{
"label": c,
"method": "restyle",
"args": [{"y": [df[c]], "name": c}, [b]],
}
for c in df.columns
],
}
for b in range(2)
]
)

Continuous Color Scale Adjusts on Filtering

I am working on a presidential elections project that involves filtering a choropleth map. My data is at the county level, and I have a drop down box that allows a user to select a state. The counties are colored by a blue to red continuous color scale representing the lean from democrat to republican. The variable I use for the color scale is the margin between the vote of both parties.
If the margin is positive, the county should be colored a shade of blue. If the margin is negative, the county should colored a shade of red.
However, when I filter to a particular state and all counties in that state voted for one party, the scale finds the lowest margin value and assigns that a color on the blue end of the spectrum even if that county voted more for the Republican.
Is there a way to fix the color scale when filtering so the counties are colored correctly?
Here is some example code:
import pandas as pd
import dash
import os
from urllib.request import urlopen
import json
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
with urlopen(
"https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json"
) as response:
counties = json.load(response)
data = [
["Delaware", "Kent County", 10001, 0.467, 0.517, -75.513210, 39.156876],
["Delaware", "New Castle County", 10003, 0.322, 0.663, -75.513210, 39.156876],
["Delaware", "Sussex County", 10005, 0.559, 0.428, -75.513210, 39.156876],
["District of Columbia", "District of Columbia",11001,0.0712,0.913,-77.014468,38.910270],
["Rhode Island", "Bristol County",44001,0.2429,0.7352,-71.41572,41.65665],
["Rhode Island", "Kent County",44003,0.45117,0.5275,-71.41572,41.65665],
["Rhode Island", "Newport County",44005,0.3406,0.6389,-71.41572,41.65665],
["Rhode Island", "Providence County",44007,0.3761,0.605177,-71.41572,41.65665],
["Rhode Island", "Washington County",44009,0.392032,0.5857,-71.41572,41.65665]
]
data = pd.DataFrame(
data,
columns=[
"State",
"County",
"fips_code",
"perc_gop",
"perc_dem",
"lon",
"lat",
],
)
state_choices = data["State"].sort_values().unique()
data['margin_perc'] = data['perc_dem'] - data['perc_gop']
app = dash.Dash(__name__, assets_folder=os.path.join(os.curdir, "assets"))
server = app.server
app.layout = html.Div([
html.Div([
dcc.Dropdown(
id="dropdown1",
options=[{"label": i, "value": i} for i in state_choices],
value=state_choices[0],
)
],style={"width": "100%", "display": "inline-block", "text-align": "center"}
),
# State Map with County Choropleth
html.Div([
dcc.Graph(id="state_map")],
style={"width": "100%", "display": "inline-block", "text-align": "center"},
)
]
)
#app.callback(Output("state_map", "figure"), Input("dropdown1", "value"))
def update_figure3(state_select):
new_df = data[data["State"] == state_select]
avg_lat = new_df["lat"].mean()
avg_lon = new_df["lon"].mean()
fig = px.choropleth_mapbox(
new_df,
geojson=counties,
locations="fips_code",
color="margin_perc",
color_continuous_scale="balance",
mapbox_style="carto-positron",
zoom=6,
center={"lat": avg_lat, "lon": avg_lon},
opacity=0.5,
labels={
"State": "State",
"County": "County",
"perc_gop": "% Republican",
"perc_dem": "% Democratic",
"margin_perc":"% Margin"
},
hover_data={
"fips_code": False,
"State": True,
"County": True,
"perc_gop": True,
"perc_dem": True,
},
)
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
return fig
app.run_server(host="0.0.0.0", port="8051")
Figured it out --> needed to read documentation more carefully :/
The color_continuous_midpoint argument came in handy. Just calculated the midpoint for the color variable for the entire dataset and used that as the fixed midpoint in the scale.

Why is my choropleth map in python empty?

So I am trying to learn to plot choropleth maps. I used a sample dataset but it comes out empty. Please could you look at it and tell me what is wrong because I have crosschecked the syntax and do not understand why it does not show my data. I have included the dataset head and the code I wrote as seen below:
datia = dict(type = 'choropleth',
locations = df['Names'],
locationmode = 'USA-states',
colorscale= 'Portland',
text= df['Names'],
z=df['2010-11'],
colorbar = {'title':'Vaccine Coverage in percentage'})
layout = dict(title = 'Vaccine Coverage in percentage',
geo = {'scope':'usa'})
choromap = go.Figure(data = [datia],layout = layout)
iplot(choromap, validate=False)
You need to give STATE Abbreviations(DC,AL,...) instead of Names.
I faced the same issue. It worked after changing that.
You can use the following code to convert names to abbreviations
def convert_state_name(name):
cd=str(name)
us_state_abbrev = {
"Alabama":"AL"
"Alaska":"AK"
"Arizona":"AZ"
"Arkansas":"AR"
"California":"CA"
"Colorado":"CO"
"Connecticut":"CT"
"Delaware":"DE"
"Washington DC":"DC"
"Florida":"FL"
"Georgia":"GA"
"Hawaii":"HI"
"Idaho":"ID"
"Illinois":"IL"
"Indiana":"IN"
"Iowa":"IA"
"Kansas":"KS"
"Kentucky":"KY"
"Louisiana":"LA"
"Maine":"ME"
"Maryland":"MD"
"Massachusetts":"MA"
"Michigan":"MI"
"Minnesota":"MN"
"Mississippi":"MS"
"Missouri":"MO"
"Montana":"MT"
"Nebraska":"NE"
"Nevada":"NV"
"New Hampshire":"NH"
"New Jersey":"NJ"
"New Mexico":"NM"
"New York":"NY"
"North Carolina":"NC"
"North Dakota":"ND"
"Ohio":"OH"
"Oklahoma":"OK"
"Oregon":"OR"
"Pennsylvania":"PA"
"Rhode Island":"RI"
"South Carolina":"SC"
"South Dakota":"SD"
"Tennessee":"TN"
"Texas":"TX"
"Utah":"UT"
"Vermont":"VT"
"Virginia":"VA"
"Washington":"WA"
"West Virginia":"WV"
"Wisconsin":"WI"
"Wyoming":"WY"
return us_state_abbrev[cd]
df['STATE_CD'] = df.Names.apply(lambda x:convert_state_name(x))
Finally you can replace locations=df['Names'] with locations=df['STATE_CD']
Hope this helps.

Categories

Resources