简体   繁体   English

在Python中根据索引及其背后的项目切片列表

[英]Slice a list based on an index and items behind it in Python

Say I have an array of degree values, like this: 假设我有一系列度数值,如下所示:

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,
]

I would pick an angle and then be able to bisect this hypothetical circle in order to make it easier to find the shortest route to the target direction. 我会选择一个角度,然后能够将这个假设的圆圈平分,以便更容易找到到达目标方向的最短路线。

Saying that, how can I pick a specific value, like 90 , and then be able to find the previous 12 elements behind that, including the index wrapping around to the end? 说,我怎样才能选择一个特定的值,比如90 ,然后能够找到后面的12个元素,包括索引到底结束?

So, taking that earlier value and applying to that list, I would get something like this: 因此,采用该早期值并应用于该列表,我会得到这样的结果:

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

Using slice notation, I tried doing this: 使用切片表示法,我尝试这样做:

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

But this only prints an empty array. 但这只会打印一个空数组。

Is there a way to slice a list so I can get the 12 previous values behind the index I'm using? 有没有办法切片列表,以便我可以获得我正在使用的索引后面的12个先前值?

EDIT: 编辑:

This turned out to be an XY Problem, my bad. 事实证明这是一个XY问题,我的不好。 Originally, I was trying to create a smooth rotation system in Pygame, with my attempts to calculate angles not working, I asked this question to solve a problem with yet another idea I was trying to implement. 最初,我试图在Pygame中创建一个平滑的旋转系统,我尝试计算角度不起作用,我问这个问题是为了解决我想要实现的另一个想法的问题。 I ended up accepting the answer that helped me set up the smooth rotation system, but there are relevant answers to the original question below that. 我最终接受了帮助我建立平滑旋转系统的答案,但下面的原始问题有相关答案。

Arithmetic with angles 算术与角度

Your goal isn't to slice, concatenate or reverse lists. 您的目标不是切片,连接或反转列表。 Your goal is to do basic arithmetic with degrees and keep the results between 0 and 359 . 你的目标是用度数进行基本算术,并将结果保持在0359之间。 For this, you really should use the modulo operator % : 为此,你真的应该使用运算符%

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

Back to the question 回到问题

If you only want to use this slicing for degrees with a constant increment, you could generate the desired list directly: 如果您只想对具有常量增量的度数使用此切片,则可以直接生成所需的列表:

>>> 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]

Your real question 你真正的问题

You wrote in a comment: 你在评论中写道:

This array of degrees is designed to work with a smooth rotation system that I'm trying to create in pygame. 这个度数数组旨在与我试图在pygame中创建的平滑旋转系统一起使用。 Normally I would just find the difference between the current direction and the target direction and increment from there, but since the rotation rolls over at zero I have to hardcode the values to make sure that it will always go the shortest route possible. 通常我会找到当前方向和目标方向之间的差异并从那里开始增量,但由于旋转在零处滚动,我必须对值进行硬编码以确保它始终以最短的路线进行。

From two angles, you need to determine if you should turn clockwise or anticlockwise. 从两个角度,您需要确定是应该顺时针还是逆时针转动。 You can use modulo again to make sure that the rotation will be between -180° and 179°: 您可以再次使用模数来确保旋转在-180°和179°之间:

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

Here's an example: 这是一个例子:

>>> 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

You can now create a list of angles, turning in the shortest direction: 您现在可以创建一个角度列表,以最短的方向转动:

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)]

As an example: 举个例子:

>>> 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]

The list uses float in order to avoid missing the end_angle if increment isn't an integer. 该列表使用float,以避免在increment不是整数时丢失end_angle

Or you could use a deque : 或者你可以使用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]

You could use that as a function: 您可以将其用作函数:

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]

Something like this might be more direct: 这样的事情可能更直接:

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

For these cases, a NumPy function that comes in handy is np.roll , which, as its name specifies, rolls the elements in the array, and as also as mentioned in the documentation: 对于这些情况,一个派上用场的NumPy函数是np.roll ,正如其名称所指定的那样,它会滚动数组中的元素,以及文档中提到的:

