最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python使用Pandas庫(kù)提升項(xiàng)目的運(yùn)行速度過(guò)程詳解

 更新時(shí)間:2019年07月12日 10:51:46   作者:千鋒Python唐唐君  
這篇文章主要介紹了python使用Pandas庫(kù)提升項(xiàng)目的運(yùn)行速度過(guò)程詳解,這是一篇關(guān)于“如何充分利用Pandas內(nèi)置的強(qiáng)大且易于上手的特性”的指引。此外,你將學(xué)習(xí)到一些實(shí)用的節(jié)省時(shí)間的技巧,需要的朋友可以參考下

前言

如果你從事大數(shù)據(jù)工作,用Python的Pandas庫(kù)時(shí)會(huì)發(fā)現(xiàn)很多驚喜。Pandas在數(shù)據(jù)科學(xué)和分析領(lǐng)域扮演越來(lái)越重要的角色,尤其是對(duì)于從Excel和VBA轉(zhuǎn)向Python的用戶。

所以,對(duì)于數(shù)據(jù)科學(xué)家,數(shù)據(jù)分析師,數(shù)據(jù)工程師,Pandas是什么呢?Pandas文檔里的對(duì)它的介紹是:

“快速、靈活、和易于理解的數(shù)據(jù)結(jié)構(gòu),以此讓處理關(guān)系型數(shù)據(jù)和帶有標(biāo)簽的數(shù)據(jù)時(shí)更簡(jiǎn)單直觀?!?/p>

快速、靈活、簡(jiǎn)單和直觀,這些都是很好的特性。當(dāng)你構(gòu)建復(fù)雜的數(shù)據(jù)模型時(shí),不需要再花大量的開(kāi)發(fā)時(shí)間在等待數(shù)據(jù)處理的任務(wù)上了。這樣可以將更多的精力集中去理解數(shù)據(jù)。

但是,有人說(shuō)Pandas慢…

第一次使用Pandas時(shí),有人評(píng)論說(shuō):Pandas是很棒的解析數(shù)據(jù)的工具,但是Pandas太慢了,無(wú)法用于統(tǒng)計(jì)建模。第一次使用的時(shí)候,確實(shí)如此,真的慢。

但是,Pandas是建立在NumPy數(shù)組結(jié)構(gòu)之上的。所以它的很多操作通過(guò)NumPy或者Pandas自帶的擴(kuò)展模塊編寫(xiě),這些模塊用Cython編寫(xiě)并編譯到C,并且在C上執(zhí)行。因此,Pandas不也應(yīng)該很快的嗎?

事實(shí)上,使用姿勢(shì)正確的話,Pandas確實(shí)很快。

在使用Pandas時(shí),使用純“python”式代碼并不是最效率的選擇。和NumPy一樣,Pandas專為向量化操作而設(shè)計(jì),它可在一次掃描中完成對(duì)整列或者數(shù)據(jù)集的操作。而單獨(dú)處理每個(gè)單元格或某一行這種遍歷的行為,應(yīng)該作為備用選擇。

跟大家說(shuō)明一下,本Python學(xué)習(xí)教程不是引導(dǎo)如何過(guò)度優(yōu)化Pandas代碼。因?yàn)镻andas在正確的使用下已經(jīng)很快了。此外,優(yōu)化代碼和編寫(xiě)清晰的代碼之間的差異是巨大的。

這是一篇關(guān)于“如何充分利用Pandas內(nèi)置的強(qiáng)大且易于上手的特性”的指引。此外,你將學(xué)習(xí)到一些實(shí)用的節(jié)省時(shí)間的技巧。在這篇Python學(xué)習(xí)教程中,你將學(xué)習(xí)到:

  • ·使用datetime時(shí)間序列數(shù)據(jù)的優(yōu)勢(shì)
  • ·處理批量計(jì)算更效率的方法
  • ·利用HDFStore節(jié)省時(shí)間

