эффективно найти интервал с ненулевыми значениями в scipy / numpy в Python? - PullRequest
4 голосов
/ 12 апреля 2010

Предположим, у меня есть список Python или массив 1-й Python (представлен в виде numpy). предположим, что существует непрерывный участок элементов, как я могу найти начальную и конечную координаты (то есть индексы) участка ненулевых элементов в этом списке или массиве? например,

a = [0, 0, 0, 0, 1, 2, 3, 4]

nonzero_coords (a) должен вернуть [4, 7]. для:

b = [1, 2, 3, 4, 0, 0]

nonzero_coords (b) должен вернуть [0, 2].

спасибо.

Ответы [ 5 ]

4 голосов
/ 12 апреля 2010

Предполагается, что существует один непрерывный участок ненулевых элементов ...

x = nonzero(a)[0]
result = [x[0], x[-1]]
2 голосов
/ 12 октября 2012

Это сработало для нескольких отверстий для меня

from numpy import *
def nonzero_intervals(value):
    lvalue = array(value)
    lvalue[0] = 0
    lvalue[-1] = 0
    a = diff((lvalue==0) * 1)
    intervals = zip( find(a == -1),find(a == 1))
    return intervals
1 голос
/ 25 декабря 2014

Было бы более согласованно с индексацией Python для nonzero_coords([0, 0, 0, 0, 1, 2, 3, 4]) возвращать (4, 8), чем (4, 7), потому что [0, 0, 0, 0, 1, 2, 3, 4][4:8] возвращает [1, 2, 3, 4].

Вот функция, которая вычисляет ненулевые интервалы. Он обрабатывает несколько интервалов:

def nonzero_intervals(vec):
    '''
    Find islands of non-zeros in the vector vec
    '''
    if len(vec)==0:
        return []
    elif not isinstance(vec, np.ndarray):
        vec = np.array(vec)

    edges, = np.nonzero(np.diff((vec==0)*1))
    edge_vec = [edges+1]
    if vec[0] != 0:
        edge_vec.insert(0, [0])
    if vec[-1] != 0:
        edge_vec.append([len(vec)])
    edges = np.concatenate(edge_vec)
    return zip(edges[::2], edges[1::2])

Если вы действительно хотите, чтобы в ответ были включены конечные индексы острова, вы можете просто изменить последнюю строку на: return zip(edges[::2], edges[1::2]-1)

Тесты:

a = [0, 0, 0, 0, 1, 2, 3, 4]
intervals = nonzero_intervals(a)
assert intervals == [(4, 8)]

a = [1, 2, 3, 4, 0, 0]
intervals = nonzero_intervals(a)
assert intervals == [(0, 4)]

a=[1, 2, 0, 0, 0, 3, 4, 0]
intervals = nonzero_intervals(a)
assert intervals == [(0, 2), (5, 7)]

a = [0, 4, 0, 6, 0, 6, 7, 0, 9]
intervals = nonzero_intervals(a)
assert intervals == [(1, 2), (3, 4), (5, 7), (8, 9)]

a = [1, 2, 3, 4]
intervals = nonzero_intervals(a)
assert intervals == [(0, 4)]

a = [0, 0, 0]
intervals = nonzero_intervals(a)
assert intervals == []

a = []
intervals = nonzero_intervals(a)
assert intervals == []
1 голос
/ 12 апреля 2010

На самом деле, nonzero_coords (b) должно возвращать [0, 3].Может ли на входе образоваться несколько отверстий?Если да, что делать тогда?Наивное решение: сканировать до первого ненулевого эл.Затем сканируйте до последнего ненулевого эл.Код ниже (извините, не проверял):

a = [0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0]
start = 0
size = len(a) # 
while (start < size and a[start] != 0): start += 1
end = start
while (end < size and a[end] != 0): end += 1
return (start, end)
0 голосов
/ 12 апреля 2010

Если вы все равно загрузили NumPy, перейдите к ответу Tom10.

Если по какой-то причине вы хотите что-то, что работает без загрузки NUMPY (не могу представить почему, если честно), то япредложить что-то вроде этого:

from itertools import groupby

def nonzero_coords(iterable):
  start = 0
  for iszero, sublist in groupby(iterable, lambda x:x==0):
    if iszero:
      start += len(list(sublist))
    else:
      return start, start+len(list(sublist))-1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...