2012-09-20 34 views
12

Un simple ejemplo de indexación numpy:Numpy Indexación: Devolver el resto

In: a = numpy.arange(10) 
In: sel_id = numpy.arange(5) 
In: a[sel_id] 
Out: array([0,1,2,3,4]) 

¿Cómo puedo devolver el resto de la matriz que no son indexados por sel_id? Lo que puedo pensar es:

In: numpy.array([x for x in a if x not in a[id]]) 
out: array([5,6,7,8,9]) 

¿Hay alguna manera más fácil?

+0

¿Se trata de una operación de 1 vez? ¿O va a reutilizar 'sel_id' (y su negación) más adelante? Además, ¿le interesa el caso multidimensional o solo el caso 1D? – mgilson

+0

En mi aplicación, funcionará en una matriz masiva multidimensional, y sí, volveré a usar sel_id. – CJLam

+0

Acabo de darme cuenta de que mi solución anterior es INCORRECTA. Si se trata de una matriz de diez 1, el código dado dará una matriz None en lugar de una matriz de cinco 1's. – CJLam

Respuesta

10

Para este caso sencillo 1D, de hecho me gustaría usar una máscara booleana:

a = numpy.arange(10) 
include_index = numpy.arange(4) 
include_idx = set(include_index) #Set is more efficient, but doesn't reorder your elements if that is desireable 
mask = numpy.array([(i in include_idx) for i in xrange(len(a))]) 

Ahora usted puede conseguir sus valores:

included = a[mask] # array([0, 1, 2, 3]) 
excluded = a[~mask] # array([4, 5, 6, 7, 8, 9]) 

Tenga en cuenta que a[mask] no necesariamente producen los mismos cosa como a[include_index] ya que el orden de include_index importa para la salida en ese escenario (debe ser aproximadamente equivalente a a[sorted(include_index)]). Sin embargo, dado que el orden de los elementos excluidos no está bien definido, esto debería funcionar bien.


EDITAR

Una mejor manera de crear la máscara es:

mask = np.zeros(a.shape,dtype=bool) 
mask[include_idx] = True 

(gracias a Seberg).

+0

Me complace solucionar los problemas que esta respuesta pueda tener si decide dejar un comentario sobre lo que está mal con él. – mgilson

+0

@BiRico - mal. Convertí 'include_index' en un' set' (llamado 'include_idx') que tiene un método' __contains__' que va en O (1). Esta solución tiene una complejidad 'O (N)'. – mgilson

+0

+1, esto es casi exactamente lo que iba a sugerir, pero tuve que alejarme de la computadora. Usar una máscara booleana es bueno para operaciones como estas porque no tienes que hacer ningún trabajo adicional para calcular el complemento relativo. Solo fyi, usar 'fromiter' en un generador en lugar de' array' en una lista de comprensión produce un pequeño aumento de velocidad de acuerdo con mis pruebas. – senderle

-1

numpy.setdiff1d(a, a[sel_id]) deberían hacer el truco. No sé si hay algo más ordenado que esto.

+0

Eso no va a funcionar si hay valores repetidos en la matriz. – reptilicus

2

Es más como:

a = numpy.array([1, 2, 3, 4, 5, 6, 7, 4]) 
exclude_index = numpy.arange(5) 
include_index = numpy.setdiff1d(numpy.arange(len(a)), exclude_index) 
a[include_index] 
# array([6, 7, 4]) 

# Notice this is a little different from 
numpy.setdiff1d(a, a[exclude_index]) 
# array([6, 7] 
0

También, si son contiguas uso del [N:] sintaxis para seleccionar el resto. Por ejemplo, arr [5:] seleccionaría el quinto al último elemento de la matriz.

+0

Y si no lo son? –

4

Esto se puede hacer muy bien con máscaras booleanas:

a = numpy.arange(10) 

mask = np.ones(len(a), dtype=bool) # all elements included/True. 
mask[[7,2,8]] = False    # Set unwanted elements to False 

print a[mask] 
# Gives (removing entries 7, 2 and 8): 
[0 1 3 4 5 6 9] 

Suma (tomado de @mgilson). La máscara binaria creada se puede usar muy bien para recuperar las divisiones originales con a[~mask]; sin embargo, esto solo es lo mismo si los índices originales fueron ordenados.


EDIT: se movió hacia abajo, ya que tenía que darse cuenta de que yo consideraría np.delete con errores en este momento (Sep. de 2012).

También podría usar np.delete, aunque las máscaras son más potentes (y en el futuro creo que debería ser una opción ACEPTABLE). Por el momento, sin embargo, es más lento que el anterior, y creará resultados inesperados con índices negativos (o pasos cuando se le da un corte).

print np.delete(a, [7,2,8]) 
+1

Sí, el segundo enfoque es el mejor hasta el momento, y el único algoritmo lineal puro ... parece obvio en retrospectiva. (Tenga en cuenta que detrás de las escenas, 'numpy.delete' simplemente usa [' setdiff1d'] (https://github.com/numpy/numpy/blob/master/numpy/lib/function_base.py#L3380) que a su vez usa ['in1d'] (https://github.com/numpy/numpy/blob/master/numpy/lib/arraysetops.py#L384). También es n log n.) ¡Sería +1 pero ya tiene el mío! – senderle

+0

@senderle en serio! Eso es gracioso, tal vez el 'np.delete' podría usar un cambio para esa ruta de ejecución ... – seberg

+1

-1 para un uso de 'np.delete'. ** Nunca ** una buena idea. –

-1

Suponiendo que a es una matriz 1D, sólo podría hacer estallar los elementos que no desee de la lista de índices:

accept = [i for i in range(a.size) if i not in avoid_list] 
a[accept] 

También podría tratar de usar algo como

accept = sorted(set(range(a.size)) - set(indices_to_discard)) 
a[accept] 

La idea es utilizar la indexación elegante en el complementario del conjunto de índices que no desea.

1

Haría esto con una máscara booleana pero un poco diferente. Que tiene el beneficio de trabajar en N dimensiones, con índices continuos o no. El uso de la memoria dependerá de si se realiza una vista o copia para la matriz enmascarada y no estoy seguro.

import numpy 
a = numpy.arange(10) 
sel_id = numpy.arange(5) 
mask = numpy.ma.make_mask_none(a.shape) 
mask[sel_id] = True 
answer = numpy.ma.masked_array(a, mask).compressed() 
print answer 
# [5 6 7 8 9] 
+0

Las matrices enmascaradas pueden ser una buena opción. Aunque '.compressed()' en cierto modo infringe la matriz enmascarada, tiene como objetivo IMO, ya que crea una copia de matriz normal. – seberg

+0

De acuerdo, pero la pregunta era obtener el resto de la matriz ... –

Cuestiones relacionadas