Подтвердить что ты не робот

Как обнаружить изменение знака для элементов в массиве numpy

У меня есть массив numpy с положительными и отрицательными значениями.

a = array([1,1,-1,-2,-3,4,5])

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

Для массива выше я ожидаю получить следующий результат

array([0,0,1,0,0,1,0])

В качестве альтернативы, список позиций в массиве, где происходят изменения знака, или список логических элементов вместо 0 и 1, являются точными.

4b9b3361

Ответ 1

Что-то вроде

a = array([1,1,-1,-2,-3,4,5])
asign = np.sign(a)
signchange = ((np.roll(asign, 1) - asign) != 0).astype(int)
print signchange
array([0, 0, 1, 0, 0, 1, 0])

Теперь numpy.roll выполняет циклический сдвиг, поэтому, если последний элемент имеет другой знак, чем первый, первым элементом в массиве signchange будет 1. Если это не желательно, можно, конечно, сделать простой

signchange[0] = 0

Кроме того, np.sign считает, что 0 имеет собственный знак, отличный от положительных или отрицательных значений. Например. массив "signchange" для [-1,0,1] будет [0,1,1], хотя нулевая линия "пересекается" только один раз. Если это нежелательно, можно вставить строки

sz = asign == 0
while sz.any():
    asign[sz] = np.roll(asign, 1)[sz]
    sz = asign == 0

между строками 2 и 3 в первом примере.

Ответ 2

(numpy.diff(numpy.sign(a)) != 0)*1

Ответ 3

Три метода определения местоположения появления смены знака

import numpy as np
a = np.array([1,1,-1,-2,-3,4,5])
Метод 1: Умножить соседние элементы в массиве и найти отрицательные
idx1 = np.where(a[:-1] * a[1:] < 0 )[0] +1
idx1
Out[2]: array([2, 5], dtype=int64)

%timeit np.where(a[:-1] * a[1:] < 0 )[0] + 1
4.31 µs ± 15.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Метод 2 (самый быстрый): если соседние знаки не равны
idx2 = np.where(np.sign(a[:-1]) != np.sign(a[1:]))[0] + 1
idx2
Out[4]: array([2, 5], dtype=int64)

%timeit np.where(np.sign(a[:-1]) != np.sign(a[1:]))[0] + 1
3.94 µs ± 20.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Метод 3: По предложению ianalis. Большинство ИМО элегантно, но немного медленнее
idx3 = np.where(np.diff(np.sign(a)) != 0)[0] + 1
idx3
Out[6]: array([2, 5], dtype=int64)

%timeit np.where(np.diff(np.sign(a)) != 0)[0] + 1
9.7 µs ± 36.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Ответ 4

Как насчет

[0 if x == 0 else 1 if numpy.sign(a[x-1]) != numpy.sign(y) else 0 for x, y in enumerate(a)]

numpy.sign присваивает 0 свой собственный знак, поэтому 0s будет знаковым изменением от всего, кроме других 0, что, вероятно, вам нужно.

Ответ 5

В ответах, приведенных выше, используйте методы списка и некоторую магию, чтобы получить желаемый результат. Вот очень прямой, если немного запутанный, способ сделать то же самое:

import numpy as np

arr = np.array([1,1,-1,-2,-3,4,5])

result = []
for i, v in enumerate(arr):
    if i == 0:
        change = False
    elif v < 0 and arr[i-1] > 0:
        change = True
    elif v > 0 and arr[i-1] < 0:
        change = True
    else:
        change = False

    result.append(change)

print result

Ответ 6

Для прямой интерпретации этого вопроса, где 0 не являются их собственным случаем, вероятно, проще использовать greater, чем sign. Вот пример:

a = array([1, 1, -1, -2, -3, 0, 4, 0, 5, 6])

x = greater_equal(a, 0)
sign_change = x[:-1]-x[1:]

Что дает при печати с T или F, чтобы указать изменение знака между разными номерами:

 1 F 1 T -1 F -2 F -3 T 0 F 4 F 0 F 5 F 6

при печати с использованием:

print `a[0]`+"".join([(" T" if sign_change[i] else " F")+" "+`a[i+1]` for i in range(len(sign_change))])

Также обратите внимание, что это один элемент короче исходного массива, что имеет смысл, так как вы просите изменить знак. Если вы хотите включить изменение между последним и первым элементом, вы можете использовать roll, как предложили другие.