How do I change the latex font when usetex=True for matplotlib? - matplotlib

I want to use a local font as the math font for matplotlib when I set usetex=True.
According to matplotlib's website (Text rendering with LaTeX), the following is an appropriate way to change the font when usetex=True.
plt.rcParams.update({
"text.usetex": True,
"font.family": "sans-serif",
"font.sans-serif": "Helvetica",
})
However, when I try this the font does not change.
I have done the following. I start with the default font in the top plot, then I try two different methods to change the font. However, all fonts look the same. The result is below.
from matplotlib import pyplot as plt
import matplotlib
# Removing unecessary and distracting lines/labels
matplotlib.rcParams['axes.spines.bottom'] = False
matplotlib.rcParams['axes.spines.left'] = False
matplotlib.rcParams['axes.spines.right'] = False
matplotlib.rcParams['axes.spines.top'] = False
matplotlib.rcParams['xtick.major.size'] = 0
matplotlib.rcParams['xtick.labelsize'] = 0
matplotlib.rcParams['ytick.major.size'] = 0
matplotlib.rcParams['ytick.labelsize'] = 0
# print(matplotlib.rcParams.keys())
testString = r'Thrust Coefficient \\ $\displaystyle \mathbf{C_T}$'
matplotlib.rcParams['font.size'] = 36
matplotlib.rcParams.update({
"text.usetex": True
})
fig, (ax1, ax2, ax3) = plt.subplots(3,1)
ax1.text(0.5,0.5, testString, ha='center')
# Font Change
# Setting all the math text types to my desired font
matplotlib.rcParams['mathtext.fontset'] = 'custom'
matplotlib.rcParams['mathtext.rm'] = 'Neo Euler'
matplotlib.rcParams['mathtext.it'] = 'Neo Euler'
matplotlib.rcParams['mathtext.bf'] = 'Neo Euler'
matplotlib.rcParams['mathtext.sf'] = 'Neo Euler'
matplotlib.rcParams['mathtext.rm'] = 'Neo Euler'
matplotlib.rcParams['mathtext.fallback'] = None
matplotlib.rcParams['mathtext.default'] = 'sf'
# Also using matplotlib suggested method
matplotlib.rcParams.update({
"text.usetex": True,
"font.family": "sans-serif",
"font.sans-serif": "Neo Euler",
})
ax2.text(0.5,0.5, testString, ha='center')
font = {
'family': 'Neo Euler',
}
ax3.text(0.5,0.5, testString, ha='center', fontdict=font)
fig.tight_layout()
plt.show()
All the fonts look the same. I am not sure what step I am missing or what went wrong. I get the desired result when usetex=False. However, I would like the extra features available with usetex, like the linebreaks.

Related

Anchoring text on matplotlib

With this code:
from pandas_datareader import data as web
import pandas as pd
import datetime
df = web.DataReader('fb', 'yahoo', start = datetime.date(2022,5,28), end = datetime.datetime.now())
df['pct'] = df.Close.pct_change()
plt.style.use('fivethirtyeight')
plt.rcParams['font.family'] = 'Serif'
fig, ax = plt.subplots()
ax2 = ax.twinx()
plt.axis('off')
df.plot.bar(y = 'pct', ax = ax, grid = True, color = '#bf3c3d')
ax.set_title('Large Title',loc='left', fontname="Times New Roman", size=28,fontweight="bold")
ax.set(xlabel=None)
ax.text(0, 0.07, '\nAttention Catcher', fontsize=13, ha='left')
ax.get_legend().remove()
plt.grid(color = 'green', linestyle = 'dotted', linewidth = 0.5)
This plot is produced:
The issue which I am running into is that I want the text "Attention Catcher" to be lined by exactly on the line where "Large Title" started.
However when the date is changed to produces more bars, the text shifts. So the x,y values of text are dependent on the plot.
What can I do for the text to remain lined up with "Large Title"
I would like it to look like this regardless of the number of bars being plotted. Please help.
By selecting the coordinate system in the string annotation, it can be set to be fixed, independent of the graph size. The value you set was set manually and should be modified.
ax.text(0, 0.07, '\nAttention Catcher', fontsize=13, ha='left')
Change to the following
ax.text(0.08, 1.07, '\nAttention Catcher', ha='left', va='top', fontsize=13, transform=fig.transFigure)
If the subject period is short

