I have a fonction that create a rugby field :
from matplotlib.patches import Rectangle
from matplotlib.patches import Arc
from matplotlib.patches import ConnectionPatch
def draw_pitch_horizontal():
# focus on only half of the pitch
#Pitch Outline & Centre Line
fig=plt.figure() #set up the figures
fig.set_size_inches(10, 7)
ax=fig.add_subplot(1,1,1)
Pitch = Rectangle([0,0], width = 120, height = 70, fill = False)
essai1 = Rectangle([0,0], width = 10, height = 70, fill = False, color='gray',hatch='/')
essai2 = Rectangle([110,0], width = 1070, height = 70, fill = False, color='gray',hatch='/')
en_but1 = ConnectionPatch([10,0], [10,80], "data", "data")
en_but2 = ConnectionPatch([110,0], [110,70], "data", "data")
cinq_metres1 = ConnectionPatch([15,0], [15,70], "data", "data",ls='--',color='gray')
cinq_metres2 = ConnectionPatch([105,0], [105,70], "data", "data",ls='--',color='gray')
midline = ConnectionPatch([60,0], [60,70], "data", "data")
vingtdeux_metres1 = ConnectionPatch([32,0], [32,70], "data", "data")
vingtdeux_metres2 = ConnectionPatch([88,0], [88,70], "data", "data")
dix_metres1 = ConnectionPatch([50,0], [50,70], "data", "data",ls='--',color='gray')
dix_metres2 = ConnectionPatch([70,0], [70,70], "data", "data",ls='--',color='gray')
centreCircle = plt.Circle((60,35),0.5,color="black", fill = True)
poteau1a = plt.Circle((10,32.2),0.5,color="black", fill = True)
poteau1b = plt.Circle((10,37.8),0.5,color="black", fill = True)
poteau2a = plt.Circle((110,32.2),0.5,color="black", fill = True)
poteau2b = plt.Circle((110,37.8),0.5,color="black", fill = True)
element = [essai1, essai2, Pitch, en_but1, en_but2, cinq_metres1, cinq_metres2, midline, vingtdeux_metres1,
vingtdeux_metres2,centreCircle,poteau1a,poteau1b,poteau2a,poteau2b,dix_metres1,dix_metres2]
for i in element:
ax.add_patch(i)
return fig,ax
I created a function with a team in argument and the function plots the average start and end of a sequence on a pitch thanks to the previous function.
def plot_sequence_moyenne(TEAM):
df_team = df_summary[((df_summary.hometeam == TEAM)|(df_summary.awayteam == TEAM))].reset_index(drop=True)
df_timeline = pd.concat([prg.Match(df_team['url'][i]).timeline for i in range(len(df_team))],axis=0)
df_timeline_team = df_timeline[df_timeline.team_name == TEAM].reset_index(drop=True)
print(df_timeline_team.x_coord.mean())
print(df_timeline_team.x_coord_end.mean())
(fig,ax) = draw_pitch_horizontal() #overlay our different objects on the pitch
plt.ylim(-2, 72)
plt.xlim(-0.2, 120.4)
ax = plt.gca()
ax.set_facecolor('#c2dff4')
plt.axis('off')
zone_sequence = Rectangle([df_timeline_team.x_coord.mean(),0], width = (df_timeline_team.x_coord_end.mean()-df_timeline_team.x_coord.mean()), height = 70, fill = True, color='darkseagreen',alpha=0.7,edgecolor='black')
ax.add_patch(zone_sequence)
plt.title("TEAM 1" + " - Séquences moyennes")
plt.show()
This is what I obtain with this :
However, I would like to know how to create a huge subplot in order to have a graph with 10 subplots of pitch if I have ten teams for exemple.
Thanks in advance for your help and your time
Related
I am working on a plotter for Finite Element Method solutions. I decided to use the Plotly library because of the carpet plots. I have my data to plot and this is my result:
Flow over NACA0012
Each element is represented as a Carpet, and for each carpet the solution is shown as a Countourcarpet. Everything is in place, but the rendering is too slow and the interactive interface is therefore nearly useless. Is there a way to enhance the performance of the rendering? I have read about different renderers in Plotly, but the plot just does not open. Is there a a way to speed up the rendering? Surely I will have to manage larger dataset. In this example I am using 740 carpets.
These are the Contourcarpet settings:
fig.add_trace(go.Contourcarpet(
a = a,
b = b,
z = u, # Sution correspondent at (a,b) parametric location
showlegend = showLegendFlag,
name = "Density",
legendgroup = "Density",
autocolorscale = False,
colorscale = "Inferno",
autocontour = False,
carpet = str(e), # The carpet on which to plot the solution is
# referenced as a string number
contours = dict(
start = start1, # Min value
end = end1, # Max value
size = abs(end1-start1) / countour_number, # Plot colour discretization
showlines = False
),
line = dict(
smoothing = 0
),
colorbar = dict(
len = 0.4,
y = 0.25
)
))
And these are the layout settings:
fig.update_layout(
plot_bgcolor="#FFF",
yaxis = dict(
zeroline = False,
range = [-1.800,1.800],
showgrid = False
),
dragmode = "pan",
height = 700,
xaxis = dict(
zeroline = False,
scaleratio = 1,
scaleanchor = 'y',
range = [-3.800,3.800],
showgrid = False
),
title = "Flow over NACA 0012",
hovermode = "closest",
margin = dict(
r = 80,
b = 40,
l = 40,
t = 80
),
width = 900
)
fig.show()
I want to show how many mma fighters each country has.This code is works. But USA has not any color.(USA has the greatest value)How can I fix it?
https://www.kaggle.com/firaterdemdogan/mma-practice
data = [ dict(
type = 'choropleth',
locations = index,
locationmode = 'country names',
z = values,
text = index,
#colorscale = [[0,'rgb(255, 255, 255)'],[1,'rgb(56, 142, 60)']],
#colorscale = [[0,'rgb(255, 255, 255)'],[1,'rgb(220, 83, 67)']],
colorscale = [[0,"rgb(5, 10, 172)"],[0.85,"rgb(40, 60, 190)"],[0.9,"rgb(70, 100, 245)"],\
[0.94,"rgb(90, 120, 245)"],[0.97,"rgb(106, 137, 247)"],[1,"rgb(220, 220, 220)"]],
autocolorscale = False,
reversescale = True,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
autotick = False,
tickprefix = '',
title = 'Countries Of Fighters'),
) ]
layout = dict(
title = 'Countries Of Fighters',
geo = dict(
showframe = False,
showcoastlines = True,
projection = dict(
type = 'Mercator'
)
)
)
fig = dict( data=data, layout=layout )
py.iplot( fig, validate=False)
fundamentally country is not normalised to standard values
have used https://restcountries.eu/#api-endpoints-name to get country ISO standard details. It's not quick but it works
now mma dataframe has two additional columns, isocountry - standardised name iso3code - standard 3 character code
there are still some countries that do not normalize
have not focused on formatting figures, just simple ones to demonstrate it works when country data has been standardised
import kaggle.cli
import sys, requests
import pandas as pd
from pathlib import Path
from zipfile import ZipFile
import plotly.express as px
# download data set
# https://www.kaggle.com/binduvr/pro-mma-fighters
sys.argv = [
sys.argv[0]
] + "datasets download binduvr/pro-mma-fighters".split(
" "
)
kaggle.cli.main()
zfile = ZipFile("pro-mma-fighters.zip")
dfs = {f.filename:pd.read_csv(zfile.open(f)) for f in zfile.infolist() }
mma = dfs['pro_mma_fighters.csv']
# country names are not clean...
def iso_dtl(c):
cmap = {}
res = requests.get(f"https://restcountries.eu/rest/v2/name/{c}")
if res.status_code==200:
if len(res.json())==1:
cmap = {"isocountry":res.json()[0]["name"], "iso3code":res.json()[0]["alpha3Code"]}
else:
df = pd.DataFrame(res.json()).sort_values("population", ascending=False).reset_index()
cmap = {"isocountry":df.loc[0,"name"], "iso3code":df.loc[0, "alpha3Code"]}
else:
cmap = {"isocountry":c, "iso3code":""}
return {**{"country":c}, **cmap}
dfcountry = pd.DataFrame([iso_dtl(c) for c in mma["country"].unique()])
mma = mma.merge(dfcountry, on="country")
px.bar(mma["isocountry"].value_counts()[:5]).show()
px.choropleth(mma["isocountry"].value_counts().reset_index(),
locations="index", locationmode="country names", color="isocountry")
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]
)
I'm using Plotly's Python interface to generate a network. I've managed to create a network with my desired nodes and edges, and to control the size of the nodes.
I am desperately looking for help on how to do the following:
add node labels
add edge labels according to a list of weights
control the edge line width according to a list of weights
All this without using the "hovering" option, as it has to go in a non-interactive paper. I'd greatly appreciate any help! Plotly's output |
In case this fails, the figure itself |
matrix.csv
This is my code (most is copy-pasted from the Plotly tutorial for Networkx):
import pandas as pd
import plotly.plotly as py
from plotly.graph_objs import *
import networkx as nx
matrix = pd.read_csv("matrix.csv", sep = "\t", index_col = 0, header = 0)
G = nx.DiGraph()
# add nodes:
G.add_nodes_from(matrix.columns)
# add edges:
edge_lst = [(i,j, matrix.loc[i,j])
for i in matrix.index
for j in matrix.columns
if matrix.loc[i,j] != 0]
G.add_weighted_edges_from(edge_lst)
# create node trace:
node_trace = Scatter(x = [], y = [], text = [], mode = 'markers',
marker = Marker(
showscale = True,
colorscale = 'YIGnBu',
reversescale = True,
color = [],
size = [],
colorbar = dict(
thickness = 15,
title = 'Node Connections',
xanchor = 'left',
titleside = 'right'),
line = dict(width = 2)))
# set node positions
pos = nx.spring_layout(G)
for node in G.nodes():
G.node[node]['pos']= pos[node]
for node in G.nodes():
x, y = G.node[node]['pos']
node_trace['x'].append(x)
node_trace['y'].append(y)
# create edge trace:
edge_trace = Scatter(x = [], y = [], text = [],
line = Line(width = [], color = '#888'),
mode = 'lines')
for edge in G.edges():
x0, y0 = G.node[edge[0]]['pos']
x1, y1 = G.node[edge[1]]['pos']
edge_trace['x'] += [x0, x1, None]
edge_trace['y'] += [y0, y1, None]
edge_trace['text'] += str(matrix.loc[edge[0], edge[1]])[:5]
# size nodes by degree
deg_dict = {deg[0]:int(deg[1]) for deg in list(G.degree())}
for node, degree in enumerate(deg_dict):
node_trace['marker']['size'].append(deg_dict[degree] + 20)
fig = Figure(data = Data([edge_trace, node_trace]),
layout = Layout(
title = '<br>AA Substitution Rates',
titlefont = dict(size = 16),
showlegend = True,
margin = dict(b = 20, l = 5, r = 5, t = 40),
annotations = [dict(
text = "sub title text",
showarrow = False,
xref = "paper", yref = "paper",
x = 0.005, y = -0.002)],
xaxis = XAxis(showgrid = False,
zeroline = False,
showticklabels = False),
yaxis = YAxis(showgrid = False,
zeroline = False,
showticklabels = False)))
py.plot(fig, filename = 'networkx')
So
1. The solution to this is relative easy, you create a list with the node ids and you set it in the text attribute of the scatter plot. Then you set the mode as "markers+text" and you're done.
2. This is a little bit more tricky. You have to calculate the middle of each line and create a list of dicts including the line's middle position and weight. Then you add set as the layout's annotation.
3. This is too compicated to be done using plotly IMO. As for now I am calculating the position of each node using networkx spring_layout function. If you'd want to set the width of each line based on its weight you would have to modify the position using a function that takes into account all the markers that each line is attached to.
Bonus I give you the option to color each of the graph's components differently.
Here's a (slightly modified) function I made a while ago that does 1 and 2:
import pandas as pd
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import networkx as nx
def scatter_plot_2d(G, folderPath, name, savePng = False):
print("Creating scatter plot (2D)...")
Nodes = [comp for comp in nx.connected_components(G)] # Looks for the graph's communities
Edges = G.edges()
edge_weights = nx.get_edge_attributes(G,'weight')
labels = [] # names of the nodes to plot
group = [] # id of the communities
group_cnt = 0
print("Communities | Number of Nodes")
for subgroup in Nodes:
group_cnt += 1
print(" %d | %d" % (group_cnt, len(subgroup)))
for node in subgroup:
labels.append(int(node))
group.append(group_cnt)
labels, group = (list(t) for t in zip(*sorted(zip(labels, group))))
layt = nx.spring_layout(G, dim=2) # Generates the layout of the graph
Xn = [layt[k][0] for k in list(layt.keys())] # x-coordinates of nodes
Yn = [layt[k][1] for k in list(layt.keys())] # y-coordinates
Xe = []
Ye = []
plot_weights = []
for e in Edges:
Xe += [layt[e[0]][0], layt[e[1]][0], None]
Ye += [layt[e[0]][1], layt[e[1]][1], None]
ax = (layt[e[0]][0]+layt[e[1]][0])/2
ay = (layt[e[0]][1]+layt[e[1]][1])/2
plot_weights.append((edge_weights[(e[0], e[1])], ax, ay))
annotations_list =[
dict(
x=plot_weight[1],
y=plot_weight[2],
xref='x',
yref='y',
text=plot_weight[0],
showarrow=True,
arrowhead=7,
ax=plot_weight[1],
ay=plot_weight[2]
)
for plot_weight in plot_weights
]
trace1 = go.Scatter( x=Xe,
y=Ye,
mode='lines',
line=dict(color='rgb(90, 90, 90)', width=1),
hoverinfo='none'
)
trace2 = go.Scatter( x=Xn,
y=Yn,
mode='markers+text',
name='Nodes',
marker=dict(symbol='circle',
size=8,
color=group,
colorscale='Viridis',
line=dict(color='rgb(255,255,255)', width=1)
),
text=labels,
textposition='top center',
hoverinfo='none'
)
xaxis = dict(
backgroundcolor="rgb(200, 200, 230)",
gridcolor="rgb(255, 255, 255)",
showbackground=True,
zerolinecolor="rgb(255, 255, 255)"
)
yaxis = dict(
backgroundcolor="rgb(230, 200,230)",
gridcolor="rgb(255, 255, 255)",
showbackground=True,
zerolinecolor="rgb(255, 255, 255)"
)
layout = go.Layout(
title=name,
width=700,
height=700,
showlegend=False,
plot_bgcolor="rgb(230, 230, 200)",
scene=dict(
xaxis=dict(xaxis),
yaxis=dict(yaxis)
),
margin=dict(
t=100
),
hovermode='closest',
annotations=annotations_list
, )
data = [trace1, trace2]
fig = go.Figure(data=data, layout=layout)
plotDir = folderPath + "/"
print("Plotting..")
if savePng:
plot(fig, filename=plotDir + name + ".html", auto_open=True, image = 'png', image_filename=plotDir + name,
output_type='file', image_width=700, image_height=700, validate=False)
else:
plot(fig, filename=plotDir + name + ".html")
The d3graph library provides the functionalities you want.
pip install d3graph
I downloaded your data and imported it for demonstration:
# Import data
df = pd.read_csv('data.csv', index_col=0)
# Import library
from d3graph import d3graph
# Convert your Pvalues. Note that any edge is set when a value in the matrix is >0. The edge width is however based on this value. A conversion is therefore useful when you work with Pvalues.
df[df.values==0]=1
df = -np.log10(df)
# Increase some distance between edges. Maybe something like this.
df = (np.exp(df)-1)/10
# Make the graph with default settings
d3 = d3graph()
# Make the graph by setting some parameters
d3.graph(df)
# Set edge properties
d3.set_edge_properties(directed=True)
# Set node properties
d3.set_node_properties(color=df.columns.values, size=size, edge_size=10, edge_color='#000000', cmap='Set2')
This will result in an interactive network graph. Two screenshots: one with the default settings and the one with tweaked settings. More examples can be found here.
I am trying to create plots in python using bokeh that allow dynamic visualization of data in bins. It's worth knowing that I am relatively new to python, very new to bokeh, and I know ZERO javascript. I have consulted this:
Link a Span or Cursor in between plots with Bokeh in Python
and this:
http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html
but am having trouble implementing the necessary parts of each. Here is my code prior to adding the requested capabilities:
from bokeh.layouts import column, widgetbox
from bokeh.models.widgets import Slider
from bokeh.models import Span, CustomJS
output_file('Raw_Spectra_and_Spillover_Data.html')
# widgets for bin setup
Pix1_LowLow = Slider(start = self.StartDAC, end = self.EndDAC, value = 129, step = 1, title = 'Pixel-1 - Low Bin - Low Thresh')
Pix1_LowHigh = Slider(start = self.StartDAC, end = self.EndDAC, value = 204, step = 1, title = 'Pixel-1 - Low Bin - High Thresh')
Pix1_HighLow = Slider(start = self.StartDAC, end = self.EndDAC, value = 218, step = 1, title = 'Pixel-1 - High Bin - Low Thresh')
Pix1_HighHigh = Slider(start = self.StartDAC, end = self.EndDAC, value = 500, step = 1, title = 'Pixel-1 - High Bin - High Thresh')
plot1spect = figure(width=700, height=250, title='pixel-1 Spectrum')
plot1spect.line(self.SpectDACvals1[0], self.SpectrumData1[0], line_width=2)
plot1spect_LowLowSpan = Span(location=Pix1_LowLow.value, dimension = 'height')
plot1spect_LowHighSpan = Span(location=Pix1_LowHigh.value, dimension = 'height')
plot1spect_HighLowSpan = Span(location=Pix1_HighLow.value, dimension = 'height')
plot1spect_HighHighSpan = Span(location=Pix1_HighHigh.value, dimension = 'height')
plot1spect.renderers.extend([plot1spect_LowLowSpan, plot1spect_LowHighSpan, plot1spect_HighLowSpan, plot1spect_HighHighSpan])
plot1spill = figure(width=700, height=250, title='pixel-1 Spillover')
plot1spill.line(self.SpillDACvals1[0], self.SpillData1[0], line_width=2)
plot1spill_LowLowSpan = Span(location=Pix1_LowLow.value, dimension = 'height')
plot1spill_LowHighSpan = Span(location=Pix1_LowHigh.value, dimension = 'height')
plot1spill_HighLowSpan = Span(location=Pix1_HighLow.value, dimension = 'height')
plot1spill_HighHighSpan = Span(location=Pix1_HighHigh.value, dimension = 'height')
plot1spill.renderers.extend([plot1spill_LowLowSpan, plot1spill_LowHighSpan, plot1spill_HighLowSpan, plot1spill_HighHighSpan])
show(row(plot1spect,plot1spill, widgetbox(Pix1_LowLow, Pix1_LowHigh, Pix1_HighLow, Pix1_HighHigh)))
This code gives me this:
If someone can show me how get Pix1_LowLow slider to dynamically control the location of plot1spect_LowLowSpan, then I can extend the technique to the other sliders and spans. Many thanks in advance!
python 3.5.2 - bokeh 12.0
Here is a minimal complete example. Note that the recommended way to add annotations like Span is with plot.add_layout as shown below:
from bokeh.layouts import row, widgetbox
from bokeh.models import Slider, Span, CustomJS
from bokeh.plotting import figure, output_file, show
slider = Slider(start=0, end=10, value=3, step=0.1, title='Slider')
plot = figure(width=700, height=250, x_range=(0,10), y_range=(-1, 1))
span = Span(location=slider.value, dimension='height')
plot.add_layout(span)
callback = CustomJS(args=dict(span=span), code="""
span.location = cb_obj.value
""")
slider.js_on_change('value', callback)
output_file('span_slider.html')
show(row(plot, widgetbox(slider)))
Thanks to #bigreddot for providing the answer. This is the code that implemented my solution specifically... Now how to do this programmatically for 128 data files... hmmmm..
from bokeh.layouts import row, widgetbox
from bokeh.models import Span, CustomJS, Slider
output_file('Raw_Spectra_and_Spillover_Data.html')
# widgets for bin setup
Pix1_LowLow = Slider(start = self.StartDAC, end = self.EndDAC, value = 129, step = 1, title = 'Pixel-1 - Low Bin - Low Thresh')
Pix1_LowHigh = Slider(start = self.StartDAC, end = self.EndDAC, value = 204, step = 1, title = 'Pixel-1 - Low Bin - High Thresh')
Pix1_HighLow = Slider(start = self.StartDAC, end = self.EndDAC, value = 218, step = 1, title = 'Pixel-1 - High Bin - Low Thresh')
Pix1_HighHigh = Slider(start = self.StartDAC, end = self.EndDAC, value = 500, step = 1, title = 'Pixel-1 - High Bin - High Thresh')
plot1spect = figure(width=700, height=250, title='pixel-1 Spectrum')
plot1spect.line(self.SpectDACvals1[0], self.SpectrumData1[0], line_width=2)
plot1spect_LowLowSpan = Span(location=Pix1_LowLow.value, dimension = 'height')
plot1spect.add_layout(plot1spect_LowLowSpan)
plot1spect_LowHighSpan = Span(location=Pix1_LowHigh.value, dimension = 'height')
plot1spect.add_layout(plot1spect_LowHighSpan)
plot1spect_HighLowSpan = Span(location=Pix1_HighLow.value, dimension = 'height')
plot1spect.add_layout(plot1spect_HighLowSpan)
plot1spect_HighHighSpan = Span(location=Pix1_HighHigh.value, dimension = 'height')
plot1spect.add_layout(plot1spect_HighHighSpan)
#plot1spect.renderers.extend([plot1spect_LowLowSpan, plot1spect_LowHighSpan, plot1spect_HighLowSpan, plot1spect_HighHighSpan])
plot1spill = figure(width=700, height=250, title='pixel-1 Spillover')
plot1spill.line(self.SpillDACvals1[0], self.SpillData1[0], line_width=2)
plot1spill_LowLowSpan = Span(location=Pix1_LowLow.value, dimension = 'height')
plot1spill.add_layout(plot1spill_LowLowSpan)
plot1spill_LowHighSpan = Span(location=Pix1_LowHigh.value, dimension = 'height')
plot1spill.add_layout(plot1spill_LowHighSpan)
plot1spill_HighLowSpan = Span(location=Pix1_HighLow.value, dimension = 'height')
plot1spill.add_layout(plot1spill_HighLowSpan)
plot1spill_HighHighSpan = Span(location=Pix1_HighHigh.value, dimension = 'height')
plot1spill.add_layout(plot1spill_HighHighSpan)
#plot1spill.renderers.extend([plot1spill_LowLowSpan, plot1spill_LowHighSpan, plot1spill_HighLowSpan, plot1spill_HighHighSpan])
Pix1_LowLow.callback = CustomJS(args=dict(span1 = plot1spect_LowLowSpan,
span2 = plot1spill_LowLowSpan,
slider = Pix1_LowLow),
code = """span1.location = slider.value; span2.location = slider.value""")
Pix1_LowHigh.callback = CustomJS(args=dict(span1 = plot1spect_LowHighSpan,
span2 = plot1spill_LowHighSpan,
slider = Pix1_LowHigh),
code = """span1.location = slider.value; span2.location = slider.value""")
Pix1_HighLow.callback = CustomJS(args=dict(span1 = plot1spect_HighLowSpan,
span2 = plot1spill_HighLowSpan,
slider = Pix1_HighLow),
code = """span1.location = slider.value; span2.location = slider.value""")
Pix1_HighHigh.callback = CustomJS(args=dict(span1 = plot1spect_HighHighSpan,
span2 = plot1spill_HighHighSpan,
slider = Pix1_HighHigh),
code = """span1.location = slider.value; span2.location = slider.value""")
Here is a repeat of the plots, but now each slider manipulates the respective span in both plots...