2009-11-05 10 views
8

Come è possibile tracciare un array 2D come un'immagine con Matplotlib con la scala y relativa alla potenza di due del valore y?Come stampare un'immagine con l'asse y non lineare con Matplotlib usando imshow?

Ad esempio la prima riga del mio array avrà un'altezza nell'immagine di 1, la seconda riga avrà un'altezza di 4, ecc. (Le unità sono irrilevanti) Non è semplice spiegare con le parole, quindi guarda questa immagine si prega di (che è il tipo di risultato che voglio):

alt text http://support.sas.com/rnd/app/da/new/802ce/iml/chap1/images/wavex1k.gif

Come si può vedere la prima riga è 2 volte più piccolo che quello superiore, e così via.

Per chi è interessato a questo che sto cercando di fare questo:

ho una abbastanza grande array (10, 700000) dei carri allegorici, che rappresenta la trasformata wavelet discreta coefficienti di un file audio. Sto provando a tracciare lo scalogramma usando quei coefficienti. Potrei copiare l'array x volte fino a ottenere la dimensione della riga dell'immagine desiderata ma la memoria non può contenere così tante informazioni ...

risposta

8

Hai provato a trasformare l'asse? Ad esempio:

ax = subplot(111) 
ax.yaxis.set_ticks([0, 2, 4, 8]) 
imshow(data) 

Ciò significa che ci deve essere lacune nei dati per le coordinate inesistenti, a meno che non ci sia un modo per fornire una funzione di trasformazione anziché solo elenchi (mai provato).

Edit:

Ammetto che era solo un vantaggio, non una soluzione completa. Ecco cosa intendevo in più dettagli.

Supponiamo di avere i dati in un array, a. È possibile utilizzare una trasformazione come questo:

class arr(object): 
    @staticmethod 
    def mylog2(x): 
     lx = 0 
     while x > 1: 
      x >>= 1 
      lx += 1 
     return lx 
    def __init__(self, array): 
     self.array = array 
    def __getitem__(self, index): 
     return self.array[arr.mylog2(index+1)] 
    def __len__(self): 
     return 1 << len(self.array) 

In sostanza si trasformerà la prima coordinata di un array o una lista con la funzione mylog2 (che è possibile trasformare come si desidera - è fatto in casa come una semplificazione di log2). Il vantaggio è che puoi riutilizzarlo per un'altra trasformazione se ne hai bisogno, e puoi facilmente controllarlo anche tu.

quindi mappare l'array a questo, che non fa una copia, ma un riferimento locale nell'istanza:

b = arr(a) 

Ora è possibile visualizzare, ad esempio:

ax = subplot(111) 
ax.yaxis.set_ticks([16, 8, 4, 2, 1, 0]) 
axis([-0.5, 4.5, 31.5, 0.5]) 
imshow(b, interpolation="nearest") 

Ecco un esempio (con una matrice contenente valori casuali):

alt text http://img691.imageshack.us/img691/8883/clipboard01f.png

+0

-1: selezionare quali segni di spunta appaiono sull'asse y non risolve la domanda originale. Con set_ticks(), l'asse y rimane lineare e imshow() disegna linearmente l'array. Il poster originale vuole "dimensioni variabili, pixel rettangolari". – EOL

+0

Ecco perché dico che i dati dovevano essere adattati, vale ancora la pena menzionarli per gli assi in sé. – RedGlyph

+0

@EOL: ... e ora anche con la trama corretta. Capisco cosa intendi, ma a volte mettere insieme i pezzi aiuta ;-) – RedGlyph

3

