Ускорение данных Excel в Pandas - PullRequest
11 голосов
/ 19 июня 2019

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

for file in unique_file_names[1:]:
        file_name = rootdir + "/" + str(file)
        test_time = time.clock()    
        try:
            wb_loop = load_workbook(file_name, read_only=True, data_only=True)
            ws_loop = wb_loop["SHEET1"]
            df = pd.DataFrame(ws_loop.values)
            print("Opening Workbook:         ", time.clock()-test_time)

            newarray = np.vstack((newarray, df.loc[4:43,:13].values))
            print("Data Manipulation:         ", time.clock()-test_time)

Итак, я попробовал несколько различных модулей для чтения в файлах Excel, в том числе непосредственно с помощью pandas.read_excel (), и это оптимальный метод, позволяющий получить время, чтобы открыть книгу до 1,5-2 с. укладка занимает 0,03 секунды.

Я думаю, что размещение данных в третьем измерении в массиве на основе индекса, вероятно, будет быстрее, но я больше сосредоточен на ускорении загрузки электронных таблиц, какие-либо предложения?

Редактировать: я также создал многопоточный пул, чтобы попытаться ускорить это, но по какой-то причине он начал использовать оперативную память 15 Гб и сломал мой компьютер

Редактировать 2:

Итак, самый быстрый способ сделать это - использовать xlrd в соответствии с рекомендацией принятых ответов. Я также понял, что быстрее удалить рабочую книгу в конце цикла. Окончательный код выглядит как

for file in unique_file_names[1:]:
        file_name = rootdir + "/" + str(file)
        test_time = time.clock()    
        try:
            wb_loop = xlrd.open_workbook(file_name, on_demand = True)
            ws_loop = wb_loop.sheet_by_name("Sheet1")
            print("Opening Workbook:         ", time.clock()-test_time)

            df = pd.DataFrame([ws_loop.row_values(n) for n in  range(ws_loop.nrows)])            

            newarray = np.vstack((newarray, df.loc[4:43,:13].values))
            del wb_loop

            print("Data Manipulation:         ", time.clock()-test_time)

        except:
            pass
        counter+=1
        print("%s %% Done" %(counter*100/len(unique_file_names)))

    wb_new = xlwt.Workbook()
    ws_new = wb_new.add_sheet("Test")
    ws_new.write(newarray)
    wb_new.save(r"C:Libraries/Documents/NewOutput.xls")

Это выводит среднее время за цикл 1,6-1,8 с. Спасибо всем за помощь.

Ответы [ 2 ]

1 голос
/ 27 июня 2019

Вот быстрый тест (расширяющий этот ).По-видимому, прямое использование xlrd немного быстрее, чем pandas для тестового файла .xlsx.Если доступны файлы .csv, их чтение определенно происходит намного быстрее, но преобразование их с помощью LibreOffice происходит значительно медленнее:

pd_base 1.96 [in seconds]
pd_float 2.03
pd_object 2.01 [see cs95´s comment to your question]
pd_xlrd 1.95
pyxl_base 2.15
xlrd_base 1.79
csv_ready 0.17
csv_convert 18.72

Вот код:

import pandas as pd
import openpyxl
import xlrd
import subprocess

file = 'test.xlsx'
df = pd.DataFrame([[i+j for i in range(50)] for j in range(100)])
df.to_excel(file, index=False)
df.to_csv(file.replace('.xlsx', '.csv'), index=False)

def pd_base():
    df = pd.read_excel(file)
def pd_float():
    df = pd.read_excel(file, dtype=np.int)
def pd_object():
    df = pd.read_excel(file, sheet_name="Sheet1", dtype=object)
def pd_xlrd():
    df = pd.read_excel(file, engine='xlrd')
def pyxl_base():
    wb = openpyxl.load_workbook(file, read_only=True, keep_links=False, data_only=True)
    sh = wb.active
    df = pd.DataFrame(sh.values)
def xlrd_base():
    wb = xlrd.open_workbook(file)
    sh = wb.sheet_by_index(0)
    df = pd.DataFrame([sh.row_values(n) for n in  range(sh.nrows)])
def csv_ready():    
    df = pd.read_csv(file.replace('.xlsx', '.csv'))
def csv_convert():    
    out = subprocess.check_output(['libreoffice --headless --convert-to csv test.xlsx'], shell=True, stderr=subprocess.STDOUT)
    df = pd.read_csv(file.replace('.xlsx', '.csv'))

def measure(func, nums=50):
    temp = time.time()
    for num in range(nums):
        func()
    diff = time.time() - temp
    print(func.__name__, '%.2f' % diff)

for func in [pd_base, pd_float, pd_object, pd_xlrd, pyxl_base, xlrd_base, csv_ready, csv_convert]:
    measure(func)    
0 голосов
/ 28 июня 2019

Два совета:

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

Если вы не предоставите тип, большинство библиотек будет использовать максимально возможное значение (64 бита). Если даже после контроля типов ваши данные не помещаются в памяти, вам нужно подумать о разбиении и разливе на диск.

Ниже приведен пример написания вашего кода для управления типами данных и с интерфейсом Executor

from concurrent.futures import ProcessPoolExecutor
from openpyxl import load_workbook
import pandas as pd
import numpy as np


def load_single(file):
    file_name = rootdir + "/" + str(file)
    wb_loop = load_workbook(file_name, read_only=True, data_only=True)
    ws_loop = wb_loop["SHEET1"]
    df = pd.DataFrame(ws_loop.values)
    partial_array = df.loc[4:43, :13].values.astype(np.float32)
    return partial_array

def run():
    executor = ProcessPoolExecutor(max_workers=4)
    files = unique_file_names[1:]
    results = executor.map(load_single, files)
    new_array = np.empty((0, 39), dtype=np.float32)
    for partial_array in results:
        new_array = np.vstack([new_array, partial_array])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...