Wenn Sie ein Streudiagramm mit einer großen Anzahl von Datenpunkten zeichnen, ist es zu voll und Sie können nicht verstehen, wie viele Daten in einem bestimmten Bereich vorhanden sind.
Betrachten Sie als Beispiel die folgenden Daten, die durch Komprimieren des handgeschriebenen numerischen Bilddatensatzes (MNIST) in zwei Dimensionen mit UMAP erhalten wurden.
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 ist die X-Koordinate, y ist die Y-Koordinate und class ist die Bezeichnung (welche Zahl von 0 bis 9 geschrieben wird).
Zeichnen Sie wie gewohnt ein Streudiagramm mit matplotlib. Übrigens, obwohl dies nicht der Hauptpunkt ist, erleichtert die kürzlich hinzugefügte Funktion `` `legend_elements``` das Erstellen einer Legende für ein Streudiagramm mit mehreren Kategorien, ohne die for-Anweisung zu drehen.
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()
Es werden 70.000 Punkte gezeichnet. Es ist schön, separate Cluster für jede Zahl zu haben, aber bei einer so großen Datengröße sind die Punkte so dicht, dass sie sich überlappen und ausfüllen, wodurch die Struktur innerhalb jeder Klasse fast unsichtbar wird. Ich möchte etwas dagegen tun.
Um Überlappungen zu vermeiden, reduzieren Sie die Größe der Punkte oder passen Sie die Transparenz der Punkte an, damit die Dichte besser sichtbar ist. Es erfordert Versuch und Irrtum und ist nicht immer leicht zu erkennen.
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()
Dies ist auch ein guter Weg, dies zu tun. Die Leinwand ist mit einem sechseckigen Raster angeordnet, und die Anzahl der Datenpunkte in jedem wird aggregiert und in Farbtiefe ausgedrückt. Einfach zu bedienende Pandas-Plotfunktion.
fig, ax = plt.subplots(figsize=(12, 12))
df.plot.hexbin(x='x', y='y', gridsize=100, ax=ax)
plt.axis('off')
plt.show()
Es ist vielseitig und einfach zu bedienen. Solange du dich daran gewöhnt hast.
Datashader ist eine Bibliothek, die schnell "gerasterte Diagramme" für große Datenmengen generiert.
Nachdem die Auflösung (Anzahl der Pixel) der zuerst auszugebenden Figur festgelegt wurde, werden die Daten für jedes Pixel aggregiert und als Bild ausgegeben, und das Zeichnen erfolgt in drei Schritten. Da jeder Schritt fein eingestellt werden kann, ist der Freiheitsgrad hoch.
Jeder Schritt wird später beschrieben, aber wenn Sie alle mit den Standardeinstellungen schreiben, ist dies wie folgt.
import datashader as ds
from datashader import transfer_functions as tf
tf.shade(ds.Canvas().points(df,'x','y'))
In Datashader
Stellen Sie die Leinwand ein
Aggregierte Funktionseinstellungen und Berechnungen
In Bild konvertieren
Machen Sie eine Handlung in drei Schritten. Jedes wird unten erklärt.
datashader.Stellen Sie verschiedene Leinwände mit Leinwand ein. Vertikale und horizontale Auflösung (Pixel), logarithmische Achse oder nicht, numerischer Bereich (xlim in matplotlib),ylim) etc.
```python
canvas = ds.Canvas(plot_width=600, plot_height=600, #Vertikal und horizontal 600 Pixel
x_axis_type='linear', y_axis_type='linear', # 'linear' or 'log'
x_range=(-10,15), y_range=(-15,10))
Ich habe oben eine Leinwand mit (600 x 600) Pixeln gemacht. Hier legen wir fest, wie die Daten für jedes dieser Pixel aggregiert werden. Ändern Sie beispielsweise die Farbdichte entsprechend der Anzahl der Datenpunkte, die in ein Pixel eintreten, oder machen Sie daraus einen Binärwert, unabhängig davon, ob auch nur ein Datenpunkt enthalten ist oder nicht.
Geben Sie beispielsweise für die oben festgelegte Zeichenflächenvariable den Datenrahmen, die x-Achsenkoordinaten (Spaltenname), die y-Achsenkoordinaten und die Aggregatfunktion wie unten gezeigt ein und führen Sie die Berechnung aus. Die Funktion `` `datashader.reductions.count``` zählt die Anzahl der Datenpunkte, die in ein Pixel gehen.
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
Auf diese Weise wurden Zeichnungsdaten durch Zählen der Anzahl von Datenpunkten in einer Matrix mit einer Größe (600 x 600) erzeugt.
Wenn Sie anhand des Binärwerts aggregieren möchten, ob Datenpunkte eingegeben werden, anstatt zu zählen, verwenden Sie die Funktion `` `datashader.reductions.any``` und gehen Sie wie folgt vor.
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
Verwenden Sie zum Konvertieren in ein Bild die Funktion `shadow``` von`
datashader.transfer_functions. Übergeben Sie die oben berechneten aggregierten Matrixdaten an das Argument der Funktion "Schatten". Zusätzlich werden verschiedene "Übertragungsfunktionen" vorbereitet, und Sie können die Bildausgabe optimieren. Hier wird das Ergebnis des Zählens und Summierens mit der Funktion `` `set_background
in einen weißen Hintergrund gebracht und abgebildet.
tf.set_background(tf.shade(canvas.points(df,'x','y', agg=ds.count())), 'white')
Die Schattierung wird entsprechend der Dichte der Datenpunkte ausgedrückt, wodurch die Struktur viel einfacher zu sehen ist.
Versuchen Sie auf die gleiche Weise, mit zwei Werten zu summieren, ob Datenpunkte enthalten sind oder nicht.
tf.set_background(tf.shade(canvas.points(df,'x','y', agg=ds.any())), 'white')
Bisher wurden nur die Koordinateninformationen der Daten für die Aggregation verwendet, aber es ist häufig der Fall, dass jeder Datenpunkt eine Bezeichnung einer Kategorie hat oder ein kontinuierlicher Wert zugewiesen wird.
Da solche Informationen nicht durch einfaches Zählen der Datenpunkte, die in das Pixel eintreten, wiedergegeben werden, gibt es für jeden eine spezielle Aggregationsfunktion.
Im Fall von MNIST gibt es eine Beschriftung für die richtige Antwortklasse, daher möchte ich sie richtig farblich kennzeichnen und zeichnen. Als Aggregatfunktion dafür gibt es `` `datashader.reductions.count_cat```. Diese Funktion zählt die Anzahl der Datenpunkte, die für jedes Etikett in ein Pixel eingehen. Mit anderen Worten, im Fall von MNIST werden 10 (600 x 600) Aggregatmatrizen erstellt.
Um count_cat verwenden zu können, müssen die Etikettendaten vom Kategorietyp Pandas (nicht vom Typ int) sein. Konvertieren Sie daher zuerst die Etikettenzeichenfolge des Datenrahmens in den Kategorietyp.
df['class'] = df['class'].astype('category')
Aggregieren Sie mit count_cat. Im Gegensatz zu den Aggregatfunktionen von `count``` und
any``` müssen Sie den Spaltennamen angeben, dessen Spalte im Datenrahmen die Bezeichnung darstellt.
agg = canvas.points(df, 'x', 'y', ds.count_cat('class'))
Die Farbe jedes Etiketts wird in einem Wörterbuch definiert, wobei das Etikett als Schlüssel verwendet wird. Extrahieren Sie die Farbe "Paired" aus matplotlib, um sie an die Farbe der Figur anzupassen, wenn Sie sie zu Beginn zeichnen. Einfache Verwendung mit einer Liste vom Typ Wörterbuch.
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'}
Versuchen Sie es sich vorzustellen. Es scheint, dass die Farbe jedes Pixels gezeichnet wird, indem jede Farbe gemäß der Anzahl der Beschriftungen von Datenpunkten gemischt wird, die in das Pixel eintreten.
tf.set_background(tf.shade(agg, color_key=color_key), 'white')
Jedem Datenpunkt kann eine Art kontinuierlicher Wert zugeordnet sein. Wenn beispielsweise bei der Einzelzellanalyse dimensional komprimierte Zahlen von Zehntausenden von Zellen verwendet werden, wird die Farbtiefe für jede Zelle um ein bestimmtes Genexpressionsniveau geändert.
Da ein Pixel mehrere Datenpunkte enthält, muss auf irgendeine Weise ein repräsentativer Wert bestimmt werden. Als Aggregatfunktion dafür werden einfache Statistiken wie Max, Mean, Mode erstellt.
MNIST verfügt nicht über Hilfsdaten mit kontinuierlichem Wert. Versuchen Sie daher, diese entsprechend zu erstellen. Berechnen wir als leicht verständliche Menge die durchschnittliche Helligkeit des zentralen Bildbereichs. Null sollte dunkel sein (da die Linie selten in der Bildmitte verläuft) und 1 sollte hell sein.
data = pd.read_csv('./mnist.csv').values[:, :784]
data.shape
(70000, 784)
#Es ist ein Bild in der Größe 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)
Versuchen Sie zunächst, normal mit matplotlib zu zeichnen.
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()
Immerhin ist es zerquetscht und ich verstehe nicht gut.
Übergeben Sie es an den Datashader und versuchen Sie, es gemäß dem "Maximalwert" der in jedem Pixel enthaltenen Datenpunkte zu malen. Es kann mit der Funktion `` `datashader.reductions.max``` aggregiert werden.
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')
Es ist leichter zu sehen. Es unterscheidet sich möglicherweise nicht wesentlich von der Anpassung der Größe auf eine kleinere Größe mit Streuung von Matplotlib, aber es ist praktisch, ohne detailliertes Ausprobieren schön zeichnen zu können.
Auch wenn die Datengröße sehr groß ist, ist sie schnell, sodass es nicht stressig ist, verschiedene Anpassungen vorzunehmen, z. B. was passiert, wenn mit Durchschnittswerten summiert wird.
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