Python - plotly - combine bubble and choropleth map - python

In the answer to this question R - plotly - combine bubble and chorpleth map, it was described nicely how to combine bubble and choropleth map within one map in plotly. I would like to replicate exactly the same example in Python, however I did not manage based on the information provided in the plotly documentation (https://plot.ly/python/reference/).
The equivalent R code reads as follows:
lon = c(-73.9865812, -118.2427266, -87.6244212, -95.3676974)
pop = c(8287238, 3826423, 2705627, 2129784)
df_cities = data.frame(cities, lat, lon, pop)
state_codes = c("NY", "CA", "IL", "TX")
pop = c(19746227.0, 38802500.0, 12880580.0, 26956958.0)
df_states = data.frame(state_codes, pop)
plot_ly(df_cities, lon=lon, lat=lat,
text=paste0(df_cities$cities,'<br>Population: ', df_cities$pop),
marker= list(size = sqrt(pop/10000) + 1), type="scattergeo",
filename="stackoverflow/choropleth+scattergeo") %>%
add_trace(z=df_states$pop,
locations=df_states$state_codes,
text=paste0(df_states$state_codes, '<br>Population: ', df_states$pop),
type="choropleth",
colors = 'Purples',
locationmode="USA-states") %>%
layout(geo = list(scope="usa"))
How can this be implemented in Python?

Simply append .py to the graph's URL (e.g. https://plot.ly/~RPlotBot/1735.py) to see corresponding python code.

I found that I had to adjust r the code just a little to get it to work:
cities = c('New York', 'Los Angeles', 'Chicago', 'Houston')
lat = c(40.7305991, 34.053717, 41.8755546, 29.7589382)
lon = c(-73.9865812, -118.2427266, -87.6244212, -95.3676974)
pop = c(8287238, 3826423, 2705627, 2129784)
df_cities = data.frame(cities, lat, lon, pop)
state_codes = c("NY", "CA", "IL", "TX")
pop = c(19746227.0, 38802500.0, 12880580.0, 26956958.0)
df_states = data.frame(state_codes, pop)
plot_ly(df_cities, lon=lon, lat=lat,
text = paste0(df_cities$cities,'<br>Population: ', df_cities$pop),
marker = list(size = sqrt(pop/10000) + 1), type="scattergeo",
filename = "stackoverflow/choropleth+scattergeo") %>%
add_trace(z=df_states$pop,
locations = df_states$state_codes,
text = paste0(df_states$state_codes, '<br>Population: ', df_states$pop),
type = "choropleth",
colors = 'Purples',
locationmode = "USA-states") %>%
layout(geo = list(scope="usa"))

Related

Adding a grid to a mapbox map in plotly python

I need to plot a 1 km by 1 km grid on mapbox map in plolty.I am attaching a code, but I am stuck in adding a grid and also integrating mapbox token.
Below is the code to the map I have made.
shp_path = "Path to shape file"
sf = shp.Reader(shp_path)
def read_shapefile(sf):
fields = [x[0] for x in sf.fields][1:]
records = sf.records()
shps = [s.points for s in sf.shapes()]
df = pd.DataFrame(columns=fields, data=records)
df = df.assign(coords=shps)
return df
fig = px.scatter_mapbox(df, lat="LAT", lon="LONG",
size_max=30, zoom=12.5,
height = 600, width = 1000, #center = dict(lat = g.center)
title='Drive Route with Mapbox',
#mapbox_style="open-street-map"
)
fig.update_layout(font_size=16, title={'xanchor': 'center','yanchor': 'top', 'y':0.9, 'x':0.5,},
title_font_size = 24, mapbox_accesstoken=api_token, mapbox_style = "mapbox://styles/strym/ckhd00st61aum19noz9h8y8kw")
fig.update_traces(marker=dict(size=6))
We tried integrating the mapbox token into the code, but it keeps giving an error:
mapbox_token = requests.get('https://api.mapbox.com/?access_token=gridactivity').text
px.set_mapbox_access_token(mapbox_token)
fig = ff.create_hexbin_mapbox(
data_frame=df, lat="centroid_lat", lon="centroid_lon",
nx_hexagon=10, opacity=0.9, labels={"color": "Point Count"},
)
fig.update_layout(margin=dict(b=0, t=0, l=0, r=0))
fig.show()
This shows an empty map

Map doesn't display using folium choropleth map for zip codes

I am trying to use folium to create a map with zip codes showing. However I can't seem to get any results displayed - there are no errors, but I don't get any output. The html just returns a map. What could be the issue?
Here's my test code below:
import pandas as pd
shelters = pd.read_csv('LAzipcodes.csv')
import json, requests
response = requests.get("https://maps.lacity.org/lahub/rest/services/Boundaries/MapServer/26/query?where=1%3D1&outFields=ZIPCODE&outSR=4326&f=json")
data = json.loads(response.content.decode('utf-8'))
lazipcodes = []
for i in range(len(data['features'])):
if data['features'][i]['attributes']['ZIPCODE'] in list(shelters['ZIP'].astype(str).unique()):
lazipcodes.append(data['features'][i])
#print(len(lazipcodes))
new_json = dict.fromkeys(['type','features'])
new_json['type'] = 'FeatureCollection'
new_json['features'] = lazipcodes
open('la-county-zipcodes.json', "w").write(
json.dumps(new_json, sort_keys=True, indent=4, separators=(',',':'))
)
la_zip = r'la-county-zipcodes.json'
import folium
m = folium.Map(location=[34.0522, -118.2437], zoom_start=11)
m.choropleth(
geo_data = la_zip,
data = shelters,
columns = ['ZIP','COUNT'],
key_on = 'feature.attributes.ZIPCODE',
fill_color = 'YlGn',
fill_opacity = 0.7,
line_opacity = 0.2,
line_color='Blue',
legend_name = 'Count Rate'
)
folium.LayerControl().add_to(m)
m.save('Zipcodes.html')
I tried your code and it is converting with different types of geojson files, but is the geometry information missing? Also folium.Choropleth(...). .add_to(m) is the correct setting, I got the zipdoce geojson from here and was able to draw a choropleth map by creating sample data.
import json
geojson = open('./data/la-zip-code-areas-2012.geojson', 'r')
data = json.load(geojson)
lazipcodes = []
for i in range(len(data['features'])):
zip_code = data['features'][i]['properties']['name']
lazipcodes.append(zip_code)
shelters = pd.DataFrame({'ZIP':lazipcodes, 'COUNT': np.random.randint(0,100, len(lazipcodes))})
import folium
m = folium.Map(location=[34.0522, -118.2437], zoom_start=8)
folium.Choropleth(
geo_data = data,
data = shelters,
columns = ['ZIP','COUNT'],
key_on = 'feature.properties.name',
fill_color = 'YlGn',
fill_opacity = 0.7,
line_opacity = 0.2,
line_color = 'Blue',
legend_name = 'Count Rate'
).add_to(m)
# folium.LayerControl().add_to(m)
# m.save('Zipcodes.html')
m

Visualization errors in Power BI using Python

Trying to run the following code, and it returns following error in Power BI:
ValueError: Invalid value of type 'builtins.float' received for the 'opacity' property of scattergeo. Received value: nan
The 'opacity' property is a number and may be specified as: - An int or float in the interval [0, 1].
From the 'cnt' column, all values are whole numbers ranging from 1 to 35.
Also. When I replace the opacity line with = 1. It says Power BI cannot create a visual, and it opens up in my browser with a working visual.... Any idea why it wont load in Power BI, but will load in my browser working as intended?
Code Below, any and all help is greatly appreciated!
# dataset = pandas.DataFrame(start_lat, start_lon, end_lat, end_lon, cnt, lat, long)
# dataset = dataset.drop_duplicates()
# Paste or type your script code here:
import plotly.graph_objects as go
import pandas as pd
import os
os.chdir(r'C:\Users\myself\Desktop\data_files\py_docs')
df_dcs = (dataset)
df_lanes = (dataset)
fig = go.Figure()
fig.add_trace(go.Scattergeo(
locationmode = 'USA-states',
lon = df_dcs['long'],
lat = df_dcs['lat'],
hoverinfo = 'text',
text = df_dcs['location'],
mode = 'markers',
marker = dict(
size = 4,
color = 'rgb(255, 153, 51)',
line = dict(
width = 3,
color = 'rgba(68, 68, 68, 0)'
)
)))
lanes = []
for i in range(len(df_lanes)):
fig.add_trace(
go.Scattergeo(
locationmode = 'USA-states',
lon = [df_lanes['start_lon'][i], df_lanes['end_lon'][i]],
lat = [df_lanes['start_lat'][i], df_lanes['end_lat'][i]],
mode = 'lines',
line = dict(width = 3,color = 'orange'),
opacity = float(df_lanes['cnt'][i]) / float(df_lanes['cnt'].max()),
)
)
fig.update_layout(
title_text = 'Route Lanes',
showlegend = False,
geo = dict(
scope = 'north america',
projection_type = 'azimuthal equal area',
showland = True,
landcolor = 'rgb(243, 243, 243)',
countrycolor = 'rgb(204, 204, 204)',
),
)
fig.show()```

Python: how to update data selection in bokeh?

I new in using bokeh.
This is what I am doing. From osmnx I get data of schools and hospitals in Haiti.
Without writing all the code I arrive to get the following
data1=dict(
x=list(schools['x'].values),
y=list(schools['y'].values)
)
data2=dict(
x=list(hospitals['x'].values),
y=list(hospitals['y'].values)
)
building = 'Schools'
buildings = {
'Schools': {
'title': 'Schools',
'data': data1,
'color': 'black'
},
'Hospitals': {
'title': 'Hospitals',
'data': data2,
'color': 'red'
}
}
building_select = Select(value=building, title='Building', options=sorted(buildings.keys()))
I would like to change the visualisation between schools and hospitals by selecting it. I define the function that change the data to take and the color.
def returnInfo(building):
dataPoints = buildings[building]['data']
color = buildings[building]['color']
return dataPoints, color
dataPoints, color = returnInfo(building)
I define the function make_plot
def make_plot(dataPoints, title, color):
TOOLS = "pan, wheel_zoom, reset,save"
p = figure(plot_width=800,
tools=TOOLS,
x_axis_location=None,
y_axis_location=None)
# Add points on top (as black points)
buildings = p.circle('x', 'y', size=4, source=data1, color=color)
hover_buildings = HoverTool(renderers = [buildings], point_policy="follow_mouse", tooltips = [("(Long, Lat)", "($x, $y)")])
p.add_tools(hover_buildings)
return p
plot = make_plot(dataPoints, "Data for " + buildings[building]['title'], color)
then I update
def update_plot(attrname, old, new):
building = building_select.value
p.title.text = "Data for " + buildings[building]['title']
src = buildings[building]['data']
dataPoints, color = returnInfo(building)
dataPoints.update
building_select.on_change('value', update_plot)
controls = column(building_select)
curdoc().add_root(row(plot, controls))
but it does not work: i.e. I am not able to change the points from schools to hospitals even if I have the cursor. Where is the error in the update section?
As first solution I suggest to use legend.click_plolicy = 'hide' to toggle visibility of your buildings on the map (Bokeh v1.1.0)
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.tile_providers import CARTODBPOSITRON_RETINA
import osmnx as ox
amenities = ['hospital', 'school']
for i, amenity in enumerate(amenities):
buildings = ox.pois_from_address("Port-au-Prince, Haiti", amenities = [amenity], distance = 3500)[['geometry', 'name', 'element_type']]
for item in ['way', 'relation']:
buildings.loc[buildings.element_type == item, 'geometry'] = buildings[buildings.element_type == item]['geometry'].map(lambda x: x.centroid)
buildings.name.fillna('Hospital' if i == 0 else 'School', inplace = True)
amenities[i] = buildings.to_crs(epsg = 3857)
p = figure(title = "Port-au-Prince, Haiti", tools = "pan,wheel_zoom,hover,reset", x_range = (-8057000, -8048500), y_range = (2098000, 2106000),
tooltips = [('Name', '#name'), ("(Long, Lat)", "($x, $y)")], x_axis_location = None, y_axis_location = None, active_scroll = 'wheel_zoom')
p.add_tile(CARTODBPOSITRON_RETINA)
p.grid.grid_line_color = None
for i, b in enumerate(amenities):
source = ColumnDataSource(data = dict(x = b.geometry.x, y = b.geometry.y, name = b.name.values))
p.circle('x', 'y', color = 'red' if i == 0 else 'blue', source = source, legend = 'Hospital' if i == 0 else 'School')
p.legend.click_policy = 'hide'
show(p)
And if you want the Select widget then here is another alternative (Bokeh v1.1.0):
from bokeh.models import ColumnDataSource, Column, Select, CustomJS
from bokeh.plotting import figure, show
from bokeh.tile_providers import CARTODBPOSITRON_RETINA
import osmnx as ox
amenities = ['hospital', 'school']
for i, amenity in enumerate(amenities):
buildings = ox.pois_from_address("Port-au-Prince, Haiti", amenities = [amenity], distance = 3500)[['geometry', 'name', 'element_type']]
for item in ['way', 'relation']:
buildings.loc[buildings.element_type == item, 'geometry'] = buildings[buildings.element_type == item]['geometry'].map(lambda x: x.centroid)
buildings.name.fillna('Hospital' if i == 0 else 'School', inplace = True)
buildings = buildings.to_crs(epsg = 3857)
amenities[i] = dict(x = list(buildings.geometry.x), y = list(buildings.geometry.y), name = list(buildings.name.values), color = (['red'] if i == 0 else ['blue']) * len(buildings.name.values))
source = ColumnDataSource(amenities[0])
p = figure(title = "Hospitals", tools = "pan,wheel_zoom,hover,reset", x_range = (-8057000, -8048500), y_range = (2098000, 2106000),
tooltips = [('Name', '#name'), ("(Long, Lat)", "($x, $y)")], x_axis_location = None, y_axis_location = None, active_scroll = 'wheel_zoom')
p.add_tile(CARTODBPOSITRON_RETINA)
p.circle(x = 'x', y = 'y', color = 'color', source = source)
p.grid.grid_line_color = None
code = ''' source.data = (cb_obj.value == 'Hospitals' ? data[0] : data[1]); p.title.text = cb_obj.value; '''
select = Select(options = ['Hospitals', 'Schools'], callback = CustomJS(args=dict(p = p, source = source, data = amenities), code = code))
show(Column(p, select))
Let me know if you need any explanation on this code.
Below are the changes required to make your code work:
In your make_plot method, since you want to update the title of the plot on selection change, replace
p = figure(plot_width=800,
tools=TOOLS,
x_axis_location=None,
y_axis_location=None)
with
p = figure(plot_width=800,
tools=TOOLS,
title=title,
x_axis_location=None,
y_axis_location=None)
Also, since you want to update the data and color of the buildings, return the buildings too in the method, so that the complete method now looks like:
def make_plot(dataPoints, title, color):
TOOLS = "pan, wheel_zoom, reset,save"
p = figure(plot_width=800,
tools=TOOLS,
title=title,
x_axis_location=None,
y_axis_location=None)
# Add points on top (as black points)
buildings = p.circle('x', 'y', size=4, source=data1, color=color)
hover_buildings = HoverTool(renderers = [buildings], point_policy="follow_mouse", tooltips = [("(Long, Lat)", "($x, $y)")])
p.add_tools(hover_buildings)
return p, buildings
Next, instead of the call to
plot = make_plot(dataPoints, "Data for " + buildings[building]['title'], color)
you need to get the returned buildings also in a variable so that it can be directly updated. So now your call will look like
plot, b = make_plot(dataPoints, "Data for " + buildings[building]['title'], color)
Finally, change your update_plot method, so that it looks like this:
def update_plot(attrname, old, new):
building = building_select.value
plot.title.text = "Data for " + buildings[building]['title']
src = buildings[building]['data']
dataPoints, color = returnInfo(building)
b.data_source.data = dataPoints
b.glyph.fill_color = color
With these changes, it would work as expected. See the results attached.
Sample data used is:
data1=dict(
x=[1,2,3],
y=[2,1,3]
)
data2=dict(
x=[1,2,3],
y=[1,3,2]
)

Plotly Geoscatter with Aggregation: show aggregation in hover Info

I have been trying to create a Geoscatter Plot with Plotly where the marker size should indicate the number of customers (row items) in one city (zip_city). I based my code on two templates from the Plotly documentation: United States Bubble Map and the aggregation part Mapping with Aggregates.
I managed to put together a code that does what I want, except for one drawback: when I hover over a bubble, I would like to see the name of the city plus number of customers (the result from the aggregation), so something like Aguadilla: 2. Can you help me on how to do this?
Here is my code (as a beginner with plotly, I am also open to code improvements):
import plotly.offline as pyo
import pandas as pd
df = pd.DataFrame.from_dict({'Customer': [111, 222, 555, 666],
'zip_city': ['Aguadilla', 'Aguadilla', 'Arecibo', 'Wrangell'],
'zip_latitude':[18.498987, 18.498987, 18.449732,56.409507],
'zip_longitude':[-67.13699,-67.13699,-66.69879,-132.33822]})
data = [dict(
type = 'scattergeo',
locationmode = 'USA-states',
lon = df['zip_longitude'],
lat = df['zip_latitude'],
text = df['Customer'],
marker = dict(
size = df['Customer'],
line = dict(width=0.5, color='rgb(40,40,40)'),
sizemode = 'area'
),
transforms = [dict(
type = 'aggregate',
groups = df['zip_city'],
aggregations = [dict(target = df['Customer'], func = 'count', enabled = True)]
)]
)]
layout = dict(title = 'Customers per US City')
fig = dict( data=data, layout=layout )
pyo.plot( fig, validate=False)
Update:
Can I access the result of the transforms argument directly in the data argument to show the number of customers per city?
You can create a list, that will contains what you want and then set text=list in data. Also do not forget specify hoverinfo='text'.
I am updated your code, so try this:
import pandas as pd
import plotly.offline as pyo
df = pd.DataFrame.from_dict({'Customer': [111, 222, 555, 666],
'zip_city': ['Aguadilla', 'Aguadilla', 'Arecibo', 'Wrangell'],
'zip_latitude':[18.498987, 18.498987, 18.449732,56.409507],
'zip_longitude':[-67.13699,-67.13699,-66.69879,-132.33822]})
customer = df['Customer'].tolist()
zipcity = df['zip_city'].tolist()
list = []
for i in range(len(customer)):
k = str(zipcity[i]) + ':' + str(customer[i])
list.append(k)
data = [dict(
type = 'scattergeo',
locationmode = 'USA-states',
lon = df['zip_longitude'],
lat = df['zip_latitude'],
text = list,
hoverinfo = 'text',
marker = dict(
size = df['Customer'],
line = dict(width=0.5, color='rgb(40,40,40)'),
sizemode = 'area'
),
transforms = [dict(
type = 'aggregate',
groups = df['zip_city'],
aggregations = [dict(target = df['Customer'], func = 'count', enabled = True)]
)]
)]
layout = dict(title = 'Customers per US City')
fig = dict(data=data, layout=layout)
pyo.plot(fig, validate=False)

Categories

Resources