Диапазон с повторяющимися последовательными номерами - PullRequest
0 голосов
/ 12 июня 2018

Я хотел бы создать диапазон (например, (1, 5)) чисел с некоторыми повторениями (например, 4):

[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]

Один из способов написать:

list(itertools.chain(*([x] * 4 for x in range(1, 5))))

Или аналогичным образом:

list(itertools.chain(*(itertools.repeat(x, 4) for x in range(1, 5))))

Однако есть шаг выравнивания, которого можно было бы избежать.

Существует ли более питонная или более компактная версия для генерации такой последовательности?

Ответы [ 7 ]

0 голосов
/ 12 июня 2018

Я просто хотел упомянуть, что extend тоже может быть вариантом.Может быть, не так красиво, как понимание списка из одной строки, но оно будет работать лучше, когда размер buckets увеличится

def listExtend():
    a = []
    for i in range(1,5):
        a.extend([i]*4)
    return a


def listComprehension():
    return [[i,x] for i in range(1, 5) for x in range(4)]


import timeit
print(timeit.timeit(stmt="listComprehension()", setup="from __main__ import listComprehension", number=10**7))
print(timeit.timeit(stmt="listExtend()", setup="from __main__ import listExtend", number=10**7))
14.2532608
8.78004566
0 голосов
/ 30 июня 2018

Один вариант, хотя он требует установки пакета, будет itertation_utilities.replicate:

>>> from iteration_utilities import replicate
>>> list(replicate(range(1, 5), 4))
[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]

Если вы не хотите устанавливать этот пакет, функция replicateпо существу эквивалентно этой функции:

from itertools import repeat

def replicate(items, repeats):
    for item in items:
        for _ in repeat(None, repeats):
            yield item

На всякий случай, если вы заинтересованы в производительности, я сделал несколько микропроцессоров для нескольких (не всех) предложенных альтернатив:

enter image description here

Как вы можете видеть, подходы NumPy и iteration_utilities были самыми быстрыми, тогда как все остальные подходы были примерно одинаково быстрыми.

Интересно отметитьчто из этих других подходов подход list.extend был самым быстрым и (мой) пользовательский генератор самым медленным.Я этого не ожидал.

А вот код для репликации эталонного теста:

from iteration_utilities import replicate
from itertools import chain, repeat
import numpy as np

def replicate_generator_impl(upper):
    for item in range(1, upper):
        for _ in repeat(None, 4):
            yield item

def replicate_generator(upper):
    return list(replicate_generator_impl(upper))

def iteration_utilities_replicate(upper):
    return list(replicate(range(1, upper), 4))

def double_comprehension(upper):
    return [i for i in range(1, upper) for _ in range(4)]

def itertools_chain(upper):
    return list(chain(*([x] * 4 for x in range(1, upper))))

def itertools_chain_from_iterable(upper):
    return list(chain.from_iterable(repeat(i, 4) for i in range(1, upper)))

def extend(upper):
    a = []
    for i in range(1, upper):
        a.extend([i] * 4)
    return a

def numpy_repeat(upper):
    return np.repeat(np.arange(1, upper), 4)

from simple_benchmark import benchmark

funcs = [replicate_generator, iteration_utilities_replicate, double_comprehension, itertools_chain, itertools_chain_from_iterable, extend, numpy_repeat]
arguments = {2**i: 2**i for i in range(1, 15)}
b = benchmark(funcs, arguments, argument_name='size')
b.plot()

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

enter image description here


Отказ от ответственности: я являюсь автором iteration_utilities и simple_benchmark.

0 голосов
/ 12 июня 2018

Вместо этого вы можете использовать список .

l = [i for i in range(1, 5) for _ in range(4)]

Выход

[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]
0 голосов
/ 12 июня 2018

Ничего плохого в вашем решении.Но вы можете использовать chain.from_iterable, чтобы избежать этапа распаковки.

В противном случае, моя единственная другая рекомендация - NumPy, если вы с удовольствием используете стороннюю библиотеку.

from itertools import chain, repeat
import numpy as np

# list solution
res = list(chain.from_iterable(repeat(i, 4) for i in range(1, 5)))

# NumPy solution
arr = np.repeat(np.arange(1, 5), 4)
0 голосов
/ 12 июня 2018

Я большой поклонник кода, который прост и понятен.С этой философией я бы использовал

[n for n in range(1,5) for repeat in range(4)]
0 голосов
/ 12 июня 2018

попробуйте,

range(1,5)*4 # if you don't consider order
sorted(range(1,5)*4) # for ordered seq

С обновлением производительности .

Михай Александру-Ионут Ответ:

%timeit [i for i in range(1, 5) for _ in range(4)]

1000000 loops, best of 3: 1.91 µs per loop

jpp ответ:

%timeit list(chain.from_iterable(repeat(i, 4) for i in range(1, 5)))

100000 loops, best of 3: 2.12 µs per loop

%timeit np.repeat(np.arange(1, 5), 4)

1000000 loops, best of 3: 1.68 µs per loop

Rory Daulton ответ:

%timeit [n for n in range(1,5) for repeat in range(4)]

1000000 loops, best of 3: 1.9 µs per loop

ответ jedwards:

%timeit list(i//4 for i in range(1*4, 5*4))

100000 loops, best of 3: 2.47 µs per loop

RoadRunner Предложено в разделе комментариев:

%timeit for i in range(1, 5): lst.extend([i] * 4)

1000000 loops, best of 3: 1.46 µs per loop

Мой ответ:

%timeit sorted(range(1,5)*4)

1000000 loops, best of 3: 1.3 µs per loop
0 голосов
/ 12 июня 2018

Я думаю, chain + repeat, вероятно, ваш лучший выбор.При этом

start = 1
stop = 5
repeat = 4

x = list(i//repeat for i in range(start*repeat, stop*repeat))

print(x)

Должно работать (по крайней мере, для положительных аргументов).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...