這篇文章,耗電量時(shí)間序列數(shù)據(jù)將被用于演示本主題。加載數(shù)據(jù)后,我們將逐步了解更有效率的方法取得最終結(jié)果。對(duì)于Pandas用戶而言,會(huì)有多種方法預(yù)處理數(shù)據(jù)。但是這不意味著所有方法都適用于更大、更復(fù)雜的數(shù)據(jù)集。

【注】

【工具】

Python 3、Pandas 0.23.1

任務(wù):

本例使用能源消耗的時(shí)間序列數(shù)據(jù)計(jì)算一年能源的總成本。由于不同時(shí)間段的電價(jià)不同,因此需要將各時(shí)段的耗電量乘上對(duì)應(yīng)時(shí)段的電價(jià)。

從CSV文件中可以讀取到兩列數(shù)據(jù):日期時(shí)間和電力消耗(千瓦)

每行數(shù)據(jù)中都包含每小時(shí)耗電量數(shù)據(jù),因此整年會(huì)產(chǎn)生8760(356×24)行數(shù)據(jù)。每行的小時(shí)數(shù)據(jù)表示計(jì)算的開(kāi)始時(shí)間,因此1/1/13 0:00的數(shù)據(jù)指1月1號(hào)第1個(gè)小時(shí)的耗電量數(shù)據(jù)。

用Datetime類節(jié)省時(shí)間

首先用Pandas的一個(gè)I/O函數(shù)讀取CSV文件:

>>z import pandas as pd
>>> pd.__version__
'0.23.1'
>>> df = pd.read_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

這結(jié)果看上去挺好,但是有個(gè)小問(wèn)題。Pandas 和NumPy有個(gè)數(shù)據(jù)類型dtypes概念。假如不指定參數(shù)的話,date_time這列將會(huì)被歸為默認(rèn)類object:

>>> df.dtypes
date_time object
energy_kwh float64
dtype: object
>>> type(df.iat[0, 0])
str

默認(rèn)類object不僅是str類的容器,而且不能齊整的適用于某一種數(shù)據(jù)類型。字符串str類型的日期在數(shù)據(jù)處理中是非常低效的,同時(shí)內(nèi)存效率也是低下的。

為了處理時(shí)間序列數(shù)據(jù),需要將date_time列格式化為datetime類的數(shù)組,Pandas 稱這種數(shù)據(jù)類型為時(shí)間戳Timestamp。用Pandas進(jìn)行格式化相當(dāng)簡(jiǎn)單:

>>> df['date_time'] = pd.to_datetime(df['date_time'])
>>> df['date_time'].dtype
datetime64[ns]

至此,新的df和CSV file內(nèi)容基本一樣。它有兩列和一個(gè)索引。

>>> 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

上述代碼簡(jiǎn)單且易懂,但是有執(zhí)行速度如何呢?這里我們使用了timing裝飾器,這里將裝飾器稱為@timeit。這個(gè)裝飾器模仿了Python標(biāo)準(zhǔn)庫(kù)中的timeit.repeat() 方法,但是它可以返回函數(shù)的結(jié)果,并且打印多次重復(fù)調(diào)試的平均運(yùn)行時(shí)間。Python的timeit.repeat() 只返回調(diào)試時(shí)間結(jié)果,但不返回函數(shù)結(jié)果。

將裝飾器@timeit放在函數(shù)上方,每次運(yùn)行函數(shù)時(shí)可以同時(shí)打印該函數(shù)的運(yùn)行時(shí)間。

>>> @timeit(repeat=3, number=10)
... def convert(df, column_name):
... return pd.to_datetime(df[column_name])
>>> # Read in again so that we have `object` dtype to start 
>>> df['date_time'] = convert(df, 'date_time')
Best of 3 trials with 10 function calls per trial:
Function `convert` ran in average of 1.610 seconds.

