How to align logarithmic scale ticks across subplots? - python

I want to fix the position of the ticks on the logarithmic scale, such that they are the same in each subplot (see red annotation in image).
My code looks like this:
ax = fig.add_subplot(2,2, axis)
ax2 = ax.twinx()
ax2.set_yscale('log')
ax2.set_ylim(0,100)
Right now, set_yscale=('log') optimizes the tick spacing for each subplot. I prefer to adopt the tick spacing of the upper right subplot.

You can achieve this by getting the limits of the left twin axis and setting it as the limits of the right twin axis.
Consider the following working example. Follow this procedure for the subplots you want to align the axes of.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 3))
axl = fig.add_subplot(121)
axr = fig.add_subplot(122)
ax1 = axl.twinx()
ax1.plot(np.logspace(-2, 3, 5))
ax1.set_yscale('log')
ax2 = axr.twinx()
ax2.plot(np.logspace(0, 3, 5))
ax2.set_yscale('log')
ax2.set_ylim(ax1.get_ylim()) # <-- This is the key line
plt.tight_layout()
plt.show()

OP's solution:
Plot a dummy curve and set alpha=0. Make sure the curve spans y_min and y_max.
fig = plt.figure()
axes = [1,2,3,4]
for axis in axes:
ax = fig.add_subplot(2,2, axis)
ax2 = ax.twinx()
ax2.set_yscale('log')
ax2.plot(x_dummy, y_dummy, alpha=0) # <-- dummy plot
x_real, y_real = func_that_loads_data() # <-- your interesting plot
curve1 = ax2.plot(x_real, y_real)
plt.show()
The solution provided by Sheldore was impractical to implement because I plot my data using a for-loop (unavoidable unless I escalate the number of variables).
Since I overwrite the ax variable on every iteration, I would have to save the y-limit as a global variable. Read here why global variables should be avoided.
ax = fig.add_subplot(2,2, axis)
ax2 = ax.twinx()
ax2.set_yscale('log')
if axis == 1:
global yscale
yscale = ax2.get_ylim() # <-- where the magic happens
elif axis > 1:
ax2.set_ylim(yscale)

Related

AttributeError: 'list' object has no attribute 'get_label'

I'm trying basic plot with two y-axis and one x-axis. To obtain the legend information for different curve I'm getting AttributeError.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2.0*np.pi, 101)
y = np.sin(x)
z = np.sinh(x)
# separate the figure object and axes object from the plotting object
fig, ax1 = plt.subplots()
# Duplicate the axes with a differebt y axis and the same x axis
ax2 = ax1.twinx() # ax2 and ax1 will have common x axis and different y axis
# plot the curves on axes 1, and 2 and get the curve hadles
curve1 = ax1.plot(x, y, label="sin", color='r')
curve2 = ax2.plot(x, z, label="sinh", color='b')
# Make a curves list to access the parameters in the curves
curves = [curve1, curve2]
# Add legend via axes1 or axex 2 object.
# ax1.legend() will not display the legend of ax2
# ax2.legend() will not display the legend of ax1
ax1.legend(curves, [curve.get_label() for curve in curves])
#ax2.legend(curves, [curve.get_label() for curve in curves]) also valid
# Global figure properties
plt.title("Plot of sine and hyperbolic sine")
plt.show()
I'm getting Error on below line:
ax1.legend(curves, [curve.get_label() for curve in curves])
Please let me know if anyone know why its happening.
This will solve your problem, try this:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2.0*np.pi, 101)
y = np.sin(x)
z = np.sinh(x)
# separate the figure object and axes object from the plotting object
fig, ax1 = plt.subplots()
# Duplicate the axes with a differebt y axis and the same x axis
ax2 = ax1.twinx() # ax2 and ax1 will have common x axis and different y axis
# plot the curves on axes 1, and 2 and get the curve hadles
curve1 = ax1.plot(x, y, label="sin", color='r')
curve2 = ax2.plot(x, z, label="sinh", color='b')
# Make a curves list to access the parameters in the curves
curves = curve1 + curve2
# Add legend via axes1 or axex 2 object.
# ax1.legend() will not display the legend of ax2
# ax2.legend() will not display the legend of ax1
labs = [curve.get_label() for curve in curves]
ax1.legend(curves, labs, loc=0)
#ax1.legend(curves, [curve.get_label() for curve in curves])
#ax2.legend(curves, [curve.get_label() for curve in curves]) also valid
# Global figure properties
plt.title("Plot of sine and hyperbolic sine")
plt.show()
if you read the pyplot documentation, you can see that the plot function returns a list, which clearly does not have a method get_label().
What you want is probably what is described in matplotlib's legend documentation, which is automatic detection of your plot's labels.
This means that you do not have to store your line results, and your legend calls goes from
ax1.legend(curves, [curve.get_label() for curve in curves])
to simply
ax1.legend()
In my opinion reading the documentation not only solves your problem in most cases, but gives you a very important ability in the world of programming, which is to be able to solve the problems on your own (as well as reading documentations).
Cheers

