Related
I'm attempting to recreate this image using coordinates. So far I haven't had much luck. Specifically I'm running into trouble being able to see all the colors. I think what I need is a way to determine which rectangles are at the forefront vs background. Would I be better off using matplotlib? Any help on this would be greatly appreciated.
CURRENT CODE
frame = tools.csv_read(file=['attack_zones'])
x = frame.groupby('identifier')
x = x.agg(Xmin=('X', np.min), Xmax=('X', np.max)).reset_index()
y = frame.groupby('identifier')
y = y.agg(Ymin=('Y', np.min), Ymax=('Y', np.max)).reset_index()
x = x.merge(y,on='identifier',how='left')
x = x.sort_values('identifier',ascending=True)
fig = go.Figure()
#Create scatter trace of text labels
fig.add_trace(go.Scatter(
x=[-48, 52],
y=[113, 242],
text=["Rectangle reference to the plot",
"Rectangle reference to the axes"],
mode="text",
))
#Set axes properties
fig.update_xaxes(range=[-134, 134])
fig.update_yaxes(range=[0, 345])
#Set identifier colors
def colors(identifier):
if identifier < 10:
return 'purple'
if identifier < 20:
return 'pink'
if identifier < 30:
return 'yellow'
else:
return 'white'
for iden,xmin,xmax,ymin,ymax in zip(x['identifier'],x['Xmin'],x['Xmax'],x['Ymin'],x['Ymax']):
fig.add_shape(type="rect",
xref="x", yref="y",
x0=xmin, y0=ymin,
x1=xmax, y1=ymax,
fillcolor=colors(iden),
)
fig.show()
There appears to be repeatable portions in your desired image – specifically each "square" can be thought of as a 3x3 array with text labels everywhere except for the center. Therefore, I would recommend writing a function with parameters for the two corners of your square, the text annotations, and fill color of the square, then calling this function repeatedly.
import plotly.graph_objects as go
fig = go.Figure()
def draw_square_with_labels(top_left, bottom_right, text_labels, fill_color, fig=fig):
""" Adds a 3x3 square trace with labels ordered clockwise on a Plotly graph_object
Args:
top_left: starting corner of a rectangle as a tuple or list of form (x0,y0)
bottom_right: ending corner of a rectangle as a tuple or list of form (x0,y0)
text_labels: a list of text labels starting from the location of the top_left and moving clockwise
fill_color: fill color for the square
Returns:
fig with a 3x3 colored square trace with labels added
"""
x0,y0 = top_left
x2,y2 = bottom_right
x1,y1 = (x0+x2)/2, (y0+y2)/2
xy_coordinates = [
[x0,y0],[x1,y0],[x2,y0],
[x2,y1],[x2,y2],[x1,y2],
[x0,y2],[x0,y1],[x0,y0]
]
x = [c[0] for c in xy_coordinates]
y = [c[1] for c in xy_coordinates]
text_positions = [
"bottom right",
"bottom center",
"bottom left",
"middle left",
"top left",
"top center",
"top right",
"middle right",
]
fig.add_trace(go.Scatter(
x=x,
y=y,
mode='lines+text',
line_color="rgba(200,200,200,0.7)",
text=text_labels,
textposition=text_positions,
fill='toself',
fillcolor = fill_color,
))
side_lengths = [10,7,4,1]
text_labels_array = [
[31,32,33,36,39,38,37,34,""],
[21,22,23,26,29,28,27,24,""],
[11,12,13,16,19,18,17,14,""],
[1,2,3,6,9,8,7,4,""],
]
fill_colors = [
"rgba(255,255,255,0.5)",
"rgba(255,255,167,0.5)",
"rgba(255,182,193,0.5)",
"rgba(219,112,147,0.5)",
]
for side_length, text_labels, fill_color in zip(side_lengths,text_labels_array,fill_colors):
draw_square_with_labels(
top_left=[-side_length,side_length],
bottom_right=[side_length,-side_length],
text_labels=text_labels,
fill_color=fill_color
)
## add '5' at the center
fig.add_annotation(x=0, y=0, text="5", showarrow=False)
## add guidelines
edge_x, edge_y = 1,10
fig.add_shape(type="line",
x0=-edge_x, y0=-edge_y, x1=-edge_x, y1=edge_y,
line=dict(color="rgba(200,200,200,0.7)")
)
fig.add_shape(type="line",
x0=edge_x, y0=-edge_y, x1=edge_x, y1=edge_y,
line=dict(color="rgba(200,200,200,0.7)")
)
fig.add_shape(type="line",
x0=-edge_y, y0=edge_x, x1=edge_y, y1=edge_x,
line=dict(color="rgba(200,200,200,0.7)")
)
fig.add_shape(type="line",
x0=-edge_y, y0=-edge_x, x1=edge_y, y1=-edge_x,
line=dict(color="rgba(200,200,200,0.7)")
)
## add green dashed rectangle
fig.add_shape(type="rect",
x0=-3.5, y0=-3.5, x1=3.5, y1=3.5,
line=dict(color="rgba(46,204,113,0.7)", dash='dash')
)
fig.update_xaxes(visible=False, showticklabels=False)
fig.update_yaxes(visible=False, showticklabels=False)
fig.update_layout(template='plotly_white', showlegend=False)
fig.show()
I'm plotting scatter3d projections of the 4d iris data set using plotly. To display all 4 possible projections in the same figure I am using sliders. However when "sliding" from one projection to the next the axis titles do not change. Normally I would use fig.update_layout() but that isn't working. How can I get these to change with the slider?
Projection 1
Projection 2
Here's the code for reference:
import numpy as np
import plotly.graph_objects as go
from matplotlib import cm
from itertools import combinations
def nd2scatter3d(X, labels = None, features = None, plot_axes = None, hovertext = None):
"""
Parameters
----------
X : array-like, shape = (n_samples, n_features).
labels : 1d int array, shape = (n_samples), optional, default None.
Target or clustering labels for each sample.
Defaults to np.ones(n_samples).
features : list, len = n_features, optional, default None.
List of feature names.
Defaults to numeric labeling.
plot_axes : list of 3-tuples, optional, default None.
List of axes to include in 3d projections. i.e. [(0,1,2), (0,1,3)] displays
projections along the 4th axis and 3rd axis in that order.
Defaults to all possible axes combinations.
hovertext : list, len = n_samples, optional, default None.
List of text to display on mouse hover.
Defaults to no text on hover.
"""
if labels is None:
labels = np.ones(X.shape[0]).astype(int)
if features is None:
features = np.arange(X.shape[1]).astype(str)
if plot_axes is None:
plot_axes = list(combinations(np.arange(X.shape[1]), 3))
if hovertext is None:
hoverinfo = 'none'
else:
hoverinfo = 'text'
fig = go.Figure()
for i in range(len(plot_axes)):
fig.add_trace(
go.Scatter3d(
visible=False,
x=X[:, plot_axes[i][0]],
y=X[:, plot_axes[i][1]],
z=X[:, plot_axes[i][2]],
mode='markers',
marker=dict(
size=3,
color = [list(cm.tab10.colors[c]) for c in labels],
opacity=1
),
hovertemplate=None,
hoverinfo= hoverinfo,
hovertext = hovertext,
),)
fig.data[0].visible = True
steps = []
for i in range(len(fig.data)):
step = dict(
method="update",
args=[{"visible": [False] * len(fig.data)},
{"title": features[plot_axes[i][0]] + ' vs. ' + features[plot_axes[i][1]] + ' vs. ' + features[plot_axes[i][2]]}, # layout attribute
],
label = str(plot_axes[i]),
)
step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible"
steps.append(step)
sliders = [dict(
active=10,
currentvalue={"prefix": "Projection: "},
pad={"t": 10},
steps=steps,
)]
fig.update_layout(
sliders=sliders
)
fig.update_layout(width=900, height = 500, margin=dict(r=45, l=45, b=10, t=50),
showlegend=False)
fig.update_layout(scene_aspectmode='cube',
scene2_aspectmode='cube',
scene3_aspectmode='cube',
scene4_aspectmode='cube',
scene = dict(
xaxis_title = features[plot_axes[0][0]],
yaxis_title = features[plot_axes[0][1]],
zaxis_title = features[plot_axes[0][2]],),
scene2 = dict(
xaxis_title = features[plot_axes[1][0]],
yaxis_title = features[plot_axes[1][1]],
zaxis_title = features[plot_axes[1][2]],),
scene3 = dict(
xaxis_title = features[plot_axes[2][0]],
yaxis_title = features[plot_axes[2][1]],
zaxis_title = features[plot_axes[2][2]],),
scene4 = dict(
xaxis_title = features[plot_axes[3][0]],
yaxis_title = features[plot_axes[3][1]],
zaxis_title = features[plot_axes[3][2]],)
)
fig.show()
Solution thanks to jayveesea, as well as some minor changes:
def nd2scatter3d(X, labels = None, features = None, plot_axes = None, hovertext = None, size = 3):
"""
Parameters
----------
X : array-like, shape = (n_samples, n_features).
labels : 1d int array, shape = (n_samples), optional, default None.
Target or clustering labels for each sample.
Defaults to np.ones(n_samples).
features : list, len = n_features, optional, default None.
List of feature names.
Defaults to numeric labeling.
plot_axes : list of 3-tuples, optional, default None.
List of axes to include in 3d projections. i.e. [(0,1,2), (0,1,3)] displays
projections along the 4th axis and 3rd axis in that order.
Defaults to all possible axes combinations.
hovertext : list, len = n_samples, optional, default None.
List of text to display on mouse hover.
Defaults to no text on hover.
size : int, default 3.
Sets marker size.
"""
if labels is None:
# Label all datapoints zero.
labels = np.zeros(X.shape[0]).astype(int)
if features is None:
# numerical features if no names are passed.
features = np.arange(X.shape[1]).astype(str)
if plot_axes is None:
# plot all possible axes if none are passed.
plot_axes = list(combinations(np.arange(X.shape[1]), 3))
if hovertext is None:
hoverinfo = 'none'
else:
hoverinfo = 'text'
# Determine colormap from number of labels.
if len(np.unique(labels)) <= 10:
color = [list(cm.tab10.colors[c]) if c >= 0 else [0,0,0,1] for c in labels]
elif len(np.unique(labels)) <= 20:
color = [list(cm.tab20.colors[c]) if c >= 0 else [0,0,0,1] for c in labels]
else:
norm_labels = labels/max(labels)
color = [cm.viridis(c) if c >= 0 else [0,0,0,1] for c in norm_labels]
# Genterate 3d scatter plot slider.
fig = go.Figure()
for i in range(len(plot_axes)):
fig.add_trace(
# Scatter plot params.
go.Scatter3d(
visible=False,
x=X[:, plot_axes[i][0]],
y=X[:, plot_axes[i][1]],
z=X[:, plot_axes[i][2]],
mode='markers',
marker=dict(
size=size,
color = color,
opacity=1
),
hovertemplate=None,
hoverinfo= hoverinfo,
hovertext = hovertext,
),)
fig.data[0].visible = True
steps = []
# Slider update params.
for i in range(len(fig.data)):
step = dict(
method="update",
args=[{"visible": [False] * len(fig.data)},
{"title": features[plot_axes[i][0]] + ' vs. '
+ features[plot_axes[i][1]] + ' vs. ' + features[plot_axes[i][2]],
"scene.xaxis.title": features[plot_axes[i][0]],
"scene.yaxis.title": features[plot_axes[i][1]],
"scene.zaxis.title": features[plot_axes[i][2]],
},
],
label = str(plot_axes[i]),
)
step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible".
steps.append(step)
sliders = [dict(
active=10,
currentvalue={"prefix": "Projection: (x, y, z) = "},
pad={"t": 10},
steps=steps,
)]
fig.update_layout(sliders=sliders)
fig.update_layout(width=900, height = 500, margin=dict(r=45, l=45, b=10, t=50))
fig.update_layout(scene_aspectmode='cube')
fig.show()
To update the axis titles you need to include the axis names with your slider entry. It may help to reference plotly's js document on update.
So instead of this chunk:
for i in range(len(fig.data)):
step = dict(
method="update",
args=[{"visible": [False] * len(fig.data)},
{"title": features[plot_axes[i][0]] + ' vs. '
+ features[plot_axes[i][1]] + ' vs. ' + features[plot_axes[i][2]]},
],
label = str(plot_axes[i]),
)
Use something like:
for i in range(len(fig.data)):
step = dict(
method="update",
args=[{"visible": [False] * len(fig.data)},
{"title": features[plot_axes[i][0]] + ' vs. '
+ features[plot_axes[i][1]] + ' vs. ' + features[plot_axes[i][2]],
"scene.xaxis.title": features[plot_axes[i][0]],
"scene.yaxis.title": features[plot_axes[i][1]],
"scene.zaxis.title": features[plot_axes[i][2]],
},
],
label = str(plot_axes[i]),
)
This creates an entry that will update the data and title and the axes titles when the slider changes.
I have a code that displays geologic well log in python using plotly,
the scale is linear and I set the range of it based on the dataframe index
I need to keep the scale as linear but want to set the vertical scale or the y-axis scale for example to be 1:100 how to achieve that
layout = go.Layout(
title='VSH',
autosize=False,
width=500,
height=1500,
yaxis=dict(
title='DEPT',
showgrid=True,
showticklabels=True,
gridcolor='#bdbdbd',
gridwidth=2
),
xaxis=dict(
title='Vsh',
showgrid=True,
showticklabels=True,
gridcolor='#bdbdbd',
gridwidth=2
)
)
df = df.loc[(df.index >= int(top)) & (df.index <= int(base))]
trace1 = go.Scatter(x = df['Vsh']/10 , y = df.index , mode='lines')
fig = go.Figure(data=[trace1] , layout = layout)
iplot(fig)
Just create a list (for example, from 1 to 100):
list1 = [i for i in range(0, 101)]
and added two parameters to yaxis in layout:
yaxis=dict(tickvals=[i for i in range(len(list1))],
ticktext=list1)
You can read more about ticktext and tickvals in plotly docs: 1 and 2.
python 3.6 latest plotly used :
The python Graph is created using plotly offline/Online function where three different dataframe inputs are used for y axis plotting and x axis are shared (In general it is Date index). The graphs are perfectly fine.
Only active area data on current layout's graph shown for the particular subplot layout, I want all the three layout data display when hovering the mouse in any layout.How to achieve this ?
eq_high = go.Scatter(
x=df.index,
y=df['High'],
name = "EQHigh",
line = dict(color = '#3EBF06'),
opacity = 0.8)
eq_low = go.Scatter(
x=df.index,
y=df['Low'],
name = "EQLow",
line = dict(color = '#FD2D00'),
opacity = 0.8)
##
op_high_ce = go.Scatter(
x=stock_opt_ce.index,
y=stock_opt_ce['High'],
name = "OpHighCE",
line = dict(color = '#15655F'),
opacity = 0.8)
op_low_ce = go.Scatter(
x=stock_opt_ce.index,
y=stock_opt_ce['Low'],
name = "OpLowCE",
line = dict(color = '#0D7B7F'),
opacity = 0.8)
op_last_ce = go.Scatter(
x=stock_opt_ce.index,
y=stock_opt_ce['Last'],
name = "OpLastCE",
line = dict(color = '#6AA6A2'),
opacity = 0.8)
op_settlePr_ce = go.Scatter(
x=stock_opt_ce.index,
y=stock_opt_ce['Settle Price'],
name = "OpSettlePrCE",
line = dict(color = '#2AADD1'),
opacity = 0.8)
##
op_high_pe = go.Scatter(
x=stock_opt_pe.index,
y=stock_opt_pe['High'],
name = "OpHighPE",
line = dict(color = '#FA6300'),
opacity = 0.8)
op_low_pe = go.Scatter(
x=stock_opt_pe.index,
y=stock_opt_pe['Low'],
name = "OpLowPE",
line = dict(color = '#AC4C0D'),
opacity = 0.8)
op_last_pe = go.Scatter(
x=stock_opt_pe.index,
y=stock_opt_pe['Last'],
name = "OpLastPE",
line = dict(color = '#E19B6D'),
opacity = 0.8)
op_settlepr_pe = go.Scatter(
x=stock_opt_pe.index,
y=stock_opt_pe['Low'],
name = "OpSettlePrPE",
line = dict(color = '#A54E1F'),
opacity = 0.8)
data = [eq_high,eq_low,op_high_ce,op_low_ce,op_settlePr_ce,op_high_pe,op_low_pe,op_settlepr_pe]
#custome Date Range plotting
layout = dict(
title = "Graph",
xaxis = dict(
range = ['2017-10-1','2017-11-27'])
)
fig = dict(data=data, layout=layout)
iplot(fig, filename = "CorrelationOfEquityAndOptionData")
plot(fig,show_link = False)
1.what changes to be made in the above code to show all three layout data values while mouse hovering.currently it shows only one layout graph values.
2.How to show the graph data points on right side or top side or bottom side or left side ,rather than showing the graph data onto the graph.
3.Any better optimized way of doing this.
Expected result:
This answer has been heavily edited after a brief discussion in the comments
Question 1:
After various attempts it seems that this is not possible at the moment. There is however an issue on github:
They would like hover labels to appear on all traces across all y-axes
with shared x-axes. Right now, they only appear in the subplot that
you are hovering in.
Question 2:
To alter the way the hoverinfo is displayed, use fig['layout']['hovermode']. The problem here is that your options are limited to one of the following: 'x', 'y', or 'closest'. And if you click the Compare data on hover option, there's no way to set it back to fig['layout']['hovermode'] = 'y' without running your code again. You can also change the way information is displayed for each series using fig['data'][ser]['hoverinfo']= 'all'. Here, you can insert multiple options like x or x+y in a list.
Heres an example with some random data:
# imports
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import pandas as pd
import plotly.graph_objs as go
import numpy as np
# setup
init_notebook_mode(connected=True)
# data
np.random.seed(1)
x = np.linspace(0, 1, 50)
y1 = np.cumsum(np.random.randn(50))
y0 = np.cumsum(np.random.randn(50))
# Data
trace0 = go.Scatter(
x=x,
y=y0,
)
trace1 = go.Scatter(
x=x,
y=y1,
)
# layout
layout = go.Layout(yaxis=dict(range=[-10,10])
)
# Plot
fig = go.Figure(data=[trace0, trace1], layout=layout)
# Edit hoveroptions
fig['layout']['hovermode'] = 'y'
for ser in range(0,len(fig['data'])):
fig['data'][ser]['hoverinfo']= 'all'
iplot(fig)
Question 3:
Im sorry to say that I don't know any other optimized way to do this.
Question 1:
The easiest way is to plot all 3 charts on a single chart by using subplots. Below is basic code to make subplots and obtain all hover information.
from plotly.subplots import make_subplots
fig=make_subplots(rows=3, cols=1, shared_xaxes=True)
fig.update_layout(hovermode='x unified')
Use the above mentioned parameters along with the others that you might need.
Question 2:
I have been searching a way to reach same outcome but haven't been successful yet.
If you find the answer please let me know.
Are there guidelines on how to set up secondary Y-axes in python for plotly?
I am assinging axis style through an iterative loop, as follows:
all_plots = ['plot1','plot2'...'plot20']
fig = tools.make_subplots(rows=nrow, cols=ncol, shared_xaxes=False, shared_yaxes=False, subplot_titles=all_plots)
for i in all_plots:
fig['layout']['yaxis'+str(j)].update()
How does the assignment of y axes work?
If my subplot included, say, 4 rows and 5 columns for a total of 20 subplots, do I have to assume that plotly needs to receive odd and even numbers, meaning:
yaxis1 and yaxis2 for plot1
....
yaxis39 and yaxis40 for plot20
It is possible, to do this, but its not particularly intuitive. Take this example where I create a plot 2x2 subplots, and add a secondary y axis to the plot in position 2,2.
When you create a subplots, they are assigned y axes: "y1","y2","y3","y4" on the left side of each subplot. To a secondary y axes, you need to use fig['layout'].updateto create new axes "y5", "y6", "y7", "y8" which correspond to "y1","y2","y3","y4". So the bottom right subplot would have axes y4(right) and y8(left). In the example below, I only create a secondary y axis for the last plot, but expanding it to more/all the subplots is pretty straightforward.
It is important to note, that creating the secondary axes, and assigning it in trace5 doesn't automatically place it on the proper axes. You still have to manually assign it with fig['data'][4].update(yaxis='y'+str(8)) to plot it relative to the left axis.
fig = tools.make_subplots(rows=2, cols=2,subplot_titles=('Air Temperature', 'Photon Flux Density',
'Ground Temps','Water Table & Precip'))
fig['layout']['xaxis1'].update( range=[174, 256])
fig['layout']['xaxis3'].update(title='Day of Year', range=[174, 256])
fig['layout']['yaxis1'].update(title='Degrees C',range=[-5,30])
fig['layout']['yaxis2'].update(title='mmol m<sup>-2</sup> m<sup>-d</sup>', range=[0, 35])
fig['layout']['yaxis3'].update(title='Ground Temps', range=[0, 11])
fig['layout']['yaxis4'].update(title='depth cm', range=[-20, 0])
fig['layout']['yaxis8'].update(title='rainfall cm', range=[0, 1.6])
fig['layout'].update(showlegend=False, title='Climate Conditions')
# In this example, I am only doing it for the last subplot, but if you wanted to do if for all,
# Just change to range(1,5)
for k in range(4,5):
fig['layout'].update({'yaxis{}'.format(k+4): dict(anchor='x'+str(k),
overlaying='y'+str(k),
side='right',
)
})
trace1 = go.Scatter(
y=Daily['AirTC_Avg'],
x=Daily.index,
marker = dict(
size = 10,
color = 'rgba(160, 0, 0, .8)',),
error_y=dict(
type='data',
array=Daily_Max['AirTC_Avg']-Daily_Min['AirTC_Avg'],
visible=True,
color = 'rgba(100, 0, 0, .5)',
),
name = 'Air Temp'
)
trace2 = go.Bar(
y=Daily['PPFD']/1000,
x=Daily.index,
name='Photon Flux',
marker=dict(
color='rgb(180, 180, 0)'
),
yaxis='y2',
)
trace3 = go.Scatter(
y=Daily['Temp_2_5_1'],
x=Daily.index,
name='Soil Temp',
marker=dict(
color='rgb(180, 0, 0)'
),
yaxis='y3',
)
trace4 = go.Scatter(
y=Daily['Table_1']*100,
x=Daily.index,
name='Water Table',
marker=dict(
color='rgb(0, 0, 180)'
),
yaxis='y4',
)
trace5 = go.Bar(
y=Daily['Rain']/10,
x=Daily.index,
name='Rain',
marker=dict(
color='rgb(0, 100, 180)'
),
yaxis='y8',
)
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 1, 2)
fig.append_trace(trace3, 2, 1)
fig.append_trace(trace4, 2, 2)
fig.append_trace(trace5, 2, 2)
## This part is important!!! you have to manually assing the data to the axis even
# though you do it when defining trace 5
fig['data'][4].update(yaxis='y'+str(8))
plot(fig, filename='FI_Climate')
Not an exact answer but I thought it might help...
I like to use pandas and cufflinks. Here is an example of how to plot two sets of data from one dataframe (df) on a graph using a secondary y axis. The data from each axis is displayed in different formats in this example (scatter and bar). The data is arranged into columns beforehand.
import pandas as pd
import cufflinks as cf
from plotly.offline import download_plotlyjs, init_notebook_mode,plot,iplot
fig1 = df.iplot(kind='scatter', mode='lines+markers', x=['col1', 'col2'],
y=['col3', 'col4',],
asFigure=True)
fig2 = df.iplot(kind='bar', x=['col1', 'col2'],
y=['col3', 'col4', ],
secondary_y=['col5','col6'],asFigure=True)
fig2['data'].extend(fig1['data'])
The naming convention is y, y2, y3... y40, and you make the reference to the axis in the trace dict.
So your traces should be like...
trace0 = dict(
x = xvals,
y = yvals,
yaxis = 'y'
)
trace1 = dict(
x = x2vals,
y = y2vals,
yaxis = 'y2'
)
....
trace40 = dict(
x = x40vals,
y = y40vals,
yaxis = 'y40'
)