I'm trying to plot an A4 PDF with the following layout:
1 chart spanning 2 columns
2 charts, each spanning 1 column
1 chart spanning 2 columns
I have the following code:
fig = plt.figure(figsize=(8.27,11.69))
ax = fig.add_subplot(311)
ax = fig.add_subplot(323)
ax = fig.add_subplot(324)
ax = fig.add_subplot(315)
but i'm getting the following error:
ValueError: num must be 1 <= num <= 3, not 5
what am i missing?
This is the correct syntax:
fig = plt.figure()
ax = fig.add_subplot(311)
ax = fig.add_subplot(323)
ax = fig.add_subplot(324)
ax = fig.add_subplot(313)
However, for this kind of thing, you will gain in readability if you use GridSpec https://matplotlib.org/3.1.1/tutorials/intermediate/gridspec.html The following code yields the exact same output, but is (at least to me) easier to understand
fig = plt.figure()
gs = fig.add_gridspec(3, 2)
fig.add_subplot(gs[0, :])
fig.add_subplot(gs[1, 0])
fig.add_subplot(gs[1, 1])
fig.add_subplot(gs[2, :])
Related
I have written plot_dataframe() to create two subplots (one for line chart and another for histogram bar chart) for a dataframe that is passed via argument.
Then I call this function from plot_kernels() with multiple dataframs.
def plot_dataframe(df, cnt):
row = df.iloc[0].astype(int) # First row in the dataframe
plt.subplot(2, 1, 1)
row.plot(legend=cnt) # Line chart
plt.subplot(2, 1, 2)
df2 = row.value_counts()
df2.reindex().plot(kind='bar', legend=cnt) # Histogram
def plot_kernels(mydict2):
plt.figure(figsize=(20, 15))
cnt=1
for key in my_dict2:
df = my_dict2[key]
plot_dataframe(df, cnt)
cnt = cnt + 1
plt.show()
The dictionary looks like
{'K1::foo(bar::z(x,u))': Value Value
0 10 2
1 5 2
2 10 2, 'K3::foo(bar::y(z,u))': Value Value
0 6 12
1 7 13
2 8 14}
And based on the values in row[0], [10,2] are shown in blue line and [6,12] are shown in orange line. For histogram, they are similar. As you can see the legends in the subplots are shown as 0 in the figure. I expect to see 1 and 2. How can I fix that?
Change legend to label, then force the legend after you plot everything:
def plot_dataframe(df, cnt,axes):
row = df.iloc[0].astype(int) # First row in the dataframe
row.plot(label=cnt, ax=axes[0]) # Line chart -- use label, not legend
df2 = row.value_counts()
df2.plot(kind='bar', ax=axes[1], label=cnt) # Histogram
def plot_kernels(d):
# I'd create the axes first and pass to the plot function
fig,axes = plt.subplots(2,1, figsize=(20, 15))
cnt=1
for key in d:
df = d[key]
plot_dataframe(df, cnt, axes=axes)
cnt = cnt + 1
# render the legend
for ax in axes:
ax.legend()
plt.show()
Output:
I want to plot 5 timetable series using nested for loop. here is my code attached and the results of the plots. I use the first loop to generate each when I put plt.show() outside the first loop, it will just plot the fifth series, and when I put the plt.show() outside the inside (second) loop, it will just plot the first series.
How can I plot all five series using the nested loop?
How can I plot all of the same y variables with the same bound (shared y) in the loop?
fig = plt.figure(figsize=(38, 55))
for i in range(len(Traj_List)): # first loop for creating subplots for each of timeseries data (Trajectory of some states like joint angle and joint speed and muscle activations)
# States.
stateNames = list(Traj_List[i].getStateNames()) # Traj_List is a list of timeseries data
numStates = len(stateNames)
dim = np.sqrt(numStates)
if dim == np.ceil(dim):
numRows = int(dim)
numCols = int(dim)
else:
numCols = int(np.min([numStates, 4]))
numRows = int(np.floor(numStates / 4))
if not numStates % 4 == 0:
numRows += 1
# color = iter(plt.rainbow(np.linspace(0, 1, 5)))
color = ['r', 'b', 'g', 'y', 'm']
lines = ["-", "--", "-.", ":", "-."]
linewidth = [3, 2.5, 3.5, 2, 3]
for j in np.arange(numStates): # the second loop plots each time series data (states) against time.
ax = fig.add_subplot(numRows, numCols, int(j + 1))
ax.plot(Traj_List[i].getTimeMat(),
Traj_List[i].getStateMat(stateNames[j]), linestyle=lines[i], color=color[i],
linewidth=linewidth[i], label=Label_List[i])
stateName = stateNames[j]
ax.set_title(stateName)
ax.set_xlabel('time (s)')
# ax.set_xlim(0, 1)
ax.legend(loc='best')
if 'value' in stateName:
ax.set_ylabel('position (rad)')
elif 'speed' in stateName:
ax.set_ylabel('speed (rad/s)')
elif 'activation' in stateName:
ax.set_ylabel('activation (-)')
ax.set_ylim(0, 1)
# plt.show()
fig.tight_layout()
plt.show()
plt.close()
Try to only make the figure once, and save the axes. In pseudo code, because I can't run your code anyway:
for i in range(N):
M, N = size(data)
if i == 0:
# only make the axes once!
fig, axs = plt.subplots(M, N)
for j in range(M*N):
axs.flat[j].plot(yourdata)
I'm sure my ranges are not correct for your data, but that is easy to sort out. The point is don't keep recreating the axes using plt.add_subplot. Just create them once, and then plot on them.
I am having a problem with preventing grid lines in 'darkgrid' from disecting a line associated with the X1 axis when plotting a twinx() plot. I can "fix" the problem by not using 'darkgrid' or by passing an empty list to X2 (and lose the axis labels to - se last line), but I do want 'darkgrid' and x2 axis labels.
#Some imports
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
sns.set_style("darkgrid")
%matplotlib inline
#Data
d = np.arange(0, 1000, 100)
x1 = d/30 #redmodel
x2 = np.sqrt(d) #bluemodel
#Figure
fig = plt.figure(figsize = (8, 12))
sy1 = 'r-' #redmodel
sy2 = 'b-' #bluemodel
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
_ = ax1.plot(x1, d, sy1)
_ = ax1.set_ylabel('D')
_ = ax1.set_ylim(0, 1000)
_ = ax1.set_xlabel('X1')
_ = ax1.set_xlim(0, 31)
_ = ax2.plot(x2, d, sy2)
_ = ax2.set_xlabel('X2')
_ = ax2.set_xlim(0, 31)
#_ = ax2.set_xticks([]) #Empty list passed to omit_xticks, otherwise ax2 gridines disect red line
As I was looking for solutions to this problem I stumbled upon the axes_grid1 toolkit collection of helper classes which has the twin() option (in addition to twinx and twiny) which may be the solution to my problem. But if you know of a simpler one please help me out.
The intent of your question is to answer as the challenge is to draw the grid lines of the x2 axis across the red line. I think you can simply set a standard for the grid lines of the x2 axis.
_ = ax2.grid(which='major', axis='x', zorder=1.0)
I have created the best fit lines for the dataset using the following code:
fig, ax = plt.subplots()
for dd,KK in DATASET.groupby('Z'):
fit = polyfit(x,y,3)
fit_fn = poly1d(fit)
ax.plot(KK['x'],KK['y'],'o',KK['x'], fit_fn(KK['x']),'k',linewidth=4)
ax.set_xlabel('x')
ax.set_ylabel('y')
The graph displays the best fit line for each group of Z. I want print the equation of the best fit line on top of the line.Please suggest what can i do out here
So you need to write some function that convert a poly parameters array to a latex string, here is an example:
import pylab as pl
import numpy as np
x = np.random.randn(100)
y = 1 + 2 * x + 3 * x * x + np.random.randn(100) * 2
poly = pl.polyfit(x, y, 2)
def poly2latex(poly, variable="x", width=2):
t = ["{0:0.{width}f}"]
t.append(t[-1] + " {variable}")
t.append(t[-1] + "^{1}")
def f():
for i, v in enumerate(reversed(poly)):
idx = i if i < 2 else 2
yield t[idx].format(v, i, variable=variable, width=width)
return "${}$".format("+".join(f()))
pl.plot(x, y, "o", alpha=0.4)
x2 = np.linspace(-2, 2, 100)
y2 = np.polyval(poly, x2)
pl.plot(x2, y2, lw=2, color="r")
pl.text(x2[5], y2[5], poly2latex(poly), fontsize=16)
Here is the output:
Here's a one liner.
If fit is the poly1d object, while plotting the fitted line, just use label argument as bellow,
label='y=${}$'.format(''.join(['{}x^{}'.format(('{:.2f}'.format(j) if j<0 else '+{:.2f}'.format(j)),(len(fit.coef)-i-1)) for i,j in enumerate(fit.coef)]))
I am having trouble using Matplotlib to plot multiple series in a loop (Matplotlib 1.0.0, Python 2.6.5, ArcGIS 10.0). Forum research pointed me to application of an Axes object, in order to plot multiple series on the same plot. I see how this works well for data generated outside of a loop (sample scripts), but when I insert the same syntax and add the second series into my loop that pulls data from database, I get the following error:
": unsupported operand type(s) for -: 'NoneType' and 'NoneType' Failed to execute (ChartAge8)."
Below is my code - any suggestions or comments are much appreciated!
import arcpy
import os
import matplotlib
import matplotlib.pyplot as plt
#Variables
FC = arcpy.GetParameterAsText(0) #feature class
P1_fld = arcpy.GetParameterAsText(1) #score field to chart
P2_fld = arcpy.GetParameterAsText(2) #score field to chart
plt.subplots_adjust(hspace=0.4)
nsubp = int(arcpy.GetCount_management(FC).getOutput(0)) #pulls n subplots from FC
last_val = object()
#Sub-plot loop
cur = arcpy.SearchCursor(FC, "", "", P1_fld)
i = 0
x1 = 1 # category 1 locator along x-axis
x2 = 2 # category 2 locator along x-axis
fig = plt.figure()
for row in cur:
y1 = row.getValue(P1_fld)
y2 = row.getValue(P2_fld)
i += 1
ax1 = fig.add_subplot(nsubp, 1, i)
ax1.scatter(x1, y1, s=10, c='b', marker="s")
ax1.scatter(x2, y2, s=10, c='r', marker="o")
del row, cur
#Save plot to pdf, open
figPDf = r"path.pdf"
plt.savefig(figPDf)
os.startfile("path.pdf")
If what you want to do is plot several stuff reusing the same plot what you should do it create the figure object outside the loop and then plot to that same object everytime, something like this:
fig = plt.figure()
for row in cur:
y1 = row.getValue(P1_fld)
y2 = row.getValue(P2_fld)
i += 1
ax1 = fig.add_subplot(nsubp, 1, i)
ax1.scatter(x1, y1, s=10, c='b', marker="s")
ax1.scatter(x2, y2, s=10, c='r', marker="o")
del row, cur