摘要:下面讓我們開始提速假設(shè)我們現(xiàn)在的電價是定值,不根據(jù)用電時間段來改變,那么中最快的方法那就是采用,這就是一個簡單的矢量化操作示范。它基本是在中運行最快的方式。
Pandas 加速
大家好,今天我們來看有關(guān)pandas加速的小技巧,不知道大家在剛剛接觸pandas的時候有沒有聽過如下的說法
</>復(fù)制代碼
pandas太慢了,運行要等半天
其實我想說的是,慢不是pandas的錯,大家要知道pandas本身是在Numpy上建立起來的包,在很多情況下是支持向量化運算的,而且還有C的底層設(shè)計,所以我今天
主要想從幾個方面和大家分享一下pandas加速的小技巧,與往常一樣,文章分成四部分,本文結(jié)構(gòu)如下:
使用datetime類型來處理和時間序列有關(guān)的數(shù)據(jù)
批量計算的技巧
通過HDFStore存儲數(shù)據(jù)節(jié)省時間
源碼,相關(guān)數(shù)據(jù)及GitHub地址
現(xiàn)在就讓我們開始吧
1. 使用datetime類型來處理和時間序列有關(guān)的數(shù)據(jù)首先這里我們使用的數(shù)據(jù)源是一個電力消耗情況的數(shù)據(jù)(energy_cost.csv),非常貼近生活而且也是和時間息息相關(guān)的,用來做測試在合適不過了,這個csv文件大家可以在第四部分找到下載的地方哈
</>復(fù)制代碼
import os
# 這兩行僅僅是切換路徑,方便我上傳Github,大家不用理會,只要確認csv文件和py文件再一起就行啦
os.chdir("F:Python教程segmentfaultpandas_sharePandas之旅_07 誰說pandas慢")
現(xiàn)在讓我們看看數(shù)據(jù)大概長什么樣子
</>復(fù)制代碼
import numpy as np
import pandas as pd
f"Using {pd.__name__},{pd.__version__}"
</>復(fù)制代碼
"Using pandas,0.23.0"
</>復(fù)制代碼
df = pd.read_csv("energy_cost.csv",sep=",")
df.head()
date_time | energy_kwh | |
---|---|---|
0 | 2001/1/13 0:00 | 0.586 |
1 | 2001/1/13 1:00 | 0.580 |
2 | 2001/1/13 2:00 | 0.572 |
3 | 2001/1/13 3:00 | 0.596 |
4 | 2001/1/13 4:00 | 0.592 |
現(xiàn)在我們看到初始數(shù)據(jù)的樣子了,主要有date_time和energy_kwh這兩列,來表示時間和消耗的電力,比較好理解,下面讓我們來看一下數(shù)據(jù)類型
</>復(fù)制代碼
df.dtypes
>>> date_time object
energy_kwh float64
dtype: object
</>復(fù)制代碼
type(df.iat[0,0])
>>> str
這里有個小問題,Pandas和NumPy有dtypes(數(shù)據(jù)類型)的概念。如果未指定參數(shù),則date_time這一列的數(shù)據(jù)類型默認object,所以為了之后運算方便,我們可以把str類型的這一列轉(zhuǎn)化為timestamp類型:
</>復(fù)制代碼
df["date_time"] = pd.to_datetime(df["date_time"])
df.dtypes
>>> date_time datetime64[ns]
energy_kwh float64
dtype: object
先在大家可以發(fā)現(xiàn)我們通過用pd.to_datetime這個方法已經(jīng)成功的把date_time這一列轉(zhuǎn)化為了datetime64類型
</>復(fù)制代碼
df.head()
date_time | energy_kwh | |
---|---|---|
0 | 2001-01-13 00:00:00 | 0.586 |
1 | 2001-01-13 01:00:00 | 0.580 |
2 | 2001-01-13 02:00:00 | 0.572 |
3 | 2001-01-13 03:00:00 | 0.596 |
4 | 2001-01-13 04:00:00 | 0.592 |
現(xiàn)在再來看數(shù)據(jù), 發(fā)現(xiàn)已經(jīng)和剛才不同了,我們還可以通過指定format參數(shù)實現(xiàn)一樣的效果,速度上也會快一些
</>復(fù)制代碼
%%timeit -n 10
def convert_with_format(df, column_name):
return pd.to_datetime(df[column_name],format="%Y/%m/%d %H:%M")
df["date_time"]=convert_with_format(df, "date_time")
>>>722 μs ± 334 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
有關(guān)具體的日期自定義相關(guān)方法,大家點擊這里查看
2. 批量計算的技巧首先,我們假設(shè)根據(jù)用電的時間段不同,電費價目表如下:
Type | cents/kwh | periode |
---|---|---|
Peak | 28 | 17:00 to 24:00 |
Shoulder | 20 | 7:00 to 17:00 |
Off-Peak | 12 | 0:00 to 7:00 |
假設(shè)我們想要計算出電費,我們可以先寫出一個根據(jù)時間動態(tài)計算電費的方法“apply_tariff“
</>復(fù)制代碼
def apply_tariff(kwh, hour):
"""Calculates cost of electricity for given hour."""
if 0 <= hour < 7:
rate = 12
elif 7 <= hour < 17:
rate = 20
elif 17 <= hour < 24:
rate = 28
else:
raise ValueError(f"Invalid hour: {hour}")
return rate * kwh
好啦,現(xiàn)在我們想要在數(shù)據(jù)中新增一列 "cost_cents" 來表示總價錢,我們有很多選擇,首先能想到的方法便是iterrows(),它可以讓我們循環(huán)遍歷Dataframe的每一行,根據(jù)條件計算并賦值給新增的‘cost_cents’列
iterrows()首先我們能做的是循環(huán)遍歷流程,讓我們先用.iterrows()替代上面的方法來試試:
</>復(fù)制代碼
%%timeit -n 10
def apply_tariff_iterrows(df):
energy_cost_list = []
for index, row in df.iterrows():
# Get electricity used and hour of day
energy_used = row["energy_kwh"]
hour = row["date_time"].hour
# Append cost list
energy_cost = apply_tariff(energy_used, hour)
energy_cost_list.append(energy_cost)
df["cost_cents"] = energy_cost_list
apply_tariff_iterrows(df)
</>復(fù)制代碼
983 ms ± 65.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
我們?yōu)榱藴y試方便,所有的方法都會循環(huán)10次來比較耗時,這里很明顯我們有很大的改進空間,下面我們用apply方法來優(yōu)化
apply()</>復(fù)制代碼
%%timeit -n 10
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)
apply_tariff_withapply(df)
</>復(fù)制代碼
247 ms ± 24.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
這回速度得到了很大的提升,但是顯然我們還沒有g(shù)et到pandas加速的精髓:矢量化操作。下面讓我們開始提速
isin()假設(shè)我們現(xiàn)在的電價是定值,不根據(jù)用電時間段來改變,那么pandas中最快的方法那就是采用(df["cost_cents"] = df["energy_kwh"] * price),這就是一個簡單的矢量化操作示范。它基本是在Pandas中運行最快的方式。
目前的問題是我們的價格是動態(tài)的,那么如何將條件判斷添加到Pandas中的矢量化運算中呢?答案就是我們根據(jù)條件選擇和分組DataFrame,然后對每個選定的組應(yīng)用矢量化操作:
</>復(fù)制代碼
#先讓我們把時間序列作為索引
df.set_index("date_time", inplace=True)
</>復(fù)制代碼
%%timeit -n 10
def apply_tariff_isin(df):
# Define hour range Boolean arrays
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))
# Apply tariffs to hour ranges
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
apply_tariff_isin(df)
</>復(fù)制代碼
5.7 ms ± 871 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
這回我們發(fā)現(xiàn)速度是真正起飛了,首先我們根據(jù)用電的三個時段把df進行分三組,再依次進行三次矢量化操作,大家可以發(fā)現(xiàn)最后減少了很多時間,原理很簡單:
在運行的時候,.isin()方法返回一個布爾值數(shù)組,如下所示:
[False, False, False, ..., True, True, True]
接下來布爾數(shù)組傳遞給DataFrame的.loc索引器時,我們獲得一個僅包含與3個用電時段匹配DataFrame切片。然后簡單的進行乘法操作就行了,這樣做的好處是我們已經(jīng)不需要剛才提過的apply方法了,因為不在存在遍歷所有行的問題
我們可以做的更好嗎?通過觀察可以發(fā)現(xiàn),在apply_tariff_isin()中,我們?nèi)匀辉谕ㄟ^調(diào)用df.loc和df.index.hour.isin()來進行一些“手動工作”。如果想要進一步提速,我們可以使用cut方法
</>復(fù)制代碼
%%timeit -n 10
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"]
</>復(fù)制代碼
140 ns ± 29.9 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
效果依然鋒利,速度上有了成倍的提升
不要忘了用Numpy眾所周知,Pandas是在Numpy上建立起來的,所以在Numpy中當(dāng)然有類似cut的方法可以實現(xiàn)分組,從速度上來講差不太多
</>復(fù)制代碼
%%timeit -n 10
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
</>復(fù)制代碼
54.9 ns ± 19.3 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
正常情況下,以上的加速方法是能滿足日常需要的,如果有特殊的需求,大家可以上網(wǎng)看看有沒有相關(guān)的第三方加速包
3. 通過HDFStore存儲數(shù)據(jù)節(jié)省時間這里主要想強調(diào)的是節(jié)省預(yù)處理的時間,假設(shè)我們辛辛苦苦搭建了一些模型,但是每次運行之前都要進行一些預(yù)處理,比如類型轉(zhuǎn)換,用時間序列做索引等,如果不用HDFStore的話每次都會花去不少時間,這里Python提供了一種解決方案,可以把經(jīng)過預(yù)處理的數(shù)據(jù)存儲為HDF5格式,方便我們下次運行時直接調(diào)用。
下面就讓我們把本篇文章的df通過HDF5來存儲一下:
</>復(fù)制代碼
# Create storage object with filename `processed_data`
data_store = pd.HDFStore("processed_data.h5")
# Put DataFrame into the object setting the key as "preprocessed_df"
data_store["preprocessed_df"] = df
data_store.close()
現(xiàn)在我們可以關(guān)機下班了,當(dāng)明天接著上班后,通過key("preprocessed_df")就可以直接使用經(jīng)過預(yù)處理的數(shù)據(jù)了
</>復(fù)制代碼
# Access data store
data_store = pd.HDFStore("processed_data.h5")
# Retrieve data using key
preprocessed_df = data_store["preprocessed_df"]
data_store.close()
</>復(fù)制代碼
preprocessed_df.head()
energy_kwh | cost_cents | |
---|---|---|
date_time | ||
2001-01-13 00:00:00 | 0.586 | 7.032 |
2001-01-13 01:00:00 | 0.580 | 6.960 |
2001-01-13 02:00:00 | 0.572 | 6.864 |
2001-01-13 03:00:00 | 0.596 | 7.152 |
2001-01-13 04:00:00 | 0.592 | 7.104 |
如上圖所示,現(xiàn)在我們可以發(fā)現(xiàn)date_time已經(jīng)是處理為index了
4. 源碼,相關(guān)數(shù)據(jù)及GitHub地址這一期為大家分享了一些pandas加速的實用技巧,希望可以幫到各位小伙伴,當(dāng)然,類似的技巧還有很多,但是核心思想應(yīng)該一直圍繞矢量化操作上,畢竟是基于Numpy上建立的包,如果大家有更好的辦法,希望可以在我的文章底下留言哈
我把這一期的ipynb文件,py文件以及我們用到的energy_cost.csv放到了Github上,大家可以點擊下面的鏈接來下載:
Github倉庫地址: https://github.com/yaozeliang/pandas_share
希望大家能夠繼續(xù)支持我,這一篇文章已經(jīng)是Pandas系列的最后一篇了,雖然一共只寫了7篇文章,但是我認為從實用性上來講并沒有太遜色于收費課程(除了少了很多漂亮的ppt),接下來我會再接再厲,分享一下我對R (ggplot2)或者matplotlib的學(xué)習(xí)經(jīng)驗?。?/p>
Pandas之旅到此結(jié)束。撒花
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/43518.html
摘要:基于上的我們還可以實現(xiàn)幾個基于的,還是老樣子,先讓我們創(chuàng)建兩個好了,現(xiàn)在我們想要實現(xiàn)兩個的,但是條件是通過的和的這樣我們也可以得到結(jié)果。 Merge, Join, Concat 大家好,我有回來啦,這周更新的有點慢,主要是因為我更新了個人簡歷哈哈,如果感興趣的朋友可以去看看哈: 我的主頁 個人認為還是很漂亮的~,不得不說,很多時候老外的設(shè)計能力還是很強。 好了,有點扯遠了,這一期我想和...
摘要:不為人知的七大實用技巧大家好,我今天勤快地回來了,這一期主要是和大家分享一些的實用技巧,會在日常生活中大大提升效率,希望可以幫助到大家還是老樣子,先給大家奉上這一期的章節(jié)目錄自定義選項,設(shè)置實用中模塊構(gòu)建測試數(shù)據(jù)巧用訪問器合并其他列拼接使用 Pandas不為人知的七大實用技巧 大家好,我今天勤快地回來了,這一期主要是和大家分享一些pandas的實用技巧,會在日常生活中大大提升效率,希望...
為什么你需要pandas 大家好,今天想和大家分享一下有關(guān)pandas的學(xué)習(xí)新的,我因工作需要,從去年12月開始接觸這個非常好用的包,到現(xiàn)在為止也是算是熟悉了一些,因此發(fā)現(xiàn)了它的強大之處,特意想要和朋友們分享,特別是如果你每天和excel打交道,總是需要編寫一些vba函數(shù)或者對行列進行g(shù)roupby啊,merge,join啊之類的,相信我,pandas會讓你解脫的。 好啦,閑話少說,這篇文章的基礎(chǔ)...
摘要:數(shù)據(jù)清洗大家好,這一期我將為大家?guī)砦业膶W(xué)習(xí)心得第二期數(shù)據(jù)清理。這一期我會和大家分享一些比較好用常見的清洗方法。首先還是讓我們來簡單看一下本文將會用到的數(shù)據(jù)源這是一個超小型的房地產(chǎn)行業(yè)的數(shù)據(jù)集,大家會在文章最后找到下載地址。 數(shù)據(jù)清洗 大家好,這一期我將為大家?guī)砦业膒andas學(xué)習(xí)心得第二期:數(shù)據(jù)清理。這一步非常重要,一般在獲取數(shù)據(jù)源之后,我們緊接著就要開始這一步,以便為了之后的各種...
摘要:有關(guān)字符串基本方法大家好,我又回來了之前的幾期我們已經(jīng)簡單了解了的基礎(chǔ)操作,但是只要涉及到數(shù)據(jù),最常見的就是字符串類型,所以很多時候我們其實都在和字符串打交道,所以今天,我會把我自己總結(jié)的,有關(guān)字符串的常用方法分享給大家,希望能夠幫到各位小 有關(guān)字符串基本方法 大家好,我又回來了! 之前的幾期我們已經(jīng)簡單了解了pandas的基礎(chǔ)操作,但是只要涉及到數(shù)據(jù),最常見的就是String(字符串...
閱讀 3328·2021-11-08 13:12
閱讀 2771·2021-10-15 09:41
閱讀 1462·2021-10-08 10:05
閱讀 3310·2021-10-08 10:04
閱讀 2123·2021-09-29 09:34
閱讀 2497·2019-08-30 15:55
閱讀 2991·2019-08-30 15:45
閱讀 2598·2019-08-29 14:17