Нарезать список на основе индекса и элементов за ним в 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, но мои попытки вычислить углы не работали, я задал этот вопрос, чтобы решить проблему с еще одной идеей, которую я пытался реализовать. В итоге я принял ответ, который помог мне настроить систему плавного вращения, но ниже приведены соответствующие ответы на первоначальный вопрос.
Ответы
Ответ 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]