элементы списка групп по разнице их относительно друг друга - PullRequest
0 голосов
/ 06 июля 2018

Дан список

A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]

Есть ли простой способ сгруппировать все те кластеры, где разница между последовательными элементами меньше 3?

То есть получить что-то вроде:

[13, 15, 17, 19, 21], [58, 59, 61, 63], [78, 79, 81]

Мне было интересно, существует ли какая-либо встроенная функция, но я не мог найти ничего подобного. Я пытался понять это, используя groupby из itertools, но я застрял. Заранее спасибо.

Ответы [ 4 ]

0 голосов
/ 06 июля 2018

Вы можете использовать itertools.groupby:

import itertools
A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
new_a = [(A[i+1]-A[i], A[i]) for i in range(len(A)-1)]
a = [[a, [c for _, c in b]] for a, b in itertools.groupby(new_a, key=lambda x:x[0] < 3)]
final_groups = [a[i][-1]+[a[i+1][-1][0]] if a[i+1][-1][0] - a[i][-1][-1] < 3 else a[i][-1] for i in range(len(a)-1) if a[i][0]]

Выход:

[[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]
0 голосов
/ 06 июля 2018

Подобно этому ответу , который запрашивал прогоны с тем же номером, можно использовать numpy.split здесь:

import numpy as np

def plateaus(A, atol=3):
    runs = np.split(A, np.where(np.abs(np.diff(A)) >= atol)[0] + 1)
    return [list(x) for x in runs if len(x) > 1]

A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
print(plateaus(A))
[[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]

Без фильтрации по длине это дает вам одноэлементные кластеры, такие как подход itertools.takehwile от @ sytech .

0 голосов
/ 06 июля 2018

На основании вашего комментария

Важно "хранить элементы до тех пор, пока" условие не будет выполнено

Вы можете использовать itertools.takewhile для этого.

takewhile (предикат, повторяемый) -> объект takewhile

Возвращать последовательные записи из итерируемого до тех пор, пока предикат оценивается как истинное для каждой записи.

В этом решении, безусловно, есть возможности для совершенствования, но вы можете воспользоваться takewhile

class Grouper:
    """simple class to perform comparison when called, storing last element given"""
    def __init__(self, diff):
        self.last = None
        self.diff = diff
    def predicate(self, item):
        if self.last is None:
            return True
        return abs(self.last - item) < self.diff
    def __call__(self, item):
        """called with each item by takewhile"""
        result = self.predicate(item)
        self.last = item
        return result


def group_by_difference(items, diff=3):
    results = []
    start = 0
    remaining_items = items
    while remaining_items:
        g = Grouper(diff)
        group = [*itertools.takewhile(g, remaining_items)]
        results.append(group)
        start += len(group)
        remaining_items = items[start:]
    return results

Это дает вам сгруппированные предметы с одноэлементными кластерами.

[[1],
 [6],
 [13, 15, 17, 18, 19, 21],
 [29],
 [36],
 [53],
 [58, 59, 61, 63],
 [78, 79, 81],
 [102],
 [114]]
0 голосов
/ 06 июля 2018

Это один подход с использованием итерации.

Ex:

A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
res = []
temp = []
l = len(A)-1

for i,v in enumerate(A):
    if i+1 > l:
        break

    if abs(v - A[i+1]) < 3:
        temp.append(v)
    else:
        if temp:
            temp.append(v)
            res.append(temp)
            temp = []
print(res)

Выход:

[[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...