Numpy和Pandas性能改善的方法和技巧

問題

  1. 設(shè)計(jì)的代碼能hold住小規(guī)模數(shù)據(jù)
  2. 你準(zhǔn)備將該代碼用來處理真實(shí)場(chǎng)景的數(shù)據(jù)
  3. 但驚喜的是你的代碼崩潰了
  4. 問題: 你的電腦只有16G內(nèi)存戳表,但現(xiàn)在卻要應(yīng)付50G大小的數(shù)據(jù)草丧。

硬件解決辦法

  1. 換裝備惠奸,比如64G內(nèi)存的電腦
  2. 租用云服務(wù)器冰肴,64核432G內(nèi)存壹置,每小時(shí)幾十元

軟件解決辦法

  1. 壓縮你的數(shù)據(jù)
  2. 分塊讀取屯仗,一次只讀一塊。
  3. 對(duì)數(shù)據(jù)進(jìn)行索引標(biāo)注娘荡,只在需要的時(shí)候?qū)雰?nèi)存

本教程涉及

numpy和pandas的三種思維來處理內(nèi)存占用和性能問題

  • 壓縮
  • 分塊
  • 索引

一干旁、 壓縮

  1. 指的是同樣的信息量數(shù)據(jù),使用更少的內(nèi)存炮沐。
  2. 在內(nèi)存上壓縮争群,而非在硬盤里壓縮

1.1 壓縮:Numpy dtype

numpy類型 介紹 數(shù)值范圍
np.int8 字節(jié) (-128 to 127)
np.int16 整數(shù) (-32768 to 32767)
np.int32 整數(shù) (-2147483648 to 2147483647)
np.int64 整數(shù) (-9223372036854775808 to 9223372036854775807)
np.uint8 無符號(hào)整數(shù) (0 to 255)
np.uint16 無符號(hào)整數(shù) (0 to 65535)
np.uint32 無符號(hào)整數(shù) (0 to 4294967295)
np.uint64 無符號(hào)整數(shù) (0 to 18446744073709551615)
np.float16 半精度浮點(diǎn)數(shù),包括:1 個(gè)符號(hào)位大年,5 個(gè)指數(shù)位换薄,10 個(gè)尾數(shù)位
np.float32 單精度浮點(diǎn)數(shù),包括:1 個(gè)符號(hào)位翔试,8 個(gè)指數(shù)位专控,23 個(gè)尾數(shù)位
np.float64 雙精度浮點(diǎn)數(shù),包括:1 個(gè)符號(hào)位遏餐,11 個(gè)指數(shù)位,52 個(gè)尾數(shù)位

同樣的整數(shù)赢底,用np.int64占用的內(nèi)存是np.int16的4倍

import numpy as np

int64arr = np.ones((1024, 1024), dtype=np.int64)
int16arr = np.ones((1024, 1024), dtype=np.int16)

#占用(內(nèi)存)的字節(jié)數(shù)
print(int64arr.nbytes)
print(int16arr.nbytes)
8388608
2097152

1.2 壓縮: 稀疏的數(shù)組

https://sparse.pydata.org/

  1. 數(shù)組中有大量的0
  2. 內(nèi)存浪費(fèi)在很多0身上
  3. 稀疏數(shù)據(jù)只存儲(chǔ)非0數(shù)據(jù)
  4. 用numpy數(shù)組對(duì)數(shù)據(jù)進(jìn)行插值
  5. 不同的表達(dá)數(shù)據(jù)的方式

sparse可以壓縮數(shù)據(jù)內(nèi)存占用量失都,看一個(gè)例子

import numpy as np

arr = np.random.random((1024, 1024))
arr[arr < 0.9] = 0

print(arr)
[[0.         0.         0.         ... 0.         0.94559922 0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]
 ...
 [0.94589484 0.         0.         ... 0.96746948 0.         0.        ]
 [0.         0.         0.         ... 0.96236294 0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]]
