[英]Why is my python DataFrame performing so slowly
我正在構建一個應用程序,它可以對大型數據集進行一些非常簡單的分析。 這些數據集以1000萬行以上,約30列的CSV文件格式提供。 (我不需要很多列。)
邏輯告訴我,整個文件放入一個DataFrame中應該可以使其訪問更快。 但是我的電腦說不。
我嘗試過分批加載,以及加載整個文件,然后分批執行功能。
但是最終結果是,執行同一過程所需的時間比使用簡單的文件讀取選項要多十倍。
這是DataFrame版本:
def runProcess():
global batchSize
batchCount = 10
if rowLimit < 0:
with open(df_srcString) as f:
rowCount = sum(1 for line in f)
if batchSize < 0:
batchSize = batchSize * -1
runProc = readFileDf
else:
runProc = readFileDfBatch
batchCount = int(rowCount / batchSize) + 1
else:
batchCount = int(rowLimit / batchSize) + 1
for i in range(batchCount):
result = runProc(batchSize, i)
print(result)
def readFileDfBatch(batch, batchNo):
sCount = 0
lCount = 0
jobStartTime = datetime.datetime.now()
eof = False
totalRowCount = 0
startRow = batch * batchNo
df_wf = pd.read_csv(df_srcString, sep='|', header=None, names=df_fldHeads.split(','), usecols=df_cols, dtype=str, nrows=batch, skiprows=startRow)
for index, row in df_wf.iterrows():
result = parseDfRow(row)
totalRowCount = totalRowCount + 1
if result == 1:
sCount = sCount + 1
elif result == 2:
lCount = lCount + 1
eof = batch > len(df_wf)
if rowLimit >= 0:
eof = (batch * batchNo >= rowLimit)
jobEndTime = datetime.datetime.now()
runTime = jobEndTime - jobStartTime
return [batchNo, sCount, lCount, totalRowCount, runTime]
def parseDfRow(row):
#df_cols = ['ColumnA','ColumnB','ColumnC','ColumnD','ColumnE','ColumnF']
status = 0
s2 = getDate(row['ColumnB'])
l2 = getDate(row['ColumnD'])
gDate = datetime.date(1970,1,1)
r1 = datetime.date(int(row['ColumnE'][1:5]),12,31)
r2 = row['ColumnF']
if len(r2) > 1:
lastSeen = getLastDate(r2)
else:
lastSeen = r1
status = False
if s2 > lastSeen:
status = 1
elif l2 > lastSeen:
status = 2
return status
這是簡單的文件閱讀器版本:
def readFileStd(rows, batch):
print("Starting read: ")
batchNo = 1
global targetFile
global totalCount
global sCount
global lCount
targetFile = open(df_srcString, "r")
eof = False
while not eof:
batchStartTime = datetime.datetime.now()
eof = readBatch(batch)
batchEndTime = datetime.datetime.now()
runTime = batchEndTime - batchStartTime
if rows > 0 and totalCount >= rows: break
batchNo = batchNo + 1
targetFile.close()
return [batchNo, sCount, lCount, totalCount, runTime]
def readBatch(batch):
global targetFile
global totalCount
rowNo = 1
rowStr = targetFile.readline()
while rowStr:
parseRow(rowStr)
totalCount = totalCount + 1
if rowNo == batch:
return False
rowStr = targetFile.readline()
rowNo = rowNo + 1
return True
def parseRow(rowData):
rd = rowData.split('|')
s2 = getDate(rd[3])
l2 = getDate(rd[5])
gDate = datetime.date(1970,1,1)
r1 = datetime.date(int(rd[23][1:5]),12,31)
r2 = rd[24]
if len(r2) > 1:
lastSeen = getLastDate(r2)
else:
lastSeen = r1
status = False
if s2 > lastSeen:
global sCount
sCount = sCount + 1
status = True
gDate = s2
elif l2 > lastSeen:
global lCount
lCount = lCount + 1
gDate = s2
難道我做錯了什么?
iterrows
沒有利用向量化運算的優勢。 使用pandas
大多數好處來自矢量化和並行操作。
用df_wf.apply(something, axis=1)
替換for index, row in df_wf.iterrows():
其中something
是一個函數,該函數封裝了iterrows
所需的邏輯,並使用了numpy
向量化操作。
另外,如果您的df
無法容納在內存中,因此您需要批量讀取,請考慮使用dask
或spark
覆蓋pandas
。
進一步閱讀: https : //pandas.pydata.org/pandas-docs/stable/enhancingperf.html
關於您的代碼的一些注釋:
global
變量都嚇到我了! 傳遞參數和返回狀態有什么問題? Pandas
任何功能,創建一個數據框只是為了用它對行進行愚蠢的迭代會導致它做很多不必要的工作 csv
模塊(可以與delimiter='|'
)提供了更緊密的接口 對於https://codereview.stackexchange.com/,這可能是一個更好的問題
只是發揮着替代行方式的性能。 從下面獲得的收獲似乎是,熊貓的“行明智”工作通常總是很慢
首先創建一個數據框進行測試:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(1, 1e6, (10_000, 2)))
df[1] = df[1].apply(str)
這需要3.65毫秒來創建帶有int
和str
列的數據幀。 接下來,我嘗試iterrows
方法:
tot = 0
for i, row in df.iterrows():
tot += row[0] / 1e5 < len(row[1])
聚合是相當愚蠢的,我只想要一些同時使用兩列的東西。 耗時長達903ms。 接下來,我嘗試手動進行迭代:
tot = 0
for i in range(df.shape[0]):
tot += df.loc[i, 0] / 1e5 < len(df.loc[i, 1])
減少到408毫秒。 接下來我嘗試apply
:
def fn(row):
return row[0] / 1e5 < len(row[1])
sum(df.apply(fn, axis=1))
與368毫秒基本相同。 終於,我找到了熊貓滿意的一些代碼:
sum(df[0] / 1e5 < df[1].apply(len))
這需要4.15毫秒。 我想到的另一種方法是:
tot = 0
for a, b in zip(df[0], df[1]):
tot += a / 1e5 < len(b)
這需要2.78毫秒。 而另一個變體:
tot = 0
for a, b in zip(df[0] / 1e5, df[1]):
tot += a < len(b)
需要2.29毫秒。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.