Is it possible to change rectangle ordenation in Plotly treemap? - python

I want to make a treemap following a predefined ordering, but I don't know how to manipulate the hierarchy defined by plotly treemap.
Here is the treemap I've created:
Here I want the O to be above the U box and the UR to be a unique column, without dividing its space with NA.
Here is my code:
import plotly.graph_objects as go
fig = go.Figure(go.Treemap(
labels = ["DR","1,2","D 1","D 2","O","U",
"3,4,5","P","B","Pe",
"6,7,8","F","C","Pl",
"9","UR",
"10","NA"],
parents = ["", "DR", "1,2","1,2","1,2","1,2","DR","3,4,5","3,4,5","3,4,5",
"DR","6,7,8","6,7,8","6,7,8",
"DR","9",
"DR","10"],
marker_colors = ["lightgrey",
"lightblue", "cornflowerblue", "cornflowerblue","goldenrod","green",
"lightblue","lightgray","chocolate","cadetblue",
"lightblue","grey",'chocolate',"saddlebrown",
"salmon","salmon",
"burlywood","burlywood",
],
branchvalues="total"
))
fig.update_layout(margin = dict(t=10, l=60, r=25, b=30))
fig.show()
Much appreciated!
I've tried creating values for each rectangle, but it didn't help and didn't work as expected.

You need the argument tiling. I think this is what you're asking for.
import plotly.graph_objects as go
fig = go.Figure(go.Treemap(
labels = ["DR","1,2","D 1","D 2","O","U",
"3,4,5","P","B","Pe",
"6,7,8","F","C","Pl",
"9","UR",
"10","NA"],
parents = ["", "DR", "1,2","1,2","1,2","1,2","DR","3,4,5","3,4,5","3,4,5",
"DR","6,7,8","6,7,8","6,7,8",
"DR","9",
"DR","10"],
marker_colors = ["lightgrey",
"lightblue", "cornflowerblue", "cornflowerblue","goldenrod","green",
"lightblue","lightgray","chocolate","cadetblue",
"lightblue","grey",'chocolate',"saddlebrown",
"salmon","salmon",
"burlywood","burlywood",
],
branchvalues="total",
tiling = dict(packing='slice-dice')
))

Related

How can i Plot arrows in a existing mplsoccer pitch?