看結(jié)果如何?處理8760行數(shù)據(jù)耗時(shí)1.6秒。這似乎沒(méi)啥毛病。但是當(dāng)處理更大的數(shù)據(jù)集時(shí),比如計(jì)算更高頻的電費(fèi)數(shù)據(jù),給出每分鐘的電費(fèi)數(shù)據(jù)去計(jì)算一整年的總成本。數(shù)據(jù)量會(huì)比現(xiàn)在多60倍,這意味著你需要大約90秒去等待輸出的結(jié)果。這就有點(diǎn)忍不了了。

實(shí)際上,作者工作中需要分析330個(gè)站點(diǎn)過(guò)去10年的每小時(shí)電力數(shù)據(jù)。按上邊的方法,需要88分鐘完成時(shí)間列的格式化轉(zhuǎn)換。

有更快的方法嗎?一般來(lái)說(shuō),Pandas可以更快的轉(zhuǎn)換你的數(shù)據(jù)。在本例中,使用格式參數(shù)將csv文件中特定的時(shí)間格式傳入Pandas的to_datetime中,可以大幅的提升處理效率。

>>> @timeit(repeat=3, number=100)
>>> def convert_with_format(df, column_name):
... return pd.to_datetime(df[column_name],
... format='%d/%m/%y %H:%M')
Best of 3 trials with 100 function calls per trial:
Function `convert_with_format` ran in average of 0.032 seconds.

新的結(jié)果如何?0.032秒,速度提升了50倍!所以之前330站點(diǎn)的數(shù)據(jù)處理時(shí)間節(jié)省了86分鐘。

一個(gè)需要注意的細(xì)節(jié)是CSV中的時(shí)間格式不是ISO 8601格式:YYYY-mm-dd HH:MM。如果沒(méi)有指定格式,Pandas將使用dateuil包將每個(gè)字符串格式的日期格式化。相反,如果原始的時(shí)間格式已經(jīng)是ISO 8601格式了,Pandas可以快速的解析日期。

【注】Pandas的read_csv()方法也提供了解析時(shí)間的參數(shù)。詳見(jiàn)parse_dates,infer_datetime_format,和date_parser參數(shù)。

遍歷

日期時(shí)間已經(jīng)完成格式化,現(xiàn)在準(zhǔn)備開(kāi)始計(jì)算電費(fèi)了。由于每個(gè)時(shí)段的電價(jià)不同,因此需要將對(duì)應(yīng)的電價(jià)映射到各個(gè)時(shí)段。此例中,電價(jià)收費(fèi)標(biāo)準(zhǔn)如下:

如果電價(jià)全天統(tǒng)一價(jià)28美分每千瓦每小時(shí),大多數(shù)人都知道可以一行代碼實(shí)現(xiàn)電費(fèi)的計(jì)算:

>>> df['cost_cents'] = df['energy_kwh'] * 28

這行代碼將創(chuàng)建一行新列,該列包含當(dāng)前時(shí)段的電費(fèi):

 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
# ...

但是電費(fèi)的計(jì)算取決于不用的時(shí)段對(duì)應(yīng)的電價(jià)。這里許多人會(huì)用非Pandas式的方式:用遍歷去完成這類計(jì)算。

在本文中,將從最基礎(chǔ)的解決方案開(kāi)始介紹,并逐步提供充分利用Pandas性能優(yōu)勢(shì)的Python式解決方案。

但是對(duì)于Pandas庫(kù)來(lái)說(shuō),什么是Python式方案?這里是指相比其他友好性較差的語(yǔ)言如C++或者Java,它們已經(jīng)習(xí)慣了“運(yùn)用遍歷”去編程。

如果不熟悉Pandas,大多數(shù)人會(huì)像以前一樣使用繼續(xù)遍歷方法。這里繼續(xù)使用@timeit裝飾器來(lái)看看這種方法的效率。

首先,創(chuàng)建一個(gè)不同時(shí)段電價(jià)的函數(shù):

def apply_tariff(kwh, hour):
 """電價(jià)函數(shù)""" 
 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

如下代碼就是一種常見(jiàn)的遍歷模式:

