Danh sách 2D phẳng Python

Thông thường, khi bạn viết mã, bạn sẽ cần giảm Danh sách lồng nhau hoặc mảng Numpy thành 1D. Nói cách khác, bạn muốn làm phẳng danh sách của mình. May mắn thay, có nhiều cách để làm điều này trong Python. Nhưng cái nào bạn nên sử dụng?

1. Danh sách Python

Dưới đây là một ví dụ về Danh sách 2D

list_2D = [[1,2,3],[4],[5,6],[7,8,9]]
Đã sao chép vào khay nhớ tạm.

và chúng tôi muốn làm phẳng nó thành

list_1D = [1,2,3,4,5,6,7,8,9]
Đã sao chép vào khay nhớ tạm.

1. 1 Danh sách Hiểu

Đây là cách Pythonic nhất. Mặc dù việc hiểu danh sách lồng nhau không dễ nhìn nhưng đó là một phương pháp thực sự đơn giản. Đối với những người hiểu hiểu danh sách, bạn có thể bỏ qua phần giải thích

list_1D = [item for sub_list in list_2D for item in sub_list]
Đã sao chép vào khay nhớ tạm.

Nếu chúng ta viết mã này bằng cách sử dụng các vòng lặp lồng nhau, chúng ta sẽ viết nó như thế này

list_1D = []
for sub_list in list_2D:
    for item in sub_list:
        list_1D.append[item]
Đã sao chép vào khay nhớ tạm.

Để chuyển đổi mã trên thành một lớp lót duy nhất, bạn nên chia mã thành 3 phần

Hình 1. hiểu danh sách
  • Vòng ngoài
  • Vòng trong
  • biểu thức đầu ra

Bạn bắt đầu hiểu danh sách bằng cách mở ngoặc vuông và viết lại các bước trên như sau

  1. Xác định biểu thức đầu ra
  2. Xác định vòng lặp bên ngoài
  3. Xác định vòng lặp bên trong

1. 2

Mặc dù phương thức này được thiết kế để tính tổng các giá trị số, nhưng nó cũng hoạt động với các danh sách nối. Đó là một giải pháp thực sự đơn giản nhưng không phải là một giải pháp lý tưởng vì nó sẽ không hoạt động tốt khi có một danh sách lớn hơn. Nhìn chung, đó là một phương pháp không hiệu quả nhưng là một cách hack nhanh. Chúng ta sẽ xem kết quả benchmark ở cuối bài viết này.

list_1D = [item for sub_list in list_2D for item in sub_list]
2 là một chức năng tích hợp có thể lặp lại và tính tổng các mục từ trái sang phải

Cảnh báo Không nên sử dụng phương pháp này trong sản xuất

list_1D = sum[list_2D, []]
Đã sao chép vào khay nhớ tạm.

Phương thức này nhận 2 đối số. Nó tính tổng các mục của iterable được truyền dưới dạng đối số đầu tiên và nó sử dụng đối số thứ hai làm giá trị ban đầu của tổng. Đây là một đối số tùy chọn, nhưng vì chúng tôi đang cố tính tổng các danh sách lồng nhau, nên chúng tôi muốn bắt đầu nối với một danh sách trống. Vì vậy, chức năng trên tương đương với điều này

list_1D = [] + [1,2,3] + [4] + [5,6] + [7,8,9]
Đã sao chép vào khay nhớ tạm.

Bạn có thể tưởng tượng điều gì sẽ xảy ra nếu chúng tôi có nhiều danh sách phụ hơn. Điều này thực sự không hiệu quả và không lý tưởng cho các ứng dụng sản xuất. Một phép ẩn dụ hay để mô tả điều này là thuật toán của họa sĩ Shlemiel được viết bởi

Ngoài ra, nếu bạn muốn kiểm tra độ phức tạp và toán học của phương pháp này thì đây là một bài viết hay về Cách không làm phẳng danh sách các danh sách trong Python

Như đã nêu trên Tài liệu Python chính thức, có một phương pháp thay thế là

list_1D = [item for sub_list in list_2D for item in sub_list]
3

1. 3

Dưới đây, bạn có thể tìm thấy mã do Tài liệu chính thức của Python cung cấp về cách sử dụng hàm chain[]

def chain[*iterables]:
    # chain['ABC', 'DEF'] --> A B C D E F
    for it in iterables:
        for element in it:
            yield element
Đã sao chép vào khay nhớ tạm.

Nó lấy nhiều lần lặp làm đầu vào và trả về từng phần tử của từng lần lặp cho đến khi hết tất cả các lần lặp

