Search code examples
pythonnumpyenumerate

How to print specific pairs of a numpy array


I want to make especific pairs in a numpy array and I show what I want with a simple print function. I have two arrays:

points=np.arange(1,15)

Then I have another array:

repetition= np.array([4, 4, 2, 2, 1, 1])

Now, I want to print the following pairs (I just wrote the comment to show what I want):

1 5 # first value of points and (1+4)th value of point
2 6 # second value of points and (2+4)th value of point
3 7 # third value of points and (3+4)th value of point
4 8 # fourth value of points and (3+4)th value of point
7 9 # seventh value of points and (6+2)th value of point
8 10 # eighth value of points and (8+2)th value of point
9 11 # ninth value of points and (9+2)th value of point
10 12 # tenth value of points and (10+2)th value of point
12 13 # twelfth value of points and (11+2)th value of point
13 14 # thirteenth value of points and (13+1)th value of point

I tried the following code but it did not give me the result I expect:

for m, n in zip (points, repetition):
    print (m, m+n)

In the fig, I visualized my question in which red lines are showing my pairs. I do appreciate any help in advance.

enter image description here


Solution

  • You can actually do it all in Python. Since your picture seems to indicate ragged arrays, doing it in numpy might be a little trickier.

    from itertools import islice, zip_longest, count, tee
    
    def pairwise(iterable):  # as per itertools recipes
        "s -> (s0,s1), (s1,s2), (s2, s3), ..."
        a, b = tee(iterable)
        next(b, None)
        return zip(a, b)
    
    def idx_pairs(repetitions):
        it = count()
        z = [list(islice(it, n))[::-1] for n in repetitions] 
        idx = sorted([
            (i, j) for seq in zip_longest(*z)
            for i, j in pairwise([k for k in seq if k is not None])])
        return idx
    
    [(points[i], points[j]) for i, j in idx_pairs(repetition)]
    

    Output:

    [(1, 5),
     (2, 6),
     (3, 7),
     (4, 8),
     (7, 9),
     (8, 10),
     (9, 11),
     (10, 12),
     (12, 13),
     (13, 14)]
    

    For a better understanding of the steps, I suggest inspecting:

    • z
    • idx
    • list(zip_longest(*z))

    The latter, in particular (done directly on the points, rather than the point indices), shows something very similar to the OP's drawing:

    it = iter(points)
    z = [list(islice(it, n))[::-1] for n in repetition]
    list(zip_longest(*z))
    # out:
    [(4, 8, 10, 12, 13, 14),
     (3, 7, 9, 11, None, None),
     (2, 6, None, None, None, None),
     (1, 5, None, None, None, None)]
    

    BTW, it is interesting to see what happens when the repetition list is not monotonically decreasing:

    repetition = np.array([4, 2, 4, 2, 1, 1])
    
    it = iter(points)
    z = [list(islice(it, n))[::-1] for n in repetition]
    list(zip_longest(*z))
    
    # out:
    [(4, 6, 10, 12, 13, 14),
     (3, 5, 9, 11, None, None),
     (2, None, 8, None, None, None),
     (1, None, 7, None, None, None)]
    

    I do believe the correct output for such a repetition should be:

    [(1, 7),
     (2, 8),
     (3, 5),
     (4, 6),
     (5, 9),
     (6, 10),
     (9, 11),
     (10, 12),
     (12, 13),
     (13, 14)]
    

    For fun, points can contain anything; Here is an example with strings:

    points = [
        'foo', 'bar', 'hi', 'hello', 'world',
        'fuzz', 'ball', 'apple', 'tom', 'nancy',
        'fred', 'james', 'mary', 'bob', 'lisa', 
    ]
    repetition = np.array([4, 2, 4, 2, 1, 1])
    [(points[i], points[j]) for i, j in idx_pairs(repetition)]
    
    # out:
    [('foo', 'ball'),
     ('bar', 'apple'),
     ('hi', 'world'),
     ('hello', 'fuzz'),
     ('world', 'tom'),
     ('fuzz', 'nancy'),
     ('tom', 'fred'),
     ('nancy', 'james'),
     ('james', 'mary'),
     ('mary', 'bob')]