Как обнаружить изменение знака для элементов в массиве numpy
У меня есть массив numpy с положительными и отрицательными значениями.
a = array([1,1,-1,-2,-3,4,5])
Я хочу создать другой массив, который содержит значение для каждого индекса, где происходит смена знака (например, если текущий элемент положительный, а предыдущий - отрицательный и наоборот).
Для массива выше я ожидаю получить следующий результат
array([0,0,1,0,0,1,0])
В качестве альтернативы, список позиций в массиве, где происходят изменения знака, или список логических элементов вместо 0 и 1, являются точными.
Ответы
Ответ 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
, как предложили другие.