Разделите список на группы на основе соседних значений различий

#python #list #sorting #grouping

Вопрос:

У меня следующая проблема с группировкой элементов в списке. После преобразования цифрового изображения я отделил центры отверстий и собрал их в списке значений, затем после вычисления различий между соседними элементами я получил diff_ar. Теперь я хочу получить индексы элементов, принадлежащих к одной группе/кластеру. Я предполагаю, что максимальная разница между элементами в одном разделе должна быть меньше 3. Кроме того, группа может быть создана только при наличии внутри не менее 7 элементов. В результате я ожидаю список кортежей, который содержит начало индекса и конец индекса каждой обнаруженной группы od (2 в этом примере).

Изображение для лучшего изложения проблемы

 values = [73.0, 143.0, 323.0, 324.0, 325.0, 325.0, 325.0, 325.0, 325.5,
          325.5, 326.0, 326.0, 326.0, 326.0, 406.0, 406.5, 432.5, 433.0,
          433.5, 434.5, 435.0, 435.0, 436.0, 436.5, 437.5, 438.0]

diff_ar = [70.0, 180.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0,
           0.0, 0.0, 80.0, 0.5, 26.0, 0.5, 0.5, 1.0, 0.5, 0.0, 1.0, 0.5,
           1.0, 0.5]

expected_output = [(2,12),(16,24)]
 

Комментарии:

1. Каждая отдельная цифра или группа цифр, которые больше 3, должны рассматриваться как разделители групп. На изображении эти значения могут быть представлены в виде фигур, которые не были правильно отфильтрованы, потому что обычный шаблон должен содержать не менее 7 одинаковых значений

Ответ №1:

Первое решение: используйте more_itertools.split_when .

 import more_itertools

values = [73.0, 143.0, 323.0, 324.0, 325.0, 325.0, 325.0, 325.0, 325.5,
          325.5, 326.0, 326.0, 326.0, 326.0, 406.0, 406.5, 432.5, 433.0,
          433.5, 434.5, 435.0, 435.0, 436.0, 436.5, 437.5, 438.0]

threshold = 3
min_items = 7
groups = [(g[0][0], g[-1][0]) for g in more_itertools.split_when(enumerate(values), lambda x,y: abs(x[1]-y[1])>=threshold) if len(g) >= min_items]

print(groups)
# [(2, 13), (16, 25)]
 

Второе решение: напишите цикл самостоятельно.

 def split_values(values, threshold, min_items):
    result = []
    prev = values[0]
    last_cut = 0
    for i,x in enumerate(values[1:], start=1):
        if abs(x - prev) >= threshold:
            if i - last_cut >= min_items:
                result.append((last_cut, i-1))
            last_cut = i
        prev = x
    if len(values) - last_cut >= min_items:
        result.append((last_cut, len(values)-1))
    return result

values = [73.0, 143.0, 323.0, 324.0, 325.0, 325.0, 325.0, 325.0, 325.5,
          325.5, 326.0, 326.0, 326.0, 326.0, 406.0, 406.5, 432.5, 433.0,
          433.5, 434.5, 435.0, 435.0, 436.0, 436.5, 437.5, 438.0]

print(split_values(values, 3, 7))
# [(2, 13), (16, 25)]
 

Комментарии:

1. Это именно то, чего я хотел, спасибо