from itertools import chain
# [1, 2, 3, 4]
list[chain[[1,2], [3,4]]
Đã sao chép vào khay nhớ tạm.

Trong trường hợp của chúng tôi, chúng tôi chỉ có một lần lặp là danh sách lồng nhau. May mắn cho chúng tôi, có một phương pháp khác được thiết kế cho phương pháp đó có tên là

list_1D = [item for sub_list in list_2D for item in sub_list]
4

list_1D = [1,2,3,4,5,6,7,8,9]
1 Đã sao chép vào khay nhớ tạm.

Và vì hàm này trả về một đối tượng chuỗi, chúng tôi đảm bảo rằng chúng tôi sử dụng hàm tạo danh sách để trả về khả năng lặp làm phẳng dưới dạng danh sách

list_1D = [1,2,3,4,5,6,7,8,9]
2 Đã sao chép vào khay nhớ tạm.

1. 4 công cụ chức năng & toán tử

list_1D = [1,2,3,4,5,6,7,8,9]
0 Đã sao chép vào khay nhớ tạm.

Funcools là một thư viện được sử dụng chủ yếu cho các hàm bậc cao hơn, một thuật ngữ được sử dụng trong Lập trình hàm. Sử dụng hàm

list_1D = [item for sub_list in list_2D for item in sub_list]
5, chúng tôi muốn chuyển một hàm làm đối số đầu tiên để giảm đối số có thể lặp lại [được chuyển làm đối số thứ hai] thành một giá trị. Đối số thứ ba là trình khởi tạo và đây là tùy chọn

Hàm được sử dụng là toán tử

list_1D = [item for sub_list in list_2D for item in sub_list]
6 về cơ bản có nghĩa là.
list_1D = [item for sub_list in list_2D for item in sub_list]
7

list_1D = [1,2,3,4,5,6,7,8,9]
1 Đã sao chép vào khay nhớ tạm.

Vì vậy, cuối cùng, điều này tương đương với

list_1D = [] + [1,2,3] + [4] + [5,6] + [7,8,9]
Đã sao chép vào khay nhớ tạm.

Đây là kết quả tương tự như phương pháp trên phần 1. 2 tuy nhiên, điều này hiệu quả hơn nhiều

2. Mảng Numpy

Mảng Numpy là một trong những cấu trúc dữ liệu hiệu quả nhất trong Python. Thư viện numpy cung cấp rất nhiều hàm khác nhau làm phẳng mảng

list_1D = [1,2,3,4,5,6,7,8,9]
3 Đã sao chép vào khay nhớ tạm.

2. 1 căn hộ

list_1D = [1,2,3,4,5,6,7,8,9]
4 Đã sao chép vào khay nhớ tạm.

2. 2 làm phẳng[]

list_1D = [1,2,3,4,5,6,7,8,9]
5 Đã sao chép vào khay nhớ tạm.

2. 3 cuộc phiêu lưu[]

list_1D = [1,2,3,4,5,6,7,8,9]
6 Đã sao chép vào khay nhớ tạm.

2. 4 định hình lại [-1]

list_1D = [1,2,3,4,5,6,7,8,9]
7 Đã sao chép vào khay nhớ tạm.

2. 5 nối []

list_1D = [1,2,3,4,5,6,7,8,9]
8 Đã sao chép vào khay nhớ tạm.

2. 6 Phương pháp so sánh

Tất cả các phương thức đều thực hiện cùng một công việc, điều khác biệt duy nhất là tốc độ và bộ nhớ. Một số trong số chúng tiêu tốn nhiều bộ nhớ hơn vì chúng tạo một bản sao mới và một số khác chỉ trả về dạng xem hoặc trình vòng lặp

Phương thức

list_1D = [item for sub_list in list_2D for item in sub_list]
8 sẽ luôn trả về một bản sao trong khi
list_1D = [item for sub_list in list_2D for item in sub_list]
9 thì không. Không phải lúc nào cũng vậy, nó sẽ chỉ trả lại một bản sao nếu cần thiết. Nó chỉ trả về một khung nhìn của mảng, do đó làm cho nó trở thành một cách tiếp cận nhanh hơn. Chúng ta có thể kiểm tra xem hàm có tạo một bản sao hay không bằng cách sử dụng phương thức cơ sở, phương thức này trả về một giá trị nếu bộ nhớ là từ một số đối tượng khác, nếu không thì Không

list_1D = [1,2,3,4,5,6,7,8,9]
9 Đã sao chép vào khay nhớ tạm.

Tất cả các phương thức trả về cùng một đầu ra ngoại trừ

list_1D = []
for sub_list in list_2D:
    for item in sub_list:
        list_1D.append[item]
0 trả về một trình vòng lặp

  • Các phương thức
    list_1D = [item for sub_list in list_2D for item in sub_list]
    8 và
    list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    2 tạo một bản sao mới, do đó tiêu tốn nhiều bộ nhớ hơn
  • list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    3 trả về cùng một đầu ra như
    list_1D = [item for sub_list in list_2D for item in sub_list]
    8 nhưng nó cũng có thể được sử dụng trong ngữ cảnh khác.
    list_1D = [item for sub_list in list_2D for item in sub_list]
    8 là một phương thức từ lớp
    list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    6 trong khi
    list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    7 đến từ lớp
    list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    8. Ví dụ: bạn có thể nối nhiều mảng có cùng hình dạng
3. điểm chuẩn

Để kiểm tra tốc độ của từng hàm, tôi đã thiết kế một thử nghiệm trong đó tôi tạo một danh sách lồng nhau, sau đó chạy từng hàm với nó và đếm thời gian của nó. Thử nghiệm này sẽ được lặp lại 100 lần và tôi lưu trữ thời gian trung bình cho từng chức năng. Nếu bạn để ý, danh sách lồng nhau sẽ tăng kích thước của nó theo từng bước để chúng ta có thể so sánh độ phức tạp của từng chức năng theo thời gian

list_1D = [item for sub_list in list_2D for item in sub_list]
0 Đã sao chép vào khay nhớ tạm.

Bạn có thể tìm thấy mã trên collab này

3. 1 Phương pháp tổng thể nhanh nhất

list_1D = [item for sub_list in list_2D for item in sub_list]
1 Đã sao chép vào khay nhớ tạm.

  • 100 thí nghiệm
  • Mỗi danh sách có 10 số
  • Kích thước danh sách lồng nhau ban đầu là 10
  • Mỗi bước tăng kích thước danh sách lồng nhau lên gấp 10 lần
Hình 2. Thời gian chức năng để làm phẳng một danh sách lồng nhau

Như bạn có thể thấy từ các ô trên, _sum là ô tồi tệ nhất và không bao giờ được sử dụng. Khả năng hiểu danh sách cũng chậm. Nhưng hãy loại bỏ chúng và xem xét kỹ hơn các phương pháp khác

Hình 3. Thời gian chức năng để làm phẳng danh sách lồng nhau mà không có phương thức hiểu tổng và danh sách

Nhìn chung, tất cả các phương pháp dường như thực sự nhanh nhưng những phương pháp nhanh nhất là

  • list_1D = []
    for sub_list in list_2D:
        for item in sub_list:
            list_1D.append[item]
    9
  • list_1D = sum[list_2D, []]
    0
  • list_1D = sum[list_2D, []]
    1

3. 2 Độ phức tạp theo thời gian trong khi chúng tôi tăng kích thước

1000 thí nghiệm

  • Mỗi danh sách có 10 số
  • Kích thước danh sách lồng nhau ban đầu là 10
  • Mỗi bước tăng kích thước danh sách lồng nhau lên gấp 10 lần
hinh 4. Độ phức tạp về thời gian trong khi chúng tôi tăng kích thước của danh sách

Hãy tách biệt các phương thức không gọn gàng trong giây lát và kiểm tra kết quả. Như chúng ta có thể thấy, trong khi chúng ta tăng kích thước của danh sách, hàm _sum trở nên rất chậm. Chúng ta có thể thấy rằng thậm chí chỉ mất gần 1 giây khi chúng ta đạt đến 1000 chiều. Từ đường cong, chúng ta có thể biết rằng độ phức tạp thời gian là O[n] bình phương

Hình 5. Độ phức tạp về thời gian trong khi chúng tôi tăng kích thước của danh sách

Khi chúng ta xóa hàm _sum và lặp lại các thử nghiệm, chúng ta có thể biết rằng list_comprehension và functools_operator là O[n], nghĩa là thời gian để hoàn thành hàm phụ thuộc vào kích thước danh sách. Vì vậy, nó liên quan tuyến tính với số lượng kích thước

Hình 6. Độ phức tạp về thời gian bằng các phương pháp nhanh nhất

Khi chúng tôi lặp lại cùng một thử nghiệm với các phương pháp nhanh nhất, chúng tôi có thể xác định rằng độ phức tạp thời gian của chúng là O[1], nghĩa là số lượng thứ nguyên sẽ không ảnh hưởng đến hiệu suất của chúng

4. Phần kết luận

Nhìn chung, tôi sẽ kết luận rằng tôi sẽ luôn sử dụng

list_1D = sum[list_2D, []]
2 khi làm việc với Danh sách Python. Đảm bảo rằng bạn không bao giờ sử dụng
list_1D = sum[list_2D, []]
3 và
list_1D = sum[list_2D, []]
4 vì chúng thực sự không hiệu quả

Danh sách Python có thể là 2 chiều không?

Python cung cấp nhiều cách để tạo danh sách/mảng 2 chiều . Tuy nhiên, người ta phải biết sự khác biệt giữa những cách này vì chúng có thể tạo ra các phức tạp trong mã mà rất khó để tìm ra.

Làm cách nào để chuyển đổi danh sách 2D thành danh sách đơn trong Python?

Làm phẳng danh sách gồm các danh sách đòi hỏi phải chuyển đổi danh sách 2D thành danh sách 1D bằng cách bỏ lồng từng mục danh sách được lưu trữ trong danh sách . e. , chuyển đổi [[1, 2, 3], [4, 5, 6], [7, 8, 9]] thành [1, 2, 3, 4, 5, 6, 7, 8, 9].

Làm cách nào để chuyển đổi mảng 2D thành danh sách trong Python?

Để chuyển đổi từ mảng Numpy thành danh sách, chúng ta chỉ cần nhập tên của mảng Numpy 2D, sau đó gọi phương thức Numpy tolist[] which produced a Python list as an output.

Chủ Đề