在Python中旋转二维数组


122

在一个程序中,我正在编写旋转二维数组的需求。在寻找最佳解决方案时,我发现了这种令人印象深刻的一线功能:

rotated = zip(*original[::-1])

我现在在程序中使用它,它按预期工作。我的问题是,我不了解它是如何工作的。

如果有人可以解释所涉及的不同功能如何实现所需的结果,我将不胜感激。


7
确实。我在这个问题中找到了它。
paldepind 2011年

Answers:


95

考虑以下二维列表:

original = [[1, 2],
            [3, 4]]

让我们将其逐步分解:

>>> original[::-1]   # elements of original are reversed
[[3, 4], [1, 2]]

此列表传递给zip()使用参数unpacking,因此zip调用最终等效于此:

zip([3, 4],
    [1, 2])
#    ^  ^----column 2
#    |-------column 1
# returns [(3, 1), (4, 2)], which is a original rotated clockwise

希望注释能够清楚说明其zip作用,它将基于索引将来自每个可迭代输入的元素进行分组,或者换句话说,将列进行分组。


2
亲密的一个。但是由于整洁的ASCII艺术,我选择了您的;)
paldepind 2011年

1
和星号??
约翰·克特吉克

@johnktejik-这是答案的“参数解压缩”部分,请单击链接以获取详细信息
JR Heard,

1
为了清楚起见,您应该指出这会顺时针旋转矩阵,并且原始列表会转换为元组。
埃弗里特(Everett)

1
绕圈走(返回列表而不是元组的列表),我这样做:rotated = [list(r) for r in zip(*original[::-1])]
哑光

94

太聪明了。

首先,如注释中所述,在Python 3中zip()返回一个迭代器,因此您需要将整个内容封装起来list()以得到实际的列表,因此从2020年开始实际上是:

list(zip(*original[::-1]))

这是细分:

  • [::-1]-以相反的顺序对原始列表进行浅表复制。也可以使用reversed()which来在列表上生成反向迭代器,而不是实际复制列表(更节省内存)。
  • *-使原始列表中的每个子列表成为一个单独的参数zip()(即,解压缩列表)
  • zip()-从每个参数中取出一个项目,并从中得到一个列表(以及一个元组),然后重复进行直到所有子列表都用尽。这是换位实际发生的地方。
  • list()将的输出转换zip()为列表。

所以假设你有这个:

[ [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9] ]

您首先得到以下内容(浅色,反向副本):

[ [7, 8, 9],
  [4, 5, 6],
  [1, 2, 3] ]

接下来,每个子列表作为参数传递给zip

zip([7, 8, 9], [4, 5, 6], [1, 2, 3])

zip() 从其每个参数的开头重复消耗一个项目,并根据它生成一个元组,直到没有更多项目为止,结果是(将其转换为列表之后):

[(7, 4, 1), 
 (8, 5, 2), 
 (9, 6, 3)]

而鲍勃是你的叔叔。

要在有关将IkMiguel朝另一个方向旋转的评论中回答@IkeMiguel的问题,这非常简单:您只需要反转进入的序列zip和结果。第一个可以通过删除来实现,[::-1]第二个可以通过将reversed()整个对象扔掉来实现。由于reversed()回报率在列表上的迭代器,我们需要把list()周围将其转换。通过几次额外的list()调用将迭代器转换为实际列表。所以:

rotated = list(reversed(list(zip(*original))))

我们可以使用“火星人的笑脸”切片而不是reversed()... 来简化一点,那么我们不需要外部的list()

rotated = list(zip(*original))[::-1]

当然,您也可以简单地将列表顺时针旋转三下。:-)


2
可以逆时针旋转吗?
米格尔·艾克

@MiguelIke是的,做zip(* matrix)[::-1]
RYS

3
^请注意,您必须将的结果zip转换为Python 3.x中的列表!
RYS

17

