2010-05-06 3 views
29

Je voudrais rouler un numpy 2D en python, sauf que je voudrais tamponner les extrémités avec des zéros plutôt que de rouler les données comme si elles étaient périodiques.python numpy roll avec remplissage

Plus précisément, le code suivant

import numpy as np 

x = np.array([[1, 2, 3], [4, 5, 6]]) 

np.roll(x, 1, axis=1) 

retours

array([[3, 1, 2],[6, 4, 5]]) 

mais ce que je préfère est

array([[0, 1, 2], [0, 4, 5]]) 

je pourrais le faire avec quelques retouches maladroites, mais je J'espère qu'il y a un moyen de le faire avec des commandes intégrées rapides.

Merci

Répondre

24

Il y a une nouvelle fonction numpy dans la version 1.7.0 numpy.pad que peut faire en une seule ligne. Pad semble être assez puissant et peut faire beaucoup plus qu'un simple "roll". Le tuple ((0,0),(1,0)) utilisé dans cette réponse indique le "côté" de la matrice qui doit être tamponné.

import numpy as np 
x = np.array([[1, 2, 3],[4, 5, 6]]) 

print np.pad(x,((0,0),(1,0)), mode='constant')[:, :-1] 

Donner

[[0 1 2] 
[0 4 5]] 
+2

Si ce n'est pas évident, voici 5 éléments: print np.pad (x, ((0,0), (5,0)), mode = 'constante') [:,: -5] –

+0

Wow, Super trouvaille pour un poste de 4 ans! –

14

Je ne pense pas que vous allez trouver un moyen plus facile de le faire qui est intégré. La retouche me semble assez simple à:

y = np.roll(x,1,axis=1) 
y[:,0] = 0 

Si vous voulez que ce soit plus direct, alors vous pourriez peut-être copier la fonction de rouleau à une nouvelle fonction et le changer pour faire ce que vous voulez. La fonction roll() se trouve dans le fichier site-packages\core\numeric.py.

+1

J'espérais faire cela en une ligne, puisque je dois faire plusieurs fois dans des directions différentes et je ne peux pas clobber y, mais votre suggestion est probablement la meilleure solution. Merci de votre aide. –

4

Je viens d'écrire ce qui suit. Il pourrait être plus optimisé en évitant zeros_like et en calculant simplement la forme pour zeros directement.

import numpy as np 
def roll_zeropad(a, shift, axis=None): 
    """ 
    Roll array elements along a given axis. 

    Elements off the end of the array are treated as zeros. 

    Parameters 
    ---------- 
    a : array_like 
     Input array. 
    shift : int 
     The number of places by which elements are shifted. 
    axis : int, optional 
     The axis along which elements are shifted. By default, the array 
     is flattened before shifting, after which the original 
     shape is restored. 

    Returns 
    ------- 
    res : ndarray 
     Output array, with the same shape as `a`. 

    See Also 
    -------- 
    roll  : Elements that roll off one end come back on the other. 
    rollaxis : Roll the specified axis backwards, until it lies in a 
       given position. 

    Examples 
    -------- 
    >>> x = np.arange(10) 
    >>> roll_zeropad(x, 2) 
    array([0, 0, 0, 1, 2, 3, 4, 5, 6, 7]) 
    >>> roll_zeropad(x, -2) 
    array([2, 3, 4, 5, 6, 7, 8, 9, 0, 0]) 

    >>> x2 = np.reshape(x, (2,5)) 
    >>> x2 
    array([[0, 1, 2, 3, 4], 
      [5, 6, 7, 8, 9]]) 
    >>> roll_zeropad(x2, 1) 
    array([[0, 0, 1, 2, 3], 
      [4, 5, 6, 7, 8]]) 
    >>> roll_zeropad(x2, -2) 
    array([[2, 3, 4, 5, 6], 
      [7, 8, 9, 0, 0]]) 
    >>> roll_zeropad(x2, 1, axis=0) 
    array([[0, 0, 0, 0, 0], 
      [0, 1, 2, 3, 4]]) 
    >>> roll_zeropad(x2, -1, axis=0) 
    array([[5, 6, 7, 8, 9], 
      [0, 0, 0, 0, 0]]) 
    >>> roll_zeropad(x2, 1, axis=1) 
    array([[0, 0, 1, 2, 3], 
      [0, 5, 6, 7, 8]]) 
    >>> roll_zeropad(x2, -2, axis=1) 
    array([[2, 3, 4, 0, 0], 
      [7, 8, 9, 0, 0]]) 

    >>> roll_zeropad(x2, 50) 
    array([[0, 0, 0, 0, 0], 
      [0, 0, 0, 0, 0]]) 
    >>> roll_zeropad(x2, -50) 
    array([[0, 0, 0, 0, 0], 
      [0, 0, 0, 0, 0]]) 
    >>> roll_zeropad(x2, 0) 
    array([[0, 1, 2, 3, 4], 
      [5, 6, 7, 8, 9]]) 

    """ 
    a = np.asanyarray(a) 
    if shift == 0: return a 
    if axis is None: 
     n = a.size 
     reshape = True 
    else: 
     n = a.shape[axis] 
     reshape = False 
    if np.abs(shift) > n: 
     res = np.zeros_like(a) 
    elif shift < 0: 
     shift += n 
     zeros = np.zeros_like(a.take(np.arange(n-shift), axis)) 
     res = np.concatenate((a.take(np.arange(n-shift,n), axis), zeros), axis) 
    else: 
     zeros = np.zeros_like(a.take(np.arange(n-shift,n), axis)) 
     res = np.concatenate((zeros, a.take(np.arange(n-shift), axis)), axis) 
    if reshape: 
     return res.reshape(a.shape) 
    else: 
     return res 
