Как быстро открыть файл Excel на Python?

#python #excel

#python #excel

Вопрос:

Сейчас я использую pyExcelerator для чтения файлов Excel, но это чрезвычайно медленно. Поскольку мне всегда нужно открывать файлы Excel размером более 100 МБ, загрузка только одного файла занимает у меня более двадцати минут.

Мне нужны следующие функциональные возможности:

  • Откройте файлы Excel, выберите определенные таблицы и загрузите их в объект Dict или List.
  • Иногда: выберите определенные столбцы и загружайте только целые строки, в которых определенные столбцы имеют определенные значения.
  • Читайте файлы Excel, защищенные паролем.

И код, который я использую сейчас, является:

 book = pyExcelerator.parse_xls(filepath)
parsed_dictionary = defaultdict(lambda: '', book[0][1])
number_of_columns = 44
result_list = []
number_of_rows = 500000
for i in range(0, number_of_rows):
    ok = False
    result_list.append([])
    for h in range(0, number_of_columns):
        item = parsed_dictionary[i,h]
        if type(item) is StringType or type(item) is UnicodeType:
            item = item.replace("t","").strip()
        result_list[i].append(item)
        if item != '':
            ok = True
    if not ok:
        break
  

Есть предложения?

Комментарии:

1. Вы уже пробовали другие библиотеки? (У меня нет технических знаний по этому вопросу, мне просто интересно)

2. Да, я пытался, но у них всегда нет функциональности при написании xls. После прочтения больших xlses я также должен выполнить некоторые вычисления и сохранить результаты в небольшом xls.

3. @FelixYan: Хорошо, приятно знать, надеюсь, вы получите несколько хороших ответов!

4. Для части записи вы могли бы использовать xlwt или, если вы просто записываете значения, вы могли бы использовать формат CSV (который можно легко импортировать в Excel).

5. Эти 20 минут включают только pyExcelerator.parse_xls() или вы считаете свой собственный последующий код?

Ответ №1:

pyExcelerator, похоже, не поддерживается. Для записи файлов xls используйте xlwt, который является форком pyExcelerator с исправлениями ошибок и множеством улучшений. (Очень простая) возможность чтения xls pyExcelerator была удалена из xlwt. Для чтения файлов xls используйте xlrd.

Если загрузка xls-файла размером 100 МБ занимает 20 минут, вы, должно быть, используете один или несколько: медленный компьютер, компьютер с очень небольшим объемом доступной памяти или более старую версию Python.

Ни pyExcelerator, ни xlrd не читают файлы, защищенные паролем.

Вот ссылка, которая охватывает xlrd и xlwt.

Отказ от ответственности: Я автор xlrd и сопровождающий xlwt.

Комментарии:

1. Спасибо, и я попробую эти два. На самом деле я использую AMD Phenom II X4 945 с 4G RAM и 2G или более из них бесплатны, SSD и Python 2.7 в ОС Linux x86_64. В другом месте процесс чтения может быть еще медленнее.

Ответ №2:

xlrd довольно хорош для чтения файлов, а xlwt довольно хорош для записи. По моему опыту, оба превосходят pyExcelerator.

Ответ №3:

Вы могли бы попробовать предварительно распределить список по его размеру в одной инструкции вместо добавления по одному элементу за раз, как это: (одно большое выделение памяти должно быть быстрее, чем много маленьких)

 book = pyExcelerator.parse_xls(filepath)
parsed_dictionary = defaultdict(lambda: '', book[0][1])
number_of_columns = 44
number_of_rows = 500000
result_list = [] * number_of_rows 
for i in range(0, number_of_rows):
    ok = False
    #result_list.append([])
    for h in range(0, number_of_columns):
        item = parsed_dictionary[i,h]
        if type(item) is StringType or type(item) is UnicodeType:
            item = item.replace("t","").strip()
        result_list[i].append(item)
        if item != '':
            ok = True
    if not ok:
        break
  

Если это дает заметное увеличение производительности, вы также можете попробовать предварительно распределить каждый элемент списка по количеству столбцов, а затем присвоить им индекс, а не добавлять по одному значению за раз. Вот фрагмент, который создает двумерный список размером 10×10 в одной инструкции с начальным значением 0:

 L = [[0] * 10 for i in range(10)]
  

Итак, в вашем коде это может работать примерно так:

 book = pyExcelerator.parse_xls(filepath)
parsed_dictionary = defaultdict(lambda: '', book[0][1])
number_of_columns = 44
number_of_rows = 500000
result_list = [[''] * number_of_rows for x in range(number_of_columns)]
for i in range(0, number_of_rows):
    ok = False
    #result_list.append([])
    for h in range(0, number_of_columns):
        item = parsed_dictionary[i,h]
        if type(item) is StringType or type(item) is UnicodeType:
            item = item.replace("t","").strip()
        result_list[i,h] = item
        if item != '':
            ok = True
    if not ok:
        break
  

Комментарии:

1. Проблема в том, что я не знаю размер файла xls. Таким образом, number_of_rows переменная имеет только максимальный размер, я думаю. Итак… не займет ли предварительное выделение слишком много памяти?

2. Вы знаете количество столбцов, но не строк? Исправлено ли количество столбцов? В любом случае, возможно, стоит попробовать. Проведите сравнение производительности подмножеств с помощью двух разных алгоритмов, скажем, для 1000 строк. Вы можете оценить оттуда.

3. Спасибо. Конечно, стоит попробовать: P. И вы правы, количество столбцов фиксировано, и я не знаю количество строк.

4. Вы могли бы установить количество строк, повторяя до item != '' и просто увеличивая счетчик. Это был бы дополнительный шаг перед назначением, но это установило бы верхнюю границу.

5. Еще одна вещь, которую вы могли бы сделать, это посмотреть на свой процесс с помощью приложения для мониторинга производительности. Следите за увеличением объема памяти и особенно за ошибками страниц. Ошибки страницы заглушат вложенный цикл, подобный вашему, и предварительное распределение — хороший способ обойти их.

Ответ №4:

Не имеет отношения к вашему вопросу: Если вы пытаетесь проверить, не является ли ни один из столбцов пустой строкой, тогда вы устанавливаете ok = True изначально и делаете это вместо этого во внутреннем цикле ( ok = ok and item != '' ). Кроме того, вы можете просто использовать isinstance(item, basestring) , чтобы проверить, является ли переменная строковой или нет.

Пересмотренная версия

 for i in range(0, number_of_rows):
    ok = True
    result_list.append([])
    for h in range(0, number_of_columns):
        item = parsed_dictionary[i,h]
        if isinstance(item, basestring):
            item = item.replace("t","").strip()
        result_list[i].append(item)
        ok = ok and item != ''

    if not ok:
        break
  

Комментарии:

1. Спасибо! Меня так долго не устраивала эта type(item) is StringType or type(item) is UnicodeType вещь! Но я не думаю, что ok = ok and item != '' легко читать после этого, только немного халтурно 🙂

Ответ №5:

Недавно я создал библиотеку, которая может представлять интерес: https://github.com/ktr/sxl. По сути, он пытается «передавать потоком» файлы Excel, как это делает Python с обычными файлами, и поэтому работает очень быстро, когда вам нужно только подмножество данных (особенно, если оно находится в начале файла).

Комментарии:

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