Python группирует список в вложенные списки списки, которые являются монотонными с равной разницей между элементами

#python #list #numpy #vectorization #numpy-ndarray

#python #Список #numpy #векторизация #numpy-ndarray

Вопрос:

 l = [2,4,6,12,14,16,21,27,29,31]
  

Я хочу разделить его на списки, чтобы элементы каждого списка представляли собой монотонный список с разницей в 2 между элементами:

 new_l = [[2,4,6], [12,14,16],[21], [27,29,31]]
  

Каков наиболее эффективный способ сделать это?

Ответ №1:

Обновить
Можно вообще избежать циклов Python и использовать только comprehensions (вместе с zip ) для некоторого повышения эффективности. Вы можете сделать это следующим образом:

 l = [2,4,6,12,14,16,21,27,29,31, 44]
n = len(l)
#Find the splitting points (where the monotonic condition breaks):
splitting_points = [0]   [k for k in range(1, n) if l[k] - l[k - 1] != 2]
if splitting_points[-1] != n:
    splitting_points.append(n)
#Then split the list into intervals having bounds in the splitting_points:
new_l = [l[i: j] for i, j in zip(splitting_points[:-1], splitting_points[1:])]
print(new_l)
  

Это «должно» быть намного быстрее, чем циклы (особенно для больших списков), но я не проводил никаких сравнительных тестов.

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

 l = [2,4,6,12,14,16,21,27,29,31]
new_l = []
current_l = [l[0]]                #initially, insert the first element
for k in range(1, len(l)):
    if l[k] - current_l[-1] == 2: #if the monotonic condition is satisfied
        current_l.append(l[k])    #we append the element
    else:
        new_l.append(current_l)   #otherwise, we append the previous list to the answer
        current_l = [l[k]]        #and clear the running sequence
new_l.append(current_l)           #there will always be a last sequence not appended to the answer
print(new_l)
  

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

1. нет ли более эффективного способа для этого?

2. С точки зрения сложности это наиболее эффективный способ, потому что вам нужно выполнить итерацию по списку хотя бы один раз. Однако циклы for в Python не очень эффективны. Я не думаю, что есть другой способ использовать понимания или что-то в этом роде, потому что это своего рода конкретная проблема, с которой вы столкнулись здесь :/

Ответ №2:

Вы можете определить индексы, которые нужно разделить, а затем применить np.split следующим образом:

 np.split(l, np.flatnonzero(np.diff(l)!=2)   1)
  

Вывод:

 [array([2, 4, 6]), array([12, 14, 16]), array([21]), array([27, 29, 31])]
  

Однако игра с массивами разной длины никогда не бывает эффективной, поэтому np.split она довольно медленная.