Skip to content

Appiattire un array NumPy con ravel() e flatten()

Python

Puoi applicare un array NumPy ndarray con la funzione numpy.label() oi metodi ravel() e flatten() di numpy.ndarray.

In questo articolo vengono descritti i seguenti contenuti.

  • Appiattisci un array NumPy con numpy.ravel()
  • Appiattisci un array NumPy con ndarray.ravel()
  • Appiattisci un array NumPy con ndarray.flatten()
  • Appiattisci un array NumPy con reshape(-1)
  • Differenza tra ravel() e flatten()
  • Confronto di velocità trascorre ravel() e flatten()
  • Il parametro dell’ordine
  • Per array multidimensionali

Vedere il seguente articolo sull’appiattimento di elenchi multidimensionali (tipo di elenco integrato di Python).

Appiattisci un array NumPy con numpy.ravel()

Puoi applicare un array NumPy con la funzione numpy.label(). Viene restituito un numpy.ndarray appiattito.

import numpy as np

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(np.ravel(a))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(type(np.ravel(a)))
# <class 'numpy.ndarray'>

Puoi specificare un cosiddetto oggetto similitudine a un array, come l’elenco integrato di Python. In tal caso, inoltre, viene restituito numpy.ndarray.

print(np.ravel([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(type(np.ravel([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])))
# <class 'numpy.ndarray'>

Si noti che se il numero di elementi nell’elenco interno è diverso, viene restituito numpy.ndarray con l’elenco come elementi.

print(np.ravel([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]))
# [list([0, 1, 2, 3]) list([4, 5, 6, 7]) list([8, 9])]

print(type(np.ravel([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]])))
# <class 'numpy.ndarray'>

Se vuoi appiattare un elenco di questo tipo, consulta il seguente articolo.

Appiattisci un array NumPy con ndarray.ravel()

ravel() viene fornito anche come metodo di numpy.ndarray.

print(a.ravel())
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

Appiattisci un array NumPy con ndarray.flatten()

flatten() viene fornito anche come metodo di numpy.ndarray.

print(a.flatten())
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

ravel() fornisce una vista se possibile, ma flatten() richiede sempre una copia. flatten() è più lento di ravel() perché deve allocare memoria. Vedi sotto per i dettagli.

Nota che a partire dalla versione 1.17, flatten() è fornito solo come metodo di numpy.ndarray, non come una funzione come numpy.flatten() (np.flatten()).

Appiattisci un array NumPy con reshape(-1)

Utilizzare reshape() per utilizzare anche la forma di un array NumPy in una dimensione. Se usi -1, la dimensione viene calcolata automaticamente, quindi puoi appiattare un array NumPy con reshape(-1).

reshape() viene fornito come metodo di numpy.ndarray.

print(a.reshape(-1))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

Viene fornito anche la funzione numpy.reshape(). numpy.reshape() può gestire oggetti simili a array come elenchi e numpy.reval().

print(np.reshape(a, -1))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(np.reshape([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], -1))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(np.reshape([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]], -1))
# [list([0, 1, 2, 3]) list([4, 5, 6, 7]) list([8, 9])]

Differenza tra ravel() e flatten()

ravel() e reshape() restituiscono una vista se possibile, mentre flatten() richiede sempre una copia.

Vedere l’articolo seguente per ulteriori informazioni sulle recensioni e sulle copie di numpy.ndarray.

print(np.shares_memory(a, a.ravel()))
# True

print(np.shares_memory(a, np.ravel(a)))
# True

print(np.shares_memory(a, a.flatten()))
# False

print(np.shares_memory(a, a.reshape(-1)))
# True

print(np.shares_memory(a, np.reshape(a, -1)))
# True

Nel caso di una vista, condividi la memoria con l’originale numpy.ndarray, quindi se modifichi il valore di uno, cambierà anche il valore dell’altro.

a_ravel = a.ravel()
print(a_ravel)
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

a_ravel[0] = 100
print(a_ravel)
# [100   1   2   3   4   5   6   7   8   9  10  11]

print(a)
# [[100   1   2   3]
#  [  4   5   6   7]
#  [  8   9  10  11]]

Nel caso di una copia, la memoria viene allocata per ciascuno, quindi vengono elaborati separatamente l’uno dall’altro.

ravel() e reshape() restituiscono viste se possibile, ma in alcuni casi restituiscono copia.

Ad esempio, ravel() e reshape() restituiscono una copia anche se la fetta con un passaggio specificato è appiattita e il passo in memoria non è costante.

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(a[:, ::3])
# [[ 0  3]
#  [ 4  7]
#  [ 8 11]]

print(np.shares_memory(a[:, ::3], np.ravel(a[:, ::3])))
# False

print(np.shares_memory(a[:, ::3], np.reshape(a[:, ::3], -1)))
# False

In alcuni casi, ravel() e reshape() danno risultati diversi. Se stride è costante anche per una fetta con un passo, reshape() richiede una vista, ma ravel() richiede una copia.

print(a[:, ::2])
# [[ 0  2]
#  [ 4  6]
#  [ 8 10]]

print(np.shares_memory(a[:, ::2], np.ravel(a[:, ::2])))
# False

print(np.shares_memory(a[:, ::2], np.reshape(a[:, ::2], -1)))
# True

Come affermano le note nella documentazione ufficiale, reshape(-1) è preferibile a ravel() se si desidera una vista nel maggior numero possibile di casi.

Quando si desidera una vista nel maggior numero possibile di casi, può essere preferibile arr.reshape(-1).
numpy.ravel — Manuale di NumPy v1.17

Confronto di velocità tra ravel() e flatten().

flatten(), che richiede una copia, è più lento di ravel() perché deve allocare nuova memoria.

Il seguente codice viene misurato utilizzando il comando magico %%timeit su Jupyter Notebook. Nota che non funziona su script Python.

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

%%timeit
a.ravel()
# 242 ns ± 2.78 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
a.flatten()
# 725 ns ± 45.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
a.reshape(-1)
# 851 ns ± 13.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Per numpy.ndarray piccolo come l’esempio sopra, non devi preoccuparti, ma se la dimensione è grande, flatten() richiederà molto più tempo. Ovviamente, flatten() utilizza più memoria di ravel().

Se non ti dispiace usare le viste invece delle copie, è meglio usare ravel().

a_large = np.arange(1000000).reshape(100, 100, 100)

%%timeit
a_large.ravel()
# 242 ns ± 3.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%%timeit
a_large.flatten()
# 2.03 ms ± 8.63 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%%timeit
a_large.reshape(-1)
# 899 ns ± 52 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Nota che ravel() alloca anche nuova quando memoria restituisce una copia, quindi la sua velocità e l’utilizzo della memoria sono gli stessi di flatten().

Il parametro dell’ordine

Il parametro order può essere specificato per ravel(), flatten() e reshape().

L’impostazione predefinita è order=”C”, che si appattisce con una riga maggiore similitudine a C come negli esempi precedenti, ma se si imposta order=”F”, si appattisce con una colonna maggiore similitudine a Fortran.

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(a.ravel())
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(a.ravel('F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(np.ravel(a, 'F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(a.flatten('F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(a.reshape(-1, order='F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(np.reshape(a, -1, order='F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

l’ordine può essere ‘C’, ‘F’, ‘A’ per reshape() e ‘C’, ‘F’, ‘A’, ‘K’ per ravel() e flatten().

Vedere la documentazione ufficiale per i dettagli su ciascuno.

Si noti che le opzioni “C” e “F” non tengono conto del layout di memoria dell’array sottostante e si seguono solo all’ordine didicizzazione degli assi. ‘A’ significa leggere gli elementi nell’ordine dell’indice similitudine a Fortran se a è un Fortran contiguo in memoria, altrimenti in ordine similitudine a C. ‘K’ significa leggere gli elementi nell’ordine in cui si trovano in memoria, tranne che per invertire i dati quando i passi sono negativi. Per definizione predefinita, viene utilizzato l’ordine dell’indice “C”.
numpy.ravel — Manuale di NumPy v1.17

Le differenze per ordine sono mostrate di seguito, insieme alle informazioni su numpy.ndarray, che possono essere verificate con np.info().

Ad esempio, se fortran è True, i risultati di ‘A’ e ‘F’ sono uguali e se fortran è False, i risultati di ‘A’ e ‘C’ sono uguali.

np.info(a)
# class:  ndarray
# shape:  (3, 4)
# strides:  (32, 8)
# itemsize:  8
# aligned:  True
# contiguous:  True
# fortran:  False
# data pointer: 0x7fe081640f90
# byteorder:  little
# byteswap:  False
# type: int64

print(a.ravel('C'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(a.ravel('F'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(a.ravel('A'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(a.ravel('K'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

Quando trasposto con attributo T:

print(a.T)
# [[ 0  4  8]
#  [ 1  5  9]
#  [ 2  6 10]
#  [ 3  7 11]]

np.info(a.T)
# class:  ndarray
# shape:  (4, 3)
# strides:  (8, 32)
# itemsize:  8
# aligned:  True
# contiguous:  False
# fortran:  True
# data pointer: 0x7fe081640f90
# byteorder:  little
# byteswap:  False
# type: int64

print(a.T.ravel('C'))
# [ 0  4  8  1  5  9  2  6 10  3  7 11]

print(a.T.ravel('F'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(a.T.ravel('A'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

print(a.T.ravel('K'))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

Fetta con passaggio negativo:

print(a.T[::-1])
# [[ 3  7 11]
#  [ 2  6 10]
#  [ 1  5  9]
#  [ 0  4  8]]

np.info(a.T[::-1])
# class:  ndarray
# shape:  (4, 3)
# strides:  (-8, 32)
# itemsize:  8
# aligned:  True
# contiguous:  False
# fortran:  False
# data pointer: 0x7fe081640fa8
# byteorder:  little
# byteswap:  False
# type: int64

print(a.T[::-1].ravel('C'))
# [ 3  7 11  2  6 10  1  5  9  0  4  8]

print(a.T[::-1].ravel('F'))
# [ 3  2  1  0  7  6  5  4 11 10  9  8]

print(a.T[::-1].ravel('A'))
# [ 3  7 11  2  6 10  1  5  9  0  4  8]

print(a.T[::-1].ravel('K'))
# [ 3  2  1  0  7  6  5  4 11 10  9  8]

Per array multidimensionali

Gli esempi finora sono per array bidimensionali, ma gli array multidimensionali di tre o più dimensioni possono essere appattiti allo stesso modo.

a_3d = np.arange(24).reshape(2, 3, 4)
print(a_3d)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]
# 
#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

print(a_3d.ravel())
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]

print(a_3d.ravel('F'))
# [ 0 12  4 16  8 20  1 13  5 17  9 21  2 14  6 18 10 22  3 15  7 19 11 23]

Si noti che l’effetto dell’ordine è complicato nel caso di array multidimensionali, come mostrato nell’esempio sopra.