How to set a font family to "erewhon" when using Latex in matplotlib? - matplotlib

I am using the latex format for the axis label and axis tick label for some of my plots. My problem is that the latex font differs from the non-latex font which is 'erewhon'. So I want to try to use 'erewhon' in the latex format.
I tried multiple approaches like the following code:
fig, ax1 = plt.subplots(figsize = (8,5))
rcParams = [{'text.usetex': True,
'svg.fonttype': 'none',
'text.latex.preamble': r'\usepackage{erewhon}',
'font.size': 20,
'font.family': 'erewhon',
'mathtext.fontset': 'custom',
'mathtext.rm': 'erewhon',
'mathtext.it': 'erewhon',
'mathtext.bf': 'erewhon'}]
xlabel='Oxygen mass flow (sccm)'
ylabel1=r'$\mathrm{\rho \; (\mu \Omega \cdot cm)}$'
ax1.semilogy(xfit, ( np.exp(m*xfit+b) ) , 'k-', lw=2)
ax1.set_yscale('log')
ax1.set_xlabel(xlabel, fontsize=20)
ax1.set_ylabel(ylabel1, fontsize=20)
This code provides the xlabel font to be 'erewhon' but the ylabel still uses any font (I even don't know which one), although, I use \mathrm{}. Is there any solution for this problem?
Thanks for your help!

Applying the super helpful comment by Ralf Stubner here, this code
import matplotlib.pyplot as plt
preamble = [r"\usepackage[proportional,scaled=1.064]{erewhon}",
r"\usepackage[erewhon,vvarbb,bigdelims]{newtxmath}",
r"\usepackage[T1]{fontenc}",
r"\renewcommand*\oldstylenums[1]{\textosf{#1}}"]
rcParams = {'text.usetex': True,
'svg.fonttype': 'none',
'text.latex.preamble': preamble,
'font.size': 20,
'font.family': 'erewhon'}
plt.rcParams.update(rcParams)
fig, ax1 = plt.subplots(figsize = (8,5))
xlabel='Oxygen mass flow (sccm)'
ylabel1=r'$\mathrm{\rho \; (\mu \Omega \cdot cm)}$'
#ax1.semilogy(xfit, ( np.exp(m*xfit+b) ) , 'k-', lw=2)
ax1.set_yscale('log')
ax1.set_xlabel(xlabel, fontsize=20)
ax1.set_ylabel(ylabel1, fontsize=20)
plt.tight_layout()
plt.show()
produces

Related

How to add a legend for a GeoAxes that adds a Cartopy shapely feature?

I copied the code for adding legend via proxy artists from matplotlib's documentation but it doesn't work. I also tried the rest in matplotlib's legends guide but nothing works. I guess it's because the element is a shapely feature which ax.legend() somehow doesn't recognize.
Code
bounds = [116.9283371, 126.90534668, 4.58693981, 21.07014084]
stamen_terrain = cimgt.Stamen('terrain-background')
fault_line = ShapelyFeature(Reader('faultLines.shp').geometries(), ccrs.epsg(32651),
linewidth=1, edgecolor='black', facecolor='none') # geometry is multilinestring
fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.set_extent(bounds)
ax.add_image(stamen_terrain, 8)
a = ax.add_feature(fault_line, zorder=1, label='test')
ax.legend([a], loc='lower left', fancybox=True) #plt.legend() has the same result
plt.show()
Result
When copying the matplotlib example, you omitted the actual "proxy" artist line!
red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])
That red_patch is the proxy artist. You have to create a dummy artist to pass to legend(). Your code as written is still passing the unrecognized Shapely feature.
It's tedious, but the relevant code would be something like:
fault_line = ShapelyFeature(Reader('faultLines.shp').geometries(), ccrs.epsg(32651), linewidth=1, edgecolor='black', facecolor='none')
ax.add_feature(fault_line, zorder=1)
# Now make a dummy object that looks as similar as possible
import matplotlib.patches as mpatches
proxy_artist = mpatches.Rectangle((0, 0), 1, 0.1, linewidth=1, edgecolor='black', facecolor='none')
# And manually add the labels here
ax.legend([proxy_artist], ['test'], loc='lower left', fancybox=True)
Here I just used a Rectangle, but depending on the feature, you can use various supported matplotlib "artists".

Format the legend-title in a matplotlib ax.twiny() plot

