J'ai un tableau avec des valeurs et je veux en créer un histogramme. Je suis principalement intéressé par les numéros bas de gamme et je souhaite collecter chaque numéro supérieur à 300 dans un seul bac. Ce bac doit avoir la même largeur que tous les autres bacs (également larges). Comment puis-je faire ceci?
Remarque: cette question est liée à cette question: Définition de la largeur du bac/échelle de l'axe des x dans l'histogramme Matplotlib
Voici ce que j'ai essayé jusqu'à présent:
import matplotlib.pyplot as plt
import numpy as np
def plot_histogram_01():
np.random.seed(1)
values_A = np.random.choice(np.arange(600), size=200, replace=True).tolist()
values_B = np.random.choice(np.arange(600), size=200, replace=True).tolist()
bins = [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 600]
fig, ax = plt.subplots(figsize=(9, 5))
_, bins, patches = plt.hist([values_A, values_B], normed=1, # normed is deprecated and will be replaced by density
bins=bins,
color=['#3782CC', '#AFD5FA'],
label=['A', 'B'])
xlabels = np.array(bins[1:], dtype='|S4')
xlabels[-1] = '300+'
N_labels = len(xlabels)
plt.xlim([0, 600])
plt.xticks(25 * np.arange(N_labels) + 12.5)
ax.set_xticklabels(xlabels)
plt.yticks([])
plt.title('')
plt.setp(patches, linewidth=0)
plt.legend()
fig.tight_layout()
plt.savefig('my_plot_01.png')
plt.close()
Voilà le résultat, qui n'a pas l'air sympa:
J'ai ensuite changé la ligne avec xlim:
plt.xlim([0, 325])
Avec le résultat suivant:
Il ressemble plus ou moins à ce que je veux, mais le dernier bac n'est pas visible maintenant. Quelle astuce me manque pour visualiser ce dernier bac d'une largeur de 25?
Numpy a une fonction pratique pour gérer cela: np.clip
. Malgré le nom du nom, il ne supprime pas les valeurs , il les limite simplement à la plage que vous spécifiez. Fondamentalement, il fait le "hack sale" d'Artem en ligne. Vous pouvez laisser les valeurs telles quelles, mais dans l'appel hist
, enveloppez simplement le tableau dans un np.clip
appeler, comme ça
plt.hist(np.clip(values_A, bins[0], bins[-1]), bins=bins)
C'est plus agréable pour plusieurs raisons:
C'est manière plus rapide - au moins pour un grand nombre d'éléments. Numpy fait son travail au niveau C. Opérer sur python lists (comme dans la compréhension des listes d'Artem) a beaucoup de surcharge pour chaque élément. En gros, si vous avez la possibilité d'utiliser numpy, vous devriez.
Vous le faites là où vous en avez besoin, ce qui réduit les risques d'erreurs dans votre code.
Vous n'avez pas besoin de garder une deuxième copie du tableau qui traîne, ce qui réduit l'utilisation de la mémoire (sauf dans cette seule ligne) et réduit encore les risques d'erreurs.
En utilisant bins[0], bins[-1]
au lieu de coder en dur, les valeurs réduisent les chances de commettre à nouveau des erreurs, car vous pouvez modifier les casiers là où bins
a été défini; vous n'avez pas besoin de vous souvenir de les changer dans l'appel à clip
ou ailleurs.
Donc, pour tout rassembler comme dans le PO:
import matplotlib.pyplot as plt
import numpy as np
def plot_histogram_01():
np.random.seed(1)
values_A = np.random.choice(np.arange(600), size=200, replace=True)
values_B = np.random.choice(np.arange(600), size=200, replace=True)
bins = np.arange(0,350,25)
fig, ax = plt.subplots(figsize=(9, 5))
_, bins, patches = plt.hist([np.clip(values_A, bins[0], bins[-1]),
np.clip(values_B, bins[0], bins[-1])],
# normed=1, # normed is deprecated; replace with density
density=True,
bins=bins, color=['#3782CC', '#AFD5FA'], label=['A', 'B'])
xlabels = bins[1:].astype(str)
xlabels[-1] += '+'
N_labels = len(xlabels)
plt.xlim([0, 325])
plt.xticks(25 * np.arange(N_labels) + 12.5)
ax.set_xticklabels(xlabels)
plt.yticks([])
plt.title('')
plt.setp(patches, linewidth=0)
plt.legend(loc='upper left')
fig.tight_layout()
plot_histogram_01()
Désolé, je ne connais pas matplotlib. J'ai donc un sale hack pour vous. Je viens de mettre toutes les valeurs supérieures à 300 dans un bac et j'ai changé la taille du bac.
La racine du problème est que matplotlib essaie de mettre tous les bacs sur l'intrigue. Dans R, je convertirais mes cases en variable factorielle, donc elles ne sont pas traitées comme des nombres réels.
import matplotlib.pyplot as plt
import numpy as np
def plot_histogram_01():
np.random.seed(1)
values_A = np.random.choice(np.arange(600), size=200, replace=True).tolist()
values_B = np.random.choice(np.arange(600), size=200, replace=True).tolist()
values_A_to_plot = [301 if i > 300 else i for i in values_A]
values_B_to_plot = [301 if i > 300 else i for i in values_B]
bins = [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325]
fig, ax = plt.subplots(figsize=(9, 5))
_, bins, patches = plt.hist([values_A_to_plot, values_B_to_plot], normed=1, # normed is deprecated and will be replaced by density
bins=bins,
color=['#3782CC', '#AFD5FA'],
label=['A', 'B'])
xlabels = np.array(bins[1:], dtype='|S4')
xlabels[-1] = '300+'
N_labels = len(xlabels)
plt.xticks(25 * np.arange(N_labels) + 12.5)
ax.set_xticklabels(xlabels)
plt.yticks([])
plt.title('')
plt.setp(patches, linewidth=0)
plt.legend()
fig.tight_layout()
plt.savefig('my_plot_01.png')
plt.close()
plot_histogram_01()