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 PythonDướ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
- 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
- Xác định biểu thức đầu ra
- Xác định vòng lặp bên ngoài
- 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ảiCả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]
31. 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]
4list_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ọnHà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]
7list_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ônglist_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
8 vàlist_1D = [item for sub_list in list_2D for item in sub_list]
2 tạo một bản sao mới, do đó tiêu tốn nhiều bộ nhớ hơnlist_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 = [] for sub_list in list_2D: for item in sub_list: list_1D.append[item]
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ớplist_1D = [item for sub_list in list_2D for item in sub_list]
6 trong khilist_1D = [] for sub_list in list_2D: for item in sub_list: list_1D.append[item]
7 đến từ lớplist_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ạnglist_1D = [] for sub_list in list_2D: for item in sub_list: list_1D.append[item]
Để 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
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
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à
9list_1D = [] for sub_list in list_2D: for item in sub_list: list_1D.append[item]
0list_1D = sum[list_2D, []]
1list_1D = sum[list_2D, []]
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
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
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
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ậnNhì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ả