Hoverinformation for shapes in plotly - python

I know there is the hovertemplate/hover_text/ option for traces (marker/line) but I cannot find such a thing for shapes.
Is there a way to have a hover text pop up when moving over a shape? Maybe a workaround?
Example:
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=[1.5, 3],
y=[2.5, 2.5],
text=["Rectangle reference to the plot",
"Rectangle reference to the axes"],
mode="markers",
))
fig.add_shape(
# Rectangle reference to the plot
type="rect",
xref="paper",
yref="paper",
x0=0.25,
y0=0,
x1=0.5,
y1=0.5,
line=dict(
color="LightSeaGreen",
width=3,
),
fillcolor="PaleTurquoise",
)
When I hover over the two points, I get a hover-template with information. How can I get something similar for the shape?

It seems that it's not possible to add hoverinfo to shapes directly. But you can obtain something very close to what seems to be the desired effect through the right combination of shapes and traces. The following plot is made from specifying two rectangles in a list like:
shapes = [[2,6,2,6],
[4,7,4,7]]
The rest of the code snippet is set up to be flexible with regards to the number of shapes, and the colors assigned to them and the corresponding traces to make that little dot in the lower right corners of the shapes.
Plot:
If this is something you can use, we can discuss ways to edit what is being displayed in the hoverinfo.
Complete code:
# Imports
import pandas as pd
#import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
# shape definitions
shapes = [[2,6,2,6],
[4,7,4,7]]
# color management
# define colors as a list
colors = px.colors.qualitative.Plotly
# convert plotly hex colors to rgba to enable transparency adjustments
def hex_rgba(hex, transparency):
col_hex = hex.lstrip('#')
col_rgb = list(int(col_hex[i:i+2], 16) for i in (0, 2, 4))
col_rgb.extend([transparency])
areacol = tuple(col_rgb)
return areacol
rgba = [hex_rgba(c, transparency=0.4) for c in colors]
colCycle = ['rgba'+str(elem) for elem in rgba]
# plotly setup
fig = go.Figure()
# shapes
for i, s in enumerate(shapes):
fig.add_shape(dict(type="rect",
x0=s[0],
y0=s[2],
x1=s[1],
y1=s[3],
layer='above',
fillcolor=colCycle[i],
line=dict(
color=colors[i],
width=3)))
# traces as dots in the lower right corner for each shape
for i, s in enumerate(shapes):
fig.add_trace(go.Scatter(x=[s[1]], y=[s[2]], name = "Hoverinfo " +str(i + 1),
showlegend=False,
mode='markers', marker=dict(color = colors[i], size=12)))
# edit layout
fig.update_layout(yaxis=dict(range=[0,8], showgrid=True),
xaxis=dict(range=[0,8], showgrid=True))
fig.show()

I thought of a solution I am happy with.
Simply draw a shape. You won't be able to see a hover text. However, if you add a trace with a fill on top of the shape, then set the trace to opacity=0 you will see the hover text from the trace pop up when moving over the shape.
Again, thanks for your responses!
import plotly.graph_objects as go
# Draw shape (you won't be able to add a hover text for it)
fig = go.Figure()
fig.add_shape(
type="rect",
x0=0, y0=0,
x1=4, y1=3,
fillcolor='LightSkyBlue',
line_color='Blue',
name='Shape 1'
)
# Adding a trace with a fill, setting opacity to 0
fig.add_trace(
go.Scatter(
x=[0,0,4,4,0],
y=[0,3,3,0,0],
fill="toself",
mode='lines',
name='',
text='Custom text on top of shape',
opacity=0
)
)
fig.show()

Related

how to change the colors of each circular sector? plotly

[enter image description here]
how can i change the colors of each sector?
Also, how can I put a caption pair for each line (pair of circles) ?
The method of changing the color of each pie chart is possible by specifying a color for each marker. Since no data is presented, I have edited the example in the reference to make the graph suitable for your purposes. The colors were determined by selecting two of the 24 colors in the default color map, combining them, and then extracting four similar colors from the list. If you already have a set of colors in mind, you can specify the number of colors you need in a list, for example. There is no way to add line labels, so I used string annotations.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.express as px
import numpy as np
import itertools
import random
#colors = ['gold', 'mediumturquoise', 'darkorange', 'lightgreen']
colors = px.colors.qualitative.Dark24 + px.colors.qualitative.Light24
fig = make_subplots(rows=4, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}],
[{'type':'domain'}, {'type':'domain'}],
[{'type':'domain'}, {'type':'domain'}],
[{'type':'domain'}, {'type':'domain'}],
])
for r,c in itertools.product([1,2,3,4],[1,2]):
fig.add_trace(go.Pie(
labels=['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen'],
values=np.random.randint(500,5000,4),
marker=dict(colors=random.sample(colors,4), line=dict(color='#000000', width=2)
)), row=r, col=c
)
fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=12, showlegend=False)
for i in range(4):
fig.add_annotation(text='Row{}'.format(i+1), textangle=-90, font=dict(size=16), x=-0.1, y=0.05+0.31*i, xref='paper', yref='paper', showarrow=False)
fig.update_layout(autosize=True,
height=600,
width=800)
fig.show()

