Транспонировать список списков

242

Давайте принимать:

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

Очень признателен

Titus
источник

Ответы:

337

Как насчет

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]). Остальное просто убедитесь, что результатом является список списков, а не список кортежей.

Jena
источник
67
Осторожно: если lразмер не является равномерным (скажем, некоторые строки короче других), zipон не компенсирует это и вместо этого отсекает строки из выходных данных. Так l=[[1,2],[3,4],[5]]дает тебе [[1,3,5]].
Badp
29
itertoolsФункция zip_longest()работает с неровными списками. Смотри ДОКУМЕНТЫ
Орегано
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]]
SiggyF
источник
8
Люблю свою вторую - я не понимал map, что смогу это сделать Вот небольшое уточнение , которое не требует 2 вызова, хотя:map(lambda *a: list(a), *l)
Ли D
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]]
inspectorG4dget
источник
12
Поскольку постижение списка теперь предпочтительнее map(), это решение является наиболее подходящим для духа Python ...
perror
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]]
matchew
источник
это то, что я искал и не мог разобраться. Тем не менее решение @ jena очень короткое
titus
3
Да, понадобилось несколько шин, чтобы понять это правильно. Хорошо, много попыток.
Матч
9
Все еще не совсем правильно - это работает, только когда размеры квадратные! Оно должно быть: [[j[i] for j in l] for i in range(len(l[0]))]. Конечно, вы должны быть уверены, что список lне пуст.
Ли Д
@LeeD до сих пор не работает для меня на примере Джены l = [[1,2], [3,4], [5]]
варенье
3
@hobs Это был пример Бадпа, отвечающий Джене. Тем не менее, я не уверен, что это имеет смысл для меня. 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() становится

По умолчанию fillvalue есть None. Благодаря ответу @ jena , где map()происходит изменение внутренних кортежей в списки. Здесь он превращает итераторы в списки. Благодаря комментариям @ Oregano и @ badp .

В Python 3 передайте результат, list()чтобы получить тот же 2D-список, что и в методе 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 работает с рваными 2D-списками, в отличие от его первого кода, который использует 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]
jasonleonhard
источник
0

Возможно, не самое элегантное решение, но вот решение, использующее вложенные циклы while:

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
footballman2399
источник
0
import numpy as np
r = list(map(list, np.transpose(l)))
reza.cse08
источник
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])
1man
источник
-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
SolarJonathan
источник