4

I'm trying to get just one decimal place for the legend. The data which the plot is based on (w_field) has values with 8 decimal places.

w_field = np.genfromtxt('w_field.dat') 
CV = plt.contour(w_field)
x,Vel = CV.legend_elements()
plt.legend(x,Vel, title= 'Vertical velocity (m/s)', fontsize= 10, bbox_to_anchor=(1.05, 1), loc='upper left') 
plt.xlabel('Nx')
plt.ylabel('Ny')

enter image description here

Liris
  • 1,399
  • 3
  • 11
  • 29
  • Does this answer your question? [How do you create a legend for a contour plot in matplotlib?](https://stackoverflow.com/questions/10490302/how-do-you-create-a-legend-for-a-contour-plot-in-matplotlib) – Zaraki Kenpachi Sep 22 '20 at 08:21
  • @ZarakiKenpachi The linked solutions are not suited for this case. The first solution is for filled contours. The second uses `clabel` which isn't suitable for short contours. – JohanC Sep 22 '20 at 09:01

2 Answers2

3

The function CV.legend_elements() accepts a formatting parameter, which should be a function returning a formatted string. Here is an example, showing the difference with the default formatting.

from matplotlib import pyplot as plt
import numpy as np

w_field = np.random.randn(60, 100).cumsum(axis=1).cumsum(axis=0) / 50
CV = plt.contour(w_field)
x, Vel = CV.legend_elements()
legend1 = plt.legend(x, Vel, title='Default formatting', fontsize=10, bbox_to_anchor=(1.02, 1.02), loc='upper left')
x, Vel = CV.legend_elements(str_format=lambda x: f'{x:.1f}')
plt.legend(x, Vel, title='With formatting function', fontsize=10, bbox_to_anchor=(1.02, 0), loc='lower left')
plt.gca().add_artist(legend1)  # matplotlib removes the legend when a second legend is created, here it is added again
plt.tight_layout()
plt.show()

example plot

PS: Even the official example from the documents show this strange formatting.

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • 1
    Great answer! It looks even nicer, if you format the string like this: `x, Vel = CV.legend_elements(str_format=lambda x: f'{x: .1f}')` – JE_Muc Sep 22 '20 at 10:09
  • @Scotty1- Something must be escaping me. You are just adding a space after the colon? Does that make a difference? – JohanC Sep 22 '20 at 15:19
  • @JohanC Yep, it introduces a blank for positive numbers, such that positive and negative numbers are aligned at the first digit. – JE_Muc Sep 22 '20 at 18:05
  • @Scotty1- OK, didn't know about that one. Thanks. Unfortunately it doesn't work in this case, because the `legend_elements` of `plt.contour` forces a TeX rendering, always adding `'$x = ...$'` around the number. Further, the TeX minus sign is much wider than one space. – JohanC Sep 22 '20 at 18:19
  • Oh right, somehow the TeX rendering was faulty. A restart now reproduces the behaviour you describe. In this case, replacing the blank with an explicit plus sign will make the formatting nicer: `x, Vel = CV.legend_elements(str_format=lambda x: f'{x:+.1f}')`. Unluckily I don't know of any built-in TeX command to make a spacing of the correct size instead of a plus. In Latex I'd use SIunitx or align env. for this purpose, but implementing it in matplotlib is quite complicated... – JE_Muc Sep 22 '20 at 19:27
1

This should work.

fig, ax = plt.subplots()
w_field = np.genfromtxt('w_field.dat') 
CV = ax.contour(w_field)
x,Vel = CV.legend_elements()
plt.legend(x,Vel, title= 'Vertical velocity (m/s)', fontsize= 10, bbox_to_anchor=(1.05, 1), loc='upper left') 
plt.xlabel('Nx')
plt.ylabel('Ny')

leg = ax.get_legend()

for lbl in leg.get_texts():
    label_text = lbl.get_text()
    float_num = label_text.split()[2]
    new_text = f'X = {float(float_num):,.1f}'
    lbl.set_text(new_text)
Pr0digy
  • 29
  • 2