Home > Net >  How to cycle colors in Matplotlib PatchCollection?
How to cycle colors in Matplotlib PatchCollection?

Time:01-04

I am trying to automatically give each Patch in a PatchCollection a color from a color map like tab20.

from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5,5))
coords = [
    (0, 0),
    (1, 2),
    (1, 3),
    (2, 2),
]
patches = [plt.Circle(coords[i], 0.1) for i in range(len(coords))]
patch_collection = PatchCollection(patches, cmap='tab20', match_original=True)
ax.add_collection(patch_collection)

ax.set_xlim(-1, 3)
ax.set_ylim(-1, 4)
plt.axis('equal')

But the above code is drawing each circle using the same color. How can the colors be cycled?

enter image description here

CodePudding user response:

enter image description here

Here I've sampled the tab20 colormap, so that the RGBA array cmap.colors has exactly 20 different entries, then I;ve assigned this RGBA array to the keyword argument facecolors that every collection accepts.

Not just for cosmetics, I've added a colormap, so that it's possible to recognize the order in which the circles were drawn.

from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt
from numpy.random import rand, seed

seed(20230104)
N = 20
coords = rand(N,2)*[2,1.2]
cmap = plt.get_cmap('tab20', N)

fig, ax = plt.subplots()
patches = [plt.Circle(coord, 0.06) for coord in coords]
# use facecolors=...
collection = PatchCollection(patches, facecolors=cmap.colors[:N-1])
ax.add_collection(collection)
cb = plt.colorbar(plt.cm.ScalarMappable(plt.Normalize(-0.5, N-0.5), cmap))
cb.set_ticks(range(N), labels=('d'%(n 1) for n in range(N)))
ax.autoscale(collection)
ax.set_aspect(1)

CodePudding user response:

This gives each patch its color from a fixed subset of colors in the selected colormap, repeating as necessary:

from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt

num_col = 3
cmap = plt.cm.tab20

fig, ax = plt.subplots(figsize=(5,5))
coords = [
    (0, 0),
    (1, 2),
    (1, 3),
    (2, 2),
]

patches = [plt.Circle(coords[i], 0.1) for i in range(len(coords))]
patch_collection = PatchCollection(patches, facecolor=cmap.colors[0:num_col])
ax.add_collection(patch_collection)

ax.set_xlim(-1, 3)
ax.set_ylim(-1, 4)
plt.axis('equal')

Output:

enter image description here

This gives a random color from the selected colormap by using numpy to generate a list of random numbers, then using the patch objects set_array method:

from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(5,5))
coords = [
    (0, 0),
    (1, 2),
    (1, 3),
    (2, 2),
]
patches = [plt.Circle(coords[i], 0.1) for i in range(len(coords))]
color_vals = np.random.rand(len(patches))
patch_collection = PatchCollection(patches, cmap='tab20', match_original=True)
patch_collection.set_array(color_vals)
ax.add_collection(patch_collection)

ax.set_xlim(-1, 3)
ax.set_ylim(-1, 4)
plt.axis('equal')

Output:

enter image description here

I don't think match_original=True is necessary as you want to change the default color of the original patches. I'm sure there other ways of doing this as well. This SO post was helpful: setting color range in matplotlib patchcollection

  • Related