Change Font Size on secondary Y axis - python-3.x

I am writing code for my internship and I am trying to modify the font size on my axes so that it can look a bit better. The section that does not seem to be working is this part.
ax.set_xticks([-95, -97, -99, -101, -103])
ax.set_yticks([33, 34, 35, 36, 37])
secax = ax.secondary_yaxis(1.0)
secax.set_yticks([33, 34, 35, 36, 37])
ax.tick_params(axis = 'both', labelsize = 16)
When I run all of my code x axis and first y axis font size changes fine but my secondary y axis font size does not change. Is there any way I can change the font size of my secondary y axis?
This is the output I get when I run the code:

In your code, ax.tick_params(axis = 'both', labelsize = 16) changes font size for primary axes. To set fonts for secondary axis add the line secax.tick_params(labelsize=16).
Here's a working MRE
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
xdata = list(range(10))
yF = 85 + 10*np.random.random((10,1))
def fahrenheit_to_celsius(x):
return (x - 32) / 1.8
def celsius_to_fahrenheit(x):
return x * 1.8 + 32
yC = (yF-32)/1.8
ax.plot(xdata, yF)
secax = ax.secondary_yaxis('right', functions = (fahrenheit_to_celsius, celsius_to_fahrenheit))
ax.tick_params(axis = 'both', labelsize = 16)
secax.tick_params(labelsize = 16)
plt.show()
Another strategy is to add a block of code near the top of your file to control font sizes. I don't remember where I found this code, but it comes in handy. Interestingly setting xtick or ytick labelsize also works for secondary axes:
SMALL_SIZE = 10
MEDIUM_SIZE = 16
BIGGER_SIZE = 18
plt.rc('font', size=SMALL_SIZE) # controls default text sizes
plt.rc('axes', titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
plt.rc('axes', titleweight='bold') # fontsize of the axes title
plt.rc('axes', labelweight='bold') # fontsize of the x and y labels

Related

Do not plot gridlines/contourlines/coastlines outside of the canvas with cartopy

I'm plotting a map panel with cartopy in eps format. The resulting plot looks fine but has very broad margins when I insert it into my latex document. When checking the plot with adobe illustrator, it seems like the cartopy plots all the gridlines/contourlines/coastlines, even those outside of the canvas, which are hidden but do take up some spaces in the plot.
I tried to use constrained_layout and tight_layout, but they are incompatible with subplots_adjust which I use for adding the shared colorbar.
The code I use to plot is as follows:
proj2 = ccrs.LambertConformal(central_longitude=0, central_latitude=50)
proj_lonlat = ccrs.PlateCarree()
fig = plt.figure(figsize=(12, 9), constrained_layout=True)
# define a function to plot
def plot_era5_500z_MSLp(f500, fsurf, time, label, ax):
# read data
for i in np.arange(len(f500.time.values)):
if pd.to_datetime(f500.time.values[i]) == pd.to_datetime(time):
print('processing time: ' + time)
lons = f500.longitude.values # 1-d array
lats = f500.latitude.values # 1-d array
gph500 = f500.z.values[i,:,:]/98 # geopotential (m2 s-2) -> geopotential height (dagpm) [time = 72, lat = 241, lon = 561]
pmsl = fsurf.msl.values[i,:,:]/100 # mean sea level pressure Pa -> hPa
# create base map
ax.set_extent([-35, 30, 25, 70]) # x0, x1, y0, y1
gl = ax.gridlines(crs=proj_lonlat, draw_labels=True, xlocs=[-60,-40,-20,0,20,40,60], ylocs=[20,30,40,50,60],
x_inline=False, y_inline=False, color='k', alpha=0.5, linestyle='dotted')
gl.top_labels=False
gl.right_labels=False
gl.xlabel_style = {'size': 14, 'color': 'k'}
gl.ylabel_style = {'size': 14, 'color': 'k'}
gl.rotate_labels = False
ax.add_feature(cfeature.COASTLINE.with_scale('50m'), lw=0.4, alpha=0.9) # add coastline feature
# plot 500hPa geopotential height (zc: z contour)
z_levels = np.arange(500, 580+10, 8)
zc = ax.contour(lons, lats, gph500, transform=proj_lonlat,
levels=z_levels, extent='both', colors='mediumblue', linewidths=0.5)
ax.clabel(zc, inline=True, fontsize=10, fmt='%.0f')
# plot MSL pressure (mslps: MSL p shading; mslpc: MSL p contour)
levels = np.arange(960, 1057, 4)
mslps = ax.contourf(lons, lats, pmsl, levels=levels, cmap='Spectral_r', transform=proj_lonlat)
mslpc = ax.contour(lons, lats, pmsl, levels=levels, colors='k', linewidths=0.5, alpha=0.6, transform=proj_lonlat)
ax.set_title(label + ' ' + time, loc= 'left', pad=0.5, fontsize=14)
return mslps
# fig (a)
ax1 = plt.subplot(2, 2, 1, projection=proj2)
plot_era5_500z_MSLp(f500_2016nov, fsurf_2016nov, '2016-11-20 12:00', '(a)', ax1)
# fig (b)
ax2 = plt.subplot(2, 2, 2, projection=proj2)
plot_era5_500z_MSLp(f500_2016nov, fsurf_2016nov, '2016-11-24 00:00', '(b)', ax2)
# fig (c)
ax3 = plt.subplot(2, 2, 3, projection=proj2)
plot_era5_500z_MSLp(f500_2017feb, fsurf_2017feb, '2017-02-27 18:00', '(c)', ax3)
# fig (4)
ax4 = plt.subplot(2, 2, 4, projection=proj2)
mslps = plot_era5_500z_MSLp(f500_2017mar, fsurf_2017mar, '2017-03-04 06:00', '(d)', ax4) # only return mslps here for plotting the sharred colorbar
fig.subplots_adjust(right=0.8, wspace=0.2, hspace=0.000001)
cbar_ax = fig.add_axes([0.82, 0.2, 0.02, 0.55]) # left border, bottom border, width, height
cbar = fig.colorbar(mslps, cax=cbar_ax)
cbar.set_label(label='Mean sea level pressure (hPa)', size=16)
cbar.ax.tick_params(labelsize=14)
The resulting eps plot looks good, but in adobe illustrator, one can see the excess lines outside of the canvas:
Is there any way I can limit the plotting range of the data, or disable the lines outside of the canvas?

Even x ticks spacing in bar plots

I am trying to have my x tick evenly spaced using matplotlib.
here is the problem plot; the code is below
Issue: The x axis bars and values are not evenly spaced, despite my best efforts
I would appreciate any and all help, thank you!
here is the code im trying to plot
# define and store student IDs
student_IDs = np.array([1453,1454,1456,1457,1459,1460,1462,1463,1464,1465,1466, 1467, 1468, 1469,1470])
before_IP_abs_scores = np.array([51,56,73,94,81,83,71,36,43,83,66,62,70,50,83])
after_IP_abs_scores = np.array([65,60,82,71,65,85,78,51,34,80,63,63,62,55,77])
change_IP_abs_scores = after_IP_abs_scores - before_IP_abs_scores
Here is how i store the valuables from this array
ip_sc = collections.OrderedDict()
for ii in student_IDs:
ip_sc[ii] = []
for count, key in enumerate(student_IDs):
sci_id[key] = [before_science_ID_abs_scores[count],after_science_ID_abs_scores[count],change_science_ID_abs_scores[count]]
ip_sc[key] = [before_IP_abs_scores[count],after_IP_abs_scores[count],change_IP_abs_scores[count]]
Here is my plotting code:
fig = plt.figure(4)
fig.set_figheight(18)
fig.set_figwidth(18)
ax = plt.subplot(111)
plt.grid(True)
# ax = fig.add_axes([0,0,1,1])
for ii in student_IDs:
# plt.plot([1,2], ip_sc[ii][:-1],label=r'${}$'.format(ii))
ax.bar(ii, ip_sc[ii][0], width=.5, color='#30524F',edgecolor="white",hatch="//",align='center')
ax.bar(ii, ip_sc[ii][1], width=.5, color='#95BC89',align='center')
ax.bar(ii, ip_sc[ii][2], width=.5, color='#4D8178',align='center')
plt.ylabel('Absolute Score',size=30)
plt.xlabel('Student ID',size=30)
plt.title('IP Scale Scores',size=30)
plt.axhspan(0, 40, facecolor='navy', alpha=0.2,)
plt.axhspan(40, 60, facecolor='#95BC89', alpha=0.2)
plt.axhspan(60, 100, facecolor='seagreen', alpha=0.3)
ax.tick_params(axis='x', which='major', labelsize=16)
ax.tick_params(axis='y', which='major', labelsize=16)
plt.xticks(student_IDs, ['1453','1454','1456','1457','1459', '1460', '1462', '1463', '1464','1465', '1466', '1467', '1468', '1469', '1470'])
# ax.set_yticks(np.arange(0, 81, 10))
plt.ylim(-25,100)
ax.legend(labels=["Intense and Frequent IP ","Moderate IP ","few IP ",'Pre', 'Post','Change'],fontsize=15)
plt.show()
The student_ids aren't numbered consecutive.
Instead of using the student_ids as x-values, you could just use their index as x. With set_xticklabels you can thereafter set the student_ids as label corresponding to these positions.
So, you could make following modifications to the code (leaving out the call to plt.xticks):
for ind, stud_id in enumerate(student_IDs):
ax.bar(ind, ip_sc[stud_id][0], width=.5, color='#30524F', edgecolor="white", hatch="//", align='center')
ax.bar(ind, ip_sc[stud_id][1], width=.5, color='#95BC89', align='center')
ax.bar(ind, ip_sc[stud_id][2], width=.5, color='#4D8178', align='center')
ax.set_xticks(range(len(student_IDs)))
ax.set_xticklabels(student_IDs)

module matplotlib has no 'Blues' member

I set color 'Blues' and it can run and show the fig successfully ,but the output show a problem at here "cmap=plt.cm.Blues" and can't save the fig
import matplotlib.pyplot as plt
x_values = list(range(1,1001))
y_values = [x**2 for x in x_values]
plt.scatter(x_values, y_values, c= y_values, cmap=plt.cm.Blues, edgecolor= 'none', s=20)
#Set chart title and label axes
plt.title("Square Number" , fontsize= 15)
plt.xlabel("Value", fontsize = 15)
plt.ylabel("Square of Value", fontsize = 10)
#set the range for each axis
plt.axis([0, 1100, 0, 1100000])
#set size of tick labels
plt.tick_params(axis= 'both', which = 'major', labelsize = 8)
plt.show()
plt.savefig('squares_plot.png', bbox_inches= 'tight')
The reason is that cm is part of matplotlib and you only imported matplotlib.pyplot.
Try out this:
import matplotlib as mpl
# ... your code goes here
plt.scatter(x_values, y_values, c= y_values, cmap=mpl.cm.Blues, edgecolor= 'none', s=20)

Matplotlib square major/minor grid for axes with different limits

I have a plot with a background grid. I need grid cells to be square (both major grid and minor grid cells) even though the limits of X and Y axes are different.
My current code is as follows:
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
import numpy as np
data = [0.014, 0.84, 0.95, -0.42, -0.79, 0.84, 0.98, 1.10, 0.56, -0.49]
fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()
# Set major and minor grid lines on X
ax.set_xticks(np.arange(0, 10, 0.2))
ax.xaxis.set_minor_locator(plticker.MultipleLocator(base=0.2 / 5.))
for xmaj in ax.xaxis.get_majorticklocs():
ax.axvline(x=xmaj, ls='-', color='red', linewidth=0.8)
for xmin in ax.xaxis.get_minorticklocs():
ax.axvline(x=xmin, ls=':', color='red', linewidth=0.6)
# Set major and minor grid lines on Y
ylim = int(np.ceil(max(abs(min(data)), max(data))))
yticks = np.arange(-ylim, ylim + 0.5, 0.5)
ax.set_yticks(yticks)
ax.yaxis.set_minor_locator(plticker.MultipleLocator(base=0.5 / 5.))
for ymaj in ax.yaxis.get_majorticklocs():
ax.axhline(y=ymaj, ls='-', color='red', linewidth=0.8)
for ymin in ax.yaxis.get_minorticklocs():
ax.axhline(y=ymin, ls=':', color='red', linewidth=0.6)
ax.axis([0, 10, -ylim, ylim])
fig.tight_layout()
# Plot
ax.plot(data)
# Set equal aspect ratio NOT WORKING
plt.gca().set_aspect('equal', adjustable='box')
plt.show()
Which generates the following plot:
Large grid cells contain 5 smaller cells each. However, the aspect ratio of large grid is not 1.
Question: How can I make sure that large grid is square?
EDIT
Current approach is to set same tick locations as suggested by #ImportanceOfBeingErnest, but change Y labels:
ylim = int(np.ceil(max(abs(min(data)), max(data))))
yticks = np.arange(-ylim, ylim + 0.2, 0.2)
ax.set_yticks(yticks)
labels = ['{:.1f}'.format(v if abs(v) < 1e-3 else (1 if v > 0 else -1)*((0.5 - abs(v)%0.5) + abs(v)))
if i%2==0 else "" for i, v in enumerate(np.arange(-ylim, ylim, 0.2))]
ax.set_yticklabels(labels)
Result: seems too hacky.
When using equal aspect ratio and aiming for a square grid you would need to use the same tickspacing for both axes. This can be achieved with a MultipleLocator where the interval needs to be the same for x and y axis.
In general, grids can be created with the grid command.
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np
data = [0.014, 0.84, 0.95, -0.42, -0.79, 0.84, 0.98, 1.10, 0.56, -0.49]
fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()
# Set major and minor grid lines on X
ax.xaxis.set_major_locator(mticker.MultipleLocator(base=.5))
ax.xaxis.set_minor_locator(mticker.MultipleLocator(base=0.5 / 5.))
ax.yaxis.set_major_locator(mticker.MultipleLocator(base=.5))
ax.yaxis.set_minor_locator(mticker.MultipleLocator(base=0.5 / 5.))
ax.grid(ls='-', color='red', linewidth=0.8)
ax.grid(which="minor", ls=':', color='red', linewidth=0.6)
## Set limits
ylim = int(np.ceil(max(abs(min(data)), max(data))))
ax.axis([0, 10, -ylim, ylim])
plt.gca().set_aspect('equal', adjustable='box')
fig.tight_layout()
# Plot
ax.plot(data)
plt.show()
If you instead want to have different tick spacings with square major cells in the grid, you would need to give up the equal aspect ratio and instead set it to the quotient of the tick spacings.
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np
data = [0.014, 0.84, 0.95, -0.42, -0.79, 0.84, 0.98, 1.10, 0.56, -0.49]
fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()
xm = 0.2
ym = 0.25
# Set major and minor grid lines on X
ax.xaxis.set_major_locator(mticker.MultipleLocator(base=xm))
ax.xaxis.set_minor_locator(mticker.MultipleLocator(base=xm / 5.))
ax.yaxis.set_major_locator(mticker.MultipleLocator(base=ym))
ax.yaxis.set_minor_locator(mticker.MultipleLocator(base=ym / 5.))
ax.grid(ls='-', color='red', linewidth=0.8)
ax.grid(which="minor", ls=':', color='red', linewidth=0.6)
## Set limits
ylim = int(np.ceil(max(abs(min(data)), max(data))))
ax.axis([0, 10, -ylim, ylim])
plt.gca().set_aspect(xm/ym, adjustable='box')
fig.tight_layout()
# Plot
ax.plot(data)
plt.show()
To then get rid of every second ticklabel, an option is
fmt = lambda x,p: "%.2f" % x if not x%(2*ym) else ""
ax.yaxis.set_major_formatter(mticker.FuncFormatter(fmt))
You should be able to achieve this by using the same locator for the both axis. However matplotlib has a limitation currently, so here's a workaround:
# matplotlib doesnt (currently) allow two axis to share the same locator
# so make two wrapper locators and combine their view intervals
def share_locator(locator):
class _SharedLocator(matplotlib.ticker.Locator):
def tick_values(self, vmin, vmax):
return locator.tick_values(vmin, vmax)
def __call__(self):
min0, max0 = shared_locators[0].axis.get_view_interval()
min1, max1 = shared_locators[1].axis.get_view_interval()
return self.tick_values(min(min0, min1), max(max0, max1))
shared_locators = (_SharedLocator(), _SharedLocator())
return shared_locators
Use like:
lx, ly = share_locator(matplotlib.ticker.AutoLocator()) # or any other locator
ax.xaxis.set_major_locator(lx)
ax.yaxis.set_major_locator(ly)

Drawing very small shapes (size in µm) with python

I want to create "L" shapes black and white structure on a 20x20 mm figure. Each L shape width and length are defined as uw, ul, lw and ll (see code). A sper my understanding matplotlib works with points per inch (PPI) of 72 and with linewidth of 1, the shape will be 1/72 inch wide. I cannot understand how I can make these figures big enough to be visible when I use plt.show() and save them in the size I want (i.e. 20x20 mm page and each L with their exact shape size with high DPI so that I can view it when I open the saved figure). My code is:
import matplotlib.pyplot as plt
import numpy as np
uw = 20e-6 #upper width in meters
ul = 100e-6 #upper length in meters
lw = 20e-6 #lower width in meters
ll = 100e-6 #lower length in meters
w_space = 50e-6 #width spacing for subplots
h_space = 50e-6 #height spacing for subplots
N = 40
coord = [[0,0], [ll,0], [ll,lw], [uw,lw], [uw,ul], [0,ul]]
coord.append(coord[0]) #repeat the first point to create a 'closed loop'
xs, ys = zip(*coord) #create lists of x and y values
fig = plt.figure(num=None, figsize=(0.1, 0.1), dpi=100, facecolor='w', edgecolor='k') #figsize cannot be chosen below 0.1
for i in range(N):
ax = fig.add_subplot(5,10,i+1)
ax.fill(xs,ys,'k',linewidth=1)
plt.axis('off')
plt.subplots_adjust(wspace = w_space, hspace = h_space)
plt.savefig('screenshots/L_shape.png' ,bbox_inches = 'tight', pad_inches = 0, dpi=10000)
plt.show()

Resources