Перевод статьи Reading and Writing CSV Files in Python.

Передача информации для обработки в прикладных программах, а также представление конечных результатов их работы возможны не только с использованием клавиатуры и консоли. В большинстве случаев для решения подобных задач организуется обмен информацией с помощью текстовых файлов. Этот способ в настоящее время является самым распространенным способом обмена информацией как между программами, так и между программой и пользователем. Одним из популярных в настоящее время  форматов, использующихся для представления табличных данных, является формат CSV.

Отметим, что для того, чтобы использовать CSV при написании своих программ на языке Python, вам не нужно писать свой собственный синтаксический анализатор (парсер). И хотя существует немало хороших библиотек, которые вы можете использовать для работы с  CSV, возможностей предоставляемых стандартной библиотекой Python, а точнее модулем csv из ее состава, достаточно для решения большинства возникающих задач. Если же вам необходимо обработать большое количество данных в формате CSV, например осуществить их статистический анализ, то в составе библиотеки pandas также реализованы удобные методы для чтения и записи файлов в этом формате.

Из этой статьи вы узнаете как читать и записывать файлы в формате CSV, а также организовывать обработку полученных данных с помощью средств языка Python. Мы рассмотрим основы работы модуля csv, а затем ознакомимся с работой парсера данных в формате CSV, входящего в состав библиотеки pandas.

Что такое файл в формате CSV

Файл CSV (Comma Separated Values file — файл со значениями разделительными запятыми) представляет собой вид простого (плоского) текстового файла, который использует специальную структуризацию для организации табличных данных. Поскольку это простой текстовый файл, он может содержать только текстовые данные, другими словами, печатные символы в кодировке ASCII или Unicode.
Способ структурной организации файла CSV следует из его названия, то есть файлы CSV используют запятую (в большинстве случаев) для разделения отдельных значений друг от друга. Так выглядит файл в формате CVS:

column 1 name,column 2 name, column 3 name
first row data 1,first row data 2,first row data 3
second row data 1,second row data 2,second row data 3
...

Обратите внимание, что отдельные значения разделяются с помощью запятых. При этом столбцы данных идентифицируется путем перечисления их имен в первой строке файла. Каждая последующая строка после строки с именами содержит непосредственно данные и ограничена только размерами файла. Разделяющий значения символ называют разделителем и, конечно же, символ , (запятая) не является единственным, который может использоваться для этих целей. Другие популярные символы, использующиеся в качестве разделителя: символ табуляции (\t), двоеточие ( : ) и точка с запятой ( ; ). Отметим, что для корректного синтаксического анализа (парсинга) файла CSV требуется знание вида разделителя, использующегося в этом файле.

Как появились CSV файлы?

Файлы CSV создаются программами, которые обрабатывают большие объемы табличных данных. Файлы CSV представляют удобный способ для экспорта данных из электронных таблиц и баз данных, с последующим использованием в других программах. Например, вы можете экспортировать результаты работы программы статистического анализа данных в файл CSV, а затем импортировать их в структуру специального вида для дальнейшего анализа, построения графиков для презентации или подготовки отчета для публикации.

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

Разбор (парсинг) CSV-файлов с помощью встроенной в Python библиотекой для обработки CSV данных

Модуль csv обеспечивает стандартный функционал для чтения и записи данных в файлы CSV. Разработанный для работы с CSV файлами созданными в Excel, он легко настраивается для работы с любыми разновидностями этого формата. csv содержит объекты и методы для чтения файлов CSV, обработки данных в этом формате, а также записи данных в файл CSV.

Чтение CSV файлов с помощью библиотеки csv

Чтение данных из файла CSV выполняется с использованием объекта reader. Файл CSV открывается как обычный текстовый файл стандартной функцией Python open(), которая возвращает объект типа файл. Затем ссылка на этот объект передается объекту reader.

Рассмотрим для примера файл employee_birthday.txt содержащий данные:

name,department,birthday month
John Smith,Accounting,November
Erica Meyers,IT,March

С помощью следующего кода откроем его для чтения, прочитаем и обработаем содержимое файла:

import csv

with open('employee_birthday.txt') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print(f'Column names are {", ".join(row)}')
            line_count += 1
        else:
            print(f'\t{row[0]} works in the {row[1]} department, and was born in {row[2]}.')
            line_count += 1
    print(f'Processed {line_count} lines.')

Результат его выполнения будет следующим:

Column names are name, department, birthday month
    John Smith works in the Accounting department, and was born in November.
    Erica Meyers works in the IT department, and was born in March.
Processed 3 lines.

Каждая строка, возвращаемая объектом reader, представляет собой список значений типа String, содержащих данные с удаленными разделителями. Кроме того первая строка возвращает имена столбцов, которые должны быть обработаны другим способом.