>>> # 注意:不要嘗試該函數(shù)!
>>> @timeit(repeat=3, number=100)
... def apply_tariff_loop(df):
... """用遍歷計(jì)算成本,將結(jié)果并入到df中"""
... energy_cost_list = []
... for i in range(len(df)):
... # 獲取每個(gè)小時(shí)的耗電量
... 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
... 
>>> apply_tariff_loop(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_loop` ran in average of 3.152 seconds.

對(duì)于沒(méi)有用過(guò)Pandas的Python用戶來(lái)說(shuō),這種遍歷很正常:對(duì)于每個(gè)x,再給定條件y下,輸出z。

但是這種遍歷很笨重。可以將上述例子視為Pandas用法的“反面案例”,原因如下幾個(gè)。

首先,它需要初始化一個(gè)列表用于存儲(chǔ)輸出結(jié)果。

其次,它用了隱晦難懂的類range(0, len(df))去做循環(huán),接著在應(yīng)用apply_tariff()函數(shù)后,還必須將結(jié)果增加到列表中用于生成新的DataFrame列。

最后,它還使用鏈?zhǔn)剿饕齞f.iloc[i]['date_time'],這可能會(huì)生產(chǎn)出很多bugs。

這種遍歷方式最大的問(wèn)題在于計(jì)算的時(shí)間成本。對(duì)于8760行數(shù)據(jù),花了3秒鐘完成遍歷。下面,來(lái)看看一些基于Pandas數(shù)據(jù)結(jié)構(gòu)的迭代方案。

用.itertuples()和.iterrow()遍歷

還有其他辦法嗎?

Pandas實(shí)際上通過(guò)引入DataFrame.itertuples()和DataFrame.iterrows()方法使得for i in range(len(df))語(yǔ)法冗余。這兩種都是產(chǎn)生一次一行的生成器方法。

.itertuples()為每行生成一個(gè)nametuple類,行的索引值作為nametuple類的第一個(gè)元素。nametuple是來(lái)自Python的collections模塊的數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)和Python中的元組類似,但是可以通過(guò)屬性查找可訪問(wèn)字段。

.iterrows()為DataFrame的每行生成一組由索引和序列組成的元組。

與.iterrows()相比,.itertuples()運(yùn)行速度會(huì)更快一些。本例中使用了.iterrows()方法,因?yàn)楹芏嘧x者很可能沒(méi)有用過(guò)nametuple。

>>> @timeit(repeat=3, number=100)
... def apply_tariff_iterrows(df):
... energy_cost_list = []
... for index, row in df.iterrows():
... #獲取每個(gè)小時(shí)的耗電量
... energy_used = row['energy_kwh']
... hour = row['date_time'].hour
... # 增加成本數(shù)據(jù)到list列表
... energy_cost = apply_tariff(energy_used, hour)
... energy_cost_list.append(energy_cost)
... df['cost_cents'] = energy_cost_list
...
>>> apply_tariff_iterrows(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_iterrows` ran in average of 0.713 seconds.

取得一些不錯(cuò)的進(jìn)步。語(yǔ)法更清晰,少了行值i的引用,整體更具有可讀性了。在時(shí)間收益方面,幾乎快了5倍!

但是,仍然有很大的改進(jìn)空間。由于仍然在使用for遍歷,意味著每循環(huán)一次都需要調(diào)用一次函數(shù),而這些本可以在速度更快的Pandas內(nèi)置架構(gòu)中完成。

Pandas的.apply()

可以用.apply()方法替代.iterrows()方法提升效率。Pandas的.apply()方法可以傳入可調(diào)用的函數(shù)并且應(yīng)用于DataFrame的軸上,即所有行或列。此例中,借助lambda功能將兩列數(shù)據(jù)傳入apply_tariff():

>>> @timeit(repeat=3, number=100)
... 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)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_withapply` ran in average of 0.272 seconds.

.apply()的語(yǔ)法優(yōu)勢(shì)很明顯,代碼行數(shù)少了,同時(shí)代碼也更易讀了。運(yùn)行速度方面,這與.iterrows()方法相比節(jié)省了大約一半時(shí)間。

但是,這還不夠快。一個(gè)原因是.apply()內(nèi)部嘗試在Cython迭代器上完成循環(huán)。但是在這種情況下,lambda中傳遞了一些無(wú)法在Cython中處理的輸入,因此調(diào)用.apply()時(shí)仍然不夠快。

如果使用.apply()在330個(gè)站點(diǎn)的10年數(shù)據(jù)上,這大概得花15分鐘的處理時(shí)間。假如這個(gè)計(jì)算僅僅是一個(gè)大型模型的一小部分,那么還需要更多的提升。下面的向量化操作可以做到這點(diǎn)。

用.isin()篩選數(shù)據(jù)

之前看到的如果只有單一電價(jià),可以將所有電力消耗數(shù)據(jù)乘以該價(jià)格df['energy_kwh'] * 28。這種操作就是一種向量化操作的一個(gè)用例,這是Pandas中最快的方式。

但是,在Pandas中如何將有條件的計(jì)算應(yīng)用在向量化操作中呢?一種方法是,根據(jù)條件將DataFrame進(jìn)行篩選并分組和切片,然后對(duì)每組數(shù)據(jù)進(jìn)行對(duì)應(yīng)的向量化操作。

在下面的例子中,將展示如何使用Pandas中的.isin()方法篩選行,然后用向量化操作計(jì)算對(duì)應(yīng)的電費(fèi)。在此操作前,將date_time列設(shè)置為DataFrame索引便于向量化操作:

df.set_index('date_time', inplace=True)
@timeit(repeat=3, number=100)
def apply_tariff_isin(df):
 # 定義每個(gè)時(shí)段的布爾型數(shù)組(Boolean)
 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)) 
 # 計(jì)算不同時(shí)段的電費(fèi)
 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

執(zhí)行結(jié)果如下:

>>> apply_tariff_isin(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_isin` ran in average of 0.010 seconds.

