面對讀取上G的數(shù)據(jù),python不能像做簡單代碼驗證那樣隨意,必須考慮到相應(yīng)的代碼的實現(xiàn)形式將對效率的影響。如下所示,對pandas對象的行計數(shù)實現(xiàn)方式不同,運行的效率差別非常大。雖然時間看起來都微不足道,但一旦運行次數(shù)達(dá)到百萬級別時,其運行時間就根本不可能忽略不計了:
故接下來的幾個文章將會整理下渣渣在關(guān)于在大規(guī)模數(shù)據(jù)實踐上遇到的一些問題,文章中總結(jié)的技巧基本是基于pandas,有錯誤之處望指正。
- 分析思路:數(shù)據(jù)量非常大時,比如一份銀行一個月的流水賬單,可能有高達(dá)幾千萬的record。對于一般性能的計算機,有或者是讀入到特殊的數(shù)據(jù)結(jié)構(gòu)中,內(nèi)存的存儲可能就非常吃力了??紤]到我們使用數(shù)據(jù)的實際情況,并不需要將所有的數(shù)據(jù)提取出內(nèi)存。當(dāng)然讀入數(shù)據(jù)庫是件比較明智的做法。若不用數(shù)據(jù)庫呢?可將大文件拆分成小塊按塊讀入后,這樣可減少內(nèi)存的存儲與計算資源
- 注意事項:open(file.csv)與pandas包的pd.read_csv(file.csv ): python32位的話會限制內(nèi)存,提示太大的數(shù)據(jù)導(dǎo)致內(nèi)存錯誤。解決方法是裝python64位。如果嫌python各種包安裝過程麻煩,可以直接安裝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對象指向了多個分塊對象,但并沒有將實際數(shù)據(jù)先讀入,而是在提取數(shù)據(jù)時才將數(shù)據(jù)提取進來。數(shù)據(jù)的處理和清洗經(jīng)常使用分塊的方式處理,這能大大降低內(nèi)存的使用量,但相比會更耗時一些
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ù)
data.to_csv(path_save, index = False, mode = 'w')`
- 對于第一個分塊使用pandas包的存儲IO:
- 保留header信息,‘w’模式寫入
data.to_csv(path_save, index = False, mode = 'w')
- 接下的分塊寫入
- 去除header信息,‘a(chǎn)’模式寫入,即不刪除原文檔,接著原文檔后繼續(xù)寫
data.to_csv(path_save, index = False, header = False, mode = a')
少量的數(shù)據(jù)用pickle(cPickle更快)輸出和讀取,非常方便 ,下面分別是寫出和讀入
寫出:
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]
userList = [] content = pd.read_csv(filename) for i in range(len(content)): line = content.ix[i]['id'] userList.append(line)
userList = [] f = open(filename) content = f.readlines() for line in content: line = line.replace('\n', '').split(',') userList.append(line)
- 問題分析:
- 縱向的合并使用list并不好,因為需要去拆解list的每一個行元素,并用extend去拓展每一行的縱向元素
- 最好使用dataframe中的concat函數(shù):c = pd.concat([a, b], axis = 1),當(dāng)axis=0時表示合并行(以行為軸)
inx1 = DataFrame(np.random.randn(nSample_neg), columns = ['randVal']) inx2 = DataFrame(range(nSample_neg), columns = ['inxVal']) inx = pd.concat([inx1, inx2], axis = 1)
ret = ret.join(dest_small, on="srch_destination_id", how='left', rsuffix="dest")
- 縱向合并數(shù)據(jù)集可以考慮一下幾種方法:
- 讀取數(shù)據(jù)為list格式,使用append函數(shù)逐行讀取
- 將數(shù)據(jù)集轉(zhuǎn)換為pandas中的dataframe格式,使用dataframe的merge與concat方法
- 方法:
- 方法一:使用dataframe讀入,使用concat把每行并起來
- 方法二:先使用list讀入并起來,最后轉(zhuǎn)換成dataframe
- 方法三:先使用list讀入并起來大塊的list,每塊list轉(zhuǎn)換成dataframe后用concat合并起來
- 比較:方法一由于concat的靜態(tài)性,每次要重新分配資源,故跑起來很慢; 方法二與三:會快很多,但具體沒有測試,以下是使用方法三的代碼:
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ù)>