Чтение CSV файлов в словарь с помощью библиотеки csv

Вместо того, чтобы иметь дело со списком значений типа String, вы можете считывать данные непосредственно в словарь (технически в объект класса Ordered Dictionary).

Например, наш файл employee_birthday.txt имеет следующий вид:

name,department,birthday month
John Smith,Accounting,November
Erica Meyers,IT,March

Приведенный ниже код преобразует прочитанные данные в словарь:

import csv

with open('employee_birthday.txt', mode='r') as csv_file:
    csv_reader = csv.DictReader(csv_file)
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print(f'Column names are {", ".join(row)}')
            line_count += 1
        print(f'\t{row["name"]} works in the {row["department"]} department, and was born in {row["birthday month"]}.')
        line_count += 1
    print(f'Processed {line_count} lines.')

Это приведет к тому же результату, что был получен ранее:

Column names are name, department, birthday month
John Smith works in the Accounting department, and was born in November.
Erica Meyers works in the IT department, and was born in March.
Processed 3 lines.

Откуда взялись значения ключей для словаря? Изначально предполагается, что первая строка файла CSV содержит ключи для создания словаря. Если их нет в вашем файле CSV, вы должны определить имена для ключей, передав их через необязательный именованный параметр fieldnames в виде списка значений имен.

Необязательные параметры объекта reader библиотеки csv

Объект reader может обрабатывать файлы CSV с различными стилями форматирования, указывая значения необязательных параметров, некоторые из которых представлены ниже, а с остальными вы можете ознакомиться в документации перейдя по ссылке:

  • delimiter указывает символ, используемый для разделения полей данных (вид разделителя). По умолчанию используется запятая ,.
  • quotechar указывает символ, используемый для обрамления (обвертывания) полей данных, которые могут после этого содержать кроме других символов символ разделителя. По умолчанию используются двойные кавычки ".
  • escapechar указывает символ, используемый для экранирования символа разделителя, если кавычки для обертывания не используются. Значение по умолчанию — символ экранирования отсутствует.

Применение этих параметров требует дополнительного пояснения. Предположим, вы работаете с файлом employee_addresses.txt следующего содержания:

name,address,date joined
john smith,1132 Anywhere Lane Hoboken NJ, 07030,Jan 4
erica meyers,1234 Smith Lane Hoboken NJ, 07030,March 2

Этот CSV файл содержит три столбца данных: имя name, адрес address и дата добавления date joined, разделенных запятыми. Проблема в том, что данные в полях столбца адрес address тоже содержат запятую для того чтобы выделить номер почтового индекса.

Существует три способа справиться с этой ситуацией:

  • Использовать другой разделитель. Таким образом, запятая может безопасно использоваться в самих данных. Вы используете необязательный именованный параметр delimiter, чтобы указать новый вид разделителя.
  • Обернуть поля данных в кавычки. Специальный вид выбранного вами разделителя игнорируется в строках, обернутых в двойные кавычки. Поэтому вы можете указать вид символа, используемый для обертывания значений полей данных через необязательный параметр quotechar. Пока этот символ отсутствует в значениях полей данных, все будет работать корректно.
  • Экранирование символа разделителя delimiter в данных поля. Экранирующие символы работают так же, как в форматированных строках, не допуская интерпретацию символа, который экранируется (в данном случае символа разделителя delimiter). Если вы используете экранирующие символы, вид его необходимо указать с помощью необязательного именованного параметра escapechar.

Запись CSV файлов с помощью библиотеки csv

Запись данных в файл CSV производится с использованием объекта writer и его метода write_row():

import csv

with open('employee_file.csv', mode='w') as employee_file:
    employee_writer = csv.writer(employee_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)

    employee_writer.writerow(['John Smith', 'Accounting', 'November'])
    employee_writer.writerow(['Erica Meyers', 'IT', 'March'])

Записанный нами файл будет иметь следующее содержимое:

John Smith,Accounting,November
Erica Meyers,IT,March

Использование необязательного именованного параметра quotechar предписывает объекту writer, использовать указанный вид символа для обертывания значений полей данных при записи (например, обернуть значения полей данных в двойные кавычки). Однако, используется ли обертывание или нет, определяется наличием необязательного параметра quoting:

  • Если значение параметра quoting задано как csv.QUOTE_MINIMAL, то метод .writerow() при записи будет оборачивать значения полей данных (например, в двойные кавычки) только в том случае, если в содержимом поля данных присутствуют символыdelimiter или quotechar. Это поведение метода .writerow() задано по умолчанию.
  • Если значение параметра quoting задано как csv.QUOTE_ALL, то метод .writerow() будет оборачивать все поля данных, вне зависимости от их содержимого.
  • Если значение параметра quoting задано как csv.QUOTE_NONNUMERIC, то метод .writerow() будет обертывать значения полей данных, содержащих текстовые данные, а поля содержащие численные данные в данные с типом float.
  • Если значение параметра quoting задано как csv.QUOTE_NONE, то вместо обертывания полей данных метод .writerow() будет экранировать символы разделителя, присутствующие в содержимом полей. В этом случае вы обязательно должны указать значение для необязательного параметра escapechar.

