Python / Numpy – заполнять промежутки между непоследовательными точками?

Я пытаюсь найти vectorized / fast / numpy дружественный способ преобразования следующих значений в столбце A, в столбец B:

ID AB 1 0 0 2 0 0 3 1 0 4 1 1 5 0 1 6 0 1 7 -1 1 8 0 0 9 1 0 10 0 1 11 0 1 12 1 1 13 0 1 14 -1 1 15 0 0 

Алгоритм определения столбца «B» должен заполнить все пробелы между группами из 1 и -1 со значением 1, пропустив первую строку в каждой паре. То есть для ID4-ID7 столбец B заполняется единицами (с учетом начального 1 в столбце A @ ID3). Далее, из ID10-ID14 заполняется единицами (поскольку столбец A @ ID9 = 1).

Хотя это легко сделать с циклом for, мне интересно, существует ли решение без петли? Решение на основе петли O (n) приведено ниже:

 import numpy as np import pandas as pd x = np.array([ 0, 0, 1, 1, 0 ,0, -1, 0, 1, 0 , 0, 1, 0, -1, 0]) def make_y(x,showminus=False): y = x * 0 state = 0 # are we in 1 or 0 or -1 for i,n in enumerate(x): if n == 1 and n != state: state = n if i < len(y)-1: y[i+1] = state elif n == -1 and n != state: y[i] = state if showminus: state = -1 else: state = 0 else: y[i] = state return y y = make_y(x) print pd.DataFrame([x,y]).T 

Вышеуказанная функция дает следующую производительность на моей машине:

 %timeit y = make_y(x) 10000 loops, best of 3: 28 µs per loop 

Я предполагаю, что должен быть какой-то способ сделать все быстрее, так как мне в конечном итоге придется иметь дело с массивами длиной 10 миллионов + элементов …

Возможное векторное решение может быть следующим:

 idx_1s, = np.where(x == -1) # find the positions of the -1's idx1s, = np.where(x == 1) # find the positions of the 1's 

Чтобы найти, какие 1 должны превратиться в 0 и пометить начало блока 1:

 idx0s = np.concatenate(([0], np.searchsorted(idx1s, idx_1s[:-1]))) idx0s = idx1s[idx0s] 

Теперь у нас есть два массива одинаковой длины: idx0s и idx_1s , обозначающие позиции первого и последнего элементов каждого блока, поэтому теперь мы можем:

 y = x.copy() y[idx0s] = 0 idx0s += 1 idx_1s += 1 mask = np.zeros_like(y, dtype=np.bool) mask[idx0s] = True mask[idx_1s] = True mask = np.logical_xor.accumulate(mask) y[mask] = 1 

Что дает желаемое:

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

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

Если я нахожу это на вашем x , он не будет бить версию цикла, но для гораздо больших массивов это, вероятно, будет.

Это прекрасно работает,

 A=[0,0,1,1,0,0,-1,0,1,0,0,1,0,-1,0] B=[] #initializing column with same number of zeros for j in range(len(A)): B.append(0) print A for i in range(len(A)): #retrieve the indices of pair (1 to -1) try: one_index=A.index(1) neg_one_index=A.index(-1) except: pass one_index=one_index+1 #replacing the zeros in column B by 1 at correct locations while one_index<=neg_one_index: B[one_index]=1 A[one_index-1]=0 A[one_index]=0 one_index=one_index+1 print B #output->[0,0,0,1,1,1,1,0,0,1,1,1,1,1,0] (ie correct)