How do we align marker and text in legends vertically in Matplotlib?

When the marker in a legend is a dot, dot and text are not aligned vertically. To solve this I tried following:
l = ax.legend()
for text in l.texts:
text.set_va('center') # Is there some setting for this in matplotlibrc, too??
plt.show()
The vertical alignment of text in a legend seems to be baseline. But no matter whether I choose center, bottom or baseline, etc., things are off:
Zooming in, this is what Matplotlib gives us out of the box:
What I want is also what other software like Inkscape gives me, when aligning two objects vertically:
Can Matplotlib do this for me/us?
This appears to work:
Set it to display only a single scatterpoint per legend entry by setting scatterpoints=1 in the call to legend()
Set the vertical offset of this point to 0 by setting scatteryoffsets=[0] in the call to legend()
After creating the legend, iterate through its text labels and set their vertical alignment to center_baseline, using for t in l.get_texts(): t.set_va('center_baseline')
figure(figsize=(2,2))
scatter([0],[0],marker='s',s=20,label='Thing 1')
scatter([1],[0],marker='s',s=20,label='t')
scatter([2],[0],marker='s',s=20,label='T¹₁')
l = legend(scatterpoints=1,scatteryoffsets=[0],handletextpad=-0.5)
for t in l.get_texts(): t.set_va('center_baseline')
Here is what I do:
import numpy as np
import matplotlib
matplotlib.use('Agg')
matplotlib.rcParams['text.latex.preamble'] = r'\usepackage{amsmath}'
matplotlib.rc('text', usetex = True)
from matplotlib import pyplot as py
## setup figure
figure = py.figure(figsize = (7.5, 5.0))
axs = [py.subplot(1, 1, 1)]
## make plot
xs = np.linspace(0.0, np.pi, 100)
ys = np.sin(xs)
axs[0].plot(xs, ys, color = 'dodgerblue', label = r'$n = 1$')
ys = np.sin(2.0 * xs)
axs[0].plot(xs, ys, color = 'seagreen', label = r'$n = 2$')
axs[0].axhline(0.0, color = 'gray', linestyle = 'dashed')
## vertical alignment
legends = axs[0].legend(frameon = False, fontsize = 25, loc = 'lower left')
shift = np.average([_.get_window_extent(renderer = figure.canvas.get_renderer()).height for _ in legends.get_texts()])
shift /= 3.6
for _ in legends.get_texts():
_.set_va('center') ## va is alias for verticalalignment
_.set_position((0, shift))
## save figure
name = 'test.pdf'
py.tight_layout()
py.savefig(name)
py.close()
It is, however, complicated and requires manual adjustments,
I am still looking for better solutions.

Trouble with Networkx + Matplotlib Animations - 'NoneType' object has no attribute 'set_visible'

I've been having some trouble getting the following animation of DFS to run.
I believe it might be because there is no background canvas, but I'm not sure exactly how to fix this, as all other similar implementations online use plt.plot rather than nx.draw in saving images to be displayed.
Can someone offer any guidance?
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
fig = plt.figure()
ax = plt.gca()
colors = [0]*len(g)
cmap = plt.get_cmap("autumn")
g = nx.random_tree(20)
pos = nx.fruchterman_reingold_layout(g, k=0.1)
ims = [[nx.draw_networkx(g, pos, node_color = colors, cmap = cmap, ax = ax, vmin=0.0, vmax=1.0)]]
artists = [(nx.draw_networkx(g, pos, node_color = colors, cmap = cmap, ax = ax, vmin=0.0, vmax=1.0),)]
stack = [0]
while stack:
node = stack.pop()
if colors[node]:
continue
colors[node] = 0.8
stack += list(g[node].keys())
img = nx.draw_networkx(g, pos, node_color=colors, cmap=cmap, ax=ax, vmin=0.0, vmax=1.0)
ims += [img]
anim = ArtistAnimation(fig, ims, blit = True)
# plt.show()
The problem with your code is that nx.draw_networkx() doesn't return anything and it's always easier to use FuncAnimation method instead. At first, you need to create a color generator to make the animation function switch to the next color set by each call. Then using the FuncAnimation you can animate your frames (plots):
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib
import matplotlib.animation as animation
matplotlib.use('TkAgg')
plt.ion()
g = nx.random_tree(20)
colors = [0] * len(g)
cmap = plt.get_cmap('autumn')
pos = nx.fruchterman_reingold_layout(g, k=0.1)
# here you make the generator
def change_colors():
stack = [0]
yield colors
while stack:
node = stack.pop()
if colors[node]:
continue
colors[node] = 0.8
stack += list(g[node].keys())
yield colors
# instantiate your generator
color_gen = change_colors()
def update_frame(n):
# clear the plot
plt.cla()
# here switch to the next colors
colors = next(color_gen)
# then draw
nx.draw(g, pos, with_labels=True, node_color=colors, cmap=cmap, vmin=0.0, vmax=1.0)
ani = animation.FuncAnimation(plt.gcf(), update_frame, repeat=False, interval=1000)
plt.ioff()
plt.show()
which will give you:
you can save it as a gif file by replacing plt.show() with ani.save('anim.gif', writer='imagemagick').

