2014-10-06 16 views
12

Ich habe ein Array mit Werten, und ich möchte ein Histogramm davon erstellen. Ich bin hauptsächlich an den niedrigen Zahlen interessiert und möchte jede Zahl über 300 in einem Fach sammeln. Dieser Behälter sollte die gleiche Breite wie alle anderen (gleich breiten) Behälter haben. Wie kann ich das machen?Matplotlib Histogramm mit Sammelbehälter für hohe Werte

Hinweis: diese Frage auf diese Frage bezogen werden: Defining bin width/x-axis scale in Matplotlib histogram

Dies ist, was ich bisher versucht:

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() 

Dies ist das Ergebnis, das nicht schön aussieht: enter image description here

Ich habe dann die Zeile mit xlim darin geändert:

plt.xlim([0, 325]) 

Mit folgendem Ergebnis: enter image description here

Es sieht mehr oder weniger wie ich es will, aber die letzten ist ist jetzt nicht sichtbar. Welchen Trick vermisse ich, um diesen letzten Behälter mit einer Breite von 25 zu visualisieren?

Antwort

18

Numpy verfügt über eine praktische Funktion mit diesem für den Umgang: np.clip. Trotz wie der Name klingen mag, entfernt es Werte nicht, es beschränkt sie nur auf den Bereich, den Sie angeben. Im Grunde macht es Artem's "Dirty Hack" inline. Sie können die Werte lassen, wie sie sind, aber in der hist Anruf, wickeln nur das Array in einem np.clip Anruf, wie so

plt.hist(np.clip(values_A, bins[0], bins[-1]), bins=bins) 

Dies ist für eine Reihe von Gründen ist schöner:

  1. Es ist Weg schneller - zumindest für eine große Anzahl von Elementen. Numpy arbeitet auf der C-Ebene. Das Arbeiten auf Python-Listen (wie in Artems Listenverständnis) hat viel Aufwand für jedes Element. Grundsätzlich, wenn Sie jemals die Möglichkeit haben, numpy zu verwenden, sollten Sie.

  2. Sie tun es genau dort, wo es gebraucht wird, was die Wahrscheinlichkeit von Fehlern in Ihrem Code verringert.

  3. Sie müssen keine zweite Kopie des Arrays herumhängen lassen, was die Speicherauslastung reduziert (außer innerhalb dieser einen Zeile) und die Fehlerwahrscheinlichkeit weiter reduziert.

  4. Mit bins[0], bins[-1] anstelle von Hard-Coding die Werte verringert die Chancen, wieder Fehler zu machen, weil Sie die Bins nur ändern können, wo bins definiert wurde; Sie müssen nicht daran denken, sie im Telefonat an clip oder anderswo zu ändern.

Also um es alle zusammen, wie im OP:

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 = 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 and will be replaced by density 
           bins=bins, color=['#3782CC', '#AFD5FA'], label=['A', 'B']) 

    xlabels = [str(b) for b in bins[1:]] 
    xlabels[-1] = '300+' 

    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() 

result of code above

4

Tut mir leid, Matplotlib kenne ich nicht. Also habe ich einen schmutzigen Hack für dich. Ich habe einfach alle Werte, die größer als 300 sind, in einen Behälter gelegt und die Behältergröße geändert.

Der Grund des Problems ist, dass Matplotlib versucht, alle Bins auf der Handlung zu setzen. In R würde ich meine Bins in eine Faktorvariable umwandeln, so dass sie nicht als reelle Zahlen behandelt werden.

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() 

enter image description here

+0

Das ist ein schmutziger Hack, aber es funktioniert! Ich akzeptiere diese Antwort, bis eine bessere Antwort kommt. – physicalattraction