Trame a dispersione in Panda / Pyplot: come tracciare per categoria


90

Sto cercando di creare un semplice grafico a dispersione in pyplot utilizzando un oggetto Pandas DataFrame, ma voglio un modo efficiente di tracciare due variabili ma avere i simboli dettati da una terza colonna (chiave). Ho provato vari modi utilizzando df.groupby, ma non con successo. Di seguito è riportato uno script df di esempio. Questo colora gli indicatori in base a "chiave1", ma a me piace vedere una legenda con categorie "chiave1". Sono vicino? Grazie.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
plt.show()

Risposte:


120

Puoi usarlo scatterper questo, ma ciò richiede di avere valori numerici per il tuo key1e non avrai una legenda, come hai notato.

È meglio usare solo plotper categorie discrete come questa. Per esempio:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

groups = df.groupby('label')

# Plot
fig, ax = plt.subplots()
ax.margins(0.05) # Optional, just adds 5% padding to the autoscaling
for name, group in groups:
    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
ax.legend()

plt.show()

inserisci qui la descrizione dell'immagine

Se desideri che le cose assomiglino allo pandasstile predefinito , aggiorna semplicemente rcParamscon il foglio di stile Panda e usa il suo generatore di colori. (Sto anche modificando leggermente la legenda):

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

groups = df.groupby('label')

# Plot
plt.rcParams.update(pd.tools.plotting.mpl_stylesheet)
colors = pd.tools.plotting._get_standard_colors(len(groups), color_type='random')

fig, ax = plt.subplots()
ax.set_color_cycle(colors)
ax.margins(0.05)
for name, group in groups:
    ax.plot(group.x, group.y, marker='o', linestyle='', ms=12, label=name)
ax.legend(numpoints=1, loc='upper left')

plt.show()

inserisci qui la descrizione dell'immagine


Perché nell'esempio RGB sopra il simbolo è mostrato due volte nella legenda? Come mostrare solo una volta?
Steve Schulist

1
@SteveSchulist - Utilizzare ax.legend(numpoints=1)per mostrare solo un marker. Ce ne sono due, come con a Line2D, spesso c'è una linea che collega i due marker.
Joe Kington,

Questo codice ha funzionato solo per me dopo l'aggiunta plt.hold(True)dopo il ax.plot()comando. Qualche idea sul perché?
Yuval Atzmon

set_color_cycle() era deprecato in matplotlib 1.5. C'è set_prop_cycle(), ora.
ale

52

Questo è semplice da fare con Seaborn ( pip install seaborn) come oneliner

sns.scatterplot(x_vars="one", y_vars="two", data=df, hue="key1") :

import seaborn as sns
import pandas as pd
import numpy as np
np.random.seed(1974)

df = pd.DataFrame(
    np.random.normal(10, 1, 30).reshape(10, 3),
    index=pd.date_range('2010-01-01', freq='M', periods=10),
    columns=('one', 'two', 'three'))
df['key1'] = (4, 4, 4, 6, 6, 6, 8, 8, 8, 8)

sns.scatterplot(x="one", y="two", data=df, hue="key1")

inserisci qui la descrizione dell'immagine

Ecco il dataframe di riferimento:

inserisci qui la descrizione dell'immagine

Poiché hai tre colonne variabili nei tuoi dati, potresti voler tracciare tutte le dimensioni a coppie con:

sns.pairplot(vars=["one","two","three"], data=df, hue="key1")

inserisci qui la descrizione dell'immagine

https://rasbt.github.io/mlxtend/user_guide/plotting/category_scatter/ è un'altra opzione.


19

Con plt.scatter, posso solo pensare a uno: usare un artista proxy:

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
fig1 = plt.figure(1)
ax1 = fig1.add_subplot(111)
x=ax1.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)