要理解這段代碼,也許需要先了解.isin()方法返回的是布爾值,如下:

[False, False, False, ..., True, True, True]

這些布爾值標(biāo)記了DataFrame日期時(shí)間索引所在的時(shí)段。然后,將這些布爾值數(shù)組傳給DataFrame的.loc索引器時(shí),會(huì)返回一個(gè)僅包含該時(shí)段的DataFrame切片。最后,將該切片數(shù)組乘以對(duì)應(yīng)的時(shí)段的費(fèi)率即可。

這與之前的遍歷方法相比如何?

首先,不需要apply_tariff()函數(shù)了,因?yàn)樗械臈l件邏輯都被應(yīng)用在了被選中的行。這大大減少了代碼的行數(shù)。

在速度方面,比普通的遍歷快了315倍,比.iterrows()方法快了71倍,且比.apply()方法快了27倍?,F(xiàn)在可以快速的處理大數(shù)據(jù)集了。

還有提升空間嗎?

在apply_tariff_isin()中,需要手動(dòng)調(diào)用三次df.loc和df.index.hour.isin()。比如24小時(shí)每個(gè)小時(shí)的費(fèi)率不同,這意味著需要手動(dòng)調(diào)用24次.isin()方法,所以這種方案通常不具有擴(kuò)展性。幸運(yùn)的是,還可以使用Pandas的pd.cut()功能:

@timeit(repeat=3, number=100)
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']

pd.cut()根據(jù)分組bins產(chǎn)生的區(qū)間生成對(duì)應(yīng)的標(biāo)簽“費(fèi)率”。

【注】include_lowest參數(shù)設(shè)定第一個(gè)間隔是否包含在組bins中,例如想要在該組中包含時(shí)間在0時(shí)點(diǎn)的數(shù)據(jù)。

這是種完全向量化的操作,它的執(zhí)行速度已經(jīng)起飛了:

