Нарезать список на основе индекса и элементов за ним в Python - программирование

Нарезать список на основе индекса и элементов за ним в Python

Скажем, у меня есть массив значений степени, например:

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

Я выбрал бы угол и затем смог бы разделить этот гипотетический круг, чтобы упростить поиск кратчайшего пути к цели.

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

Итак, взяв это более раннее значение и применив к этому списку, я получу что-то вроде этого:

[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Используя обозначение среза, я попытался сделать это:

index = DEGREES.index(90)
print(DEGREES[index-12:index]) # start 12 values back, stop at index

Но это только печатает пустой массив.

Есть ли способ нарезать список, чтобы я мог получить 12 предыдущих значений за индекс, который я использую?

РЕДАКТИРОВАТЬ:

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

4b9b3361

Ответ 1

Арифметика с углами

Ваша цель не разрезать, объединять или реверсировать списки. Ваша цель состоит в том, чтобы сделать основную арифметику со степенями и держать результаты между 0 и 359. Для этого вам действительно следует использовать оператор по модулю %:

>>> 90 % 360
90
>>> 390 % 360
30
>>> -60 % 360
300
>>> 360 % 360
0

Вернуться к вопросу

Если вы хотите использовать эту нарезку только для градусов с постоянным приращением, вы можете создать нужный список напрямую:

>>> STEP = 15
>>> list(range(0, 360, STEP))
[0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
>>> def previous_degrees(start, n, step=STEP):
...     return [(start - i * step) % 360 for i in range(n + 1)]
... 
>>> previous_degrees(90, 12)
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]
>>> previous_degrees(90, 12, 30)
[90, 60, 30, 0, 330, 300, 270, 240, 210, 180, 150, 120, 90]
>>> previous_degrees(90, 6, 45)
[90, 45, 0, 315, 270, 225, 180]

Ваш реальный вопрос

Вы написали в комментарии:

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

С двух сторон вам необходимо определить, следует ли вам поворачивать по часовой стрелке или против часовой стрелки. Вы можете снова использовать модуль по модулю, чтобы убедиться, что поворот будет между -180 ° и 179 °:

def shortest_rotation(start_angle, end_angle):
    return (end_angle - start_angle + 180) % 360 - 180

Вот пример:

>>> shortest_rotation(0, 90)
90
>>> shortest_rotation(90, 0)
-90
>>> shortest_rotation(90, 90)
0
>>> shortest_rotation(90, 330)
-120
>>> shortest_rotation(0, 180)
-180
>>> shortest_rotation(0, 181)
-179
>>> shortest_rotation(0, 179)
179
>>> shortest_rotation(10, 350)
-20

Теперь вы можете создать список углов, поворачиваясь в кратчайшем направлении:

def rotation_steps(start_angle, end_angle, n):
    increment = shortest_rotation(start_angle, end_angle) / n
    return [(start_angle + i * increment) % 360 for i in range(n + 1)]

В качестве примера:

>>> rotation_steps(90, 270, 12)
[90.0, 75.0, 60.0, 45.0, 30.0, 15.0, 0.0, 345.0, 330.0, 315.0, 300.0, 285.0, 270.0]
>>> rotation_steps(10, 350, 2)
[10.0, 0.0, 350.0]

Список использует float, чтобы не пропустить end_angle если increment не является целым числом.

Ответ 2

Или вы можете использовать deque:

from collections import deque
from itertools import islice

dq = deque(reversed((0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345)))

index = dq.index(90)
dq.rotate(-index)
res = list(islice(dq, 13))
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Вы можете использовать это как функцию:

def f(i):
    dq.rotate(-dq.index(i))
    return list(islice(dq, 13))

#  f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Ответ 3

Нечто подобное может быть более прямым:

index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])

Ответ 4

Для этих случаев удобной функцией NumPy является np.roll, которая, как указывает ее имя, выполняет свертывание элементов в массиве, а также, как указано в документации:

Элементы, которые выходят за пределы последней позиции, повторно вводятся при первой

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

Таким образом, одним из подходов может быть использование индекса, где 90 появляется с использованием метода списка index, и смещение массива до -k, где k - данный индекс. Затем мы можем просто нарезать список и перевернуть его последние n элементов:

import numpy as np

l = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

def roll_n_reversed(l, i, n):
    return np.roll(l, -l.index(i)-1)[:-(n+1):-1]

roll_n_reversed(l, 90, 13)

Что дает ожидаемый результат:

array([ 90,  75,  60,  45,  30,  15,   0, 345, 330, 315, 300, 285, 270])

Ответ 5

itertools ( cycle и islice):

from itertools import cycle, islice

DEGREES = cycle(reversed((
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345)))

next(item for item in DEGREES if item == 90)  # advance to next 90
res = [90] + list(islice(DEGREES, 12))
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Вы можете упаковать это в однострочную функцию:

def f(i):
    return [next(d for d in DEGREES if d == i), *islice(DEGREES, 12)]

#  f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Или даже используя dropwhile (как упомянуто в комментариях):

from itertools import cycle, islice, dropwhile

def f(i):
    return list(islice(dropwhile(lambda d: d != i, DEGREES), 13))

Если ваш список точно такой же, как вы напечатали выше, вы также можете генерировать фрагменты на лету, используя range:

