Fitting multiple sets of subplots into one figure - python

Is it at all possible for me to make one set of subplots (with 2 plots) in a for loop that runs three times, and then fit the three sets of subplots into one main figure. The whole point of this is to be able to have 6 plots on one figure, but have a space between every other plot. I know how to have 6 plots in one figure, but I can only put space between every plot instead of every other plot. I hope my question makes sense. As for the data that I'm using, it is a pretty basic data set I'm using for practice right now. Each pair of plot share the same x-axis, which is why I don't want a space between them.
import matplotlib.pyplot as plt
x1 = [0,1,2,3,4,5]
y1 = [i*2 for i in x1]
y2 = [i*3 for i in x1]
x2 = [4,8,12,16,20]
y3 = [i*5 for i in x2]
y4 = [i*3 for i in x2]
x3 = [0,1,2,3,4,5]
y5 = [i*4 for i in x3]
y6 = [i*7 for i in x3]
fig = plt.figure(1,figsize=(5,5))
ax1 = plt.subplot(611)
ax1.plot(x1,y1)
ax2 = plt.subplot(612)
ax2.plot(x1,y2)
ax3 = plt.subplot(613)
ax3.plot(x2,y3)
ax4 = plt.subplot(614)
ax4.plot(x2,y4)
ax5 = plt.subplot(615)
ax5.plot(x3,y5)
ax6 = plt.subplot(616)
ax6.plot(x3,y6)
fig.subplots_adjust(hspace=0.5)
plt.show()
This is what I get:

Your code makes a graph with six sub-plots. If you make eight subplots and leave two of them empty, you get your added space. Here is the code I used, slightly modified from your code.
import matplotlib.pyplot as plt
x1 = [0,1,2,3,4,5]
y1 = [i*2 for i in x1]
y2 = [i*3 for i in x1]
x2 = [4,8,12,16,20]
y3 = [i*5 for i in x2]
y4 = [i*3 for i in x2]
x3 = [0,1,2,3,4,5]
y5 = [i*4 for i in x3]
y6 = [i*7 for i in x3]
fig = plt.figure(1,figsize=(5,7))
ax1 = plt.subplot(811)
ax1.plot(x1,y1)
ax2 = plt.subplot(812)
ax2.plot(x1,y2)
ax3 = plt.subplot(814)
ax3.plot(x2,y3)
ax4 = plt.subplot(815)
ax4.plot(x2,y4)
ax5 = plt.subplot(817)
ax5.plot(x3,y5)
ax6 = plt.subplot(818)
ax6.plot(x3,y6)
fig.subplots_adjust(hspace=0.5)
plt.show()
I get this result:
I had to increase the figure size height to 7 inches to accommodate the extra space. Is that what you want?

Related

Image size of 747x3360143 pixels is too large. It must be less than 2^16 in each direction: when use legend