I tried to do the tutorial of McKay Johns on YT (reference to the Jupyter Notebook to see the data (https://github.com/mckayjohns/passmap/blob/main/Pass%20map%20tutorial.ipynb).
I understood everything but I wanted to do a little change. I wanted to change plt.plot(...) with:
plt.arrow(df['x'][x],df['y'][x], df['endX'][x] - df['x'][x], df['endY'][x]-df['y'][x],
shape='full', color='green')
But the problem is, I still can't see the arrows. I tried multiple changes but I've failed. So I'd like to ask you in the group.
Below you can see the code.
## Read in the data
df = pd.read_csv('...\Codes\Plotting_Passes\messibetis.csv')
#convert the data to match the mplsoccer statsbomb pitch
#to see how to create the pitch, watch the video here: https://www.youtube.com/watch?v=55k1mCRyd2k
df['x'] = df['x']*1.2
df['y'] = df['y']*.8
df['endX'] = df['endX']*1.2
df['endY'] = df['endY']*.8
# Set Base
fig ,ax = plt.subplots(figsize=(13.5,8))
# Change background color of base
fig.set_facecolor('#22312b')
# Change color of base inside
ax.patch.set_facecolor('#22312b')
#this is how we create the pitch
pitch = Pitch(pitch_type='statsbomb',
pitch_color='#22312b', line_color='#c7d5cc')
# Set the axes to our Base
pitch.draw(ax=ax)
# X-Achsen => 0 to 120
# Y-Achsen => 80 to 0
# Lösung: Y-Achse invertieren:
plt.gca().invert_yaxis()
#use a for loop to plot each pass
for x in range(len(df['x'])):
if df['outcome'][x] == 'Successful':
#plt.plot((df['x'][x],df['endX'][x]),(df['y'][x],df['endY'][x]),color='green')
plt.scatter(df['x'][x],df['y'][x],color='green')
**plt.arrow(df['x'][x],df['y'][x], df['endX'][x] - df['x'][x], df['endY'][x]-df['y'][x],
shape='full', color='green')** # Here is the problem!
if df['outcome'][x] == 'Unsuccessful':
plt.plot((df['x'][x],df['endX'][x]),(df['y'][x],df['endY'][x]),color='red')
plt.scatter(df['x'][x],df['y'][x],color='red')
plt.title('Messi Pass Map vs Real Betis',color='white',size=20)
It always shows:
The problem is that plt.arrow has default values for head_width and head_length, which are too small for your figure. I.e. it is drawing arrows, the arrow heads are just way too tiny to see them (even if you zoom out). E.g. try something as follows:
import pandas as pd
import matplotlib.pyplot as plt
from mplsoccer.pitch import Pitch
df = pd.read_csv('https://raw.githubusercontent.com/mckayjohns/passmap/main/messibetis.csv')
...
# create a dict for the colors to avoid repetitive code
colors = {'Successful':'green', 'Unsuccessful':'red'}
for x in range(len(df['x'])):
plt.scatter(df['x'][x],df['y'][x],color=colors[df.outcome[x]], marker=".")
plt.arrow(df['x'][x],df['y'][x], df['endX'][x] - df['x'][x],
df['endY'][x]-df['y'][x], color=colors[df.outcome[x]],
head_width=1, head_length=1, length_includes_head=True)
# setting `length_includes_head` to `True` ensures that the arrow head is
# *part* of the line, not added on top
plt.title('Messi Pass Map vs Real Betis',color='white',size=20)
Result:
Note that you can also use plt.annotate for this, passing specific props to the parameter arrowprops. E.g.:
import pandas as pd
import matplotlib.pyplot as plt
from mplsoccer.pitch import Pitch
df = pd.read_csv('https://raw.githubusercontent.com/mckayjohns/passmap/main/messibetis.csv')
...
# create a dict for the colors to avoid repetitive code
colors = {'Successful':'green', 'Unsuccessful':'red'}
for x in range(len(df['x'])):
plt.scatter(df['x'][x],df['y'][x],color=colors[df.outcome[x]], marker=".")
props= {'arrowstyle': '-|>,head_width=0.25,head_length=0.5',
'color': colors[df.outcome[x]]}
plt.annotate("", xy=(df['endX'][x],df['endY'][x]),
xytext=(df['x'][x],df['y'][x]), arrowprops=props)
plt.title('Messi Pass Map vs Real Betis',color='white',size=20)
Result (a bit sharper, if you ask me, but maybe some tweaking with params in plt.arrow can also achieve that):

Loop over geoJson features and add plotly graph in popup html string to each

I've got a geoJson file with a bunch of features which I'm trying to display on an interactive folium map and I'm trying to add a plotly graph that pops up when you click on a polygon. At this moment I already have a folder with all plotly graphs for each city, 'currentWorkingDirectory/graphs/CityName.HTML'. I also have the interactive folium map with the different polygons which I can hover over or click for a popup.
Now I'm having trouble with adding the plotly graphs as a html string to the geojson popups. Could someone help me with this? I'll add a code snippet of the folium map and what I've tried:
import folium
import geopandas as gpd
import codecs
map = folium.Map(location=['51.096246199999996', '4.178629103169916'], tiles="cartodbpositron", zoom_start=9)
geojson_file_df = gpd.read_file('Refgem_geojson.json')
loc = 'Project GEO ICT'
title_html = '''
<h3 align="center" style="font-size:20px"><b>{}</b></h3>
'''.format(loc)
map.get_root().html.add_child(folium.Element(title_html))
g_map = folium.GeoJson(
geojson_file,
name="GeoJson",
style_function=lambda x: {'fillColor': 'orange'}
).add_to(map)
folium.GeoJsonTooltip(
fields=['NISCODE','NAAM', 'OPPERVL'],
aliases=['NISCODE', 'Naam', 'Oppervlakte'],
sticky=False
).add_to(g_map)
folium.GeoJsonPopup(
fields=["NAAM", "Average Prices: " ,"Woonhuis", "Villa", "Studio"],
aliases=["Naam", "Average Prices: ","Woonhuis", "Villa", "Studio"]
).add_to(g_map)
html="""
<iframe src=\"""" + codecs.open("graphs/AARTSELAAR.html", 'r').read() + """\" width="850" height="400" frameborder="0">
"""
popup1 = folium.Popup(folium.Html(html, script=True))
folium.Marker(['51.096246199999996','4.178629103169916'],popup=popup1,icon=folium.Icon( icon='home', prefix='fa')).add_to(map)
map
Here ^ I tried to add the popup to a marker, but that didn't work for me (it's also not really what I want, I want to add the popup to a polygon).
I believe I should make some sort of loop that iterates over all features in the geoJson and adds a popup for every iteration.
You have not provided sample data / geometry so used standard geopandas sample data
this will create popups / tooltips for each geometry. The popup is a plotly figure convented to an embedded URI encoded image. A pie chart of population of country as %age of population of all geometries.
investigated customising GeoJsonPopup() but found no solution
hence create a layer for each feature with it's own popup
import geopandas as gpd
import folium
from statistics import mean
import plotly.express as px
import base64, io
# some geometry
gdf = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres")).loc[
lambda d: d["continent"].eq("Europe") & ~d.bounds.lt(-30).any(axis=1)
]
# create the map, nicely centered and zoomed
bounds = gdf.total_bounds
x = mean([bounds[0], bounds[2]])
y = mean([bounds[1], bounds[3]])
location = (y, x)
m = folium.Map(location=location)
m.fit_bounds([[bounds[1], bounds[0]], [bounds[3], bounds[2]]])
# given need to create a geojson layer for each figure, create
# feature group to contain them
fg = folium.FeatureGroup(name="Europe", show=False)
# create an encocded image of graph...
# change to generate graph you want
def b64image(vals=[1, 2]):
fig = (
px.pie(values=vals)
.update_layout(margin={"l": 0, "r": 0, "t": 0, "b": 0})
.update_traces(texttemplate="%{percent:.0%}")
)
b = io.BytesIO(fig.to_image(format="png", width=80, height=80))
b64 = base64.b64encode(b.getvalue())
return "data:image/png;base64," + b64.decode("utf-8")
tot_pop = gdf["pop_est"].sum()
# create a geojson layer for each feature
for i, r in gdf.iterrows():
# geodataframe of row
gdf_ = gpd.GeoDataFrame(r.to_frame().T, crs=gdf.crs)
# URI encoded image of plotly figure
img_ = f'<img src="{b64image([r["pop_est"], tot_pop-r["pop_est"]])}"/>'
choro_ = folium.GeoJson(
gdf_.__geo_interface__,
name=r["name"],
style_function=lambda x: {"fillColor": "orange"},
tooltip=folium.GeoJsonTooltip(gdf_.drop(columns="geometry").columns.tolist()),
)
# this is the real work around, add to layer which is a choro
folium.Popup(img_).add_to(choro_)
choro_.add_to(fg)
fg.add_to(m)
m

How to add label name to hoverlabel in Plotly parallel category plot?

I want to add the label name to the respective hoverlabels. Eg- The hoverlabel in the image that reads %{label} should instead read Workplace Closing and so on.
There is no custom_data or text property for parallel category plots. I tried using meta by passing it a list of all the labels (meta=[dim[x]['label'] for x in range(len(dim))]), but it displays the entire list on every hoverlabel rather than one element per hoverlabel. I also tried using %{label}, %{labels}, %{dimension} and some more to find any built in functionality, like you would use %{x} or %{y} in a plot with x and y arguments.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
df = {}
dim = []
for idx,var in enumerate(['country','School closing','Workplace closing','Cancel public events',
'Restrictions on gatherings','Close public transport','Stay at home requirements',
'Restrictions on internal movement','International travel controls',
'Public information campaigns']):
df[var] = np.random.randint(4, size=4)
dim.append(go.parcats.Dimension(values=df[var], label=var.title(), categoryorder='category ascending'))
df = pd.DataFrame(df)
fig = go.Figure(data = [go.Parcats(dimensions=[x for x in dim],
line={'color': df.country})])
fig.update_traces(hovertemplate='%{label}')
Help would be appreciated!

How to update histogram based on selection of points (Altair)?

My goal is to update the histogram shown on the right side of 1 based on the selection of points on the left side.
Initially the plot seems to be alright, however once a selection is made the histogram won't be redrawn (altair 3.2.0)
Does anybody know how to do this?
below is the code to recreate the example:
import altair as alt
import pandas as pd
import numpy as np
from random import choice
dates = pd.date_range("1.1.2019", "2.28.2019")
np.random.seed(999)
Y = np.random.normal(0.5, 0.1, len(dates))
features = [choice(["a", "b", "c"]) for i in range(len(dates))]
df = pd.DataFrame({"dates": dates, "Y": Y, "features": features})
base = alt.Chart(df)
area_args = {"opacity": 0.3, "interpolate": "step"}
pts = alt.selection(type="interval", encodings=["x"])
points = (
base.mark_circle()
.encode(alt.X("dates:T"), alt.Y("Y:Q"), color="features:N")
.add_selection(pts)
)
yscale = alt.Scale(domain=(0, 1))
right_hist = (
base.mark_area(**area_args)
.encode(
alt.Y(
"Y:Q", bin=alt.Bin(maxbins=20, extent=yscale.domain), stack=None, title=""
),
alt.X("count()", stack=None, title=""),
alt.Color("features:N"),
)
.transform_filter(pts)
)
(points | right_hist)
edit1: another image to clarify my point #jvp
Solved in the comments as an issue with the OPs setup and how the plots were rendered on their end.

Updating bokeh patches in Jupiter notebook

I am curious about the right way to update the Patches glyph in bokeh.
My minimal example is:
p_blur = figure(x_range=(0, 300), y_range=(0, 300))
source = ColumnDataSource({'xs':[[100,200,300], [10,50,500,400]], 'ys':[[30,150,70], [10,500,50,50]]})
polygons = Patches(xs="xs", ys="ys",fill_color="#fb9a99")
glyph = p_blur.add_glyph(source, polygons)
nb = show(p_blur, notebook_handle=True)
If I now want to update the glyph e.g. by
source1 = ColumnDataSource({'xs':[[10,20,30], [10,50,50,40]], 'ys':[[30,15,70], [10,50,50,50]]})
glyph.data_source = source1
push_notebook( nb )
I don't see any change. However, if I do:
p_blur.renderers.remove(glyph)
glyph = p_blur.add_glyph(source1, polygons)
push_notebook( nb )
The change is reflected. Seems though that the second way is too hacky. Is there more correct way to do this?
Thanks!
You can assign new data to source.data, try this:
source.data = {'xs':[[10,20,30], [10,50,50,40]], 'ys':[[30,15,70], [10,50,50,50]]}
push_notebook(nb)

Categories

Resources