Quel est le meilleur moyen de créer une série de diagrammes de dispersion en utilisant matplotlib
à partir d'un fichier de données pandas
en Python?
Par exemple, si j'ai un cadre de données df
qui a quelques colonnes d'intérêt, je me trouve généralement à tout convertir en tableaux:
import matplotlib.pylab as plt
# df is a DataFrame: fetch col1 and col2
# and drop na rows if any of the columns are NA
mydata = df[["col1", "col2"]].dropna(how="any")
# Now plot with matplotlib
vals = mydata.values
plt.scatter(vals[:, 0], vals[:, 1])
Le problème avec la conversion de tout en tableau avant de tracer, c'est que cela vous oblige à sortir des cadres de données.
Pensez à ces deux cas d’utilisation où la trame de données complète est essentielle au traçage:
Par exemple, si vous vouliez maintenant examiner toutes les valeurs de col3
pour les valeurs correspondantes que vous avez tracées dans l'appel à scatter
, et coloriez-vous chaque point (ou taille) par cette valeur? Vous devez revenir en arrière, extraire les valeurs non-na de col1,col2
et vérifiez quelles sont leurs valeurs correspondantes.
Existe-t-il un moyen de tracer tout en préservant le cadre de données? Par exemple:
mydata = df.dropna(how="any", subset=["col1", "col2"])
# plot a scatter of col1 by col2, with sizes according to col3
scatter(mydata(["col1", "col2"]), s=mydata["col3"])
De même, imaginez que vous souhaitiez filtrer ou colorer chaque point différemment en fonction des valeurs de certaines de ses colonnes. Par exemple. Et si vous vouliez tracer automatiquement les étiquettes des points qui rencontrent un certain seuil sur col1, col2
à côté d’eux (où les étiquettes sont stockées dans une autre colonne de la df), ou coloriez ces points différemment, comme le font les gens avec des images dans R. Par exemple:
mydata = df.dropna(how="any", subset=["col1", "col2"])
myscatter = scatter(mydata[["col1", "col2"]], s=1)
# Plot in red, with smaller size, all the points that
# have a col2 value greater than 0.5
myscatter.replot(mydata["col2"] > 0.5, color="red", s=0.5)
Comment cela peut-il être fait?
EDIT Répondre à crewbum:
Vous dites que le meilleur moyen est de tracer chaque condition (comme subset_a
, subset_b
) séparément. Et si vous avez plusieurs conditions, par exemple vous voulez diviser les dispersés en 4 types de points ou même plus, en traçant chacun sous une forme/couleur différente. Comment pouvez-vous appliquer avec élégance la condition a, b, c, etc. et vous assurer de tracer ensuite "le reste" (ce qui n'est pas le cas dans ces conditions) comme dernière étape?
De même dans votre exemple où vous tracez col1,col2
différemment en fonction de col3
, que se passe-t-il s’il existe des valeurs NA qui rompent l’association entre col1,col2,col3
? Par exemple, si vous voulez tracer tous les col2
valeurs basées sur leur col3
valeurs, mais certaines lignes ont une valeur NA dans col1
ou col3
, vous obligeant à utiliser dropna
en premier. Alors tu ferais:
mydata = df.dropna(how="any", subset=["col1", "col2", "col3")
alors vous pouvez tracer en utilisant mydata
comme vous le montrer - traçant la dispersion entre col1,col2
en utilisant les valeurs de col3
. Mais il manquera à mydata
des points ayant des valeurs pour col1,col2
mais sont NA pour col3
, et ceux-ci doivent encore être tracés ... Alors, comment voulez-vous essentiellement tracer "le reste" des données, c'est-à-dire les points qui sont et non dans le jeu filtré mydata
?
Essayez de passer les colonnes de DataFrame
directement à matplotlib, comme dans les exemples ci-dessous, au lieu de les extraire sous forme de tableaux numpy.
df = pd.DataFrame(np.random.randn(10,2), columns=['col1','col2'])
df['col3'] = np.arange(len(df))**2 * 100 + 100
In [5]: df
Out[5]:
col1 col2 col3
0 -1.000075 -0.759910 100
1 0.510382 0.972615 200
2 1.872067 -0.731010 500
3 0.131612 1.075142 1000
4 1.497820 0.237024 1700
plt.scatter(df.col1, df.col2, s=df.col3)
# OR (with pandas 0.13 and up)
df.plot(kind='scatter', x='col1', y='col2', s=df.col3)
colors = np.where(df.col3 > 300, 'r', 'k')
plt.scatter(df.col1, df.col2, s=120, c=colors)
# OR (with pandas 0.13 and up)
df.plot(kind='scatter', x='col1', y='col2', s=120, c=colors)
Cependant, le moyen le plus simple que j'ai trouvé de créer un nuage de points avec une légende est d'appeler plt.scatter
Une fois pour chaque type de point.
cond = df.col3 > 300
subset_a = df[cond].dropna()
subset_b = df[~cond].dropna()
plt.scatter(subset_a.col1, subset_a.col2, s=120, c='b', label='col3 > 300')
plt.scatter(subset_b.col1, subset_b.col2, s=60, c='r', label='col3 <= 300')
plt.legend()
D'après ce que je peux dire, matplotlib ignore simplement les points avec les coordonnées NA x/y ou les paramètres de style NA (par exemple, couleur/taille). Pour trouver des points ignorés à cause de NA, essayez la méthode isnull
: df[df.col3.isnull()]
Pour fractionner une liste de points en plusieurs types, jetez un oeil à numpy select
, qui est une implémentation vectorisée du type if-then-else et accepte une valeur par défaut facultative. Par exemple:
df['subset'] = np.select([df.col3 < 150, df.col3 < 400, df.col3 < 600],
[0, 1, 2], -1)
for color, label in Zip('bgrm', [0, 1, 2, -1]):
subset = df[df.subset == label]
plt.scatter(subset.col1, subset.col2, s=120, c=color, label=str(label))
plt.legend()
Il n’ya pas grand chose à ajouter à la réponse de Garrett, mais pandas a aussi une scatter
méthode En utilisant ça, c'est aussi simple que
df = pd.DataFrame(np.random.randn(10,2), columns=['col1','col2'])
df['col3'] = np.arange(len(df))**2 * 100 + 100
df.plot.scatter('col1', 'col2', df['col3'])
Je recommanderai d'utiliser une méthode alternative en utilisant seaborn
qui est un outil plus puissant pour le traçage des données. Vous pouvez utiliser seaborn scatterplot
et définissez la colonne 3 comme hue
et size
.
Code de travail:
import pandas as pd
import seaborn as sns
import numpy as np
#creating sample data
sample_data={'col_name_1':np.random.Rand(20),
'col_name_2': np.random.Rand(20),'col_name_3': np.arange(20)*100}
df= pd.DataFrame(sample_data)
sns.scatterplot(x="col_name_1", y="col_name_2", data=df, hue="col_name_3",size="col_name_3")