面對(duì)讀取上G的數(shù)據(jù),python不能像做簡(jiǎn)單代碼驗(yàn)證那樣隨意,必須考慮到相應(yīng)的代碼的實(shí)現(xiàn)形式將對(duì)效率的影響。如下所示,對(duì)pandas對(duì)象的行計(jì)數(shù)實(shí)現(xiàn)方式不同,運(yùn)行的效率差別非常大。雖然時(shí)間看起來(lái)都微不足道,但一旦運(yùn)行次數(shù)達(dá)到百萬(wàn)級(jí)別時(shí),其運(yùn)行時(shí)間就根本不可能忽略不計(jì)了:

故接下來(lái)的幾個(gè)文章將會(huì)整理下渣渣在關(guān)于在大規(guī)模數(shù)據(jù)實(shí)踐上遇到的一些問(wèn)題,文章中總結(jié)的技巧基本是基于pandas,有錯(cuò)誤之處望指正。
1、外部csv文件讀寫(xiě)
大數(shù)據(jù)量csv讀入到內(nèi)存
- 分析思路:數(shù)據(jù)量非常大時(shí),比如一份銀行一個(gè)月的流水賬單,可能有高達(dá)幾千萬(wàn)的record。對(duì)于一般性能的計(jì)算機(jī),有或者是讀入到特殊的數(shù)據(jù)結(jié)構(gòu)中,內(nèi)存的存儲(chǔ)可能就非常吃力了??紤]到我們使用數(shù)據(jù)的實(shí)際情況,并不需要將所有的數(shù)據(jù)提取出內(nèi)存。當(dāng)然讀入數(shù)據(jù)庫(kù)是件比較明智的做法。若不用數(shù)據(jù)庫(kù)呢?可將大文件拆分成小塊按塊讀入后,這樣可減少內(nèi)存的存儲(chǔ)與計(jì)算資源
- 注意事項(xiàng):open(file.csv)與pandas包的pd.read_csv(file.csv ): python32位的話會(huì)限制內(nèi)存,提示太大的數(shù)據(jù)導(dǎo)致內(nèi)存錯(cuò)誤。解決方法是裝python64位。如果嫌python各種包安裝過(guò)程麻煩,可以直接安裝Anaconda2 64位版本
chunker = pd.read_csv(PATH_LOAD, chunksize = CHUNK_SIZE)
columns = ("date_time", "user_id")
chunks_train = pd.read_csv(filename, usecols = columns, chunksize = 100000)
chunker對(duì)象指向了多個(gè)分塊對(duì)象,但并沒(méi)有將實(shí)際數(shù)據(jù)先讀入,而是在提取數(shù)據(jù)時(shí)才將數(shù)據(jù)提取進(jìn)來(lái)。數(shù)據(jù)的處理和清洗經(jīng)常使用分塊的方式處理,這能大大降低內(nèi)存的使用量,但相比會(huì)更耗時(shí)一些
for rawPiece in chunker_rawData:
current_chunk_size = len(rawPiece.index) #rawPiece 是dataframe
for i in range(current_chunk_size ):
timeFlag = timeShape(rawPiece.ix[i]) #獲取第i行的數(shù)據(jù)
將數(shù)據(jù)存到硬盤(pán)
data.to_csv(path_save, index = False, mode = 'w')`
- 對(duì)于第一個(gè)分塊使用pandas包的存儲(chǔ)IO:
- 保留header信息,‘w’模式寫(xiě)入
data.to_csv(path_save, index = False, mode = 'w')
- 接下的分塊寫(xiě)入
- 去除header信息,‘a(chǎn)’模式寫(xiě)入,即不刪除原文檔,接著原文檔后繼續(xù)寫(xiě)
data.to_csv(path_save, index = False, header = False, mode = a')
少量的數(shù)據(jù)用pickle(cPickle更快)輸出和讀取,非常方便 ,下面分別是寫(xiě)出和讀入
寫(xiě)出:
import cPickle as pickle
def save_trainingSet(fileLoc, X, y):
pack = [X, y]
with open(fileLoc, 'w') as f:
pickle.dump(pack, f)
讀入:
import cPickle as pickle
def read_trainingSet(fileLoc):
with open(fileLoc, 'r') as f:
pack = pickle.load(f)
return pack[0], pack[1]
高效讀取外部csv到python內(nèi)部的list數(shù)據(jù)結(jié)構(gòu)
- 效率低下的方法:使用pd讀入需要從pd轉(zhuǎn)換到python本身的數(shù)據(jù)結(jié)構(gòu),多此一舉
userList = []
content = pd.read_csv(filename)
for i in range(len(content)):
line = content.ix[i]['id']
userList.append(line)
- 效率高的方法:直接將外部數(shù)據(jù)讀入進(jìn)來(lái)
userList = []
f = open(filename)
content = f.readlines()
for line in content:
line = line.replace('\n', '').split(',')
userList.append(line)
2、數(shù)據(jù)分析時(shí)常用數(shù)據(jù)結(jié)構(gòu)之間的轉(zhuǎn)化
數(shù)據(jù)集的橫向與縱向合并
- 簡(jiǎn)單地橫向合并數(shù)據(jù)集:
- 問(wèn)題分析:
- 縱向的合并使用list并不好,因?yàn)樾枰ゲ鸾鈒ist的每一個(gè)行元素,并用extend去拓展每一行的縱向元素
- 最好使用dataframe中的concat函數(shù):c = pd.concat([a, b], axis = 1),當(dāng)axis=0時(shí)表示合并行(以行為軸)
inx1 = DataFrame(np.random.randn(nSample_neg), columns = ['randVal'])
inx2 = DataFrame(range(nSample_neg), columns = ['inxVal'])
inx = pd.concat([inx1, inx2], axis = 1)
- 類似數(shù)據(jù)庫(kù)的表合并:join(待完整)
ret = ret.join(dest_small, on="srch_destination_id", how='left', rsuffix="dest")
- 簡(jiǎn)單縱向合并數(shù)據(jù)集:
- 縱向合并數(shù)據(jù)集可以考慮一下幾種方法:
- 讀取數(shù)據(jù)為list格式,使用append函數(shù)逐行讀取
- 將數(shù)據(jù)集轉(zhuǎn)換為pandas中的dataframe格式,使用dataframe的merge與concat方法
- 方法:
- 方法一:使用dataframe讀入,使用concat把每行并起來(lái)
- 方法二:先使用list讀入并起來(lái),最后轉(zhuǎn)換成dataframe
- 方法三:先使用list讀入并起來(lái)大塊的list,每塊list轉(zhuǎn)換成dataframe后用concat合并起來(lái)
- 比較:方法一由于concat的靜態(tài)性,每次要重新分配資源,故跑起來(lái)很慢; 方法二與三:會(huì)快很多,但具體沒(méi)有測(cè)試,以下是使用方法三的代碼:
data = []
cleanedPiece = []
for i in range(CHUNK_SIZE):
line = rawPiece.ix[i]
uid = [line['user_id'], line['item_id'],
line['behavior_type'], timeFlag]
cleanedPiece.append(uid)
cleanedPiece = DataFrame(cleanedPiece, columns = columns)
data = pd.concat([data, cleanedPiece], axis = 0)
<未完待續(xù)>
|