Related
I want to create a before-and-after plot of the three axis of my measurement system. This is close to what I want. However:
How do I have the "before" and "after" titles span subplot 1+2 and 4+5, respectively? (the tabs dont work as expected)
Like "before" and "after" should be above a column, i would like to have the "x-Axis", "y-Axis" etc infront of the row of graphs. How do I do that?
How do I join subplot 1+2 and 4+5 together, so that they touch? wspace=.0 doesnt work as expected.
How do I reduce the width in the middle, where subplot 3 would be, so that the other sides can take up more space?
How do I add some hspace between the fig.suptitle and the graphs?
How can I make my code more elegant? There is a lot of repetition.
from matplotlib.pyplot import figure
def plot_before_and_after(data_before, data_after, title):
shape = data_before.shape
sensor_num = shape[0]
n_start = 20 # number picked at random
N = 2 ** 12 # power of two is good
n_stop = n_start + N
p_stop = n_start + 40 # one periode #50Hz at the sampling rate
x_long = range(n_start, n_stop)
x_short = range(n_start, p_stop)
cmap = plt.get_cmap('jet_r')
axis_list = ['x', 'y', 'z']
fig = figure(num=None, figsize=(10, 10), dpi=80, facecolor='w', edgecolor='k')
fig.suptitle(title + "\nbefore \t\t\t\t\tafter")
plots = []
for axis_cnt in range(0, 3):
ax0 = plt.subplot(3, 5, axis_cnt * 5 + 1,
title="before, {}-Axis".format(axis_list[axis_cnt]))
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_long, data_before[sensor_cnt, n_start:n_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
ax1 = plt.subplot(3, 5, axis_cnt * 5 + 2,
title="before, {}-Axis".format(axis_list[axis_cnt]),
sharey=ax0)
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_short, data_before[sensor_cnt, n_start:p_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
plt.setp(ax1.get_yticklabels(), visible=False)
ax3 = plt.subplot(3, 5, axis_cnt * 5 + 4,
title="after, {}-Axis".format(axis_list[axis_cnt]))
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_long, data_after[sensor_cnt, n_start:n_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
ax4 = plt.subplot(3, 5, axis_cnt * 5 + 5,
title="after, {}-Axis".format(axis_list[axis_cnt]),sharey=ax3)
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_short, data_after[sensor_cnt, n_start:p_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
plt.setp(ax4.get_yticklabels(), visible=False)
plt.subplots_adjust(wspace=.0)
plt.legend()
plt.show()
Here's a preliminary answer that may help you. I used Matplotlib's GridSpec (see here for useful information) and the add_subplot method, both of which seem to be convenient in these cases. The gridspec is what allows us to create the two groups of subplots independently formatted; the add_subplot generates the individual axes.
Code
import matplotlib.pyplot as plt
nrow, ncol = 3, 2 # Number of rows and cols of gridspecs
lborder = [0.1, 0.6] # Left border coordinates of gridspecs
rborder = [0.45, 0.95] # Right border coordinates of gridspecs
tborder = 0.92 # Top border coordinate of gridspecs
gtitles = ['Before', 'After']
txt_axis = ['X-axis', 'Y-axis', 'Z-axis']
fig = plt.figure(figsize=(10, 10), dpi=80, facecolor='w', edgecolor='k')
for i in range(2):
gs = fig.add_gridspec(nrows=nrow, ncols=ncol, left=lborder[i],
right=rborder[i], top=tborder, hspace=0.45, wspace=0)
for j in range(nrow):
ax0 = fig.add_subplot(gs[j,0])
ax0.plot([1,2,3])
plt.text(-0.4, 0.5, txt_axis[j],
horizontalalignment='center',verticalalignment='center',
transform = ax0.transAxes, fontsize = 12)
if j == 0:
fig.text(1, 1.1, gtitles[i], fontsize=12, horizontalalignment =
'center', transform = ax0.transAxes)
for k in range(1, ncol):
ax1 = fig.add_subplot(gs[j,k], sharey = ax0)
plt.setp(ax1.get_yticklabels(), visible=False)
ax1.plot([1,2,3])
fig.suptitle('Figure title', fontsize = 14)
As for your questions:
I created the 'Before' and 'After' titles using text, based on this answer).
Same thing for the "-axis" text. Note that it will probably overlap with any axes label you write on the vertical axis. Also note that now we have to shift the left gridspec slightly to the right (via the leftargument of add_gridspec).
wspace can be introduced in add_gridspec too. I don't know why it doesn't work in your code.
For the space in between the 2 gridspecs, use the left and right arguments in the add_gridspec function.
The space between the main title and the subplots can be achieved via the top argument in add_gridspec.
Your inner loops seem very similar, perhaps you could define a function and save some lines of code. In my case, I tried to encase everything in a loop.
Hope it helps.
I have a 2 dimensional time series plotted as FacetGrid via xarray.
p = gmt.plot.line(x='time', add_legend=False, alpha = 0.1, color = ('k'), ylim = (-1, 1.2), col='MCrun', col_wrap = 5)
I want to add another lineplot with the same axes and dimensions on top. For individual members that's simply:
gmt.isel(MCrun=0).plot.line(x='time', add_legend=False, alpha = 0.1, color = 'k', ylim = (-3, 1.2))
gmt_esmean.isel(MCrun=0).plot.line(x='time', add_legend=False, color = 'red')
But using the same with two facet grids results in 20 plots - 10 with the individual lines and 10 with the mean. The closest I've come is
def smean_plot(*args, **kwargs):
gmt_esmean.plot.line(x='time', add_legend=False, color = 'red')
p = gmt.plot.line(x='time', add_legend=False, alpha = 0.1, color = ('k'), ylim = (-1, 1.2), col='MCrun', col_wrap = 5)
p.map(smean_plot)
Which plot all means in all plots and adds unwanted axes titles.
Any ideas how to only add the mean to the corresponding ensemble are greatly appreciated.
Ok one approach I was happy with is to plot the figures one-by-one via subplot in a loop. Set x and y axes as shared and reduce figure margin. It's not as elegant as I would've hoped but works just fine.
fig, axs = plt.subplots(ncols=5, nrows=2, figsize=(18,6), sharex=True, sharey=True, gridspec_kw={'hspace': 0.2, 'wspace': 0.1})
axs = axs.ravel()
for i in range(10):
gmt.isel(MCrun=i).plot.line(ax = axs[i], x='time', add_legend=False, alpha = 0.1, color = ('k'), ylim = (-1.2, 0.8))
gmt_esmean.isel(MCrun=i).plot.line(ax = axs[i], x='time', add_legend=False, color = 'red')+ 1
plt.draw()
We conduct experiments, our oscilloscope gives all plots on same screen though each variables is different in magnitude. Is it possible to achive same in the python using the experimental data?
My present code and output:
import random
x = [i for i in range(1,11,1)]
y1 = random.sample(range(100, 1000), 10)
y2 = random.sample(range(0, 10), 10)
plt.plot(x,y1,'-r')
plt.plot(x,y2,'-g')
plt.legend(['y1','y2'])
plt.show()
There is a pretty simple solution to that just use subplots
import random
import matplotlib.pyplot as plt
x = [i for i in range(1,11,1)]
y1 = random.sample(range(100, 1000), 10)
y2 = random.sample(range(0, 10), 10)
ax1 = plt.subplot(211)
plt.plot(x,y1,'-r')
ax2 = plt.subplot(212,sharex=ax1)
plt.plot(x,y2,'-g')
ax1.get_shared_x_axes().join(ax1, ax2)
#make x axis on upper invisible
plt.setp(ax1.get_xaxis(), visible=False)
ax1.legend(['y1'])
ax2.legend(['y2'])
plt.show()
Looks like this
You can remove the bottom-border from the upper subplot and the upper border from the lower subplot with this:
ax1.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
GridSpec might help you to remove margins, however I gues there should be a simpler way to remove the distance between two subplots
I have a code that create 16 histograms. My problems are:
I think the code is too much repetative and that there are ways to write it shorter.
2.I have 13 fields to create histograms, and as 13 is prime number, I face a problem how to show all of them nicely but without blank plots.
I want to show the distribution but I get only 1 bar, even though I have change dit to 0, 100 and 500 (I have more than 1000 observations).
some columns are float and have too many 0 after the dot and I can't change it.
This is my code:
f, axes = plt.subplots(4, 4, figsize=(20,20), sharex=True)
sns.distplot(data['HR90'], color="skyblue", ax=axes[0,0],bins=500)
sns.distplot(data['HC90'], color="olive", ax=axes[0,1],bins=100)
sns.distplot(data['RD90'], color="gold", ax=axes[0,2],bins=100)
sns.distplot(data['PO90'], color="teal", ax=axes[0,3], bins=100)
sns.distplot(data['PS90'], color="red", ax=axes[1,0], bins=100)
sns.distplot(data['UE90'], color="green", ax=axes[1,1], bins=100)
sns.distplot(data['DV90'], color="blue", ax=axes[1,2], bins=100)
sns.distplot(data['MA90'], color="purple", ax=axes[1,3], bins=100)
sns.distplot(data['POL90'], color="orange", ax=axes[2,0], bins=100)
sns.distplot(data['DNL90'], color="green", ax=axes[2,1], bins=100)
sns.distplot(data['BLK90'], color="pink", ax=axes[2,2], bins=100)
sns.distplot(data['GI89'], color="silver", ax=axes[2,3], bins=100)
sns.distplot(data['FH90'], color="cyan", ax=axes[3,1], bins=100)
and this is the results:
as you can see I have some empty plots and the bins look like one.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
position = []
for x in range(0, 4):
for y in range (0, 4):
position.append([x, y])
groups = ['PO90', 'HC90', 'RD90', 'HR90', 'PS90', 'UE90', 'DV90', 'MA90', 'POL90', 'DNL90', 'BLK90', 'GI89','FH90']
graph_colors = ["skyblue", "olive", "gold", "teal", "red", "green", "blue", "purple", "orange", "green", "pink", "silver", "cyan"]
graph_bins = [500, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
data = pd.DataFrame(np.random.randint(low=0, high=10, size=(100, 13)), columns=groups)
f, axes = plt.subplots(4, 4, figsize=(20,20), sharex=False, sharey=False)
for i in range(0, 13):
sns.distplot(data[groups[i]], color=graph_colors[i], ax=axes[position[i][0], position[i][1]], bins=graph_bins[i])
The plot will look like this:
To get rid of the empty plot, sub plots must be added in a slightly different way, like so:
fig = plt.figure(figsize=(20,20))
# Generating 1st column.
for sp_index in range(1, 14, 4):
ax = fig.add_subplot(4, 4, sp_index)
sns.distplot(data[groups[sp_index-1]], color=graph_colors[sp_index-1], ax=ax, bins=graph_bins[sp_index-1])
# Generating 2nd column.
for sp_index in range(2, 14, 4):
ax = fig.add_subplot(4, 4, sp_index)
sns.distplot(data[groups[sp_index-1]], color=graph_colors[sp_index-1], ax=ax, bins=graph_bins[sp_index-1])
# Generating 3rd column.
for sp_index in range(3, 14, 4):
ax = fig.add_subplot(4, 4, sp_index)
sns.distplot(data[groups[sp_index-1]], color=graph_colors[sp_index-1], ax=ax, bins=graph_bins[sp_index-1])
# Generating 4thcolumn.
for sp_index in range(4, 14, 4):
ax = fig.add_subplot(4, 4, sp_index)
sns.distplot(data[groups[sp_index-1]], color=graph_colors[sp_index-1], ax=ax, bins=graph_bins[sp_index-1])
Then the plot will look like this (N.B. the graphs will look slightly different to the version above, since the data frame values were generated, using np.random.randint function several times, whilst experimenting with the solution):
I found the solution:
I had to change the sharex and sharey :
f, axes = plt.subplots(4, 4, figsize=(60,60), sharex=False, sharey=False)
This way the don't share the same axes and it works
I want to make a figure which consist of a frame with 4 figures, but in each figure there are three subplots. I am using the current version of Matplotlib
I show my code in order to do each individual figure, the point as I comment before, is how put 4 of this plots together in order to make a single figure
filename1 = "file1.txt"
filename2 = "file2.txt"
filename3 = "file3.txt"
datalist1 = np.loadtxt(filename1)
datalist2 = np.loadtxt(filename2)
datalist3 = np.loadtxt(filename3)
f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=True)
#First subplot
ax1.plot(datalist1[:,0], datalist1[:,1], 'k-')
ax1.plot(datalist2[:,0], datalist2[:,1], 'b-')
ax1.plot(datalist2[:,0], datalist2[:,2], 'g-')
ax1.plot(datalist2[:,0], datalist2[:,3], 'r-')
ax1.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax1.set_ylim(-1.2, 1.2)
ax1.set_xlim(0, 10)
major_ticks_x = np.arange(0.0, 11, 2.0)
minor_ticks_x = np.arange(0.0, 11, 1.0)
major_ticks_y = np.arange(-1, 1.05, 1.0)
minor_ticks_y = np.arange(-1, 1.05, 0.25)
ax1.set_yticks(major_ticks_y)
ax1.set_yticks(minor_ticks_y, minor=True)
#Second subplot
ax2.plot(datalist1[:,0], datalist1[:,2], 'k-')
ax2.plot(datalist2[:,0], datalist2[:,4], 'b-')
ax2.plot(datalist2[:,0], datalist2[:,5], 'g-')
ax2.plot(datalist2[:,0], datalist2[:,6], 'r-')
ax2.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax2.set_ylim(-1.2, 1.2)
ax2.set_xlim(0, 10)
ax2.set_yticks(major_ticks_y)
ax2.set_yticks(minor_ticks_y, minor=True)
#Third subplot
ax3.plot(datalist1[:,0], datalist1[:,3], 'k-')
ax3.plot(datalist2[:,0], datalist2[:,7], 'b-')
ax3.plot(datalist2[:,0], datalist2[:,8], 'g-')
ax3.plot(datalist2[:,0], datalist2[:,9], 'r-')
ax3.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax3.set_ylim(-1.2, 1.2)
ax3.set_xlim(0, 10)
ax3.set_yticks(major_ticks_y)
ax3.set_yticks(minor_ticks_y, minor=True)
ax3.set_xticks(major_ticks_x)
ax3.set_xticks(minor_ticks_x, minor=True)
ax3.set_xlabel(r"$t$")
f.subplots_adjust(hspace=0.0)
plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False)
The plot that I want to obtain is somtehing like this, in a single figure:
Somebody knows how can be do it?? Thanks for your attention.
OK, I'll bite. It is unclear what you want, but I assume you want 12 subplots (6 rows, 2 columns) grouped into 4 groups with shared x-axis.
As usual creating the subplots and plotting is easy. Sharing x-axis is straightforward as well, but requires some manual work. You can either set up the shared x-axis during the subplot creation or modify it after. I think modifying after is simpler.
Sorry for the manual part in the middle - it is possible to automate obviously.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
fig, axx = plt.subplots(6, 2, figsize=(10,14))
## merge axis
axx[0, 0].get_shared_x_axes().join(axx[0, 0], axx[2, 0])
axx[0, 0].set_xticklabels([])
axx[1, 0].get_shared_x_axes().join(axx[1, 0], axx[2, 0])
axx[1, 0].set_xticklabels([])
axx[0, 1].get_shared_x_axes().join(axx[0, 1], axx[2, 1])
axx[0, 1].set_xticklabels([])
axx[1, 1].get_shared_x_axes().join(axx[1, 1], axx[2, 1])
axx[1, 1].set_xticklabels([])
axx[3, 0].get_shared_x_axes().join(axx[3, 0], axx[5, 0])
axx[3, 0].set_xticklabels([])
axx[4, 0].get_shared_x_axes().join(axx[4, 0], axx[5, 0])
axx[4, 0].set_xticklabels([])
axx[3, 1].get_shared_x_axes().join(axx[3, 1], axx[5, 1])
axx[3, 1].set_xticklabels([])
axx[4, 1].get_shared_x_axes().join(axx[4, 1], axx[5, 1])
axx[4, 1].set_xticklabels([])
# plot some data
for i, row in enumerate(axx):
for j, cell in enumerate(row):
if i <= 2:
cell.plot(np.random.rand(100))
else:
cell.plot(np.random.rand(200))
Here is the result.