3

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

Ali_d
  • 1,089
  • 9
  • 24
  • I've been thinking back about this; out of curiosity, what is the application? – Pierre D Dec 11 '20 at 14:13
  • I have some points. I am connecting these points to create lines. Then, I want to create surfaces using these lines which I am stuck again in that step:-(. I want to create surfaces in a Python packages. – Ali_d Dec 11 '20 at 14:15
  • Oh, I see. I presume you are familiar with the [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation)? And BTW, Matplotlib optionally uses that algorithm for you in its [`Triangulation`](https://matplotlib.org/api/tri_api.html#matplotlib.tri.Triangulation) class. (Memory lane: I remember, back in the early nineties, implementing very fast Delaunay (and Voronoi) in C). – Pierre D Dec 11 '20 at 14:27
  • I know a little bit about Delaunay triangulation, but I am using a Python package called gmsh [https://gmsh.info/] which creates mesh for me. I just need to import points into it. Then, by giving the number of points (which your solution is doing that for me), it creates lines. Then I need to connect lines to create surfaces and finally make mesh based on the surfaces. – Ali_d Dec 11 '20 at 14:31

2 Answers2

4

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')]
Pierre D
  • 24,012
  • 7
  • 60
  • 96
  • 1
    oops -- correcting a small bug: if repetitions are not monotonically decreasing... (e.g. `[4, 2, 4, 2, 1, 1]`... – Pierre D Dec 10 '20 at 18:36
  • Dear @Pierre D, it is not generating what I looked for. Sorry, I get it now. – Ali_d Dec 10 '20 at 18:43
  • I fixed it already -- do you see have an issue? – Pierre D Dec 10 '20 at 18:51
  • Unfortunately yes, for example it is giving me `(5, 9)` while I want `(7, 9)` and so on. If you check the fig or my expected result with `pairs`, you can se differences. I do apprecate your help and thanks for devoting time to my problem. – Ali_d Dec 10 '20 at 18:54
  • 1
    Oh, I see: the ragged lists needed to be reversed. Simple fix -- see updated answer. – Pierre D Dec 10 '20 at 19:07
1

It was only after @Pierre D.'s explanation that I really understood the problem. Thanks. I corrected my previous code and put some print function inside to ease the interpretation.

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

L=[]
k=0
for r in repetition:
    last= k+r
    L.append(points[k:last])
    k= last

print(L,"\n")

lmax= len(max(L,key=len))
L=[ np.concatenate((np.full(lmax-len(l),None),l))  for l in L ]
print(*L,"\n",sep="\n")

print(*zip(*L),"\n",sep="\n")

L= [ [ e for e in l if e] for l in zip(*L) ]
print(*L,"\n",sep="\n")

P= sorted([ p  for l in L for p in zip(l,l[1:]) ])
print(*P,"\n",sep="\n")
kantal
  • 2,331
  • 2
  • 8
  • 15
  • 1
    this doesn't work in the case where `repetition = np.array([4, 2, 4, 2, 1, 1])`. I believe the correct output in that case should be: `[(1, 7), (2, 8), (3, 5), (4, 6), (5, 9), (6, 10), (9, 11), (10, 12), (12, 13), (13, 14)]`. – Pierre D Dec 10 '20 at 19:15
  • @Pierre D You are right. I corrected the code above. – kantal Dec 10 '20 at 21:28