def f(i, d=15, n=13):
    return [deg % 360 for deg in range(i, i-n*d, -d)]

# f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Ответ 6

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

previous12 = [DEGREES[p-i] for p in [DEGREES.index(90)] for i in range(13)]

или это:

previous12 = (DEGREES+DEGREES[:DEGREES.index(90)+1])[:-14:-1]

Ответ 7

Вы не можете сделать это с одним кусочком, к сожалению. Вы можете объединить фрагменты, что может быть немного неудобно:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
result = DEGREES[index:index - 12:-1] if index >= 12 else (DEGREES[index::-1] + DEGREES[:index - 12:-1])
print(result)
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285]

Или просто используйте понимание списка:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
result = [DEGREES[i] for i in range(index, index - 12, -1)]
print(result)
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285]

Ответ 8

Я думаю, что itertools.chain может быть полезен здесь:

from itertools import chain

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345
]

def get_list_of_degrees(degree, resulting_list_length):
    index = DEGREES.index(degree)
    lower_index = index - (resulting_list_length)
    if index >= resulting_list_length:
        result = DEGREES[lower_index: index]  # start 12 values back, stop at index
    else:
        result = list(chain(DEGREES[lower_index:], DEGREES[:index])) # start 12 values back, stop at index
    return result

my_degrees = get_list_of_degrees(90, 12)
print(my_degrees)

Урожайность:

[270, 285, 300, 315, 330, 345, 0, 15, 30, 45, 60, 75]

Что вы указали, только в обратном направлении

Возможно, более простым и масштабируемым/изменяемым методом было бы создание углов на лету без списка DEGREES. Что-то вроде:

def get_angles(start_angle=90, increment=-15, return_array_size=12):
    angles = [i for i in range(start_angle + increment, start_angle + (return_array_size*increment) + increment, increment)]
    for index in range(len(angles)):
        while angles[index] < 0:
            angles[index] += 360
    return angles

print(get_angles())

Возвращает:

[75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

При этом вы можете с легкостью возвращать только 5 углов, или идти с шагом 2 градуса и т.д. Например.

print(get_angles(increment=-2))

Теперь возвращается:

[88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66]

С очень минимальными изменениями, внесенными в ваш код (в противном случае вам потребуется сгенерировать новый массив DEGREES чтобы выполнить это)

Ответ 9

Причина, по которой вы получили пустой список, в том, что у вас просто нет 12 пунктов до значения 90.

Что вам нужно, это оператор для обработки этого исключения:

index = DEGREES.index(90)
if index >= 12:
    print(DEGREES[index-12:index])
else: 
    print(DEGREES[:index])

Ответ 10

По нарезке списка:

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

value = 90
index = DEGREES.index(value)



result = DEGREES[:index+1][::-1] + DEGREES[index+1:][::-1]
result = result[:13]
print(result)

Выход

[90, 75, 60, 45, 30, 15, 0, 345, 330,
 315, 300, 285, 270]

или же

RES= [ DEGREES[i] for i in range(index,index-12-1,-1)]

Ответ 11

Я думаю, что вам нужно сделать некоторую арифметику.

index = DEGREES.index(90) + 1
offset = 12
start = index - offset
length = len(DEGREES)
print(
    list(reversed(DEGREES[max(0, start):index])) + 
    (list(reversed(DEGREES[length + start - 1 :length])))
     if start < 0
     else [])
)

В качестве альтернативы:

Ответ 12

В вашем примере элементы, которые вы хотите распечатать, это DEGREES[-6:6]. Возможно, вы захотите добавить условные выражения, чтобы позаботиться о начальных индексах, которые в конечном итоге будут повторяться. Что-то вроде этого:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
start_idx = index - 12
if start_idx < 0:
    print(DEGREES[start_idx:] + DEGREES[:index + 1])
else:
    print(DEGREES[start_idx:index + 1])

это должно вернуть следующее:

[270, 285, 300, 315, 330, 345, 0, 15, 30, 45, 60, 75, 90]

какое ваше решение, но обратное.

Ответ 13

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]


index = DEGREES.index(90)

subFront = DEGREES[:index + 1][-12:]
subFront.reverse()

remainLen = 12 - len(subFront) + 1
if remainLen > 0:
    subBack = DEGREES[-remainLen:]
    subBack.reverse()
    subFront = subFront + subBack
print(subFront)
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

Ответ 14

Я бы посоветовал вам попробовать itertools.cycle() для любого количества предыдущих значений.

Просто переверните список и попробуйте cycle().

import itertools

degrees = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
n=12
degrees.reverse()
ind = degrees.index(90)
degrees = degrees[ind:]+degrees[:ind]
rev_cycle = itertools.cycle(degrees)
for i in range(n+1):
    print(next(rev_cycle))

Это эффективно, так как использует генераторы.

Ответ 15

Или же

import numpy as np

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]
idx = DEGREES.index(90)
new_list = DEGREES[::-1]
newList = np.roll(new_list, idx+1)
print(newList)

Ответ 16

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

def wrapping_slice(lst, *args):
    return [lst[i%len(lst)] for i in range(*args)]

Пример:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

start = DEGREES.index(90)

print(wrapping_slice(DEGREES, start, start-13, -1))

Выход:

$ python test.py
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]