Overlay plots and scroll independently matplotlib - python

The problem I have can be described as follows:
Two different datasets with two different x and y axes (lets call them t1,y1,t2, and y2), t1 and t2 can be the same.
What I need to do is to overlay/plot both plots together (i.e, not in subplots, or in subplots that are the same size and exactly overlap one another) and be able to scroll each axis independently. My goal is to be able to visually line them up to I can compare them.
What I have until not is the following:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.widgets import Slider
dArray = np.genfromtxt("t5_24.csv",delimiter=',');
y1 = dArray[:,2];
y2 = dArray[:,3];
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
t = np.linspace(0,len(temp1),len(temp1))
p1 = plt.plot(t,y1,t,y2)
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], axisbg=axcolor)
spos = Slider(axpos, 'Pos', 0.1, len(t))
def update(val):
pos = spos.val
# ax.xlim(pos,pos+30*60)
ax.axis([pos,pos+120*60,0,500])
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
which was taken from this stackoverflow post
Essentially what I need to do (I think) is to have two axes, completely overlapping, and with two scrollbars, on the same figure.
Any help is greatly appreciated.
Sorry for any English mistakes, ESL

Here's a basic example I can get working with two random datasets where you can vary the x-axis position of the two datasets independently on the same plot.
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
t = np.linspace(0, 10, 101)
y1, y2 = np.random.rand(2, 101)
fig, ax1 = plt.subplots()
ax2 = ax1.twiny()
fig.subplots_adjust(bottom=0.25)
ax1_pos = fig.add_axes([0.2, 0.1, 0.65, 0.03])
ax2_pos = fig.add_axes([0.2, 0.05, 0.65, 0.03])
s1 = Slider(ax1_pos, 'Pos1', 0.1, len(x))
s2 = Slider(ax2_pos, 'Pos2', 0.1, len(x))
def update1(v):
pos = s1.val
ax1.axis([pos,pos+2,0,1])
fig.canvas.draw_idle()
def update2(v):
pos = s2.val
ax2.axis([pos,pos+2,0,1])
fig.canvas.draw_idle()
s1.on_changed(update1)
s2.on_changed(update2)
ax1.plot(t, y1, 'b-')
ax2.plot(t, y2, 'r-')
plt.show()
This results in the following:
You will likely need to change the update functions to fit your actual data (mine are different than the one listed in the OP).
If you are instead interested in the having the same x-axis values but would like to vary the y-axis positions of each plot independently, you can use ax2 = ax1.twiny() and change the update functions accordingly (something like ax1.axis([xmin, xmax, pos, pos+2])).

Related

How does python draw on the specified image?