Puoi guardare matplotlib.image.NonUniformImage. Ma questo aiuta solo ad avere assi non uniformi - Non penso che sarai in grado di tracciare in modo adattivo come vuoi (penso che ogni punto dell'immagine avrà sempre la stessa area - quindi stai andando a avere più righe più volte). C'è qualche ragione per cui è necessario tracciare l'intero array? Ovviamente il dettaglio completo non verrà mostrato in nessuna trama - quindi suggerirei di eseguire il downsampling della matrice originale in modo da poter copiare le righe come richiesto per ottenere l'immagine senza esaurire la memoria.

+0

Sì, ho pensato di eseguire il downsampling ma occasionalmente devo eseguire uno zoom piuttosto profondo sugli ultimi coefficienti dei dettagli e la perdita di precisione è spesso troppo alta. Guarderò comunque a NonUniformImage, grazie per il suggerimento non ne ero a conoscenza. – attwad

+0

Penso che questo potrebbe funzionare per disegnare l'immagine che vuole il poster originale e che ciò sarebbe più efficace della duplicazione di "pixel" attraverso una duplicazione di righe. Ottenere le zecche degli assi corrispondenti è ancora una domanda aperta, però. – EOL

2

Se si desidera sia poter zoomare e salvare memoria, è possibile eseguire il disegno "a mano". Matplotlib permette di disegnare rettangoli (che sarebbero i tuoi "pixel rettangolari"):

from matplotlib import patches 
axes = subplot(111) 
axes.add_patch(patches.Rectangle((0.2, 0.2), 0.5, 0.5)) 

Si noti che le estensioni degli assi non sono impostati per add_patch(), ma è possibile impostare voi stessi per i valori desiderati (axes.set_xlim, ...).

PS: Mi sembra che la risposta di thrope (matplotlib.image.NonUniformImage) possa effettivamente fare ciò che vuoi, in un modo più semplice che il metodo "manuale" qui descritto!

+0

+1 per un'alternativa efficiente, anche se sembra un po 'più di lavoro velocizzerebbe il rendering (come discusso in altre soluzioni). – RedGlyph

+0

@RedGlyph Grazie! Direi che la soluzione del thrope disegna rettangoli come questo (con l'opzione neighbor più vicina), ma direttamente attraverso Matplotlib. – EOL

3

Il modo migliore che ho trovato per realizzare uno scalogramma usando matplotlib è utilizzare imshow, simile all'implementazione di specgram. L'uso dei rettangoli è lento, perché devi creare un glifo separato per ogni valore. Allo stesso modo, non si vuole fare le cose in un array NumPy uniforme, perché probabilmente si esaurirà la memoria velocemente, dal momento che il livello più alto sarà pari a metà del segnale.

Ecco un esempio utilizzando SciPy e PyWavelets:

from pylab import * 
import pywt 
import scipy.io.wavfile as wavfile 

# Find the highest power of two less than or equal to the input. 
def lepow2(x): 
    return 2 ** floor(log2(x)) 

# Make a scalogram given an MRA tree. 
def scalogram(data): 
    bottom = 0 

    vmin = min(map(lambda x: min(abs(x)), data)) 
    vmax = max(map(lambda x: max(abs(x)), data)) 

    gca().set_autoscale_on(False) 

    for row in range(0, len(data)): 
     scale = 2.0 ** (row - len(data)) 

     imshow(
      array([abs(data[row])]), 
      interpolation = 'nearest', 
      vmin = vmin, 
      vmax = vmax, 
      extent = [0, 1, bottom, bottom + scale]) 

     bottom += scale 

# Load the signal, take the first channel, limit length to a power of 2 for simplicity. 
rate, signal = wavfile.read('kitten.wav') 
signal = signal[0:lepow2(len(signal)),0] 
tree = pywt.wavedec(signal, 'db5') 

# Plotting. 
gray() 
scalogram(tree) 
show() 

Si consiglia inoltre di scalare valori adattivo per ogni livello.

Questo funziona abbastanza bene per me. L'unico problema che ho è che matplotlib crea uno spazio sottile tra i livelli. Sto ancora cercando un modo per risolvere questo problema.

P.S. - Anche se questa domanda è piuttosto vecchia ora, ho pensato di rispondere qui, perché questa pagina si avvicinò su Google quando stavo cercando un metodo per creare scalogrammi usando MPL.

Problemi correlati