仅使用列表理解重新排列(列表列表)矩阵
Rearranging a (list of lists) matrix using list comprehensions only
考虑以下 3 x 4 矩阵实现为 Python 中长度为 4 的 3 个列表的列表:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
以下列表理解将重新排列矩阵转置行和列:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
但假设我需要这个作为结果:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
这是一个重新排列的 4 x 3 矩阵,以便获得原始矩阵的顺序 "scrolling",每 3 个元素 "breaks" 到一个新行。
我知道可以制定一个算法来完成任务,但是 是否可以仅使用列表理解来获得它?(而且,如果可以,如何实现? )
编辑:
采纳的答案需满足以下要求:
- 必须在 base/clean Python 安装上工作(没有额外的库);
- 必须是(类似于矩阵转置)一个 "one-liner".
第二次编辑+接受的答案动机:
这是我在寻找解决方案时所做的(基于我在下面的评论中给出的建议):
mat = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
[[mat[(3*(i-1)+j -1)//4+1][(3*(i-1)+j -1)%4] for j in range(3)] for i in range(4)]
请注意,我写的解决方案是针对这种情况的,但是,正如 Clodion 也注意到的那样,"formula" 可以是 "generalized" 以便重新安排初始(列表列表)矩阵到不同的 "shapes".
这样的事情怎么样:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(zip(*[it]*3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]
如果你想要列表列表:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(map(list, zip(*[it]*3)))
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
解释:
首先我们创建一个生成器,它给出列表的元素(就好像它被展平成一个列表——即 1,2,3,4,5,6,... ,12) - 我们将其存储在 it
.
中
然后我们调用zip()
给它it
三次;由于生成器是相同的,它每次都在 it
中提供下一个元素。
进一步注意:如果需要,您甚至可以将初始矩阵重新排列为 "shapes" 而不是 4 x 3,只需将 3
更改为所需的列数即可。 (例如,将其更改为 2
或 6
,您将分别获得 6 x 2 或 2 x 6 重新排列的矩阵)。
一种不使用 zip
的方法,只使用列表理解,但需要两行 -
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> [[next(it) for _ in range(3)] for _ in range(4)]
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
numpy 是一个选项吗?
import numpy
x = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
y = numpy.array(x).reshape((4, 3))
print y
输出:
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
这至少是一个列表理解,我现在缺乏创造力,但这在一定程度上起到了作用:P
print [[[z for y in x for z in y][i*3+j] for j in range(len(x))] for i in range(len(x[0]))]
输出:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
好吧,只有列表,它很粗糙(建议将不胜感激?):
matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
lst = [y for row in matrix for y in row]
lst = [[lst[x+y*3] for x in range(3)] for y in range(4)]
print(lst)
结果:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
是的!在一个列表理解中:
nr, nc = 4, 3
ic = len(matrix[0])
lst = [[matrix[(r*nc+c)//ic][(r*nc+c)%ic] for c in range(nc)] for r in range(nr)]
print(*lst, sep="\n")
结果:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, 11, 12]
但你也可以拥有(nr, nc = 6, 2
)
[1, 2]
[3, 4]
[5, 6]
[7, 8]
[9, 10]
[11, 12]
这是你的问题,不是吗?
以下解决方案仅使用列表推导,不使用 zip:
m = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
flat = [l2 for l1 in m for l2 in l1]
output = [[flat.pop(0), flat.pop(0), flat.pop(0)] for x in range(4)]
print(output)
输出为:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
或者对于一个非常简单的衬垫:
print [m[0][:3],[m[0][3]]+m[1][0:2],m[1][2:]+[m[2][0]],m[2][1:]]
考虑以下 3 x 4 矩阵实现为 Python 中长度为 4 的 3 个列表的列表:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
以下列表理解将重新排列矩阵转置行和列:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
但假设我需要这个作为结果:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
这是一个重新排列的 4 x 3 矩阵,以便获得原始矩阵的顺序 "scrolling",每 3 个元素 "breaks" 到一个新行。
我知道可以制定一个算法来完成任务,但是 是否可以仅使用列表理解来获得它?(而且,如果可以,如何实现? )
编辑:
采纳的答案需满足以下要求:
- 必须在 base/clean Python 安装上工作(没有额外的库);
- 必须是(类似于矩阵转置)一个 "one-liner".
第二次编辑+接受的答案动机:
这是我在寻找解决方案时所做的(基于我在下面的评论中给出的建议):
mat = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
[[mat[(3*(i-1)+j -1)//4+1][(3*(i-1)+j -1)%4] for j in range(3)] for i in range(4)]
请注意,我写的解决方案是针对这种情况的,但是,正如 Clodion 也注意到的那样,"formula" 可以是 "generalized" 以便重新安排初始(列表列表)矩阵到不同的 "shapes".
这样的事情怎么样:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(zip(*[it]*3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]
如果你想要列表列表:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(map(list, zip(*[it]*3)))
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
解释:
首先我们创建一个生成器,它给出列表的元素(就好像它被展平成一个列表——即 1,2,3,4,5,6,... ,12) - 我们将其存储在
it
. 中
然后我们调用
zip()
给它it
三次;由于生成器是相同的,它每次都在it
中提供下一个元素。
进一步注意:如果需要,您甚至可以将初始矩阵重新排列为 "shapes" 而不是 4 x 3,只需将 3
更改为所需的列数即可。 (例如,将其更改为 2
或 6
,您将分别获得 6 x 2 或 2 x 6 重新排列的矩阵)。
一种不使用 zip
的方法,只使用列表理解,但需要两行 -
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> [[next(it) for _ in range(3)] for _ in range(4)]
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
numpy 是一个选项吗?
import numpy
x = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
y = numpy.array(x).reshape((4, 3))
print y
输出:
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
这至少是一个列表理解,我现在缺乏创造力,但这在一定程度上起到了作用:P
print [[[z for y in x for z in y][i*3+j] for j in range(len(x))] for i in range(len(x[0]))]
输出:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
好吧,只有列表,它很粗糙(建议将不胜感激?):
matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
lst = [y for row in matrix for y in row]
lst = [[lst[x+y*3] for x in range(3)] for y in range(4)]
print(lst)
结果:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
是的!在一个列表理解中:
nr, nc = 4, 3
ic = len(matrix[0])
lst = [[matrix[(r*nc+c)//ic][(r*nc+c)%ic] for c in range(nc)] for r in range(nr)]
print(*lst, sep="\n")
结果:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, 11, 12]
但你也可以拥有(nr, nc = 6, 2
)
[1, 2]
[3, 4]
[5, 6]
[7, 8]
[9, 10]
[11, 12]
这是你的问题,不是吗?
以下解决方案仅使用列表推导,不使用 zip:
m = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
flat = [l2 for l1 in m for l2 in l1]
output = [[flat.pop(0), flat.pop(0), flat.pop(0)] for x in range(4)]
print(output)
输出为:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
或者对于一个非常简单的衬垫:
print [m[0][:3],[m[0][3]]+m[1][0:2],m[1][2:]+[m[2][0]],m[2][1:]]