Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently adapt a python list according to another list

I could not come up with a better title for my question, sorry.

I have two lists of the same length, e.g.

a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]
           i      j                  i'     j'

I want to adjust list a in such a way that whenever there is a block of or a single False in list a going from index i to j, it is adjusted according to the following condition:

if b[i-1]==b[j+1]:
    a[i:j+1]=[True]*(j-i+1)

in the above example there are two such blocks: i,j=1,2 and i',j'=5,6. The result should be:

a = [True, True, True, True, True, False, False, True]

I wrote a solution with a for loop using ifs but that is too slow since I want to use it on very large lists.

a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]

#Edit: the next two lines were originally and wrongly inside the for loop
moving=True
istart=1
for i,trp in enumerate((a)):
    if trp==False:
        if moving==False:
            # if this condition holds, the particle just started a new move
            istart = i
            moving = True
    else:
        if moving==True:
            # is this condition holds, the particle has stopped its move
            moving = False
            if b[i]==b[istart-1]:
                # if this holds, a needs to be adjusted
                a[istart:i]=[True]*(i-istart)

Any help would be greatly appreciated. (The comments and variable names are like that since it's for analyzing a physics simulations)

like image 498
Johann Avatar asked Jun 21 '26 19:06

Johann


1 Answers

You can try this:

import itertools
a = [True, False, False, True, True, False, False, True]
b = [1,    2,     2,     1,    1,    3,     3,     2   ]
new_a = [(a, list(b)) for a, b in itertools.groupby(zip(a, b), key=lambda x:x[0])]
final_list = list(itertools.chain(*[[True]*len(b) if not a and new_a[i-1][-1][-1] == new_a[i+1][-1][-1] and i > 0 else [c for c, d in b] for i, [a, b] in enumerate(new_a)]))

Output:

[True, True, True, True, True, False, False, True]

Edit: test with new input:

a = [True, False, True]
b = [1, 3, 1]
new_a = [(a, list(b)) for a, b in itertools.groupby(zip(a, b), key=lambda x:x[0])]
final_list = list(itertools.chain(*[[True]*len(b) if not a and new_a[i-1][-1][-1] == new_a[i+1][-1][-1] and i > 0 else [c for c, d in b] for i, [a, b] in enumerate(new_a)]))

Output:

[True, True, True]

Code explanation:

itertools.groupby forms the consecutive blocks of True/False values into single lists. Then, final_list stores the result of iterating over the lists stored in new_a, creating a new sublist of True values if the sublist is composed entirely of False values if and only if the preceding and following values are the same. This is determined by using enumerate to garner the current index for each iteration. That index can then be used to access the preceding and following values via i-1, i+1.

like image 52
Ajax1234 Avatar answered Jun 24 '26 08:06

Ajax1234