how to add one more plot in matplotlib script

My matplotlib script plots a file "band.hdf5", which is in hdf5 format, with
f = h5py.File('band.hdf5', 'r')
I want to add one more hdf5 file "band-new.hdf5" here in such a way that the output plot will have one more plot on right side for new file. Y-axis label should be avoided for "band-new.hdf5" and X-axis label should be common for both file.
The header of the script is this
import h5py
import matplotlib.pyplot as plt
import warnings
import matplotlib
This script is taken from the accepted answer
https://stackoverflow.com/questions/62099211/how-to-plot-two-case1-hdf5-and-case2-hdf5-files-in-matplotlib-seeking-help-to-c?rq=1
Is this the solution you needed?
I take the code from
and adapted it to draw two plots side-to-side from the data you shared.
import h5py
import matplotlib.pyplot as plt
import warnings
import matplotlib
warnings.filterwarnings("ignore") # Ignore all warnings
cmap = matplotlib.cm.get_cmap('jet', 4)
ticklabels=['A','B','C','D','E']
params = {
'mathtext.default': 'regular',
'axes.linewidth': 1.2,
'axes.edgecolor': 'Black',
'font.family' : 'serif'
}
#get the viridis cmap with a resolution of 3
#apply a scale to the y axis. I'm just picking an arbritrary number here
scale = 10
offset = 0 #set this to a non-zero value if you want to have your lines offset in a waterfall style effect
f_left = h5py.File('band-222.hdf5', 'r')
f_right = h5py.File('band-332.hdf5', 'r')
print ('datasets from left are:')
print(list(f_left.keys()))
print ('datasets from right are:')
print(list(f_right.keys()))
# PLOTTING
plt.rcParams.update(params)
fig = plt.figure(figsize=(16,8))
ax1 = fig.add_subplot(121)
# LEFT ONE
dist=f_left[u'distance']
freq=f_left[u'frequency']
kpt=f_left[u'path']
lbl = {0:'AB', 1:'BC', 2:'CD', 3:'fourth'}
for i, section in enumerate(dist):
for nbnd, _ in enumerate(freq[i][0]):
x = section # to_list() you may need to convert sample to list.
y = (freq[i, :, nbnd] + offset*nbnd) * scale
if (nbnd<3):
color=f'C{nbnd}'
else:
color='black'
ax1.plot(x, y, c=color, lw=2.0, alpha=0.8, label = lbl[nbnd] if nbnd < 3 and i == 0 else None)
ax1.legend()
# Labels and axis limit and ticks
ax1.set_ylabel(r'Frequency (THz)', fontsize=12)
ax1.set_xlabel(r'Wave Vector (q)', fontsize=12)
ax1.set_xlim([dist[0][0],dist[len(dist)-1][-1]])
xticks=[dist[i][0] for i in range(len(dist))]
xticks.append(dist[len(dist)-1][-1])
ax1.set_xticks(xticks)
ax1.set_xticklabels(ticklabels)
# Plot grid
ax1.grid(which='major', axis='x', c='green', lw=2.5, linestyle='--', alpha=0.8)
# RIGHT ONE
ax2 = fig.add_subplot(122)
dist=f_right[u'distance']
freq=f_right[u'frequency']
kpt=f_right[u'path']
lbl = {0:'AB', 1:'BC', 2:'CD', 3:'fourth'}
for i, section in enumerate(dist):
for nbnd, _ in enumerate(freq[i][0]):
x = section # to_list() you may need to convert sample to list.
y = (freq[i, :, nbnd] + offset*nbnd) * scale
if (nbnd<3):
color=f'C{nbnd}'
else:
color='black'
ax2.plot(x, y, c=color, lw=2.0, alpha=0.8, label = lbl[nbnd] if nbnd < 3 and i == 0 else None)
ax2.legend()
# remove y axis
ax2.axes.get_yaxis().set_visible(False)
ax2.set_xlabel(r'Wave Vector (q)', fontsize=12)
ax2.set_xlim([dist[0][0],dist[len(dist)-1][-1]])
xticks=[dist[i][0] for i in range(len(dist))]
xticks.append(dist[len(dist)-1][-1])
ax2.set_xticks(xticks)
ax2.set_xticklabels(ticklabels)
# Plot grid
ax2.grid(which='major', axis='x', c='green', lw=2.5, linestyle='--', alpha=0.8)
fig.tight_layout() # Or equivalently, "plt.tight_layout()"
# Save to pdf
plt.savefig('plots.pdf', bbox_inches='tight')
The final figure is like this.

