Python'da iki boyutlu bir diziyi döndürme


122

Yazdığım bir programda iki boyutlu bir diziyi döndürme ihtiyacı ortaya çıktı. En uygun çözümü ararken, işi yapan bu etkileyici tek astarı buldum:

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

Şimdi programımda kullanıyorum ve olması gerektiği gibi çalışıyor. Yine de sorunum, nasıl çalıştığını anlamıyorum.

Birisi ilgili farklı işlevlerin istenen sonucu nasıl elde ettiğini açıklayabilirse memnun olurum.


7
Aslında. Bu SO sorusunda buldum .
paldepind

Yanıtlar:


96

Aşağıdaki iki boyutlu listeyi düşünün:

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

Bunu adım adım inceleyelim:

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

Bu liste, argüman açmazip() yöntemine aktarılır , bu nedenle çağrı şuna eşdeğer olur:zip

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

Umarım yorumlar ne zipişe yaradığını netleştirir, her girdideki öğeleri indekse göre yinelenebilir şekilde gruplandırır veya başka bir deyişle sütunları gruplandırır.


2
Yakın bir. Ama zarif ASCII sanatı nedeniyle sizinkini seçtim;)
paldepind

1
ve yıldız işareti ??
john ktejik

@johnktejik - cevabın "tartışmayı açma" kısmı bu, ayrıntılar için bağlantıya tıklayın
JR Heard

1
Netlik sağlamak için, bunun matrisi saat yönünde döndürdüğünü ve orijinal listelerin tuple'lere dönüştürüldüğünü belirtmelisiniz.
Everett

1
tam daire yapmak için (listelerin bir listesini geri alın, tupellerin değil) Bunu yaptım:rotated = [list(r) for r in zip(*original[::-1])]
matt

94

Bu akıllıca bir parça.

İlk olarak, bir yorumda belirtildiği gibi, Python 3'te zip()bir yineleyici döndürür, bu nedenle list()gerçek bir listeyi geri almak için her şeyi içine almanız gerekir , böylece 2020 itibariyle aslında:

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

İşte döküm:

  • [::-1]- orijinal listenin yüzeysel bir kopyasını ters sırada yapar. reversed()Listeyi gerçekte kopyalamak yerine, liste üzerinde bir ters yineleyici üretecek olanı da kullanabilir (bellek açısından daha verimli).
  • *- orijinal listedeki her bir alt listeyi ayrı bir argüman haline getirir zip()(yani, listeyi kaldırır)
  • zip()- her argümandan bir öğe alır ve bunlardan bir liste (iyi, bir demet) yapar ve tüm alt listeler tükenene kadar tekrar eder. Transpozisyonun gerçekte olduğu yer burasıdır.
  • list()çıktısını zip()bir listeye dönüştürür .

Buna sahip olduğunuzu varsayarsak:

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

Önce bunu alırsınız (sığ, tersine çevrilmiş kopya):

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

Daha sonra alt listelerin her biri bir argüman olarak şunlara iletilir zip:

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

zip() argümanlarının her birinin başından bir öğeyi art arda tüketir ve başka öğe kalmayana kadar ondan bir demet oluşturur, sonuçta (bir listeye dönüştürüldükten sonra):

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

Ve Bob senin amcan.

@ IkeMiguel'in sorusunu, onu diğer yöne döndürmekle ilgili bir yorumda yanıtlamak oldukça basittir: hem giren dizileri hem zipde sonucu tersine çevirmeniz yeterlidir . Birincisi kaldırılarak elde edilebilir [::-1]ve ikincisi reversed()her şeyin etrafına bir fırlatılarak elde edilebilir . Yana reversed()listesi üzerinde döner bir yineleyici, biz koymak gerekir list()etrafında o dönüştürmek için. list()Yineleyicileri gerçek bir listeye dönüştürmek için birkaç ekstra çağrı ile. Yani:

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

