- Питон: работа с файлами
- Ввод данных
- Вывод
- Считываем числовые данные из файла на Python
- Введение
- Способы чтения данных из файла
- Способ 1 — построчное считывание с преобразованием
- Способ 2 — преобразование при помощи map
- Способ 3 — с использованием регулярного выражения
- Способ 4 — с использованием CSV Reader
- Способ 5 — Numpy loadtxt
- Способ 6 — Numpy genfromtxt
- Способ 7 — Pandas read_csv
- Методы тестирования скорости чтения
- Сравнение с компилируемыми языками программирования
- Fortran
- C++
- Результаты тестирования
- Итог
- 10 commentaries to post
Питон: работа с файлами
На серьезных олимпиадах, а также во многих других ситуациях вам надо читать данные не с клавиатуры, а из файла, и выводить данные в файл, а не на «экран». В таком случае вы должны знать имена этих файлов; в задачах они, как правило, указаны, на этом сайте имена файлов почти всегда — input.txt для входных данных и output.txt для выходных.
Во многих языках программирования ввод/вывод данных через файлы очень похож на ввод/вывод с клавиатуры — те же команды, только немного другие параметры. В питоне, к сожалению, это не так.
Ввод данных
Самый простой способ ввести данные из файла в питоне — это сразу считать их в массив строк. Это делается так:
data = open("input.txt", "r").readlines()
Здесь input.txt — файл, откуда надо считать данные, параметр «r» указывает, что вы собираетесь именно читать (read) данные, а не записывать (write, см. ниже). Команда open , как говорят, «открывает файл на чтение», а команда readlines считывает весь файл в массив строк.
Теперь data — это массив строк, каждый элемент которого — это очередная строка из входного файла. Например, если в файле было написано
то data будет содержать массив [«1 2 3\n», «4 5 6\n», «some text\n»] . (Здесь «\n» — это символ перевода строки, он будет на конце каждой строки массива data , кроме, возможно, последней. Если вы считываете числа, то можете о нем не беспокоиться, а если считываете строки и он вам мешает, то, как правило, от него можно избавиться командой a = a.rstrip(«\n») , где a — строка, у которого вы хотите его убрать.)
Каждый элемент массива data — это как результат отдельной команды input() , которой вы пользовались для чтения с клавиатуры. Поэтому дальше вы пишете программу так же, как писали и раньше, но вместо каждого очередного обращения к input() вы обращаетесь к очередному элементу массива data . В простых случаях все просто, в более сложных вам надо аккуратно подсчитывать, к какому именно элементу массива вам надо обратиться.
Пример. Пусть во входном файле два числа по одному на строке. Их считываем так (здесь и далее слева — пример чтения с клавиатуры, справа — из файла):
data = open("input.txt", "r").readlines() a = int(data[0]) b = int(data[1])
Если же два числа в одной строке:
data = open("input.txt", "r").readlines() a, b = map(int, data[0].split())
Более сложный пример: сначала число N , а потом N строк по одному числу в каждой:
n = int(input()) for i in range(n): x = int(input()) #. что-то делаем с x
data = open("input.txt", "r").readlines() n = int(data[0]) for i in range(n): x = int(data[i + 1]) #. что-то делаем с x
Обратите внимание на то, что здесь написано i + 1 — потому что в первой (нулевой) строке было n .
Вывод
Проще всего выводить данные — записав их в одну большую строку и выведя эту строку. Вывод строки в файл делается так:
здесь s — строка, которую нужно вывести. Это должна быть именно строка, а не число и т.д. Используйте функцию str , чтобы превратить что угодно в строку, вручную добавляйте пробелы и переводы строк (перевод строки записывается так: «\n» ).
Пример: пусть надо вывести два числа в одну строку:
open(«output.txt», «w»).write(str(a) + » » + str(b))
Пусть надо вывести два числа на отдельных строках:
open(«output.txt», «w»).write(str(a) + «\n» + str(b))
Пусть надо вывести массив чисел по одному числу на строке:
s = "" for x in a: s = s + str(x) + "\n" open("output.txt", "w").write(s)
или проще, используя строковую магию:
open("output.txt", "w").write("\n".join(a))
Имейте в виду, что такой вызов должен быть только один раз. Если вы хотите вызывать функцию write несколько раз, то надо суметь вызвать функцию open только один раз. Рекомендуемый вариант — такой:
with open("output.txt", "w") as f: f.write("1") f.write("2")
Считываем числовые данные из файла на Python
В данной статье речь пойдет о простой на первой взгляд задаче — считывании числовых данных из текстовых файлов на Python. В сети можно найти десятки способов решения этой задачи, однако эти алгоритмы оказываются малоэффективными при работе с большим объемом данных. В данной статье будут разобраны самые популярные методики, а также произведено сравнение их скорости работы.
Введение
Когда я только начинал изучать Python, главным помощником в работе для меня, как наверное и для большинства программистов, был Stack Overflow. Я почерпнул оттуда много полезной информации, в том числе и о работе с файлами. Однако даже такая тривиальная задача, как оказалось, имеет несколько различных решений, отличающихся друг от друга простотой реализации и скоростью работы.
Большинство предложенных методов предполагают чтение файла построчно с дальнейшим разбиением на блоки и их преобразованием из строкового типа в числовой, поскольку Python в отличии от C/C++ работает с файлами как с массивом строк. Выполнить последовательное чтение данных в массив без преобразования типов, как это можно сделать в C/C++, стандартными средствами языка невозможно (насколько мне известно), и это существенно увеличивает время работы программы при обработке больших объемов данных.
Способы чтения данных из файла
Как уже было сказано выше, файлы в Python представляют собой массив строк, поэтому все найденные методы можно символически поделить на два типа в зависимости от используемого подхода:
- построчное считывание с разбиением и преобразованием типов
- использование библиотек, которые средствами других языков (например, C/C++) считывают файл и передают полученные данные интерпретатору Python
Ниже представлена подборка самых популярных методов чтения числовых данных на Python, отмеченных сообществом Stack Overflow как «best answer».
Способ 1 — построчное считывание с преобразованием
Самый популярный и простой вариант. Заключается в построчном чтении с разбиением полученной строки на блоки, которые затем преобразуются к необходимому типу данных (в данном случае float) и добавляются к заранее созданному списку.
data = [] with open("data.txt") as f: for line in f: data.append([float(x) for x in line.split()])
Способ 2 — преобразование при помощи map
Способ аналогичен предыдущему, за исключением того, что преобразованием данных из строкового формата в числовой занимается функция map.
file = open("data.txt", "r") data = [map(float, line.split("\t")) for line in file]
Способ 3 — с использованием регулярного выражения
Данный способ можно назвать стрельбой из пушки по воробьям, однако у него все же есть свои плюсы: если данные в файле расположены хаотично и отсутствует постоянная структура, то функции split невозможно задать конкретный разделитель и для решения задачи можно использовать регулярное выражение, которое найдет в строке все числа, несмотря на их расположение и наличие разделителей.
import re file = open("data.txt") values = file.read().split("\n") data = [] for key in values: value = re.findall(r"[-+]?\d*\.\d+|\d+", key) if value != []: data.append(value)
Способ 4 — с использованием CSV Reader
Если данные записаны в виде матрицы с постоянными разделителями, то выполнить их чтение можно при помощи модуля CSV Reader, указав в качестве параметра значение разделителя.
import csv with open("data.txt") as f: data = [map(float, row) for row in csv.reader(f, delimiter='\t')]
Способ 5 — Numpy loadtxt
Библиотека Numpy предоставляет широкий набор модулей и функций для обработки числовых данных, в том числе и для чтения массивов из файлов. Одна из реализаций возможна с помощью функции loadtxt, результат работы которой будет записан в numpy.array.
import numpy as np data = np.loadtxt("data.txt", delimiter='\t', dtype=np.float)
Способ 6 — Numpy genfromtxt
Данный способ не сильно отличается от предыдущего, за исключением того, что genfromtxt предоставляет более широкий набор входных параметров: указание различных типов данных для каждого из столбцов, передача ключей для создания ассоциативного массива и так далее.
import numpy as np data = np.genfromtxt("data.txt", delimiter='\t', dtype=np.float)
Способ 7 — Pandas read_csv
Pandas — мощная библиотека для обработки данных на Python. В данном примере рассматривается только чтение данных, но её возможности этим не ограничены. Метод read_csv предоставляет широкий набор входных параметров, а также показывается высокую скорость работы даже при работе с большими объемами данных.
import pandas as pd data = pd.read_csv("data.txt", sep="\t", header=None)
Методы тестирования скорости чтения
Для тестирования скорости чтения числовых данных были сгенерированы 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 строчки кода при условии корректности входных данных.
real, dimension (5, 1000) :: data open (1, file='data.txt') read(1, *) data
C++
Дискуссии о том, что лучше: Fortran или C++ ведутся уже давно, даже среди авторов EasyCoding этот спор возникал несколько раз, поэтому мне было еще интересней протестировать чтение матриц на данном языке.
ifstream file(«data.txt»); int count = 100000; float** data = new float*[count]; for(int i = 0; i
Результаты тестирования
В ходе эксперимента были протестированы 7 программ на языке Python и по одной на Fortran и C++, код которых представлен выше. Запуск программ осуществлялся на компьютере с Intel Core i5 2.7 GHz и 8 Гб оперативной памяти.
Для запуска программ использовались следующие интерпретаторы и компиляторы:
Для каждой программы проводилась серия испытаний и измерялось время работы, после чего записывался результат в виде среднего арифметического полученных данных. В таблице ниже жирным в каждой строке выделено наименьшее время работы в зависимости от способа чтения и размера входного файла.
Число строк | Способ | ||||||||
---|---|---|---|---|---|---|---|---|---|
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, который предоставляет более широкий набор входных параметров: указание различных типов данных для каждого из столбцов, передача ключей для создания ассоциативного массива и так далее.
Спасибо. Сэкономили время на поиск единственного, что нужно для моих вычислений по таблице «тексты-слова»…
FILE* f = fopen(«data.txt», «rb») int count = 10000; float** data = new float*[count]; for(int i = 0; i < count; ++i)
Причем не особо кошерная реализация. Но соответствует предоставленному коду.
Кошерная реализация это:
fread(data, sizeof(float) * 5 * count, f);