import matplotlib
import matplotlib.pyplot as plt
x =[0,1,2,3,4]
plt.rcParams.update({"text.usetex": False, "font.family": "serif", "font.serif": ["Palatino"]})
y1 =np.array([e1["E"]/-28486.111])
y2 =np.array([e2["E"]/-28372.065])
y3 =np.array([e3["E"]/-28258.018])
y4 =np.array([e4["E"]/-27687.787])
x1 =np.array([e1["step"]/10000000])
x2 =np.array([e2["step"]/10000000])
x3 =np.array([e3["step"]/10000000])
x4 =np.array([e4["step"]/10000000])
fig, ax = plt.subplots(figsize=(12,8),frameon=False, dpi=100)
ax = plt.gca()
ax.plot(x1,y1,"o:",label= '300K',c='blue',linewidth=0.5)
ax.plot(x1,y2,"o:",label= '400K',c='red',linewidth=0.5)
ax.plot(x1,y3,"o:",label= '500K',c='black',linewidth=0.5)
ax.plot(x1,y4,"o:",label= '1000K',c='green',linewidth=0.5)
#corners = ((x1, y1), (x1, y2),(x1, y3),(x1, y4))
#ax.update_datalim(corners)
plt.grid()
plt.tick_params(axis ='both', labelsize =12)
plt.legend(loc="best")
plt.title(r"Variation of the kinetic energy of system",
fontdict={'family': 'serif',
'color':'black',
'weight':'bold',
'size':18})
plt.xlabel(r'Time(ns)',
fontdict={'family': 'serif','color':'black','weight':'bold','size':16})
plt.ylabel(r"$\mathbf{E}$/$\mathbf{E_0}$",fontsize=16)
ax.set_xticks(x)
plt.savefig("energy.png")
#plt.show()
ValueError: Image size of 747x3360143 pixels is too large. It must be less than 2^16 in each direction.
Hello everyone, when I validate this code for a representation of 4 graphs and I put the legend of each graph, I have an error of size. I have attached the output image that I have with the error
I think this is happening because of your legend. I'm not sure why it is functioning like in the image you posted - I could not re-create that behavior with your code. I would recommend trying to comment out the legend and see if your code works. You may find the matplotlib documentation legend tutorial helpful.
If you can provide more info about the e1, e2, e3, and e4 values it would help simulate and troubleshoot more effectively.
A couple of other comments:
You don't need the line ax = plt.gca(), it is already defined as such
If e1['E'] and others like it are pandas series or numpy arrays, then you don't need to re-initialize the arrays for each variable
I attempted to simulate some lines like yours and implemented in your code.
import matplotlib
import matplotlib.pyplot as plt
x =[0,1,2,3,4]
plt.rcParams.update({"text.usetex": False, "font.family": "serif", "font.serif": ["Palatino"]})
#e appears to be some large array
#simulate with some large array
e = np.linspace(0.001, 4, 1000)
x1 = x2 = x3 = x4 = e
y1 = np.log(e)
y2 = np.log(e) + 0.5
y3 = np.log(e) + 1
y4 = np.log(e) + 1.5
fig, ax = plt.subplots(figsize=(12,8),frameon=False, dpi=100)
ax.plot(x1,y1,"o:",label= '300K',c='blue',linewidth=0.5)
ax.plot(x1,y2,"o:",label= '400K',c='red',linewidth=0.5)
ax.plot(x1,y3,"o:",label= '500K',c='black',linewidth=0.5)
ax.plot(x1,y4,"o:",label= '1000K',c='green',linewidth=0.5)
plt.grid()
plt.tick_params(axis ='both', labelsize =12)
plt.legend(loc="best")
title = plt.title(r"Variation of the kinetic energy of system", fontsize=18, fontweight='bold')
x_label = plt.xlabel(r'Time(ns)', fontsize=16, fontweight='bold')
y_label = plt.ylabel(r"$\mathbf{E}$/$\mathbf{E_0}$",fontsize=16)
#ax.set_xticks(x)

Drawing lines between two points with twinaxes

I have followed this example (Drawing lines between two plots in Matplotlib) but am running into problems. I believe it has something to do with the fact that I essentially have two different y points, but am not sure how to amend the code to fix it. I would like the line to start at one point and end at the other point directly below it, as well as plotting for all lines.
fig=plt.figure(figsize=(22,10), dpi=150)
ax1 = fig.add_subplot(1, 1, 1)
ax2 = ax1.twinx()
n = 10
y1 = np.random.random(n)
y2 = np.random.random(n) + 1
x1 = np.arange(n)
ax1.scatter(x1, y1)
ax2.scatter(x1, y2)
i = 1
xy = (x1[i],y1[i])
con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data",
axesA=ax1, axesB=ax2, color="red")
ax2.add_artist(con)
ax1.plot(x1[i],y1[i],'g+',markersize=12)
ax2.plot(x1[i],y1[i],'g+',markersize=12)
Just iterate over zipped (x, y1, y2):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import ConnectionPatch
fig = plt.figure(figsize=(10, 5), dpi=100)
ax1 = fig.add_subplot(1, 1, 1)
ax2 = ax1.twinx()
n = 10
y1 = np.random.random(n)
y2 = np.random.random(n) + 1
x1 = np.arange(n)
# I add some colors blue for left y-axis, red for right y-axis
ax1.scatter(x1, y1, c='b')
ax2.scatter(x1, y2, c='r')
# Now iterate over paired x, and 2 y values:
for xi, y1i, y2i in zip(x1, y1, y2):
con = ConnectionPatch(
xyA=(xi, y1i),
xyB=(xi, y2i),
coordsA="data",
coordsB="data",
axesA=ax1,
axesB=ax2,
color='g',
)
ax1.add_artist(con)
plt.show()
Out:

