I'm trying to plot two sets of data in a bar graph with matplotlib, so I'm using two axes with the twinx() method. However, the second y-axis label gets cut off. I've tried a few different methods with no success (tight_layout(), setting the major_pads in rcParams, etc...). I feel like the solution is simple, but I haven't come across it yet.
Here's a MWE:
#!/usr/bin/env python
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
matplotlib.rcParams.update({'font.size': 21})
ax = plt.gca()
plt.ylabel('Data1') #Left side
ax2 = ax.twinx()
for i in range(10):
if(i%2==0):
ax.bar(i,np.random.randint(10))
else:
ax2.bar(i,np.random.randint(1000),color='k')
plt.ylabel('Data2') #Right
side
plt.savefig("test.png")
I just figured it out: the trick is to use bbox_inches='tight' in savefig.
E.G. plt.savefig("test.png",bbox_inches='tight')
I encountered the same issue which plt.tight_layout() did not automatically solve.
Instead, I used the labelpad argument in ylabel/set_ylabel as such:
ax.set_ylabel('label here', rotation=270, color='k', labelpad=15)
I guess this was not implemented when you asked this question, but as it's the top result on google, hopefully it can help users of the current matplotlib version.
Related
I'm trying to make a somewhat interactive map plot with matplotlib utilizing a button added to the toolbar in Matplotlib's navigation Toolbar.
The objective:
The objective that I'm trying to achieve is to be able to change the axes projection on the fly without creating a new axes. There are many methods in the axes object that gets created by Matplotlib to change other aspects of the plot, but I want to be able to change projection from say,
PlateCarree to NorthPolarStereo and vice versa.
Some Source:
import os
import sys
import matplotlib.pyplot as plt
import matplotlib
import mpl_toolkits
import numpy as np
import cartopy
import cartopy.crs as ccrs
fig = plt.figure()
ax = plt.axes(projection=ccrs.NorthPolarStereo())
ax.stock_img()
ny_lon, ny_lat = -75, 43
delhi_lon, delhi_lat = 77.23, 28.61
plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
color='blue', linewidth=2, marker='o',
transform=ccrs.Geodetic(),
)
plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
color='gray', linestyle='--',
transform=ccrs.PlateCarree(),
)
ax.add_patch(matplotlib.patches.Polygon([[0,0],[20,0],[20,20],[0,20]],
fill = False,color='g',ls='--',
transform=ccrs.PlateCarree()))
ax.add_patch(matplotlib.patches.Circle([30,30],radius=10,color='g',ls='--',
transform=ccrs.PlateCarree()))
plt.text(ny_lon - 3, ny_lat - 12, 'New York',
horizontalalignment='right',
transform=ccrs.Geodetic())
plt.text(delhi_lon + 3, delhi_lat - 12, 'Delhi',
horizontalalignment='left',
transform=ccrs.Geodetic())
# ax.set_extent([-180,180,-90,90])
ax.set_global()
The problem:
As can be seen, the axes is created with a projection = ccrs.NorthPolarStereo().
The projection of the axes can be obtained by executing the following:
ax.projection
Then, I try setting the projection to ccrs.PlateCarree()
ax.projection = ccrs.PlateCaree()
This alone does not update the plot, however. I have noticed that in editing some of the properties you need to draw the canvas again with
ax.figure.canvas.draw()
however, this doesn't seem to have an effect. But if I do
ax.set_global()
to set the extent to the maximum, the axes updates and changes to the correct projection... however, the data on the plot does not get updated again. Yet calling
ax.projection
indicates that the projection is now a PlateCarree projection.
How can I update the children of the axes to reflect this new projection?
I have tried
ax.update(ax.properties())
as per the matplotlib dox, however, it throws an error.
Any Ideas?
Edit:
If it is not obvious... You will need to run this in an iPython console and run those extra commands while the figure is open in order to edit it. And it must be done this way in order to achieve what I want to do. I know I can just make a new axes with a new projection, however, the end goal of this project is to maintain this axes. This is for editing and different viewing purposes that my project requires. Also, I'm stuck using matplotlib and cartopy, so no new library recommendations for plotting, please.
In reading this question I notice that I use 'however' way too often.
Updated MRE with subplots
I'm not sure of the usefulness of the original question and MRE. The margin padding seems to be properly adjusted for large x and y labels.
The issue is reproducible with subplots.
Using matplotlib 3.4.2
fig, axes = plt.subplots(ncols=2, nrows=2, figsize=(8, 6))
axes = axes.flatten()
for ax in axes:
ax.set_ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
ax.set_xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$')
plt.show()
Original
I am plotting a dataset using matplotlib where I have an xlabel that is quite "tall" (it's a formula rendered in TeX that contains a fraction and is therefore has the height equivalent of a couple of lines of text).
In any case, the bottom of the formula is always cut off when I draw the figures. Changing figure size doesn't seem to help this, and I haven't been able to figure out how to shift the x-axis "up" to make room for the xlabel. Something like that would be a reasonable temporary solution, but what would be nice would be to have a way to make matplotlib recognize automatically that the label is cut off and resize accordingly.
Here's an example of what I mean:
import matplotlib.pyplot as plt
plt.figure()
plt.ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
plt.xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$', fontsize=50)
plt.title('Example with matplotlib 3.4.2\nMRE no longer an issue')
plt.show()
The entire ylabel is visible, however, the xlabel is cut off at the bottom.
In the case this is a machine-specific problem, I am running this on OSX 10.6.8 with matplotlib 1.0.0
Use:
import matplotlib.pyplot as plt
plt.gcf().subplots_adjust(bottom=0.15)
# alternate option without .gcf
plt.subplots_adjust(bottom=0.15)
to make room for the label, where plt.gcf() means get the current figure. plt.gca(), which gets the current Axes, can also be used.
Edit:
Since I gave the answer, matplotlib has added the plt.tight_layout() function.
See matplotlib Tutorials: Tight Layout Guide
So I suggest using it:
fig, axes = plt.subplots(ncols=2, nrows=2, figsize=(8, 6))
axes = axes.flatten()
for ax in axes:
ax.set_ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
ax.set_xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$')
plt.tight_layout()
plt.show()
In case you want to store it to a file, you solve it using bbox_inches="tight" argument:
plt.savefig('myfile.png', bbox_inches="tight")
An easy option is to configure matplotlib to automatically adjust the plot size. It works perfectly for me and I'm not sure why it's not activated by default.
Method 1
Set this in your matplotlibrc file
figure.autolayout : True
See here for more information on customizing the matplotlibrc file: http://matplotlib.org/users/customizing.html
Method 2
Update the rcParams during runtime like this
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
The advantage of using this approach is that your code will produce the same graphs on differently-configured machines.
plt.autoscale() worked for me.
You can also set custom padding as defaults in your $HOME/.matplotlib/matplotlib_rc as follows. In the example below I have modified both the bottom and left out-of-the-box padding:
# The figure subplot parameters. All dimensions are a fraction of the
# figure width or height
figure.subplot.left : 0.1 #left side of the subplots of the figure
#figure.subplot.right : 0.9
figure.subplot.bottom : 0.15
...
There is also a way to do this using the OOP interface, applying tight_layout directly to a figure:
fig, ax = plt.subplots()
fig.set_tight_layout(True)
https://matplotlib.org/stable/api/figure_api.html
for some reason sharex was set to True so I turned it back to False and it worked fine.
df.plot(........,sharex=False)
You need to use sizzors to modify the axis-range:
import sizzors as sizzors_module
sizzors_module.reshape_the_axis(plt).save("literlymylief.tiff")
I am trying to plot data to a figure and respective axis in matplotlib and as new work comes up, recall the figure with the additional plot on the axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
x=np.arange(0,20)
y=2*x
fig,ax=plt.subplots()
ax.scatter(x,x)
ax.scatter(x,y)
fig
Which works fine with matplotlib, if I however use seaborn's regplot:
fig2,ax2=plt.subplots()
sns.regplot(x,x,ax=ax2,fit_reg=False)
sns.regplot(x,y,ax=ax2,fit_reg=False)
fig2
fig2 generates the figure that I want but the regplot command generates an empty figure. Is there a way to suppress the regplot's empty output or have it display the updated ax2 without recalling fig2?
It seems you are using the jupyter notebook with the inline backend. In some circumstances regplot triggers the creation of a new figure even if the artists are being added to the previous one and this messes up the output. I don't know why this happens but I found a workaround that might help you, using plt.ioff to temporarily disable automatic display of figures.
plt.ioff()
fig, ax = plt.subplots()
sns.regplot(x, x, ax=ax)
fig
sns.regplot(x, 2 * x, ax=ax)
fig
You have to call plt.ioff before creating the figure for this to work. After that you have to explicitly display the figure. Then you can call plt.ion to restore the default behaviour.
regplot does not generate an empty figure. According to the documentation:
Understanding the difference between regplot() and lmplot() can be a
bit tricky. In fact, they are closely related, as lmplot() uses
regplot() internally and takes most of its parameters. However,
regplot() is an axes-level function, so it draws directly onto an axes
(either the currently active axes or the one provided by the ax
parameter), while lmplot() is a figure-level function and creates its
own figure, which is managed through a FacetGrid.
When I do the following:
fig2,ax2 = plt.subplots()
same_fig2 = sns.regplot(x,x,ax=ax2,fit_reg=False)
same_fig2.figure is fig2
>>> True
Try to do a graph using python matplotlib: but keep getting the following waring message:
"UserWaring: tight_layout: falling back to Agg renderer warnings.warn("tight_layout: falling back to Agg renderer")
My code is below:
plt.legend(loc='upper left',prop = {'size':7},bbox_to_anchor=(1,1))
plt.tight_layout(pad=7)
plt.xlabel ('Build')
plt.ylabel ('Time/Sec')
plt.title ('Performance Test')
plt.grid()
plt.show()
How to fix that warning message?
Are you using MacOSX? It seems to be a known and open issue
https://github.com/matplotlib/matplotlib/issues/1852
I will suggest to reorganize the code such that you will use Figure instead of pyplot. You can get the Figure from plt.figure() method. Then, on the Figure instance call set_tight_layout(True).
Try this example code:
import matplotlib
matplotlib.use('pdf')
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
fig.set_tight_layout(True)
fig.savefig('asd.pdf') # No warning now
As a side note, have a look at matplotlib documentation
tight_layout() can take keyword arguments of pad, w_pad and h_pad. These control the extra padding around the figure border and between subplots. The pads are specified in fraction of fontsize.
http://matplotlib.org/users/tight_layout_guide.html
This suggest that your code
plt.tight_layout(pad=7)
is wrong, as the value of pad should be between 0 and 1.
I'm editing my graphs step by step. Doing so, plt functions from matplotlib.pyplot apply instantly to my graphical output of pylab. That's great.
If I address axes of a subplot, it does not happen anymore.
Please find both alternatives in my minimal working example.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
f = plt.figure()
sp1 = f.add_subplot(1,1,1)
f.show()
# This works well
sp1.set_xlim([1,5])
# Now I plot the graph
df = pd.Series([0,5,9,10,15])
df.hist(bins=50, color="red", alpha=0.5, normed=True, ax=sp1)
# ... and try to change the ticks of the x-axis
sp1.set_xticks(np.arange(1, 15, 1))
# Unfortunately, it does not result in an instant change
# because my plot has already been drawn.
# If I wanted to use the code above,
# I would have to execute him before drawing the graph.
# Therefore, I have to use this function:
plt.xticks(np.arange(1, 15, 1))
I understand that there is a difference between matplotlib.pyplot and an axis instance. Did I miss anything or does it just work this way?
Most of pyplot functions (if not all) have a call to plt.draw_if_interactive() before returning. So if you do
plt.ion()
plt.plot([1,2,3])
plt.xlim([-1,4])
you obtain that the plot is updated as you go. If you have interactive off, it won't create or update the plot until you don't call plt.show().
But all pyplot functions are wrappers around corresponding (usually) Axes methods.
If you want to use the OO interface, and still draw stuff as you type, you can do something like this
plt.ion() # if you don't have this, you probably don't get anything until you don't call a blocking `plt.show`
fig, ax = plt.subplots() # create an empty plot
ax.plot([1,2,3]) # create the line
plt.draw() # draw it (you can also use `draw_if_interactive`)
ax.set_xlim([-1,4]) #set the limits
plt.draw() # updata the plot
You don't have to use the pyplot you don't want, just remember to draw
The plt.xticks() method calls a function draw_if_interactive() that comes from pylab_setup(), who is updating the graph. In order to do it using sp1.set_xticks(), just call the corresponding show() method:
sp1.figure.show()