2013-09-21 43 views
7

trong sổ tay gấu trúc, có ví dụ này về lập chỉ mục:pandas python: tại sao bản đồ nhanh hơn?

In [653]: criterion = df2['a'].map(lambda x: x.startswith('t')) 
In [654]: df2[criterion] 

sau đó Wes đã viết:

**# equivalent but slower** 
In [655]: df2[[x.startswith('t') for x in df2['a']]] 

ai ở đây có thể giải thích một chút lý do tại sao phương pháp bản đồ là nhanh hơn? Đây có phải là một tính năng python hay đây là một tính năng gấu trúc?

+0

Tôi đặt cược bạn 'từ toán tử importcaller \\ df2 ['a']. Map (methodcaller (" startswith "," t "))' sẽ nhanh hơn đáng kể. – Veedrac

+0

@TimPietzcker; nó không sử dụng 'map' được xây dựng sẵn (mà sẽ chậm hơn trong trường hợp này). – Veedrac

+0

@Veedrac: Tôi hiểu; Tôi đã chỉ tự hỏi nơi tham số thứ hai để 'bản đồ' là :) –

Trả lời

17

Đối số về lý do tại sao một cách nhất định để làm việc trong Python "nên" nhanh hơn không thể được thực hiện quá nghiêm trọng, bởi vì bạn thường đo chi tiết triển khai có thể hoạt động khác nhau trong các tình huống nhất định. Kết quả là, khi mọi người đoán những gì nên được nhanh hơn, họ thường (thường là?) Sai. Ví dụ: tôi thấy rằng map thực sự có thể chậm hơn. Sử dụng mã thiết lập này:

import numpy as np, pandas as pd 
import random, string 

def make_test(num, width): 
    s = [''.join(random.sample(string.ascii_lowercase, width)) for i in range(num)] 
    df = pd.DataFrame({"a": s}) 
    return df 

Hãy so sánh thời gian họ làm để làm cho các đối tượng lập chỉ mục - cho dù một Series hoặc một list - và thời gian kết quả cần thiết để sử dụng đối tượng đó để chỉ mục vào DataFrame. Ví dụ, có thể tạo danh sách nhanh nhưng trước khi sử dụng nó làm chỉ mục, nó cần được chuyển đổi nội bộ thành một số Series hoặc ndarray hoặc thứ gì đó và do đó có thêm thời gian ở đó.

Đầu tiên, đối với một khung nhỏ:

>>> df = make_test(10, 10) 
>>> %timeit df['a'].map(lambda x: x.startswith('t')) 
10000 loops, best of 3: 85.8 µs per loop 
>>> %timeit [x.startswith('t') for x in df['a']] 
100000 loops, best of 3: 15.6 µs per loop 
>>> %timeit df['a'].str.startswith("t") 
10000 loops, best of 3: 118 µs per loop 
>>> %timeit df[df['a'].map(lambda x: x.startswith('t'))] 
1000 loops, best of 3: 304 µs per loop 
>>> %timeit df[[x.startswith('t') for x in df['a']]] 
10000 loops, best of 3: 194 µs per loop 
>>> %timeit df[df['a'].str.startswith("t")] 
1000 loops, best of 3: 348 µs per loop 

và trong trường hợp này listcomp là nhanh nhất. Điều đó không thực sự gây ngạc nhiên cho tôi quá nhiều, thành thật mà nói, bởi vì đi qua một lambda có thể chậm hơn so với việc sử dụng trực tiếp str.startswith nhưng thực sự khó đoán. 10 là đủ nhỏ, chúng tôi vẫn có thể đo lường những thứ như chi phí thiết lập cho Series; những gì xảy ra trong một khung lớn hơn?

>>> df = make_test(10**5, 10) 
>>> %timeit df['a'].map(lambda x: x.startswith('t')) 
10 loops, best of 3: 46.6 ms per loop 
>>> %timeit [x.startswith('t') for x in df['a']] 
10 loops, best of 3: 27.8 ms per loop 
>>> %timeit df['a'].str.startswith("t") 
10 loops, best of 3: 48.5 ms per loop 
>>> %timeit df[df['a'].map(lambda x: x.startswith('t'))] 
10 loops, best of 3: 47.1 ms per loop 
>>> %timeit df[[x.startswith('t') for x in df['a']]] 
10 loops, best of 3: 52.8 ms per loop 
>>> %timeit df[df['a'].str.startswith("t")] 
10 loops, best of 3: 49.6 ms per loop 

Và bây giờ có vẻ như map là chiến thắng khi được sử dụng làm chỉ mục, mặc dù sự khác biệt là biên. Nhưng không quá nhanh: nếu chúng ta chuyển listcomp theo cách thủ công thành array hoặc Series thì sao?

>>> %timeit df[np.array([x.startswith('t') for x in df['a']])] 
10 loops, best of 3: 40.7 ms per loop 
>>> %timeit df[pd.Series([x.startswith('t') for x in df['a']])] 
10 loops, best of 3: 37.5 ms per loop 

và giờ đây listcomp lại thắng!

Kết luận: ai biết? Nhưng đừng bao giờ tin bất cứ điều gì mà không cần kết quả timeit, và thậm chí sau đó bạn phải hỏi xem bạn có đang thử nghiệm những gì bạn nghĩ bạn đang có.

+0

bạn có thể gửi này như là một PR cho tài liệu: https://github.com/pydata/pandas/issues/3871, cố gắng tạo ra một phần mới – Jeff

+0

+ 1 Câu trả lời hay. –

+0

Đây có lẽ cũng là phần của tài liệu mà Wes nói rằng [startswith chậm hơn slicing] (http://stackoverflow.com/questions/13270888/why-is-startswith-slower-than-slicing)! –

Các vấn đề liên quan