Làm thế nào để chuyển đổi danh sách các bộ giá trị thành nhiều danh sách?


107

Giả sử tôi có một danh sách các bộ giá trị và tôi muốn chuyển đổi thành nhiều danh sách.

Ví dụ, danh sách các bộ giá trị là

[(1,2),(3,4),(5,6),]

Có bất kỳ hàm tích hợp nào trong Python có thể chuyển đổi nó thành:

[1,3,5],[2,4,6]

Đây có thể là một chương trình đơn giản. Nhưng tôi chỉ tò mò về sự tồn tại của hàm tích hợp như vậy trong Python.

Câu trả lời:


161

Chức năng tích hợp zip()gần như sẽ làm những gì bạn muốn:

>>> zip(*[(1, 2), (3, 4), (5, 6)])
[(1, 3, 5), (2, 4, 6)]

Sự khác biệt duy nhất là bạn nhận được bộ giá trị thay vì danh sách. Bạn có thể chuyển đổi chúng thành danh sách bằng cách sử dụng

map(list, zip(*[(1, 2), (3, 4), (5, 6)]))

40

Từ tài liệu về trăn :

zip () kết hợp với toán tử * có thể được sử dụng để giải nén danh sách:

Ví dụ cụ thể:

>>> zip((1,3,5),(2,4,6))
[(1, 2), (3, 4), (5, 6)]
>>> zip(*[(1, 2), (3, 4), (5, 6)])
[(1, 3, 5), (2, 4, 6)]

Hoặc, nếu bạn thực sự muốn danh sách:

>>> map(list, zip(*[(1, 2), (3, 4), (5, 6)]))
[[1, 3, 5], [2, 4, 6]]


4

Franklsf95 biểu diễn trong câu trả lời của mình và chọn list.append() , nhưng chúng không tối ưu.

Thêm toàn bộ danh sách, tôi đã kết thúc với những điều sau:

def t1(zs):
    xs, ys = zip(*zs)
    return xs, ys

def t2(zs):
    xs, ys = [], []
    for x, y in zs:
        xs.append(x)
        ys.append(y)
    return xs, ys

def t3(zs):
    xs, ys = [x for x, y in zs], [y for x, y in zs]
    return xs, ys

if __name__ == '__main__':
    from timeit import timeit
    setup_string='''\
N = 2000000
xs = list(range(1, N))
ys = list(range(N+1, N*2))
zs = list(zip(xs, ys))
from __main__ import t1, t2, t3
'''
    print(f'zip:\t\t{timeit('t1(zs)', setup=setup_string, number=1000)}')
    print(f'append:\t\t{timeit('t2(zs)', setup=setup_string, number=1000)}')
    print(f'list comp:\t{timeit('t3(zs)', setup=setup_string, number=1000)}')

Điều này cho kết quả:

zip:            122.11585397789766
append:         356.44876132614047
list comp:      144.637765085659

Vì vậy, nếu bạn đang theo đuổi hiệu suất, bạn có thể nên sử dụng zip()mặc dù khả năng hiểu danh sách không quá xa. Hiệu suất của appendthực sự là khá kém so với.


1

Mặc dù có *zipnhiều Pythonic hơn, nhưng đoạn mã sau có hiệu suất tốt hơn nhiều:

xs, ys = [], []
for x, y in zs:
    xs.append(x)
    ys.append(y)

Ngoài ra, khi danh sách gốc zstrống,*zip sẽ tăng lên, nhưng mã này có thể xử lý đúng cách.

Tôi vừa chạy một thử nghiệm nhanh và đây là kết quả:

Using *zip:     1.54701614s
Using append:   0.52687597s

Chạy nó nhiều lần, appendnhanh hơn gấp 3 - 4 lần zip! Kịch bản thử nghiệm ở đây:

#!/usr/bin/env python3
import time

N = 2000000
xs = list(range(1, N))
ys = list(range(N+1, N*2))
zs = list(zip(xs, ys))

t1 = time.time()

xs_, ys_ = zip(*zs)
print(len(xs_), len(ys_))

t2 = time.time()

xs_, ys_ = [], []
for x, y in zs:
    xs_.append(x)
    ys_.append(y)
print(len(xs_), len(ys_))

t3 = time.time()

print('Using *zip:\t{:.8f}s'.format(t2 - t1))
print('Using append:\t{:.8f}s'.format(t3 - t2))

Phiên bản Python của tôi:

Python 3.6.3 (default, Oct 24 2017, 12:18:40)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

1

Ngoài câu trả lời của Claudiu, bạn có thể sử dụng:

>>>a, b = map(list, zip(*[(1, 2), (3, 4), (5, 6)]))
>>>a
[1,3,5]
>>>b
[2,4,6]

Biên tập theo @Peyman mohseni kiasari


1
Không! nó sẽ cung cấp cho bạn (1, 3, 5)(2, 4, 6)không liệt kê. bạn nên sử dụngmap(list, zip(*[(1, 2), (3, 4), (5, 6)]))
Peyman

0

Thêm vào câu trả lời của Claudiu và Claudiu và vì bản đồ cần được nhập từ itertools trong python 3, bạn cũng sử dụng danh sách hiểu như:

[[*x] for x in zip(*[(1,2),(3,4),(5,6)])]
>>> [[1, 3, 5], [2, 4, 6]]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.