Believe it or not I need help with formatting the title of the legend (not the title of the plot) in a simple plot. I am plotting two series of data (X1 and X2) against Y in a twiny() plot.
I call matplotlib.lines to construct lines for the legend and then call plt.legend to construct a legend pass text strings to name/explain the lines, format that text and place the legend. I could also pass a title-string to plt.legend but I cannot format it.
The closest I have come to a solution is to create another 'artist' for the title using .legend()set_title and then format the title text. I assign it to a variable and call the variable in the above mentioned plt.legend. This does not result in an error nor does it produce the desired effect. I have no control over the placement of the title.
I have read through a number of S-O postings and answers on legend-related issues, looked at the MPL docs, various tutorial type web-pages and even taken a peak at a GIT-hub issue (#10391). Presumably the answer to my question is somewhere in there but not in a format that I have been able to successfully implement.
#Imports
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import numpy as np
import seaborn as sns
plt.style.use('seaborn')
#Some made up data
y = np.arange(0, 1200, 100)
x1 = (np.log(y+1))
x2 = (2.2*x1)
#Plot figure
fig = plt.figure(figsize = (12, 14))
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
sy1, sy2 = 'b-', 'r-'
tp, bm = 0, 1100
red_ticks = np.arange(0, 11, 2)
ax1.plot(x1, y, sy1)
ax1.set_ylim(tp, bm)
ax1.set_xlim(0, 10)
ax1.set_ylabel('Distance (m)')
ax1.set_xlabel('Area')
ax1.set_xticks(red_ticks)
blue_ticks = np.arange(0, 22, 4)
ax2.plot(x2, y, sy2)
ax2.set_xlim(0, 20)
ax2.set_xlabel('Volume')
ax2.set_xticks(blue_ticks)
ax2.grid(False)
x1_line = mlines.Line2D([], [], color='blue')
x2_line = mlines.Line2D([], [], color='red')
leg = ax1.legend().set_title('Format Legend Title ?',
prop = {'size': 'large',
'family':'serif',
'style':'italic'})
plt.legend([x1_line, x2_line], ['Blue Model', 'Red Model'],
title = leg,
prop ={'size':12,
'family':'serif',
'style':'italic'},
bbox_to_anchor = (.32, .92))
So what I want is a simple way to control the formatting of both the legend-title and legend-text in a single artist, and also have control over the placement of said legend.
The above code returns a "No handles with labels found to put in legend."
You need one single legend. You can set the title of that legend (not some other legend); then style it to your liking.
leg = ax2.legend([x1_line, x2_line], ['Blue Model', 'Red Model'],
prop ={'size':12, 'family':'serif', 'style':'italic'},
bbox_to_anchor = (.32, .92))
leg.set_title('Format Legend Title ?', prop = {'size': 24, 'family':'sans-serif'})
Unrelated, but also important: Note that you have two figures in your code. You should remove one of them.

How do I extend the margin at the bottom of a figure in Matplotlib?

The following screenshot shows my x-axis.
I added some labels and rotated them by 90 degrees in order to better read them. However, pyplot truncates the bottom such that I'm not able to completely read the labels.
How do I extend the bottom margin in order to see the complete labels?
Two retroactive ways:
fig, ax = plt.subplots()
# ...
fig.tight_layout()
Or
fig.subplots_adjust(bottom=0.2) # or whatever
Here's a subplots_adjust example: http://matplotlib.org/examples/pylab_examples/subplots_adjust.html
(but I prefer tight_layout)
A quick one-line solution that has worked for me is to use pyplot's auto tight_layout method directly, available in Matplotlib v1.1 onwards:
plt.tight_layout()
This can be invoked immediately before you show the plot (plt.show()), but after your manipulations on the axes (e.g. ticklabel rotations, etc).
This convenience method avoids manipulating individual figures of subplots.
Where plt is the standard pyplot from:
import matplotlib.pyplot as plt
fig.savefig('name.png', bbox_inches='tight')
works best for me, since it doesn't reduce the plot size compared to
fig.tight_layout()
Subplot-adjust did not work for me, since the whole figure would just resize with the labels still out of bounds.
A workaround I found was to keep the y-axis always a certain margin over the highest or minimum y-values:
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,y1 - 100 ,y2 + 100))
fig, ax = plt.subplots(tight_layout=True)
This is rather complicated, but it gives a general and neat solution.
import numpy as np
value1 = 3
xvalues = [0, 1, 2, 3, 4]
line1 = [2.0, 3.0, 2.0, 5.0, 4.0]
stdev1 = [0.1, 0.2, 0.1, 0.4, 0.3]
line2 = [1.7, 3.1, 2.5, 4.8, 4.2]
stdev2 = [0.12, 0.18, 0.12, 0.3, 0.35]
max_times = [max(line1+stdev1),max(line2+stdev2)]
min_times = [min(line1+stdev1),min(line2+stdev2)]
font_size = 25
max_total = max(max_times)
min_total = min(min_times)
max_minus_min = max_total - min_total
step_size = max_minus_min/10
head_space = (step_size*3)
plt.figure(figsize=(15, 15))
plt.errorbar(xvalues, line1, yerr=stdev1, fmt='', color='b')
plt.errorbar(xvalues, line2, yerr=stdev2, fmt='', color='r')
plt.xlabel("xvalues", fontsize=font_size)
plt.ylabel("lines 1 and 2 Test "+str(value1), fontsize=font_size)
plt.title("Let's leave space for the legend Experiment"+ str(value1), fontsize=font_size)
plt.legend(("Line1", "Line2"), loc="upper left", fontsize=font_size)
plt.tick_params(labelsize=font_size)
plt.yticks(np.arange(min_total, max_total+head_space, step=step_size) )
plt.grid()
plt.tight_layout()
Result:

