Selecting multiple buttons at once in a plotly graph - python

I've been able to figure out how to update a Plotly graph with new data using buttons. That is, if I select a certain button (say X1 in the example below), the plot will change so that it'll plot that variable, etc.
However, I want to be able to select multiple buttons at once. For example, I want to be able to select X1 and X2 and plot both on the chart.
I haven't been able to make any progress on this, so I was hoping someone could provide some clues on a way forward.
import plotly
import plotly.graph_objs as go
import numpy as np
import pandas as pd
plotly.offline.init_notebook_mode(connected=True)
x0 = np.linspace(0,99,100)
y0 = np.random.normal(2, 0.4, 100)
y1 = np.random.normal(2, 0.4, 100)
y2 = np.random.normal(2, 0.4, 100)
trace0 = go.Scatter(x = x0, y = y0)
trace1 = go.Scatter(x = x0, y = y1, visible = False)
trace2 = go.Scatter(x = x0, y = y2, visible = False)
data = [trace0, trace1, trace2]
button1 = dict(label = 'X0',
method = 'update',
args = [{'visible': [True, False, False]}])
button2 = dict(label = 'X1',
method = 'update',
args = [{'visible': [False, True, False]}])
button3 = dict(label = 'X2',
method = 'update',
args = [{'visible': [False, False, True]}])
updatemenus = list([
dict(type="buttons", active = 0,
buttons = [button1, button2, button3], yanchor = 'top')])
layout = dict(title='Chart', showlegend=False,
updatemenus=updatemenus)
fig = dict(data=data, layout=layout)
plotly.offline.iplot(fig)

Related

How to make a plotly graph wider and higher? [duplicate]

I have made a scatter plot using matplotlib and Plotly. I want the height, width and the markers to be scatter as in matplotlib plot. Please see the attached plots.
I used the following code in Plotly
import plotly
import plotly.plotly as py
from plotly.graph_objs import Scatter
import plotly.graph_objs as go
trace1 = go.Scatter(
x=x1_tsne, # x-coordinates of trace
y=y1_tsne, # y-coordinates of trace
mode='markers ', # scatter mode (more in UG section 1)
text = label3,
opacity = 1,
textposition='top center',
marker = dict(size = 25, color = color_4, symbol = marker_list_2, line=dict(width=0.5)),
textfont=dict(
color='black',
size=18, #can change the size of font here
family='Times New Roman'
)
)
layout = {
'xaxis': {
'showticklabels': False,
'showgrid': False,
'zeroline': False,
'linecolor':'black',
'linewidth':2,
'mirror':True,
'autorange':False,
'range':[-40, 40][![enter image description here][1]][1]
},
'yaxis': {
'showticklabels': False,
'showgrid': False,
'zeroline': False,
'linecolor':'black',
'linewidth':2,
'mirror':True,
'autorange':False,
'range':[-40, 40]
}
}
data = [trace1]
fig = go.Figure(
data= data,
layout= layout)
py.iplot(fig)
I try to tune the range but did not help. In addition, I use Autorange that did not help. Could you please help me with this.
I want the image as
##update: This can be done by the following code. I am updating this in the question:
trace1 = go.Scatter(
x=x1_tsne, # x-coordinates of trace
y=y1_tsne, # y-coordinates of trace
mode='markers +text ', # scatter mode (more in UG section 1)
text = label3,
opacity = 1,
textposition='top center',
marker = dict(size = 25, color = color_4, symbol = marker_list_2, line=dict(width=0.5)),
textfont=dict(
color='black',
size=18, #can change the size of font here
family='Times New Roman'
)
)
data = [trace1]
layout = go.Layout(
autosize=False,
width=1000,
height=1000,
xaxis= go.layout.XAxis(linecolor = 'black',
linewidth = 1,
mirror = True),
yaxis= go.layout.YAxis(linecolor = 'black',
linewidth = 1,
mirror = True),
margin=go.layout.Margin(
l=50,
r=50,
b=100,
t=100,
pad = 4
)
)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig, filename='size-margins')
Have you considered to use
fig.update_layout(
autosize=False,
width=800,
height=800,)
and eventually reduce the size of your marker?
UPDATE
Full Code
import plotly.graph_objs as go
trace1 = go.Scatter(
x=x1_tsne, # x-coordinates of trace
y=y1_tsne, # y-coordinates of trace
mode='markers +text ', # scatter mode (more in UG section 1)
text = label3,
opacity = 1,
textposition='top center',
marker = dict(size = 12, color = color_4, symbol = marker_list_2, line=dict(width=0.5)),
textfont=dict(
color='black',
size=18, #can change the size of font here
family='Times New Roman'
)
)
data = [trace1]
layout = go.Layout(
autosize=False,
width=1000,
height=1000,
xaxis= go.layout.XAxis(linecolor = 'black',
linewidth = 1,
mirror = True),
yaxis= go.layout.YAxis(linecolor = 'black',
linewidth = 1,
mirror = True),
margin=go.layout.Margin(
l=50,
r=50,
b=100,
t=100,
pad = 4
)
)
fig = go.Figure(data=data, layout=layout)

