Почему мой питон DataFrame работает так медленно - PullRequest
0 голосов
/ 16 января 2019

Я создаю приложение, которое обеспечивает очень простой анализ больших наборов данных.Эти наборы данных поставляются в CSV-файлах размером более 10 миллионов строк и около 30 столбцов.(Мне не нужно много столбцов.)

Логика говорит мне, что весь файл в DataFrame должен сделать его более быстрым для доступа.Но мой компьютер говорит нет.

Я пробовал загружать пакетами, а также загружать все файлы, а затем выполнять функции пакетами.

Но конечный результат заключается в том, что для выполнения одного и того же процесса требуется более чем в 10 раз больше, чем при использовании простой опции чтения файлов.

Вот версия 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

Я что-то не так делаю?

Ответы [ 2 ]

0 голосов
/ 16 января 2019

несколько комментариев о вашем коде:

  • все эти 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])

агрегация довольно тупая, я просто хотел что-то, что использует оба столбца. это занимает страшные длинные 903 мс. затем я пытаюсь повторить вручную:

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 мс.

0 голосов
/ 16 января 2019

iterrows не использует векторизованные операции. Большинство преимуществ использования pandas заключаются в векторизованных и параллельных операциях.

Замените for index, row in df_wf.iterrows(): на df_wf.apply(something, axis=1), где something - это функция, которая инкапсулирует необходимую логику из iterrows и использует numpy векторизованные операции.

Также, если ваш df не помещается в памяти так, что вам нужно пакетное чтение, рассмотрите возможность использования dask или spark вместо pandas.

Дополнительное чтение: https://pandas.pydata.org/pandas-docs/stable/enhancingperf.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...