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
Related
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.
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])).
My Problem
I'm having trouble maintaining formatting and modifications applied to a matplotlib Axes object after offsetting the spines.
An example
Consider the following simplified work-flow:
%matplotlib inline
import matplotlib.pyplot as plt
def funky_formatting(ax):
ax.set_xticks([0.1, 0.2, 0.5, 0.7, 0.9])
ax.set_xticklabels(list('abcde'), rotation=60)
ax.set_xticks([0.4, 0.6, 0.8], minor=True)
ax.set_xticklabels(list('xzy'), rotation=-60, minor=True)
ax.set_yticks([0.2, 0.5, 0.7, 0.8])
ax.set_yticklabels(list('ABCD'), rotation=35)
ax.tick_params(axis='both', labelsize=18, labelcolor='r')
ax.set_ylabel('r$y_{\mathrm{ii}}$ test', color='b', fontweight='extra bold', fontsize=20)
ax.set_xlabel('r$y_{\mathrm{ii}}$ test', color='r', fontweight='light', fontsize=16)
def offset_spines(ax):
for spine in ax.spines.values():
spine.set_position(('outward', 10))
# create two axes
fig, axes = plt.subplots(nrows=2)
# format both axes the same way:
for ax in axes:
funky_formatting(ax)
# offset the spines of only the top subplot
offset_spines(axes[0])
fig.tight_layout()
Which yields:
As you can see, after offsetting the spines, I lost my x/y labels, tick placements, and tick labels, and (some) tick label formatting. Unfortunately, I cannot offset the spines prior to the rest of the axes formatting since my goal is to create a general function that will handle axes created by other functions that all format their axes very differently.
What I've tried so far
It is possible to cache a lot of these properties by hand:
# cache the properties - x-axis
xlabels = [t.get_text() for t in ax.get_xticklabels()]
xlabelrot = ax.get_xticklabels()[0].get_rotation()
xticks = ax.get_xticks()
xlabel = ax.get_xlabel()
# cache the properties - y-axis
ylabels = [t.get_text() for t in ax.get_yticklabels()]
ylabelrot = ax.get_yticklabels()[0].get_rotation()
yticks = ax.get_yticks()
ylabel = ax.get_ylabel()
# offset spines
for spine in ax.spines.values():
spine.set_position(('outward', offset))
# restore properties - x-axis
ax.set_xticks(xticks)
ax.set_xticklabels(xlabels, rotation=xlabelrot)
ax.set_xlabel(xlabel)
# restore properties - y-axis
ax.set_yticks(yticks)
ax.set_yticklabels(ylabels, rotation=ylabelrot)
ax.set_ylabel(ylabel)
While that does the trick, it is:
very repetitive
needs to be about twice as long to cover the possibility of having minor tick labels.
The main question:
Is there a more efficient way to achieve this without manually picking up and restoring 2 properties x 2 axes x major+minor ticks + 2 labels?
I modified you code, and it can product the same ticks now.
%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib.artist import ArtistInspector
def funky_formatting(ax):
ax.set_xticks([0.1, 0.2, 0.5, 0.7, 0.9])
ax.set_xticklabels(list('abcde'), rotation=60)
ax.set_yticks([0.2, 0.5, 0.7, 0.8])
ax.set_yticklabels(list('ABCD'), rotation=35)
ax.tick_params(axis='both', labelsize=18, labelcolor='r')
ax.set_ylabel('r$y_{\mathrm{ii}}$ test', color='b', fontweight='extra bold', fontsize=20)
ax.set_xlabel('r$y_{\mathrm{ii}}$ test', color='r', fontweight='light', fontsize=16)
def try_update(artist, p):
for k,v in p.iteritems():
try:
artist.update({k:v})
except:
pass
def offset_spines(ax):
for spine in ax.spines.values():
paxis = spine.axis.properties()
ptick = [label.properties() for label in spine.axis.get_ticklabels()]
spine.set_position(('outward', 10))
try_update(spine.axis, paxis)
for label, p in zip(spine.axis.get_ticklabels(), ptick):
p.pop("transform")
try_update(label, p)
# create two axes
fig, axes = plt.subplots(nrows=2)
# format both axes the same way:
for ax in axes:
funky_formatting(ax)
# offset the spines of only the top subplot
offset_spines(axes[0])
fig.tight_layout()
Here is the output:
When using axhline right after twinx(), the horizontal line drawn still follows the coordinates of the first y-axis.
Any tip on how to make it scale to the second y-axis ?
You could call the axhline method from the Axes objects, as in the example below, or set te current Axes with sca.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = np.sin(x)
y2 = 2.0 * np.cos(x)
fig = plt.figure()
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ax1.axhline( 0.5, 0.1, 0.5, color='r', lw=3)
ax2.axhline(-0.5, 0.5, 0.9, color='b', lw=3)
ax1.plot(x, y1, 'r', lw=2)
ax2.plot(x, y2, 'b', lw=2)
plt.show()
In case you don't have access to the return value of twinx() (for example when it was called for you by Pandas) you can access the left and right axes using the Axes object's left_ax and right_ax attributes.
Only one of these will be present, as each links to the other axis.
If you have a handle to the left axis, its right_ax attribute will point to the linked right axis.
If you have a handle to the right axis, its left_ax attribute will point to the linked left axis.
For example:
df = pandas.DataFrame({'d1': numpy.random.rand(10),
'd2': numpy.random.rand(10) * 10})
ax = df.plot(secondary_y=['d2']) # returns the left axis
ax.axhline(y=0.5, alpha=0.5) # draw a line from it
ax.right_ax.axhline(y=10, color="red", alpha=0.5) # draw a line from the right axis
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: