【詳解】Python處理大量資料與DICT遍歷的優化問題
前言:本例我們的需求是寫一個每天0點執行的指令碼。這個指令碼從一個實時更新的資料庫中提取資料。
每天跑一個Excel表出來,表裡是當天零點與昨天零點時的差異的資料展示。
其實是很簡單的需求,遇到的關鍵問題是資料量。該例的資料量太大,每次都能從資料庫中拿出20多萬條資料。
資料量大的話遇到的問題有這麼幾個:
1. 資料無法裝入Excel表,因為使用Python處理Excel資料,最多插入65536行資料,多了就會報錯;
2. 遍歷篩選問題。我們拿到兩天的資料進行對比,然後生成一個差異對比表。就需要遍歷對比兩張表的資料,資料量太大,遍歷所用時間過長。
對這兩個關鍵的問題,我們現作闡述。
【問題一:Excel表改為Csv表】
我們發現,Csv格式的表,是沒有行數限制的,我們可以把20多萬條資料直接插入csv表中。
【問題二:DICT型別資料的遍歷】
按我們以往的經驗,生成對比資訊的字典程式碼如下:
def getCurrentCompareMessageDict0(dict0, dict1): '''未被優化的獲取當前對比資訊字典''' dlist0=list(dict0.keys()) dlist1=list(dict1.keys()) dict2={} for i in range(len(dlist1)): if dlist1[i] not in dlist0: key=dlist1[i] value=[0即,先構建兩個dict的key列表。, dict1[dlist1[i]]] dict2[key]=value else: if dict1[dlist1[i]]/100.0 != dict0[dlist1[i]]: key=dlist1[i] value=[dict0[dlist1[i]], dict1[dlist1[i]]] dict2[key]=value return dict2
然後,以key列表的長度為上限,進行for迴圈,採用DICT[KEY]的方式來進行列表資料的篩選。
這個方法的執行是超級慢的。
經過研究我們將該方法改進如下:
def getCurrentCompareMessageDict(dict0, dict1): '''優化的獲取當前對比資訊字典''' dict2={} i=0 for d, x in dict1.items(): if dict0.has_key(str(d)): if x/100.0 != string.atof(dict0[str(d)]): key=d value=[string.atof(dict0[str(d)]), x] dict2[key] = value else: key=d value=[0, x] dict2[key]=value return dict2採用該方法後,兩組20多萬條資料的比對篩選,在1秒內就完成了。
經測試,優化方法後速度提高了大約400倍!
這個方法優化了哪裡呢?
首先,遍歷dict的方法改為了
for d, x in dict1.items():其中,d為key,x為value。其實也可以這樣寫
for (d, x) in dict1.items():網上找到的資料稱,加括號的在200次以內的遍歷效率較高,不加括號的在200次以上的遍歷效率較高。(參考連結:python兩種遍歷方式的比較)
我們沒有去測試,採用了不加括號的方式。
其次,檢測某key是否存在於dict中的方法改為了
if dict0.has_key(str(d)):這個has_key函式返回的是布林值True或False。
原先的檢測方法:
if dlist1[i] not in dlist0:捨棄!
其實提高了效率的部分就兩步,遍歷和檢測!至於到底是哪一步提高了,……應該是都提高了。
因為這兩步的程式碼不是分開的,是聯絡在一起的。
只有採用了for d,x in dict.items()這種遍歷方法,才能夠直接使用d和x這兩個引數,而不用取值。
關鍵問題就是如上兩個。還有過程中遇到的幾個問題需要闡述一下:
1. python比較兩個陣列中的元素是否完全相等的問題。
>>> a = [(1,1),(2,2),(3,3),(4,4)] >>> b = [(4,4),(1,1),(2,2),(3,3)]
>>> a.sort() >>> b.sort()
>>> a==b True即,先排序後比較。只檢驗其中的元素是否一致,不考慮順序的影響。
2.python如何將字串轉為數字?
最終程式碼中我們用到了
string.atof(浮點數字符串)
string.atoi(整數字符串)
注意:需要
import string
3.讀取csv檔案
我們之前都是寫csv檔案。這裡需要讀,並將其中的資料裝入dict中,方便使用。
方法如下:
def getHandleDataDict(fileName): '''獲取昨天零點資料字典''' dict={} csvfile=file(fileName, 'rb') reader=csv.reader(csvfile) for i in reader: key=i[0] value=i[1] dict[key]=value return dict關鍵程式碼兩行:
csvfile=file(fileName, 'rb') reader=csv.reader(csvfile)
for i in reader:i 就是dict中每條資料。每個i是個列表,i[0]是key,i[1]是value。
4.Python的KeyError
這個錯誤我們不是第一次遇到,這裡著重說明,以示重視
KeyError的意思是:dict中不存在這個鍵。這種情況,我們如果dict[key]去取這個key對應的value,就會報KeyError的錯誤。
有可能是key的資料型別出錯,也有可能就是不存在這個鍵,兩種情況都要考慮。
我們在本例中遇到了資料型別出錯的情況,所以才會有2問題,將字串轉為數字blabala。。。。
【指令碼撰寫思想闡述】
還有一個指令碼的撰寫思想,先貼出最終版程式碼如下。
#!/usr/bin/python # -*- coding: UTF-8 -*- __author__ = "$Author: wangxin.xie$" __version__ = "$Revision: 1.0 $" __date__ = "$Date: 2015-01-05 10:01$" ############################################################### #功能: 當前0點與昨天0點餘額資訊對比表,每天00:00執行 ############################################################### import sys import datetime import xlwt import csv import string from myyutil.DBUtil import DBUtil #######################全域性變數#################################### memberDBUtil = DBUtil('moyoyo_member') today = datetime.datetime.today() todayStr = datetime.datetime.strftime(today, "%Y-%m-%d") handleDate = today - datetime.timedelta(1) handleDateStr = datetime.datetime.strftime(handleDate, "%Y-%m-%d") fileDir = 'D://' handleCsvFileName= fileDir+handleDateStr+'_balance_data.csv' currentCsvfileName = fileDir+todayStr+'_balance_data.csv' currentexcelFileName= fileDir+todayStr+'_balance_compare_message.xls' style1 = xlwt.XFStyle() font1 = xlwt.Font() font1.height = 220 font1.name = 'SimSun' style1.font = font1 csvfile1=file(currentCsvfileName, 'wb') writer1 = csv.writer(csvfile1, dialect='excel') ################################################################## def genCurrentBalanceData(): '''獲取當前餘額資料''' sql = ''' SELECT MEMBER_ID, (TEMP_BALANCE_AMOUNT + TEMP_FROZEN_AMOUNT) FROM moyoyo_member.MONEY_INFO WHERE (TEMP_BALANCE_AMOUNT + TEMP_FROZEN_AMOUNT) != 0 ''' rs = memberDBUtil.queryList(sql, ()) if not rs: return None return rs def getCurrentDataDict(rs): '''將當前資料組裝為字典''' dict={} for i in range(len(rs)): key=rs[i][0] value=rs[i][1] dict[key]=value return dict def writeCsv(x,writer): '''csv資料寫入函式''' writer.writerow(x) def writeCurrentCsvFile(): '''寫包含當前資料的csv檔案''' rs=genCurrentBalanceData() dict=getCurrentDataDict(rs) for d, x in dict.items(): writeCsv([d, x/100.0], writer1) csvfile1.close() return dict def getHandleDataDict(fileName): '''獲取昨天零點資料字典''' dict={} csvfile=file(fileName, 'rb') reader=csv.reader(csvfile) for i in reader: key=i[0] value=i[1] dict[key]=value return dict def getCurrentCompareMessageDict(dict0, dict1): '''獲取當前對比資訊字典''' dict2={} for d, x in dict1.items(): if dict0.has_key(str(d)): if x/100.0 != string.atof(dict0[str(d)]): key=d value=[string.atof(dict0[str(d)]), x] dict2[key] = value else: key=d value=[0, x] dict2[key]=value return dict2 def writeExcelHeader(): '''寫Excel表表頭''' wb = xlwt.Workbook(encoding = "UTF-8", style_compression = True) sht0 = wb.add_sheet("餘額資訊對比列表", cell_overwrite_ok = True) sht0.col(0).width=3000 sht0.col(1).width=4000 sht0.col(2).width=4000 num=today.day sht0.write(0, 0, '使用者ID', style1) sht0.write(0, 1, str(num-1)+'日零點餘額', style1) sht0.write(0, 2, str(num)+'日零點餘額', style1) return wb def writeCurrentCompareMessageInfo(sht,dict): '''寫當前對比資訊資料''' dlist=list(dict.keys()) for i in range(len(dlist)): sht.write(i+1, 0, dlist[i], style1) sht.write(i+1, 1, dict[dlist[i]][0], style1) sht.write(i+1, 2, dict[dlist[i]][1]/100.0, style1) def writeCurrentCompareMessageExcel(dict): '''寫當前對比資訊Excel表''' wb = writeExcelHeader() sheet0 = wb.get_sheet(0) writeCurrentCompareMessageInfo(sheet0, dict) wb.save(currentexcelFileName) def main(): print "===%s start===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")) currentDataDict=writeCurrentCsvFile() handleDataDict = getHandleDataDict(handleCsvFileName) currentCompareMessageDict = getCurrentCompareMessageDict(handleDataDict, currentDataDict) writeCurrentCompareMessageExcel(currentCompareMessageDict) print "===%s end===%s"%(sys.argv[0], datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")) if __name__ == '__main__': try: main() finally: if memberDBUtil: memberDBUtil.close()之所以要說,指令碼撰寫思想。
是因為我們在寫這個指令碼時,需要注意的很多問題,沒有加以重視。
尤其是流程方面。先做什麼後做什麼,拿到的資料如何處理。有沒有可以省去的步驟之類的。
都是在寫各個方法時需要注意的。
思想一:指令碼執行時間的指導作用
我們這個指令碼需求裡說,指令碼需要在每日零點取資料。資料庫中的資料是實時改變的。
所以既然要求了0點取資料,所以取資料的方法肯定是要放在最前面的。
即,指令碼的方法排列,與指令碼要求的執行時間是有密切關係的。
指令碼為什麼要選在0點執行,0點的時候幹了些什麼,是需要我們多加考慮的。
因為,最終影響的是資料的準確性。
即,如果我們先運行了別的方法,比如讀取昨天0點的csv檔案之類的方法。
讀了20多秒後,才執行這個取資料的方法。這時候取的資料就不是零點資料了。
思想二:不要重複勞動。
我們來分析一下本例中的資料流向。
Dict0--------昨天0點的資料在csv中。
Dict1--------該指令碼於當日0點執行時從資料庫中取的資料。先寫入csv中。
Dict2--------昨天的資料與剛跑出來的資料,經過對比篩選出來的差異資料字典。
需要注意的是生成Dict2時程式碼的操作。Dict0的資料自然是直接取,Dict1的資料存在於程式碼中的Dict1,可以直接return。
但是之前我們犯了一個錯誤,Dict1的資料我們從剛生成的csv檔案中提取。
這樣是沒有必要的。我們直接從程式碼中取就可以。這個資料程式碼中就有,不需要到檔案中提取了。
會因為這個無端延長指令碼的執行時間的。屬於基本的邏輯疏漏。
所以最終版程式碼中的這個方法。
def writeCurrentCsvFile(): '''寫包含當前資料的csv檔案''' rs=genCurrentBalanceData() dict=getCurrentDataDict(rs) for d, x in dict.items(): writeCsv([d, x/100.0], writer1) csvfile1.close() return dict在寫完csv檔案後,用過的dict就直接return了,因為後面還要用。
生成的csv檔案只是為了與明天的資料作對比。
思想三:資料產生的意義。
犯了上述錯誤。我們可以反思一下,資料的作用。。還有檔案的作用。
我們生成dict是為了什麼,當然資料可能不止一個作用,這個要注意。
csv0是為了提供dict0,dict0是為了與當天資料對比。
dict1是為了生成明天的csv,還有生成當天的dict2。
即,csv1根本不是為了dict1而存在的。只是為了為明天而做準備。
明白了這一點,就不會做出從csv1中取dict1的傻事了。
相關推薦
【詳解】Python處理大量資料與DICT遍歷的優化問題
前言:本例我們的需求是寫一個每天0點執行的指令碼。這個指令碼從一個實時更新的資料庫中提取資料。 每天跑一個Excel表出來,表裡是當天零點與昨天零點時的差異的資料展示。 其實是很簡單的需求,遇到的關鍵問題是資料量。該例的資料量太大,每次都能從資料庫中拿出20多萬條資料。
【詳解】Python專題開發
使用Python從指令碼到專題開發,才知道為什麼會有人說Python大法好,別的都去死! 因為相較於JAVA,開發起來要爽太多,方方面面…… 【需求】 1. 本例,因為著急上線一個新的活動版塊,所以使用Python來開發管理後臺。 2. 功能很簡單,增刪改查都實現即可。
【詳解】Python下載圖片
我們已經可以熟練的利用Python抓取網頁上的字串和數字資訊了 本例,我們來介紹使用Python下載圖片的簡單方法! 因為簡單,我們先貼出程式碼如下: #!/usr/bin/python # -*- coding: utf-8 -*- __author__ = "$Au
【詳解】銀行信用評分卡中的WOE在幹什麼?WOE的意義?為什麼可以使用WOE值代替原來的特徵值來做LR的訓練輸入資料
其實我是帶著這個問題發現這篇帖子的 為什麼可以使用WOE值代替原來的特徵值來做LR的訓練輸入資料 以下為原文 https://zhuanlan.zhihu.com/p/30026040 WOE & IV woe全稱叫Weight of Evidence,常用在風險評估、授
【原創】Python處理海量資料的實戰研究
感謝July ^_^ 他用的是Java的Hash Map等方法做了處理,講解的非常深刻入骨 我也一時興起,想拿Python試試刀,看看Python對於海量資料的處理能力如何。無奈在百度和Google輸入“Python 海量資料”都無果。可能是國內使用python的不多,
【詳解】【記錄】Python寫Excel預約資訊表併發送郵件
類似於本例我們寫過兩個指令碼了,但還是遇到了一些問題。 本例是比較基礎且標準的一個版本。 實現的操作是:從資料庫中取出資料,寫入Excel表,併發送郵件。相當簡單的一個Excel表。 【傳送給單人版】 #!/usr/bin/python # -*- coding: UT
(轉載)--SG函數和SG定理【詳解】
nbsp 發現 方式 spa 賦值 problem eve 查詢 mex 在介紹SG函數和SG定理之前我們先介紹介紹必勝點與必敗點吧. 必勝點和必敗點的概念: P點:必敗點,換而言之,就是誰處於此位置,則在雙方操作正確的情況下必敗。 N
【詳解】KMP算法
是不是 代碼 ++ 大牛 bilibili 開始 最長 [] 分別是 前言 KMP算法是學習數據結構 中的一大難點,不是說它有多難,而是它這個東西真的很難理解(反正我是這麽感覺的,這兩天我一直在研究KMP算法,總算感覺比較理解了這套算法, 在這裏我將自己的思路分享給大家
快速冪 二進位制 取模 【詳解】
本來想昨天寫的 看到了cod:ww2 我:我就玩一把,真的,就一把 然後就到了12點 真香~ 程式碼如下 不想理解可以直接拿來用 時間複雜度 logn typedef long long ll; ll quickmod(ll n) {
C++ MFC程序間通訊之剪貼簿【詳解】
Windows剪貼簿是一種比較簡單的程序間通訊機制,同時它的開銷相對較小。它的實現原理很簡單,其實就是由由作業系統維護的一塊記憶體區域, 這塊記憶體區域不屬於任何單獨的程序,但是每一個程序又都可以訪問這塊記憶體區域,當一個程序將資料放到該記憶體區域中,而另一個
HTTP協議【詳解】——經典面試題(轉載)
http請求由三部分組成,分別是:請求行、訊息報頭、請求正文 HTTP(超文字傳輸協議)是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的連線方式,HTTP1.1版本中給出一種持續連線的機制,絕大多數的Web開發,都是構建在HTTP協議之上的Web應用。
js網站國際化,多國語言切換【詳解】
JS網站國際化,多國語言切換【詳解】 作者:Anmbition 在web開發過程中通常會碰到需要多國語言支援的需求,我也看過一些文件,但寫的都不盡人意,最終我整理了一套通過js程式碼完成解決方案,並對程式碼進行了很大程度的優化,在使用過程中只需極少的程式碼即可完成。 第一步:核心JS
【詳解】快速冪&龜速乘&快速乘
我相信進來看的人都會快速冪,對吧(和善的眼神) 如果不會。。。。那我們現在開始講吧(要不然為什麼叫詳解2333 ) 如果已經知道,就跳到下面去看吧~ 1. 快速冪 1.0 快速冪的誕生——最初的思路 我們通常需要求解形如 ab mod c 的式子,當b比較小的時
【詳解】CS231n assignment1KNN中不使用迴圈計算距離:從原理到程式
本文主要講述不使用迴圈結構來計算兩個矩陣的歐氏距離, 設訓練集矩陣為train,size為num_train * num_features,設驗證集矩陣為validate,size為num_test,num_features。 因此我們計算每一個驗證集樣本到訓練集樣本的距離,就是將訓練集
【詳解】JNI(Java Native Interface)
前言: 一提到JNI,多數程式設計者會下意識地感受到一種無法言喻的恐懼。它給人的第一感覺就是"難",因為它不是單純地在JVM環境內操作Java程式碼,而是跳出虛擬機器與其他程式語言進行互動。 你可能至今還沒聽說過這個技術,但是如果你是一個原始碼愛好者,或者有翻閱過JDK的一些原始碼,那你一定有接觸過nat
【詳解】CopyOnWriteArrayList
前言 之前看《Java併發程式設計》這本書的時候,有看到這個,只記得"讀多寫少"、"寫入時複製"。書中沒有過多講述,只是一筆帶過(不過現在回頭看,發現講的都是精髓。老外的書大多重理論,喜歡花大篇幅講概念,這點我非常喜歡)記得當時是覺得可能有點難,先跳過了,結果就忘記回頭看了。今天突然想起來,就看了一下,整
【詳解】以銀行零售業務為例,一個案例說清楚視覺化微服務架構_Kubernetes中文社群
Part 1: API設計和策略 軟體系統的複雜性是一個很痛苦的問題,而且無法避免。Fred Brooks將複雜性描述為,軟體系統解決業務問題所固有的本質複雜性,以及實施該解決方案所帶來的偶發複雜性。 隨著與採用“API優先”工程實踐和微服務架構的組織進行更密切的合作,我發現這種描述越來
必須知道的八大種排序演算法【java實現】(二) 選擇排序,插入排序,希爾演算法【詳解】
一、選擇排序 1、基本思想:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然後在剩下的數當中再找最小的與第二個位置的數交換,如此迴圈到倒數第二個數和最後一個數比較為止。 2、例項 3、演算法實現 /** * 選擇排序演算法 * 在未
HTTP協議【詳解】——經典面試題
http請求由三部分組成,分別是:請求行、訊息報頭、請求正文 HTTP(超文字傳輸協議)是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的連線方式,HTTP1.1版本中給出一種持續連線的機制,絕大多數的Web開發,都是構建在HTTP協議之上的Web應用。 1、常用的HTTP方法有哪些?GET:
【詳解】WebSocket相關知識整理
前言 記得大概半年前就產生了疑惑,即後臺如何主動向前端推送資料。問了下專業老師,知道了原來有一個叫WebSocket的技術可以用於推送資料。於是,當時我就找了個教程,用的是Spring WebSocket。照著敲了一遍,也就搭起來了,依葫蘆畫瓢而已。當時有其他東西要學,也沒有相關的需求,就沒再接觸過。前