>>> apply_tariff_cut(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_cut` ran in average of 0.003 seconds.

至此,現(xiàn)在可以將330個(gè)站點(diǎn)的數(shù)據(jù)處理時(shí)間從88分鐘縮小到只需不到1秒。但是,還有最后一個(gè)選擇,就是使用NumPy庫(kù)來(lái)操作DataFrame下的每個(gè)NumPy數(shù)組,然后將處理結(jié)果集成回DataFrame數(shù)據(jù)結(jié)構(gòu)中。

還有NumPy!

別忘了Pandas的Series和DataFrame是在NumPy庫(kù)的基礎(chǔ)上設(shè)計(jì)的。這提供了更多的靈活性,因?yàn)镻andas和NumPy數(shù)組可以無(wú)縫操作。

在下一例中,將演示NumPy的digitize()功能。它和Pandas的cut()功能類似,將數(shù)據(jù)分組。本例中將DataFrame中的索引(日期時(shí)間)進(jìn)行分組,將三個(gè)時(shí)段分入三組。然后將分組后的電力消耗數(shù)組應(yīng)用在電價(jià)數(shù)組上:

@timeit(repeat=3, number=100)
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

和cut()一樣,語(yǔ)法簡(jiǎn)單易讀。但是速度如何呢?

>>> apply_tariff_digitize(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_digitize` ran in average of 0.002 seconds.

執(zhí)行速度上,仍然有提升,但是這種提升已經(jīng)意義不大了。不如將更多精力去思考其他的事情。

Pandas可以提供很多批量處理數(shù)據(jù)方法的備用選項(xiàng),這些已經(jīng)在上邊都一一演示過(guò)了。這里將最快到最慢的方法排序如下:

1. 使用向量化操作:沒(méi)有for遍歷的Pandas方法和函數(shù)。

2. 使用.apply()方法。

3. 使用.itertuples():將DataFrame行作為nametuple類從Python的collections模塊中進(jìn)行迭代。

4. 使用.iterrows():將DataFrame行作為(index,pd.Series)元組數(shù)組進(jìn)行迭代。雖然Pandas的Series是一種靈活的數(shù)據(jù)結(jié)構(gòu),但將每一行生成一個(gè)Series并且訪問(wèn)它,仍然是一個(gè)比較大的開(kāi)銷。

5. 對(duì)逐個(gè)元素進(jìn)行循環(huán),使用df.loc或者df.iloc對(duì)每個(gè)單元格或者行進(jìn)行處理。

【注】以上順序不是我的建議,而是Pandas核心開(kāi)發(fā)人員給的建議。

以下是本文中所有函數(shù)的調(diào)試時(shí)間匯總:

用HDFstore存儲(chǔ)預(yù)處理數(shù)據(jù)

已經(jīng)了解了用Pandas快速處理數(shù)據(jù),現(xiàn)在我們需要探討如何避免重復(fù)的數(shù)據(jù)處理過(guò)程。這里使用了Pandas內(nèi)置的HDFStore方法。

通常在建立一些復(fù)雜的數(shù)據(jù)模型時(shí),對(duì)數(shù)據(jù)做一些預(yù)處理是很常見(jiàn)的。例如,假如有10年時(shí)間跨度的分鐘級(jí)的高頻數(shù)據(jù),但是模型只需要20分鐘頻次的數(shù)據(jù)或者其他低頻次數(shù)據(jù)。你不希望每次測(cè)試分析模型時(shí)都需要預(yù)處理數(shù)據(jù)。

一種方案是,將已經(jīng)完成預(yù)處理的數(shù)據(jù)存儲(chǔ)在已處理數(shù)據(jù)表中,方便需要時(shí)隨時(shí)調(diào)用。但是如何以正確的格式存儲(chǔ)數(shù)據(jù)?如果將預(yù)處理數(shù)據(jù)另存為CSV,那么會(huì)丟失datetime類,再次讀入時(shí)必須重新轉(zhuǎn)換格式。

Pandas有個(gè)內(nèi)置的解決方案,它使用HDF5,這是一種專門用于存儲(chǔ)數(shù)組的高性能存儲(chǔ)格式。Pandas的HDFstore方法可以將DataFrame存儲(chǔ)在HDF5文件中,可以有效讀寫(xiě),同時(shí)仍然保留DataFrame各列的數(shù)據(jù)類型和其他元數(shù)據(jù)。它是一個(gè)類似字典的類,因此可以像Python中的dict類一樣讀寫(xiě)。

以下是將已經(jīng)預(yù)處理的耗電量DataFrame寫(xiě)入HDF5文件的方法:

# 創(chuàng)建存儲(chǔ)類文件并命名 `processed_data`
data_store = pd.HDFStore('processed_data.h5')
#將DataFrame寫(xiě)入存儲(chǔ)文件中,并設(shè)置鍵(key) 'preprocessed_df'
data_store['preprocessed_df'] = df
data_store.close()

將數(shù)據(jù)存儲(chǔ)在硬盤(pán)以后,可以隨時(shí)隨地調(diào)取預(yù)處理數(shù)據(jù),不再需要重復(fù)加工。以下是關(guān)于如何從HDF5文件中訪問(wèn)數(shù)據(jù)的方法,同時(shí)保留了數(shù)據(jù)預(yù)處理時(shí)的數(shù)據(jù)類型:

# 訪問(wèn)數(shù)據(jù)倉(cāng)庫(kù)
data_store = pd.HDFStore('processed_data.h5')
# 讀取鍵(key)為'preprocessed_df'的DataFrame
preprocessed_df = data_store['preprocessed_df']
data_store.close()

一個(gè)數(shù)據(jù)倉(cāng)庫(kù)可以存儲(chǔ)多張表,每張表配有一個(gè)鍵。

【注】使用Pandas的HDFStore需要安裝PyTables>=3.0.0,因此安裝Pandas后,需要更新PyTables:

pip install --upgrade tables

總結(jié)

如果覺(jué)得你的Pandas項(xiàng)目不具備速度快、靈活、簡(jiǎn)單且直觀的特征,那么該重新思考使用該庫(kù)的方式了。

本次的Python學(xué)習(xí)教程已經(jīng)相當(dāng)直觀的展示了正確的使用Pandas是可以大幅改善運(yùn)行時(shí)間,以及代碼可讀性的。以下是應(yīng)用Pandas的一些經(jīng)驗(yàn)性的建議:

① 嘗試更多的向量化操作,盡量避免類似for x in df的操作。如果代碼中本身就有許多for循環(huán),那么盡量使用Python自帶的數(shù)據(jù)結(jié)構(gòu),因?yàn)镻andas會(huì)帶來(lái)很多開(kāi)銷。

