When using a cartopy map I cannot add an xlabel or ylabel. Is there a way to do this? I am not looking for ticklabels.
import matplotlib.pyplot as plt
import cartopy
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
ax.set_xlabel('lon')
ax.set_ylabel('lat')
plt.show()
Cartopy's matplotlib gridliner takes over the xlabel and ylabel and uses it to manage grid lines and labels.
https://github.com/SciTools/cartopy/blob/master/lib/cartopy/mpl/gridliner.py#L93
import matplotlib.pyplot as plt
import cartopy
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
gridlines = ax.gridlines(draw_labels=True)
# this would not function, due to the gridliner
# ax.set_xlabel('lon')
# ax.set_ylabel('lat')
plt.show()
If you want to add labels to the axis instances of a cartopy axes, you ought to place them so they don't overlap with the gridliner. At present you need to do this by hand, such as:
import matplotlib.pyplot as plt
import cartopy
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
gridlines = ax.gridlines(draw_labels=True)
ax.text(-0.07, 0.55, 'latitude', va='bottom', ha='center',
rotation='vertical', rotation_mode='anchor',
transform=ax.transAxes)
ax.text(0.5, -0.2, 'longitude', va='bottom', ha='center',
rotation='horizontal', rotation_mode='anchor',
transform=ax.transAxes)
plt.show()
you need to tune the values for the ax.text placement to get the effect you want in each case, which can be a bit frustrating, but it is functional.
It would be a nice feature to add to cartopy to automate this placement.
By chance I found that running...
import matplotlib.pyplot as plt
import cartopy
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.COASTLINE)
ax.set_xlabel('lon')
ax.set_ylabel('lat')
ax.set_xticks([-180,-120,-60,0,60,120,180])
ax.set_yticks([-90,-60,-30,0,30,60,90])
plt.show()
...it prints the xticks and yticks but also the xlabel and ylabel. In other cases where the xticks and yticks are already defined, they would be restored doing...
ax.set_xticks(ax.get_xticks())
ax.set_yticks(ax.get_yticks())
or in case they are automatically generated out of the map limits
ax.set_xticks(ax.get_xticks()[abs(ax.get_xticks())<=180])
ax.set_yticks(ax.get_yticks()[abs(ax.get_yticks())<=90])
For adding the grid...
plt.grid()
Related
I have a matplotlib plot with a colorbar attached. I want to position the colorbar so that it is horizontal, and underneath my plot.
I have almost done this via the following:
plt.colorbar(orientation="horizontal",fraction=0.07,anchor=(1.0,0.0))
But the colorbar is still overlapping with the plot slightly (and the labels of the x axis). I want to move the colorbar further down, but I can't figure out how to do it.
using padding pad
In order to move the colorbar relative to the subplot, one may use the pad argument to fig.colorbar.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
fig.colorbar(im, orientation="horizontal", pad=0.2)
plt.show()
using an axes divider
One can use an instance of make_axes_locatable to divide the axes and create a new axes which is perfectly aligned to the image plot. Again, the pad argument would allow to set the space between the two axes.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np; np.random.seed(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
divider = make_axes_locatable(ax)
cax = divider.new_vertical(size="5%", pad=0.7, pack_start=True)
fig.add_axes(cax)
fig.colorbar(im, cax=cax, orientation="horizontal")
plt.show()
using subplots
One can directly create two rows of subplots, one for the image and one for the colorbar. Then, setting the height_ratios as gridspec_kw={"height_ratios":[1, 0.05]} in the figure creation, makes one of the subplots much smaller in height than the other and this small subplot can host the colorbar.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, (ax, cax) = plt.subplots(nrows=2,figsize=(4,4),
gridspec_kw={"height_ratios":[1, 0.05]})
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
fig.colorbar(im, cax=cax, orientation="horizontal")
plt.show()
Edit: Updated for matplotlib version >= 3.
Three great ways to do this have already been shared in this answer.
The matplotlib documentation advises to use inset_locator. This would work as follows:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import numpy as np
rng = np.random.default_rng(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(rng.random((11, 16)))
ax.set_xlabel("x label")
axins = inset_axes(ax,
width="100%",
height="5%",
loc='lower center',
borderpad=-5
)
fig.colorbar(im, cax=axins, orientation="horizontal")
I'm using matplotlib to produce a plot where I want to show labels on the right and left y-axis. You will notice by running the code that the grid-lines formed by the right-side y-axis appear on top of the plot line, where the left-side lines appear below. I would like them all to appear below the plot. I've tried zorder and set_axisbelow(True) without success.
Example code below:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
t = np.linspace(0,5)
x = np.exp(-t)*np.sin(2*t)
fig, ax1 = plt.subplots()
ax1.plot(t, x)
ax2 = ax1.twinx()
ax2.plot(t, x, alpha=0.0)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0.1, 0.2])
ax2.set_yticks([0.3, 0.4, 0.5])
ax1.grid(True, color='lightgray')
ax2.grid(True, color='lightgray')
for a in [ax1, ax2]:
a.spines["top"].set_visible(False)
a.spines["right"].set_visible(False)
a.spines["left"].set_visible(False)
a.spines["bottom"].set_visible(False)
ax1.set_axisbelow(True)
ax2.set_axisbelow(True)
plt.savefig('fig.pdf')
plt.show()
If I do rotation to the colorbar labels, the format I used seem to be reset (ignored).
The fig.colorbar does not accept rotation, while cb.ax.set_xticklabels does not accept format.
I couldn't find any way to do both settings.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import FormatStrFormatter
test = np.random.rand(100, 100)
np.random.seed(12345)
fig, axs = plt.subplots(1, 2, figsize=(6, 5))
fmts = ["%d", "%.5f"]
for i, ax in enumerate(axs.tolist()):
im = ax.imshow(test, origin="lower")
cb = fig.colorbar(im, ax=ax, orientation='horizontal',
format=FormatStrFormatter(fmts[i]))
ax.set_title(f"Format {fmts[i]}")
cb.ax.set_xticklabels(cb.get_ticks(), rotation=45)
plt.tight_layout()
plt.show()
The colorbar tick labels should be in the format of "%d" and "%.5f" but as you can see, neither does.
I don't think that the original formatting is kept when you call cb.ax.set_xticklabels(), you could add cb.ax.xaxis.set_major_formatter(FormatStrFormatter(fmts[i])) to re-apply the custom formatting afterwards.
As an alternative, use plt.setp(cb.ax.get_xticklabels(),rotation=45) instead to rotate the labels.
I've tried the other threads, but can't work out how to solve. I'm attempting to create a discrete colorbar. Much of the code appears to be working, a discrete bar does appear, but the labels are wrong and it throws the error: "No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf)."
Pretty sure the error is because I'm missing an argument in plt.colorbar, but not sure what it's asking for or how to define it.
Below is what I have. Any thoughts gratefully received:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
norm = mpl.colors.BoundaryNorm(np.arange(-0.5,4), cmap.N)
ex2 = sample_data.plot.scatter(x='order_count', y='total_value',c='cluster', marker='+', ax=ax, cmap='plasma', norm=norm, s=100, edgecolor ='none', alpha=0.70)
plt.colorbar(ticks=np.linspace(0,3,4))
plt.show()
Indeed, the fist argument to colorbar should be a ScalarMappable, which would be the scatter plot PathCollection itself.
Setup
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({"x" : np.linspace(0,1,20),
"y" : np.linspace(0,1,20),
"cluster" : np.tile(np.arange(4),5)})
cmap = mpl.colors.ListedColormap(["navy", "crimson", "limegreen", "gold"])
norm = mpl.colors.BoundaryNorm(np.arange(-0.5,4), cmap.N)
Pandas plotting
The problem is that pandas does not provide you access to this ScalarMappable directly. So one can catch it from the list of collections in the axes, which is easy if there is only one single collection present: ax.collections[0].
fig, ax = plt.subplots()
df.plot.scatter(x='x', y='y', c='cluster', marker='+', ax=ax,
cmap=cmap, norm=norm, s=100, edgecolor ='none', alpha=0.70, colorbar=False)
fig.colorbar(ax.collections[0], ticks=np.linspace(0,3,4))
plt.show()
Matplotlib plotting
One could consider using matplotlib directly to plot the scatter in which case you would directly use the return of the scatter function as argument to colorbar.
fig, ax = plt.subplots()
scatter = ax.scatter(x='x', y='y', c='cluster', marker='+', data=df,
cmap=cmap, norm=norm, s=100, edgecolor ='none', alpha=0.70)
fig.colorbar(scatter, ticks=np.linspace(0,3,4))
plt.show()
Output in both cases is identical.
I have plotted my data with factorplot in seaborn and get facetgrid object, but still cannot understand how the following attributes could be set in such a plot:
Legend size: when I plot lots of variables, I get very small legends, with small fonts.
Font sizes of y and x labels (a similar problem as above)
You can scale up the fonts in your call to sns.set().
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
x = np.random.normal(size=37)
y = np.random.lognormal(size=37)
# defaults
sns.set()
fig, ax = plt.subplots()
ax.plot(x, y, marker='s', linestyle='none', label='small')
ax.legend(loc='upper left', bbox_to_anchor=(0, 1.1))
sns.set(font_scale=5) # crazy big
fig, ax = plt.subplots()
ax.plot(x, y, marker='s', linestyle='none', label='big')
ax.legend(loc='upper left', bbox_to_anchor=(0, 1.3))
The FacetGrid plot does produce pretty small labels. While #paul-h has described the use of sns.set as a way to the change the font scaling, it may not be the optimal solution since it will change the font_scale setting for all plots.
You could use the seaborn.plotting_context to change the settings for just the current plot:
with sns.plotting_context(font_scale=1.5):
sns.factorplot(x, y ...)
I've made some modifications to #paul-H code, such that you can independently set the font size for the x/y axes and legend:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
x = np.random.normal(size=37)
y = np.random.lognormal(size=37)
# defaults
sns.set()
fig, ax = plt.subplots()
ax.plot(x, y, marker='s', linestyle='none', label='small')
ax.legend(loc='upper left', fontsize=20,bbox_to_anchor=(0, 1.1))
ax.set_xlabel('X_axi',fontsize=20);
ax.set_ylabel('Y_axis',fontsize=20);
plt.show()
This is the output:
For the legend, you can use this
plt.setp(g._legend.get_title(), fontsize=20)
Where g is your facetgrid object returned after you call the function making it.
This worked for me
g = sns.catplot(x="X Axis", hue="Class", kind="count", legend=False, data=df, height=5, aspect=7/4)
g.ax.set_xlabel("",fontsize=30)
g.ax.set_ylabel("Count",fontsize=20)
g.ax.tick_params(labelsize=15)
What did not work was to call set_xlabel directly on g like g.set_xlabel() (then I got a "Facetgrid has no set_xlabel" method error)