Python: Set only 1/4 subplots as polar

I have an assignment where I am trying to replicate the following subplots
I successfully replicated the three non-polar plots, but I cannot figure out how to set the fourth plot to polar. Here is what I have so far with my code, only including code relevant to the polar plot.
nmax=101 # choose a high number to "smooth out" lines in plots
x = np.linspace(0,20,nmax) # create an array x
y = np.exp(-x/4)*np.sin(x) # y for the top two subplots
fig, axs = plt.subplots(2, 2)
# bottom right subplot controls
axs[1, 1].polar(x, y)
This will give the error, AttributeError: 'AxesSubplot' object has no attribute 'polar'. How would I set the subplot to polar so I can replicate the plot?
You might have to define each axis separately, rather than using plt.subplots
fig = plt.figure()
ax1 = plt.subplot(221)
ax2 = plt.subplot(222)
ax3 = plt.subplot(223)
ax4 = plt.subplot(224, projection = 'polar')

Removing overlapping x-axis labels in pyplot

I'm new to python and attempting to chart some time series data. I'm using pyplot to create 3 stacked line charts which have the same x-axis (dates), but a different scale for the y-axes. However, each y-axis, as well as the x-axis for the bottom chart, have overlapping labels. There are labels generated from 0 to 1, as well as axis labels from my data set. How do I turn 'off' the auto-generated 0 to 1 labels on the y-axes and the bottom x-axis?
fig, ax = plt.subplots(3,1,sharex='all', squeeze=False, figsize=(12,8))
ax = fig.add_subplot(3,1,1)
plt.plot(df1['date'], df1['value'])
ax2 = fig.add_subplot(3,1,2)
plt.plot(df2['date'], df2['value'])
ax3 = fig.add_subplot(3,1,3)
plt.plot(df3['date'], df3['value'])
plt.show()
You can see the issue in the below picture. Any help is greatly appreciated!
You have already created subplots with all the axes in the initial assignment
fig, ax = plt.subplots(3,1,sharex='all', squeeze=False, figsize=(12,8))
therefore the following assignements of
ax = fig.add_subplot(3,1,1)
ax2 = fig.add_subplot(3,1,2)
ax3 = fig.add_subplot(3,1,3)
are not only unnecessary, but they seem to overlap the already created subplots (if you change it to add_subplot(2,1,1) you will notice it just starts dividing figure again and overlaying axes on top of each other).
What you want to do, is access the axes created in plt.subplots() call:
fig, ax = plt.subplots(3,1,sharex='all', squeeze=False, figsize=(12,8))
ax[0].plot(df1['date'], df1['value'])
ax[1].plot(df2['date'], df2['value'])
ax[2].plot(df3['date'], df3['value'])
plt.show()
Simulated Output:
Data from seaborn tips dataset

Changing Index of the axis in a Graph in matplotlib