+0

Merci, on dirait que ça pourrait être utile. Cependant, je joue un peu avec votre suggestion, et il semble être plus lent que la suggestion originale de Justin d'environ un facteur de deux (1.8sec vs 0.8sec sur un tableau aléatoire (1e4 x 1e4), selon cProfile). Il semble que les appels de concaténation entraînent le double temps d'exécution. –

0

Vous pouvez également utiliser le triu de numpy et le circulant de scipy.linalg. Faites une version circulante de votre matrice. Ensuite, sélectionnez la partie triangulaire supérieure en commençant à la première diagonale (l'option par défaut en triu). L'index de ligne correspond au nombre de zéros remplis que vous voulez. Si vous n'avez pas scipy, vous pouvez générer une matrice circulante nXn en créant une matrice d'identité (n-1) X (n-1) et en empilant une ligne [0 0 ... 1] par-dessus et la colonne [1 0 ... 0] à droite de celui-ci.

2

Un peu en retard, mais vous avez l'impression d'être un moyen rapide de faire ce que vous voulez en une ligne. Peut-être serait le mieux si enveloppé dans une fonction intelligente (exemple ci-dessous prévu juste pour axe horizontal):

import numpy 

a = numpy.arange(1,10).reshape(3,3) # an example 2D array 

print a 

[[1 2 3] 
[4 5 6] 
[7 8 9]] 

shift = 1 
a = numpy.hstack((numpy.zeros((a.shape[0], shift)), a[:,:-shift])) 

print a 

[[0 1 2] 
[0 4 5] 
[0 7 8]] 
+0

Un mot d'avertissement: cela ne fonctionne pas avec 'shift = 0', à cause de' a [:,: - shift] '. Cela peut être important si la procédure de changement de vitesse est placée dans une fonction générale. – EOL

1
import numpy as np 

def shift_2d_replace(data, dx, dy, constant=False): 
    """ 
    Shifts the array in two dimensions while setting rolled values to constant 
    :param data: The 2d numpy array to be shifted 
    :param dx: The shift in x 
    :param dy: The shift in y 
    :param constant: The constant to replace rolled values with 
    :return: The shifted array with "constant" where roll occurs 
    """ 
    shifted_data = np.roll(data, dx, axis=1) 
    if dx < 0: 
     shifted_data[:, dx:] = constant 
    elif dx > 0: 
     shifted_data[:, 0:np.abs(dx)] = constant 

    shifted_data = np.roll(shifted_data, dy, axis=0) 
    if dy < 0: 
     shifted_data[dy:, :] = constant 
    elif dy > 0: 
     shifted_data[0:np.abs(dy), :] = constant 
    return shifted_data 

Cette fonction fonctionnerait sur les tableaux 2D et remplacer les valeurs laminées avec une constante de votre choix.

0

Élaborant sur la réponse par Hooked (car il m'a fallu quelques minutes pour comprendre)

Le code ci-dessous des premières plages d'une certaine quantité de zéros dans le haut, le bas, les marges gauche et droite et sélectionne alors la matrice originale à l'intérieur du rembourré. Un code parfaitement inutile, mais bon pour la compréhension np.pad.

import numpy as np 
x = np.array([[1, 2, 3],[4, 5, 6]]) 
y = np.pad(x,((1,3),(2,4)), mode='constant')[1:-3,2:-4] 

print np.all(x==y) 

maintenant pour faire un changement plus de 2 combiné avec un décalage vers la droite de 1 position que l'on doit faire

print np.pad(x,((0,2),(1,0)), mode='constant')[2:0,0:-1]