2015/06/19

Python筆記:operator.itemgetter

若有底下這種資料,是個串列,含有多個字典。
data = [
    {'age': 31, 'city': 'taipei', 'name': 'amy'},
    {'age': 71, 'city': 'tokyo', 'name': 'john'},
    {'age': 16, 'city': 'london', 'name': 'zoe'},
    {'age': 16, 'city': 'rio', 'name': 'cathy'},
    {'age': 48, 'city': 'frankfurt', 'name': 'david'}]

那麼,呼叫內建函式sorted時,能以參數key傳入lambda,指定要根據哪個元素進行排序。

根據age年齡排序:
print(sorted(data, key=lambda x: x['age']))

先排age年齡、再排name名字:
print(sorted(data, key=lambda x: (x['age'], x['name'])))

不過也能運用operator.itemgetter取出想排序的元素,而且聽說速度更快,試試看吧。
import random
import string
from operator import itemgetter

先寫個會亂數產生字串的函式,預設回傳長度介於3到10的字串,都是小寫字母。
def random_str(size_lower=3, size_upper=10):
    size = random.randint(size_lower, size_upper)
    return ''.join(random.choice(string.ascii_lowercase) for _ in range(size))

然後撰寫亂數產生資料的函式,參數size代表想要幾個元素(字典)。
def random_data(size):
    result = []
    for _ in range(size):
        d = {}
        d['age'] = random.randint(1, 99)
        d['name'] = random_str()
        d['city'] = random_str()
        result.append(d)
    return result

產生資料,此處寫著DATA_SIZE為1000,之後測試時可修改。
DATA_SIZE = 1000
data = random_data(DATA_SIZE)

受測函式一,使用lambda,分別進行幾次排序。
def test_lambda():
    data0 = sorted(data, key=lambda x: x['age'])
    data1 = sorted(data, key=lambda x: x['name'])
    data2 = sorted(data, key=lambda x: x['city'])
    data3 = sorted(data, key=lambda x: (x['age'], x['name']))
    data4 = sorted(data, key=lambda x: (x['age'], x['city']))

受測函式二,使用itemgetter,分別進行幾次排序。
def test_itemgetter():
    data0 = sorted(data, key=itemgetter('age'))
    data1 = sorted(data, key=itemgetter('name'))
    data2 = sorted(data, key=itemgetter('city'))
    data3 = sorted(data, key=itemgetter('age', 'name'))
    data4 = sorted(data, key=itemgetter('age', 'city'))

測量所需時間,此處TEST_COUNT為100,代表跑100次,可修改。
if __name__ == '__main__':
    from timeit import Timer
    TEST_COUNT = 100
    t_lambda = Timer('test_lambda()', 'from __main__ import test_lambda')
    print('lambda: ', t_lambda.timeit(TEST_COUNT))
    t_itemgetter = Timer('test_itemgetter()', 'from __main__ import test_itemgetter')
    print('itemgetter: ', t_itemgetter.timeit(TEST_COUNT))

來測試看看吧,環境是WinXP、Cygwin、Python 2.7.10:
DATA_SIZE 1000,TEST_COUNT 100
lambda 0.734375
itemgetter 0.71875

DATA_SIZE 1000,TEST_COUNT 1000
lambda 7.46875
itemgetter 6.25

DATA_SIZE 1000,TEST_COUNT 10000
lambda 77.421875
itemgetter 62.921875

加大DATA_SIZE試試看吧:

DATA_SIZE 10000,TEST_COUNT 100
lambda 12.203125
itemgetter 10.953125

DATA_SIZE 10000,TEST_COUNT 1000
lambda 123.90625
itemgetter 110.375

綜合而言,itemgetter比較快,所需時間大約是lambda的80%~97%。

我也用Python 3.4.3測試,情況差不多,不過實際測得的所需時間,都比Python 2.7.10版的時候要久,也就是說Python 3比2慢啦。

No comments:

Post a Comment