2010-05-06 62 views
29

Me gustaría rodar un numpy 2D en python, excepto que me gustaría rellenar los extremos con ceros en lugar de pasar los datos como si fueran periódicos.python numpy roll con relleno

En concreto, el siguiente código

import numpy as np 

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

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

vuelve

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

pero lo que yo prefiero es

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

que podía hacer esto con unos pocos retoques incómodas, pero yo' Estoy esperando que haya una manera de hacerlo con comandos rápidos incorporados.

Gracias

Respuesta

24

hay una nueva función numpy en la versión 1.7.0 numpy.pad que puede hacer esto en una línea. Pad parece ser bastante poderoso y puede hacer mucho más que un simple "roll". La tupla ((0,0),(1,0)) utilizada en esta respuesta indica el "lado" de la matriz que se debe rellenar.

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

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

Dar

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

Si no es obvio, aquí está cambiando 5 elementos: print np.pad (x, ((0,0), (5,0)), mode = 'constant') [:: -5] –

+0

Wow, ¡gran hallazgo para una publicación de 4 años! –

14

No creo que se va a encontrar una manera más fácil de hacer esto que está incorporado. El retoque parece bastante simple para mí:

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

Si desea que esta sea más directo, entonces tal vez usted podría copiar la función rollo para una nueva función y modificarlo para que haga lo que quiera. La función roll() está en el archivo site-packages\core\numeric.py.

+1

Tenía la esperanza de hacer esto en una sola línea, ya que tengo que hacer esto varias veces en diferentes direcciones y no puedo truncar, pero tu sugerencia es probablemente la mejor solución. Gracias por tu ayuda. –

4

Acabo de escribir lo siguiente. Podría ser más optimizado evitando zeros_like y simplemente calculando la forma para zeros directamente.

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

Gracias, parece que podría ser útil. Sin embargo, estoy jugando un poco con tu sugerencia, y parece ser más lenta que la sugerencia original de Justin en un factor de dos (1.8seg frente a 0.8seg en una matriz aleatoria (1e4 x 1e4), según cProfile). Parece que las llamadas de concatenación están causando el doble tiempo de ejecución. –

0

También puede usar numpy's triu y scipy.linalg's circulant. Haz una versión circulante de tu matriz. Luego, selecciona la parte triangular superior que comienza en la primera diagonal (la opción predeterminada en triu). El índice de fila corresponderá al número de ceros rellenos que desee.

Si no tiene scipy puede generar una matriz circulante nXn haciendo una matriz de identidad (n-1) X (n-1) y apilando una fila [0 0 ... 1] encima de ella y la columna [1 0 ... 0] a la derecha de ella.

2

Un poco tarde, pero se siente como una manera rápida de hacer lo que quiere en una línea. Tal vez sería mejor si está envuelto dentro de una función inteligente (ejemplo proporcionado a continuación sólo para eje 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

Una palabra de advertencia: esto no funciona con 'shift = 0', debido a' a [:,: - shift] '. Esto puede importar si el procedimiento de cambio se pone en una función general. – 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 

Esta función sería trabajar en matrices 2D y reemplazar los valores laminados con una constante de su elección.

0

Abundando en la respuesta por Enganchado (ya que me tomó unos minutos para entenderlo)

El código de abajo primeras pastillas de una cierta cantidad de ceros en el arriba, abajo, los márgenes izquierdo y derecho y luego selecciona el matriz original dentro del acolchado. Un código perfectamente inútil, pero bueno para entender 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) 

ahora para hacer un turno de más de 2 combinado con un giro a la derecha de 1 posición uno debe hacer

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