Считываем числовые данные из файла на Python
В данной статье речь пойдет о простой на первой взгляд задаче — считывании числовых данных из текстовых файлов на Python. В сети можно найти десятки способов решения этой задачи, однако эти алгоритмы оказываются малоэффективными при работе с большим объемом данных. В данной статье будут разобраны самые популярные методики, а также произведено сравнение их скорости работы.
Введение
Когда я только начинал изучать Python, главным помощником в работе для меня, как наверное и для большинства программистов, был Stack Overflow. Я почерпнул оттуда много полезной информации, в том числе и о работе с файлами. Однако даже такая тривиальная задача, как оказалось, имеет несколько различных решений, отличающихся друг от друга простотой реализации и скоростью работы.
Большинство предложенных методов предполагают чтение файла построчно с дальнейшим разбиением на блоки и их преобразованием из строкового типа в числовой, поскольку Python в отличии от C/C++ работает с файлами как с массивом строк. Выполнить последовательное чтение данных в массив без преобразования типов, как это можно сделать в C/C++, стандартными средствами языка невозможно (насколько мне известно), и это существенно увеличивает время работы программы при обработке больших объемов данных.
Способы чтения данных из файла
Как уже было сказано выше, файлы в Python представляют собой массив строк, поэтому все найденные методы можно символически поделить на два типа в зависимости от используемого подхода:
- построчное считывание с разбиением и преобразованием типов
- использование библиотек, которые средствами других языков (например, C/C++) считывают файл и передают полученные данные интерпретатору Python
Ниже представлена подборка самых популярных методов чтения числовых данных на Python, отмеченных сообществом Stack Overflow как «best answer».
Способ 1 — построчное считывание с преобразованием
Самый популярный и простой вариант. Заключается в построчном чтении с разбиением полученной строки на блоки, которые затем преобразуются к необходимому типу данных (в данном случае float) и добавляются к заранее созданному списку.
Способ 2 — преобразование при помощи map
Способ аналогичен предыдущему, за исключением того, что преобразованием данных из строкового формата в числовой занимается функция map.
Способ 3 — с использованием регулярного выражения
Данный способ можно назвать стрельбой из пушки по воробьям, однако у него все же есть свои плюсы: если данные в файле расположены хаотично и отсутствует постоянная структура, то функции split невозможно задать конкретный разделитель и для решения задачи можно использовать регулярное выражение, которое найдет в строке все числа, несмотря на их расположение и наличие разделителей.
Способ 4 — с использованием CSV Reader
Если данные записаны в виде матрицы с постоянными разделителями, то выполнить их чтение можно при помощи модуля CSV Reader, указав в качестве параметра значение разделителя.
Способ 5 — Numpy loadtxt
Библиотека Numpy предоставляет широкий набор модулей и функций для обработки числовых данных, в том числе и для чтения массивов из файлов. Одна из реализаций возможна с помощью функции loadtxt, результат работы которой будет записан в numpy.array.
Способ 6 — Numpy genfromtxt
Данный способ не сильно отличается от предыдущего, за исключением того, что genfromtxt предоставляет более широкий набор входных параметров: указание различных типов данных для каждого из столбцов, передача ключей для создания ассоциативного массива и так далее.
Способ 7 — Pandas read_csv
Pandas — мощная библиотека для обработки данных на Python. В данном примере рассматривается только чтение данных, но её возможности этим не ограничены. Метод read_csv предоставляет широкий набор входных параметров, а также показывается высокую скорость работы даже при работе с большими объемами данных.
Методы тестирования скорости чтения
Для тестирования скорости чтения числовых данных были сгенерированы 7 тестовых файлов, содержащих 5 столбцов и 10, 100, 1 000, 10 000, 100 000, 1 000 000 и 10 000 000 строк случайных чисел формата float. Размер самого большого файла составил 742 Мб.
Для измерения времени работы программы использовалась функция time. Существует мнение, что измерять с её помощью время работы некорректно. Однако в данном случае меня интересовало работа с большими объемами данных, когда время работы программы составляло несколько десятков секунд. В таком случае отклонение в полсекунды вносило погрешность менее 1%.
Сравнение с компилируемыми языками программирования
Программы, созданные на компилируемых языках программирования, работают быстрее, чем их аналоги, написанные на интерпретируемых языках. Мне было интересно сравнить скорость чтения каждого метода с Fortran и C++ — самыми популярными языками в научном программировании, с которыми мне также приходится иметь дело в силу специфики моей работы.
Fortran
Несмотря на то, что Fortran считается устаревшим языком, он все еще очень популярен в научном программировании благодаря простоте написания кода, скорости обмена данных и обширном количестве библиотек, созданных за последние полвека.
Например, считать числовую матрицу из файла можно всего за 3 строчки кода при условии корректности входных данных.
Дискуссии о том, что лучше: Fortran или C++ ведутся уже давно, даже среди авторов EasyCoding этот спор возникал несколько раз, поэтому мне было еще интересней протестировать чтение матриц на данном языке.
Результаты тестирования
В ходе эксперимента были протестированы 7 программ на языке Python и по одной на Fortran и C++, код которых представлен выше. Запуск программ осуществлялся на компьютере с Intel Core i5 2.7 GHz и 8 Гб оперативной памяти.
Для запуска программ использовались следующие интерпретаторы и компиляторы:
GNU Fortran (GCC) 6.1.0
Для каждой программы проводилась серия испытаний и измерялось время работы, после чего записывался результат в виде среднего арифметического полученных данных. В таблице ниже жирным в каждой строке выделено наименьшее время работы в зависимости от способа чтения и размера входного файла.
| Число строк | Способ | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fortran | C++ | |
| 10 | 0.048 | 0.048 | 0.045 | 0.044 | 0.173 | 0.216 | 0.479 | 0.005 | 0.005 |
| 100 | 0.053 | 0.052 | 0.05 | 0.048 | 0.185 | 0.223 | 0.511 | 0.007 | 0.006 |
| 1 000 | 0.056 | 0.053 | 0.053 | 0.052 | 0.187 | 0.233 | 0.6 | 0.01 | 0.01 |
| 10 000 | 0.085 | 0.076 | 0.096 | 0.083 | 0.305 | 0.292 | 0.636 | 0.032 | 0.041 |
| 100 000 | 0.414 | 0.403 | 0.561 | 0.482 | 1.537 | 0.874 | 0.796 | 0.244 | 0.363 |
| 1 000 000 | 3.835 | 4.502 | 6.086 | 5.276 | 13.607 | 6.754 | 1.763 | 2.584 | 3.662 |
| 10 000 000 | 47.931 | 156.944 | 137.398 | 144.75 | 162.724 | 85.642 | 13.632 | 25.652 | 36.622 |
В ходе данного исследования были протестированы 7 самых популярных варианта чтения числовых матриц на языке Python, предложенными пользователями сайта Stack Overflow и отмеченными сообществом как «верный ответ». Как видно из таблицы с результатами, скорость работы программ не сильно отличается при использовании способов 1-4 на небольших объемах данных. Это связано с тем, что интерпретатор не тратит время на инициализацию сторонней библиотеки, как в методах 5-7.
Однако при увеличении объема входных данных лучше всех себя показал метод 7 с использованием библиотеки Pandas, который даже обогнал по скорости чтения данных языки C++ и Fortran.
Также из результатов теста можно видеть, что программа на Fortran справилась с чтением данных быстрей аналога на C++, что еще раз доказывает его превосходство над самым популярным языком программирования в мире.
10 commentaries to post
Наконец, нашел, что искал. Способ 6 — Numpy genfromtxt, который предоставляет более широкий набор входных параметров: указание различных типов данных для каждого из столбцов, передача ключей для создания ассоциативного массива и так далее.
Спасибо. Сэкономили время на поиск единственного, что нужно для моих вычислений по таблице «тексты-слова»…
ошибка в таблице ! 0.044 не меньше чем 0.005 а больше почти в 9 раз!
Нет ошибки. Автор сравнивал скорости Python решений.
С++, который обгонит всё перечисленное:
Причем не особо кошерная реализация. Но соответствует предоставленному коду.
Кошерная реализация это:
Спасибо за приведенное полезное сравнение. Не хватает сравнения скорости записи (в рам для точности)
К способу 6:
ModuleNotFoundError: No module named ‘numpy’
Ругается на import в начале
Необходимо установить соответствующую библиотеку numpy либо из репозиториев, либо посредством pip.
2 способ выдает:
[/, /, /]
при числах в файле:
1 2 3
4 5 6
7 8 9
Что не так?
Проверьте версию интерпретатора Python. В 3.7 работает нормально.
На С++ вы читали потоками, это медленно. Надо было попробовать функциями ввода вывода,fopen, fclose, fread должно быть быстрее. В С++ тоже несколько способов. Могло получиться сопоставимо с лучшим результатом.
Name already in use
py_training_ru / lecture_08.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Лекция 8. Ввод-вывод в файлы. Конструкция with
Значки, используемые в тексте
- — отмечает, что высказывание не совсем верно, но будет уточнено позднее.
- ⭐ — выделяет определение нового понятия.
- ❗ — привлекает внимание к высказыванию.
- ☝️ — указывает на небольшое отступление от темы или уточнение.
- — индикатор традиций и договорённостей.
- ⚠️ — требуется осторожность: возможно, сложное высказывание.
Повторение терминов из седьмой лекции
- Конструкция try-except-else — синтаскическая конструкция (сложное утверждение), предназначенная для обработки исключений из блока try в блоках except и выполнения блока else в случае отсутствия ошибок.
- Утверждение raise — утверждение, дающее возможность непосредственно вызвать исключение в коде.
Что такое файл? С точки зрения Python, это объект файловой системы, поддерживающий потоковое чтение и запись.
Для того, чтобы начать работу с файлом (чтение или запись), необходимо открыть файл, с указанием вида работы.
Файл обычно идентифицируется именем, и Python также использует имя файла для его открытия, которое осуществляется встроенной функцией open() :
Функция open() принимает как минимум 1 аргумент — имя файла — и возвращает объект типа file (также встроенный в Python тип), с которым далее и ведётся работа. Вторым аргументом функции указывается способ работы, то есть чтение или запись; по умолчанию, файл открывается на чтение.
У объекта файла есть набор атрибутов и методов, которые в дальнейшем используются для работы с файлом.
Имя файла, связанного с объектом, доступно через атрибут file.name :
При отсутствии такого файла или невозможности открыть его (например, нет разрешений, либо файл является каталогом) возникнет исключение IOError .
Чтобы прочитать содержимое файла, используется метод file.read() . Он возвращает Python-строку ( str ) состоящую из всего содержимого файла, либо если передать ему 1 аргумент — целое число N, то строка будет состоять из первых N байт файла (либо файла целиком, если его размер меньше N байт).
Можно написать программу из 2 строк, которая выведет собственный код, для этого достаточно сохранить её в файл например program1.py и написать:
Указатель позиции в файле
Если вызвать метод file.read() без аргументов более одного раза, то он вернёт пустую строку:
Объяснение этому таково. Файл можно представить как упорядоченную последовательность байт, каждый из которых имеет индекс, начиная с 0 (первый байт) и до «размер файла — 1». При чтении данных из файла, каждый байт условно по очереди передаётся в строку-результат, и индекс (указатель на текущий байт) увеличивается. По окончании чтения, индекс остаётся в том же месте, и следующее чтение начнётся с этого индекса. Если мы уже прочитали весь файл, то логично, что данных после текущего указателя уже нет.
Метод file.read() не только читает данные из файла, но и увеличивает указатель на кол-во прочитанных байт. Если в цикле читать из файла по одному байту, то каждый раз в результате чтения будет следующий байт, до тех пор, пока мы не достигнем конца файла.
Текстовые и бинарные файлы
Во многих системах файлы бывают двух «разновидностей»: текстовые и двоичные (бинарные). В общем любой файл можно считать бинарным, особенно если нам не требуется работать с файлом по частям. Если мы считываем весь файл в строку с помощью метода file.read() , то далее мы можем работать со строкой, полностью игнорируя вид файла.
Главное отличие этих видов в семантике доступа: для текстовых файлов имеет смысл построчный доступ, для бинарных — посимвольный или буферизованный.
С точки зрения пользователя, вид файла не всегда имеет значение, скажем, файл с данными xml или json по содержанию является текстовым, но едва ли имеет смысл обрабатывать его построчно, так как данные представляют собой единую структуру. В то же время файл типа doc для пользователя представляет двоичную кодировку текста.
Работа с текстовыми файлами
Типичным текстовым файлом является файл программы на Python. Для построчного чтения текстовых файлов существует метод file.readline() , который читает строку из файла и увеличивает указатель на длину прочитанной строки. При этом конец строки определяется настройками текущей ОС: для Linux концом строки служит символ перевода строки «\n» (или «\x0a» ), для Windows — последовательность «\r\n» (или «\x0d\x0a» ).
Этот метод удобно использовать в цикле while , опять же проверяя, что считанная строка не пустая. Также в этом методе можно задавать максимальную длину считываемой строки.
Для большего удобства построчного чтения файлов целиком, в цикле for мы можем использовать метод file.readlines() :
Или даже ещё проще:
Объект типа файл является итерируемым, как списки и строки, и итерируется по строкам, содержащимся в файле.
Работа с бинарными файлами
Для итеративной (поблочной) работы с бинарными файлами используется метод file.read(N) , где N — размер считываемого блока. Проверять, что достигнут конец файла, можно, сравнивая длину результата с N, либо также считывать до получения пустой строки.
У бинарных файлов есть ещё одно отличие: мы можем узнать, где сейчас указатель, и передвинуть указатель вручную на нужное место. (То есть, мы можем это делать и в текстовых файлах, но там это бессмысленно.) Для этого предназначены два соответствующих метода: file.tell() и file.seek() .
Первый возвращает индекс байта, на котором сейчас указатель; второй — принимает число-индекс в качестве аргумента и устанавливает указатель на этот индекс.
Таким образом, мы в теории сможем при достижении конца файла вернуть указатель на начало и прочитать содержимое заново. Впрочем, на практике это крайне редко используется.
Для разрешения записи в файл, как мы уже упомянули выше, необходимо сообщить функции open() , что мы открываем файл на запись. Режим работы с файлом передаётся вторым аргументом функции open() и представляет собой строку.
Запись может быть двух видов: перезапись (создание файла, если его ещё нет, либо пересоздание файла, если он уже есть) и дозапись (добавление в конец файла, если он есть, либо ошибка IOError в случае отсутствия).
Режим записи обозначается строкой «w» , режим добавления — строкой «a» .
Далее, собственно писать в файл можно исользуя метод file.write() , который принимает параметр — Python-строку с содержимым для записи. При записи, как и при чтении, указатель позиции в файле увеличивается на количество записанных байт.
Однако если просто записать в открытый файл, записанные данные не сохранятся. Для сохранения данных необходимо закрыть файл, это делается методом file.close() .
Примечание: файлы сами закрываются при выходе из программы, но крайне рекомендуется закрывать их вручную во избежание ошибок.
Смешанные режимы открытия файла
Возможно также открыть файл в режиме, совмещающем чтение и запись. Существует три таких режима:
- чтение с возможностью записи, режим «r+»
- перезапись с возможностью чтения, режим «w+»
- дозапись с возможностью чтения, режим «a+»
Они в целом соответствуют своим «основным» режимам.
Некоторые синтаксические конструкции, облегчающие работу с файлами
Представим себе ситуацию, что мы обрабатываем файл построчно и ищем какую-то строку, после которой дальнейшая обработка уже не имеет смысла. Используя цикл for , нам бы пришлось создавать переменную-флаг и «вхолостую» пробегать остаток файла, проверяя этот флаг в каждой итерации.
К счастью, в Python можно обойтись куда более простой конструкцией.
Утверждение break даёт команду немедленно выйти из цикла к следующему за ним утверждению, полностью пропустив оставшиеся итерации и также все утверждения, идущие в блоке кода цикла после break .
В данном случае будет выводиться на экран каждая строка до строки, содержащей «end», не включая её, потому что цикл закончится до вывода её на экран.
Утверждение break можно использовать не только в обработке файлов, и не только в цикле for : циклы while также поддерживают его. Но в любом другом блоке кода, который не относится к циклам, использование break является синтаксической ошибкой.
Утверждение continue является дополнением break : оно пропускает все следующие за ним утверждения в блоке кода цикла и переходит к следующей его итерации. В случае с циклом for это означает — к обработке следующего элемента (если он есть), в случае while — к следующей проверке условия.
Его удобно использовать, если нам нужно, наоборот, начать обработку файла с какой-то строки файла или пропустить обработку строки.
Этот код будет пропускать все строки файла до строки, содержащей «start», начиная с неё, он начнёт выводить все те строки, которые не содержат «skip».
Объект файла, будучи итерируемым объектом (типа контейнера) является ещё и так называемым контекстным менеджером.
Контекстный менеджер умеет автоматически выполнять некторые действия по окончанию выполнения определённого контекста, который задаётся конструкцией with :
БЛОК_КОДА в данном случае и является контекстом. Независимо от того, произошли ли ошибки в БЛОКе_КОДА, контекстный менеджер всегда выполнит завершающие действия, а именно для файлов — их закрытие.
Возьмём пример, использованный выше:
Его можно переписать так:
И файл автоматически будет закрыт по окончанию блока кода в with .
Записать в новый текстовый файл числа от 1 до 10 по одному в строке.
- в файл можно записать только строковые данные.
- В конец записываемой строки необходимо добавлять символы перевода строки, для Windows это последовательность «\r\n» .
Прочитать из записанного файла числа от 3 до 7 включительно, используя break и continue . В результате должен получиться список из 5 чисел.
Примеры работы с текстовыми файлами
В данной теме представлены примеры записи и чтения информации для текстовых файлов.
Содержание
- 1. Чтение/запись списка, содержащего n целых чисел
- 2. Чтение/запись списка, содержащего строки
- 3. Чтение/запись кортежа, содержащего объекты чисел с плавающей запятой
- 4. Чтение/запись кортежа содержащего разнотипные объекты
- 5. Чтение/запись словаря
- 6. Чтение/запись двумерной матрицы целых чисел, представленной в виде списка
- 7. Чтение/запись множества, которое содержит целые числа
- 8. Чтение/запись данных разных типов: список и кортеж
Поиск на других ресурсах:
1. Чтение/запись списка, содержащего n целых чисел
В примере демонстрируются следующие операции:
- создание списка из 10 случайных чисел;
- сохранение списка в текстовом файле;
- чтение из файла в новый список с целью контроля.
Текст программы следующий:
Результат работы программы
2. Чтение/запись списка, содержащего строки
При чтении/записи строк не нужно реализовывать дополнительные преобразования из одного типа в другой, так как данные из файла читаются в виде строк.
Результат работы программы
3. Чтение/запись кортежа, содержащего объекты чисел с плавающей запятой
Пример демонстрирует запись и чтение кортежа, который содержит объекты чисел с плавающей запятой.
Результат работы программы
Вид файла myfile5.txt
4. Чтение/запись кортежа содержащего разнотипные объекты
В случае, если кортеж содержит объекты разных типов при записи/чтении важно придерживаться последовательности этапов конвертирования объектов в нужный тип. Ниже приведен пример записи и чтения кортежа, который содержит объекты целого, логического и строчного типов.
Результат выполнения программы
5. Чтение/запись словаря
Словарь также можно записывать в файл. В данном примере записывается и читается словарь, который содержит перечень номеров дней недели и их названий. Для облегчения чтения данных каждый элемент словаря размещается в отдельной строке.
Результат работы программы
Вид файла myfile6.txt
6. Чтение/запись двумерной матрицы целых чисел, представленной в виде списка
В примере демонстрируется запись и чтение двумерной матрицы целых чисел размерностью 3*4.
Результат работы программы
Вид файла myfile8.txt
7. Чтение/запись множества, которое содержит целые числа
В примере демонстрируется возможный вариант сохранения множества в текстовом файле
Результат работы программы
Вид файла myfile7.txt
8. Чтение/запись данных разных типов: список и кортеж
Чтобы записать в текстовый файл данные разных базовых типов нужно последовательно записать данные одного типа, затем другого типа. При считывании таких данных нужно придерживаться такого самого порядка чтобы не нарушить полученную структуру данных.
В примере демонстрируется последовательная запись в файл списка и кортежа. При чтении придерживается такая же последовательность: сначала читается список, затем кортеж. Список включает строки. Кортеж содержит вещественные числа. Чтобы облегчить работу по распознаванию формата файла, каждый записываемый (читаемый) элемент размещается в отдельной строке файла.
Поскольку список и кортеж могут содержать разное количество элементов, то в файл записываются их размерности.
Reading integers from binary file in Python
I’m trying to read a BMP file in Python. I know the first two bytes indicate the BMP firm. The next 4 bytes are the file size. When I execute:
ValueError: invalid literal for int() with base 10: ‘F#\x13’
What I want to do is reading those four bytes as an integer, but it seems Python is reading them as characters and returning a string, which cannot be converted to an integer. How can I do this correctly?
7 Answers 7
The read method returns a sequence of bytes as a string. To convert from a string byte-sequence to binary data, use the built-in struct module: http://docs.python.org/library/struct.html.
Note that unpack always returns a tuple, so struct.unpack(‘i’, fin.read(4))[0] gives the integer value that you are after.
You should probably use the format string ‘<i’ (< is a modifier that indicates little-endian byte-order and standard size and alignment — the default is to use the platform’s byte ordering, size and alignment). According to the BMP format spec, the bytes should be written in Intel/little-endian byte order.