Python/ploty - How to reverse axis direction in a Parallel Coordinates plot?

I'm trying to make a Parallel Coordinates plot with plotly.graph_objs
Here is my code.
'''
import plotly.graph_objs as go
layout = go.Layout( autosize=False, width=800, height=600)
fig = go.Figure(data =
go.Parcoords(
dimensions = list([
dict(range = [1,9],
label = 'col1', values = df.col1),
dict(range = [1,9],
label = 'col2', values = df.col2),
dict(range = [1,9],
label = 'col3', values = df.col3)
])
), layout = layout
)
fig.show()
'''
This graph works, but I need to reversed y-axis.
Here 3 are what i tried
fig.update_layout(yaxis=dict(autorange="reversed"))
fig.update_yaxes(autorange='reversed')
fig['layout']['yaxis']['autorange'] = 'reversed'
But none of these works.
How would I have a reversed graph?
If I understood correctly you could just reverse the range parameter:
import plotly.graph_objs as go
layout = go.Layout( autosize=False, width=800, height=600)
fig = go.Figure(data =
go.Parcoords(
# Reversed ranges.
dimensions = list([
dict(range = [9, 1],
label = 'col1', values = df.col1),
dict(range = [9, 1],
label = 'col2', values = df.col2),
dict(range = [9, 1],
label = 'col3', values = df.col3)
])
), layout = layout
)
fig.show()
'''
Before (fake data):
After:

Is there a way to use ONE Plotly drop down button to interact with data as well as a plot?

I am trying to showcase a scatter plot as well as the data for the the plot side by side in jupyter. I wish to add a plotly button (dropdown) that will show the filtered data as well as the corresponding scatter plot. Is this possible WITHOUT USING ipywidgets, using plotly dropdowns?
I was able to build two separate plots for data and scatter plot with dropdowns but cannot combine them together. Following is the code I tried. Here the dropdown only interacts with the table, the scatter plot is not updating.
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import pandas as pd
df = px.data.iris()
species = sorted(set(df['species']))
#fig=go.Figure()
default_species = "setosa"
df_default = df[df['species']==default_species]
fig = make_subplots(
rows=1, cols=2,
shared_xaxes=True,
horizontal_spacing=0.02,
specs=[[{"type": "scatter"},{"type": "table"}]]
)
fig.add_trace(
go.Scatter(
x=df_default["sepal_length"],
y=df_default["sepal_width"],
mode="markers"
),
row=1, col=1
)
fig.add_trace(
go.Table(
header=dict(
values=["sepal<br>length","sepal<br>length","petal<br>length","petal<br>width","species","species<br>id"],
font=dict(size=10),
align="left"
),
cells=dict(
values=[df_default[k].tolist() for k in df_default.columns],
align = "left")
),
row=1, col=2
)
buttons = []
for s in species:
s_data = df[df['species']==s]
buttons.append(dict(
method='restyle',
label=s,
visible=True,
args=[
{'values':[["sepal<br>length","sepal<br>length","petal<br>length","petal<br>width","species","species<br>id"]],
'cells':[dict(values=[s_data[k].tolist() for k in s_data.columns],align = "left")]},
{'y':[s_data["sepal_width"]],
'x':[s_data['sepal_length']],
'type':'scatter',
'mode':'markers'}
]
))
#fig.update_layout(width=1500, height=500)
# some adjustments to the updatemenus
updatemenu = []
your_menu = dict()
updatemenu.append(your_menu)
updatemenu[0]['buttons'] = buttons
updatemenu[0]['direction'] = 'down'
updatemenu[0]['showactive'] = True
updatemenu[0]['active'] = species.index(default_species)
updatemenu[0]['x'] = 0
updatemenu[0]['xanchor'] = 'left'
updatemenu[0]['y'] = 1.2
updatemenu[0]['yanchor'] = 'top'
# add dropdown menus to the figure
fig.update_layout(showlegend=False,
updatemenus=updatemenu,
xaxis_title="Sepal_Length",
yaxis_title="Sepal_Width"
)
fig.show()
I do not want to use ipywidgets.
The code that worked for me: Editing hope it helps some one
## Create Combination of Scatter Plot and Plotly Tables with interactive Static HTML
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
df = px.data.iris()
species_list = sorted(set(df['species']))
# --- function ---
def make_multi_plot(df1, item_list):
fig = make_subplots(rows=1,
cols=2,
#shared_xaxes=True,
#vertical_spacing=0.2,
specs = [[{}, {"type": "table"}]]
)
for item_id in item_list:
#print('item_id:', item_id)
trace1 = go.Scatter(
x=df1.loc[df1.species.isin([item_id])].sepal_length,
y=df1.loc[df1.species.isin([item_id])].sepal_width,
mode='markers',
name = str(item_id)
)
fig.append_trace(trace1, 1, 1)
trace2 = go.Table(
columnorder = [1,2,3,4,5,6],
columnwidth = [2,2,2,2,4,2],
header=dict(
values = ["sepal<br>length","sepal<br>length","petal<br>length","petal<br>width","species","species<br>id"],
font = dict(size=10),
align = "left"
),
cells = dict(
values = [df1[df1.species.isin([item_id])][k].tolist() for k in df1[df1.species.isin([item_id])].columns[:]],
align = "left"
)
)
fig.append_trace(trace2,1,2)
Ld = len(fig.data)
#print(Ld)
Lc = len(item_list)
for k in range(2, Ld):
fig.update_traces(visible=False, selector=k)
def create_layout_button(k, item_id):
#print(k, item_id)
visibility = [False]*2*Lc
for tr in [2*k, 2*k+1]:
visibility[tr] = True
#print(visibility)
return dict(label = item_id,
method = 'restyle',
args = [{'visible': visibility,
'title': item_id
}])
#updatemenu[0]['x'] = 0
#updatemenu[0]['xanchor'] = 'left'
#updatemenu[0]['y'] = 1.2
#updatemenu[0]['yanchor'] = 'top'
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
buttons=[create_layout_button(k, item_id) for k, item_id in enumerate(item_list)],
x=0.5,
y=1.3
)],
#title='Model Raporu',
#template='plotly_dark',
#height=800
xaxis_title="Sepal_Length",
yaxis_title="Sepal_Width"
)
fig.show()
# --- main ---
make_multi_plot(df1=df, item_list=species_list)

How to update y-axis column for multiple figures using dropdown?

I have a pandas dataframe with multiple columns. I have multiple scatter plots where the x-axis is a different column from the dataframe while the y-axis is the same across all the figures. I want to be able to change the y-axis based on the selected value in a dropwdown button. I'm new using bokeh, I haven't be able to figure how exactly to build the callback function for this case. Any help is much appreciated!
def callback(attr, old, new):
y_axis = select.value
source.data = {'y' : y_axis}
p1.circle.y = y_axis
columns = {'TC2B':'Temperature 2B','TC1B':'Temperature 1B', 'PCV-2006 Pos':'2006 valve position',
'TCV-2008 DownS Ctrl PV':'Down Stream'}
select = Select(title='Y axis:', value='TCV-2008 Pos', options=list(columns.keys()))
y_select = select.value
color_select = 'TC2B'#'PCV-2006 Pos'
#source = ColumnDataSource(data={'y':y_select})
source = ColumnDataSource(data)
p1 = figure(x_axis_label='Temperature 1B', y_axis_label=columns[y_select])
p1.circle(x='TC1B',
y=y_select,
source=source)
p2 = figure(x_axis_label='2006 valve position', y_axis_label=columns[y_select])
p2.circle(x='PCV-2006 Pos',
y=y_select,
source=source)
p3 = figure(x_axis_label='Down Stream', y_axis_label=columns[y_select])
p3.circle(x='TCV-2008 DownS Ctrl PV',
y=y_select,
source=source)
p1.y_range = p2.y_range = p3.y_range
select.on_change('value', callback)
layout = column(select, row(column(p1, p2, p3)))
curdoc().add_root(layout)
You can recreate the plots again from scratch in the onchange callback and update the layout content. Take a look at this:
from bokeh.models import ColumnDataSource, Select
from bokeh.plotting import figure
from bokeh.layouts import column, row
from bokeh.io import curdoc
import numpy as np
N = 3000
data = {
'X': np.random.random(size=N) * 100,
'Y': np.random.random(size=N) * 100,
'Z': np.random.random(size=N) * 100,
'A': np.random.random(size=N) * 100,
'B': np.random.random(size=N) * 100,
'C': np.random.random(size=N) * 100,
}
select = Select(title='Y axis:', value='Y', options=list(data.keys()))
source = ColumnDataSource(data=data)
p1 = figure(
x_axis_label='X',
y_axis_label='Y'
)
c1 = p1.circle(
x='X',
y='Y',
source=source
)
p2 = figure(
x_axis_label='A',
y_axis_label='Y'
)
c2 = p2.circle(
x='A',
y='Y',
source=source
)
p1.y_range = p2.y_range
r = row(children=[p1, p2])
layout = column(select, r)
def onchange_value(attr, old, new):
p1 = p2 = None
p1 = figure(
x_axis_label='X',
y_axis_label=new
)
p1.circle(
x='X',
y=new,
source=source
)
p2 = figure(
x_axis_label='A',
y_axis_label=new
)
p2.circle(
x='A',
y=new,
source=source
)
r.children = [p1, p2]
select.on_change('value', onchange_value)
curdoc().add_root(layout)
Note: You can also read this other question about updating the layout dynamically to get more ideas

Bokeh: Widget to Show/Hide Figures

Looking to do something along the lines of a UI as here: Bokeh: Using Checkbox widget to hide and show plots wherein I can selectively show/hide the whole figure in a column of figures. A drop down menu (OptionMenu with multiple selections) where I could select which plots showed up (assuming I could name the figures) would be preferable.
I am not familiar with JS, any guidance? (Thanks in advance)
I'd hope that the image wouldn't be visible anymore and the next figure would jump up like so:
eg:
I have multiple figures in a column generated as:
from bokeh.io import output_file, show
from bokeh.layouts import column
from bokeh.plotting import figure
output_file("layout.html")
x = list(range(11))
y0 = x
y1 = [10 - i for i in x]
y2 = [abs(i - 5) for i in x]
# create a new plot
s1 = figure(plot_width=250, plot_height=250, title=None)
s1.circle(x, y0, size=10, color="navy", alpha=0.5)
# create another one
s2 = figure(plot_width=250, plot_height=250, title=None)
s2.triangle(x, y1, size=10, color="firebrick", alpha=0.5)
# create and another
s3 = figure(plot_width=250, plot_height=250, title=None)
s3.square(x, y2, size=10, color="olive", alpha=0.5)
# put the results in a column and show
show(column(s1, s2, s3))
Plots don't have a visible toggle, at least as of version 0.13. So you will have to reset the children value of the layout widget. I'm not quite sure what interaction you intend with a dropdown. Here is a complete example with a checkbox:
from bokeh.io import output_file, show
from bokeh.layouts import column, row
from bokeh.plotting import figure
from bokeh.models import CheckboxGroup, CustomJS
output_file("layout.html")
x = list(range(11))
y0 = x
y1 = [10 - i for i in x]
y2 = [abs(i - 5) for i in x]
s1 = figure(plot_width=250, plot_height=250, title=None)
s1.circle(x, y0, size=10, color="navy", alpha=0.5)
s2 = figure(plot_width=250, plot_height=250, title=None)
s2.triangle(x, y1, size=10, color="firebrick", alpha=0.5)
s3 = figure(plot_width=250, plot_height=250, title=None)
s3.square(x, y2, size=10, color="olive", alpha=0.5)
col = column(s1, s2, s3)
checkbox = CheckboxGroup(labels=["Plot 1", "Plot 2", "Plot 3"],
active=[0, 1, 2], width=100)
callback = CustomJS(args=dict(plots=[s1,s2, s3], col=col, checkbox=checkbox), code="""
const children = []
for (const i of checkbox.active) {
children.push(plots[i])
}
col.children = children
""")
checkbox.js_on_change('active', callback)
show(row(checkbox, col))
You could do something similar with a MultiSelect:
select = MultiSelect(options=[("0", "Plot 1"), ("1", "Plot 2"), ("2", "Plot 3")],
value=["0", "1", "2"], width=100)
callback = CustomJS(args=dict(plots=[s1,s2, s3], col=col, select=select), code="""
const children = []
for (const i of select.value) {
children.push(plots[i])
}
col.children = children
""")
select.js_on_change('value', callback)
Small FYI that that code is a little sloppy—it's relying on JS implicitly casting strings like "0" to numbers.
s1.tags, s2.tags, s3.tags = ['Foo'], ['Bar'], ['Arr'] # name your plots
plots = [s1, s2, s3]
labels = [(plots[i].tags[0]) for i in range(len(plots))]
active = list(range(0, len(plots)))
chkbx = CheckboxButtonGroup(labels=labels, active=active)
callback = CustomJS(args=dict(plots=plots, chkbx=chkbx), code="""
for (let i = 0; i < plots.length; i++){
plots[i].visible = chkbx.active.includes(i)
}
""")
chkbx.js_on_click(callback)
show(column([chkbx] + plots))
Thanks to #bigreddot and their answer for laying foundation for this solution.

Categories