Python extract indices from tuples if second item in tuple is sequential

Viewed 528

I have a list of tuples generated from some items I have in another list. I want to get the indices (first item in tuple) in a list if the second tuple items are sequential.

e.g. list called "processed"

[(1, 'Chip 1'),
 (1, 'Chip 2'),
 (1, 'Chip 3'),
 (2, 'Chip 4'),
 (4, 'Chip 1'),
 (4, 'Chip 2'),
 (4, 'Chip 3'),
 (4, 'Chip 4'),
 (5, 'Chip 5'),
 (7, 'Chip 1'),
 (7, 'Chip 2'),
 (7, 'Chip 3'),
 (7, 'Chip 4'),
 (8, 'Chip 5'),
 (10, 'Chip 1'),
 (10, 'Chip 2'),
 (10, 'Chip 3'),
 (12, 'Chip 1'),
 (12, 'Chip 2'),
 (14, 'Chip 1'),
 (16, 'Chip 1'),
 (16, 'Chip 2'),
 (18, 'Chip 1'),
 (18, 'Chip 2'),
 (20, 'Chip 1'),
 (20, 'Chip 2'),
 (20, 'Chip 3'),
 (20, 'Chip 4'),
 (23, 'Chip 1'),
 (25, 'Chip 1'),
 (27, 'Chip 1'),
 (27, 'Chip 2')]

I can get the chips in sequential lists by using more_itertools but I don't know how to process this further. Can someone help me figure it?

import more_itertools as mit
chip_nums = [int(p[1][-1]) for p in processed]
for group in mit.consecutive_groups(chip_nums):
    print(list(group))

[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3]
[1, 2]
[1]
[1, 2]
[1, 2]
[1, 2, 3, 4]
[1]
[1]
[1, 2]
3 Answers

groups contains the indexes of elements (start and end inclusive) from the processed list which are continuous.
Note: some tuple contains the same/equal start and end value which signifies that there was no continuity.

groups = []
start = 0
for i in range(len(processed) - 1):
    if int(processed[i][1].split()[-1]) + 1 == int(
        processed[i + 1][1].split()[-1]
    ):  # checks if the next element is 1 greater than previous element
        pass
    else:
        groups.append((start, i))  # stores the start and ending index of a continuous group
        start = i + 1
groups.append((start, i + 1))  # this handles the last remaining element

index_list = [[item[0] for item in processed[start : end + 1]] for start, end in groups]

Output:

[[1, 1, 1, 2], [4, 4, 4, 4, 5], [7, 7, 7, 7, 8], [10, 10, 10], [12, 12], [14], [16, 16], [18, 18], [20, 20, 20, 20], [23], [25], [27, 27]]

You can do it like that:

(You'll find an explanation after that code)

from itertools import groupby
from operator import itemgetter

# PART 1

# List that will contain lists of indexes of "chip_nums". 
# Each list of indexes will correspond to consecutive chips values
list_of_indexes =list()

for k,g in groupby(enumerate(chip_nums),lambda x:x[0]-x[1]):
    group = (map(itemgetter(0),g))
    group = list(map(int,group))
    list_of_indexes.append(group)


# PART 2

# From the retrieved indexes, get fist value in tuple ("index in tuple")
list_of_indexes_contained_in_tuples = list()

for indexes in list_of_indexes:
    consecutive_indexes = list()

    for index in indexes:
        tuple_in_processed = processed[index]
        index_in_tuple = tuple_in_processed[0]
        consecutive_indexes.append(index_in_tuple)
    list_of_indexes_contained_in_tuples.append(consecutive_indexes)


print(list_of_indexes_contained_in_tuples)

Output:

[[1, 1, 1, 2], [4, 4, 4, 4, 5], [7, 7, 7, 7, 8], [10, 10, 10], [12, 12], [14], [16, 16], [18, 18], [20, 20, 20, 20], [23], [25], [27, 27]]

Explanation:

The "PART 1" is a modified version of the function consecutive_groups from more_itertools. I don't know if you knew that, the source code of consecutive_groups (https://github.com/more-itertools/more-itertools/blob/master/more_itertools/more.py) is an implementation of this recipe that one can find on Python.org here: https://docs.python.org/2.6/library/itertools.html#examples

This recipe was first written in Python 2.6:

from operator import itemgetter
from itertools import groupby
data = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17]
for k, g in groupby(enumerate(data), lambda (i,x):i-x):
    print map(itemgetter(1), g)

Transposed in Python 3, you will have this:

from operator import itemgetter
from itertools import groupby
data = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17]
for k, g in groupby(enumerate(data), lambda x:x[0]-x[1]):
    print(list(map(itemgetter(1), g)))

The modification consists of an adaptation of the lambda function to Python 3. Note that we also wrapped the map function with list() in order to obtain list objects insead of map objects.

As for now, with this function we get what you get with the code you provided.

Output:

[2, 3, 4, 5]
[12, 13, 14, 15, 16, 17]

Now, we will modify this function in order to get the indexes of the elements instead of the values in the list (here named "data"). To that end, we just have to pass 0 as argument to itemgetter instead of 1:

data = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17]
for k, g in groupby(enumerate(data), lambda x:x[0]-x[1]):
    print(list(map(itemgetter(0), g)))

No, we have the indexes.

Output:

[0, 1, 2, 3]
[4, 5, 6, 7, 8, 9]

The second part just uses the indexes to get the first items inside the tuples (so the indexes you want).

So, in a nutshell, I adapted the original function consecutive_groups from more_itertools in order to return the indexes of the items instead of their values, and used these indexes to retrieved the first values in each tuple.

Procession with itertools and operator modules:

from itertools import islice
from operator import itemgetter

import more_itertools as mit


it = iter(processed)
getter = itemgetter(0)
chip_nums = map(lambda x: int(x[1][-1]), processed)
for group in mit.consecutive_groups(chip_nums):
    print([*islice(map(getter, it), len([*group]))])

Output:

[1, 1, 1, 2]
[4, 4, 4, 4, 5]
[7, 7, 7, 7, 8]
[10, 10, 10]
[12, 12]
[14]
[16, 16]
[18, 18]
[20, 20, 20, 20]
[23]
[25]
[27, 27]
Related