<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Pandas 處理資料的效率還是很優秀的,相對於大規模的資料集只要掌握好正確的方法,就能讓在資料處理時間上節省很多很多的時間。
Pandas 是建立在 NumPy 陣列結構之上的,許多操作都是在 C 中執行的,要麼通過 NumPy,要麼通過 Pandas 自己的 Python 擴充套件模組庫,這些模組用 Cython 編寫並編譯為 C。理論上來說處理速度應該是很快的。
那麼為什麼同樣一份資料由2個人處理,在裝置相同的情況下處理時間會出現天差地別呢?
需要明確的是,這不是關於如何過度優化 Pandas 程式碼的指南。如果使用得當 Pandas 已經構建為可以快速執行。此外優化和編寫乾淨的程式碼之間存在很大差異。
這是以 Python 方式使用 Pandas 以充分利用其強大且易於使用的內建功能的指南。
此範例的目標是應用分時能源關稅來計算一年的能源消耗總成本。 也就是說,在一天中的不同時間,電價會有所不同,因此任務是將每小時消耗的電量乘以消耗該小時的正確價格。
從一個包含兩列的 CSV 檔案中讀取資料,一列用於日期加時間,另一列用於以千瓦時 (kWh) 為單位消耗的電能。
import pandas as pd df = pd.read_csv('資料科學必備Pandas實操資料處理加速技巧彙總/demand_profile.csv') df.head() date_time energy_kwh 0 1/1/13 0:00 0.586 1 1/1/13 1:00 0.580 2 1/1/13 2:00 0.572 3 1/1/13 3:00 0.596 4 1/1/13 4:00 0.592
乍一看這看起來不錯,但有一個小問題。 Pandas 和 NumPy 有一個 dtypes(資料型別)的概念。 如果未指定任何引數,則 date_time 將採用 object dtype。
df.dtypes date_time object energy_kwh float64 dtype: object type(df.iat[0, 0]) str
object 不僅是 str 的容器,而且是任何不能完全適合一種資料型別的列的容器。將日期作為字串處理會既費力又低效(這也會導致記憶體效率低下)。為了處理時間序列資料,需要將 date_time 列格式化為日期時間物件陣列( Timestamp)。
df['date_time'] = pd.to_datetime(df['date_time']) df['date_time'].dtype datetime64[ns]
現在有一個名為 df 的 DataFrame,有兩列和一個用於參照行的數位索引。
df.head() date_time energy_kwh 0 2013-01-01 00:00:00 0.586 1 2013-01-01 01:00:00 0.580 2 2013-01-01 02:00:00 0.572 3 2013-01-01 03:00:00 0.596 4 2013-01-01 04:00:00 0.592
使用 Jupyter 自帶的 %%time 計時裝飾器進行測試。
def convert(df, column_name): return pd.to_datetime(df[column_name]) %%time df['date_time'] = convert(df, 'date_time') Wall time: 663 ms def convert_with_format(df, column_name): return pd.to_datetime(df[column_name],format='%d/%m/%y %H:%M') %%time df['date_time'] = convert(df, 'date_time') Wall time: 1.99 ms
處理效率提高將近350倍。如果在處理大規模資料的情況下,處理資料的時間會無限的放大。
既然日期和時間格式處理完畢,就可以著手計算電費了。成本因小時而異,因此需要有條件地將成本因素應用於一天中的每個小時。
在此範例中,使用時間成本將定義成三個部分。
data_type = { # 高峰 "Peak":{"Cents per kWh":28,"Time Range":"17:00 to 24:00"}, # 正常時段 "Shoulder":{"Cents per kWh":20,"Time Range":"7:00 to 17:00"}, # 非高峰 "Off-Peak":{"Cents per kWh":12,"Time Range":"0:00 to 7:00"}, }
如果價格是一天中每小時每千瓦時 28 美分。
df['cost_cents'] = df['energy_kwh'] * 28
date_time energy_kwh cost_cents 0 2013-01-01 00:00:00 0.586 16.408 1 2013-01-01 01:00:00 0.580 16.240 2 2013-01-01 02:00:00 0.572 16.016 3 2013-01-01 03:00:00 0.596 16.688 4 2013-01-01 04:00:00 0.592 16.576 ...
但是成本計算取決於一天中的不同時間。這就是你會看到很多人以意想不到的方式使用 Pandas 的地方,通過編寫一個迴圈來進行條件計算。
def apply_tariff(kwh, hour): """計算給定小時的電費""" if 0 <= hour < 7: rate = 12 elif 7 <= hour < 17: rate = 20 elif 17 <= hour < 24: rate = 28 else: raise ValueError(f'無效時間: {hour}') return rate * kwh def apply_tariff(kwh, hour): """計算給定小時的電費""" if 0 <= hour < 7: rate = 12 elif 7 <= hour < 17: rate = 20 elif 17 <= hour < 24: rate = 28 else: raise ValueError(f'無效時間: {hour}') return rate * kwh def apply_tariff_loop(df): energy_cost_list = [] for i in range(len(df)): # 迴圈資料直接修改df energy_used = df.iloc[i]['energy_kwh'] hour = df.iloc[i]['date_time'].hour energy_cost = apply_tariff(energy_used, hour) energy_cost_list.append(energy_cost) df['cost_cents'] = energy_cost_list Wall time: 2.59 s
Pandas 實際上 for i in range(len(df)) 通過引入 DataFrame.itertuples() 和 DataFrame.iterrows() 方法使語法就可能顯得多餘,這些都是yield一次一行的生成器方法。
.itertuples() 為每一行生成一個命名元組,行的索引值作為元組的第一個元素。 名稱元組是來自 Python 集合模組的資料結構,其行為類似於 Python 元組,但具有可通過屬性查詢存取的欄位。
.iterrows() 為 DataFrame 中的每一行生成 (index, Series) 對(元組)。
def apply_tariff_iterrows(df): energy_cost_list = [] for index, row in df.iterrows(): energy_used = row['energy_kwh'] hour = row['date_time'].hour energy_cost = apply_tariff(energy_used, hour) energy_cost_list.append(energy_cost) df['cost_cents'] = energy_cost_list %%time apply_tariff_iterrows(df) Wall time: 808 ms
速度提高又3倍之多。
可以使用 .apply() 方法進一步改進此操作。 Pandas 的 .apply() 方法採用函數(可呼叫物件)並將它們沿 DataFrame 的軸(所有行或所有列)應用。
lambda 函數將兩列資料傳遞給 apply_tariff()。
def apply_tariff_withapply(df): df['cost_cents'] = df.apply( lambda row: apply_tariff( kwh=row['energy_kwh'], hour=row['date_time'].hour), axis=1) %%time apply_tariff_withapply(df) Wall time: 181 ms
.apply() 的語法優勢很明顯,程式碼簡潔、易讀、明確。在這種情況下所用時間大約是該 .iterrows() 方法的4分之一。
但是如何在 Pandas 中將條件計算應用為向量化操作呢?一個技巧是根據的條件選擇和分組 DataFrame 的部分,然後對每個選定的組應用向量化操作。
使用 Pandas 的.isin()方法選擇行,然後在向量化操作中應用。在執行此操作之前,如果將 date_time 列設定為 DataFrame 的索引會更方便。
df.set_index('date_time', inplace=True) def apply_tariff_isin(df): peak_hours = df.index.hour.isin(range(17, 24)) shoulder_hours = df.index.hour.isin(range(7, 17)) off_peak_hours = df.index.hour.isin(range(0, 7)) df.loc[peak_hours, 'cost_cents'] = df.loc[peak_hours, 'energy_kwh'] * 28 df.loc[shoulder_hours,'cost_cents'] = df.loc[shoulder_hours, 'energy_kwh'] * 20 df.loc[off_peak_hours,'cost_cents'] = df.loc[off_peak_hours, 'energy_kwh'] * 12 %%time apply_tariff_isin(df) Wall time: 53.5 ms
其中整個過程方法返回一個布林列表。
[False, False, False, ..., True, True, True]
設定時間切分的列表和對那個計算的函數公式,讓操作起來更簡單,但是這個對於新手來說程式碼閱讀起來有些困難。
def apply_tariff_cut(df): cents_per_kwh = pd.cut(x=df.index.hour, bins=[0, 7, 17, 24], include_lowest=True, labels=[12, 20, 28]).astype(int) df['cost_cents'] = cents_per_kwh * df['energy_kwh'] %%time apply_tariff_cut(df) Wall time: 2.99 ms
Pandas Series 和 DataFrames 是在 NumPy 庫之上設計的。這為提供了更大的計算靈活性,因為 Pandas 可以與 NumPy 陣列和操作無縫共同作業。
使用 NumPy 的digitize()函數。它與 Pandas 的相似之處cut()在於資料將被分箱,但這次它將由一個索引陣列表示,該陣列表示每個小時屬於哪個箱。然後將這些索引應用於價格陣列。
import numpy as np def apply_tariff_digitize(df): prices = np.array([12, 20, 28]) bins = np.digitize(df.index.hour.values, bins=[7, 17, 24]) df['cost_cents'] = prices[bins] * df['energy_kwh'].values %%time apply_tariff_digitize(df) Wall time: 1.99 ms
對比一下上面幾種不同的處理方式的效率吧。
功能 | 執行時間(秒) |
---|---|
apply_tariff_loop() | 2.59 s |
apply_tariff_iterrows() | 808 ms |
apply_tariff_withapply() | 181 ms |
apply_tariff_isin() | 53.5 ms |
apply_tariff_cut() | 2.99 ms |
apply_tariff_digitize() | 1.99 ms |
通常構建複雜的資料模型時,對資料進行一些預處理會很方便。如果有 10 年的分鐘頻率用電量資料,即指定了格式引數簡單地將日期和時間轉換為日期時間也可能需要 20 分鐘。只需要這樣做一次而不是每次執行模型時都需要進行測試或分析。
可以在這裡做的一件非常有用的事情是預處理,然後以處理後的形式儲存資料,以便在需要時使用。但是如何才能以正確的格式儲存資料而無需再次重新處理呢?如果要儲存為 CSV 只會丟失您的日期時間物件,並且在再次存取時必須重新處理它。
Pandas 有一個內建的解決方案使用 HDF5,一種專為儲存表格資料陣列而設計的高效能儲存格式。Pandas 的 HDFStore 類允許將 DataFrame 儲存在 HDF5 檔案中,以便可以有效地存取它,同時仍保留列型別和其他後設資料。dict 是一個類似字典的類,因此可以像對 Python物件一樣進行讀寫。
將預處理的耗電量 DataFrame 儲存df在 HDF5 檔案中。
data_store = pd.HDFStore('processed_data.h5') # 將 DataFrame 放入物件中,將鍵設定為 preprocessed_df data_store['preprocessed_df'] = df data_store.close()
從 HDF5 檔案存取資料的方法,並保留資料型別。
data_store = pd.HDFStore('processed_data.h5') preprocessed_df = data_store['preprocessed_df'] data_store.close()
到此這篇關於Pandas資料處理加速技巧彙總的文章就介紹到這了,更多相關Pandas資料處理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45