236 votes

"Clonage" de vecteurs lignes ou colonnes

Il est parfois utile de "cloner" un vecteur ligne ou colonne en une matrice. Par clonage, j'entends la conversion d'un vecteur ligne tel que

[1, 2, 3]

Dans une matrice

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

ou un vecteur colonne tel que

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

en

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

Dans MATLAB ou octave, cela se fait assez facilement :

 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

Je veux répéter cela en numpy, mais sans succès

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

Pourquoi la première méthode ( In [16] ) fonctionne ? Existe-t-il un moyen de réaliser cette tâche en python de manière plus élégante ?

7 votes

Dans Matlab, notez qu'il est beaucoup plus rapide d'utiliser repmat : repmat([1 2 3],3,1) ou repmat([1 2 3].',1,3)

0 votes

Octave a également repmat .

0 votes

Pour ceux qui souhaitent faire la même chose avec un dataframe pandas, consultez la page tile_df lien ici

423voto

pv. Points 9935

Utilisation numpy.tile :

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

ou pour les colonnes répétitives :

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

28 votes

Upvote ! Sur mon système, pour un vecteur de 10000 éléments répété 1000 fois, la fonction tile est 19,5 fois plus rapide que la méthode de la réponse actuellement acceptée (utilisant la méthode de l'opérateur de multiplication).

1 votes

Dans la deuxième section ("répétition des colonnes"), pouvez-vous expliquer ce que fait la deuxième série de crochets, c'est-à-dire [[1,2,3]] ?

0 votes

@Ant il fait un tableau 2D de longueur 1 sur le premier axe (vertical sur votre écran) et de longueur 3 sur le deuxième axe (horizontal sur votre écran). En transposant, on obtient un tableau de longueur 3 sur le premier axe et de longueur 1 sur le deuxième axe. Une forme de tuile de (1, 3) recopie cette colonne trois fois, ce qui explique que les lignes du résultat contiennent chacune un seul élément distinct.

111voto

Peter Points 38320

Voici une manière élégante et pythonique de le faire :

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

le problème de la [16] Il semble que la transposition n'ait pas d'effet pour un tableau. Vous voulez probablement une matrice à la place :

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

(transpose fonctionne pour les tableaux 2D, par exemple pour le tableau carré de l'exemple, ou lorsqu'il est transformé en un tableau (N,1) -Tableau de formes en utilisant .reshape(-1, 1) )

44 votes

C'est très inefficace. Utiliser numpy.tile comme indiqué dans Réponse de pv. .

0 votes

Comme le dit Peter, il s'agit d'une méthode pythonique, qui favorise les lisibilité sur l'efficacité. Si la vitesse est si importante, je me demande pourquoi nous utilisons Python.

61voto

tom10 Points 19886

Notez tout d'abord qu'avec la fonction diffusion il n'est généralement pas nécessaire de dupliquer les lignes et les colonnes. Voir aussi cette y cette pour les descriptions.

Mais pour ce faire, répéter y nouvel axe sont probablement le meilleur moyen

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

Cet exemple concerne un vecteur de lignes, mais l'application à un vecteur de colonnes est, je l'espère, évidente. repeat semble l'épeler correctement, mais vous pouvez également le faire par multiplication, comme dans votre exemple.

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

7 votes

Newaxis présente l'avantage supplémentaire de ne pas copier les données tant que cela n'est pas nécessaire. Ainsi, si vous faites cela pour multiplier ou ajouter à un autre tableau 3x3, la répétition n'est pas nécessaire. Lisez un peu sur la diffusion numpy pour vous faire une idée.

0 votes

@AFoglia - Bon point. J'ai mis à jour ma réponse pour le signaler.

3 votes

Quels sont les avantages de l'utilisation np.repeat vs np.tile ?

18voto

muntoo Points 4905

Laissez :

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

Allocations à coût zéro

A vue ne nécessite pas de mémoire supplémentaire. Ces déclarations sont donc instantanées :

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

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

Allocation forcée

Si vous voulez forcer le contenu à résider dans la mémoire :

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

Les trois méthodes ont à peu près la même vitesse.

Calcul

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

Les trois méthodes ont à peu près la même vitesse.


Conclusion

Si vous souhaitez répliquer avant un calcul, envisagez d'utiliser l'une des méthodes d'"allocation à coût zéro". Vous ne subirez pas la pénalité de performance de l'"allocation forcée".

9voto

smartkevin Points 13

Je pense que l'utilisation de la diffusion dans numpy est la meilleure, et la plus rapide

J'ai effectué une comparaison comme suit

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

environ 15 fois plus rapide en utilisant la diffusion

0 votes

Vous pouvez indexer avec None de faire la même chose.

0 votes

Qu'est-ce que newaxis ?

0 votes

Np.newaxis est un alias de None (aucun)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X