How can i plot a png image on a cartopy basemap in a special projection?

I'm trying to open a png-Image to plot this image on a basemap of cartopy.
I already followed these instructions on:
https://scitools.org.uk/cartopy/docs/v0.15/examples/geostationary.html
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy
from PIL import Image
def create_map(image):
res = '10m'
proj = ccrs.NorthPolarStereo(central_longitude=10.0)
img = plt.imread(image)
img_extent = (2.0715, 15.72, 46.9526, 54.5877)
ax = plt.axes(projection = proj)
ax.set_extent = ([3.0889, 17.1128, 46.1827, 55.5482])
land_10m = cfeature.NaturalEarthFeature('physical', 'land', res,
edgecolor = 'face',
facecolor=cfeature.COLORS['land'],
zorder=0)
state_provinces_10m = cfeature.NaturalEarthFeature(category = 'cultural',
name = 'admin_1_states_provinces_lines',
scale = res,
facecolor = none)
ax.add_feature(state_provinces_10m, edgecolor='gray')
ax.add_feature(land_10m)
ax.add_feature(cartopy.feature.BORDERS.with_scale(res), linestyle='-', linewith=1)
ax.add_feature(cartopy.feature.COASTLINE.with_scale(res), linestyle='-')
plt.imshow(img, origin='upper', extent=img_extent, transform = proj)
plt.show()
create_map('image.png')
My results are a basemap of the defined extent but without my image. What i am doing wrong?
regards
Your transform argument for imshow is almost certainly incorrect. An image extent of (2.0715, 15.72, 46.9526, 54.5877) in North polar stereographic projection is a very small region near the North Pole, which is not within your desired map extent. From context it looks like the extent is specified in geographic coordinates, in which case the solution should be to use transform=ccrs.PlateCarree() in your imshow call.
In general I recommend being explicit about what your coordinate system is at all times, so I would suggest
def create_map(image):
res = '10m'
proj = ccrs.NorthPolarStereo(central_longitude=10.0)
img = plt.imread(image)
img_extent = (2.0715, 15.72, 46.9526, 54.5877)
ax = plt.axes(projection = proj)
# EXPLICIT CRS HERE:
ax.set_extent([3.0889, 17.1128, 46.1827, 55.5482], crs=ccrs.PlateCarree())
land_10m = cfeature.NaturalEarthFeature('physical', 'land', res,
edgecolor = 'face',
facecolor=cfeature.COLORS['land'],
zorder=0)
state_provinces_10m = cfeature.NaturalEarthFeature(category = 'cultural',
name = 'admin_1_states_provinces_lines',
scale = res,
facecolor = none)
ax.add_feature(state_provinces_10m, edgecolor='gray')
ax.add_feature(land_10m)
ax.add_feature(cartopy.feature.BORDERS.with_scale(res), linestyle='-', linewith=1)
ax.add_feature(cartopy.feature.COASTLINE.with_scale(res), linestyle='-')
# USE CORRECT CRS HERE
plt.imshow(img, origin='upper', extent=img_extent, transform=ccrs.PlateCarree())
plt.show()
This documentation provides guidance on transforms/projections in Cartopy: https://scitools.org.uk/cartopy/docs/latest/tutorials/understanding_transform.html