import sparse #需要安裝sparse
sparse_arr = sparse.COO(arr)
print(sparse_arr)
<COO: shape=(1024, 1024), dtype=float64, nnz=104998, fill_value=0.0>
print(arr.nbytes)
print(sparse_arr.nbytes)
8388608
2519952

1.3 壓縮: Pandas dtype

如果知道數(shù)據(jù)的字段,可以在pandas導(dǎo)入數(shù)據(jù)時(shí)就設(shè)定字段的dtype參數(shù)幸冻,減少不必要的內(nèi)存開支粹庞。例如

import pandas as pd
import numpy as np

#不設(shè)定dtype
df1 = pd.read_csv('data.csv')
df1
df.png

trip_id是整數(shù),默認(rèn)pandas用的是np.int64, 我們可以將其設(shè)定為np.int32

#設(shè)定dtype參數(shù)
df2 = pd.read_csv('data.csv', dtype={"trip_id": np.int32})
df2
df.png
print(df1['trip_id'].nbytes)
print(df2['trip_id'].nbytes)
40
20

我們可以看到通過指定dtype洽损,trip_id字段占用的內(nèi)存少了一半庞溜。

二、 分塊

2.1 分塊處理全部的數(shù)據(jù)

也可以分塊處理全部的數(shù)據(jù)碑定,最后將結(jié)果再匯總流码,減少電腦的內(nèi)存壓力。比如我們想求長(zhǎng)度為1024的數(shù)組arr中的最大值

import numpy as np

#長(zhǎng)度1024的數(shù)組arr
arr = np.random.random(1024)

arr
array([0.37143228, 0.14093017, 0.67051473, ..., 0.42278493, 0.38588344,
       0.11637298])
#一次性求最大
max(arr)
0.9994997367530419
#分塊延刘,匯總求最大
max(max(arr[:500]), max(arr[500:]))
0.9994997367530419

2.2 分塊:Pandas也能分塊

分塊依次讀取漫试,這樣可以對(duì)比電腦內(nèi)存還大的數(shù)據(jù)進(jìn)行運(yùn)算操作。

import pandas as pd

max_record = 0

#分塊依次讀取碘赖,專業(yè)
for chunk in pd.read_csv('my.csv',
                         chunksize=100):#塊的記錄數(shù)為100條
    max_record = max(
        max_record,
        max(chunk['某個(gè)需要求最大值的字段名'])
    )
    
print(max_record)

598000

2.3 并行: 對(duì)很多塊并行處理

  1. 如果數(shù)據(jù)塊之間彼此獨(dú)立
  2. 且對(duì)數(shù)據(jù)塊的計(jì)算也是獨(dú)立的
  3. 我們可以利用電腦多核進(jìn)行并行運(yùn)算
  4. 并不會(huì)降低內(nèi)存占用驾荣,但是會(huì)提高運(yùn)行速度

塊的大小外构,需要滿足

  • 64G內(nèi)存, 并行數(shù)為1時(shí)播掷,處理的塊數(shù)據(jù)大小不超過60G
  • 64G內(nèi)存审编, 并行數(shù)為4時(shí),處理的塊數(shù)據(jù)大小不超過15G

三歧匈、索引

3.1 索引:需要的時(shí)候再調(diào)用

  1. 索引是對(duì)數(shù)據(jù)的準(zhǔn)確描述
  2. 索引對(duì)應(yīng)的數(shù)據(jù)一定比內(nèi)存小很多
  3. 索引能告訴程序數(shù)據(jù)的子集在哪里

3.2 索引 vs 分塊

  • 分塊
    需要導(dǎo)入所有的數(shù)據(jù)垒酬, "What is the longest word in this book?"需要研究這本書的每一頁(yè)。

  • 索引
    只導(dǎo)入數(shù)據(jù)的子集, "How much money did we spend in July?"眯亦,只需要在意July伤溉,其他月份不用考慮。

  • 兩者經(jīng)常搭配使用

3.3 索引:Pandas不支持索引

所以需要自定義,實(shí)現(xiàn)索引功能

