Перенести список списків


241

Давай візьмемо:

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

Я шукаю результат

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

і ні

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

Цінується

Відповіді:


336

Як щодо

map(list, zip(*l))
--> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Для python 3.x користувачі можуть використовувати

list(map(list, zip(*l)))

Пояснення:

Ми повинні знати дві речі, щоб зрозуміти, що відбувається:

  1. Підпис zip : zip(*iterables)Це означає, що zipочікується довільна кількість аргументів, кожен з яких повинен бути ітерабельним. Напр zip([1, 2], [3, 4], [5, 6]).
  2. Нерозпаковані списки аргументів : З огляду на послідовність аргументів args, f(*args)буде викликати fтакий, що кожен елемент у args- окремий позиційний аргумент f.

Повертаючись до входу від питання l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)буде еквівалентно zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). Решта - просто переконатися, що результат - це список списків, а не список кортежів.


67
Обережно: якщо lне однакові розміру (скажімо, деякі рядки коротше , ніж інші), zipбуде НЕ компенсувати його і замість того, щоб рубати геть рядки з висновку. Так l=[[1,2],[3,4],[5]]дає вам [[1,3,5]].
badp

29
itertoolsФункція zip_longest()працює з нерівними списками. Дивіться DOCS
Орегано

13
Пояснення у відповідь було б приємно :)
Борис Чурзін

7
Я думаю, що навіть list(zip(*l))правильно працює в Python 3.
Стефано

3
@Stefano Це працює (як і zip(*l)в Python 2), але ви отримуєте список кортежів, а не список списків. Звичайно, list(list(it))завжди те саме, що list(it).
Олексій Шпілкін

62

Один із способів зробити це - за допомогою транспозиції NumPy. Для списку:

>>> import numpy as np
>>> np.array(a).T.tolist()
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Або інший без блискавки:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

8
Любіть свій другий - я не розумів, що mapможе це зробити. Ось невелике уточнення, яке не вимагає 2 дзвінків, проте:map(lambda *a: list(a), *l)
Лі Д

7
Чи не може це бути кращою відповіддю, оскільки вона піклується про нерівні списки?
Леон

15
map(None, ...)Схоже, не працює для Py3. Генератор створюється , але next()відразу ж видає помилку: TypeError: 'NoneType' object is not callable.
Божевільний фізик

57

Еквівалентно рішенню Йени:

>>> l=[[1,2,3],[4,5,6],[7,8,9]]
>>> [list(i) for i in zip(*l)]
... [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

12
Оскільки в даний час воліють розуміння списку map(), це рішення є тим, яке найбільше в дусі Пітона ...
переслідування

26

просто для розваги, дійсні прямокутники та припускаючи, що m [0] існує

>>> m = [[1,2,3],[4,5,6],[7,8,9]]
>>> [[row[i] for row in m] for i in range(len(m[0]))]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

це те, що я шукав і не міг опустити голову. Рішення досі @ jena дійсно коротке
titus

3
Так, знадобилося кілька шин, щоб це було правильно. Гаразд, багато спроб.
матч

9
Все-таки не зовсім правильно - це трапляється лише тоді, коли розміри квадратні! Воно повинно бути: [[j[i] for j in l] for i in range(len(l[0]))]. Звичайно, ви повинні бути впевнені, що список lне порожній.
Лі Д

@LeeD досі не працює для мене на прикладі jena l = [[1,2], [3,4], [5]]
варильні панелі

3
@hobs Це був приклад Badp, відповідаючи на Джену. Однак я не впевнений, що це має сенс для мене. IMO, транспозиція передбачає прямокутну матрицю - якщо вона представлена ​​у вигляді списку списків, це означає, що всі внутрішні списки повинні бути однакової довжини. Який результат ви б хотіли зробити як "транспозиція" цього прикладу?
Лі Д

22

Методи 1 і 2 працюють у Python 2 або 3, і вони працюють у нерівних, прямокутних 2D списках. Це означає, що внутрішні списки не повинні мати однакові довжини, як один одного (нерівні), або як зовнішні списки (прямокутні). Інші методи, ну, це складно.

установка

import itertools
import six

list_list = [[1,2,3], [4,5,6, 6.1, 6.2, 6.3], [7,8,9]]

спосіб 1 - map(),zip_longest()

>>> list(map(list, six.moves.zip_longest(*list_list, fillvalue='-')))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

six.moves.zip_longest() стає

Значення за замовчуванням - None. Завдяки @ Jena в відповіді , де map()змінюються внутрішні кортежі списків. Тут він перетворює ітератори на списки. Завдяки коментарям @ Oregano та @ badp .

У Python 3 передайте результат, list()щоб отримати той самий двовимірний список, що і метод 2.


метод 2 - перелік розуміння, zip_longest()

>>> [list(row) for row in six.moves.zip_longest(*list_list, fillvalue='-')]
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

@ InspectorG4dget альтернатива .


метод 3 - map()оф map()- розбитий у Python 3.6

>>> map(list, map(None, *list_list))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]]

