2011-03-07 19 views
5

Necesito cortar una matriz de un índice dado hasta que se cumpla una determinada condición.Python: divide la matriz hasta que se cumpla determinada condición

>>> a = numpy.zeros((10), dtype='|S1') 
>>> a[2] = 'A' 
>>> a[4] = 'X' 
>>> a[8] = 'B' 
>>> a 
array(['', '', 'A', '', 'X', '', '', '', 'B', ''], dtype='|S1') 

Por ejemplo, para la matriz anterior, quiero un subconjunto de un índice dado hasta los primeros valores distintos de cero en ambas direcciones. Por ejemplo, para los valores de índice 2, 4, 8, los resultados serían:

['', '', A, '']  # 2 
['', X, '', '', ''] # 4 
['', '', '', B, ''] # 8 

¿Alguna sugerencia sobre la forma más sencilla de hacer esto mediante la API numpy? Al aprender Python y Numpy, agradecería cualquier ayuda. ¡Gracias!

+0

¿Puede aclarar su pregunta? ¿Qué quiere decir "hasta los primeros valores que no son Ninguno en ambas direcciones"? –

+0

El hecho de que esté utilizando matrices 'object' (no es muy común y no consume mucha memoria) presenta un problema particular cuando se trata de determinar el índice de elementos de matriz que no son None. ¿Podría persuadirse de usar un tipo de byte fijo? Si está comprometido con el tipo 'object', ¿es cierto que cualquier elemento" non-None "se evaluará como' True' cuando typecast sea 'bool'? Cualquiera de estos ayudaría a simplificar mucho las cosas. – Paul

+0

@Paul Estoy usando una matriz 'object' para almacenar cadenas de caracteres individuales. Esencialmente, todo lo que necesito es una matriz 'char'. ¿Hay una alternativa 'dtype' que podría usar' dtype'? – armandino

Respuesta

6

este es un trabajo para las matrices enmascarados, numpy.ma tiene un montón de funciones para trabajar con subconjuntos.

a = np.zeros((10), dtype=str) 
a[2] = 'A' 
a[4] = 'X' 
a[8] = 'B' 

vamos a enmascarar elementos no vacíos:

am=np.ma.masked_where(a!='', a) 

np.ma.notmasked_contiguous pasa a través de la matriz (muy eficiente) y encuentra todas las rebanadas de elementos contiguos en los que no se enmascara la matriz:

slices = np.ma.notmasked_contiguous(am) 
[slice(0, 1, None), slice(3, 3, None), slice(5, 7, None), slice(9, 9, None)] 

por lo tanto, la matriz está continuamente vacía entre los elementos 5 y 7, por ejemplo. Ahora sólo tiene que unirse a las rodajas de su interés en entrar, primero se obtiene el índice de inicio de cada rebanada:

slices_start = np.array([s.start for s in slices]) 

se obtiene la ubicación del índice que busca:

slices_start.searchsorted(4) #4 
Out: 2 

Así que desea la división 1 y 2: a [sectores [1] .start: sectores [2] .stop + 1] matriz (['', 'X', '', '', ''], dtype = '| S1')

o probemos 8:

i = slices_start.searchsorted(8) 
a[slices[i-1].start:slices[i].stop+1] 
Out: array(['', '', '', 'B', ''], 
    dtype='|S1') 

Si probablemente debería jugar un poco con esto en ipython para entenderlo mejor.

+0

Andrea muy interesante. Gracias por las explicaciones. ¡Muy apreciado! – armandino

0

Dos bucles son lo primero que viene a la mente. Algo como esto funcionaría:

'''Given an array and an index...''' 
def getNoneSlice(a, i): 

    # get the first non-None index before i 
    start = 0 
    for j in xrange(i - 1, -1, -1): 
     if a[j] is not None: # or whatever condition 
      start = j + 1 
      break 

    # get the first non-None index after i 
    end = len(a) - 1 
    for j in xrange(i + 1, len(a)): 
     if a[j] is not None: # or whatever condition 
      end = j - 1 
      break 

    # return the slice 
    return a[start:end + 1] 
+0

Gracias Mike. La solución funciona perfectamente (+1). Sin embargo, esperaba que hubiera un método 'numpy' para algo como esto. – armandino

+0

Bajé la votación porque esto es muy ineficiente para grandes matrices dispersas. Use los métodos numpy de las otras respuestas. – steabert

+0

Sí, Steabert, estuvo de acuerdo ... Al menos aprendí algo nuevo :-P –

-2
def getSlice(a, n): 
    try: 
     startindex = a[:n].nonzero()[0][-1] 
    except IndexError: 
     startindex = 0 
    try: 
     endindex = a[(n+1):].nonzero()[0][0] + n+1 
    except IndexError: 
     endindex = len(a) 
    return a[startindex: endindex] 
+0

Me temo que no funcionó. Obtengo '['' 'A'] ['' 'X'] ['' 'B'] ' – armandino

+0

el método diferente de cero no funciona para las cadenas vacías – steabert

+0

La pregunta no tenía cadenas vacías cuando respondí, tenía 'Ninguno'. El método distinto de cero funciona con 'Ninguno'. – pwdyson

7

Si configura su problema como este:

import numpy 
a = numpy.zeros((10), dtype=str) 
a[2] = 'A' 
a[4] = 'X' 
a[8] = 'B' 

Usted puede conseguir fácilmente los índices de cadenas no vacías de este modo:

i = numpy.where(a!='')[0] # array([2, 4, 8]) 

Alternativamente, numpy.argwhere(..) también funciona bien.

A continuación, puede cortar distancia utilizando esta matriz:

out2 = a[:i[1]]  # 2 ['' '' 'A' ''] 
out4 = a[i[0]+1:i[2]] # 4 ['' 'X' '' '' ''] 

etc.

+0

Gracias Paul. Eso se parece a lo que estoy buscando. – armandino

2

Tenga en cuenta que esto podría hacerse limpiamente en Python puro utilizando itertools y functools.

import functools, itertools 
arr = ['', '', 'A', '', 'X', '', '', '', 'B', ''] 

f = functools.partial(itertools.takewhile, lambda x: not x) 
def g(a, i): 
    return itertools.chain(f(reversed(a[:i])), [a[i]], f(a[i+1:])) 

Definimos f como el sub-iterador encontrar buscando hasta que el elemento evalúa como verdadero, y g como la combinación de la aplicación de este en la zona inversa de la lista antes de que el índice y la lista después de que el índice.

Esto devuelve generadores que se pueden convertir en listas que contienen nuestros resultados.

>>> list(g(arr, 2)) 
['', '', 'A', ''] 
>>> list(g(arr, 4)) 
['', 'X', '', '', ''] 
>>> list(g(arr, 8)) 
['', '', '', 'B', ''] 
Cuestiones relacionadas