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))
Related
As per this question, moving the xticks and labels of an AxesSubplot object can be done with ax.xaxis.tick_top(). However, I cannot get this to work with multiple axes inside a figure.
Essentially, I want to move the xticks to the very top of the figure (only displayed at the top for the subplots in the first row).
Here's a silly example of what I'm trying to do:
fig, axs = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
fig.set_figheight(5)
fig.set_figwidth(10)
for ax in axs.flatten():
ax.xaxis.tick_top()
plt.show()
Which shows
My desired result is this same figure but with the xticks and xticklabels at the top of the two plots in the first row.
Credits to #BigBen for the sharex comment. It is indeed what's preventing tick_top to work.
To get your results, you can combine using tick_top for the two top plots and use tick_params for the bottom two:
fig, axs = plt.subplots(2, 2, sharex=False) # Do not share xaxis
for ax in axs.flatten()[0:2]:
ax.xaxis.tick_top()
for ax in axs.flatten()[2:]:
ax.tick_params(axis='x',which='both',labelbottom=False)
See a live implementation here.
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
When I run the code shown below I get a figure containing 2 legends. I can't figure out why two are being plotted and I havent been able to remove one of them. My aim is to keep the legend that is outside of the figure, remove the one thats inside the figure and also somehow stop the weird cropping that is cutting off the right side of the legend outside the figure.
I had a previous question asking something similar, but that issue was solved by using seaborns scatterplot instead of the relplot. Sadly neither of the answers that worked in that question work here. If this problem is arising out of an "uncoventional" way of plotting the type of figure I'm trying to make, then please let me know. Doing it properly is better than hacking your way to the solution...
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
#setup
sns.set(font_scale=2)
sns.set_context('poster')
#figure and axes
fig = plt.figure(figsize=(20,20))
axs = {i:fig.add_subplot(330+i) for i in range(1,10)}
#create random data
r = np.random.randint
N=10
df = pd.DataFrame(columns=['No.','x1','x2','x3','y1','y2','y3'])
for i in range(N):
df.loc[i] = i+1,r(50,high=100),r(50,high=100),r(50,high=100),r(50,high=100),r(50,high=100),r(50,high=100)
#create axes labels
x_labels = ['x1','x2','x3']
y_labels = ['y1','y2','y3']
xy_labels = [(x,y) for y in y_labels for x in x_labels ]
#plot on axes
for i,(x_label,y_label) in enumerate(xy_labels):
if i ==0:#if statement so only one of the plots has legend='full'
a = sns.scatterplot(
data=df,
x=x_label,
y=y_label,
legend='full', #create the legend
ax=axs[i+1],
hue='No.',
palette=sns.color_palette("hls", N)
)
fig.legend(bbox_to_anchor=(1, 0.7), loc=2, borderaxespad=0.) #Move the legend outside the plot
a.legend_.remove() #attempt to remove the legend
else:
a = sns.scatterplot(
data=df,
x=x_label,
y=y_label,
legend=False,
ax=axs[i+1],
hue='No.',
palette=sns.color_palette("hls", N)
)
#remove axes labels from specific plots
if i not in [0,3,6]: axs[i+1].set_ylabel('')
if i not in [6,7,8]: axs[i+1].set_xlabel('')
#add line plots and set limits
for ax in axs.values():
sns.lineplot(x=range(50,100),y=range(50,100), ax=ax, linestyle='-')
ax.set_xlim([50,100])
ax.set_ylim([50,100])
fig.tight_layout()
You can add legend=False in the last part of your code.
#setup
sns.set(font_scale=2)
sns.set_context('poster')
#figure and axes
fig = plt.figure(figsize=(20,20))
axs = {i:fig.add_subplot(330+i) for i in range(1,10)}
#create axes labels
x_labels = ['x1','x2','x3']
y_labels = ['y1','y2','y3']
xy_labels = [(x,y) for y in y_labels for x in x_labels ]
#plot on axes
for i,(x_label,y_label) in enumerate(xy_labels):
if i ==0:#if statement so only one of the plots has legend='full'
a = sns.scatterplot(
data=df,
x=x_label,
y=y_label,
legend='full', #create the legend
ax=axs[i+1],
hue='No.',
palette=sns.color_palette("hls", N)
)
fig.legend(bbox_to_anchor=(1, 0.7), loc=2, borderaxespad=0.) #Move the legend outside the plot
a.legend_.remove() #attempt to remove the legend
else:
a = sns.scatterplot(
data=df,
x=x_label,
y=y_label,
legend=False,
ax=axs[i+1],
hue='No.',
palette=sns.color_palette("hls", N)
)
#remove axes labels from specific plots
if i not in [0,3,6]: axs[i+1].set_ylabel('')
if i not in [6,7,8]: axs[i+1].set_xlabel('')
#add line plots and set limits
for ax in axs.values():
sns.lineplot(x=range(50,100),y=range(50,100), ax=ax, linestyle='-', legend=False)
ax.set_xlim([50,100])
ax.set_ylim([50,100])
fig.tight_layout()
Result:
I am trying to get the values of xticks from one plot and then use these values for another plot but set the new ticks as 10 to the power of the other plot's ticks. The following lines doesn't do the job I am aiming for
labels=[item for item in ax1.get_xticklabels()]
ax2.set_xticklabels(['$10^{{{:d}}}$'.format(int(i)) for i in labels])
I will appreciate for any suggestion.
What about sharing axes ? This will fix the same limits and number of ticks for ax1 and ax2 :
fig, ax = plt.subplots(1, 2, sharex=True)
ax1 = ax[0]
ax2 = ax[1]
Then your code will do the trick since you are sure that both subplots have the same xticks
labels = [item for item in ax2.get_xticklabels()]
ax2.set_xticklabels(['$10^{{{:d}}}$'.format(int(i)) for i in labels])
Define data
x = np.linspace(0,2*np.pi,100)
y = 2*np.sin(x)
Plot
fig = plt.figure()
ax = plt.axes()
fig.add_subplot(ax)
ax.plot(x,y)
Add second axis
newax = plt.axes(axisbg='none')
Gives me ValueError: Unknown element o, even though it does the same thing as what I am about to describe. I can also see that this works (no error) to do the same thing:
newax = plt.axes()
fig.add_subplot(newax)
newax.set_axis_bgcolor('none')
However, it turns the background color of the original figure "gray" (or whatever the figure background is)? I don't understand, as I thought this would make newax transparent except for the axes and box around the figure. Even if I switch the order, same thing:
plt.close('all')
fig = plt.figure()
newax = plt.axes()
fig.add_subplot(newax)
newax.set_axis_bgcolor('none')
ax = plt.axes()
fig.add_subplot(ax)
ax.plot(x,y)
This is surprising because I thought the background of one would be overlaid on the other, but in either case it is the newax background that appears to be visible (or at least this is the color I see).
What is going on here?
You're not actually adding a new axes.
Matplotlib is detecting that there's already a plot in that position and returning it instead of a new axes object.
(Check it for yourself. ax and newax will be the same object.)
There's probably not a reason why you'd want to, but here's how you'd do it.
(Also, don't call newax = plt.axes() and then call fig.add_subplot(newax) You're doing the same thing twice.)
Edit: With newer (>=1.2, I think?) versions of matplotlib, you can accomplish the same thing as the example below by using the label kwarg to fig.add_subplot. E.g. newax = fig.add_subplot(111, label='some unique string')
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# If you just call `plt.axes()` or equivalently `fig.add_subplot()` matplotlib
# will just return `ax` again. It _won't_ create a new axis unless we
# call fig.add_axes() or reset fig._seen
newax = fig.add_axes(ax.get_position(), frameon=False)
ax.plot(range(10), 'r-')
newax.plot(range(50), 'g-')
newax.axis('equal')
plt.show()
Of course, this looks awful, but it's what you're asking for...
I'm guessing from your earlier questions that you just want to add a second x-axis? If so, this is a completely different thing.
If you want the y-axes linked, then do something like this (somewhat verbose...):
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
newax = ax.twiny()
# Make some room at the bottom
fig.subplots_adjust(bottom=0.20)
# I'm guessing you want them both on the bottom...
newax.set_frame_on(True)
newax.patch.set_visible(False)
newax.xaxis.set_ticks_position('bottom')
newax.xaxis.set_label_position('bottom')
newax.spines['bottom'].set_position(('outward', 40))
ax.plot(range(10), 'r-')
newax.plot(range(21), 'g-')
ax.set_xlabel('Red Thing')
newax.set_xlabel('Green Thing')
plt.show()
If you want to have a hidden, unlinked y-axis, and an entirely new x-axis, then you'd do something like this:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2)
newax = fig.add_axes(ax.get_position())
newax.patch.set_visible(False)
newax.yaxis.set_visible(False)
for spinename, spine in newax.spines.iteritems():
if spinename != 'bottom':
spine.set_visible(False)
newax.spines['bottom'].set_position(('outward', 25))
ax.plot(range(10), 'r-')
x = np.linspace(0, 6*np.pi)
newax.plot(x, 0.001 * np.cos(x), 'g-')
plt.show()
Note that the y-axis values for anything plotted on newax are never shown.
If you wanted, you could even take this one step further, and have independent x and y axes (I'm not quite sure what the point of it would be, but it looks neat...):
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2, right=0.85)
newax = fig.add_axes(ax.get_position())
newax.patch.set_visible(False)
newax.yaxis.set_label_position('right')
newax.yaxis.set_ticks_position('right')
newax.spines['bottom'].set_position(('outward', 35))
ax.plot(range(10), 'r-')
ax.set_xlabel('Red X-axis', color='red')
ax.set_ylabel('Red Y-axis', color='red')
x = np.linspace(0, 6*np.pi)
newax.plot(x, 0.001 * np.cos(x), 'g-')
newax.set_xlabel('Green X-axis', color='green')
newax.set_ylabel('Green Y-axis', color='green')
plt.show()
You can also just add an extra spine at the bottom of the plot. Sometimes this is easier, especially if you don't want ticks or numerical things along it. Not to plug one of my own answers too much, but there's an example of that here: How do I plot multiple X or Y axes in matplotlib?
As one last thing, be sure to look at the parasite axes examples if you want to have the different x and y axes linked through a specific transformation.