4

I want to assign a value to some segments of an array. I've got indexes of the segments as tuples (start_idx, end_idx). Segments may overlay or be sub-segments of each other.

a = np.zeros(12)
segments = np.array([(0, 3), (1, 2), (6, 8), (8, 10)])
a[segments] = 1

The results is:

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

How can I mask all segments to get this output:

a
>> array([1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])
Katerina
  • 2,580
  • 1
  • 22
  • 25
  • Are you sure the desired output is correct? It seems like the `[3]` element is wrong?! Shouldn't it be `0`? – MSeifert Aug 21 '17 at 21:20
  • @MSeifert It seems its inclusive of the segment ends. – Divakar Aug 21 '17 at 21:21
  • @Divakar But how should `10` be handled then? Normally it would be an `IndexError` at least for `a = np.zeros(10)`. – MSeifert Aug 21 '17 at 21:23
  • 1
    @MSeifert Guessing that's a typo : `a = np.zeros(12)` as the final output seems to have 12 elems. – Divakar Aug 21 '17 at 21:24
  • @Divakar Ah, that's a possibility as well. But seems like the answers exclude the end point - at least the accepted one. So I thought the desired output may be wrong (but wrong input would make sense too). – MSeifert Aug 21 '17 at 21:28

4 Answers4

2

Here's one vectorized approach with the idea being borrowed from this post -

def segment_arr(segments, L): # L being length of output array
    s = np.zeros(L,dtype=int)
    stop = segments[:,1]+1
    np.add.at(s,segments[:,0],1)
    np.add.at(s,stop[stop<len(s)],-1)
    return (s.cumsum()>0).astype(int)

Sample run -

In [298]: segments = np.array([(0, 3), (1, 2), (6, 8), (8, 10)])

In [299]: segment_arr(segments, L=12)
Out[299]: array([1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0])
Divakar
  • 218,885
  • 19
  • 262
  • 358
1

Try this:

a = np.zeros(10)
segments = np.array([(0, 3), (1, 2), (6, 8), (8, 10)])
a[range(3)+range(1,2)+range(6,8)+range(8,10)] = 1
print (a)
d_void
  • 123
  • 10
1

One option is to simply loop through the segments, and convert ranges to actual index:

a = np.zeros(10)
segments = np.array([(0, 3), (1, 2), (6, 8), (8, 10)])

a[[i for s in segments for i in range(*s)]] = 1    
a
# array([ 1.,  1.,  1.,  0.,  0.,  0.,  1.,  1.,  1.,  1.])
Psidom
  • 209,562
  • 33
  • 339
  • 356
1

Just to mention the trivial solution: Use a for-loop over the segments and assign to the slices:

import numpy as np
a = np.zeros(12)
segments = np.array([(0, 3), (1, 2), (6, 8), (8, 10)])

for seg in segments.tolist():  # the "tolist" is just an optimization here, you *could* omit it.
    a[seg[0]: seg[1]+1] = 1    # or just "seq[1]" if you want to exclude the end point
print(a)
# array([ 1.,  1.,  1.,  1.,  0.,  0.,  1.,  1.,  1.,  1.,  1.,  0.])
MSeifert
  • 145,886
  • 38
  • 333
  • 352