Запись файла CSV из словаря с помощью библиотеки csv

Поскольку вы можете читать данные из файла CSV непосредственно в словарь, то соответственно, вы также сможете легко записать их из словаря в файл:

import csv

with open('employee_file2.csv', mode='w') as csv_file:
    fieldnames = ['emp_name', 'dept', 'birth_month']
    writer = csv.DictWriter(csv_file, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerow({'emp_name': 'John Smith', 'dept': 'Accounting', 'birth_month': 'November'})
    writer.writerow({'emp_name': 'Erica Meyers', 'dept': 'IT', 'birth_month': 'March'})

Для записи данных в формате CSV из словаря в файл, в отличие от DictReader, объекту DictWriter требуется задать значение для параметра fieldnames . Без списка имен столбцов fieldnames DictWriter не может понять, какие ключи использовать для извлечения соответствующих значений из вашего словаря. Далее он использует имена ключей для записи первой строки в качестве имен столбцов данных.

Приведенный выше код генерирует файл следующего содержания:

emp_name,dept,birth_month
John Smith,Accounting,November
Erica Meyers,IT,March

Синтаксический анализ (парсинг) CSV файлов с помощью библиотеки pandas

Конечно же модуль из стандартной библиотеки Python csv не единственный в своем роде. Чтение CSV файлов также возможно с использованием библиотеки pandas. Но применять её рекомендуется если у вас есть большое количество данных для последующей обработки и анализа средствами этой же библиотеки.

pandas — это библиотека Python с открытым исходным кодом, которая предоставляет высокопроизводительные инструменты для анализа данных, а также свои встроенные простые в использовании структуры данных. pandas доступна для всех версий Python. Она является основой дистрибутива Anaconda и отлично работает совместно с Jupyter notebooks, который используемого для обмена кодом и представления результатов анализа, а также дополнения сопутствующими описаниями.

Установка pandas и его зависимостей в Anaconda выполняется легко:

$ conda install pandas

Тоже с использованием другого установщика пакетов Python pip/pipenv:

$ pip install pandas

В этой статье не рассматриваются особенности работы pandas. Для углубленного изучения вопросов использования pandas для чтения и анализа больших наборов данных ознакомьтесь в превосходной статье Shantnu Tiwari О работе с большими файлами Excel в pandas.

Чтение файлов CSV с помощью pandas

Для демонстрации возможностей библиотеки pandas по чтению файлов в формате CSV, используем файл hrdata.csv. Он содержит информацию о сотрудниках компании и имеет следующую (более сложную чем рассмотренные ранее) структуру данных:

Name,Hire Date,Salary,Sick Days remaining
Graham Chapman,03/15/14,50000.00,10
John Cleese,06/01/15,65000.00,8
Eric Idle,05/12/14,45000.00,10
Terry Jones,11/01/13,70000.00,3
Terry Gilliam,08/12/14,48000.00,7
Michael Palin,05/23/13,66000.00,8

Чтение CSV файла с использованием библиотеки pandas, а точнее ее класса DataFrame, производится достаточно просто:

import pandas
df = pandas.read_csv('hrdata.csv')
print(df)

Это всего лишь три строки кода и только одна из них выполняет фактическую работу. Метод pandas.read_csv() открывает для чтения, анализирует, считает данные из файла CSV, а затем сохраняет их в специальную структуру данных, описываемых классом DataFrame. Вывод в консоли полученного результата покажет следующее:

Name Hire Date   Salary  Sick Days remaining
0  Graham Chapman  03/15/14  50000.0                   10
1     John Cleese  06/01/15  65000.0                    8
2       Eric Idle  05/12/14  45000.0                   10
3     Terry Jones  11/01/13  70000.0                    3
4   Terry Gilliam  08/12/14  48000.0                    7
5   Michael Palin  05/23/13  66000.0                    8

Исходя из этого приведем несколько соображений:

  • Во-первых, pandas распознала, что первая строка файла CSV содержит имена столбцов и автоматически использовала их.
  • Также pandas самостоятельно назначила для отдельных строк данных в DataFrame целочисленные индексы, начинающиеся с нуля. Это потому, что мы не определили какими они должны быть и нужны ли они вообще.
  • Кроме того, если вы посмотрите на типы данных в наших столбцах, то вы увидите, что pandas правильно преобразовала значения столбцов Salary and Sick Days в числа, но значения в столбце Hire Date по-прежнему остались строкой String. Что легко проверить в консоли:
>>> print(type(df['Hire Date'][0]))
<class 'str'>

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

Последовательно рассмотрим способы решения описанных выше проблем.

Чтобы использовать один из столбцов структуры DataFrame в качестве индекса, необходимо задать значение для необязательного параметра index_col:

import pandas
df = pandas.read_csv('hrdata.csv', index_col='Name')
print(df)

Теперь поле Name — наш индекс в структуре данных DataFrame:

Hire Date   Salary  Sick Days remaining
Name                                                  
Graham Chapman  03/15/14  50000.0                   10
John Cleese     06/01/15  65000.0                    8
Eric Idle       05/12/14  45000.0                   10
Terry Jones     11/01/13  70000.0                    3
Terry Gilliam   08/12/14  48000.0                    7
Michael Palin   05/23/13  66000.0                    8

Теперь давайте переопределим поведение по умолчанию метода для значений поля Hire Date. В этом случае вы можете обязать pandas считывать значения этого поля как дату, используя необязательный параметр parse_dates, в который передается список имен list соответствующих столбцов:

import pandas
df = pandas.read_csv('hrdata.csv', index_col='Name', parse_dates=['Hire Date'])
print(df)

Обратите внимание на отличие полученных результатов от полученных выше:

Hire Date   Salary  Sick Days remaining
Name                                                   
Graham Chapman 2014-03-15  50000.0                   10
John Cleese    2015-06-01  65000.0                    8
Eric Idle      2014-05-12  45000.0                   10
Terry Jones    2013-11-01  70000.0                    3
Terry Gilliam  2014-08-12  48000.0                    7
Michael Palin  2013-05-23  66000.0                    8

Теперь значение даты интерпретируется правильно, что легко проверяется в консоли:

>>> print(type(df['Hire Date'][0]))
<class 'pandas._libs.tslibs.timestamps.Timestamp'>

Если ваши данные в файле CSV не имеют первой строки с именами столбцов, то вы можете использовать необязательный параметр names для определения своего списка list имен столбцов. Вы также можете использовать эту возможность, если хотите переопределить имена столбцов, указанных в первой строке файла. В этом случае вы должны указать методу pandas.read_csv(), чтобы тот игнорировал существующие имена столбцов, используя необязательный именованный параметр header=0:

import pandas
df = pandas.read_csv('hrdata.csv', 
            index_col='Employee', 
            parse_dates=['Hired'], 
            header=0, 
            names=['Employee', 'Hired','Salary', 'Sick Days'])
print(df)

Обратите внимание, что поскольку имена столбцов изменены, данные в столбцах, измененных с помощью необязательных параметров index_col и parse_dates, также изменятся. Что приведет к следующему результату:

Hired   Salary  Sick Days
Employee                                     
Graham Chapman 2014-03-15  50000.0         10
John Cleese    2015-06-01  65000.0          8
Eric Idle      2014-05-12  45000.0         10
Terry Jones    2013-11-01  70000.0          3
Terry Gilliam  2014-08-12  48000.0          7
Michael Palin  2013-05-23  66000.0          8

Запись CSV файлов с помощью pandas

Запись данных структуры DataFrame в файл CSV так же проста, как и его чтение. Давайте запишем данные с новыми именами столбцов в новый файл:

import pandas
df = pandas.read_csv('hrdata.csv', 
            index_col='Employee', 
            parse_dates=['Hired'],
            header=0, 
            names=['Employee', 'Hired', 'Salary', 'Sick Days'])
df.to_csv('hrdata_modified.csv')

Единственное различие между этим кодом и приведенным выше для чтения данных из файла заключается в том, что вызов функции print(df) был заменен на метод df.to_csv(), которому передано имя нового файла. Полученный файл будет выглядеть следующим образом:

Employee,Hired,Salary,Sick Days
Graham Chapman,2014-03-15,50000.0,10
John Cleese,2015-06-01,65000.0,8
Eric Idle,2014-05-12,45000.0,10
Terry Jones,2013-11-01,70000.0,3
Terry Gilliam,2014-08-12,48000.0,7
Michael Palin,2013-05-23,66000.0,8

Выводы

Если вы поймете из этой статьи основы работы с CSV файлами, то сможете импортировать и экспортировать данные в этом формате для использования в своих программах. Большинство задач чтения, обработки и записи данных в формате CSV могут быть легко решены с использованием модуля стандартной библиотеки csv. Если у вас большой объем данных для чтения и последующей статистической обработки, то библиотека pandas обеспечит более удобную работу с данными в этом формате.

Существуют ли другие средства для анализа текстовых файлов? Конечно! Это такие библиотеки, как ANTLR, PLY и PlyPlus, которые помогут при разработки алгоритмов сложного синтаксического анализа (парсинга). Но это темы для других статей…

Оставить комментарий