Bunu biraz "Marslı gülen" dilimini kullanarak basitleştirebiliriz reversed()... o zaman dış kısma ihtiyacımız olmaz list():

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

Elbette, listeyi saat yönünde üç kez de döndürebilirsiniz. :-)


2
Saat yönünün tersine döndürmek mümkün mü?
Miguel Ike

@MiguelIke evet, zip yapın (* matrix) [:: - 1]
RYS

3
^ sonucunu zipPython 3.x'te bir listeye yansıtmanız gerektiğini unutmayın!
RYS

17

Bunun üç bölümü var:

  1. orijinal [:: - 1] orijinal diziyi tersine çevirir. Bu gösterim Python liste dilimlemesidir. Bu size [başlangıç: bitiş: adım] tarafından açıklanan orijinal listenin bir "alt listesini" verir, başlangıç ​​ilk öğe, bitiş alt listede kullanılacak son öğedir. adım, her adımdaki öğeyi ilkinden sonuncuya kadar götür diyor. İhmal edilen başlangıç ​​ve bitiş, dilimin listenin tamamı olacağı anlamına gelir ve negatif adım, öğeleri tersine çevireceğiniz anlamına gelir. Dolayısıyla, örneğin, orijinal [x, y, z] olsaydı, sonuç [z, y, x] olurdu
  2. Bir işlev çağrısının bağımsız değişken listesindeki bir listeden / tuple'dan önce gelen *, listeyi / tuple'ı "genişletmek" anlamına gelir, böylece öğelerinin her biri, listenin / başlığın kendisi yerine işlev için ayrı bir bağımsız değişken haline gelir. Böylece, örneğin args = [1,2,3] ise, zip (args) zip ([1,2,3]) ile aynıdır, ancak zip (* args) zip (1, 2,3).
  3. zip, her biri m uzunluğunda n bağımsız değişken alan ve m uzunluğunda bir liste oluşturan, öğeleri n uzunluğunda olan ve orijinal listelerin her birinin karşılık gelen öğelerini içeren bir işlevdir. Örneğin, zip ([1,2], [a, b], [x, y]) [[1, a, x], [2, b, y]] şeklindedir. Ayrıca Python belgelerine bakın .

+1 çünkü muhtemelen ilk adımı açıklayan tek kişi sizsiniz.
paldepind

8

Sadece bir gözlem. Girdi, listelerin bir listesidir, ancak çok güzel bir çözümün çıktısı: rotated = zip (* orijinal [:: - 1]), bir demet listesi döndürür.

Bu bir sorun olabilir veya olmayabilir.

Bununla birlikte, kolayca düzeltilebilir:

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

Liste kompozisyonu veya haritanın her ikisi de iç dizileri tekrar listelere dönüştürecektir.


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
Başkalarının daha iyi anlayabilmesi için lütfen çözümünüzü açıklayın.
HelloSpeakman

Tabii ki, ilk işlev (ruota_antiorario) saat yönünün tersine ve ikinci işlev (ruota_orario) saat yönünde döner
user9402118

1

Bu sorunu kendim yaşadım ve konuyla ilgili harika bir wikipedia sayfasını buldum ("Ortak rotasyonlar" paragrafında:
https://en.wikipedia.org/wiki/Rotation_matrix#Ambiguities

Sonra neler olup bittiğini net bir şekilde anlamak için aşağıdaki kodu yazdım, süper ayrıntılı.

Umarım yayınladığınız çok güzel ve zekice hazırlanmış tek satırlık yazıda daha fazla kazmayı yararlı bulursunuz.

Hızlı bir şekilde test etmek için buraya kopyalayabilir / yapıştırabilirsiniz:
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

Liste ve Dikte Olarak Saat Yönünün Tersine Döndürme (standart sütundan satır eksenine)

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

üretir:

[['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
Bu, nasıl zip(*original[::-1])çalıştığına dair bir açıklama isteyen soruyla ilgisizdir .
kaya3
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.