2014-10-06 9 views
12

Ho un array con valori e voglio creare un istogramma di esso. Sono principalmente interessato ai numeri di fascia bassa e voglio raccogliere ogni numero sopra 300 in un cestino. Questo cestino dovrebbe avere la stessa larghezza di tutti gli altri (ugualmente ampi) contenitori. Come posso fare questo?Istogramma Matplotlib con raccoglitore di raccolta per valori elevati

Nota: questa domanda è legata a questa domanda: Defining bin width/x-axis scale in Matplotlib histogram

Questo è quello che ho provato finora:

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

Questo è il risultato, che non sembra bello: enter image description here

Poi ho cambiato la riga con xlim:

plt.xlim([0, 325]) 

Con il seguente risultato: enter image description here

Sembra più o meno come lo voglio, ma l'ultimo bidone non è visibile oggi. Quale trucco mi manca per visualizzare questo ultimo cestino con una larghezza di 25?

risposta

18

Numpy ha una funzione utile per affrontare questo: np.clip. Nonostante quello che può sembrare il nome, non è rimuovere i valori, li limita semplicemente all'intervallo specificato. Fondamentalmente, è in linea "lo sporco trucco" di Artem. È possibile lasciare i valori così come sono, ma nella chiamata hist, basta avvolgere la matrice in un np.clip chiamata, in questo modo

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

Questo è più bello per una serie di ragioni:

  1. E ' modo più veloce - almeno per un numero elevato di elementi. Numpy fa il suo lavoro a livello C. Operare su liste python (come nella comprensione delle liste di Artem) ha un sacco di spese generali per ogni elemento. Fondamentalmente, se hai mai la possibilità di usare numpy, dovresti.

  2. Lo si fa esattamente dove è necessario, il che riduce la possibilità di commettere errori nel codice.

  3. Non è necessario tenere una seconda copia dell'array in giro, il che riduce l'utilizzo della memoria (tranne all'interno di questa linea) e riduce ulteriormente le possibilità di commettere errori.

  4. Utilizzando bins[0], bins[-1] invece di codifica hard, i valori riducono le possibilità di errori di nuovo, perché è possibile modificare gli scomparti proprio dove è stato definito bins; non è necessario ricordarsi di cambiarli nella chiamata a clip o altrove.

Quindi, per mettere tutto insieme, come nel 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).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

Scusa non ho dimestichezza con matplotlib. Quindi ho uno sporco trucco per te. Ho solo messo tutti i valori superiori a 300 in un cestino e cambiato la dimensione del cestino.

La radice del problema è che matplotlib tenta di mettere tutti i bin sulla trama. In R vorrei convertire i miei bidoni in variabile fattore, quindi non sono trattati come numeri reali.

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

Questo è un hack sporco, ma funziona! Accetto questa risposta finché non arriva una risposta migliore. – physicalattraction

Problemi correlati