Colorbar/Legend options when overlaying tricontour plots

I have created three tricontour plots and have overlayed these plots as shown below:
finalDf = pca_cont('%_helix')
x1 = list(finalDf['pc1'])
y1 = list(finalDf['pc2'])
z1 = list(finalDf['%_helix'])
zf1 = threshold (z1, 0.60)
finalDf2 = pca_cont('%_sheet')
x2 = list(finalDf2['pc1'])
y2 = list(finalDf2['pc2'])
z2 = list(finalDf2['%_sheet'])
zf2 = threshold (z2, 0.60)
finalDf3 = pca_cont('%_coil')
x3 = list(finalDf3['pc1'])
y3 = list(finalDf3['pc2'])
z3 = list(finalDf3['%_coil'])
zf3 = threshold (z3, 0.90)
plt.figure(figsize = (16,16))
plt.tricontour(x1,y1,zf1,500,cmap="Reds", alpha=.5)
plt.tricontour(x2,y2,zf2,500,cmap="Greens", alpha=.5)
plt.tricontour(x3,y3,zf3,500,cmap="Blues", alpha=.5)
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.title("100 Dimension Embedding")
plt.colorbar()
plt.show()
But my problem is that I have three sets of colours and various depths so when I add a colorbar using plt.colorbar() it wont show all three. What is the best way to represent the three sets of data? Should I just plot a simple legend or is there a way to represent the three sets of data using three colorbars?
Thank you

Python (numpy) - correlate two binned plots

My question is how do I correlate my two binned plots and output a Pearson's correlation coefficient?
I'm not sure how to properly extract the binned arrays necessary for the np.corrcoef function. Here's my script:
import numpy as np
import matplotlib.pyplot as plt
A = np.genfromtxt('data1.txt')
x1 = A[:,1]
y1 = A[:,2]
B=np.genfromtxt('data2.txt')
x2 = B[:,1]
y2 = B[:,2]
fig = plt.figure()
plt.subplots_adjust(hspace=0.5)
plt.subplot(121)
AA = plt.hexbin(x1,y1,cmap='jet',gridsize=500,vmin=0,vmax=450,mincnt=1)
plt.axis([-180,180,-180,180])
cb = plt.colorbar()
plt.title('Data1')
plt.subplot(122)
BB = plt.hexbin(x2,y2,cmap='jet',gridsize=500,vmin=0,vmax=450,mincnt=1)
plt.axis([-180,180,-180,180])
cb = plt.colorbar()
plt.title('Data 2')
array1 = np.ndarray.flatten(AA)
array2 = np.ndarray.flatten(BB)
print np.corrcoef(array1,array2)
plt.show()
The answer can be found in the documentation:
Returns: object
a PolyCollection instance; use get_array() on this PolyCollection to get the counts in each hexagon.
Here's a revised version of you code:
A = np.genfromtxt('data1.txt')
x1 = A[:,1]
y1 = A[:,2]
B = np.genfromtxt('data2.txt')
x2 = B[:,1]
y2 = B[:,2]
# make figure and axes
fig, (ax1, ax2) = plt.subplots(1, 2)
# define common keyword arguments
hex_params = dict(cmap='jet', gridsize=500, vmin=0, vmax=450, mincnt=1)
# plot and set titles
hex1 = ax1.hexbin(x1, y1, **hex_params)
hex2 = ax2.hexbin(x2, y2, **hex_params)
ax1.set_title('Data 1')
ax2.set_title('Data 2')
# set axes lims
[ax.set_xlim(-180, 180) for ax in (ax1, ax2)]
[ax.set_ylim(-180, 180) for ax in (ax1, ax2)]
# add single colorbar
fig.subplots_adjust(right=0.8, hspace=0.5)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(hex2, cax=cbar_ax)
# get binned data and corr coeff
binned1 = hex1.get_array()
binned2 = hex2.get_array()
print np.corrcoef(binned1, binned2)
plt.show()
Two comments though: are you sure you want the pearson correlation coefficient? What are you actually trying to show? If you want to show the distributions are the same/different, you might want to use a Kolmogorov-Smirnov test.
Also don't use jet as a colormap. Jet is bad.

