簡單兩步,大幅提高python數據處理速度

在數據分析領域,最熱門的莫過於Python和R語言。其中,數據分析庫pandas是Python最經典的庫之一。它使用一個二維的DataFrame來表示表格式的數據,相比較於Numpy,Pandas可以存儲混合的數據結構,同時使用NaN來表示缺失的數據,而不用像Numpy一樣要手工處理缺失的數據,並且Pandas使用軸標籤來表示行和列。

一般來說,用pandas處理小於100兆的數據,性能不是問題。但當我們要處理大量數據時,如何優化DataFrame的操作就需要仔細斟酌了。

例子

我們想像一個例子,你要處理一個csv文件,你想要把數據清洗後輸出到另一個dataframe中,做後續處理。一個簡單的程序如下:

# -*- coding: utf-8 -*-import pandas as pdimport datetime as dtindf = pd.read_csv("600369.csv", index_col = False, encoding = "gb2312")output_index_num = 8000outdf = pd.DataFrame(columns = indf.columns)times1 = dt.datetime.now()for outindex in range(0, output_index_num): # have some fun here outdf = outdf.append(indf.loc[int(len(indf) * outindex / output_index_num)],ignore_index=True)times2 = dt.datetime.now()print("Time spent: "+ str(times2-times1))

十分簡單,不是嗎?我們僅僅是把輸入的dataframe(indf)擴展到輸出outdf中去了。我們看一下運行時間:

Time spent: 0:01:58.830000

2分鐘處理8000個單元,還行?這個遠遠不夠,想像一下我們有10萬個這樣的csv要處理怎麼辦?那需要278天!怎麼提高效能呢?

第一步

Dataframe的append效能很低,如果我們知道未來表的條目,預先分配好空間,將來直接向裡面填入內容會快不少,我們來修改一下程序:

# -*- coding: utf-8 -*-import pandas as pdimport datetime as dtindf = pd.read_csv("600369.csv", index_col = False, encoding = "gb2312")#outdf = pd.DataFrame(columns = indf.columns)output_index_num = 8000outdf = pd.DataFrame(pd.np.empty((output_index_num, len(indf.columns))) * pd.np.nan, columns = indf.columns)times1 = dt.datetime.now()for outindex in range(0, output_index_num): # have some fun here outdf.loc[outindex] = indf.loc[int(len(indf) * outindex / output_index_num)]times2 = dt.datetime.now()print("Time spent: "+ str(times2-times1))

結果好了不少:

Time spent: 0:01:27.636000

少了半分鐘,是不是就這樣了呢?還遠遠不夠。

第二步

Dataframe檢索每一行有好幾個操作:loc,iloc,ix,at。它們效能有什麼差別嗎?我們來試一下:

%timeit outdf.loc[0] = indf.loc[0]100 loops, best of 3: 11.7 ms per loop%timeit outdf.iloc[0] = indf.iloc[0]100 loops, best of 3: 11.4 ms per loop %timeit outdf.ix[0] = indf.ix[0]100 loops, best of 3: 11.6 ms per loop%timeit outdf.at[0,"time"] = indf.at[0,"time"]10000 loops, best of 3: 25.3 μs per loop

loc,iloc,ix看起來速度都差不多,at可是快了將近1000倍啊,可惜的是at只能選擇某個單元而不是整行。沒關係,我們來改一下程序:

# -*- coding: utf-8 -*-import pandas as pdimport datetime as dtindf = pd.read_csv("600369.csv", index_col = False, encoding = "gb2312")#outdf = pd.DataFrame(columns = indf.columns)output_index_num = 8000outdf = pd.DataFrame(pd.np.empty((output_index_num, len(indf.columns))) * pd.np.nan, columns = indf.columns)times1 = dt.datetime.now()outdf.loc[0] = indf.loc[0]for outindex in range(0, output_index_num): # have some fun here for column in indf.columns: outdf.at[outindex,column] = indf.at[int(len(indf) * outindex / output_index_num),column]times2 = dt.datetime.now()print("Time spent: "+ str(times2-times1))

結果大為改善:

Time spent: 0:00:07.850000

只有7秒,比以前快了12倍更比開始的時候快了15倍多。

結論

如果目標數據集行數確定,我們應該先分配出來,在一項項填入而不是append,要做填空題而不是做補充題;盡量選擇用at/iat而是不是方便的loc/iloc或者ix。

最後給大家一個思考題,為什麼我在最後的程序裡面加了個

outdf.loc[0] = indf.loc[0]

不加會怎麼樣?

人生苦短,我用Python;發財要早,我要量化!歡迎關注本專欄。


推薦閱讀:

大數據那些事(21):腰斬的大象之Hortonworks
數據分析學習計劃
這麼輕鬆學會桑基圖製作,小夥伴都驚呆了!
數據平台維度模型設計十個技巧

TAG:Python | 编程 | 大数据 |