Elements that roll beyond the last position are re-introduced at the first 超出最后位置的元素将在第一个位置重新引入

Which is exactly what we need in order to roll at the back the first items in the list up to the index where 90 appears. 这正是我们需要的,以便在列表中的第一个项目后面滚动到90出现的索引。

So one approach could be to use the index where 90 appears using the index list method, and shift the array up to -k positions, k being the given index. 因此,一种方法可以是使用index列表方法出现90index ,并将数组向上移动到-k位置, k是给定索引。 Then we can just slice the list and take its last n elements reversed: 然后我们可以切片列表并将其最后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)

Which yields the expected output: 这产生了预期的输出:

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

An itertools ( cycle and islice ) based solution: 基于itertoolscycleislice )的解决方案:

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]

You can pack that into a one-liner function: 您可以将其打包成单线功能:

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]

Or even using dropwhile (as mentioned in the comments): 甚至使用dropwhile (如评论中所述):

from itertools import cycle, islice, dropwhile

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

If your list is exactly as you printed above, you could also generate the slices on the fly using range : 如果您的列表与上面打印的完全相同,您还可以使用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]

You can use this: 你可以用这个:

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

or this: 或这个:

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

You cannot do that with one slice unfortunately. 不幸的是,你不能用一个切片做到这一点。 You can either concatenate the pieces, which can be a bit awkward: 你可以连接各个部分,这可能有点尴尬:

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]

Or just use a list comprehension: 或者只使用列表理解:

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]

I think itertools.chain might be useful here: 我认为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)

Yields: 产量:

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

Which is what you specified, just backwards 这是你指定的,只是向后

Perhaps a more straightforward and scaleable/alterable method would be to generate the angles on the fly without a DEGREES list. 也许更直接和可扩展/可变的方法是在没有DEGREES列表的情况下即时生成角度。 Something like: 就像是:

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())

Returns: 返回:

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

While allowing you the flexibility to return only 5 angles easily, or go in step sizes of 2 degrees, etc.. For instance 同时允许您灵活地轻松返回5个角度,或者以2度的步长等进行操作。例如

print(get_angles(increment=-2))

Now returns: 现在返回:

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

With very minimal change done to your code (otherwise you would have to generate a new DEGREES array to accomplish this) 只需对代码进行非常小的更改(否则您必须生成一个新的DEGREES数组才能完成此操作)

The reason you got empty list is because you simply don't have 12 items prior to value of 90. 您获得空列表的原因是因为您在90之前没有12个项目。

What you need is a statement to handle this exception: 你需要的是一个处理这个异常的语句:

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

By list slicing: 按列表切片:

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)

Output 产量

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

or 要么

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

I think you need to do some arithmetic. 我想你需要做一些算术。

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 [])
)

Alternatively: 或者:

I would suggest you to try itertools.cycle() for any number of previous values. 我建议你尝试使用itertools.cycle()获取任意数量的先前值。

Just reverse the list and try 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))

This is efficient as it is using generators. 这是有效的,因为它使用发电机。

In your example, the elements you wish to print out are DEGREES[-6:6] . 在您的示例中,您要打印的元素是DEGREES[-6:6] You might want to add conditionals to take care of starting indexes that end up looping back around. 您可能希望添加条件来处理最终循环回来的索引。 Something like this: 像这样的东西:

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])

this should return the following: 这应该返回以下内容:

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

which is your solution, but reversed. 这是你的解决方案,但是相反。

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]

Or 要么

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)

I have this handy function that implements wrapping slicing. 我有这个方便的功能 ,实现包装切片。 While your use case might be better solved by directly computing the angle values as other answers have already shown. 通过直接计算角度值可以更好地解决您的用例,因为其他答案已经显示出来。 This might do the trick: 这可能会成功:

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

Example: 例:

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))

The output is: 输出是:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM