Массив дозирования, повторяющий последние значения X

#python #arrays #numpy

Вопрос:

У меня длинный массив, и я хочу применить его к пакету. Но, кроме того, я хочу ввести последние X значения в новую партию.

Предположим, мне нужны пакеты из 10 значений, и я хочу повторить последние 2 значения.

 import numpy as np

np.random.seed(9)
vals = np.random.randint(0, 9, 55)
 

Из: [5 6 8 6 1 6 4 8 1 8 5 1 0 8 8 8 2 6 8 1 8 3 5 3 6 7 0 8 1 8 1 6 6 2 8 4 5 3 4 0 8 0 4 5 4 8 3 8 4 8 0 1 2 3 7]

Тогда моя цель -:

 [5 6 8 6 1 6 4 8 1 8]
[1 8 5 1 0 8 8 8 2 6]
[2 6 8 1 8 3 5 3 6 7]
[6 7 0 8 1 8 1 6 6 2]
[6 2 8 4 5 3 4 0 8 0]
[8 0 4 5 4 8 3 8 4 8]
[4 8 0 1 2 3 7]
 

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

Я пытаюсь найти логику в этом, и я нашел следующую:

 bs, ct = 10, 2 # ct = X in my question

print(vals[bs*0-0:bs*1-0])
print(vals[bs*1-2:bs*2-2])
print(vals[bs*2-4:bs*3-4])
print(vals[bs*3-6:bs*4-6])
print(vals[bs*4-8:bs*5-8])
print(vals[bs*5-10:bs*6-10])
print(vals[bs*6-12:bs*7-12])
 

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

 print(vals[0 : bs])
for i in range(1, math.ceil(len(vals)/bs)):
    print(vals[bs*i-2**i : bs*(i 1)-2**i])
 

Я попытался сделать следующее:

 # Without repeating values works ok but not my goal
for i in range(0, len(vals), bs):
    print(vals[i:i bs])
 
 for i in range(0, len(vals), bs):
    print(vals[i-ct:i bs])
 

Я пробую множество комбинаций, используя ct и bt , но всегда испытываю некоторые проблемы. Кто-нибудь может мне помочь, пожалуйста? Я знаю, что это должно быть проще, но я не могу найти логику…

Существует ли какой-то другой вариант более непосредственно, без использования для циклов? Может numpy быть ? Я нашел np.split и думаю np.reshape , что, возможно, это сработает, но проблема в повторении значений X.

Спасибо!

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

1. [vals[i: i 10] for i in range(0, vals.size, 8)] ? Где 8 == (bs - ct)

2. Можете ли вы написать это в качестве ответа? Я выберу его в качестве решения и закрою его

3. Конечно, через минуту.

4. @Cyttorak Вы уверены, что необходимо решение для составления списка? Разве для этого нет собственного решения numpy?

5. @Gulzar Есть, используя as_strided, но это немного опасно. И другие решения были бы дорогостоящими для памяти. Я постараюсь включить это в свой ответ.

Ответ №1:

Это можно сделать с помощью нарезки:

 >>> bs, ct = 10, 2
>>> result = [vals[i: i bs] for i in range(0, vals.size, (bs - ct))]
>>> result
[array([5, 6, 8, 6, 1, 6, 4, 8, 1, 8]),
 array([1, 8, 5, 1, 0, 8, 8, 8, 2, 6]),
 array([2, 6, 8, 1, 8, 3, 5, 3, 6, 7]),
 array([6, 7, 0, 8, 1, 8, 1, 6, 6, 2]),
 array([6, 2, 8, 4, 5, 3, 4, 0, 8, 0]),
 array([8, 0, 4, 5, 4, 8, 3, 8, 4, 8]),
 array([4, 8, 0, 1, 2, 3, 7])]
 

Чего-то подобного можно также достичь с помощью numpy.lib.stride_tricks.as_strided

 >>> from numpy.lib.stride_tricks import as_strided
>>> steps = vals.itemsize
>>> as_strided(
        vals, 
        shape=(math.ceil(vals.size/(bs -ct)), 10), 
        strides=(steps*(bs - ct), steps)
)
array([[  5,   6,   8,   6,   1,   6,   4,   8,   1,   8],
       [  1,   8,   5,   1,   0,   8,   8,   8,   2,   6],
       [  2,   6,   8,   1,   8,   3,   5,   3,   6,   7],
       [  6,   7,   0,   8,   1,   8,   1,   6,   6,   2],
       [  6,   2,   8,   4,   5,   3,   4,   0,   8,   0],
       [  8,   0,   4,   5,   4,   8,   3,   8,   4,   8],
       [  4,   8,   0,   1,   2,   3,   7, 121, 274,  34]])
 

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

Более безопасная альтернатива была введена в numpy.lib.stride_tricks.sliding_window_view

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

1. Никогда раньше не читал этих заметок! Теперь мой внутренний хакер хочет выйти!