Vettori di riga o colonna di "clonazione"


155

A volte è utile "clonare" un vettore di riga o colonna su una matrice. Con clonazione intendo convertire un vettore di riga come

[1,2,3]

In una matrice

[[1,2,3]
 [1,2,3]
 [1,2,3]
]

o un vettore di colonna come

[1
 2
 3
]

in

[[1,1,1]
 [2,2,2]
 [3,3,3]
]

In matlab o ottava questo è fatto abbastanza facilmente:

 x = [1,2,3]
 a = ones(3,1) * x
 a =

    1   2   3
    1   2   3
    1   2   3

 b = (x') * ones(1,3)
 b =

    1   1   1
    2   2   2
    3   3   3

Voglio ripetere questo in maniera intorpidita, ma senza successo

In [14]: x = array([1,2,3])
In [14]: ones((3,1)) * x
Out[14]:
array([[ 1.,  2.,  3.],
       [ 1.,  2.,  3.],
       [ 1.,  2.,  3.]])
# so far so good
In [16]: x.transpose() * ones((1,3))
Out[16]: array([[ 1.,  2.,  3.]])
# DAMN
# I end up with 
In [17]: (ones((3,1)) * x).transpose()
Out[17]:
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

Perché il primo metodo ( In [16]) non funzionava? C'è un modo per realizzare questo compito in Python in un modo più elegante?


6
In Matlab, nota che è molto più veloce da usare repmat: repmat([1 2 3],3,1)oppurerepmat([1 2 3].',1,3)
Luis Mendo l'

Anche Octave ha repmat.
ma11hew28

Per coloro che cercano di fare lo stesso con un dataframe di panda, controlla il tile_df link qui
zelusp,

Risposte:


80

Ecco un modo elegante e pitone per farlo:

>>> array([[1,2,3],]*3)
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

>>> array([[1,2,3],]*3).transpose()
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

il problema [16]sembra essere che la trasposizione non ha alcun effetto per un array. probabilmente vorrai invece una matrice:

>>> x = array([1,2,3])
>>> x
array([1, 2, 3])
>>> x.transpose()
array([1, 2, 3])
>>> matrix([1,2,3])
matrix([[1, 2, 3]])
>>> matrix([1,2,3]).transpose()
matrix([[1],
        [2],
        [3]])

1
(trasporre le opere per le matrici 2D, ad esempio per quella quadrata nell'esempio, o quando si trasforma in un (N,1)array di forme usando .reshape(-1, 1))
Mark

34
Questo è altamente inefficiente. Usa numpy.tilecome mostrato nella risposta di pv .
David Heffernan,

304

Utilizzare numpy.tile:

>>> tile(array([1,2,3]), (3, 1))
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

o per ripetere le colonne:

>>> tile(array([[1,2,3]]).transpose(), (1, 3))
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

16
Upvote! Sul mio sistema, per un vettore con 10000 elementi ripetuti 1000 volte, il tilemetodo è 19,5 volte più veloce del metodo nella risposta attualmente accettata (usando il metodo dell'operatore di moltiplicazione).
Dr. Jan-Philip Gehrcke,

1
Nella seconda sezione ("colonne ripetute"), puoi spiegare cosa fa la seconda serie di parentesi quadre, ovvero [[1,2,3]]
Ant

@Ant si trasforma in un array 2D con lunghezza 1 nel primo asse (verticale sullo schermo) e lunghezza 3 nel secondo asse (orizzontale sullo schermo). La trasposizione rende quindi la lunghezza 3 nel primo asse e la lunghezza 1 nel secondo asse. Una forma a tessera (1, 3)copia questa colonna più di tre volte, motivo per cui le righe del risultato contengono ciascuna un singolo elemento distinto.
BallpointBen,

Questa dovrebbe essere la risposta accettata poiché puoi passare qualsiasi vettore già inizializzato mentre quello accettato può funzionare solo se aggiungi la virgola mentre inizializzi il vettore. Grazie !
Yohan Obadia,

Non riesco a farlo funzionare per una soluzione da 2d a 3d :(
john ktejik,

42

Prima nota che con le operazioni di trasmissione di numpy di solito non è necessario duplicare righe e colonne. Vedi questo e questo per le descrizioni.

Ma per fare questo, ripetere e newaxis sono probabilmente il modo migliore

In [12]: x = array([1,2,3])

In [13]: repeat(x[:,newaxis], 3, 1)
Out[13]: 
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

In [14]: repeat(x[newaxis,:], 3, 0)
Out[14]: 
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

Questo esempio è per un vettore di riga, ma si spera di applicarlo a un vettore di colonna. ripeti sembra scrivere bene, ma puoi anche farlo tramite la moltiplicazione come nel tuo esempio

In [15]: x = array([[1, 2, 3]])  # note the double brackets

In [16]: (ones((3,1))*x).transpose()
Out[16]: 
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

5
newaxis ha l'ulteriore vantaggio di non copiare i dati fino a quando non è necessario. Quindi, se lo stai facendo per moltiplicare o aggiungere a un altro array 3x3, la ripetizione non è necessaria. Leggi le trasmissioni intorpidite per avere l'idea.
AFoglia,

@AFoglia - Ottimo punto. Ho aggiornato la mia risposta per evidenziarlo.
tom10,

1
Quali sono i vantaggi dell'utilizzo di np.repeatvs np.tile?
mrgloom,

@mrgloom: nessuno, principalmente, per questo caso. Per un piccolo array 1D, sono simili e non c'è una differenza significativa / beneficio / vantaggio / ecc. Personalmente trovo che la simmetria tra la clonazione di riga e colonna sia più intuitiva e non mi piace la trasposizione necessaria per la piastrella, ma è solo una questione di gusti. La risposta di Mateen Ulhaq dice anche che la ripetizione è più veloce, ma ciò può dipendere dal caso d'uso esatto considerato, sebbene la ripetizione sia molto più vicina alla funzionalità C, quindi probabilmente rimarrà un po 'più veloce. In 2D hanno comportamenti diversi, quindi è importante lì.
tom10

12

Permettere:

>>> n = 1000
>>> x = np.arange(n)
>>> reps = 10000

Allocazioni a costo zero

Una vista non richiede memoria aggiuntiva. Pertanto, queste dichiarazioni sono istantanee:

# New axis
x[np.newaxis, ...]

# Broadcast to specific shape
np.broadcast_to(x, (reps, n))

Allocazione forzata

Se vuoi forzare il contenuto a risiedere in memoria:

>>> %timeit np.array(np.broadcast_to(x, (reps, n)))
10.2 ms ± 62.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.repeat(x[np.newaxis, :], reps, axis=0)
9.88 ms ± 52.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.tile(x, (reps, 1))
9.97 ms ± 77.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Tutti e tre i metodi hanno all'incirca la stessa velocità.

Calcolo

>>> a = np.arange(reps * n).reshape(reps, n)
>>> x_tiled = np.tile(x, (reps, 1))

>>> %timeit np.broadcast_to(x, (reps, n)) * a
17.1 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x[np.newaxis, :] * a
17.5 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x_tiled * a
17.6 ms ± 240 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Tutti e tre i metodi hanno all'incirca la stessa velocità.


Conclusione

Se si desidera replicare prima di un calcolo, considerare l'utilizzo di uno dei metodi di "allocazione a costo zero". Non subirai la penalità di "allocazione forzata".


8

Penso che usare la trasmissione in numpy sia il migliore e più veloce

Ho fatto un confronto come segue

import numpy as np
b = np.random.randn(1000)
In [105]: %timeit c = np.tile(b[:, newaxis], (1,100))
1000 loops, best of 3: 354 µs per loop

In [106]: %timeit c = np.repeat(b[:, newaxis], 100, axis=1)
1000 loops, best of 3: 347 µs per loop

In [107]: %timeit c = np.array([b,]*100).transpose()
100 loops, best of 3: 5.56 ms per loop

circa 15 volte più veloce usando la trasmissione


Puoi indicizzare con Noneper fare la stessa cosa.
DanielSank,

cos'è newaxis ?!
Dreab

np.newaxis è un alias per None
john ktejik,

la ripetizione è stata più rapida: 5,56 ms = 5560 µs
Augusto Fadel

4

Una soluzione pulita consiste nell'utilizzare la funzione del prodotto esterno di NumPy con un vettore di quelli:

np.outer(np.ones(n), x)

nrighe ripetute. Cambia l'ordine degli argomenti per ottenere colonne ripetute. Per ottenere lo stesso numero di righe e colonne che potresti fare

np.outer(np.ones_like(x), x)

3

Puoi usare

np.tile(x,3).reshape((4,3))

tile genererà le ripetizioni del vettore

e rimodellare gli darà la forma che desideri


1

Se hai un frame di dati Panda e vuoi preservare i tipi, anche i categorici, questo è un modo veloce per farlo:

import numpy as np
import pandas as pd
df = pd.DataFrame({1: [1, 2, 3], 2: [4, 5, 6]})
number_repeats = 50
new_df = df.reindex(np.tile(df.index, number_repeats))

-1
import numpy as np
x=np.array([1,2,3])
y=np.multiply(np.ones((len(x),len(x))),x).T
print(y)

rendimenti:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.