Getting interval cuts between two 2D numpy arrays contining a given range

31 views Asked by At

I have been struggling to write a function to cut up intervals in two numpy arrays (a1,a2) that contain intervals in the full range 0, 6000. intervals from a1 and a2 can not overlap in any way, if a interval is in some values in a1 it can not be in a2, and vice versa I need a function shuffle that would take a given interval, the part of the interval that is in a1 gets moved to a2 and the part of the interval that is in a2 gets moved to a1. finally the 2D arrays are sorted and compacted ensuring that the arrays do not grow in size needlessly when the intervals are near. for example if an array has [[0,1],[1,3]] this will need to get compacted to [[0,3]]

I have tried different things, but can't get a vectorized variant of this. I have managed to write a unittest test case of how outputs should look.



import unittest
class TestCases(unittest.TestCase):


    def test_invertRange(self):
        def shuffle(interval, a1, a2):
            #implement here
            pass
            return a1, a2
        a1  = np.array([[1,300],[500,600],[5000,6000]])
        a2 = np.array([[0,1],[300,500],[600,5000]])
        a1,a2 = shuffle(np.array([[0.5,1.5]]),a1,a2)
        self.assertEqual(a1,np.array([[0.5,1],[1.5,300],[500,600],[5000,6000]]))
        self.assertEqual(a2,np.array([[0,0.5],[1,1.5],[300,500],[600,5000]]))
        suma1 = np.sum(a1[:,1] - a1[:,0])
        suma2 =np.sum(a2[:,1] - a2[:,0])
        total = suma1 + suma2
        # the sum of intervals should always equal the sum of the range 0,6000
        self.assertEqual(total, np.sum(np.linspace(0,6001)))
        a1,a2 = shuffle(np.array([[0.5,1.5]]),a1,a2)
        self.assertEqual(a1,np.array([[1,300],[500,600],[5000,6000]]))
        self.assertEqual(a2,np.array([[0,1],[300,500],[600,5000]]))
        suma1 = np.sum(a1[:,1] - a1[:,0])
        suma2 =np.sum(a2[:,1] - a2[:,0])
        total = suma1 + suma2
        # the sum of intervals should always equal the sum of the range 0,6000
        self.assertEqual(total, np.sum(np.linspace(0,6001)))

1

There are 1 answers

0
Andrej Kesely On

I'm not sure, if pure-numpy vectorized way is (easily) possible. Here is vanilla Python version:

def invert_interval(i1, i2, out_, in_):
    a1, b1 = i1
    a2, b2 = i2

    if a2 < a1 and a1 <= b2 <= b1:
        out_.append([b2, b1])
        in_.append([a1, b2])
    elif b2 > b1 and a1 <= a2 <= b1:
        out_.append([a1, a2])
        in_.append([a2, b1])
    elif a2 <= a1 and b2 >= b1:
        in_.append([a1, b1])
    else:
        out_.append([a1, b1])


def shuffle(interval, a1, a2):
    to_a2 = []
    new_a1 = []

    to_a1 = []
    new_a2 = []

    for i in a1:
        invert_interval(i, interval, new_a1, to_a2)

    for i in a2:
        invert_interval(i, interval, new_a2, to_a1)

    a1[:] = sorted(new_a1 + to_a1)
    a2[:] = sorted(new_a2 + to_a2)


a1 = [[1, 300], [500, 600], [5000, 6000]]
a2 = [[0, 1], [300, 500], [600, 5000]]

shuffle([0.5, 1.5], a1, a2)

print(a1)
print(a2)

Prints:

[[0.5, 1], [1.5, 300], [500, 600], [5000, 6000]]
[[0, 0.5], [1, 1.5], [300, 500], [600, 5000]]