Ця надзвичайно компактна друга альтернатива @SiggyF працює з роздвоєними двовимірними списками, на відміну від першого коду, який використовує numpy для переміщення та проходження через нерівні списки. Але None не повинен бути значенням заповнення. (Ні, None, переданий на внутрішню карту (), не є значенням заповнення. Це означає, що немає функції для обробки кожного стовпця. Стовпці просто передаються до зовнішньої карти (), яка перетворює їх з кортежів у списки.

Десь у Python 3 map()перестали миритися з усіма цими зловживаннями: перший параметр не може бути None, а ірраторські ітератори просто усікаються до найкоротшого. Інші методи все ще працюють, оскільки це стосується лише внутрішньої карти ().


Метод 4 - map()з map()знову

>>> list(map(list, map(lambda *args: args, *list_list)))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]   // Python 2.7
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]] // 3.6+

На жаль, нерівні рядки НЕ стають ірваними стовпцями в Python 3, вони просто усічені. Бу-хо-прогрес.


7

Три варіанти на вибір:

1. Карта за допомогою Zip

solution1 = map(list, zip(*l))

2. Перелічити розуміння

solution2 = [list(i) for i in zip(*l)]

3. Для циклу, що додається

solution3 = []
for i in zip(*l):
    solution3.append((list(i)))

І щоб переглянути результати:

print(*solution1)
print(*solution2)
print(*solution3)

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

0

Можливо, не найелегантніше рішення, але ось рішення з використанням вкладених циклів:

def transpose(lst):
    newlist = []
    i = 0
    while i < len(lst):
        j = 0
        colvec = []
        while j < len(lst):
            colvec.append(lst[j][i])
            j = j + 1
        newlist.append(colvec)
        i = i + 1
    return newlist


0

more_itertools.unzip() легко читати, а також працює з генераторами.

import more_itertools
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists

або рівнозначно

import more_itertools
l = more_itertools.chunked(range(1,10), 3)
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists

-1

Ось рішення для переміщення списку списків, який не обов'язково є квадратним:

maxCol = len(l[0])
for row in l:
    rowLength = len(row)
    if rowLength > maxCol:
        maxCol = rowLength
lTrans = []
for colIndex in range(maxCol):
    lTrans.append([])
    for row in l:
        if colIndex < len(row):
            lTrans[colIndex].append(row[colIndex])

-2
    #Import functions from library
    from numpy import size, array
    #Transpose a 2D list
    def transpose_list_2d(list_in_mat):
        list_out_mat = []
        array_in_mat = array(list_in_mat)
        array_out_mat = array_in_mat.T
        nb_lines = size(array_out_mat, 0)
        for i_line_out in range(0, nb_lines):
            array_out_line = array_out_mat[i_line_out]
            list_out_line = list(array_out_line)
            list_out_mat.append(list_out_line)
        return list_out_mat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.