Numpy: проверка, находится ли элемент в многомерном массиве в кортеже

Кажется, я все еще борюсь с «> оператор «in» в numpy. Вот ситуация:

>>> a = np.random.randint(1, 10, (2, 2, 3))
>>> a
array([[[9, 8, 8],
        [4, 9, 1]],

       [[6, 6, 3],
        [9, 3, 5]]])

Я хотел бы получить индексы тех триплетов, у которых второй элемент находится в (6, 8). Я интуитивно пробовал так:

>>> a[:, :, 1] in (6, 8)
ValueError: The truth value of an array with more than one element...

Моей конечной целью было бы вставить в эти позиции число, предшествующее числу, умноженному на два. Используя приведенный выше пример, a должно стать:

array([[[9, 18, 8],   #8 @ pos #2 --> replaced by 9 @ pos #1 by 2
        [4, 9, 1]],

       [[6, 12, 3],   #6 @ pos #2 --> replaced by 6 @ pos #1 by 2
        [9, 3, 5]]])

Заранее спасибо за совет и время!


person mac    schedule 06.11.2011    source источник


Ответы (4)


import numpy as np
a = np.array([[[9, 8, 8],
               [4, 9, 1]],

              [[6, 6, 3],
               [9, 3, 5]]])

ind=(a[:,:,1]<=8) & (a[:,:,1]>=6)
a[ind,1]=a[ind,0]*2
print(a)

урожаи

[[[ 9 18  8]
  [ 4  9  1]]

 [[ 6 12  3]
  [ 9  3  5]]]

Если вы хотите проверить членство в наборе, который не является простым диапазоном, мне нравятся оба идея Mac об использовании цикла Python и идея белламиджа об использовании np.in1d. Что быстрее, зависит от размера check_tuple:

test.py:

import numpy as np
np.random.seed(1)

N = 10
a = np.random.randint(1, 1000, (2, 2, 3))
check_tuple = np.random.randint(1, 1000, N)

def using_in1d(a):
    idx = np.in1d(a[:,:,1], check_tuple)
    idx=idx.reshape(a[:,:,1].shape)
    a[idx,1] = a[idx,0] * 2
    return a

def using_in(a):
    idx = np.zeros(a[:,:,0].shape,dtype=bool)
    for n in check_tuple:
        idx |= a[:,:,1]==n
    a[idx,1] = a[idx,0]*2
    return a

assert np.allclose(using_in1d(a),using_in(a))    

Когда N = 10, using_in немного быстрее:

% python -m timeit -s'import test' 'test.using_in1d(test.a)'
10000 loops, best of 3: 156 usec per loop
% python -m timeit -s'import test' 'test.using_in(test.a)'
10000 loops, best of 3: 143 usec per loop

Когда N = 100, using_in1d намного быстрее:

% python -m timeit -s'import test' 'test.using_in1d(test.a)'
10000 loops, best of 3: 171 usec per loop
% python -m timeit -s'import test' 'test.using_in(test.a)'
1000 loops, best of 3: 1.15 msec per loop
person unutbu    schedule 06.11.2011
comment
Есть ли значок для официального обучения? Если бы это было так, вы бы его получили. :) У вас есть какой-нибудь хороший учебный материал по numpy? Официальный [предварительный] учебник и справочный материал структурированы таким образом, что мне трудно найти нужную мне информацию... :-/ - person mac; 06.11.2011
comment
Кстати: это изящный обходной путь, но если бы можно было использовать оператор in, я бы предпочел, так как в моем реальном случае у меня есть пул примерно из 10 значений, а не только (6, 8). - person mac; 06.11.2011
comment
Хитрость не в том, что вы читаете, а в том, как вы это читаете. Для каждой функции потратьте несколько минут на то, чтобы спросить, какой простой пример демонстрирует, как ведет себя эта функция? Когда я могу использовать это? Начните создавать файл примеров, который упражняет/демонстрирует каждую функцию/концепцию. Обучение происходит как побочный эффект создания файла примеров. Предварительное руководство и официальные документы — отличные ресурсы. Вы также можете попробовать провести выходные с книгой numpy и руководство пользователя. Удачи! - person unutbu; 06.11.2011

Вот метод, который будет работать для кортежа произвольной длины. Он использует функцию numpy.in1d.

import numpy as np
np.random.seed(1)

a = np.random.randint(1, 10, (2, 2, 3))
print(a)

check_tuple = (6, 9, 1)

bool_array = np.in1d(a[:,:,1], check_tuple)
ind = np.where(bool_array)[0]
a0 = a[:,:,0].reshape((len(bool_array), ))
a1 = a[:,:,1].reshape((len(bool_array), ))
a1[ind] = a0[ind] * 2

print(a)

И вывод:

[[[6 9 6]
  [1 1 2]]

 [[8 7 3]
  [5 6 3]]]

[[[ 6 12  6]
  [ 1  2  2]]

 [[ 8  7  3]
  [ 5 10  3]]]
person joshayers    schedule 06.11.2011
comment
Интересно узнать об использовании in1d. Это немного многословно (с точки зрения количества преобразованных операций), но с ним стоит поэкспериментировать! +1 - person mac; 06.11.2011

Есть еще один метод, основанный на использовании справочной таблицы, о котором я узнал от одного из разработчиков Cellprofiler. Сначала вам нужно создать таблицу поиска (LUT), размер которой равен наибольшему числу в вашем массиве. Для каждого возможного значения массива LUT имеет значение True или False. Пример:

# create a large volume image with random numbers
a = np.random.randint(1, 1000, (50, 1000 , 1000))
labels_to_find=np.unique(np.random.randint(1,1000,500))

# create filter mask LUT 
def find_mask_LUT(inputarr, obs):
    keep = np.zeros(np.max(inputarr)+1, bool)
    keep[np.array(obs)] = True
    return keep[inputarr]

# This will return a mask that is the 
# same shape as a, with True is a is one of the 
# labels we look for, False otherwise
find_mask_LUT(a, labels_to_find)

Это работает очень быстро (гораздо быстрее, чем np.in1d, и скорость не зависит от количества объектов.)

person VolkerH    schedule 21.01.2015

Вдохновленный Ответ unutbu. Я нашел это возможное решение:

>>> l = (8, 6)
>>> idx = np.zeros((2, 2), dtype=bool)
>>> for n in l:
...     idx |= a[:,:,1] == n
>>> idx
array([[ True, False],
       [ True, False]], dtype=bool)
>>> a[idx]
array([[9, 8, 8],
       [6, 6, 3]])

Однако для предварительного исследования требуется знать размеры массива.

person mac    schedule 06.11.2011