def get_subset(csvf, field, conditon):
    """
    從csv數(shù)據(jù)中抽取出field值為condition的所有數(shù)據(jù)妻率。
    csvf: csv文件的路徑
    field: 需要的字段
    conditon: 字段field需要滿足的條件
    """
    return pd.concat(
        df[df.field==conditon] 
        for df in pd.read_csv(csvf, chunksize=1000)
    )

3.4 索引: SQLite&pandas

如何讓sqlite數(shù)據(jù)庫(kù)也能分塊

import sqlite

def create_index(csvf, dbname, field):
    """
    將csv中的數(shù)據(jù)轉(zhuǎn)移至sqlite數(shù)據(jù)庫(kù)乱顾,并給field創(chuàng)建索引
    dbname: sqlite數(shù)據(jù)庫(kù)庫(kù)名
    field: 需要?jiǎng)?chuàng)建索引的字段名
    """
    db=sqlite.connect("{}.sqlite".format(dbname))

    for chunk in pd.read_csv(csvf, chunksize=1000):
        chunk.to_sql(dbname, db, if_exists='append')

    db.execute("CRESTE INDEX {field} ON {dbname}({field})".format(field=field, dbname=dbname, field=field))
    db.close()

def get_subset(dbname, field, conditon):
    """
    從dbname中抽取出field值為condition的所有數(shù)據(jù)。
    dbname: sqlite數(shù)據(jù)庫(kù)庫(kù)名
    field: 需要的字段
    condition: 字段field需要滿足的條件
    """
    conn = sqlite3.connect("{}.sqlite".format(dbname))
    q = ("SELECT * FROM {db} WHERE {field} = {condtion}".format(db=dbname, field=field, condition=conditon))
    return pd.read_sql_query(q, conn)

3.5 索引:SQLite vs csv

使用70k voters數(shù)據(jù)對(duì)比

  • Cambridge,MA : 70k voters
類型 操作 內(nèi)存占用情況
CSV 分塊依次讀取10000行 + 按條件找出需要的數(shù)據(jù) 574ms
SQLite 索引找出需要的數(shù)據(jù) 10ms

總結(jié)

  • 同樣的問題
  1. 內(nèi)存快但貴
  2. 硬盤便宜但慢
  • 解決辦法:壓縮宫静、分塊(有條件的并行)走净、索引
  • 對(duì)了,如果不差錢孤里,事情會(huì)好辦不少伏伯。。捌袜。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末说搅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子虏等,更是在濱河造成了極大的恐慌弄唧,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霍衫,死亡現(xiàn)場(chǎng)離奇詭異候引,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)敦跌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門澄干,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柠傍,你說我怎么就攤上這事麸俘。” “怎么了携兵?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵疾掰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我徐紧,道長(zhǎng)静檬,這世上最難降的妖魔是什么炭懊? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮拂檩,結(jié)果婚禮上侮腹,老公的妹妹穿的比我還像新娘。我一直安慰自己稻励,他們只是感情好父阻,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著望抽,像睡著了一般加矛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煤篙,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天斟览,我揣著相機(jī)與錄音,去河邊找鬼辑奈。 笑死苛茂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸠窗。 我是一名探鬼主播妓羊,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼稍计!你這毒婦竟也來了躁绸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤臣嚣,失蹤者是張志新(化名)和其女友劉穎涨颜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茧球,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年星持,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抢埋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旺坠,死狀恐怖玫恳,靈堂內(nèi)的尸體忽然破棺而出较沪,到底是詐尸還是另有隱情,我是刑警寧澤饥努,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站八回,受9級(jí)特大地震影響酷愧,放射性物質(zhì)發(fā)生泄漏驾诈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一溶浴、第九天 我趴在偏房一處隱蔽的房頂上張望乍迄。 院中可真熱鬧,春花似錦士败、人聲如沸闯两。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)漾狼。三九已至,卻和暖如春饥臂,著一層夾襖步出監(jiān)牢的瞬間逊躁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工擅笔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留志衣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓猛们,卻偏偏與公主長(zhǎng)得像念脯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弯淘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容