Нарезать список на основе индекса и элементов за ним в Python - PullRequest
39 голосов
/ 14 мая 2019

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

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 предыдущих значений за индекс, который я использую?

EDIT:

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

Ответы [ 16 ]

39 голосов
/ 14 мая 2019

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

Ваша цель не состоит в том, чтобы разрезать, объединять или переворачивать списки.Ваша цель - сделать основную арифметику со степенями и сохранить результаты между 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 нетцелое число.

18 голосов
/ 14 мая 2019

Или вы можете использовать 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]
11 голосов
/ 14 мая 2019

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

index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])
7 голосов
/ 14 мая 2019

Решение на основе 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]
7 голосов
/ 14 мая 2019

Для этих случаев удобной функцией 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])
4 голосов
/ 14 мая 2019

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

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

или это:

previous12 = (DEGREES+DEGREES[:DEGREES.index(90)+1])[:-14:-1]
3 голосов
/ 14 мая 2019

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

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]
1 голос
/ 15 мая 2019

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

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]
1 голос
/ 14 мая 2019

Или

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)
1 голос
/ 14 мая 2019
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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...