Ускорение данных Excel в Pandas - программирование
Подтвердить что ты не робот

Ускорение данных Excel в Pandas

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

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")

'''' This outputs an average time per loop of 1.6-1.8s. Thanks for everyones help.

4b9b3361

Ответ 1

Вот быстрый тест (расширяющий этот). По-видимому, прямое использование 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)    

Ответ 2

  1. Если это возможно, используйте формат .csv, а не .xlsx
  2. Используйте pd.read_excel() и укажите имя листа и имена столбцов

Ответ 3

Это все еще WIP, но посмотрите, можете ли вы использовать этот модуль - https://github.com/modin-project/modin

Ответ 4

Два совета:

  • 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])