ccm=x.get_cmap()
circles=[Line2D(range(1), range(1), color='w', marker='o', markersize=10, markerfacecolor=item) for item in ccm((array([4,6,8])-4.0)/4)]
leg = plt.legend(circles, ['4','6','8'], loc = "center left", bbox_to_anchor = (1, 0.5), numpoints = 1)

E il risultato è:

inserisci qui la descrizione dell'immagine


10

Puoi usare df.plot.scatter e passare un array all'argomento c = che definisce il colore di ogni punto:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), index = pd.date_range('2010-01-01', freq = 'M', periods = 10), columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)
colors = np.where(df["key1"]==4,'r','-')
colors[df["key1"]==6] = 'g'
colors[df["key1"]==8] = 'b'
print(colors)
df.plot.scatter(x="one",y="two",c=colors)
plt.show()

inserisci qui la descrizione dell'immagine


4

Puoi anche provare Altair o ggpot che si concentrano su visualizzazioni dichiarative.

import numpy as np
import pandas as pd
np.random.seed(1974)

# Generate Data
num = 20
x, y = np.random.random((2, num))
labels = np.random.choice(['a', 'b', 'c'], num)
df = pd.DataFrame(dict(x=x, y=y, label=labels))

Codice Altair

from altair import Chart
c = Chart(df)
c.mark_circle().encode(x='x', y='y', color='label')

inserisci qui la descrizione dell'immagine

codice ggplot

from ggplot import *
ggplot(aes(x='x', y='y', color='label'), data=df) +\
geom_point(size=50) +\
theme_bw()

inserisci qui la descrizione dell'immagine


4

Da matplotlib 3.1 in poi puoi usare .legend_elements(). Un esempio è mostrato in Creazione automatica della legenda . Il vantaggio è che può essere utilizzata una singola chiamata scatter.

In questo caso:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                  index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                  columns = ('one', 'two', 'three'))
df['key1'] = (4,4,4,6,6,6,8,8,8,8)


fig, ax = plt.subplots()
sc = ax.scatter(df['one'], df['two'], marker = 'o', c = df['key1'], alpha = 0.8)
ax.legend(*sc.legend_elements())
plt.show()

inserisci qui la descrizione dell'immagine

Nel caso in cui le chiavi non siano state fornite direttamente come numeri, apparirebbe come

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.normal(10,1,30).reshape(10,3), 
                  index = pd.date_range('2010-01-01', freq = 'M', periods = 10), 
                  columns = ('one', 'two', 'three'))
df['key1'] = list("AAABBBCCCC")

labels, index = np.unique(df["key1"], return_inverse=True)

fig, ax = plt.subplots()
sc = ax.scatter(df['one'], df['two'], marker = 'o', c = index, alpha = 0.8)
ax.legend(sc.legend_elements()[0], labels)
plt.show()

inserisci qui la descrizione dell'immagine


Ho ricevuto un errore che diceva che l'oggetto "PathCollection" non ha l'attributo "legends_elements". Il mio codice è il seguente. fig, ax = plt.subplots(1, 1, figsize = (4,4)) scat = ax.scatter(rand_jitter(important_dataframe["workout_type_int"], jitter = 0.04), important_dataframe["distance"], c = color_list, marker = 'o', alpha = 0.9) print(scat.legends_elements()) #ax.legend(*scat.legend_elements())
Nandish Patel

1
@NandishPatel Controlla la prima frase di questa risposta. Assicurati anche di non confondere legends_elementse legend_elements.
ImportanceOfBeingErnest

Si Grazie. Era un errore di battitura (leggende / leggenda). Stavo lavorando a qualcosa dalle ultime 6 ore, quindi la versione di Matplotlib non mi è venuta in mente. Pensavo di usare l'ultimo. Ero confuso che la documentazione dica che esiste un tale metodo ma il codice dava un errore. Grazie ancora. Adesso posso dormire.
Nandish Patel


1

seaborn ha una funzione wrapper scatterplotche lo fa in modo più efficiente.

sns.scatterplot(data = df, x = 'one', y = 'two', data =  'key1'])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.