How to plot vertical lines in plotly offline? - python-3.x

How would one plot a vertical line in plotly offline, using python? I want to add lines at x=20, x=40, and x=60, all in the same plot.
def graph_contracts(self):
trace1 = go.Scatter(
x=np.array(range(len(all_prices))),
y=np.array(all_prices), mode='markers', marker=dict(size=10, color='rgba(152, 0, 0, .8)'))
data = [trace1]
layout = go.Layout(title='Market Contracts by Period',
xaxis=dict(title='Contract #',
titlefont=dict(family='Courier New, monospace', size=18, color='#7f7f7f')),
yaxis=dict(title='Prices ($)',
titlefont=dict(family='Courier New, monospace', size=18, color='#7f7f7f')))
fig = go.Figure(data=data, layout=layout)
py.offline.plot(fig)

You can add lines via shape in layout, e.g.
import plotly
plotly.offline.init_notebook_mode()
import random
x=[i for i in range(100)]
trace = plotly.graph_objs.Scatter(x=x,
y=[random.random() for _ in x],
mode='markers')
shapes = list()
for i in (20, 40, 60):
shapes.append({'type': 'line',
'xref': 'x',
'yref': 'y',
'x0': i,
'y0': 0,
'x1': i,
'y1': 1})
layout = plotly.graph_objs.Layout(shapes=shapes)
fig = plotly.graph_objs.Figure(data=[trace],
layout=layout)
plotly.offline.plot(fig)
would give you

This is my example. The most important instruction is this.
fig.add_trace(go.Scatter(x=[12, 12], y=[-300,300], mode="lines", name="SIGNAL"))
The most important attribute is MODE='LINES'.
Actually this example is about a segment with x=12
EXAMPLE
import pandas as pd
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import numpy as np
import plotly.tools as tls
df1 = pd.read_csv('./jnjw_f8.csv')
layout = go.Layout(
xaxis = go.layout.XAxis(
tickmode = 'linear',
tick0 = 1,
dtick = 3
),
yaxis = go.layout.YAxis(
tickmode = 'linear',
tick0 = -100,
dtick = 3
))
fig = go.Figure(layout = layout)
fig.add_trace(go.Scatter(x = df1['x'], y =
df1['y1'],name='JNJW_sqrt'))
fig.add_trace(go.Scatter(x=[12, 12], y=[-300,300],
mode="lines", name="SIGNAL"))
fig.show()
Look here too.
how to plot a vertical line with plotly

A feature for vertical and horizontal lines is implemented with Plotly.py 4.12 (released 11/20). It works for plotly express and graph objects. See here: https://community.plotly.com/t/announcing-plotly-py-4-12-horizontal-and-vertical-lines-and-rectangles/46783
Simple example:
import plotly.express as px
df = px.data.stocks(indexed=True)
fig = px.line(df)
fig.add_vline(x='2018-09-24')
fig.show()

fig.add_vline(x=2.5, line_width=3, line_dash="dash", line_color="green")

Related

Why does not this plot get bigger as expected?

From a previous question, I got that plt.figure(figsize = 2 * np.array(plt.rcParams['figure.figsize'])) will increase the plot size by 2 times. With below code, I want to plot 4 subplots in the grid 2x2.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
%config InlineBackend.figure_format = 'svg' # Change the image format to svg for better quality
don = pd.read_csv('https://raw.githubusercontent.com/leanhdung1994/Deep-Learning/main/donclassif.txt.gz', sep=';')
fig, ax = plt.subplots(nrows = 2, ncols = 2)
plt.figure(figsize = 2 * np.array(plt.rcParams['figure.figsize'])) # This is to have bigger plot
for row in ax:
for col in row:
kmeans = KMeans(n_clusters = 4)
kmeans.fit(don)
y_kmeans = kmeans.predict(don)
col.scatter(don['V1'], don['V2'], c = y_kmeans, cmap = 'viridis')
centers = kmeans.cluster_centers_
col.scatter(centers[:, 0], centers[:, 1], c = 'red', s = 200, alpha = 0.5);
plt.show()
Could you please explain why plt.figure(figsize = 2 * np.array(plt.rcParams['figure.figsize'])) does not work in this case?
I post #JohanC's comment to remove this question from unanswered list.
It could be written as fig, axes = plt.subplots(nrows=2, ncols=2, figsize=2 * np.array(plt.rcParams['figure.figsize'])). Just calling plt.figure without storing the result creates a dummy new figure, without changing fig and without creating the axes on that new figure won't have the desired result.

Matplotlib- Add a color bar below a multi-colored line subplot as shown in the image

I am having a multicolored line plot and I want to add a color bar under it in the same figure like as shown in the image below, Is it possible?
I have attached a color bar image as a reference which I took from another code.
My intention here is to use the color bar like a legend for each segment of the line in the plot.
Edit-1: I want to have the color bar using a mappable object such as an image, So don't want to create a new subplot for the sole purpose of the color bar.
Any suggestion is welcome. Thanks in Advance.
This is the code for multicolored line plot
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
plt.show()
This is a workaround I'am using:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import matplotlib.colorbar as mcolorbar
import matplotlib.colors as mcolors
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots(2, 1, gridspec_kw={'height_ratios' : [5,1]})
ax[0].add_collection(lc)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax[0].set_xlim([0, 15])
ax[0].set_ylim([0, 10])
cb2 = mcolorbar.ColorbarBase(ax = ax[1], cmap = cmap, orientation = 'horizontal', extendfrac='auto')
cb2.set_ticks(bounds)
cb2.set_ticklabels(labels)
plt.tight_layout()
plt.show()
If you specifically want to avoid subplots, you can use a scalar mappable:
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
cmap = mcolors.ListedColormap(['r','g','b','y'])
sm = plt.cm.ScalarMappable(cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar = fig.colorbar(sm, ax=ax, orientation='horizontal',aspect=90)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax.set_xlim([0, 15])
ax.set_ylim([0, 10])
cbar.set_ticks(bounds)
cbar.set_ticklabels(labels)
plt.tight_layout()
plt.show()
This helped me to get what I asked.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
c=[1,2,3,4,5]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
cmap = mcolors.ListedColormap(['r','g','b','y'])
norm = mcolors.BoundaryNorm([1,2,3,4,5],4)
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar=fig.colorbar(sm, ticks=c, orientation='horizontal')
cbar.set_ticklabels(['Action1', 'Action2', 'Action3', 'Action4'])
plt.show()

How to visualize a list of strings on a colorbar in matplotlib

I have a dataset like
x = 3,4,6,77,3
y = 8,5,2,5,5
labels = "null","exit","power","smile","null"
Then I use
from matplotlib import pyplot as plt
plt.scatter(x,y)
colorbar = plt.colorbar(labels)
plt.show()
to make a scatter plot, but cannot make colorbar showing labels as its colors.
How to get this?
I'm not sure, if it's a good idea to do that for scatter plots in general (you have the same description for different data points, maybe just use some legend here?), but I guess a specific solution to what you have in mind, might be the following:
from matplotlib import pyplot as plt
# Data
x = [3, 4, 6, 77, 3]
y = [8, 5, 2, 5, 5]
labels = ('null', 'exit', 'power', 'smile', 'null')
# Customize colormap and scatter plot
cm = plt.cm.get_cmap('hsv')
sc = plt.scatter(x, y, c=range(5), cmap=cm)
cbar = plt.colorbar(sc, ticks=range(5))
cbar.ax.set_yticklabels(labels)
plt.show()
This will result in such an output:
The code combines this Matplotlib demo and this SO answer.
Hope that helps!
EDIT: Incorporating the comments, I can only think of some kind of label color dictionary, generating a custom colormap from the colors, and before plotting explicitly grabbing the proper color indices from the labels.
Here's the updated code (I added some additional colors and data points to check scalability):
from matplotlib import pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
# Color information; create custom colormap
label_color_dict = {'null': '#FF0000',
'exit': '#00FF00',
'power': '#0000FF',
'smile': '#FF00FF',
'addon': '#AAAAAA',
'addon2': '#444444'}
all_labels = list(label_color_dict.keys())
all_colors = list(label_color_dict.values())
n_colors = len(all_colors)
cm = LinearSegmentedColormap.from_list('custom_colormap', all_colors, N=n_colors)
# Data
x = [3, 4, 6, 77, 3, 10, 40]
y = [8, 5, 2, 5, 5, 4, 7]
labels = ('null', 'exit', 'power', 'smile', 'null', 'addon', 'addon2')
# Get indices from color list for given labels
color_idx = [all_colors.index(label_color_dict[label]) for label in labels]
# Customize colorbar and plot
sc = plt.scatter(x, y, c=color_idx, cmap=cm)
c_ticks = np.arange(n_colors) * (n_colors / (n_colors + 1)) + (2 / n_colors)
cbar = plt.colorbar(sc, ticks=c_ticks)
cbar.ax.set_yticklabels(all_labels)
plt.show()
And, the new output:
Finding the correct middle point of each color segment is (still) not good, but I'll leave this optimization to you.

Legend in separate subplot and grid

I have a collection of plots, arranged in two grids. In the left grid, I have one plot in the top (whole width) and two in the bottom (side-by-side). The two in the bottom are sharing legends. In my right grid, I want the legends, it is a lots of data series, and I would like to use the whole height of my figure.
The appearance of the data series are animated, but I would like the legends not to be.
My idea was to draw the time series in my right grid with legends, and hide the data series. But my only solution is ax.set_visible(False), which removes everything.
This is principally how the script looks like (simplified version):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
data = np.array([[1],[2],[3],[4]])
sett = np.array([1,2,3,4])
data1 = np.hstack((data,data*2, data*3, data*4))
data2 = np.hstack((3*data, 3*data/2, 3*data/3, 3*data/4))
df1 = pd.DataFrame(data = np.array(data1), index = [1,2,3,4], columns =
sett).transpose()
df2 = pd.DataFrame(data = np.array(data2), index = [1,2,3,4], columns =
sett).transpose()
gs1 = GridSpec(2,2)
gs1.update(left=0.05, right = 0.80, hspace = 0.05)
gs2 = GridSpec(3,1)
gs2.update(left=0.85, right = 0.98, hspace = 0.05)
figure = plt.figure()
plt.clf()
ax1 = plt.subplot(gs1[0,:])
ax2 = plt.subplot(gs1[1,0])
ax3 = plt.subplot(gs1[1,1], sharey = ax2)
ax4 = plt.subplot(gs2[:,0])
ax1.set_ylim(0,25)
label = ['s0', 's1', 's2', 's3', 's4']
ax4.plot(df1[1], df2[:])
ax4.legend(labels = label)
def make_frame(i):
ct=sett[i]
ax2.plot(df1[1], df1[ct])
ax3.plot(df1[1], df2[ct])
ax3.legend(labels = label)
ani = anim.FuncAnimation(figure, make_frame, frames = len(sett),
interval =500, repeat = False)
plt.show()
How can I remove the data series and keep the legend in gs2/ax4?
Don't bother I plot the first data series twice in ax2 and ax3 - it is ok in my original script. However - if someone can enlighten me on why, it is very much appreciated.
I'm not entirely sure what the desired output should be. Are you trying to put the legend at the place of ax4 right now, but not have the plot shown in ax4 at the moment.
My solution would be to not create ax4 at all. Instead you can use bbox_to_anchor to move the position of the legend. Here I use the transform from ax1 to establish a location in reference to ax1 and I move the legend slightly past the right edge and at the top of ax1.
See "legend guide" for more information.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
data = np.array([[1], [2], [3], [4]])
sett = np.array([1, 2, 3, 4])
data1 = np.hstack((data, data * 2, data * 3, data * 4))
data2 = np.hstack((3 * data, 3 * data / 2, 3 * data / 3, 3 * data / 4))
df1 = pd.DataFrame(data=np.array(data1), index=[1, 2, 3, 4], columns=sett).transpose()
df2 = pd.DataFrame(data=np.array(data2), index=[1, 2, 3, 4], columns=sett).transpose()
gs1 = GridSpec(2, 2)
gs1.update(left=0.05, right=0.80, hspace=0.05)
figure = plt.figure()
plt.clf()
ax1 = plt.subplot(gs1[0, :])
ax2 = plt.subplot(gs1[1, 0])
ax3 = plt.subplot(gs1[1, 1], sharey=ax2)
label = ['s0', 's1', 's2', 's3', 's4']
def make_frame(i):
ct = sett[i]
ax2.plot(df1[1], df1[ct])
ax3.plot(df1[1], df2[ct])
ax3.legend(labels=label, loc='upper left', bbox_to_anchor=(1.05, 1.), bbox_transform=ax1.transAxes)
ani = anim.FuncAnimation(figure, make_frame, frames=len(sett),
interval=500, repeat=False)
plt.show()
EDIT: using a proxy artist to create all the legends before the animation starts
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
data = np.array([[1], [2], [3], [4]])
sett = np.array([1, 2, 3, 4])
data1 = np.hstack((data, data * 2, data * 3, data * 4))
data2 = np.hstack((3 * data, 3 * data / 2, 3 * data / 3, 3 * data / 4))
df1 = pd.DataFrame(data=np.array(data1), index=[1, 2, 3, 4], columns=sett).transpose()
df2 = pd.DataFrame(data=np.array(data2), index=[1, 2, 3, 4], columns=sett).transpose()
gs1 = GridSpec(2, 2)
gs1.update(left=0.05, right=0.80, hspace=0.05)
figure = plt.figure()
plt.clf()
ax1 = plt.subplot(gs1[0, :])
ax2 = plt.subplot(gs1[1, 0])
ax3 = plt.subplot(gs1[1, 1], sharey=ax2)
ax1.set_ylim(0, 25)
labels = ['s0', 's1', 's2', 's3', 's4']
colors = ['C0', 'C1', 'C2', 'C3', 'C4']
proxies = [plt.plot([], [], c=c)[0] for c in colors]
ax1.legend(proxies, labels, bbox_to_anchor=(1., 1.), loc="upper left")
def init_frame():
pass
def make_frame(i):
ct = sett[i]
ax2.plot(df1[1], df1[ct], c=colors[i], label=labels[i])
ax3.plot(df1[1], df2[ct], c=colors[i], label=labels[i])
ax3.legend()
ani = anim.FuncAnimation(figure, make_frame, init_func=init_frame, frames=len(sett),
interval=500, repeat=False)
plt.show()
I would create the line plots prior to animating anything. You can initialize them with empty lists and then set the data one by one.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
data = np.array([1,2,3,4,5])
data1 = np.vstack((data,data*2, data*3, data*4))
data2 = np.vstack((3*data, 3*data/2, 3*data/3, 3*data/4))
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
sett = np.arange(len(df1.columns))
gs1 = GridSpec(2,2)
gs1.update(left=0.05, right = 0.80, hspace = 0.05)
figure = plt.figure()
ax1 = plt.subplot(gs1[0,:])
ax2 = plt.subplot(gs1[1,0])
ax3 = plt.subplot(gs1[1,1], sharey = ax2, sharex= ax2)
ax2.set_ylim(0,25)
lines1 = ax2.plot(*[[] for _ in range(len(sett)*2)])
lines2 = ax3.plot(*[[] for _ in range(len(sett)*2)])
label = ['s0', 's1', 's2', 's3', 's4']
ax1.legend(handles = lines1, labels=label, bbox_to_anchor=(1.05,1), loc="upper left")
def init():
for line in lines1+lines2:
line.set_data([],[])
def make_frame(i):
ct=sett[i]
lines1[i].set_data(df1.index, df1[ct])
lines2[i].set_data(df1.index, df2[ct])
ax2.relim()
ax2.autoscale_view()
ani = anim.FuncAnimation(figure, make_frame, init_func=init, frames = len(sett),
interval =500, repeat = False)
ani.save("anigif.gif", writer="imagemagick")
plt.show()

How to set scientific notation on axis in matplotlib

I am trying to plot a graph with two separate x-axis. One being some valve openning and the other the corresponding leak rate. I managed to make it work pretty well, though the format of that secondary axis doesn't always show scientific notations as seen on the figure down bellow
Awful overlapping labels, see the upper axis
How to force scientific notation display so that the labels wont overlap?
Here is the script I am using:
#HEADERS
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
from matplotlib import rc
rc('font', **{'family':'sans-serif','sans-serif':['Helvetica']})
rc('text', usetex=True)
#/HEADERS
turns = np.array([11.000, 11.500, 11.750, 12.000, 12.250, 12.375])
leak = np.array([3.89e-05, 4.63e-05, 1.67e-04, 1.45000000e-03, 8.61e-03, 1.71e-02])
pressure1 = np.array([7.9e-07, 3.0e-06, 3.5e-05, 6.1e-04, 5.1e-03, 1.8e-02])
pressure2 = np.array([8.22e-07, 8.22e-07, 8.71e-07, 1.8e-06, 1.150e-05, 7.24e-05])
pressure3 = np.array([2e-06, 2e-06, 2e-06, 1.2e-05, 1.2e-04, 6e-04])
fig = plt.figure(num='valve', figsize = (6.68, 6.68*1.3))
fig, ax1 = plt.subplots()
ax1.plot(turns, pressure1, 'r.', label= '$P_1$')
ax1.plot(turns, pressure2, 'b.', label= '$P_2$')
ax1.plot(turns, pressure3,'k.', label= '$P_3$')
plt.legend()
plt.minorticks_on()
plt.grid(b = True, which = 'major', axis = 'both')
ax1.errorbar(turns, pressure1, yerr = .4*pressure1, fmt='none', ecolor = 'k', elinewidth = 1, capsize = 1, label= '$P_{1err}$')
ax1.errorbar(turns, pressure2, yerr = .15*pressure2, fmt='none', ecolor = 'k', elinewidth = 1, capsize = 1, label= '$P_{2err}$')
plt.rc('text', usetex=True)
plt.rc('font', family='serif')
ax1.set_yscale('log', nonposy = 'mask')
ax1.set_ylabel(r'$P$')
ax1.set_xscale('linear')
ax1.set_xlabel('Opening (turns)')
plt.minorticks_on()
#plt.grid(b = True, which = 'major', axis = 'both')
#adding a secondary x-axis above
ax2 = ax1.twiny()
ax2.set_xlim(ax1.get_xlim())
new_tick_locations = turns
new_tick_label = leak #dtype here ?
ax2.set_xticks(new_tick_locations)
ax2.set_xticklabels(new_tick_label)
# I tried those commands from other threads but they all result in an error.
#ax2.xaxis.set_scientific(True)
#ax2.get_xaxis().set_major_formatter((matplotlib.ticker.Formatter(set_scientific(True)))
#ax2.get_xaxis().set_major_formatter().set_scientific(True)
ax2.set_xlabel(r'Leak rate (mbar$\times$L/s)')
plt.tight_layout()
#export png
plt.savefig(('export.png'), format = 'png', transparent=False, dpi = 300)
plt.show()
I'm using Python 3.6.
Thanks for your help.
Since you override the ticks, you can format them yourself and rotate them as well for more space:
new_tick_label = ['{:5.2e}'.format(x) for x in leak]
ax2.set_xticks(new_tick_locations)
ax2.set_xticklabels(new_tick_label, rotation=30)
Result:

Resources