I have written a code which will plot a graph of Time VS Amplitude. Now , I want to change the index which is on the horizontal axis. I want to know how I can do it for a single plot and also for the subplots. I want the range of the horizontal axis to be from 0 to 2*pi.
#the following code was written for plotting
fig, (ax1, ax2 ,ax3) = plt.subplots(3 ,constrained_layout = True)
fig.suptitle('AMPLITUDE MODULATION' ,color = 'Red')
ax1.plot(message_signal)
ax1.set_title('Message Signal' ,color = 'green')
I expect the x-axis to go from 0 to 2*pi only. In short, I want to customize the indexing of the x-axis
You can use xlim to set the limits of the x-axis for whole plot or specific axes, e.g. plt.xlim(0, 1) or ax1.set_xlim(0, 1).
Here I set the limits for the x-axis to be [0, 3*pi]
fig, (ax1, ax2 ,ax3) = plt.subplots(3, constrained_layout = True)
fig.suptitle('AMPLITUDE MODULATION', color = 'Red')
x = np.linspace(0, 2*np.pi, 1000)
ax1.plot(x, np.sin(x))
ax1.set_title('Message Signal', color = 'green')
ax1.set_xlim(0, 3*np.pi)

plot changes not affecting all subplots [duplicate]

Im trying to plot a scatter matrix. I'm building on the example given in this thread Is there a function to make scatterplot matrices in matplotlib?. Here I have just modified the code slightly to make the axis visible for all the subplots. The modified code is given below
import itertools
import numpy as np
import matplotlib.pyplot as plt
def main():
np.random.seed(1977)
numvars, numdata = 4, 10
data = 10 * np.random.random((numvars, numdata))
fig = scatterplot_matrix(data, ['mpg', 'disp', 'drat', 'wt'],
linestyle='none', marker='o', color='black', mfc='none')
fig.suptitle('Simple Scatterplot Matrix')
plt.show()
def scatterplot_matrix(data, names, **kwargs):
"""Plots a scatterplot matrix of subplots. Each row of "data" is plotted
against other rows, resulting in a nrows by nrows grid of subplots with the
diagonal subplots labeled with "names". Additional keyword arguments are
passed on to matplotlib's "plot" command. Returns the matplotlib figure
object containg the subplot grid."""
numvars, numdata = data.shape
fig, axes = plt.subplots(nrows=numvars, ncols=numvars, figsize=(8,8))
fig.subplots_adjust(hspace=0.05, wspace=0.05)
for ax in axes.flat:
# Hide all ticks and labels
ax.xaxis.set_visible(True)
ax.yaxis.set_visible(True)
# # Set up ticks only on one side for the "edge" subplots...
# if ax.is_first_col():
# ax.yaxis.set_ticks_position('left')
# if ax.is_last_col():
# ax.yaxis.set_ticks_position('right')
# if ax.is_first_row():
# ax.xaxis.set_ticks_position('top')
# if ax.is_last_row():
# ax.xaxis.set_ticks_position('bottom')
# Plot the data.
for i, j in zip(*np.triu_indices_from(axes, k=1)):
for x, y in [(i,j), (j,i)]:
axes[x,y].plot(data[x], data[y], **kwargs)
# Label the diagonal subplots...
for i, label in enumerate(names):
axes[i,i].annotate(label, (0.5, 0.5), xycoords='axes fraction',
ha='center', va='center')
# Turn on the proper x or y axes ticks.
for i, j in zip(range(numvars), itertools.cycle((-1, 0))):
axes[j,i].xaxis.set_visible(True)
axes[i,j].yaxis.set_visible(True)
fig.tight_layout()
plt.xticks(rotation=45)
fig.show()
return fig
main()
I cant seem to be able to rotate the x-axis text of all the subplots. As it can be seen, i have tried the plt.xticks(rotation=45) trick. But this seems to perform the rotation for the last subplot alone.
Just iterate through the axes tied to the figure, set the active axes to the iterated object, and modify:
for ax in fig.axes:
matplotlib.pyplot.sca(ax)
plt.xticks(rotation=90)
plt only acts on the current active axes. You should bring it inside your last loop where you set some of the labels visibility to True:
# Turn on the proper x or y axes ticks.
for i, j in zip(range(numvars), itertools.cycle((-1, 0))):
axes[j,i].xaxis.set_visible(True)
axes[i,j].yaxis.set_visible(True)
for tick in axes[i,j].get_xticklabels():
tick.set_rotation(45)
for tick in axes[j,i].get_xticklabels():
tick.set_rotation(45)
for ax in fig.axes:
ax.tick_params(labelrotation=90)

Categories

Resources