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')
Related
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
I'm trying to fit the sizes of 2x2 subplots so they line up correctly.
I want to create the following subplot/axes structure:
ax1 has a set aspect ratio in data coordinates (e.g. data is not scaled when aspect=1)
ax2 and ax3 have a set (box) aspect ratio in display/figure coordinates (e.g. they appear as squares when box_aspect=1)
ax1 and ax3 share the x axis limits and have the same width
ax1 and ax2 share the y axis limits and have the same height
ax4 fits in the last box so that it has the width of ax2 and the height of ax3
I need this for the following project (it's an animation):
Imagine having ax1 as the world space in which a point is moving. (the coordinates should not be warped)
The adjacent plots show the x(ax3) and y(ax2) coordinates of the point over time. (I want to be able to set the aspect ratio of these subplots)
The last axes/plot is not related to any of the coordinates but should fit in nicely.
I created a minimal example and added descriptive text to the picture:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = plt.subplot(221, anchor='SE', aspect=1, xlim=(0,1), ylim=(0,2))
ax2 = plt.subplot(222, anchor='SW', box_aspect=1, sharey=ax1, xlim=(0,3))
ax3 = plt.subplot(223, anchor='NE', box_aspect=1, sharex=ax1, ylim=(0,4))
ax4 = plt.subplot(224, anchor='NW', xlim=(0,5), ylim=(0,6))
plt.show()
Notes:
the anchors are set so there is not so much whitespace between the subplots
the x and y limits are arbitrarily chosen and should not matter
I encountered a similar issue as I was plotting GeoJson data in matplotlib.
I solved it by creating a one subplot figure setting aspect=1
# import the required libraries
import geopandas as gpd
import matplotlib.pyplot as plt
# Define the file path
fp = r"\Your-file-full-path\file.geojson"
# Read the GeoJSON file similarly as Shapefile
mygeojson = gpd.read_file(fp)
# Create a figure with one subplot
fig = plt.figure()
# Plot the grid with column-to-plot (as you set cmap, scheme, and aspect hyper-params)
mygeojson.plot(aspect=1, column = 'geojson-column-to-plot', cmap = 'gist_rainbow', scheme = 'equalinterval', k=9, linewidth=0, legend=True);
# Add title
plt.title("Your GeoDataFrame object title");
# Remove white space around the figure
plt.tight_layout()
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
I am trying to make the below grid of plots a little bit cleaner. I don't want the tick marks on the left side and the bottom to overlap. I have tried to despine the axes by trying the below code, but it doesn't seem to work. Anyone have any suggestions?
fig, ax = plt.subplots(figsize=(15,10))
cols = ['x6', 'x7', 'x16', 'x17']
subset = df[cols]
normed_df = (subset-subset.min())/(subset.max()-subset.min())
style.use('seaborn-darkgrid')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
for sp in range(4):
ax = fig.add_subplot(2,2, sp+1)
ax.hist(normed_df[cols[sp]], density=True)
normed_df[cols[sp]].plot.kde(ax=ax)
ax.tick_params(bottom="off", top="off", left="off", right="off")
After running the above code, I am getting the following plots, however, the ticks are still overlapping.
either do what #Arne suggested:
fig, ax = plt.subplots(rows, cols) #makes a grid of subplots
or make your first two lines this:
fig, ax = plt.subplots(figsize=(15,10))
ax.axis('off')
this will remove the axis around the entire subplot before adding your additional subplots
When you call plt.subplots() without specifying a grid, it creates those axes across the whole figure whose tick marks and labels interfere with your subplot tick labels in the final plot. So change your first line of code to this:
fig, ax = plt.subplots(2, 2, figsize=(15,10))
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)