Python Plotly figure with secondary x axis linked to primary - python

I've been struggling with this seemingly simple task: How to align two x axis with related data. In my case one axis is in Celsius and the other in Fahrenheit.
What I want to achieve is to obtain alignment of the two x axis so that:
32°F = 0°C
And
50°F = 10°C
With this relation, the two datasets will be aligned in terms of temperature.
I want to have both unit sets on the same graph so that the viewer can interpret the data according to the units they are used to.
Here is my code:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly.graph_objs.layout import YAxis,XAxis,Margin
layout = go.Layout(
title="Double X Axis Example",
xaxis=XAxis(
title="Celcius"
),
xaxis2 = XAxis(
title="Fahrenheits",
overlaying= 'x',
side= 'top',
),
yaxis=dict(
title="Y values"
),
)
# Create figure with secondary x-axis
fig = go.Figure(layout=layout)
# Add traces
fig.add_trace(
go.Scatter(x=[10, 20, 30], y=[4.5, 6, 5], name="data set in celcius"),
)
fig.add_trace(
go.Scatter(x=[40, 60, 80], y=[4, 5, 6.5], name="data set in fahrenheit", xaxis='x2'),
)
fig.show()
Here is the resulting figure with the unaligned axes (10°C = 40°F !?):
Thank you,

In this case it might help to set the ranges for the x-axes, something like this:
fig.add_trace(
go.Scatter(x=[10, 20, 30], y=[4.5, 6, 5,], name="data set in celcius",xaxis="x1"),
)
fig.add_trace(
go.Scatter(x=[40, 60, 80], y=[4, 5, 6.5], name="data set in fahrenheit", xaxis='x2'),
)
fig.update_layout(
xaxis1=dict(range=[0, 100]),
xaxis2=dict(range=[32, 212]),
)
...possibly calculating the limit needed of x1 and then base x2 limit on that.

This is my solution and code to your concern. Here, I set the range of the first and second x axes to [0, 100] and [32, 212], respectively. To align the two axes, I made 26 tick marks for both axes and they are aligned because of the equal number of tick marks. Having an equal number of tick marks for both axes (and equal ranges) is crucial so that the aligned numbers are actually equal. Assuming that most data sets that will be plotted are between 0 and 100 degrees Celsius (for data in Celsius) --- or 32 and 212 degrees Fahrenheit (for data in Fahrenheit) --- I believe this solution overflows the data and the traces won't cover the full x ranges. Plot of the graph here.
import numpy as np
import plotly.graph_objects as go
arr1 = np.array([10, 20, 30])
arr2 = np.array([4.5, 6, 5])
arr3 = np.array([40, 60, 80])
arr4 = np.array([4, 5, 6.5])
fig = go.Figure(go.Scatter( x=arr1, y=arr2, name='data set in celsius' ) )
fig.add_trace(go.Scatter( x=arr3, y=arr4, xaxis='x2', name='data set in fahrenheit' ))
fig.update_layout(title_text='Double X Axis Example',
legend=dict(yanchor='top', y=0.875, xanchor='right', x=1),
yaxis=dict(domain = [0.05, 0.875], title='Y values', spikemode='toaxis', spikesnap='cursor'), template='plotly_dark',
xaxis =dict(position = 0, title='Celsius', spikemode='across', spikesnap='cursor',
tickmode='array', tickvals=np.linspace(0,100,26), range=[0,100]),
xaxis2=dict(position = 0.9, title='Fahrenheit', anchor='free', overlaying='x', side='top', tickmode='array',
tickvals=np.linspace(32,212,26), range=[32,212], spikemode='across', spikesnap='cursor' )
)
fig.show()

Related

How can I plot a line with a confidence interval in python using plotly?

