Vectores de fila o columna de "clonación"


155

A veces es útil "clonar" un vector de fila o columna a una matriz. Al clonar me refiero a convertir un vector de fila como

[1,2,3]

En una matriz

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

o un vector de columna como

[1
 2
 3
]

dentro

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

En matlab u octava, esto se hace con bastante facilidad:

 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

Quiero repetir esto en numpy, pero sin éxito

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.]])

¿Por qué no funcionaba el primer método ( In [16])? ¿Hay alguna manera de lograr esta tarea en Python de una manera más elegante?


66
En Matlab, tenga en cuenta que es mucho más rápido de usar repmat: repmat([1 2 3],3,1)orepmat([1 2 3].',1,3)
Luis Mendo

Octave también tiene repmat.
ma11hew28

Para aquellos que buscan hacer algo similar con un marco de datos de pandas, consulte el tile_df
enlace

Respuestas:


80

Aquí hay una forma elegante y pitónica de hacerlo:

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

El problema con [16]parece ser que la transposición no tiene ningún efecto para una matriz. probablemente quieras una matriz en su lugar:

>>> 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
(la transposición funciona para matrices 2D, por ejemplo, para la cuadrada en el ejemplo, o cuando se convierte en una (N,1)matriz de forma usando .reshape(-1, 1))
Marque

34
Esto es altamente ineficiente. Use numpy.tilecomo se muestra en la respuesta de pv .
David Heffernan

304

Uso numpy.tile:

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

o para repetir columnas:

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

16
¡Votación a favor! En mi sistema, para un vector con 10000 elementos repetidos 1000 veces, el tilemétodo es 19.5 veces más rápido que el método en la respuesta actualmente aceptada (usando el método del operador de multiplicación).
Dr. Jan-Philip Gehrcke

1
En la segunda sección ("columnas repetitivas"), ¿puede explicar lo que hace el segundo conjunto de corchetes, es decir, [[1,2,3]]
Ant

@Ant se convierte en una matriz 2D con longitud 1 en el primer eje (vertical en su pantalla) y longitud 3 en el segundo eje (horizontal en su pantalla). La transposición hace que tenga una longitud 3 en el primer eje y una longitud 1 en el segundo eje. Una forma de mosaico (1, 3)copia tres veces esta columna, por lo que las filas del resultado contienen un único elemento distinto cada una.
BallpointBen

Esta debería ser la respuesta aceptada, ya que puede pasar cualquier vector ya inicializado, mientras que el aceptado solo puede funcionar si agrega la coma mientras inicializa el vector. Gracias !
Yohan Obadia

No puedo hacer que esto funcione para una solución 2d a 3d :(
john ktejik

42

Primero tenga en cuenta que con las operaciones de transmisión de numpy generalmente no es necesario duplicar filas y columnas. Vea esto y esto para descripciones.

Pero para hacer esto, repetir y newaxis son probablemente la mejor manera

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

Este ejemplo es para un vector de fila, pero es de esperar que aplicar esto a un vector de columna sea obvio. la repetición parece deletrear esto bien, pero también puedes hacerlo mediante la multiplicación como en tu ejemplo

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.]])

55
newaxis tiene el beneficio adicional de que en realidad no copia los datos hasta que sea necesario. Entonces, si está haciendo esto para multiplicar o agregar a otra matriz 3x3, la repetición es innecesaria. Lea sobre transmisiones numpy para tener la idea.
AFoglia

@AFoglia - Buen punto. Actualicé mi respuesta para señalar esto.
tom10

1
¿Qué beneficios de usar np.repeatvs np.tile?
mrgloom

@mrgloom: Ninguno, principalmente, para este caso. Para una pequeña matriz 1D, son similares y no hay una diferencia / beneficio / ventaja / etc significativa. Personalmente, considero que la simetría entre la clonación de filas y columnas es más intuitiva, y no me gusta la transposición necesaria para el mosaico, pero es solo cuestión de gustos. La respuesta de Mateen Ulhaq también dice que la repetición es más rápida, pero esto puede depender del caso de uso exacto que se considere, aunque la repetición está mucho más cerca de la funcionalidad C, por lo que probablemente seguirá siendo algo más rápido. En 2D tienen comportamientos diferentes, por lo que importa allí.
tom10

12

Dejar:

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

Asignaciones de costo cero

Una vista no toma memoria adicional. Por lo tanto, estas declaraciones son instantáneas:

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

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

Asignación forzada

Si desea forzar los contenidos a residir en la 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)

Los tres métodos tienen aproximadamente la misma velocidad.

Cálculo

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

Los tres métodos tienen aproximadamente la misma velocidad.


Conclusión

Si desea replicar antes de un cálculo, considere usar uno de los métodos de "asignación de costo cero". No sufrirá la penalización de rendimiento de la "asignación forzada".


8

Creo que usar la transmisión en numpy es lo mejor y más rápido

Hice una comparación de la siguiente manera

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

aproximadamente 15 veces más rápido usando la transmisión


Puede indexar Nonepara hacer lo mismo.
DanielSank

¿Qué es newaxis?
dreab

np.newaxis es un alias para None
john ktejik

la repetición fue más rápida: 5.56 ms = 5560 µs
Augusto Fadel

4

Una solución limpia es usar la función de producto externo de NumPy con un vector de unos:

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

da nfilas repetidas. Cambie el orden de los argumentos para obtener columnas repetidas. Para obtener el mismo número de filas y columnas, puede hacer

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

3

Puedes usar

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

el mosaico generará las repeticiones del vector

y remodelar le dará la forma que deseas


1

Si tiene un marco de datos de pandas y desea preservar los tipos, incluso los categóricos, esta es una forma rápida de hacerlo:

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)

rendimientos:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.