[英]Slice a list based on an index and items behind it in 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中创建一个平滑的旋转系统,我尝试计算角度不起作用,我问这个问题是为了解决我想要实现的另一个想法的问题。 我最终接受了帮助我建立平滑旋转系统的答案,但下面的原始问题有相关答案。
您的目标不是切片,连接或反转列表。 你的目标是用度数进行基本算术,并将结果保持在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,以避免在increment
不是整数时丢失end_angle
。
或者你可以使用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]
这样的事情可能更直接:
index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])
对于这些情况,一个派上用场的NumPy函数是np.roll
,正如其名称所指定的那样,它会滚动数组中的元素,以及文档中提到的:
超出最后位置的元素将在第一个位置重新引入
这正是我们需要的,以便在列表中的第一个项目后面滚动到90
出现的索引。
因此,一种方法可以是使用index
列表方法出现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])
基于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]
你可以用这个:
previous12 = [DEGREES[p-i] for p in [DEGREES.index(90)] for i in range(13)]
或这个:
previous12 = (DEGREES+DEGREES[:DEGREES.index(90)+1])[:-14:-1]
不幸的是,你不能用一个切片做到这一点。 你可以连接各个部分,这可能有点尴尬:
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]
我认为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
数组才能完成此操作)
您获得空列表的原因是因为您在90之前没有12个项目。
你需要的是一个处理这个异常的语句:
index = DEGREES.index(90)
if index >= 12:
print(DEGREES[index-12:index])
else:
print(DEGREES[:index])
按列表切片:
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)]
我想你需要做一些算术。
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 [])
)
或者:
我建议你尝试使用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))
这是有效的,因为它使用发电机。
在您的示例中,您要打印的元素是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]
这是你的解决方案,但是相反。
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]
要么
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)
我有这个方便的功能 ,实现包装切片。 通过直接计算角度值可以更好地解决您的用例,因为其他答案已经显示出来。 这可能会成功:
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]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.