Align ylabel with yticks

The code below draws a plot that looks almost exactly the way I want it to be. However, I'd like the ylabel to be horizontal and left-aligned with the yticks. Currently, the ylabel is placed left relative to the yticks which looks ugly (the image below shows the upper left corner of the plot). Does someone know how to fix this?
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
# setting the x=... option does NOT help
yprops = dict(rotation=0, y=1.05, horizontalalignment='left')
plt.subplot(111,axisbg='#BBBBBB',alpha=0.1)
plt.grid(color='white', alpha=0.5, linewidth=2, linestyle='-', axis='y')
for spine_name in ['top', 'left', 'right']:
plt.gca().spines[spine_name].set_color('none')
plt.ylabel('y label', **yprops)
plt.xlabel('x label')
plt.gca().tick_params(direction='out', length=0, color='k')
plt.plot(xvals, yvals(1), 'bo-', linewidth=2)
plt.gca().set_axisbelow(True)
plt.show()
You can adjust the coordinates using ax.yaxis.set_label_coords like in this example.
With your data:
import matplotlib.pyplot as plt
import numpy as np
xvals = range(0,10);
yvals = lambda s: [ x*x*s for x in xvals ]
yprops = dict(rotation=0, x=0, y=1.05)
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
print(ax.get_position())
ax.yaxis.set_label_coords(-0.1,1.05)
fig.savefig('cucu.png')
plt.show()
Note that if you go further away, the label will be placed outside the figure. If that is the case, you can adjust the margins before:
fig, ax = plt.subplots(1, 1, figsize=(5,3))
ax.set_ylabel('y label', **yprops )
ax.set_xlabel('x label')
ax.plot(xvals, yvals(1), 'bo-', linewidth=2)
fig.subplots_adjust(left=0.2, bottom=0.2, right=0.8, top=0.8)
ax.yaxis.set_label_coords(-0.2,1.2)
fig.savefig('cucu2.png')
plt.show()
See also this answer

Matplotlib: Changing the color of an axis

Is there a way to change the color of an axis (not the ticks) in matplotlib? I have been looking through the docs for Axes, Axis, and Artist, but no luck; the matplotlib gallery also has no hint.
Any idea?
When using figures, you can easily change the spine color with:
ax.spines['bottom'].set_color('#dddddd')
ax.spines['top'].set_color('#dddddd')
ax.spines['right'].set_color('red')
ax.spines['left'].set_color('red')
Use the following to change only the ticks:
which="both" changes both the major and minor tick colors
ax.tick_params(axis='x', colors='red')
ax.tick_params(axis='y', colors='red')
And the following to change only the label:
ax.yaxis.label.set_color('red')
ax.xaxis.label.set_color('red')
And finally the title:
ax.title.set_color('red')
You can do it by adjusting the default rc settings.
import matplotlib
from matplotlib import pyplot as plt
matplotlib.rc('axes',edgecolor='r')
plt.plot([0, 1], [0, 1])
plt.savefig('test.png')
For the record, this is how I managed to make it work:
fig = pylab.figure()
ax = fig.add_subplot(1, 1, 1)
for child in ax.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color('#dddddd')
Setting edge color for all axes globally:
matplotlib.rcParams['axes.edgecolor'] = '#ff0000'