Si vous dessinez un diagramme de dispersion avec un grand nombre de points de données, il sera trop encombré et vous ne pourrez pas comprendre la quantité de données existant dans une certaine zone.
À titre d'exemple, considérons les données suivantes obtenues en compressant l'ensemble de données d'images numériques manuscrites (MNIST) en deux dimensions avec UMAP.
import pandas as pd
df = pd.read_csv('./mnist_embedding.csv', index_col=0)
display(df)
x | y | class | |
---|---|---|---|
0 | 1.273394 | 1.008444 | 5 |
1 | 12.570375 | 0.472456 | 0 |
2 | -2.197421 | 8.652475 | 4 |
3 | -5.642218 | -4.971571 | 1 |
4 | -3.874749 | 5.150311 | 9 |
... | ... | ... | ... |
69995 | -0.502520 | -7.309745 | 2 |
69996 | 3.264405 | -0.887491 | 3 |
69997 | -4.995078 | 8.153721 | 4 |
69998 | -0.226225 | -0.188836 | 5 |
69999 | 8.405535 | -2.277809 | 6 |
70000 rows × 3 columns
x est la coordonnée X, y est la coordonnée Y et la classe est l'étiquette (quel nombre de 0 à 9 est écrit).
Essayez de dessiner un diagramme de dispersion avec matplotlib comme d'habitude. À propos, bien que ce ne soit pas le point principal, la fonction récemment ajoutée `` legend_elements '' facilite la création d'une légende pour un diagramme de dispersion multi-catégorie sans tourner l'instruction for.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 12))
sc = ax.scatter(df['x'], df['y'], c=df['class'], cmap='Paired', s=6, alpha=1.0)
ax.add_artist(ax.legend(*sc.legend_elements(), loc="upper right", title="Classes"))
plt.axis('off')
plt.show()
70 000 points sont tracés. C'est bien d'avoir des clusters séparés pour chaque nombre, mais avec une taille de données si grande, les points sont si denses qu'ils se chevauchent et se remplissent, rendant la structure de chaque classe presque invisible. Je veux faire quelque chose à ce sujet.
Pour éviter les chevauchements, réduisez la taille des points ou ajustez la transparence des points pour rendre la densité plus facile à voir. Cela nécessite des essais et des erreurs, et ce n'est pas toujours facile à voir.
fig, ax = plt.subplots(figsize=(12, 12))
sc = ax.scatter(df['x'], df['y'], c=df['class'], cmap='Paired', s=3, alpha=0.1)
ax.add_artist(ax.legend(*sc.legend_elements(), loc="upper right", title="Classes"))
plt.axis('off')
plt.show()
C'est aussi une bonne façon de le faire. Le canevas est disposé avec une grille hexagonale et le nombre de points de données dans chacun est agrégé et exprimé en profondeur de couleur. Fonction de tracé Pandas facile à utiliser.
fig, ax = plt.subplots(figsize=(12, 12))
df.plot.hexbin(x='x', y='y', gridsize=100, ax=ax)
plt.axis('off')
plt.show()
Il est polyvalent et facile à utiliser. Tant que vous vous y habituez.
Datashader est une bibliothèque qui génère rapidement des "tracés rasterisés" pour les grands ensembles de données.
Après avoir décidé de la résolution (nombre de pixels) de la figure à sortir en premier, les données sont agrégées pour chaque pixel et sorties sous forme d'image, ce qui correspond aux trois étapes du dessin. Etant donné que chaque étape peut être finement ajustée, le degré de liberté est élevé.
Chaque étape sera décrite plus tard, mais si vous les écrivez toutes avec les paramètres par défaut, ce sera comme suit.
import datashader as ds
from datashader import transfer_functions as tf
tf.shade(ds.Canvas().points(df,'x','y'))
Dans Datashader
Définissez la toile
Paramètres et calculs de la fonction d'agrégation
Convertir en image
Faites un tracé en trois étapes. Chacun est expliqué ci-dessous.
datashader.Définissez diverses toiles avec Canvas. Résolution verticale et horizontale (pixels), axe logarithmique ou non, plage numérique (xlim dans matplotlib),ylim) etc.
```python
canvas = ds.Canvas(plot_width=600, plot_height=600, #600 pixels verticaux et horizontaux
x_axis_type='linear', y_axis_type='linear', # 'linear' or 'log'
x_range=(-10,15), y_range=(-15,10))
J'ai fait une toile de (600 x 600) pixels ci-dessus. Ici, nous définissons comment agréger les données pour chacun de ces pixels. Par exemple, modifiez la densité de couleur en fonction du nombre de points de données qui entrent un pixel ou faites-en une valeur binaire, même si un seul point de données est inclus ou non.
Par exemple, pour la variable de canevas définie ci-dessus, entrez le bloc de données, les coordonnées de l'axe x (nom de la colonne), les coordonnées de l'axe y et la fonction d'agrégation comme indiqué ci-dessous, puis exécutez le calcul. La fonction `` datashader.reductions.count '' compte le nombre de points de données qui entrent dans un pixel.
canvas.points(df, 'x', 'y', agg=ds.count())
<xarray.DataArray (y: 600, x: 600)> array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=int32) Coordinates: * x (x) float64 -9.979 -9.938 -9.896 -9.854 ... 14.85 14.9 14.94 14.98 * y (y) float64 -14.98 -14.94 -14.9 -14.85 ... 9.854 9.896 9.938 9.979
De cette manière, les données de dessin ont été générées en comptant le nombre de points de données dans une matrice de taille (600 x 600).
Si vous souhaitez agréger par la valeur binaire de la saisie ou non des points de données au lieu de compter, utilisez la fonction
datashader.reductions.any '' et procédez comme suit.
canvas.points(df, 'x', 'y', agg=ds.any())
<xarray.DataArray (y: 600, x: 600)> array([[False, False, False, ..., False, False, False], [False, False, False, ..., False, False, False], [False, False, False, ..., False, False, False], ..., [False, False, False, ..., False, False, False], [False, False, False, ..., False, False, False], [False, False, False, ..., False, False, False]]) Coordinates: * x (x) float64 -9.979 -9.938 -9.896 -9.854 ... 14.85 14.9 14.94 14.98 * y (y) float64 -14.98 -14.94 -14.9 -14.85 ... 9.854 9.896 9.938 9.979
Pour convertir en image, utilisez la fonction
shader de `` datashader.transfer_functions
. Passez les données matricielles agrégées calculées ci-dessus à l'argument de la fonction ombre ''. De plus, diverses
fonctions de transfert '' sont préparées et vous pouvez affiner la sortie de l'image. Ici, le résultat du comptage et du total est transformé en fond blanc avec la fonction `` set_background '' et mis en image.
tf.set_background(tf.shade(canvas.points(df,'x','y', agg=ds.count())), 'white')
L'ombrage est exprimé en fonction de la densité des points de données, ce qui rend la structure beaucoup plus facile à voir.
De la même manière, essayez le cas d'un total avec deux valeurs indiquant si les points de données sont inclus ou non.
tf.set_background(tf.shade(canvas.points(df,'x','y', agg=ds.any())), 'white')
Jusqu'à présent, seules les informations de coordonnées des données étaient utilisées pour l'agrégation, mais il arrive souvent que chaque point de données ait une étiquette d'une catégorie ou une valeur continue est attribuée.
Puisque ces informations ne sont pas reflétées en comptant simplement les points de données qui entrent dans le pixel, il existe une fonction d'agrégation spéciale pour chacun.
Dans le cas de MNIST, il y a une étiquette pour la classe de réponse correcte, donc je veux la coder correctement et la tracer. En tant que fonction d'agrégation pour cela, il y a
datashader.reductions.count_cat```. Cette fonction compte le nombre de points de données qui entrent dans un pixel pour chaque étiquette. En d'autres termes, dans le cas de MNIST, 10 matrices agrégées (600 x 600) seront créées.
Pour utiliser count_cat, les données d'étiquette doivent être du type de catégorie Pandas (le type int n'est pas bon), donc convertissez d'abord la chaîne d'étiquette du bloc de données en type de catégorie.
df['class'] = df['class'].astype('category')
Agréger avec count_cat. Contrairement aux fonctions d'agrégation de `` count '' et
`any '', vous devez spécifier le nom de colonne dont la colonne du bloc de données représente l'étiquette.
agg = canvas.points(df, 'x', 'y', ds.count_cat('class'))
La couleur de chaque étiquette est définie dans un dictionnaire en utilisant l'étiquette comme clé. Extrayez la couleur "Paired" de matplotlib pour qu'elle corresponde à la couleur de la figure lorsqu'elle est dessinée au début. Inclusion de liste de type dictionnaire facile à utiliser.
import matplotlib
color_key = {i:matplotlib.colors.rgb2hex(c[:3]) for i, c
in enumerate(matplotlib.cm.get_cmap('Paired', 10).colors)}
print(color_key)
{0: '#a6cee3', 1: '#1f78b4', 2: '#b2df8a', 3: '#fb9a99', 4: '#e31a1c', 5: '#fdbf6f', 6: '#cab2d6', 7: '#6a3d9a', 8: '#ffff99', 9: '#b15928'}
Essayez de l'imaginer. Il semble que la couleur de chaque pixel soit dessinée en mélangeant chaque couleur en fonction du nombre d'étiquettes de points de données qui entrent dans le pixel.
tf.set_background(tf.shade(agg, color_key=color_key), 'white')
Une sorte de valeur continue peut être associée à chaque point de données. Par exemple, dans l'analyse d'une cellule unique, lorsque des chiffres compressés dimensionnellement de dizaines de milliers de cellules sont utilisés, la profondeur de couleur est modifiée en fonction du niveau d'expression d'un gène pour chaque cellule.
Puisqu'un pixel contient plusieurs points de données, une valeur représentative doit être déterminée d'une manière ou d'une autre. En tant que fonction d'agrégation pour cela, des statistiques simples telles que max, moyenne, mode sont préparées.
MNIST n'a pas de données auxiliaires de valeur continue, essayez donc de le créer de manière appropriée. Comme une quantité facile à comprendre, calculons la luminosité moyenne de la zone centrale de l'image. Zéro doit être sombre (car la ligne passe rarement au milieu de l'image) et 1 doit être clair.
data = pd.read_csv('./mnist.csv').values[:, :784]
data.shape
(70000, 784)
#C'est une image de taille 28 x 28.
upper_left = 28 * 13 + 14
upper_right = 28 * 13 + 15
bottom_left = 28 * 14 + 14
bottom_right = 28 * 14 + 15
average_center_area = data[:, [upper_left, upper_right,
bottom_left, bottom_right]].mean(axis=1)
Tout d'abord, essayez de dessiner normalement avec matplotlib.
fig, ax = plt.subplots(figsize=(12, 12))
sc = ax.scatter(df['x'], df['y'], c=average_center_area, cmap='viridis',
vmin=0, vmax=255, s=6, alpha=1.0)
plt.colorbar(sc)
plt.axis('off')
plt.show()
Après tout il est écrasé et je ne comprends pas bien.
Passez-le au Datashader et essayez de le peindre en fonction de la "valeur maximale" des points de données contenus dans chaque pixel. Il peut être agrégé avec la fonction `` datashader.reductions.max ''.
df['value'] = average_center_area
agg = canvas.points(df, 'x', 'y', agg=ds.max('value'))
tf.set_background(tf.shade(agg, cmap=matplotlib.cm.get_cmap('viridis')), 'white')
C'est plus facile à voir. Cela peut ne pas être très différent d'ajuster la taille à une taille plus petite avec dispersion de matplotlib, mais il est pratique de pouvoir dessiner magnifiquement sans essais et erreurs détaillés.
De plus, même si la taille des données est énorme, elle est rapide, il n'est donc pas stressant de faire divers ajustements tels que ce qui se passe lors d'un total avec des valeurs moyennes.
agg = canvas.points(df, 'x', 'y', agg=ds.mean('value'))
tf.set_background(tf.shade(agg, cmap=matplotlib.cm.get_cmap('viridis')), 'white')
Recommended Posts