Connecting data points with lines in a Plotly boxplot in Python

I am working on some boxplots. I found this code very helpful and I managed to replicate it for my needs:
import plotly.express as px
import numpy as np
import pandas as pd
np.random.seed(1)
y0 = np.random.randn(50) - 1
y1 = np.random.randn(50) + 1
df = pd.DataFrame({'graph_name':['trace 0']*len(y0)+['trace 1']*len(y1),
'value': np.concatenate([y0,y1],0),
'color':np.random.choice([0,1,2,3,4,5,6,7,8,9], size=100, replace=True)}
)
fig = px.strip(df,
x='graph_name',
y='value',
color='color',
stripmode='overlay')
fig.add_trace(go.Box(y=df.query('graph_name == "trace 0"')['value'], name='trace 0'))
fig.add_trace(go.Box(y=df.query('graph_name == "trace 1"')['value'], name='trace 1'))
fig.update_layout(autosize=False,
width=600,
height=600,
legend={'traceorder':'normal'})
fig.show()
I am now trying to put some lines connecting the datapoints with the same colors, but I am lost. Any idea?
Something similar to this:
My first idea was to add lines to your figure by using plotly shapes and specifying the start and end points in x- and y-axis coordinates. However, when you use px.strip, plotly implements jittering (adding randomly generated small values, say between -0.1 and 0.1, to the x-coordinates under the hood to avoid points overlapping), but as far as I know, there is no way to retrieve the exact x-coordinates of each point.
However we can get around this by using go.Scatter to plot all the paired points individually, adding jittering as needed to the x-values and connecting each pair of points with a line. We are basically implementing px.strip ourselves but with full control of the exact coordinates of each point.
In order to toggle colors the same way that px.strip allows you to, we need to assign all points of the same color to the same legendgroup, and also only show the legend entry the first time a color is plotted (as we don't want an legend entry for each point)
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import pandas as pd
np.random.seed(1)
y0 = np.random.randn(50) - 1
y1 = np.random.randn(50) + 1
## sort both sets of data so we can easily connect them with line annotations
y0.sort()
y1.sort()
df = pd.DataFrame({'graph_name':['trace 0']*len(y0)+['trace 1']*len(y1),
'value': np.concatenate([y0,y1],0)}
# 'color':np.random.choice([0,1,2,3,4,5,6,7,8,9], size=100, replace=True)}
)
fig = go.Figure()
## i will set jittering to 0.1
x0 = np.array([0]*len(y0)) + np.random.uniform(-0.1,0.1,len(y0))
x1 = np.array([1]*len(y0)) + np.random.uniform(-0.1,0.1,len(y0))
## px.colors.sequential.Plasma contains 10 distinct colors
## colors_list = np.random.choice(px.colors.qualitative.D3, size=50)
## for simplicity, we repeat it 5 times instead of selecting randomly
## this guarantees the colors appear in order in the legend
colors_list = px.colors.qualitative.D3*5
color_number = {i:color for color,i in enumerate(px.colors.qualitative.D3)}
## keep track of whether the color is showing up for the first time as we build out the legend
colors_legend = {color:False for color in colors_list}
for x_start,x_end,y_start,y_end,color in zip(x0,x1,y0,y1,colors_list):
## if the color hasn't been added to the legend yet, add a legend entry
if colors_legend[color] == False:
fig.add_trace(
go.Scatter(
x=[x_start,x_end],
y=[y_start,y_end],
mode='lines+markers',
marker=dict(color=color),
line=dict(color="rgba(100,100,100,0.5)"),
legendgroup=color_number[color],
name=color_number[color],
showlegend=True,
hoverinfo='skip'
)
)
colors_legend[color] = True
## otherwise omit the legend entry, but add it to the same legend group
else:
fig.add_trace(
go.Scatter(
x=[x_start,x_end],
y=[y_start,y_end],
mode='lines+markers',
marker=dict(color=color),
line=dict(color="rgba(100,100,100,0.5)"),
legendgroup=color_number[color],
showlegend=False,
hoverinfo='skip'
)
)
fig.add_trace(go.Box(y=df.query('graph_name == "trace 0"')['value'], name='trace 0'))
fig.add_trace(go.Box(y=df.query('graph_name == "trace 1"')['value'], name='trace 1'))
fig.update_layout(autosize=False,
width=600,
height=600,
legend={'traceorder':'normal'})
fig.show()

Plotly Set Trace Position in a Figure

I'm a newbie in Plotly and I was wondering if there is a way to specify where a new trace needs to be centered within the Figure object.
Just to be more clear, this is an example:
import plotly.express as px
import plotly.graph_objects as go
df = pd.DataFrame(something)
fig = go.Figure()
for i in [40,45,50]:
fig.add_shape(
go.layout.Shape(
type='line',
xref='x',
yref='y',
x0=line_data[i]["min"],
y0=i,
x1=line_data[i]["max"],
y1=i,
),
)
fig.add_trace(
go.Scatter(
x=df.ColA.values,
y=df.ColB.values,
mode='markers',
)
)
This is the result
My goal is to build an histogram of the points in each horizontal line.
I don't know if there is a better and faster way, but my idea was to add more traces, each one with an histogram, and then center those traces in each line. Is there a way to do it? Maybe some position parameter for a trace, like (xcenter=7.5, ycenter=50)?
My ideal result should be:
you describe histogram / frequency multiple observed items
have mapped these to y-axis using base
import numpy as np
import plotly.graph_objects as go
df = pd.DataFrame({40:np.random.normal(5,2, 200).astype(int),50:np.random.normal(6,2, 200).astype(int),60:np.random.normal(6.5,2, 200).astype(int)})
# change to frequency of observed values
df2 = df[40].value_counts().to_frame().join(df[50].value_counts(), how="outer").join(df[60].value_counts(), how="outer")
# plot bar of frequency, setting base based on observation
fig = go.Figure([go.Bar(x=df2.index, y=df2[c]/len(df2), base=c, name=c) for c in df2.columns])
fig.update_layout(barmode="overlay")

Putting box plot below scatter plot [Plotly]

I want a box plot to be placed on top of a scatter plot.
Neither putting the box trace at first and adding the scatter plot on top nor adding an "layer='below'" attribute leads to the desired result. The box always stays in the back.
Any suggestions?
import plotly.graph_objects as go
import numpy as np
np.random.seed(10)
rand = np.random.uniform(-100, 100, 100)
fig = go.Figure()
fig.add_trace(go.Box(
x=rand,
name='Markers',
line_color='rgba(128, 128, 128, .0)',
fillcolor='darkgrey'
))
fig.add_trace(go.Scatter(
x=rand,
y=['Markers']*len(rand),
name='Markers',
mode="markers",
marker_color='orange',
marker_size=8,
# layer='below' # does not work
))
fig.show()
As suggested here, you have to attach the box plot to another axis:
fig = go.Figure()
fig.add_trace(go.Box(
x=rand,
name='Markers',
line_color='rgba(128, 128, 128, .0)',
fillcolor='darkgrey',
yaxis='y2'
))
fig.add_trace(go.Scatter(
x=rand,
y=['Markers']*len(rand),
name='Markers',
mode="markers",
marker_color='orange',
marker_size=8
# layer='below' # does not work
))
fig.update_layout(yaxis2=dict(
matches='y',
layer="above traces",
overlaying="y",
),)
fig.show()

Plotly: How to set a fill color between two vertical lines?

Using matplotlib, we can "trivially" fill the area between two vertical lines using fill_between() as in the example:
https://matplotlib.org/3.2.1/gallery/lines_bars_and_markers/fill_between_demo.html#selectively-marking-horizontal-regions-across-the-whole-axes
Using matplotlib, I can make what I need:
We have two signals, and I''m computing the rolling/moving Pearson's and Spearman's correlation. When the correlations go either below -0.5 or above 0.5, I want to shade the period (blue for Pearson's and orange for Spearman's). I also darken the weekends in gray in all plots.
However, I'm finding a hard time to accomplish the same using Plotly. And it will also be helpful to know how to do it between two horizontal lines.
Note that I'm using Plotly and Dash to speed up the visualization of several plots. Users asked for a more "dynamic type of thing." However, I'm not a GUI guy and cannot spend time on this, although I need to feed them with initial results.
BTW, I tried Bokeh in the past, and I gave up for some reason I cannot remember. Plotly looks good since I can use either from Python or R, which are my main development tools.
Thanks,
Carlos
I don't think there is any built-in Plotly method that that is equivalent to matplotlib's fill_between() method. However you can draw shapes so a possible workaround is to draw a grey rectangle and set the the parameter layer="below" so that the signal is still visible. You can also set the coordinates of the rectangle outside of your axis range to ensure the rectangle extends to the edges of the plot.
You can fill the area in between horizontal lines by drawing a rectangle and setting the axes ranges in a similar manner.
import numpy as np
import plotly.graph_objects as go
x = np.arange(0, 4 * np.pi, 0.01)
y = np.sin(x)
fig = go.Figure()
fig.add_trace(go.Scatter(
x=x,
y=y
))
# hard-code the axes
fig.update_xaxes(range=[0, 4 * np.pi])
fig.update_yaxes(range=[-1.2, 1.2])
# specify the corners of the rectangles
fig.update_layout(
shapes=[
dict(
type="rect",
xref="x",
yref="y",
x0="4",
y0="-1.3",
x1="5",
y1="1.3",
fillcolor="lightgray",
opacity=0.4,
line_width=0,
layer="below"
),
dict(
type="rect",
xref="x",
yref="y",
x0="9",
y0="-1.3",
x1="10",
y1="1.3",
fillcolor="lightgray",
opacity=0.4,
line_width=0,
layer="below"
),
]
)
fig.show()
You haven't provided a data sample so I'm going to use a synthetical time-series to show you how you can add a number of shapes with defined start and stop dates for several different categories using a custom function bgLevel
Two vertical lines with a fill between them very quickly turns into a rectangle. And rectangles can easily be added as shapes using fig.add_shape. The example below will show you how to find start and stop dates for periods given by a certain critera. In your case these criteria are whether or not the value of a variable is higher or lower than a certain level.
Using shapes instead of traces with fig.add_trace() will let you define the position with regards to plot layers using layer='below'. And the shapes outlines can easily be hidden using line=dict(color="rgba(0,0,0,0)).
Plot 1: Time series figure with random data:
Plot 2: Background is set to an opaque grey when A > 100 :
Plot 2: Background is also set to an opaque red when D < 60
Complete code:
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import datetime
pd.set_option('display.max_rows', None)
# data sample
nperiods = 200
np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(nperiods, 4)),
columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),periods=nperiods).tolist()
df['dates'] = datelist
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum().reset_index()
# function to set background color for a
# specified variable and a specified level
# plotly setup
fig = px.line(df, x='dates', y=df.columns[1:])
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')
def bgLevels(fig, variable, level, mode, fillcolor, layer):
"""
Set a specified color as background for given
levels of a specified variable using a shape.
Keyword arguments:
==================
fig -- plotly figure
variable -- column name in a pandas dataframe
level -- int or float
mode -- set threshold above or below
fillcolor -- any color type that plotly can handle
layer -- position of shape in plotly fiugre, like "below"
"""
if mode == 'above':
m = df[variable].gt(level)
if mode == 'below':
m = df[variable].lt(level)
df1 = df[m].groupby((~m).cumsum())['dates'].agg(['first','last'])
for index, row in df1.iterrows():
#print(row['first'], row['last'])
fig.add_shape(type="rect",
xref="x",
yref="paper",
x0=row['first'],
y0=0,
x1=row['last'],
y1=1,
line=dict(color="rgba(0,0,0,0)",width=3,),
fillcolor=fillcolor,
layer=layer)
return(fig)
fig = bgLevels(fig = fig, variable = 'A', level = 100, mode = 'above',
fillcolor = 'rgba(100,100,100,0.2)', layer = 'below')
fig = bgLevels(fig = fig, variable = 'D', level = -60, mode = 'below',
fillcolor = 'rgba(255,0,0,0.2)', layer = 'below')
fig.show()
I think that fig.add_hrect() and fig.add_vrect() are the simplest approaches to reproducing the MatPlotLib fill_between functionality in this case:
https://plotly.com/python/horizontal-vertical-shapes/
For your example, add_vrect() should do the trick.

Categories