I want to write the operations and parameters that I usually use in drawing in a function. In the future, just pass x and y to draw according to the default parameters. But now the question I am facing is, how do I determine which picture plt.plot is drawing on? For example, I want to draw two curves on a picture.
def draw(x,y):
... %some operations
plt.plot(x,y) % draw picture operations
... %some operations
draw(x,y),
dray(x2,y2)
How to ensure that these two curves are drawn on a picture. That is, what parameters do I need to pass to make plt.plot focus on the picture I specify.
def plotLine(coordinate,figName='test',xylabel=[],ax=None):
# assert(len(coordinate)<=2)
if (len(coordinate)==2) :
x=coordinate[0]
y=coordinate[1]
assert(len(x)==len(y))
else:
y=coordinate
x =np.linspace(0,len(y)-1,len(y))
minn=min(y)
maxx=max(y)
plt.switch_backend('Agg')
if ax == None:
fig,ax = plt.subplots()
fig = plt.figure(num=None, figsize=(3.5, 1.5), dpi=300, facecolor='w')
plt.subplots_adjust(right = 0.98, top = 0.98, bottom=0.35,left=0.32,wspace=0, hspace=0.2)
ax.set_xlim([0,len(x)])
ax.set_ylim([0,maxx+maxx/3])
plt.xticks(fontsize=5)
plt.yticks(fontsize=5)
bar_width = 0.35
opacity = 0.8
lsmarkersize = 2.5
lslinewidth = 0.6
ax.plot(x,y,'-', linewidth=1, markersize=lsmarkersize, markeredgewidth=0)
plt.savefig(figName+".png",bbox_inches='tight',dpi=500)
# os.system("code "+figName+".png")
if ax!=None:
return ax
else:
return plt.gca()
x=[1,2,3,4,5,6]
y=[1,2,3,4,4,5]
ax = plotLine([x,y])
x=[1,2,3,4,5,6]
y=[12,13,14,15,16,17]
plotLine([x,y],ax=ax)
I tried to pass ax as a parameter. But the picture drawn at the end is blank.
You can use subplots to specify the axes to plot on. For example, create a figure with a single subplot:
fig, ax = plt.subplots()
ax.plot(x, y)
For your function you could do the following
fig, ax = plt.subplots()
def draw(x, y, ax):
ax.plot(x, y)
def dray(x2, y2, ax):
ax.plot(x2, y2)
I am not attempting to modify your code. This is more a general approach answer. Imho, it is better (in terms of keeping track of what's going on) to define the figure and plots outside the function and doing only the actual plotting inside the function.
import numpy as np
from matplotlib import pyplot as plt
np.random.seed(123)
#the plotting function, taking ax and label as optional parameters
def draw_the_line(x, y, current_ax=None, current_label=None):
if not current_ax:
current_ax=plt.gca()
if not current_label:
current_label="missing label"
current_ax.plot(x, y, label=current_label)
plt.sca(current_ax)
fig, (ax1, ax2) = plt.subplots(2, figsize=(6, 8))
#normal plot into panel 1
x1 = np.arange(6)
y1 = np.random.randint(1, 10, len(x1))
draw_the_line(x1, y1, ax1, "data1")
#normal plot into panel 2
x2 = np.arange(5)
y2 = np.random.randint(10, 20, len(x2))
draw_the_line(x2, y2, ax2, "data2")
#plot into panel 1 with missing label
x3 = np.arange(4)
y3 = np.random.randint(20, 30, len(x3))
draw_the_line(x3, y3, ax1)
#plot into the last panel used
x4 = np.arange(3)
y4 = np.random.randint(30, 40, len(x4))
draw_the_line(x4, y4, current_label="data4")
ax1.legend()
ax2.legend()
plt.show()
Sample output:

Can I add a knob that changes drawn lines on a matplotlib plot in a Labview style?

Say that I have a plot like this
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-3, 3, 0.01)
j = 2
y = np.sin(np.pi*x*j)
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(x, y)
plt.show()
is there anything in Matplotlib or more in general in Python in order to add something like a knob on the figure that changes the value of j and consequently the graph done in a Labview style?
The answer is yes! The 'knob' is called Slider and you can use it to change parameters in the plot. See the example at the matplotlib page.
If this is not enough, you would need to create your own knob. Since Matplotlib can be integrated in GUIs like PyQt and Tkinter, this is quite easy but of course requires to be acquainted with those tools.
Here I report a short code from the link previously suggested:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 3
s = a0*np.sin(2*np.pi*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0)
def update(val):
freq = sfreq.val
l.set_ydata(3*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
plt.show()
I believe this will make easier the life to who want to understand how Slider works

Showing legend for only one subplot using matplotlib

I'm facing a problem in showing the legend in the correct format using matplotlib.
EDIT: I have 4 subplots in a figure in 2 by 2 format and I want legend only on the first subplot which has two lines plotted on it. The legend that I got using the code attached below contained endless entries and extended vertically throughout the figure. When I use the same code using linspace to generate fake data the legend works absolutely fine.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import os
#------------------set default directory, import data and create column output vectors---------------------------#
path="C:/Users/Pacman/Data files"
os.chdir(path)
data =np.genfromtxt('vrp.txt')
x=np.array([data[:,][:,0]])
y1=np.array([data[:,][:,6]])
y2=np.array([data[:,][:,7]])
y3=np.array([data[:,][:,9]])
y4=np.array([data[:,][:,11]])
y5=np.array([data[:,][:,10]])
nrows=2
ncols=2
tick_l=6 #length of ticks
fs_axis=16 #font size of axis labels
plt.rcParams['axes.linewidth'] = 2 #Sets global line width of all the axis
plt.rcParams['xtick.labelsize']=14 #Sets global font size for x-axis labels
plt.rcParams['ytick.labelsize']=14 #Sets global font size for y-axis labels
plt.subplot(nrows, ncols, 1)
ax=plt.subplot(nrows, ncols, 1)
l1=plt.plot(x, y2, 'yo',label='Flow rate-fan')
l2=plt.plot(x,y3,'ro',label='Flow rate-discharge')
plt.title('(a)')
plt.ylabel('Flow rate ($m^3 s^{-1}$)',fontsize=fs_axis)
plt.xlabel('Rupture Position (ft)',fontsize=fs_axis)
# This part is not working
plt.legend(loc='upper right', fontsize='x-large')
#Same code for rest of the subplots
I tried to implement a fix suggested in the following link, however, could not make it work:
how do I make a single legend for many subplots with matplotlib?
Any help in this regard will be highly appreciated.
If I understand correctly, you need to tell plt.legend what to put as legends... at this point it is being loaded empty. What you get must be from another source. I have quickly the following, and of course when I run fig.legend as you do I get nothing.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.4, 0.7])
ax2 = fig.add_axes([0.55, 0.1, 0.4, 0.7])
x = np.arange(0.0, 2.0, 0.02)
y1 = np.sin(2*np.pi*x)
y2 = np.exp(-x)
l1, l2 = ax1.plot(x, y1, 'rs-', x, y2, 'go')
y3 = np.sin(4*np.pi*x)
y4 = np.exp(-2*x)
l3, l4 = ax2.plot(x, y3, 'yd-', x, y4, 'k^')
fig.legend(loc='upper right', fontsize='x-large')
#fig.legend((l1, l2), ('Line 1', 'Line 2'), 'upper left')
#fig.legend((l3, l4), ('Line 3', 'Line 4'), 'upper right')
plt.show()
I'd suggest doing one by one, and then applying for all.
It is useful to work with the axes directly (ax in your case) when when working with subplots. So if you set up two plots in a figure and only wish to have a legend in your second plot:
t = np.linspace(0, 10, 100)
plt.figure()
ax1 = plt.subplot(2, 1, 1)
ax1.plot(t, t * t)
ax2 = plt.subplot(2, 1, 2)
ax2.plot(t, t * t * t)
ax2.legend('Cubic Function')
Note that when creating the legend, I am doing so on ax2 as opposed to plt. If you wish to create a second legend for the first subplot, you can do so in the same way but on ax1.

