Развернуть список с одинаковыми элементами - PullRequest
3 голосов
/ 05 февраля 2020

Предположим, у меня есть следующий вложенный список:

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

Я бы хотел превратить его в следующее:

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

Если бы меня не волновал порядок, я мог бы сделать что-то вроде

new_list = [sorted(x*3) for x in initial_list]

Однако порядок должен остаться таким же, как в initial_list. Лучшее, что я мог бы сделать, это поместить каждый элемент в список и умножить его на 3 (произвольное число), а затем соединить получившиеся inner_list:

multiplied_list = [[[element]*3 for element in inner_list] for inner_list in initial_list]
desired_list = [[element for element_list in inner_list for element in element_list] for inner_list in multiplied_list]

(в двух списках для понимания человеком)

Есть ли более понятный / адекватный / pythoni c способ сделать это?

Ответы [ 4 ]

4 голосов
/ 05 февраля 2020

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

Код:

>>> initial_list = [[1, 3, 2], [4, 5, 6], [7, 8, 9]]
>>> [[x for x in sl for _ in range(3)] for sl in initial_list]
[[1, 1, 1, 3, 3, 3, 2, 2, 2],
 [4, 4, 4, 5, 5, 5, 6, 6, 6],
 [7, 7, 7, 8, 8, 8, 9, 9, 9]]

Или добавьте ключ к отсортированной функции в вашем примере:

>>> [sorted(x*3, key=x.index) for x in initial_list]
[[1, 1, 1, 3, 3, 3, 2, 2, 2],
 [4, 4, 4, 5, 5, 5, 6, 6, 6],
 [7, 7, 7, 8, 8, 8, 9, 9, 9]]

Сравнение времени методов с различным размером списка n * n:

enter image description here

Генерируется с использованием perfplot - код для воспроизведения:

from itertools import chain
from functools import reduce
import perfplot
from copy import deepcopy
import numpy as np
import random

def shuffle(x):
    random.shuffle(x)
    return x

def cdjb(initial_list):
    return [[x for x in sl for _ in range(3)] for sl in initial_list]

def aurora_sorted(initial_list):
    return [sorted(x*3, key=x.index) for x in initial_list]

def aurora_list_comp(initial_list):
    return [[element for element_list in inner_list for element in element_list] for inner_list in [[[element]*3 for element in inner_list] for inner_list in initial_list]]

def kederrac(initial_list):
    new_list = deepcopy(initial_list)
    for l in new_list:
        for j in range(0, 3*len(l), 3):
            l[j: j + 1] = [l[j]] * 3
    return new_list

def alain_chain(initial_list):
    return [list(chain(*(i3 for i3 in zip(*[sl]*3)))) for sl in initial_list]

def alain_reduce(initial_list):
    return [list(reduce(lambda a,e:a+[e]*3,sl,[]))for sl in initial_list]

def alain_zip(initial_list):
    return [[i for i3 in zip(*[sl]*3) for i in i3] for sl in initial_list]

def binyamin_numpy(initial_list):
    return np.array(initial_list).repeat(3).reshape(len(initial_list), -1).tolist()


perfplot.show(
    setup=lambda n: [shuffle([i for i in range(n)]) for j in range(n)],
    n_range=[2**k for k in range(12)],
    kernels=[
        cdjb,aurora_sorted, aurora_list_comp, kederrac, alain_chain, alain_reduce, alain_zip, binyamin_numpy
        ],
    xlabel='len(x)',
    )
2 голосов
/ 05 февраля 2020

numpy это и 1 строка кода:

 arr=np.array(initial_list)
 arr.repeat(3).reshape(3,-1)

вывод:

Out[44]: 
array([[1, 1, 1, 2, 2, 2, 3, 3, 3],
       [4, 4, 4, 5, 5, 5, 6, 6, 6],
       [7, 7, 7, 8, 8, 8, 9, 9, 9]])
1 голос
/ 05 февраля 2020

Вы могли бы сделать это, используя zip и chain (из itertools):

  from itertools import chain
  aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
  aList3 = [ list(chain(*(i3 for i3 in zip(*[sl]*3)))) for sl in aList ]

или используя уменьшение от functools (которое оказалось намного медленнее в больших списках):

  from functools import reduce
  aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  aList3 = [ list(reduce(lambda a,e:a+[e]*3,sl,[]))for sl in aList ]

или zip с вложенным пониманием (что немного быстрее, чем zip-цепочка):

  aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  aList3 = [[i for i3 in zip(*[sl]*3) for i in i3] for sl in initial_list]
1 голос
/ 05 февраля 2020

вот простой пример использования 2 для циклов:

for l in initial_list:
    for j in range(0, 3*len(l), 3):
        l[j: j + 1] = [l[j]] * 3

Я тестировал решение @CDJB (с сортировкой):

from random import choice
def test1():
    initial_list = [[choice(range(1000)) for _ in range(1000)] for _ in range(100)] 
    def my_func(initial_list):
        for l in initial_list:
            for j in range(0, 3*len(l), 3):
                l[j: j + 1] = [l[j]] * 3
        return initial_list

    my_func(initial_list)

def test2():
    initial_list = [[choice(range(1000)) for _ in range(1000)] for _ in range(100)]  
    [sorted(x*3, key=x.index) for x in initial_list]

вот результаты:

%timeit test2()
1.55 s ± 5.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

и:

%timeit test1()
165 ms ± 542 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

это простое решение в 9 раз быстрее, зависит от ваших данных, конечно

...