I've been trying to plot a shapefile over a basemap. My issue here is the placement of the legend. I wanted it to be placed outside (next to) the map.
Specifically, I am plotting the "Ecoregion" column of the shapefile which basically labels each polygon with a colour (I figured this was better than actually putting the names on each polygon). I've tried the following code and receive an error:
pip install geopandas
pip install contextily
import geopandas as gpd
import contextily as ctx
data = gpd.read_file("icemap.shp")
plt.rcParams.update({'font.size': 14})
ax = data.plot(
figsize=(12, 10),
column="Ecoregion",
cmap="tab10",
)
map = Basemap(
llcrnrlon=-50,
llcrnrlat=30,
urcrnrlon=70.0,
urcrnrlat=85.0,
resolution="i",
lat_0=39.5,
lon_0=1,
)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
map.fillcontinents(color="lightgreen")
map.drawcoastlines()
map.drawparallels(np.arange(10,90,20),labels=[1,1,1,1])
map.drawmeridians(np.arange(-180,180,30),labels=[1,1,0,1])
plt.title("Map", fontsize=16)
The error is -
WARNING:matplotlib.legend:No handles with labels found to put in legend.
So I tried adding
legend-True
in the "ax" parentheses and removed the "ax.legend(...)", but then the legend appears on top of the map as the picture below.
Does "handle" refer to the column that is plotted? If so, I'm confused as to why I get this error. Or do I need to add another line of code?
I'd be grateful to receive some help in this.
(Attached file link: https://drive.google.com/file/d/1OfOAstBbbxiqSybpl_CQf-o47YgpbY7D/view?usp=sharing)
I would also consider Cartopy, since Basemap has been end-of-life for a long time. The resolution of your vector is also well beyond what's plotted on screen, so you could really increase performance by simplifying it a little.
But you can pass the legend keywords along when plotting the Geodataframe.
ax = data.plot(
figsize=(10, 8),
column="Ecoregion",
cmap="tab10",
legend=True,
legend_kwds=dict(bbox_to_anchor=(1.05, 1), loc='upper left'),
)
I have two ndarrays: Mat, labels
Currently I display Mat:
plt.imshow(Mat, cmap='gray', vmin=0, vmax=1, interpolation='None')
labels has the same shape as Mat, and lables[(i,j)] contains a label of Mat[(i,j)].
How can I show the label on each pixel?
The easiest approach uses Seaborn's heatmap. When annot=True it prints the data values into the cells. But annot= can also be a matrix of labels. In that case it is important to set the print format to string (fmt='s'). annot_kws= can set additional keywords, such as fontsize or color. x and yticklabels can be incorporated in the call to heatmap(), or be set afterwards using matplotlib.
An important benefit of the default coloring is that Sorn uses black on the light colored cells and white on the dark cells.
Here is an example that uses some utf8 characters as labels.
from matplotlib import pyplot as plt
import numpy as np
import seaborn as sns
M, N = 5, 10
mat = np.random.rand(M, N)
labels = np.random.choice(['X', '☀', '★', '♛'], size=(M, N))
ax = sns.heatmap(mat, cmap="inferno", annot=labels, annot_kws={'fontsize': 16}, fmt='s')
plt.show()
PS: There is a matplotlib example in the documentation to create something similar without Seaborn. It can be easily adapted to print strings from a different matrix, and also a test can be added to change the color depending on the cell darkness.
I am trying to plot a confusion matrix of my predictions. My data is multi-class (13 different labels) so I'm using a heatmap.
As you can see below, my heat map looks generally okay but the labels are a bit out of position: y ticks should be a little lower and x ticks should be a bit more to the right. I want to move both axis ticks a bit so they will aligned with the center of each square.
my code:
sns.set()
my_mask = np.zeros((con_matrix.shape[0], con_matrix.shape[0]), dtype=int)
for i in range(con_matrix.shape[0]):
for j in range(con_matrix.shape[0]):
my_mask[i][j] = con_matrix[i][j] == 0
fig_dims = (10, 10)
plt.subplots(figsize=fig_dims)
ax = sns.heatmap(con_matrix, annot=True, fmt="d", linewidths=.5, cmap="Pastel1", cbar=False, mask=my_mask, vmax=15)
plt.xticks(range(len(party_names)), party_names, rotation=45)
plt.yticks(range(len(party_names)), party_names, rotation='horizontal')
plt.show()
and for reproduction purpose, here are con_matrix and party_names hard-coded:
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
con_matrix = np.array([[55, 0, 0, 0,0, 0, 0,0,0,0,0,0,2], [0,199,0,0,0,0,0,0,0,0,2,0,1],
[0, 0,52,0,0,0,0,0,0,0,0,0,1],
[0,0,0,39,0,0,0,0,0,0,0,0,0],
[0,0,0,0,90,0,0,0,0,0,0,4,3],
[0,0,0,1,0,35,0,0,0,0,0,0,0],
[0,0,0,0,5,0,26,0,0,1,0,1,0],
[0,5,0,0,0,1,0,44,0,0,3,0,1],
[0,1,0,0,0,0,0,0,52,0,0,0,0],
[0,1,0,0,2,0,0,0,0,235,0,1,1],
[1,2,0,0,0,0,0,3,0,0,34,0,3],
[0,0,0,0,5,0,0,0,0,1,0,40,0],
[0,0,0,0,0,0,0,0,0,1,0,0,46]])
party_names = ['Blues', 'Browns', 'Greens', 'Greys', 'Khakis', 'Oranges', 'Pinks', 'Purples', 'Reds', 'Turquoises', 'Violets', 'Whites', 'Yellows']
I already tried to work with position argument of different axes, but it did not turn out well. Could not find an exactly answer in this site as well (at least not a solution that works for categorical data).
I'm new in visualization with seaborn, any improvement with explanations would be appreciated (not only for my problem but on my code & visualization as well).
You can shift both the ticklabels by 0.5 offset to have the desired alignment. To do so, I have used NumPy's arange that enables vectorized addition of 0.5 to the whole array.
plt.xticks(np.arange(len(party_names))+0.5, party_names, rotation=45)
plt.yticks(np.arange(len(party_names))+0.5, party_names, rotation='horizontal')
I would like to create my graphs to match my LaTeX document and use the Helvetica font for both.
In LaTeX I have
\usepackage{helvet}
\renewcommand{\familydefault}{\sfdefault}
set.
The code in Python looks like this:
import matplotlib.pyplot as plt
import numpy as np
import locale
plt.rc('text', usetex=True)
plt.rcParams['text.latex.preamble'] = [
r'\usepackage[detect-all,locale=DE]{siunitx}', #SI-Einheiten, Komma
r'\usepackage{helvet}', #Helvetica als Schrift
r'\usepackage{icomma}']
locale.setlocale(locale.LC_NUMERIC, "de_DE.UTF-8")
plt.ticklabel_format(useLocale=True)
x = [1, 2, 3, 4]
y = [5, 6, 7.2, 8.1]
plt.plot(x, y, marker="o", label="setting1")
plt.xticks(np.arange(1.0, 4.2, step=0.5))
plt.xlabel("x (\si{\milli\metre})")
plt.ylabel("y (\si{\pascal})")
plt.legend()
plt.grid(True)
plt.savefig('test.pdf', bbox_inches='tight')
The problem is that "Pa" from the figure does not match the "Pa" in LaTeX
Adding this to my matplotlibrc file worked for me.
mathtext.fontset : custom
mathtext.it : Helvetica:italic
Also, I needed to have Helvetica-Oblique.ttf in my /usr/local/anaconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf directory. Olga Botvinnik has some good instructions in her blog. Someday, I'll put a similar set of instructions together on mine.
Note, that you're going to have to clear out your cache under ~/.matplotlib in order to refresh this.
Matplotlib says that custom fontsets are not supported and this might all break in a future update of Matplotlib.
I'd like to NOT specify a color for each plotted line, and have each line get a distinct color. But if I run:
from matplotlib import pyplot as plt
for i in range(20):
plt.plot([0, 1], [i, i])
plt.show()
then I get this output:
If you look at the image above, you can see that matplotlib attempts to pick colors for each line that are different, but eventually it re-uses colors - the top ten lines use the same colors as the bottom ten. I just want to stop it from repeating already used colors AND/OR feed it a list of colors to use.
I usually use the second one of these:
from matplotlib.pyplot import cm
import numpy as np
#variable n below should be number of curves to plot
#version 1:
color = cm.rainbow(np.linspace(0, 1, n))
for i, c in zip(range(n), color):
plt.plot(x, y, c=c)
#or version 2:
color = iter(cm.rainbow(np.linspace(0, 1, n)))
for i in range(n):
c = next(color)
plt.plot(x, y, c=c)
Example of 2:
matplotlib 1.5+
You can use axes.set_prop_cycle (example).
matplotlib 1.0-1.4
You can use axes.set_color_cycle (example).
matplotlib 0.x
You can use Axes.set_default_color_cycle.
You can use a predefined "qualitative colormap" like this:
from matplotlib.cm import get_cmap
name = "Accent"
cmap = get_cmap(name) # type: matplotlib.colors.ListedColormap
colors = cmap.colors # type: list
axes.set_prop_cycle(color=colors)
Tested on matplotlib 3.0.3. See https://github.com/matplotlib/matplotlib/issues/10840 for discussion on why you can't call axes.set_prop_cycle(color=cmap).
A list of predefined qualititative colormaps is available at https://matplotlib.org/gallery/color/colormap_reference.html :
prop_cycle
color_cycle was deprecated in 1.5 in favor of this generalization: http://matplotlib.org/users/whats_new.html#added-axes-prop-cycle-key-to-rcparams
# cycler is a separate package extracted from matplotlib.
from cycler import cycler
import matplotlib.pyplot as plt
plt.rc('axes', prop_cycle=(cycler('color', ['r', 'g', 'b'])))
plt.plot([1, 2])
plt.plot([2, 3])
plt.plot([3, 4])
plt.plot([4, 5])
plt.plot([5, 6])
plt.show()
Also shown in the (now badly named) example: http://matplotlib.org/1.5.1/examples/color/color_cycle_demo.html mentioned at: https://stackoverflow.com/a/4971431/895245
Tested in matplotlib 1.5.1.
I don't know if you can automatically change the color, but you could exploit your loop to generate different colors:
for i in range(20):
ax1.plot(x, y, color = (0, i / 20.0, 0, 1)
In this case, colors will vary from black to 100% green, but you can tune it if you want.
See the matplotlib plot() docs and look for the color keyword argument.
If you want to feed a list of colors, just make sure that you have a list big enough and then use the index of the loop to select the color
colors = ['r', 'b', ...., 'w']
for i in range(20):
ax1.plot(x, y, color = colors[i])
You can also change the default color cycle in your matplotlibrc file.
If you don't know where that file is, do the following in python:
import matplotlib
matplotlib.matplotlib_fname()
This will show you the path to your currently used matplotlibrc file.
In that file you will find amongst many other settings also the one for axes.color.cycle. Just put in your desired sequence of colors and you will find it in every plot you make.
Note that you can also use all valid html color names in matplotlib.
As Ciro's answer notes, you can use prop_cycle to set a list of colors for matplotlib to cycle through. But how many colors? What if you want to use the same color cycle for lots of plots, with different numbers of lines?
One tactic would be to use a formula like the one from https://gamedev.stackexchange.com/a/46469/22397, to generate an infinite sequence of colors where each color tries to be significantly different from all those that preceded it.
Unfortunately, prop_cycle won't accept infinite sequences - it will hang forever if you pass it one. But we can take, say, the first 1000 colors generated from such a sequence, and set it as the color cycle. That way, for plots with any sane number of lines, you should get distinguishable colors.
Example:
from matplotlib import pyplot as plt
from matplotlib.colors import hsv_to_rgb
from cycler import cycler
# 1000 distinct colors:
colors = [hsv_to_rgb([(i * 0.618033988749895) % 1.0, 1, 1])
for i in range(1000)]
plt.rc('axes', prop_cycle=(cycler('color', colors)))
for i in range(20):
plt.plot([1, 0], [i, i])
plt.show()
Output:
Now, all the colors are different - although I admit that I struggle to distinguish a few of them!