web-dev-qa-db-fra.com

Comment rendre les nuages ​​de points Matplotlib transparents en tant que groupe?

Je fais quelques nuages ​​de points en utilisant Matplotlib (python 3.4.0, matplotlib 1.4.3, fonctionnant sous Linux Mint 17). Il est assez facile de définir la transparence alpha pour chaque point individuellement; existe-t-il un moyen de les définir en tant que groupe, de sorte que deux points qui se chevauchent du même groupe ne changent pas la couleur?

Exemple de code:

import matplotlib.pyplot as plt
import numpy as np

def points(n=100):
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y
x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
ax.scatter(x1, y1, s=100, color="blue", alpha=0.5)
ax.scatter(x2, y2, s=100, color="red", alpha=0.5)
fig.savefig("test_scatter.png")

Résultats dans cette sortie:

enter image description here

mais je veux quelque chose de plus comme celui-ci:

enter image description here

Je peux contourner ce problème en enregistrant au format SVG et en les regroupant manuellement dans Inkscape, puis en définissant la transparence, mais je préfère vraiment quelque chose que je peux coder. Aucune suggestion?

15
Jason

Oui, question intéressante. Vous pouvez obtenir ce nuage de points avec Shapely . Voici le code:

import matplotlib.pyplot as plt
import matplotlib.patches as ptc
import numpy as np
from shapely.geometry import Point
from shapely.ops import cascaded_union

n = 100
size = 0.02
alpha = 0.5

def points():
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y

x1, y1 = points()
x2, y2 = points()
polygons1 = [Point(x1[i], y1[i]).buffer(size) for i in range(n)]
polygons2 = [Point(x2[i], y2[i]).buffer(size) for i in range(n)]
polygons1 = cascaded_union(polygons1)
polygons2 = cascaded_union(polygons2)

fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
for polygon1 in polygons1:
    polygon1 = ptc.Polygon(np.array(polygon1.exterior), facecolor="red", lw=0, alpha=alpha)
    ax.add_patch(polygon1)
for polygon2 in polygons2:
    polygon2 = ptc.Polygon(np.array(polygon2.exterior), facecolor="blue", lw=0, alpha=alpha)
    ax.add_patch(polygon2)
ax.axis([-0.2, 1.2, -0.2, 1.2])

fig.savefig("test_scatter.png")

et le résultat est:

Test scatter

9
Flabetvibes

Question intéressante, je pense que toute utilisation de la transparence entraînera l'effet d'empilement que vous souhaitez éviter. Vous pouvez définir manuellement une couleur de type de transparence pour vous rapprocher des résultats souhaités,

import matplotlib.pyplot as plt
import numpy as np

def points(n=100):
    x = np.random.uniform(size=n)
    y = np.random.uniform(size=n)
    return x, y
x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
alpha = 0.5
ax.scatter(x1, y1, s=100, lw = 0, color=[1., alpha, alpha])
ax.scatter(x2, y2, s=100, lw = 0, color=[alpha, alpha, 1.])
plt.show()

Le chevauchement entre les différentes couleurs n'est pas inclus de cette façon mais vous obtenez,

enter image description here

8
Ed Smith

C'est un hack terrible, terrible, mais ça marche.

Vous voyez alors que Matplotlib trace des points de données comme des objets séparés qui peuvent se chevaucher, il trace la ligne entre eux comme un seul objet - même si cette ligne est divisée en plusieurs morceaux par NaN dans les données.

Dans cet esprit, vous pouvez le faire:

import numpy as np
from matplotlib import pyplot as plt

plt.rcParams['lines.solid_capstyle'] = 'round'

def expand(x, y, gap=1e-4):
    add = np.tile([0, gap, np.nan], len(x))
    x1 = np.repeat(x, 3) + add
    y1 = np.repeat(y, 3) + add
    return x1, y1

x1, y1 = points()
x2, y2 = points()
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, title="Test scatter")
ax.plot(*expand(x1, y1), lw=20, color="blue", alpha=0.5)
ax.plot(*expand(x2, y2), lw=20, color="red", alpha=0.5)

fig.savefig("test_scatter.png")
plt.show()

Et chaque couleur se chevauchera avec l'autre couleur mais pas avec elle-même.

enter image description here

Une mise en garde est que vous devez être prudent avec l'espacement entre les deux points que vous utilisez pour faire chaque cercle. S'ils sont éloignés de deux, la séparation sera visible sur votre tracé, mais s'ils sont trop rapprochés, matplotlib ne trace pas du tout la ligne. Cela signifie que la séparation doit être choisie en fonction de la plage de vos données, et si vous envisagez de créer un tracé interactif, il y a un risque de disparition soudaine de tous les points de données si vous effectuez un zoom arrière trop important et d'étirement si vous effectuez un zoom avant. trop.

Comme vous pouvez le voir, j'ai trouvé que 1e-5 est une bonne séparation pour les données avec une plage de [0,1].

3
Arcanist Lupus