2012-09-25 37 views
9

Tengo una gran matriz unidimensional de enteros que necesito para cortar. Eso es trivial, solo haría a[start:end]. El problema es que necesito más de estas rebanadas. a[start:end] no funciona si el inicio y el final son matrices. Para loop podría usarse para esto, pero necesito que sea lo más rápido posible (es un cuello de botella), por lo que una solución numpy nativa sería bienvenida.Cortar la matriz numpy con otra matriz

Para ilustrar aún más, tengo esto:

a = numpy.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], numpy.int16) 
start = numpy.array([1, 5, 7], numpy.int16) 
end = numpy.array([2, 10, 9], numpy.int16) 

y necesita hacer algún modo en esto:

[[1], [5, 6, 7, 8, 9], [7, 8]] 
+0

estoy teniendo dificultades para entender lo que '' start' y tienen end' para hacer con esto Por otro lado, no creo que puedas hacer esto completamente en numpy ya que las matrices numpy deben ser rectangulares. – mgilson

+0

Puede tratar de hacer que los valores de inicio como tuplas en una lista. – Keith

+0

Como parece que no hay una solución numérica canónica aquí, si necesita más ideas, tal vez desee agregar lo que realmente hace con ella después y si las rebanadas tienen algunas propiedades especiales. – seberg

Respuesta

1

No es una solución numpy "puro" (aunque, como @ de mgilson notas de comentario , es difícil ver cómo la salida irregular podría ser una matriz numpy), pero:

a = numpy.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], numpy.int16) 
start = numpy.array([1, 5, 7], numpy.int16) 
end = numpy.array([2, 10, 9], numpy.int16) 

map(lambda range: a[range[0]:range[1]],zip(start,end)) 

se obtiene:

[array([1], dtype=int16), array([5, 6, 7, 8, 9], dtype=int16), array([7, 8], dtype=int16)] 

según sea necesario.

5

No hay un método numpy para hacer esto. Tenga en cuenta que dado que es irregular, solo sería una lista de matrices/sectores de todos modos. Sin embargo, me gustaría añadir que para todos (binario) ufuncs que son casi todas las funciones en numpy (o al menos están basadas en ellas), existe el método reduceat, que podría ayudarlo a evitar la creación de una lista de sectores, y por lo tanto, si las rodajas son pequeñas, acelerar los cálculos también:

In [1]: a = np.arange(10) 

In [2]: np.add.reduceat(a, [0,4,7]) # add up 0:4, 4:7 and 7:end 
Out[2]: array([ 6, 15, 24]) 

In [3]: np.maximum.reduceat(a, [0,4,7]) # maximum of each of those slices 
Out[3]: array([3, 6, 9]) 

In [4]: w = np.asarray([0,4,7,10]) # 10 for the total length 

In [5]: np.add.reduceat(a, w[:-1]).astype(float)/np.diff(w) # equivalent to mean 
Out[5]: array([ 1.5, 5. , 8. ]) 

EDIT: Desde sus rebanadas se superponen, voy a añadir que esto está bien también:

# I assume that start is sorted for performance reasons. 
reductions = np.column_stack((start, end)).ravel() 
sums = np.add.reduceat(a, reductions)[::2] 

El [::2] debe haber grandes trate aquí normalmente, ya que no se realiza ningún trabajo adicional real para la superposición de sectores.

También hay un problema aquí con las divisiones para las cuales stop==len(a). Esto debe ser evitado. Si usted tiene exactamente una rebanada con él, sólo podía hacer reductions = reductions[:-1] (si es el último), pero de lo contrario sólo tendrá que añadir un valor a a para engañar reduceat:

a = np.concatenate((a, [0])) 

Como añadir un valor a el final no importa ya que trabajas en las rebanadas de todos modos.

7

Esto puede (¿casi?) Hacerse en pura numpy usando matrices enmascaradas y trucos de zancadas. En primer lugar, creamos nuestra máscara:

>>> indices = numpy.arange(a.size) 
>>> mask = ~((indices >= start[:,None]) & (indices < end[:,None])) 

O más simplemente:

>>> mask = (indices < start[:,None]) | (indices >= end[:,None]) 

La máscara se False (es decir, los valores no enmascarados) para aquellos índices que son >= al valor de inicio y < el valor final . (Cortar con None (también conocido como numpy.newaxis) agrega una nueva dimensión, que permite la transmisión.) Ahora, nuestra máscara se parece a esto:

>>> mask 
array([[ True, False, True, True, True, True, True, True, True, 
     True, True, True], 
     [ True, True, True, True, True, False, False, False, False, 
     False, True, True], 
     [ True, True, True, True, True, True, True, False, False, 
     True, True, True]], dtype=bool) 

Ahora hay que estirar la matriz para adaptarse a la máscara usando stride_tricks:

>>> as_strided = numpy.lib.stride_tricks.as_strided 
>>> strided = as_strided(a, mask.shape, (0, a.strides[0])) 
>>> strided 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 
     [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 
     [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], dtype=int16) 

Esto se ve como una matriz 3x12, pero cada uno de los puntos de fila al mismo memoria. Ahora podemos combinarlos en un conjunto de máscaras:

>>> numpy.ma.array(strided, mask=mask) 
masked_array(data = 
[[-- 1 -- -- -- -- -- -- -- -- -- --] 
[-- -- -- -- -- 5 6 7 8 9 -- --] 
[-- -- -- -- -- -- -- 7 8 -- -- --]], 
      mask = 
[[ True False True True True True True True True True True True] 
[ True True True True True False False False False False True True] 
[ True True True True True True True False False True True True]], 
     fill_value = 999999) 

Esto no es exactamente lo mismo que lo que pidieron, pero debe comportarse de manera similar.

+0

Idea genial, sería interesante saber si este enfoque funciona para su caso de uso (en versiones numpy más nuevas). El actual carece de la palabra clave 'where' para' ufunc's (1.7 tampoco lo tendrá para las reducciones). Lo que significa que tu matriz stride tricks se copiará en la versión completa, para casi cualquier cosa que hagas en él ... – seberg

+0

Mmh, la falta de 'where' en' ufunc' no tiene * nada que ver con el problema en cuestión, y 'np.ma' por lo general evita las copias ... No es realmente una cuestión de usar' np.ma' (buena idea en sí misma) lo que me molesta, es que probablemente no supere la construcción de diapositivas con un bucle o lista de comprensión (solo por duplicar el tamaño de la matriz) ... Aún así, es divertido, +1 –

+0

@PierreGM, sí, como que solo pensé en las funciones de reducción allí, pero en algún momento es probable que se deseen ... – seberg

0

Solución similar como timday. velocidad similar:

a = np.random.randint(0,20,1e6) 
start = np.random.randint(0,20,1e4) 
end = np.random.randint(0,20,1e4) 

def my_fun(arr,start,end): 
     return arr[start:end] 

%timeit [my_fun(a,i[0],i[1]) for i in zip(start,end)] 
%timeit map(lambda range: a[range[0]:range[1]],zip(start,end)) 

100 loops, best of 3: 7.06 ms per loop 100 loops, best of 3: 6.87 ms per loop

0

Si desea que en una sola línea, sería:

x=[list(a[s:e]) for (s,e) in zip(start,end)]