② 如果因?yàn)樗惴◤?fù)雜無(wú)法使用向量化操作,可以嘗試.apply()方法。

③ 如果必須循環(huán)遍歷數(shù)組,可用.iterrows()或者.itertuples()改進(jìn)語(yǔ)法和提升速度。

④ Pandas有很多可選項(xiàng)操作,總有幾種方法可以完成從A到B的過(guò)程,比較不同方法的執(zhí)行方式,選擇最適合項(xiàng)目的一種。

⑤ 做好數(shù)據(jù)處理腳本后,可以將中間輸出的預(yù)處理數(shù)據(jù)保存在HDFStore中,避免重新處理數(shù)據(jù)。

⑥ 在Pandas項(xiàng)目中,利用NumPy可以提高速度同時(shí)簡(jiǎn)化語(yǔ)法。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決已經(jīng)安裝requests,卻依然提示No module named requests問(wèn)題

    解決已經(jīng)安裝requests,卻依然提示No module named requests問(wèn)題

    今天小編就為大家分享一篇解決已經(jīng)安裝requests,卻依然提示No module named 'requests'問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • python 簡(jiǎn)單搭建阻塞式單進(jìn)程,多進(jìn)程,多線程服務(wù)的實(shí)例

    python 簡(jiǎn)單搭建阻塞式單進(jìn)程,多進(jìn)程,多線程服務(wù)的實(shí)例

    下面小編就為大家?guī)?lái)一篇python 簡(jiǎn)單搭建阻塞式單進(jìn)程,多進(jìn)程,多線程服務(wù)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • 詳談pandas中agg函數(shù)和apply函數(shù)的區(qū)別

    詳談pandas中agg函數(shù)和apply函數(shù)的區(qū)別

    下面小編就為大家分享一篇詳談pandas中agg函數(shù)和apply函數(shù)的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • Python實(shí)現(xiàn)貪吃蛇小游戲(雙人模式)

    Python實(shí)現(xiàn)貪吃蛇小游戲(雙人模式)

    這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)雙人模式的貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 一次python-flask藍(lán)圖的踩坑記錄

    一次python-flask藍(lán)圖的踩坑記錄

    簡(jiǎn)單來(lái)說(shuō),藍(lán)圖就是一個(gè)存儲(chǔ)操作路由映射方法的容器,主要用來(lái)實(shí)現(xiàn)客戶端請(qǐng)求和URL相互關(guān)聯(lián)的功能,在Flask中,使用藍(lán)圖可以幫助我們實(shí)現(xiàn)模塊化應(yīng)用的功能,這篇文章主要給大家介紹了關(guān)于python-flask藍(lán)圖踩坑的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • pytorch如何使用訓(xùn)練好的模型預(yù)測(cè)新數(shù)據(jù)

    pytorch如何使用訓(xùn)練好的模型預(yù)測(cè)新數(shù)據(jù)

    這篇文章主要介紹了pytorch如何使用訓(xùn)練好的模型預(yù)測(cè)新數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Django跨域請(qǐng)求問(wèn)題的解決方法示例

    Django跨域請(qǐng)求問(wèn)題的解決方法示例

    這篇文章主要給大家介紹了關(guān)于Django跨域請(qǐng)求問(wèn)題解決的相關(guān)資料,文中介紹的實(shí)現(xiàn)方法包括:使用django-cors-headers全局控制、使用JsonP,只能用于Get方法以及在views.py里設(shè)置響應(yīng)頭,只能控制單個(gè)接口,需要的朋友可以參考下
    2018-06-06
  • python繪制鉛球的運(yùn)行軌跡代碼分享

    python繪制鉛球的運(yùn)行軌跡代碼分享

    這篇文章主要介紹了python繪制鉛球的運(yùn)行軌跡代碼分享,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • python中實(shí)現(xiàn)控制小數(shù)點(diǎn)位數(shù)的方法

    python中實(shí)現(xiàn)控制小數(shù)點(diǎn)位數(shù)的方法

    今天小編就為大家分享一篇python中實(shí)現(xiàn)控制小數(shù)點(diǎn)位數(shù)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Python通過(guò)for循環(huán)理解迭代器和生成器實(shí)例詳解

    Python通過(guò)for循環(huán)理解迭代器和生成器實(shí)例詳解

    這篇文章主要介紹了Python通過(guò)for循環(huán)理解迭代器和生成器,結(jié)合實(shí)例形式詳細(xì)分析了迭代器和生成器的概念、原理、使用方法及相關(guān)操作技巧,需要的朋友可以參考下
    2019-02-02

最新評(píng)論

南乐县| 阜新市| 淳化县| 芒康县| 永顺县| 寿宁县| 余姚市| 自治县| 漳州市| 宿州市| 南平市| 卫辉市| 文化| 杭锦后旗| 正蓝旗| 张家口市| 博客| 唐河县| 阿坝| 泰来县| 太仆寺旗| 淮北市| 彭山县| 广昌县| 福建省| 行唐县| 隆化县| 辽中县| 苍梧县| 象州县| 浮山县| 安多县| 襄垣县| 吉安市| 大荔县| 广东省| 三亚市| 普陀区| 唐河县| 张家界市| 忻城县|