这包括三个部分:

  1. original [::-1]反转原始数组。该表示法是Python列表切片。这为您提供了[start:end:step]描述的原始列表的“子列表”,start是要在子列表中使用的第一个元素,end是最后一个要使用的元素。步骤说从头到尾走每一步。省略开始和结束意味着切片将是整个列表,而否定步骤意味着您将获得相反的元素。因此,例如,如果原始值为[x,y,z],则结果将为[z,y,x]
  2. *在函数调用的参数列表中的列表/元组前面时,*表示“扩展”列表/元组,以便其每个元素成为函数的单独参数,而不是列表/元组本身。因此,如果args = [1,2,3],则zip(args)与zip([1,2,3])相同,但是zip(* args)与zip(1, 2,3)。
  3. zip是一个函数,它接受n个参数,每个参数的长度为m,并生成一个长度为m的列表,其中的元素的长度为n,并包含每个原始列表的对应元素。例如,zip([1,2 ,, [a,b],[x,y])是[[1,a,x],[2,b,y]]。另请参阅Python文档。

+1,因为您可能是唯一解释第一步的人。
paldepind 2011年

8

只是一个观察。输入是一个列表列表,但是非常好的解决方案的输出:旋转= zip(* original [::-1])返回一个元组列表。

这可能是问题,也可能不是问题。

但是,它很容易纠正:

original = [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
            ]


def rotated(array_2d):
    list_of_tuples = zip(*array_2d[::-1])
    return [list(elem) for elem in list_of_tuples]
    # return map(list, list_of_tuples)

print(list(rotated(original)))

# [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

list comp或map都将内部元组转换回list。


2
def ruota_orario(matrix):
   ruota=list(zip(*reversed(matrix)))
   return[list(elemento) for elemento in ruota]
def ruota_antiorario(matrix):
   ruota=list(zip(*reversed(matrix)))
   return[list(elemento)[::-1] for elemento in ruota][::-1]

4
请说明您的解决方案,以便其他人可以更好地理解它。
HelloSpeakman

当然,第一个函数(ruota_antiorario)沿逆时针方向旋转,第二个函数(ruota_orario)沿顺时针方向旋转
user9402118

1

我自己遇到了这个问题,并且找到了关于该主题的出色维基百科页面(在“常见轮换”段落中:
https //en.wikipedia.org/wiki/Rotation_matrix#Ambiguities

然后,我编写了以下超级冗长的代码,以便对发生的事情有一个清晰的了解。

我希望您会发现在您发布的非常漂亮和聪明的单线中进行更多挖掘很有用。

要快速测试它,您可以在此处复制/粘贴它:http :
//www.codeskulptor.org/

triangle = [[0,0],[5,0],[5,2]]
coordinates_a = triangle[0]
coordinates_b = triangle[1]
coordinates_c = triangle[2]

def rotate90ccw(coordinates):
    print "Start coordinates:"
    print coordinates
    old_x = coordinates[0]
    old_y = coordinates[1]
# Here we apply the matrix coming from Wikipedia
# for 90 ccw it looks like:
# 0,-1
# 1,0
# What does this mean?
#
# Basically this is how the calculation of the new_x and new_y is happening:
# new_x = (0)(old_x)+(-1)(old_y)
# new_y = (1)(old_x)+(0)(old_y)
#
# If you check the lonely numbers between parenthesis the Wikipedia matrix's numbers
# finally start making sense.
# All the rest is standard formula, the same behaviour will apply to other rotations, just
# remember to use the other rotation matrix values available on Wiki for 180ccw and 170ccw
    new_x = -old_y
    new_y = old_x
    print "End coordinates:"
    print [new_x, new_y]

def rotate180ccw(coordinates):
    print "Start coordinates:"
    print coordinates
    old_x = coordinates[0]
    old_y = coordinates[1] 
    new_x = -old_x
    new_y = -old_y
    print "End coordinates:"
    print [new_x, new_y]

def rotate270ccw(coordinates):
    print "Start coordinates:"
    print coordinates
    old_x = coordinates[0]
    old_y = coordinates[1]  
    new_x = -old_x
    new_y = -old_y
    print "End coordinates:"
    print [new_x, new_y]

print "Let's rotate point A 90 degrees ccw:"
rotate90ccw(coordinates_a)
print "Let's rotate point B 90 degrees ccw:"
rotate90ccw(coordinates_b)
print "Let's rotate point C 90 degrees ccw:"
rotate90ccw(coordinates_c)
print "=== === === === === === === === === "
print "Let's rotate point A 180 degrees ccw:"
rotate180ccw(coordinates_a)
print "Let's rotate point B 180 degrees ccw:"
rotate180ccw(coordinates_b)
print "Let's rotate point C 180 degrees ccw:"
rotate180ccw(coordinates_c)
print "=== === === === === === === === === "
print "Let's rotate point A 270 degrees ccw:"
rotate270ccw(coordinates_a)
print "Let's rotate point B 270 degrees ccw:"
rotate270ccw(coordinates_b)
print "Let's rotate point C 270 degrees ccw:"
rotate270ccw(coordinates_c)
print "=== === === === === === === === === "

-1

逆时针旋转(标准列到行枢轴)作为列表和Dict

rows = [
  ['A', 'B', 'C', 'D'],
  [1,2,3,4],
  [1,2,3],
  [1,2],
  [1],
]

pivot = []

for row in rows:
  for column, cell in enumerate(row):
    if len(pivot) == column: pivot.append([])
    pivot[column].append(cell)

print(rows)
print(pivot)
print(dict([(row[0], row[1:]) for row in pivot]))

产生:

[['A', 'B', 'C', 'D'], [1, 2, 3, 4], [1, 2, 3], [1, 2], [1]]
[['A', 1, 1, 1, 1], ['B', 2, 2, 2], ['C', 3, 3], ['D', 4]]
{'A': [1, 1, 1, 1], 'B': [2, 2, 2], 'C': [3, 3], 'D': [4]}

1
这与问题无关,该问题要求解释zip(*original[::-1])工作方式。
kaya3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.