add vertical line intersecting 3 different axes in a figure in matplotlib

i have to plot eeg data of 3 different channels in my graph. I would like to plot of all of them in one figure seperated by horozintal lines. X axis common to all the channels.
I can do this easily by using add_axes. But i want to draw a vertical line intersecting these axes. But i m not able to do it.
Currently, my sample code look like this.
from pylab import figure, show, setp
from numpy import sin, cos, exp, pi, arange
t = arange(0.0, 2.0, 0.01)
s1 = sin(2*pi*t)
s2 = exp(-t)
s3 = 200*t
fig = figure()
t = arange(0.0, 2.0, 0.01)
yprops = dict(rotation=0,
horizontalalignment='right',
verticalalignment='center',
x=-0.1)
axprops = dict(yticks=[])
ax1 =fig.add_axes([0.1, 0.5, 0.8, 0.2], **axprops)
ax1.plot(t, s1)
ax1.set_ylabel('S1', **yprops)
axprops['sharex'] = ax1
#axprops['sharey'] = ax1
# force x axes to remain in register, even with toolbar navigation
ax2 = fig.add_axes([0.1, 0.3, 0.8, 0.2], **axprops)
ax2.plot(t, s2)
ax2.set_ylabel('S2', **yprops)
ax3 = fig.add_axes([0.1, 0.1, 0.8, 0.2], **axprops)
ax3.plot(t, s3)
ax3.set_ylabel('S3', **yprops)
# turn off x ticklabels for all but the lower axes
for ax in ax1, ax2:
setp(ax.get_xticklabels(), visible=False)
show()
I want my final image to look like the one below. In my current output, i can get the same graph without the green vertical line.
can any one please help ??? I dont want to use subplots and also i dont want to add axvline for each axes.
Thank u,
thothadri
use
vl_lst = [a.axvline(x_pos, color='g', lw=3, linestyle='-') for a in [ax1, ax2, ax3]]
to update for each frame:
new_x = X
for v in vl_lst:
v.set_xdata(new_x)
axvline doc

draw a border around subplots in matplotlib

Anyone know how to draw a border around an individual subplot within a figure in matplotlib? I'm using pyplot.
eg:
import matplotlib.pyplot as plt
f = plt.figure()
ax1 = f.add_subplot(211)
ax2 = f.add_subplot(212)
# ax1.set_edgecolor('black')
..but Axes objects have no 'edgecolor', and I can't seem to find a way to outline the plot from the figure level either.
I'm actually wrapping mpl code and adding a wx UI with controls that I would like to have context depending on which subplot is selected. i.e. User clicks on subplot within figure canvas -- subplot is 'selected' (has an outline drawn around it, ideally sawtooth) -- GUI updates to present controls to modify that specific subplot.
You essentially want to draw outside of the axes, right?
I adapted this from here. It would need clean up as I used some hard-coded "fudge-factors" in there.
#!/usr/bin/env python
from pylab import *
def f(t):
s1 = cos(2*pi*t)
e1 = exp(-t)
return multiply(s1,e1)
t1 = arange(0.0, 5.0, 0.1)
t2 = arange(0.0, 5.0, 0.02)
t3 = arange(0.0, 2.0, 0.01)
figure(figsize=(4, 4))
sub1 = subplot(211)
l = plot(t1, f(t1), 'bo', t2, f(t2), 'k--', markerfacecolor='green')
grid(True)
title('A tale of 2 subplots')
ylabel('Damped oscillation')
## I ADDED THIS
autoAxis = sub1.axis()
rec = Rectangle((autoAxis[0]-0.7,autoAxis[2]-0.2),(autoAxis[1]-autoAxis[0])+1,(autoAxis[3]-autoAxis[2])+0.4,fill=False,lw=2)
rec = sub1.add_patch(rec)
rec.set_clip_on(False)
subplot(212)
plot(t3, cos(2*pi*t3), 'r.')
grid(True)
xlabel('time (s)')
ylabel('Undamped')
savefig('test.png')
Produces:
An alternative solution is derived from this answer on SO regarding placing Rectangle patches directly to the figure canvas, rather than to individual axes:
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=2, ncols=1)
axes[0].plot(np.cumsum(np.random.randn(100)))
axes[1].plot(np.cumsum(np.random.randn(100)))
rect = plt.Rectangle(
# (lower-left corner), width, height
(0.02, 0.5), 0.97, 0.49, fill=False, color="k", lw=2,
zorder=1000, transform=fig.transFigure, figure=fig
)
fig.patches.extend([rect])
plt.tight_layout()
plt.show()
Result:

Categories