I am trying to use plotly to plot a graph similar to the one here below:
Unfortunately I am only able to plot something like this
What I would like is to have normal boundaries (upper and lower defined by two dataframe columns and only one entry in the legend.
import plotly.graph_objs as go
# Create a trace for the lower bound
trace1 = go.Scatter(x=df.index,
y=df['lower'],
name='Lower Bound',
fill='tonexty',
fillcolor='rgba(255,0,0,0.2)',
line=dict(color='blue'))
# Create a trace for the median
trace2 = go.Scatter(x=df.index,
y=df['median'],
name='median',
line=dict(color='blue', width=2))
# Create a trace for the upper bound
trace3 = go.Scatter(x=df.index,
y=df['upper'],
name='Upper Bound',
fill='tonexty',
fillcolor='rgba(255,0,0,0.2)',
line=dict(color='blue'))
# Create the layout
layout = go.Layout(xaxis=dict(title='Date'),
yaxis=dict(title='title'))
# Create the figure with the three traces and the layout
fig = go.Figure(data=[trace1, trace2, trace3], layout=layout)
context['pltyplot'] = pltyplot(fig, output_type="div")
I want to use plotly because I am integrating the resulting figure into a django web page and plotly enables, with the las line, to import the whole object in a clean, simple and interactive way into the poge.
Any ideas?
You can try this code:
import plotly.graph_objs as go
x = [1, 2, 3, 4, 5]
y = [2, 4, 5, 3, 6]
# Define the confidence interval
interval = 0.6 * np.std(y) / np.mean(y)
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Line'))
fig.add_trace(go.Scatter(x=x+x[::-1],
y=y+[i + interval for i in y[::-1]],
fill='toself',
fillcolor='rgba(0,100,80,0.2)',
line=dict(width=0),
showlegend=False))
fig.add_trace(go.Scatter(x=x+x[::-1],
y=y+[i - interval for i in y[::-1]],
fill='toself',
fillcolor='rgba(0,100,80,0.2)',
line=dict(width=0),
showlegend=False))
fig.show()

Insert space between ticks on y-Axsis / Control height of ticks on Python Plotly

I have a linegraph with twolines it in. On the x-Axis there is the time, on the y-Axes the values. On the y-Axis I want to set the space between the ticks manually.
My data is very dense in the range from 0 - 5, so I want the ticks in this range to be far away from each other in order distinguish between the two lines plotted in the graph.
Between 5 - 10 my data has more or less the same values, so the ticks can be closer together here.
From 10 - 15 need to spread the ticks again because my data is dense here.
I tried setting the values of the ticks manually, but that does not add/remove space/height of the ticks
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
df['Date']=pd.to_datetime(df['Date'])
dfg = df.groupby([pd.Grouper(key='Date', freq='M'), 'direction']).size().to_frame('counts')
dfg.reset_index(inplace=True)
layout = Layout(
title='Foo',
plot_bgcolor='rgba(0,0,0,0)',
yaxis = dict(
tickmode = 'array',
tickvals = [0, 0.5, 1, 1.5, ..., 5, 6, 7, ..., 10.5, 11, 11.5, 12, ...],
)
)
fig = go.Figure()
for d,c in zip(dfg['direction'].unique(), ['red','green']):
dfs = dfg.query('direction == #d')
fig.add_trace(
go.Scatter(
x=dfs['Date'],
y=dfs['counts'],
mode='lines',
line=dict(
color=c,
width=3
),
name=d
)
)
fig.show()
Note: The data I added here is not my real data. It's just some data to work with.
You need to scale your y data as you want, and then provide the ticks values and tick text
Assume:
y=[1,1.1,1.2,1.3,5,10,11,11.1,11.2,11.3]
y_trans=custom_trans(y) -> [1,2,3,4,5,6,7,8,9,10] # index
----------
go.Scatter(,y=y_trans,)
----------
fig.update_yaxes(
ticktext=["1", "1.1", "1.2", "5","10", ...],
tickvals=[1,2,3,5,6,..] #index position from y_trans
)
This is a very weird request though, and I would rather plot the difference in the two lines if it makes sense, or choose another method of visualizing, try log y axis?

How do I show only available values in the x-axis

I would like to plot a chart with plotly that shows only the existing values in the x-axis.
When I execute the code below, a chart that looks like in the following image appears:
The range on the x-axis as well as the range on the y-axis is evenly set from zero up to the maximal value.
import plotly.graph_objs as go
from plotly.offline import plot
xValues = [1, 2, 27, 50]
yValues = [7, 1, 2, 3]
trace = go.Scatter( x = xValues, y = yValues, mode='lines+markers', name='high limits' )
plottedData = [trace]
plot( plottedData )
Now, I would like to show only the existing values on the x axis. Related to my example, I want just the values [1, 2, 27, 50] to appear. And they should have the same space in between. Is this possible? If yes, how?
You can force the xaxis.type to be category like this:
plot( dict(data=plottedData, layout=go.Layout(xaxis = {"type": "category"} )))

Label specific bubbles in Plotly bubble chart

I am having trouble figuring out how to label specific bubbles in a plot.ly bubble chart. I want certain "outlier" bubbles to have text written inside the bubble instead of via hover text.
Let's say I have this data:
import plotly.plotly as py
import plotly.graph_objs as go
trace0 = go.Scatter(
x=[1, 2, 3, 4],
y=[10, 11, 12, 13],
mode='markers',
marker=dict(
size=[40, 60, 80, 100],
)
)
data = [trace0]
py.iplot(data, filename='bubblechart-size')
I'd like to only add text markers on bubbles that correspond to (1,10) and (4,13). Furthermore, is it possible to control the location of text markers?
You can achieve this with annotations.
This allows you to write any text you want on the chart and reference it to your data. You can also control where the text appears using position anchors or by applying an additional calculation on top of the x and y data. For example:
x_data = [1, 2, 3, 4]
y_data = [10, 11, 12, 13]
z_data = [40, 60, 80, 100]
annotations = [
dict(
x=x,
y=y,
text='' if 4 > x > 1 else z, # Some conditional to define outliers
showarrow=False,
xanchor='center', # Position of text relative to x axis (left/right/center)
yanchor='middle', # Position of text relative to y axis (top/bottom/middle)
) for x, y, z in zip(x_data, y_data, z_data)
]
trace0 = go.Scatter(
x=x_data,
y=y_data,
mode='markers',
marker=dict(
size=z_data,
)
)
data = [trace0]
layout = go.Layout(annotations=annotations)
py.iplot(go.Figure(data=data, layout=layout), filename='bubblechart-size')
Edit
If using cufflinks, then the above can be adapted slightly to:
bubbles_to_annotate = df[(df['avg_pos'] < 2) | (df['avg_pos'] > 3)] # Some conditional to define outliers
annotations = [
dict(
x=row['avg_pos'],
y=row['avg_neg'],
text=row['subreddit'],
showarrow=False,
xanchor='center', # Position of text relative to x axis (left/right/center)
yanchor='middle', # Position of text relative to y axis (top/bottom/middle)
) for _, row in bubbles_to_annotate.iterrows()
]
df.iplot(kind='bubble', x='avg_pos', y='avg_neg', size='counts',
text='subreddit', xTitle='Average Negative Sentiment',
yTitle='Average Positive Sentiment', annotations=annotations,
filename='simple-bubble-chart')
You will still need to define the annotations since you need a conditional argument. Then pass this directly to cufflinks via annotations.

How do you add labels to a plotly boxplot in python?

I have the following code;
y = errnums
err_box = Box(
y=y,
name='Error Percent',
boxmean='sd',
marker=Marker(color='red'),
boxpoints='all',
jitter=0.5,
pointpos=-2.0
)
layout = Layout(
title='Error BoxPlot',
height=500,
width=500
)
fig = Figure(data=Data([err_box]), layout=layout)
plotly.image.save_as(fig, os.path.join(output_images, 'err_box.png'))
Which generates the following image;
What I would like to do is the following two things;
1) Add % next to the y-axis numbers. (Instead of having a traditional y-axis label saying "Error (%)")
2) Label all the vital points: mean, first quartile, third quartile, and stdev. Ideally the label would be a 4 sig-fig ('.2f') number next to the line.
Also, the stdev is the dotted line, and the diamond represents 1 sigma? 2 sigma?
For labels, try annotations. You'll have to compute the quartiles and mean yourself to position the labels.
Simple example:
import plotly.plotly as py
from plotly.graph_objs import *
data = Data([
Box(
y=[0, 1, 1, 2, 3, 5, 8, 13, 21],
boxpoints='all',
jitter=0.3,
pointpos=-1.8
)
])
layout = Layout(
annotations=Annotations([
Annotation(
x=0.3,
y=8.822,
text='3rd Quartile',
showarrow=False,
font=Font(
size=16
)
)
])
)
fig = Figure(data=data, layout=layout)
plot_url = py.plot(fig)
Simple Python boxplot
I recommend adding and positioning the annotations in the Plotly workspace, and then viewing the generated code:
The diamond shows the mean, and +- 1 standard deviation away from it.
It's not currently possible to add a % to the y-axis labels.

Categories