2017-02-06 15 views
18

Từ this post Tôi đã học được rằng bạn có thể nối dữ liệu với:CONCATENATE tuples sử dụng sum()

>>> tuples = (('hello',), ('these', 'are'), ('my', 'tuples!')) 
>>> sum(tuples,()) 
('hello', 'these', 'are', 'my', 'tuples!') 

nào trông khá đẹp. Nhưng tại sao điều này lại hiệu quả? Và, điều này là tối ưu, hoặc có cái gì đó từ itertools mà sẽ là thích hợp hơn cho cấu trúc này?

+0

Tại sao không nên cho nó hoạt động? Nó chỉ thêm các bộ dữ liệu với nhau, nhưng nó không đặc biệt hiệu quả. Hãy xem [itertools.chain] (https://docs.python.org/3/library/itertools.html#itertools.chain). Ví dụ: 'tuple (chuỗi (* tuples))' –

+0

@ PM2Ring. Tránh sử dụng 'chuỗi' như thế vì nó thậm chí còn kém hiệu quả hơn' tổng hợp' (trừ khi tập hợp các bộ dữ liệu là rất nhỏ). Thay vào đó hãy sử dụng 'chain.from_iterable'. – ekhumoro

+0

@ekhumoro Rất tiếc! Có, chain.from_iterable là tốt hơn. Và như câu trả lời của Boud cho thấy, nó thực sự chậm hơn tổng cho các bộ sưu tập nhỏ của bộ dữ liệu. –

Trả lời

19

các concatenates điều hành bổ sung bản ghi trong python:

('a', 'b')+('c', 'd') 
Out[34]: ('a', 'b', 'c', 'd') 

Từ docstring của sum:

Return tổng của một giá trị 'start' (mặc định: 0) cộng với một iterable của số

Điều đó có nghĩa là sum không bắt đầu bằng yếu tố đầu tiên của bạn có thể lặp lại, nhưng thay vì với giá trị ban đầu được chuyển qua đối số start=.

Theo mặc định sum được sử dụng với số, do đó giá trị bắt đầu mặc định là 0. Vì vậy, việc tổng hợp các bộ dữ liệu có thể lặp lại cần phải bắt đầu bằng một bộ trống. () là một tuple trống:

type(()) 
Out[36]: tuple 

Do đó nối làm việc.

Theo hiệu suất, đây là một so sánh:

%timeit sum(tuples,()) 
The slowest run took 9.40 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 285 ns per loop 


%timeit tuple(it.chain.from_iterable(tuples)) 
The slowest run took 5.00 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 625 ns per loop 

Bây giờ với t2 của 10000 kích thước:

%timeit sum(t2,()) 
10 loops, best of 3: 188 ms per loop 

%timeit tuple(it.chain.from_iterable(t2)) 
1000 loops, best of 3: 526 µs per loop 

Vì vậy, nếu danh sách các tuples là nhỏ, bạn đừng bận tâm. Nếu kích thước trung bình hoặc lớn hơn, bạn nên sử dụng itertools.

+0

Thời gian thú vị. Bạn đã sử dụng phiên bản Python nào? –

+0

@ PM2Ring 3,5 64bits – Boud

+0

'best of 3' => vui lòng tham khảo% tài liệu timeit trong ipython – Boud

5

Đó là thông minh và tôi phải cười vì sự giúp đỡ rõ ràng cấm dây, nhưng nó hoạt động

sum(...) 
    sum(iterable[, start]) -> value 

    Return the sum of an iterable of numbers (NOT strings) plus the value 
    of parameter 'start' (which defaults to 0). When the iterable is 
    empty, return start. 

Bạn có thể thêm các bộ để có được một mới, tuple lớn hơn. Và kể từ khi bạn đưa ra một tuple như là một giá trị bắt đầu, việc bổ sung hoạt động.

+0

Trong ví dụ này,' sum' chưa tổng hợp chuỗi: không có hai chuỗi nào được tách biệt trong đầu vào đã được tham gia đây. (Ví dụ: không có cách nào để biến 'hello' và' world' thành 'helloworld' bằng cách sử dụng' sum'.) – ShreevatsaR

+1

IMO những gì Python làm chỉ là ngu xuẩn. Sum sẽ có thể tổng hợp bất kỳ thứ gì hỗ trợ toán tử '+'. Strings làm. Rõ ràng không cho phép trường hợp cụ thể cho các chuỗi trong tên hiệu suất và quy ước tốt (trong khi python có nhiều antipattern khác không được phép) chỉ là thiết kế không tốt – progo

+0

@ShreevatsaR tôi khá ý thức về điều đó. Sự trợ giúp đề cập đến các chuỗi nhưng tôi tiếp tục nói rằng điều này thực sự là thêm các bộ dữ liệu. Tôi chỉ nghĩ rằng nó là thú vị và giả định mọi người có thể đọc. – tdelaney

0

Chỉ cần để bổ sung cho câu trả lời chấp nhận với một số tiêu chuẩn hơn:

import functools, operator, itertools 
import numpy as np 
N = 10000 
M = 2 

ll = np.random.random((N, M)).tolist() 

Lựa chọn 1:

%timeit functools.reduce(operator.add, ll) 
359 ms ± 6.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

Phương án 2:

%timeit functools.reduce(lambda x, y: x + y, ll) 
327 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

Lựa chọn 3:

%timeit sum(ll,()) 
324 ms ± 2.28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

Lựa chọn 4:

%timeit itertools.chain(*ll) 
70.3 µs ± 249 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 

Lựa chọn 5:

%timeit itertools.chain.from_iterable(ll) 
254 ns ± 3.61 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)