Multiple plots on same figure with DataFrame.Plot

While I can get multiple lines on a chart and multiple bars on a chart - I cannot get a line and bar on the same chart using the same PeriodIndex.
Faux code follows ...
# play data
n = 100
x = pd.period_range('2001-01-01', periods=n, freq='M')
y1 = (Series(np.random.randn(n)).diff() + 5).tolist()
y2 = (Series(np.random.randn(n)).diff()).tolist()
df = pd.DataFrame({'bar':y2, 'line':y1}, index=x)
# let's plot
plt.figure()
ax = df['bar'].plot(kind='bar', label='bar')
df['line'].plot(kind='line', ax=ax, label='line')
plt.savefig('fred.png', dpi=200)
plt.close()
Any help will be greatly appreciated ...
The problem is: bar plots don't use index values as x axis, but use range(0, n). You can use twiny() to create a second axes that share yaxis with the bar axes, and draw line curve in this second axes.
The most difficult thing is how to align x-axis ticks. Here we define the align function, which will align ax2.get_xlim()[0] with x1 in ax1 and ax2.get_xlim()[1] with x2 in ax1:
def align_xaxis(ax2, ax1, x1, x2):
"maps xlim of ax2 to x1 and x2 in ax1"
(x1, _), (x2, _) = ax2.transData.inverted().transform(ax1.transData.transform([[x1, 0], [x2, 0]]))
xs, xe = ax2.get_xlim()
k, b = np.polyfit([x1, x2], [xs, xe], 1)
ax2.set_xlim(xs*k+b, xe*k+b)
Here is the full code:
from matplotlib import pyplot as plt
import pandas as pd
from pandas import Series
import numpy as np
n = 50
x = pd.period_range('2001-01-01', periods=n, freq='M')
y1 = (Series(np.random.randn(n)) + 5).tolist()
y2 = (Series(np.random.randn(n))).tolist()
df = pd.DataFrame({'bar':y2, 'line':y1}, index=x)
# let's plot
plt.figure(figsize=(20, 4))
ax1 = df['bar'].plot(kind='bar', label='bar')
ax2 = ax1.twiny()
df['line'].plot(kind='line', label='line', ax=ax2)
ax2.grid(color="red", axis="x")
def align_xaxis(ax2, ax1, x1, x2):
"maps xlim of ax2 to x1 and x2 in ax1"
(x1, _), (x2, _) = ax2.transData.inverted().transform(ax1.transData.transform([[x1, 0], [x2, 0]]))
xs, xe = ax2.get_xlim()
k, b = np.polyfit([x1, x2], [xs, xe], 1)
ax2.set_xlim(xs*k+b, xe*k+b)
align_xaxis(ax2, ax1, 0, n-1)
and the output:

Categories