Группировать списки списков с несколькими критериями в python - PullRequest
0 голосов
/ 03 августа 2020

Я хочу сгруппировать список списков в списки списков в соответствии с несколькими критериями:

  1. Индекс от 0 до 2 каждого списка должен быть идентичным
  2. Индекс 3 каждого списка находится в пределах диапазона плюс-минус 5 для четвертого элемента.

Если оба условия верны, сгруппируйте списки в список списков. Если нет, не группируйте списки.

Мне удалось сгруппировать списки в список списков, только если первое условие верно с пониманием списка с использованием operator.itemgetter.

Вот пример кода, соответствующего первому условию:

 input_list = [[0, 0, 'increase', 3, 6, 3],
 [0, 0, 'increase', 10, 6, -4],
 [0, 0, 'increase', 11, 6, -5],
 [0, 0, 'increase', 20, 6, -1],
 [0, 1, 'increase', 3, 7, 4],
 [0, 2, 'low', 6, 2, -4]]

from operator import itemgetter
groupby_list = [list(g) for _,g in groupby(input_list,itemgetter(0,1,2))]

При текущем коде первые четыре списка группируются вместе, хотя последний не должен быть сгруппирован с первыми тремя списками в качестве его индекса 3 (например, 20) находится вне диапазона плюс-минус 5 своего четвертого элемента (например, 6) (т.е. 6 not in [20-5 : 20+5 ]).

Вот желаемый результат, удовлетворяющий как условию 1, так и 2:

desired_output = [[[0, 0, 'increase', 3, 6, 3],
  [0, 0, 'increase', 10, 6, -4],
  [0, 0, 'increase', 11, 6, -5]],
 [[0, 0, 'increase', 20, 6, -1]],
 [[0, 1, 'increase', 3, 7, 4]],
 [[0, 2, 'low', 6, 2, -4]]]

Как включить второе условие в текущий код или альтернативным методом?

Ответы [ 3 ]

0 голосов
/ 03 августа 2020

Хорошо, с пояснением, что данные не отсортированы - это выходит за рамки сложности, с которой я лично попытался бы справиться с пониманием или itertools рецептами. Вместо этого я бы l oop через и использовал dict, чтобы сопоставить ключ группировки со списком, который нужно добавить. Примерно так:

def groupable(row, catchment=5):
    return abs(row[4] - row[3]) <= catchment

def grouping_key(row):
    return row[0:2]

collectors = {}
result = []
for row in input_list:
    if groupable(row):
        key = grouping_key(row)
        if not key in collectors:
            # You could use a defaultdict or the setdefault method to shorten this a little
            # But you do still need to explicitly check whether or not you have a new collector and therefore need to append
            new_collector = []
            result.append(new_collector)
            collectors[key] = new_collector
        collectors[key].append(row)
    else:
        result.append([row])

Это стабильно по сравнению с обнаруженным начальным порядком, за исключением того, что он группирует более поздние элементы обратно в список с более ранними значениями, которые имеют общий ключ группировки.

Это значительно проще если вы можете принять вывод, который не поддерживает исходный порядок - в частности, разделение негруппированных значений имеет значение. В этом случае вы можете разделить список на группируемый предикат, а затем использовать groupby, в конце концов, на тех, которые можно группировать (сначала сортируя группируемый раздел, если он еще не был).

* В рецептах 1011 * есть пример раздела:

def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

Таким образом, используя эту и функции удобства, которые я объявил выше (необязательно, вы можете использовать лямбды - или itemgetter, как вы первоначально заметили вместо автор: grouping_key. Примерно так:

groupable_rows, ungroupable_rows = partition(groupable, input_list)
return [list(g) for _,g in groupby(groupable_rows, grouping_key)] + [[row] for row in ungroupable_rows]

Это не проверено, но я надеюсь, что идея ясна.

0 голосов
/ 03 августа 2020
from itertools import groupby


input_list = [[0, 0, 'increase', 3, 6, 3],
           [0, 0, 'increase', 10, 6, -4],
           [0, 0, 'increase', 11, 6, -5],
           [0, 0, 'increase', 20, 6, -1],
           [0, 1, 'increase', 3, 7, 4],
           [0, 2, 'low', 6, 2, -4]]

s = sorted(input_list, key=lambda k: (k[0], k[1], k[2], abs(k[3] - k[4]) > 5))

all_data = []
for _, g in groupby(s, lambda k: (k[0], k[1], k[2], abs(k[3] - k[4]) > 5)):
    all_data.append(list(g))

from pprint import pprint
pprint(all_data)

Печать:

[[[0, 0, 'increase', 3, 6, 3],
  [0, 0, 'increase', 10, 6, -4],
  [0, 0, 'increase', 11, 6, -5]],
 [[0, 0, 'increase', 20, 6, -1]],
 [[0, 1, 'increase', 3, 7, 4]],
 [[0, 2, 'low', 6, 2, -4]]]
0 голосов
/ 03 августа 2020

Сортировать ввод с помощью ключа как itemgetter(0,1,2) вместе с x[3]<=x[4]+5 and x[3]>=x[4]-5

>>> f1 = itemgetter(0,1,2)
>>> f2 = lambda x: (f1(x), x[3]<=x[4]+5 and x[3]>=x[4]-5)
>>> groupby_list = [list(g) for _,g in groupby(sorted(input_list, key=f2), f2)]
>>> pprint(groupby_list)
[[[0, 0, 'increase', 3, 6, 3],
  [0, 0, 'increase', 10, 6, -4],
  [0, 0, 'increase', 11, 6, -5]],
 [[0, 0, 'increase', 20, 6, -1]],
 [[0, 1, 'increase', 3, 7, 4]],
 [[0, 2, 'low', 6, 2, -4]]]
>>> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...