Spec-Zone.ru › pandas 1

Инструменты ввода-вывода (текст, CSV, HDF5 и т. д.)

API pandas для ввода-вывода — это набор основных reader функций, к которым обращаются так, как к pandas.read_csv(), и которые, как правило, возвращают объект pandas. Соответствующие writer функции — это методы объектов, к которым обращаются так, как к DataFrame.to_csv(). Ниже приведена таблица, содержащая доступные readers и writers.

Тип формата

Описание данных

Чтение

Запись

текст

CSV

read_csv

to_csv

текст

Файл с фиксированной шириной

read_fwf

текст

JSON

read_json

to_json

текст

HTML

read_html

to_html

текст

LaTeX

Styler.to_latex

текст

XML

read_xml

to_xml

текст

Буфер обмена

read_clipboard

to_clipboard

двоичный

MS Excel

read_excel

to_excel

двоичный

OpenDocument

read_excel

двоичный

Формат HDF5

read_hdf

to_hdf

двоичный

Формат Feather

read_feather

to_feather

двоичный

Формат Parquet

read_parquet

to_parquet

двоичный

Формат ORC

read_orc

to_orc

двоичный

Stata

read_stata

to_stata

двоичный

SAS

read_sas

двоичный

SPSS

read_spss

двоичный

Формат Python Pickle

read_pickle

to_pickle

SQL

SQL

read_sql

to_sql

SQL

Google BigQuery

read_gbq

to_gbq

Здесь представлено сравнение производительности некоторых из этих методов ввода-вывода.

Примечание

Для примеров, использующих класс StringIO, убедитесь, что вы импортировали его с помощью from io import StringIO для Python 3.

CSV & текстовые файлы

Функция для чтения текстовых файлов (также известных как плоские файлы) — это read_csv(). См. пособие для некоторых расширенных стратегий.

Параметры парсинга

read_csv() принимает следующие общие аргументы:

Основные

filepath_or_buffer:разные

Путь к файлу (строка, str, pathlib.Path, или py:py._path.local.LocalPath), URL (включая http, ftp и S3-локации) или любой объект с методом read() (таким как открытый файл или StringIO).

sep:строка, по умолчанию ',' для read_csv(), \t для read_table()

Разделитель. Если sep равен None, движок C не может автоматически определить разделитель, но движок парсинга Python может, что означает, что последний будет использован и автоматически определит разделитель с помощью встроенного инструмента определения разделителя Python, csv.Sniffer. Кроме того, разделители длиной более 1 символа и отличные от '\s+' будут интерпретироваться как регулярные выражения и также будут принудительно использовать движок парсинга Python. Обратите внимание, что разделители, основанные на регулярных выражениях, могут игнорировать данные в кавычках. Пример регулярного выражения: '\\r\\t'.

delimiter:строка, по умолчанию None

Альтернативное имя аргумента для sep.

delim_whitespace:булево значение, по умолчанию False

Указывает, будет ли использоваться пробел (например, ' ' или '\t') в качестве разделителя. Эквивалентно установке sep='\s+'. Если этот параметр установлен в True, для параметра delimiter ничего не следует передавать.

Расположение и имена столбцов и индексов

header:целое число или список целых чисел, по умолчанию 'infer'

Номер(ы) строки, используемые в качестве имен столбцов и начала данных. По умолчанию имена столбцов вычисляются: если имена не переданы, поведение идентично header=0, и имена столбцов вычисляются из первой строки файла; если имена столбцов явно переданы, поведение идентично header=None. Явно передайте header=0, чтобы заменить существующие имена.

Заголовок может быть списком целых чисел, определяющих местоположения строк для MultiIndex столбцов, например, [0,1,3]. Промежуточные строки, которые не указаны, будут пропущены (например, 2 в этом примере пропущен).

Обратите внимание, что этот параметр игнорирует прокомментированные и пустые строки, если skip_blank_lines=True, поэтому header=0 обозначает первую строку данных, а не первую строку файла.

names:последовательность, по умолчанию None

Список имен столбцов для использования. Если файл не содержит строки заголовка, то необходимо явно передать header=None . Дубликаты в этом списке не допускаются.

index_col:целое число, строка, последовательность целых чисел/строк или False, необязательно, по умолчанию None

Столбец(ы) для использования в качестве меток строк DataFrame, заданный именем строки или индексом столбца. Если задана последовательность целых чисел/строк, используется MultiIndex.

Примечание

index_col=False можно использовать для принудительного отказа pandas от использования первого столбца в качестве индекса, например, при наличии поврежденного файла с разделителями в конце каждой строки.

Значение по умолчанию None указывает pandas на угадывание. Если количество полей в строке заголовка столбцов равно количеству полей в теле файла данных, то используется индекс по умолчанию. Если оно больше, то используются первые столбцы в качестве индекса, чтобы оставшееся количество полей в теле было равно количеству полей в заголовке.

Первая строка после заголовка используется для определения количества столбцов, которое войдет в индекс. Если последующие строки содержат меньше столбцов, чем первая строка, они заполняются NaN.

Этого можно избежать, используя usecols. Это гарантирует, что столбцы берутся как есть, и отбрасываются последующие данные.

usecols:последовательность или вызываемый объект, по умолчанию None

Возвращает подмножество столбцов. Если это последовательность, все элементы должны быть либо позиционными (т. е. целочисленными индексами в столбцах документа), либо строками, соответствующими именам столбцов, предоставленными пользователем в names или выведенными из строки(строк) заголовка документа. Если names заданы, строки(строки) заголовка документа не учитываются. Например, допустимым параметром последовательности usecols было бы [0, 1, 2] или ['foo', 'bar', 'baz'].

squeeze:булево значение, по умолчанию False

Если проанализированные данные содержат только один столбец, возвращается Series.

prefix:строка, по умолчанию None

Префикс, который нужно добавить к номерам столбцов, когда нет заголовка, например, «X» для X0, X1, …

mangle_dupe_cols:булево значение, по умолчанию True

Дублирующие столбцы будут указаны как «X», «X.1»… «X.N», а не «X»…«X».

Общие параметры парсинга

dtype:Имя типа или словарь столбец -> тип, по умолчанию None

Тип данных для данных или столбцов. Например, {'a': np.float64, 'b': np.int32, 'c': 'Int64'} Используйте str или object вместе с соответствующими параметрами na_values для сохранения и предотвращения интерпретации типа данных. Если заданы преобразователи, они будут применяться ВМЕСТО преобразования типа данных.

engine:{'c', 'python', 'pyarrow'}

Движок парсера для использования. Движки C и pyarrow быстрее, а движок python в настоящее время более функционален. Многопоточность в настоящее время поддерживается только движком pyarrow.

converters:словарь, по умолчанию None

Словарь функций для преобразования значений в определенных столбцах. Ключи могут быть целыми числами или метками столбцов.

true_values:список, по умолчанию None

Значения, которые следует рассматривать как True.

false_values:список, по умолчанию None

Значения, которые следует рассматривать как False.

skipinitialspace:булево значение, по умолчанию False

Пропустить пробелы после разделителя.

skiprows:последовательность или целое число, по умолчанию None

Номера строк для пропуска (с индексом 0) или количество строк для пропуска (целое число) в начале файла.

skipfooter:целое число, по умолчанию 0

Количество строк в конце файла, которые нужно пропустить (не поддерживается с engine='c').

nrows:целое число, по умолчанию None

Количество строк файла для чтения. Полезно для чтения фрагментов больших файлов.

low_memory:булево значение, по умолчанию True

Внутренняя обработка файла частями, что приводит к меньшему использованию памяти при парсинге, но, возможно, к смешанному выводу типа. Чтобы гарантировать отсутствие смешанных типов, установите False, или укажите тип с параметром dtype . Обратите внимание, что весь файл считывается в один DataFrame независимо, используйте параметры chunksize или iterator для возврата данных частями. (Только с C парсером)

memory_map:булево значение, по умолчанию False

Если для filepath_or_buffer задан путь к файлу, отобразить объект файла непосредственно в памяти и получить доступ к данным непосредственно из него. Использование этого параметра может повысить производительность, так как больше нет накладных расходов ввода-вывода.

Обработка значений NA и отсутствующих данных

na_values:скаляр, строка, список-подобный объект или словарь, по умолчанию None

Дополнительные строки для распознавания значений NA/NaN. Если передан словарь, значения NA специфичны для каждого столбца. См. на значения const ниже для списка значений, интерпретируемых как NaN по умолчанию.

keep_default_na:булево значение, по умолчанию True

Включать ли значения NaN по умолчанию при разборе данных. В зависимости от того, что передано na_values, поведение следующее:

  • Если keep_default_na равно True, а na_values заданы, na_values добавляется к значениям NaN по умолчанию, используемым для разбора.

  • Если keep_default_na равно True, а na_values не заданы, для разбора используются только значения NaN по умолчанию.

  • Если keep_default_na равно False, а na_values заданы, для разбора используются только значения NaN, указанные na_values.

  • Если keep_default_na равно False, а na_values не заданы, ни одна строка не будет интерпретироваться как NaN.

Обратите внимание, что если na_filter передано как False, параметры keep_default_na и na_values будут игнорироваться.

na_filter:булево значение, по умолчанию True

Обнаруживать маркеры отсутствующих значений (пустые строки и значения na_values). В данных без значений NA передача na_filter=False может улучшить производительность чтения большого файла.

verbose:булево значение, по умолчанию False

Указывать количество значений NA, помещенных в нечисловые столбцы.

skip_blank_lines:булево значение, по умолчанию True

Если True, пропускать пустые строки вместо интерпретации их как значений NaN.

Обработка дат и времени

parse_dates:булево значение или список целых чисел или имен или список списков или словарь, по умолчанию False.
  • Если True -> попытка разбора индекса.

  • Если [1, 2, 3] -> попытка разбора столбцов 1, 2, 3 как отдельных столбцов дат.

  • Если [[1, 3]] -> объединение столбцов 1 и 3 и разбор как единого столбца дат.

  • Если {'foo': [1, 3]} -> разбор столбцов 1, 3 как дат и присвоение результату имени ‘foo’.

Примечание

Существует быстрый путь для дат в формате iso8601.

infer_datetime_format:булево значение, по умолчанию False

Если True и parse_dates включены для столбца, попробуйте определить формат даты и времени для ускорения обработки.

keep_date_col:булево значение, по умолчанию False

Если True и parse_dates определяют объединение нескольких столбцов, сохранить исходные столбцы.

date_parser:функция, по умолчанию None

Функция для преобразования последовательности столбцов со строками в массив экземпляров даты и времени. По умолчанию используется dateutil.parser.parser для преобразования. pandas попытается вызвать date_parser тремя различными способами, переходя к следующему, если произойдет исключение: 1) Передать один или несколько массивов (как определено parse_dates) в качестве аргументов; 2) конкатенировать (по строкам) строковые значения из столбцов, определенных parse_dates, в один массив и передать его; и 3) вызвать date_parser один раз для каждой строки, используя одну или несколько строк (соответствующих столбцам, определенным parse_dates) в качестве аргументов.

dayfirst:булево значение, по умолчанию False

Даты в формате ДД/ММ, международный и европейский формат.

cache_dates:булево значение, по умолчанию True

Если True, использовать кэш уникальных преобразованных дат для применения преобразования даты и времени. Может значительно ускорить разбор, если строки дат дублируются, особенно с часовыми поясами.

Новая функция в версии 0.25.0.

Итерация

iterator:булево значение, по умолчанию False

Возвратить TextFileReader объект для итерации или извлечения фрагментов с get_chunk().

chunksize:целое число, по умолчанию None

Возвратить TextFileReader объект для итерации. См. итерацию и фрагментацию ниже.

Кавычки, сжатие и формат файла

compression:{'infer', 'gzip', 'bz2', 'zip', 'xz', 'zstd', None, dict}, default 'infer'

Для динамического распаковки данных на диске. Если ‘infer’, использовать gzip, bz2, zip, xz или zstandard, если filepath_or_buffer является путём, заканчивающимся на ‘.gz’, ‘.bz2’, ‘.zip’, ‘.xz’, ‘.zst’, соответственно, и иначе - без распаковки. Если использовать ‘zip’, ZIP-файл должен содержать только один файл данных для чтения. Установите в None для предотвращения распаковки. Также может быть словарь с ключом 'method', установленным в одном из {'zip', 'gzip', 'bz2', 'zstd'} и другие пары ключ-значение передаются zipfile.ZipFile, gzip.GzipFile, bz2.BZ2File, или zstandard.ZstdDecompressor. В качестве примера можно передать следующее для более быстрого сжатия и создания воспроизводимого gzip-архива: compression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1}.

Изменено в версии 1.1.0: Расширение параметра dict для поддержки gzip и bz2.

Изменено в версии 1.2.0: Предыдущие версии передавали записи словаря для ‘gzip’ в gzip.open.

thousands:строка, по умолчанию None

Разделитель тысяч.

decimal:строка, по умолчанию '.'

Символ для распознавания десятичной точки. Например, используйте ',' для европейских данных.

float_precision:строка, по умолчанию None

Указывает, какой конвертер C-движок должен использовать для значений с плавающей точкой. Варианты: None для обычного конвертера, high для конвертера высокой точности и round_trip для конвертера, обеспечивающего обратное преобразование.

lineterminator:строка (длина 1), по умолчанию None

Символ для разбиения файла на строки. Действителен только с C-парсером.

quotechar:строка (длина 1)

Символ, используемый для обозначения начала и конца цитируемого элемента. Цитируемые элементы могут содержать разделитель, и он будет проигнорирован.

quoting:целое число или экземпляр csv.QUOTE_*, по умолчанию 0

Управление поведением цитирования полей по csv.QUOTE_* константам. Используйте один из QUOTE_MINIMAL (0), QUOTE_ALL (1), QUOTE_NONNUMERIC (2) или QUOTE_NONE (3).

doublequote:булево значение, по умолчанию True

Когда quotechar указано, и quoting не QUOTE_NONE, указывает, интерпретировать ли два последовательных quotechar элемента **внутри** поля как один quotechar элемент.

escapechar:строка (длина 1), по умолчанию None

Символ длиной в один символ, используемый для экранирования разделителя, когда цитирование QUOTE_NONE.

comment:строка, по умолчанию None

Указывает, что остальная часть строки не должна анализироваться. Если найдена в начале строки, строка будет проигнорирована. Этот параметр должен быть символом. Как и пустые строки (пока skip_blank_lines=True), полностью комментированные строки игнорируются параметром header, но не skiprows. Например, если comment='#', разбор ‘#empty\na,b,c\n1,2,3’ с header=0 приведет к тому, что ‘a,b,c’ будет рассматриваться как заголовок.

encoding:строка, по умолчанию None

Кодировка для использования с UTF при чтении/записи (например, 'utf-8'). Список стандартных кодировок Python.

dialect:строка или экземпляр csv.Dialect, по умолчанию None

Если предоставлено, этот параметр переопределит значения (по умолчанию или нет) следующих параметров: delimiter, doublequote, escapechar, skipinitialspace, quotechar, и quoting. Если необходимо переопределить значения, будет выведено предупреждение ParserWarning. См. csv.Dialect документацию для получения дополнительной информации.

Обработка ошибок

error_bad_lines:boolean, optional, default None

Строки с слишком большим количеством полей (например, строка CSV с слишком многими запятыми) по умолчанию вызывают исключение, и не будет возвращено DataFrame. Если False, то эти «плохие строки» будут удалены из DataFrame , которое возвращается. См. плохие строки ниже.

Устарело начиная с версии 1.3.0: Вместо параметра on_bad_lines следует использовать параметр для указания поведения при обнаружении плохой строки.

warn_bad_lines:boolean, optional, default None

Если error_bad_lines равно False, и warn_bad_lines равно True, будет выводиться предупреждение для каждой «плохой строки».

Устарело начиная с версии 1.3.0: Вместо параметра on_bad_lines следует использовать параметр для указания поведения при обнаружении плохой строки.

on_bad_lines:(‘error’, ‘warn’, ‘skip’), default ‘error’

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

  • ‘error’, вызов ParserError при обнаружении плохой строки.

  • ‘warn’, вывод предупреждения при обнаружении плохой строки и пропуск этой строки.

  • ‘skip’, пропуск плохих строк без вывода предупреждений или исключений при их обнаружении.

Добавлена в версии 1.3.0.

Указание типов данных столбцов

Вы можете указать тип данных для всего DataFrame или отдельных столбцов:

In [13]: import numpy as np

In [14]: data = "a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11"

In [15]: print(data)
a,b,c,d
1,2,3,4
5,6,7,8
9,10,11

In [16]: df = pd.read_csv(StringIO(data), dtype=object)

In [17]: df
Out[17]: 
   a   b   c    d
0  1   2   3    4
1  5   6   7    8
2  9  10  11  NaN

In [18]: df["a"][0]
Out[18]: '1'

In [19]: df = pd.read_csv(StringIO(data), dtype={"b": object, "c": np.float64, "d": "Int64"})

In [20]: df.dtypes
Out[20]: 
a      int64
b     object
c    float64
d      Int64
dtype: object

К счастью, pandas предлагает более одного способа гарантировать, что ваши столбцы содержат только один dtype. Если вы не знакомы с этими понятиями, вы можете ознакомиться здесь, чтобы узнать больше о типах данных, и здесь, чтобы узнать больше о object преобразованиях в pandas.

Например, вы можете использовать аргумент converters метода read_csv():

In [21]: data = "col_1\n1\n2\n'A'\n4.22"

In [22]: df = pd.read_csv(StringIO(data), converters={"col_1": str})

In [23]: df
Out[23]: 
  col_1
0     1
1     2
2   'A'
3  4.22

In [24]: df["col_1"].apply(type).value_counts()
Out[24]: 
<class 'str'>    4
Name: col_1, dtype: int64

Или вы можете использовать функцию to_numeric() для принудительного преобразования типов данных после чтения данных,

In [25]: df2 = pd.read_csv(StringIO(data))

In [26]: df2["col_1"] = pd.to_numeric(df2["col_1"], errors="coerce")

In [27]: df2
Out[27]: 
   col_1
0   1.00
1   2.00
2    NaN
3   4.22

In [28]: df2["col_1"].apply(type).value_counts()
Out[28]: 
<class 'float'>    4
Name: col_1, dtype: int64

что преобразует все допустимые преобразования в числа с плавающей точкой, оставив недопустимые преобразования в виде NaN.

В конечном счёте, как вы будете обрабатывать чтение столбцов, содержащих смешанные типы данных, зависит от ваших конкретных потребностей. В приведенном выше примере, если вы хотите NaN выбросить аномалии данных, то to_numeric() , вероятно, является лучшим вариантом. Однако, если вам нужно, чтобы все данные были приведены к одному типу, независимо от типа, то использование аргумента converters метода read_csv() определённо стоит попробовать.

Примечание

В некоторых случаях чтение аномальных данных со столбцами, содержащими смешанные типы данных, приведет к несовместимому набору данных. Если вы полагаетесь на pandas для вывода типов данных ваших столбцов, движок парсинга будет определять типы данных для различных фрагментов данных, а не для всего набора данных сразу. В результате у вас могут получиться столбцы со смешанными типами данных. Например,

In [29]: col_1 = list(range(500000)) + ["a", "b"] + list(range(500000))

In [30]: df = pd.DataFrame({"col_1": col_1})

In [31]: df.to_csv("foo.csv")

In [32]: mixed_df = pd.read_csv("foo.csv")

In [33]: mixed_df["col_1"].apply(type).value_counts()
Out[33]: 
<class 'int'>    737858
<class 'str'>    262144
Name: col_1, dtype: int64

In [34]: mixed_df["col_1"].dtype
Out[34]: dtype('O')

приведёт к mixed_df содержащему тип данных int для определённых фрагментов столбца и str для других из-за смешанных типов данных из считанных данных. Важно отметить, что весь столбец будет помечен типом dtype object, который используется для столбцов со смешанными типами данных.

Указание типа данных категорий

Столбцы Categorical могут быть распарсены напрямую, указав dtype='category' или dtype=CategoricalDtype(categories, ordered).

In [35]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3"

In [36]: pd.read_csv(StringIO(data))
Out[36]: 
  col1 col2  col3
0    a    b     1
1    a    b     2
2    c    d     3

In [37]: pd.read_csv(StringIO(data)).dtypes
Out[37]: 
col1    object
col2    object
col3     int64
dtype: object

In [38]: pd.read_csv(StringIO(data), dtype="category").dtypes
Out[38]: 
col1    category
col2    category
col3    category
dtype: object

Отдельные столбцы могут быть распарсены как Categorical с помощью указания словаря:

In [39]: pd.read_csv(StringIO(data), dtype={"col1": "category"}).dtypes
Out[39]: 
col1    category
col2      object
col3       int64
dtype: object

Указание dtype='category' приведёт к неотсортированной Categorical, категории которой будут уникальные значения, наблюдаемые в данных. Для большего контроля над категориями и порядком, создайте CategoricalDtype заранее и передайте его для dtype столбца.

In [40]: from pandas.api.types import CategoricalDtype

In [41]: dtype = CategoricalDtype(["d", "c", "b", "a"], ordered=True)

In [42]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).dtypes
Out[42]: 
col1    category
col2      object
col3       int64
dtype: object

При использовании dtype=CategoricalDtype, «неожиданные» значения, которые не входят в dtype.categories, обрабатываются как пропущенные значения.

In [43]: dtype = CategoricalDtype(["a", "b", "d"])  # No 'c'

In [44]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).col1
Out[44]: 
0      a
1      a
2    NaN
Name: col1, dtype: category
Categories (3, object): ['a', 'b', 'd']

Это соответствует поведению Categorical.set_categories().

Примечание

При использовании dtype='category', результирующие категории всегда будут распарсены как строки (объектный тип данных). Если категории являются числовыми, их можно преобразовать, используя функцию to_numeric(), или, как соответствующим образом, другой конвертер, например, to_datetime().

Когда dtype является CategoricalDtype с однородными categories (все числовые, все даты и т. д.), преобразование выполняется автоматически.

In [45]: df = pd.read_csv(StringIO(data), dtype="category")

In [46]: df.dtypes
Out[46]: 
col1    category
col2    category
col3    category
dtype: object

In [47]: df["col3"]
Out[47]: 
0    1
1    2
2    3
Name: col3, dtype: category
Categories (3, object): ['1', '2', '3']

In [48]: new_categories = pd.to_numeric(df["col3"].cat.categories)

In [49]: df["col3"] = df["col3"].cat.rename_categories(new_categories)

In [50]: df["col3"]
Out[50]: 
0    1
1    2
2    3
Name: col3, dtype: category
Categories (3, int64): [1, 2, 3]

Именование и использование столбцов

Обработка имён столбцов

Файл может или не может иметь строку заголовка. pandas предполагает, что первая строка должна использоваться в качестве имён столбцов:

In [51]: data = "a,b,c\n1,2,3\n4,5,6\n7,8,9"

In [52]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9

In [53]: pd.read_csv(StringIO(data))
Out[53]: 
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9

Указав аргумент names в сочетании с header, вы можете указать другие имена для использования и указать, удалять ли строку заголовка (если она есть):

In [54]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9

In [55]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=0)
Out[55]: 
   foo  bar  baz
0    1    2    3
1    4    5    6
2    7    8    9

In [56]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=None)
Out[56]: 
  foo bar baz
0   a   b   c
1   1   2   3
2   4   5   6
3   7   8   9

Если заголовок находится в строке, отличной от первой, передайте номер строки аргументу header. Это пропустит предшествующие строки:

In [57]: data = "skip this skip it\na,b,c\n1,2,3\n4,5,6\n7,8,9"

In [58]: pd.read_csv(StringIO(data), header=1)
Out[58]: 
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9

Примечание

По умолчанию имена столбцов выводятся: если имена не передаются, поведение идентично header=0 , и имена столбцов выводятся из первой непустой строки файла; если имена столбцов переданы явно, поведение идентично header=None.

Обработка дублирующихся имён

Устарело начиная с версии 1.5.0: mangle_dupe_cols никогда не реализовывался, и вместо него будет добавлен новый аргумент, где можно указать шаблон переименования.

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

In [59]: data = "a,b,a\n0,1,2\n3,4,5"

In [60]: pd.read_csv(StringIO(data))
Out[60]: 
   a  b  a.1
0  0  1    2
1  3  4    5

Больше нет дублирующихся данных, потому что mangle_dupe_cols=True по умолчанию, что изменяет серию дублирующихся столбцов «X», …, «X» на «X», «X.1», …, «X.N».

Фильтрация столбцов (usecols)

Аргумент usecols позволяет вам выбрать любой подмножество столбцов в файле, используя имена столбцов, номера позиций или вызываемую функцию:

In [61]: data = "a,b,c,d\n1,2,3,foo\n4,5,6,bar\n7,8,9,baz"

In [62]: pd.read_csv(StringIO(data))
Out[62]: 
   a  b  c    d
0  1  2  3  foo
1  4  5  6  bar
2  7  8  9  baz

In [63]: pd.read_csv(StringIO(data), usecols=["b", "d"])
Out[63]: 
   b    d
0  2  foo
1  5  bar
2  8  baz

In [64]: pd.read_csv(StringIO(data), usecols=[0, 2, 3])
Out[64]: 
   a  c    d
0  1  3  foo
1  4  6  bar
2  7  9  baz

In [65]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["A", "C"])
Out[65]: 
   a  c
0  1  3
1  4  6
2  7  9

Аргумент usecols также может использоваться для указания столбцов, которые не должны использоваться в конечном результате:

In [66]: pd.read_csv(StringIO(data), usecols=lambda x: x not in ["a", "c"])
Out[66]: 
   b    d
0  2  foo
1  5  bar
2  8  baz

В этом случае вызываемая функция указывает, что мы исключаем столбцы «a» и «c» из результата.

Комментарии и пустые строки

Игнорирование комментариев и пустых строк

Если параметр comment указан, то полностью прокомментированные строки будут проигнорированы. По умолчанию, также будут проигнорированы полностью пустые строки.

In [67]: data = "\na,b,c\n  \n# commented line\n1,2,3\n\n4,5,6"

In [68]: print(data)

a,b,c
  
# commented line
1,2,3

4,5,6

In [69]: pd.read_csv(StringIO(data), comment="#")
Out[69]: 
   a  b  c
0  1  2  3
1  4  5  6

Если skip_blank_lines=False, то read_csv не будет игнорировать пустые строки:

In [70]: data = "a,b,c\n\n1,2,3\n\n\n4,5,6"

In [71]: pd.read_csv(StringIO(data), skip_blank_lines=False)
Out[71]: 
     a    b    c
0  NaN  NaN  NaN
1  1.0  2.0  3.0
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  4.0  5.0  6.0

Предупреждение

Наличие проигнорированных строк может создать неоднозначность, связанную с номерами строк; параметр header использует номера строк (игнорируя прокомментированные/пустые строки), в то время как skiprows использует номера строк (включая прокомментированные/пустые строки):

In [72]: data = "#comment\na,b,c\nA,B,C\n1,2,3"

In [73]: pd.read_csv(StringIO(data), comment="#", header=1)
Out[73]: 
   A  B  C
0  1  2  3

In [74]: data = "A,B,C\n#comment\na,b,c\n1,2,3"

In [75]: pd.read_csv(StringIO(data), comment="#", skiprows=2)
Out[75]: 
   a  b  c
0  1  2  3

Если и header, и skiprows указаны, header будет относиться к концу skiprows. Например:

In [76]: data = (
   ....:     "# empty\n"
   ....:     "# second empty line\n"
   ....:     "# third emptyline\n"
   ....:     "X,Y,Z\n"
   ....:     "1,2,3\n"
   ....:     "A,B,C\n"
   ....:     "1,2.,4.\n"
   ....:     "5.,NaN,10.0\n"
   ....: )
   ....: 

In [77]: print(data)
# empty
# second empty line
# third emptyline
X,Y,Z
1,2,3
A,B,C
1,2.,4.
5.,NaN,10.0


In [78]: pd.read_csv(StringIO(data), comment="#", skiprows=4, header=1)
Out[78]: 
     A    B     C
0  1.0  2.0   4.0
1  5.0  NaN  10.0

Комментарии

Иногда в файл могут быть включены комментарии или метаданные:

In [79]: print(open("tmp.csv").read())
ID,level,category
Patient1,123000,x # really unpleasant
Patient2,23000,y # wouldn't take his medicine
Patient3,1234018,z # awesome

По умолчанию, парсер включает комментарии в выходные данные:

In [80]: df = pd.read_csv("tmp.csv")

In [81]: df
Out[81]: 
         ID    level                        category
0  Patient1   123000           x # really unpleasant
1  Patient2    23000  y # wouldn't take his medicine
2  Patient3  1234018                     z # awesome

Мы можем подавить комментарии, используя ключевое слово comment:

In [82]: df = pd.read_csv("tmp.csv", comment="#")

In [83]: df
Out[83]: 
         ID    level category
0  Patient1   123000       x 
1  Patient2    23000       y 
2  Patient3  1234018       z 

Обработка данных Unicode

Аргумент encoding должен использоваться для закодированных данных Unicode, что приведет к декодированию байтовых строк в Unicode в результате:

In [84]: from io import BytesIO

In [85]: data = b"word,length\n" b"Tr\xc3\xa4umen,7\n" b"Gr\xc3\xbc\xc3\x9fe,5"

In [86]: data = data.decode("utf8").encode("latin-1")

In [87]: df = pd.read_csv(BytesIO(data), encoding="latin-1")

In [88]: df
Out[88]: 
      word  length
0  Träumen       7
1    Grüße       5

In [89]: df["word"][1]
Out[89]: 'Grüße'

Некоторые форматы, которые кодируют все символы как несколько байтов, такие как UTF-16, не будут правильно парситься без указания кодировки. Полный список стандартных кодировок Python.

Индексные столбцы и разделители в конце

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

In [90]: data = "a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"

In [91]: pd.read_csv(StringIO(data))
Out[91]: 
        a    b     c
4   apple  bat   5.7
8  orange  cow  10.0
In [92]: data = "index,a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"

In [93]: pd.read_csv(StringIO(data), index_col=0)
Out[93]: 
            a    b     c
index                   
4       apple  bat   5.7
8      orange  cow  10.0

Обычно это поведение можно получить, используя опцию index_col.

Существуют некоторые исключительные случаи, когда файл был подготовлен с разделителями в конце каждой строки данных, что вводит в заблуждение парсер. Чтобы явно отключить вычисление индексного столбца и отбросить последний столбец, передайте index_col=False:

In [94]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"

In [95]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,

In [96]: pd.read_csv(StringIO(data))
Out[96]: 
        a    b   c
4   apple  bat NaN
8  orange  cow NaN

In [97]: pd.read_csv(StringIO(data), index_col=False)
Out[97]: 
   a       b    c
0  4   apple  bat
1  8  orange  cow

Если анализируется подмножество данных с помощью опции usecols, спецификация index_col основана на этом подмножестве, а не на исходных данных.

In [98]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"

In [99]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,

In [100]: pd.read_csv(StringIO(data), usecols=["b", "c"])
Out[100]: 
     b   c
4  bat NaN
8  cow NaN

In [101]: pd.read_csv(StringIO(data), usecols=["b", "c"], index_col=0)
Out[101]: 
     b   c
4  bat NaN
8  cow NaN

Обработка дат

Указание столбцов дат

Для лучшей работы с данными datetime, read_csv() использует ключевые аргументы parse_dates и date_parser для возможности указывать различные столбцы и форматы дат/времени, чтобы преобразовать входные текстовые данные в объекты datetime.

Простейший случай — передача parse_dates=True:

In [102]: with open("foo.csv", mode="w") as f:
   .....:     f.write("date,A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5")
   .....: 

# Use a column as an index, and parse it as dates.
In [103]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True)

In [104]: df
Out[104]: 
            A  B  C
date               
2009-01-01  a  1  2
2009-01-02  b  3  4
2009-01-03  c  4  5

# These are Python datetime objects
In [105]: df.index
Out[105]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)

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

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

In [106]: data = (
   .....:     "KORD,19990127, 19:00:00, 18:56:00, 0.8100\n"
   .....:     "KORD,19990127, 20:00:00, 19:56:00, 0.0100\n"
   .....:     "KORD,19990127, 21:00:00, 20:56:00, -0.5900\n"
   .....:     "KORD,19990127, 21:00:00, 21:18:00, -0.9900\n"
   .....:     "KORD,19990127, 22:00:00, 21:56:00, -0.5900\n"
   .....:     "KORD,19990127, 23:00:00, 22:56:00, -0.5900"
   .....: )
   .....: 

In [107]: with open("tmp.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [108]: df = pd.read_csv("tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]])

In [109]: df
Out[109]: 
                  1_2                 1_3     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

По умолчанию парсер удаляет столбцы компонент дат, но вы можете выбрать их сохранение с помощью ключевого слова keep_date_col:

In [110]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True
   .....: )
   .....: 

In [111]: df
Out[111]: 
                  1_2                 1_3     0  ...          2          3     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  ...   19:00:00   18:56:00  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  ...   20:00:00   19:56:00  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD  ...   21:00:00   20:56:00 -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD  ...   21:00:00   21:18:00 -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD  ...   22:00:00   21:56:00 -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD  ...   23:00:00   22:56:00 -0.59

[6 rows x 7 columns]

Обратите внимание, что если вы хотите объединить несколько столбцов в один столбец дат, необходимо использовать вложенный список. Другими словами, parse_dates=[1, 2] указывает, что второй и третий столбцы должны быть проанализированы как отдельные столбцы дат, в то время как parse_dates=[[1, 2]] означает, что два столбца должны быть проанализированы в один столбец.

Вы также можете использовать словарь для указания пользовательских имён столбцов:

In [112]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

In [113]: df = pd.read_csv("tmp.csv", header=None, parse_dates=date_spec)

In [114]: df
Out[114]: 
              nominal              actual     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

Важно помнить, что если несколько текстовых столбцов должны быть проанализированы в один столбец дат, то новый столбец добавляется в данные. Спецификация index_col основана на этом новом наборе столбцов, а не на исходных столбцах данных:

In [115]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

In [116]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=date_spec, index_col=0
   .....: )  # index is the nominal column
   .....: 

In [117]: df
Out[117]: 
                                 actual     0     4
nominal                                            
1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

Примечание

Если столбец или индекс содержит непроверяемую дату, весь столбец или индекс будут возвращены без изменений как тип данных объект. Для нестандартного анализа datetime используйте to_datetime() после pd.read_csv.

Примечание

read_csv имеет быстрый путь для парсинга строк datetime в формате iso8601, например “2000-01-01T00:01:02+00:00” и аналогичных вариаций. Если вы можете организовать хранение datetime в этом формате, время загрузки значительно сократится, было замечено ускорение ~20 раз.

Функции парсинга дат

Наконец, парсер позволяет указать пользовательскую функцию date_parser для полного использования гибкости API парсинга дат:

In [118]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=date_spec, date_parser=pd.to_datetime
   .....: )
   .....: 

In [119]: df
Out[119]: 
              nominal              actual     0     4
0 1999-01-27 19:00:00 1999-01-27 18:56:00  KORD  0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00  KORD  0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00  KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00  KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00  KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00  KORD -0.59

pandas попытается вызвать функцию date_parser тремя различными способами. Если возникнет исключение, будет выполнена следующая попытка:

  1. date_parser сначала вызывается с одним или несколькими массивами в качестве аргументов, как определено с помощью parse_dates (например, date_parser(['2013', '2013'], ['1', '2'])).

  2. Если #1 завершится неудачей, date_parser вызывается со всеми столбцами, конкатенированными построчно в один массив (например, date_parser(['2013 1', '2013 2'])).

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

  1. Попробуйте определить формат с помощью infer_datetime_format=True (см. раздел ниже).

  2. Если вы знаете формат, используйте pd.to_datetime(): date_parser=lambda x: pd.to_datetime(x, format=...).

  3. Если у вас действительно нестандартный формат, используйте пользовательскую функцию date_parser. Для оптимальной производительности она должна быть векторизованной, т.е. принимать массивы в качестве аргументов.

Парсинг CSV с различными часовыми поясами

pandas не может напрямую представлять столбец или индекс со смешанными часовыми поясами. Если ваш CSV-файл содержит столбцы с различными часовыми поясами, результат по умолчанию будет столбцом типа object со строками, даже с parse_dates.

In [120]: content = """\
   .....: a
   .....: 2000-01-01T00:00:00+05:00
   .....: 2000-01-01T00:00:00+06:00"""
   .....: 

In [121]: df = pd.read_csv(StringIO(content), parse_dates=["a"])

In [122]: df["a"]
Out[122]: 
0    2000-01-01 00:00:00+05:00
1    2000-01-01 00:00:00+06:00
Name: a, dtype: object

Чтобы проанализировать значения со смешанными часовыми поясами как столбец datetime, передайте частично применённую функцию to_datetime() с utc=True в качестве date_parser.

In [123]: df = pd.read_csv(
   .....:     StringIO(content),
   .....:     parse_dates=["a"],
   .....:     date_parser=lambda col: pd.to_datetime(col, utc=True),
   .....: )
   .....: 

In [124]: df["a"]
Out[124]: 
0   1999-12-31 19:00:00+00:00
1   1999-12-31 18:00:00+00:00
Name: a, dtype: datetime64[ns, UTC]

Определение формата datetime

Если для некоторых или всех ваших столбцов включена функция parse_dates, и все ваши строки дат имеют одинаковый формат, вы можете значительно ускорить процесс, установив infer_datetime_format=True. Если установлено, pandas попытается угадать формат ваших строк datetime и затем использовать более быстрый способ анализа строк. Было отмечено ускорение парсинга в 5-10 раз. pandas вернётся к обычному парсингу, если формат не может быть угадан или формат, который был угадан, не может правильно проанализировать весь столбец строк. Таким образом, в целом, infer_datetime_format не должно иметь негативных последствий при включении.

Вот некоторые примеры строк datetime, которые могут быть угаданы (все представляют 30 декабря 2011 года в 00:00:00):

  • “20111230”

  • “2011/12/30”

  • “20111230 00:00:00”

  • “12/30/2011 00:00:00”

  • “30/Dec/2011 00:00:00”

  • “30/Декабря/2011 00:00:00”

Обратите внимание, что infer_datetime_format чувствителен к dayfirst. С dayfirst=True, он угадает “01/12/2011” как 1 декабря. С dayfirst=False (по умолчанию) он угадает “01/12/2011” как 12 января.

# Try to infer the format for the index column
In [125]: df = pd.read_csv(
   .....:     "foo.csv",
   .....:     index_col=0,
   .....:     parse_dates=True,
   .....:     infer_datetime_format=True,
   .....: )
   .....: 

In [126]: df
Out[126]: 
            A  B  C
date               
2009-01-01  a  1  2
2009-01-02  b  3  4
2009-01-03  c  4  5

Международные форматы дат

В то время как американские форматы дат обычно используют MM/DD/YYYY, многие международные форматы используют DD/MM/YYYY вместо этого. Для удобства предоставлено ключевое слово dayfirst:

In [127]: data = "date,value,cat\n1/6/2000,5,a\n2/6/2000,10,b\n3/6/2000,15,c"

In [128]: print(data)
date,value,cat
1/6/2000,5,a
2/6/2000,10,b
3/6/2000,15,c

In [129]: with open("tmp.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [130]: pd.read_csv("tmp.csv", parse_dates=[0])
Out[130]: 
        date  value cat
0 2000-01-06      5   a
1 2000-02-06     10   b
2 2000-03-06     15   c

In [131]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0])
Out[131]: 
        date  value cat
0 2000-06-01      5   a
1 2000-06-02     10   b
2 2000-06-03     15   c

Запись CSV в двоичные объекты файлов

Добавлено в версии 1.2.0.

df.to_csv(..., mode="wb") позволяет записывать CSV в объект файла, открытый в двоичном режиме. В большинстве случаев нет необходимости указывать mode, так как Pandas автоматически определит, открыт ли объект файла в текстовом или двоичном режиме.

In [132]: import io

In [133]: data = pd.DataFrame([0, 1, 2])

In [134]: buffer = io.BytesIO()

In [135]: data.to_csv(buffer, encoding="utf-8", compression="gzip")

Указание метода преобразования чисел с плавающей точкой

Параметр float_precision может быть указан для использования определённого преобразователя чисел с плавающей точкой во время разбора с помощью движка C. Варианты — обычный преобразователь, преобразователь высокой точности и преобразователь обратного преобразования (который гарантирует обратное преобразование значений после записи в файл). Например:

In [136]: val = "0.3066101993807095471566981359501369297504425048828125"

In [137]: data = "a,b,c\n1,2,{0}".format(val)

In [138]: abs(
   .....:     pd.read_csv(
   .....:         StringIO(data),
   .....:         engine="c",
   .....:         float_precision=None,
   .....:     )["c"][0] - float(val)
   .....: )
   .....: 
Out[138]: 5.551115123125783e-17

In [139]: abs(
   .....:     pd.read_csv(
   .....:         StringIO(data),
   .....:         engine="c",
   .....:         float_precision="high",
   .....:     )["c"][0] - float(val)
   .....: )
   .....: 
Out[139]: 5.551115123125783e-17

In [140]: abs(
   .....:     pd.read_csv(StringIO(data), engine="c", float_precision="round_trip")["c"][0]
   .....:     - float(val)
   .....: )
   .....: 
Out[140]: 0.0

Разделители тысяч

Для больших чисел, записанных с разделителями тысяч, вы можете установить ключевое слово thousands в строку длиной 1, чтобы целые числа анализировались корректно:

По умолчанию числа с разделителями тысяч будут анализироваться как строки:

In [141]: data = (
   .....:     "ID|level|category\n"
   .....:     "Patient1|123,000|x\n"
   .....:     "Patient2|23,000|y\n"
   .....:     "Patient3|1,234,018|z"
   .....: )
   .....: 

In [142]: with open("tmp.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [143]: df = pd.read_csv("tmp.csv", sep="|")

In [144]: df
Out[144]: 
         ID      level category
0  Patient1    123,000        x
1  Patient2     23,000        y
2  Patient3  1,234,018        z

In [145]: df.level.dtype
Out[145]: dtype('O')

Ключевое слово thousands позволяет корректно анализировать целые числа:

In [146]: df = pd.read_csv("tmp.csv", sep="|", thousands=",")

In [147]: df
Out[147]: 
         ID    level category
0  Patient1   123000        x
1  Patient2    23000        y
2  Patient3  1234018        z

In [148]: df.level.dtype
Out[148]: dtype('int64')

Значения NA

Чтобы контролировать, какие значения анализируются как пропущенные значения (обозначаемые как NaN), укажите строку в na_values. Если вы указываете список строк, то все значения в нём считаются пропущенными значениями. Если вы указываете число (например, float, как 5.0 или integer, как 5 ), соответствующие эквивалентные значения также будут означать пропущенное значение (в этом случае эффективно [5.0, 5] распознаются как NaN).

Чтобы полностью переопределить значения по умолчанию, распознаваемые как пропущенные, укажите keep_default_na=False.

Значения по умолчанию NaN, распознаваемые значения ['-1.#IND', '1.#QNAN', '1.#IND', '-1.#QNAN', '#N/A N/A', '#N/A', 'N/A', 'n/a', 'NA', '<NA>', '#NA', 'NULL', 'null', 'NaN', '-NaN', 'nan', '-nan', ''].

Рассмотрим несколько примеров:

pd.read_csv("path_to_file.csv", na_values=[5])

В примере выше 5 и 5.0 будут распознаны как NaN, в дополнение к значениям по умолчанию. Сначала строка будет интерпретироваться как числовое 5, затем как NaN.

pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=[""])

Выше только пустое поле будет распознано как NaN.

pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=["NA", "0"])

Выше, как строки, NA и 0 будут NaN.

pd.read_csv("path_to_file.csv", na_values=["Nope"])

Значения по умолчанию, кроме строки "Nope", распознаются как NaN.

Бесконечность

inf подобные значения будут анализироваться как np.inf (положительная бесконечность), а -inf как -np.inf (отрицательная бесконечность). При этом регистр значения игнорируется, что означает, что Inf, также будет анализироваться как np.inf.

Возвращение Series

Используя ключевое слово squeeze, парсер вернёт вывод с одним столбцом как Series.

Устарело начиная с версии 1.4.0: Пользователи должны добавить .squeeze("columns") к DataFrame, возвращённому read_csv.

In [149]: data = "level\nPatient1,123000\nPatient2,23000\nPatient3,1234018"

In [150]: with open("tmp.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [151]: print(open("tmp.csv").read())
level
Patient1,123000
Patient2,23000
Patient3,1234018

In [152]: output = pd.read_csv("tmp.csv", squeeze=True)

In [153]: output
Out[153]: 
Patient1     123000
Patient2      23000
Patient3    1234018
Name: level, dtype: int64

In [154]: type(output)
Out[154]: pandas.core.series.Series

Булевы значения

Общие значения True, False, TRUE, и FALSE распознаются как булевы. Иногда вам может потребоваться распознать другие значения как булевы. Для этого используйте опции true_values и false_values следующим образом:

In [155]: data = "a,b,c\n1,Yes,2\n3,No,4"

In [156]: print(data)
a,b,c
1,Yes,2
3,No,4

In [157]: pd.read_csv(StringIO(data))
Out[157]: 
   a    b  c
0  1  Yes  2
1  3   No  4

In [158]: pd.read_csv(StringIO(data), true_values=["Yes"], false_values=["No"])
Out[158]: 
   a      b  c
0  1   True  2
1  3  False  4

Обработка «плохих» строк

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

In [159]: data = "a,b,c\n1,2,3\n4,5,6,7\n8,9,10"

In [160]: pd.read_csv(StringIO(data))
---------------------------------------------------------------------------
ParserError                               Traceback (most recent call last)
Cell In [160], line 1
----> 1 pd.read_csv(StringIO(data))

File ~/work/pandas/pandas/pandas/util/_decorators.py:211, in deprecate_kwarg.<locals>._deprecate_kwarg.<locals>.wrapper(*args, **kwargs)
    209     else:
    210         kwargs[new_arg_name] = new_arg_value
--> 211 return func(*args, **kwargs)

File ~/work/pandas/pandas/pandas/util/_decorators.py:317, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
    311 if len(args) > num_allow_args:
    312     warnings.warn(
    313         msg.format(arguments=arguments),
    314         FutureWarning,
    315         stacklevel=find_stack_level(inspect.currentframe()),
    316     )
--> 317 return func(*args, **kwargs)

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:950, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)
    935 kwds_defaults = _refine_defaults_read(
    936     dialect,
    937     delimiter,
   (...)
    946     defaults={"delimiter": ","},
    947 )
    948 kwds.update(kwds_defaults)
--> 950 return _read(filepath_or_buffer, kwds)

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:611, in _read(filepath_or_buffer, kwds)
    608     return parser
    610 with parser:
--> 611     return parser.read(nrows)

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1772, in TextFileReader.read(self, nrows)
   1765 nrows = validate_integer("nrows", nrows)
   1766 try:
   1767     # error: "ParserBase" has no attribute "read"
   1768     (
   1769         index,
   1770         columns,
   1771         col_dict,
-> 1772     ) = self._engine.read(  # type: ignore[attr-defined]
   1773         nrows
   1774     )
   1775 except Exception:
   1776     self.close()

File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:243, in CParserWrapper.read(self, nrows)
    241 try:
    242     if self.low_memory:
--> 243         chunks = self._reader.read_low_memory(nrows)
    244         # destructive to chunks
    245         data = _concatenate_chunks(chunks)

File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:808, in pandas._libs.parsers.TextReader.read_low_memory()

File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:866, in pandas._libs.parsers.TextReader._read_rows()

File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:852, in pandas._libs.parsers.TextReader._tokenize_rows()

File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:1973, in pandas._libs.parsers.raise_parser_error()

ParserError: Error tokenizing data. C error: Expected 3 fields in line 3, saw 4

Вы можете выбрать пропуск плохих строк:

In [29]: pd.read_csv(StringIO(data), on_bad_lines="warn")
Skipping line 3: expected 3 fields, saw 4

Out[29]:
   a  b   c
0  1  2   3
1  8  9  10

Или передать вызываемую функцию для обработки плохой строки, если engine="python". Плохая строка будет списком строк, разделённых sep.

In [29]: external_list = []

In [30]: def bad_lines_func(line):
    ...:     external_list.append(line)
    ...:     return line[-3:]

In [31]: pd.read_csv(StringIO(data), on_bad_lines=bad_lines_func, engine="python")
Out[31]:
   a  b   c
0  1  2   3
1  5  6   7
2  8  9  10

In [32]: external_list
Out[32]: [4, 5, 6, 7]

.. versionadded:: 1.4.0

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

In [33]: pd.read_csv(StringIO(data), usecols=[0, 1, 2])

 Out[33]:
    a  b   c
 0  1  2   3
 1  4  5   6
 2  8  9  10

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

In [34]: pd.read_csv(StringIO(data), names=['a', 'b', 'c', 'd'])

Out[34]:
    a  b   c  d
 0  1  2   3  NaN
 1  4  5   6  7
 2  8  9  10  NaN

Диалект

Ключевое слово dialect предоставляет большую гибкость в указании формата файла. По умолчанию используется диалект Excel, но вы можете указать имя диалекта или экземпляр csv.Dialect.

Предположим, у вас были данные с необрамлёнными кавычками:

In [161]: data = "label1,label2,label3\n" 'index1,"a,c,e\n' "index2,b,d,f"

In [162]: print(data)
label1,label2,label3
index1,"a,c,e
index2,b,d,f

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

Мы можем обойти это, используя dialect:

In [163]: import csv

In [164]: dia = csv.excel()

In [165]: dia.quoting = csv.QUOTE_NONE

In [166]: pd.read_csv(StringIO(data), dialect=dia)
Out[166]: 
       label1 label2 label3
index1     "a      c      e
index2      b      d      f

Все опции диалекта можно указать отдельно в качестве ключевых аргументов:

In [167]: data = "a,b,c~1,2,3~4,5,6"

In [168]: pd.read_csv(StringIO(data), lineterminator="~")
Out[168]: 
   a  b  c
0  1  2  3
1  4  5  6

Ещё одна распространённая опция диалекта — skipinitialspace, чтобы пропустить любые пробелы после разделителя:

In [169]: data = "a, b, c\n1, 2, 3\n4, 5, 6"

In [170]: print(data)
a, b, c
1, 2, 3
4, 5, 6

In [171]: pd.read_csv(StringIO(data), skipinitialspace=True)
Out[171]: 
   a  b  c
0  1  2  3
1  4  5  6

Парсеры прилагают все усилия, чтобы «сделать правильно» и не быть хрупкими. Вывод типов — очень важная вещь. Если столбец может быть приведён к целочисленному типу без изменения содержимого, парсер это сделает. Любые нечисловые столбцы будут приходить как тип object, как и с остальными объектами pandas.

Кавычки и символы экранирования

Кавычки (и другие символы экранирования) в вложенных полях могут обрабатываться различными способами. Один из способов — использовать обратные слэши; чтобы правильно проанализировать эти данные, вы должны передать опцию escapechar:

In [172]: data = 'a,b\n"hello, \\"Bob\\", nice to see you",5'

In [173]: print(data)
a,b
"hello, \"Bob\", nice to see you",5

In [174]: pd.read_csv(StringIO(data), escapechar="\\")
Out[174]: 
                               a  b
0  hello, "Bob", nice to see you  5

Файлы с фиксированной шириной столбцов

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

  • colspecs: Список пар (кортежей), задающих границы полей фиксированной ширины каждой строки как полуоткрытых интервалов (т.е. [от, до[ ). Строковое значение ‘infer’ может быть использовано для указания парсеру попробовать определить спецификации столбцов из первых 100 строк данных. По умолчанию, если не указано, используется инференция.

  • widths: Список ширины полей, которые могут быть использованы вместо ‘colspecs’, если интервалы являются непрерывными.

  • delimiter: Символы, которые следует рассматривать как символы заполнителя в файле фиксированной ширины. Могут быть использованы для задания символа заполнителя полей, если он не пробелы (например, ‘~’).

Рассмотрим типичный файл данных фиксированной ширины:

In [175]: data1 = (
   .....:     "id8141    360.242940   149.910199   11950.7\n"
   .....:     "id1594    444.953632   166.985655   11788.4\n"
   .....:     "id1849    364.136849   183.628767   11806.2\n"
   .....:     "id1230    413.836124   184.375703   11916.8\n"
   .....:     "id1948    502.953953   173.237159   12468.3"
   .....: )
   .....: 

In [176]: with open("bar.csv", "w") as f:
   .....:     f.write(data1)
   .....: 

Для того чтобы проанализировать этот файл в DataFrame, нам просто нужно предоставить спецификации столбцов функции read_fwf вместе с именем файла:

# Column specifications are a list of half-intervals
In [177]: colspecs = [(0, 6), (8, 20), (21, 33), (34, 43)]

In [178]: df = pd.read_fwf("bar.csv", colspecs=colspecs, header=None, index_col=0)

In [179]: df
Out[179]: 
                 1           2        3
0                                      
id8141  360.242940  149.910199  11950.7
id1594  444.953632  166.985655  11788.4
id1849  364.136849  183.628767  11806.2
id1230  413.836124  184.375703  11916.8
id1948  502.953953  173.237159  12468.3

Обратите внимание, как парсер автоматически выбирает имена столбцов X.<номер столбца>, когда указан аргумент header=None. В качестве альтернативы, вы можете указать только ширину столбцов для непрерывных столбцов:

# Widths are a list of integers
In [180]: widths = [6, 14, 13, 10]

In [181]: df = pd.read_fwf("bar.csv", widths=widths, header=None)

In [182]: df
Out[182]: 
        0           1           2        3
0  id8141  360.242940  149.910199  11950.7
1  id1594  444.953632  166.985655  11788.4
2  id1849  364.136849  183.628767  11806.2
3  id1230  413.836124  184.375703  11916.8
4  id1948  502.953953  173.237159  12468.3

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

По умолчанию read_fwf попытается вывести colspecs файла, используя первые 100 строк файла. Это возможно только в тех случаях, когда столбцы выровнены и корректно разделены предоставленным delimiter (разделитель по умолчанию — пробел).

In [183]: df = pd.read_fwf("bar.csv", header=None, index_col=0)

In [184]: df
Out[184]: 
                 1           2        3
0                                      
id8141  360.242940  149.910199  11950.7
id1594  444.953632  166.985655  11788.4
id1849  364.136849  183.628767  11806.2
id1230  413.836124  184.375703  11916.8
id1948  502.953953  173.237159  12468.3

read_fwf поддерживает параметр dtype для задания типов анализируемых столбцов, отличных от выведенного типа.

In [185]: pd.read_fwf("bar.csv", header=None, index_col=0).dtypes
Out[185]: 
1    float64
2    float64
3    float64
dtype: object

In [186]: pd.read_fwf("bar.csv", header=None, dtype={2: "object"}).dtypes
Out[186]: 
0     object
1    float64
2     object
3    float64
dtype: object

Индексы

Файлы с «неявным» столбцом индекса

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

In [187]: data = "A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5"

In [188]: print(data)
A,B,C
20090101,a,1,2
20090102,b,3,4
20090103,c,4,5

In [189]: with open("foo.csv", "w") as f:
   .....:     f.write(data)
   .....: 

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

In [190]: pd.read_csv("foo.csv")
Out[190]: 
          A  B  C
20090101  a  1  2
20090102  b  3  4
20090103  c  4  5

Обратите внимание, что даты не были автоматически распарсены. В этом случае вам нужно сделать, как и прежде:

In [191]: df = pd.read_csv("foo.csv", parse_dates=True)

In [192]: df.index
Out[192]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', freq=None)

Чтение индекса с MultiIndex

Предположим, у вас есть данные, индексированные по двум столбцам:

In [193]: data = 'year,indiv,zit,xit\n1977,"A",1.2,.6\n1977,"B",1.5,.5'

In [194]: print(data)
year,indiv,zit,xit
1977,"A",1.2,.6
1977,"B",1.5,.5

In [195]: with open("mindex_ex.csv", mode="w") as f:
   .....:     f.write(data)
   .....: 

Аргумент index_col к read_csv может принимать список номеров столбцов, чтобы преобразовать несколько столбцов в MultiIndex для индекса возвращаемого объекта:

In [196]: df = pd.read_csv("mindex_ex.csv", index_col=[0, 1])

In [197]: df
Out[197]: 
            zit  xit
year indiv          
1977 A      1.2  0.6
     B      1.5  0.5

In [198]: df.loc[1977]
Out[198]: 
       zit  xit
indiv          
A      1.2  0.6
B      1.5  0.5

Чтение столбцов с MultiIndex

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

In [199]: from pandas._testing import makeCustomDataframe as mkdf

In [200]: df = mkdf(5, 3, r_idx_nlevels=2, c_idx_nlevels=4)

In [201]: df.to_csv("mi.csv")

In [202]: print(open("mi.csv").read())
C0,,C_l0_g0,C_l0_g1,C_l0_g2
C1,,C_l1_g0,C_l1_g1,C_l1_g2
C2,,C_l2_g0,C_l2_g1,C_l2_g2
C3,,C_l3_g0,C_l3_g1,C_l3_g2
R0,R1,,,
R_l0_g0,R_l1_g0,R0C0,R0C1,R0C2
R_l0_g1,R_l1_g1,R1C0,R1C1,R1C2
R_l0_g2,R_l1_g2,R2C0,R2C1,R2C2
R_l0_g3,R_l1_g3,R3C0,R3C1,R3C2
R_l0_g4,R_l1_g4,R4C0,R4C1,R4C2


In [203]: pd.read_csv("mi.csv", header=[0, 1, 2, 3], index_col=[0, 1])
Out[203]: 
C0              C_l0_g0 C_l0_g1 C_l0_g2
C1              C_l1_g0 C_l1_g1 C_l1_g2
C2              C_l2_g0 C_l2_g1 C_l2_g2
C3              C_l3_g0 C_l3_g1 C_l3_g2
R0      R1                             
R_l0_g0 R_l1_g0    R0C0    R0C1    R0C2
R_l0_g1 R_l1_g1    R1C0    R1C1    R1C2
R_l0_g2 R_l1_g2    R2C0    R2C1    R2C2
R_l0_g3 R_l1_g3    R3C0    R3C1    R3C2
R_l0_g4 R_l1_g4    R4C0    R4C1    R4C2

read_csv также может интерпретировать более распространённый формат индексов по нескольким столбцам.

In [204]: data = ",a,a,a,b,c,c\n,q,r,s,t,u,v\none,1,2,3,4,5,6\ntwo,7,8,9,10,11,12"

In [205]: print(data)
,a,a,a,b,c,c
,q,r,s,t,u,v
one,1,2,3,4,5,6
two,7,8,9,10,11,12

In [206]: with open("mi2.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [207]: pd.read_csv("mi2.csv", header=[0, 1], index_col=0)
Out[207]: 
     a         b   c    
     q  r  s   t   u   v
one  1  2  3   4   5   6
two  7  8  9  10  11  12

Примечание

Если index_col не указан (например, у вас нет индекса или вы его написали с помощью df.to_csv(..., index=False)), то любые names на индексе столбцов будут утеряны.

Автоматическое «определениe» разделителя

read_csv может определять файлы с разделителями (не обязательно с запятыми), так как pandas использует класс csv.Sniffer из модуля csv. Для этого вам нужно указать sep=None.

In [208]: df = pd.DataFrame(np.random.randn(10, 4))

In [209]: df.to_csv("tmp.csv", sep="|")

In [210]: df.to_csv("tmp2.csv", sep=":")

In [211]: pd.read_csv("tmp2.csv", sep=None, engine="python")
Out[211]: 
   Unnamed: 0         0         1         2         3
0           0  0.469112 -0.282863 -1.509059 -1.135632
1           1  1.212112 -0.173215  0.119209 -1.044236
2           2 -0.861849 -2.104569 -0.494929  1.071804
3           3  0.721555 -0.706771 -1.039575  0.271860
4           4 -0.424972  0.567020  0.276232 -1.087401
5           5 -0.673690  0.113648 -1.478427  0.524988
6           6  0.404705  0.577046 -1.715002 -1.039268
7           7 -0.370647 -1.157892 -1.344312  0.844885
8           8  1.075770 -0.109050  1.643563 -1.469388
9           9  0.357021 -0.674600 -1.776904 -0.968914

Чтение нескольких файлов для создания одного DataFrame

Лучше использовать concat() для объединения нескольких файлов. См. справочник для примера.

Итерация по файлам по частям

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

In [212]: df = pd.DataFrame(np.random.randn(10, 4))

In [213]: df.to_csv("tmp.csv", sep="|")

In [214]: table = pd.read_csv("tmp.csv", sep="|")

In [215]: table
Out[215]: 
   Unnamed: 0         0         1         2         3
0           0 -1.294524  0.413738  0.276662 -0.472035
1           1 -0.013960 -0.362543 -0.006154 -0.923061
2           2  0.895717  0.805244 -1.206412  2.565646
3           3  1.431256  1.340309 -1.170299 -0.226169
4           4  0.410835  0.813850  0.132003 -0.827317
5           5 -0.076467 -1.187678  1.130127 -1.436737
6           6 -1.413681  1.607920  1.024180  0.569605
7           7  0.875906 -2.211372  0.974466 -2.006747
8           8 -0.410001 -0.078638  0.545952 -1.219217
9           9 -1.226825  0.769804 -1.281247 -0.727707

Указав chunksize для read_csv, возвращаемое значение будет итерируемым объектом типа TextFileReader:

In [216]: with pd.read_csv("tmp.csv", sep="|", chunksize=4) as reader:
   .....:     reader
   .....:     for chunk in reader:
   .....:         print(chunk)
   .....: 
   Unnamed: 0         0         1         2         3
0           0 -1.294524  0.413738  0.276662 -0.472035
1           1 -0.013960 -0.362543 -0.006154 -0.923061
2           2  0.895717  0.805244 -1.206412  2.565646
3           3  1.431256  1.340309 -1.170299 -0.226169
   Unnamed: 0         0         1         2         3
4           4  0.410835  0.813850  0.132003 -0.827317
5           5 -0.076467 -1.187678  1.130127 -1.436737
6           6 -1.413681  1.607920  1.024180  0.569605
7           7  0.875906 -2.211372  0.974466 -2.006747
   Unnamed: 0         0         1         2         3
8           8 -0.410001 -0.078638  0.545952 -1.219217
9           9 -1.226825  0.769804 -1.281247 -0.727707

Изменено в версии 1.2: read_csv/json/sas возвращает менеджер контекста при итерации по файлу.

Указание iterator=True также вернёт TextFileReader объект:

In [217]: with pd.read_csv("tmp.csv", sep="|", iterator=True) as reader:
   .....:     reader.get_chunk(5)
   .....: 

Указание движка анализатора

Pandas в настоящее время поддерживает три движка: C-движок, Python-движок и экспериментальный движок pyarrow (требуется пакет pyarrow). Как правило, движок pyarrow быстрее при больших объёмах данных и эквивалентен по скорости C-движку в большинстве других случаев. Python-движок, как правило, медленнее, чем pyarrow и C-движки в большинстве случаев. Однако движок pyarrow намного менее надёжен, чем C-движок, которому не хватает нескольких функций по сравнению с Python-движком.

Когда это возможно, pandas использует C-анализатор (указанный как engine='c'), но может переключиться на Python, если указаны параметры, не поддерживаемые C.

В настоящее время параметры, не поддерживаемые C- и pyarrow-движками, включают:

  • sep кроме одного символа (например, разделители вида регулярного выражения)

  • skipfooter

  • sep=None с delim_whitespace=False

Указание любого из вышеперечисленных параметров приведёт к ParserWarning, если не выбран явно Python-движок с помощью engine='python'.

Параметры, не поддерживаемые pyarrow-движком, не охваченные списком выше, включают:

  • float_precision

  • chunksize

  • comment

  • nrows

  • thousands

  • memory_map

  • dialect

  • warn_bad_lines

  • error_bad_lines

  • on_bad_lines

  • delim_whitespace

  • quoting

  • lineterminator

  • converters

  • decimal

  • iterator

  • dayfirst

  • infer_datetime_format

  • verbose

  • skipinitialspace

  • low_memory

Указание этих параметров с engine='pyarrow' вызовет ValueError.

Чтение/запись удалённых файлов

Вы можете передать URL для чтения или записи удалённых файлов во многие функции ввода-вывода pandas — следующий пример демонстрирует чтение CSV-файла:

df = pd.read_csv("https://download.bls.gov/pub/time.series/cu/cu.item", sep="\t")

Добавлена в версии 1.3.0.

Пользовательский заголовок может быть отправлен вместе с HTTP(s)-запросами путём передачи словаря сопоставлений ключей и значений заголовков в аргумент storage_options как показано ниже:

headers = {"User-Agent": "pandas"}
df = pd.read_csv(
    "https://download.bls.gov/pub/time.series/cu/cu.item",
    sep="\t",
    storage_options=headers
)

Все URL, которые не являются локальными файлами или HTTP(s), обрабатываются через fsspec, если установлен, и его различные реализации файловых систем (включая Amazon S3, Google Cloud, SSH, FTP, webHDFS…). Некоторые из этих реализаций потребуют установки дополнительных пакетов, например, URL-адреса S3 требуют библиотеки s3fs:

df = pd.read_json("s3://pandas-test/adatafile.json")

При работе с удалёнными системами хранения вам, возможно, потребуется дополнительная настройка с переменными окружения или конфигурационными файлами в специальных расположениях. Например, для доступа к данным в вашем ведре S3 вам необходимо определить учетные данные одним из нескольких способов, перечисленных в документации S3Fs. То же самое относится к нескольким бэкендам хранения, и вам следует перейти по ссылкам в fsimpl1 для реализаций, встроенных в fsspec и fsimpl2 для тех, что не входят в основное распределение fsspec.

Вы также можете передать параметры непосредственно драйверу бэкенда. Например, если у вас нет учетных данных S3, вы всё ещё можете получить доступ к общедоступным данным, указав анонимное подключение, например

Добавлена в версии 1.2.0.

pd.read_csv(
    "s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/SaKe2013"
    "-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv",
    storage_options={"anon": True},
)

fsspec также позволяет использовать сложные URL для доступа к данным в сжатых архивах, кэширования файлов локально и многое другое. Чтобы кэшировать вышеприведенный пример локально, вы измените вызов на

pd.read_csv(
    "simplecache::s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/"
    "SaKe2013-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv",
    storage_options={"s3": {"anon": True}},
)

где мы указываем, что параметр «anon» предназначен для части «s3» реализации, а не для реализации кэширования. Обратите внимание, что это кэширует только в временную директорию на время сессии, но вы также можете указать постоянное хранилище.

END_OF_DOCUMENT_MARKER

Вывод данных

Запись в формате CSV

Объекты Series и DataFrame имеют метод экземпляра to_csv, который позволяет сохранить содержимое объекта в виде файла со значениями, разделенными запятыми. Функция принимает несколько аргументов. Только первый является обязательным.

  • path_or_buf: Строковый путь к файлу для записи или объект файла. Если объект файла, он должен быть открыт с использованием newline=''

  • sep: Разделитель полей для выходного файла (по умолчанию “,”)

  • na_rep: Строковое представление пропущенного значения (по умолчанию ‘’)

  • float_format: Форматная строка для чисел с плавающей точкой

  • columns: Столбцы для записи (по умолчанию None)

  • header: Выводить заголовки столбцов (по умолчанию True)

  • index: Выводить имена строк (индексы) (по умолчанию True)

  • index_label: Метки столбцов для столбцов индекса, если необходимо. Если None (по умолчанию), и header и index равны True, то используются имена индексов. (Последовательность должна быть указана, если DataFrame использует MultiIndex).

  • mode: Режим записи Python, по умолчанию ‘w’

  • encoding: Строка, представляющая кодировку для использования, если содержимое не ASCII, для версий Python до 3

  • lineterminator: Последовательность символов, обозначающая конец строки (по умолчанию os.linesep)

  • quoting: Установка правил цитирования, как в модуле csv (по умолчанию csv.QUOTE_MINIMAL). Обратите внимание, что если вы установили float_format, то числа с плавающей точкой преобразуются в строки, и csv.QUOTE_NONNUMERIC будет рассматривать их как нечисловые

  • quotechar: Символ, используемый для цитирования полей (по умолчанию ‘”’)

  • doublequote: Управление цитированием quotechar в полях (по умолчанию True)

  • escapechar: Символ, используемый для экранирования sep и quotechar, если необходимо (по умолчанию None)

  • chunksize: Количество строк для записи за один раз

  • date_format: Форматная строка для объектов datetime

Запись отформатированной строки

Объект DataFrame имеет метод экземпляра to_string, который позволяет управлять строковым представлением объекта. Все аргументы являются необязательными:

  • buf по умолчанию None, например, объект StringIO

  • columns по умолчанию None, которые столбцы записывать

  • col_space по умолчанию None, минимальная ширина каждого столбца.

  • na_rep по умолчанию NaN, представление значения NA

  • formatters по умолчанию None, словарь (по столбцам) функций, каждая из которых принимает один аргумент и возвращает отформатированную строку

  • float_format по умолчанию None, функция, которая принимает один аргумент (float) и возвращает отформатированную строку; должна применяться к числам с плавающей точкой в DataFrame.

  • sparsify по умолчанию True, установить в False для DataFrame с иерархическим индексом, чтобы вывести каждый ключ MultiIndex в каждой строке.

  • index_names по умолчанию True, выведет имена индексов

  • index по умолчанию True, выведет индекс (т.е., метки строк)

  • header по умолчанию True, выведет метки столбцов

  • justify по умолчанию left, выведет заголовки столбцов выровненными влево или вправо

Объект Series также имеет метод to_string, но только с аргументами buf, na_rep, float_format. Также есть аргумент length, который, если установлен в True, дополнительно выведет длину Series.

JSON

Чтение и запись файлов и строк в формате JSON.

Запись JSON

Объект Series или DataFrame может быть преобразован в строку JSON. Используйте to_json с необязательными параметрами:

  • path_or_buf : путь к файлу или буфер для записи вывода. Может быть None, в этом случае возвращается строка JSON.

  • orient:

    Series:
    • по умолчанию index

    • допустимые значения {split, records, index}

    DataFrame:
    • по умолчанию columns

    • допустимые значения {split, records, index, columns, values, table}

    Формат строки JSON

    split

    словарь типа {индекс -> [индекс], столбцы -> [столбцы], данные -> [значения]}

    records

    список типа [{столбец -> значение}, … , {столбец -> значение}]

    index

    словарь типа {индекс -> {столбец -> значение}}

    columns

    словарь типа {столбец -> {индекс -> значение}}

    values

    только массив значений

    table

    соответствие схеме таблицы JSON

  • date_format : строка, тип преобразования даты, ‘epoch’ для отметки времени, ‘iso’ для ISO8601.

  • double_precision : количество десятичных знаков для кодирования чисел с плавающей точкой, по умолчанию 10.

  • force_ascii : принудительно кодировать строку в ASCII, по умолчанию True.

  • date_unit : единица времени для кодирования, управляет точностью отметки времени и ISO8601. Одно из значений ‘s’, ‘ms’, ‘us’ или ‘ns’ для секунд, миллисекунд, микросекунд и наносекунд соответственно. По умолчанию ‘ms’.

  • default_handler : обработчик, вызываемый, если объект не может быть преобразован в подходящий для JSON формат. Принимает один аргумент — объект для преобразования и возвращает сериализуемый объект.

  • lines : Если records orient, то каждый запис будет записываться в новой строке как json.

Примечание: NaN, NaT и None будут преобразованы в null, а datetime объекты будут преобразованы на основе параметров date_format и date_unit.

In [218]: dfj = pd.DataFrame(np.random.randn(5, 2), columns=list("AB"))

In [219]: json = dfj.to_json()

In [220]: json
Out[220]: '{"A":{"0":-0.1213062281,"1":0.6957746499,"2":0.9597255933,"3":-0.6199759194,"4":-0.7323393705},"B":{"0":-0.0978826728,"1":0.3417343559,"2":-1.1103361029,"3":0.1497483186,"4":0.6877383895}}'

Варианты orient

Существует ряд вариантов формата результирующего файла/строки JSON. Рассмотрим следующие DataFrame и Series:

In [221]: dfjo = pd.DataFrame(
   .....:     dict(A=range(1, 4), B=range(4, 7), C=range(7, 10)),
   .....:     columns=list("ABC"),
   .....:     index=list("xyz"),
   .....: )
   .....: 

In [222]: dfjo
Out[222]: 
   A  B  C
x  1  4  7
y  2  5  8
z  3  6  9

In [223]: sjo = pd.Series(dict(x=15, y=16, z=17), name="D")

In [224]: sjo
Out[224]: 
x    15
y    16
z    17
Name: D, dtype: int64

Ориентированный по столбцам (по умолчанию для DataFrame ) сериализует данные в виде вложенных объектов JSON со столбцами в качестве основного индекса:

In [225]: dfjo.to_json(orient="columns")
Out[225]: '{"A":{"x":1,"y":2,"z":3},"B":{"x":4,"y":5,"z":6},"C":{"x":7,"y":8,"z":9}}'

# Not available for Series

Ориентированный по индексу (по умолчанию для Series ) аналогично ориентированному по столбцам, но метки индексов теперь являются основными:

In [226]: dfjo.to_json(orient="index")
Out[226]: '{"x":{"A":1,"B":4,"C":7},"y":{"A":2,"B":5,"C":8},"z":{"A":3,"B":6,"C":9}}'

In [227]: sjo.to_json(orient="index")
Out[227]: '{"x":15,"y":16,"z":17}'

Ориентированный по записям сериализует данные в массив JSON с записями столбец -> значение, метки индексов не включаются. Это полезно для передачи данных DataFrame в библиотеки визуализации, например, в JavaScript-библиотеку d3.js:

In [228]: dfjo.to_json(orient="records")
Out[228]: '[{"A":1,"B":4,"C":7},{"A":2,"B":5,"C":8},{"A":3,"B":6,"C":9}]'

In [229]: sjo.to_json(orient="records")
Out[229]: '[15,16,17]'

Ориентированный по значениям — базовый вариант, который сериализует данные в вложенные массивы JSON только со значениями, метки столбцов и индексов не включаются:

In [230]: dfjo.to_json(orient="values")
Out[230]: '[[1,4,7],[2,5,8],[3,6,9]]'

# Not available for Series

Ориентированный по разделу сериализует данные в JSON-объект, содержащий отдельные записи для значений, индексов и столбцов. Имя также включено для Series:

In [231]: dfjo.to_json(orient="split")
Out[231]: '{"columns":["A","B","C"],"index":["x","y","z"],"data":[[1,4,7],[2,5,8],[3,6,9]]}'

In [232]: sjo.to_json(orient="split")
Out[232]: '{"name":"D","index":["x","y","z"],"data":[15,16,17]}'

Ориентированный по таблице сериализует данные в JSON схему таблицы, позволяя сохранять метаданные, включая, но не ограничиваясь, типы данных и имена индексов.

Примечание

Любой вариант orient, который кодирует в JSON-объект, не сохранит порядок меток индексов и столбцов во время обратного сериализации. Если вы хотите сохранить порядок меток, используйте опцию split , так как она использует упорядоченные контейнеры.

Обработка дат

Запись в формате даты ISO:

In [233]: dfd = pd.DataFrame(np.random.randn(5, 2), columns=list("AB"))

In [234]: dfd["date"] = pd.Timestamp("20130101")

In [235]: dfd = dfd.sort_index(axis=1, ascending=False)

In [236]: json = dfd.to_json(date_format="iso")

In [237]: json
Out[237]: '{"date":{"0":"2013-01-01T00:00:00.000","1":"2013-01-01T00:00:00.000","2":"2013-01-01T00:00:00.000","3":"2013-01-01T00:00:00.000","4":"2013-01-01T00:00:00.000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'

Запись в формате даты ISO с микросекундами:

In [238]: json = dfd.to_json(date_format="iso", date_unit="us")

In [239]: json
Out[239]: '{"date":{"0":"2013-01-01T00:00:00.000000","1":"2013-01-01T00:00:00.000000","2":"2013-01-01T00:00:00.000000","3":"2013-01-01T00:00:00.000000","4":"2013-01-01T00:00:00.000000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'

Отметки времени epoch в секундах:

In [240]: json = dfd.to_json(date_format="epoch", date_unit="s")

In [241]: json
Out[241]: '{"date":{"0":1356998400,"1":1356998400,"2":1356998400,"3":1356998400,"4":1356998400},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'

Запись в файл с индексом даты и столбцом даты:

In [242]: dfj2 = dfj.copy()

In [243]: dfj2["date"] = pd.Timestamp("20130101")

In [244]: dfj2["ints"] = list(range(5))

In [245]: dfj2["bools"] = True

In [246]: dfj2.index = pd.date_range("20130101", periods=5)

In [247]: dfj2.to_json("test.json")

In [248]: with open("test.json") as fh:
   .....:     print(fh.read())
   .....: 
{"A":{"1356998400000":-0.1213062281,"1357084800000":0.6957746499,"1357171200000":0.9597255933,"1357257600000":-0.6199759194,"1357344000000":-0.7323393705},"B":{"1356998400000":-0.0978826728,"1357084800000":0.3417343559,"1357171200000":-1.1103361029,"1357257600000":0.1497483186,"1357344000000":0.6877383895},"date":{"1356998400000":1356998400000,"1357084800000":1356998400000,"1357171200000":1356998400000,"1357257600000":1356998400000,"1357344000000":1356998400000},"ints":{"1356998400000":0,"1357084800000":1,"1357171200000":2,"1357257600000":3,"1357344000000":4},"bools":{"1356998400000":true,"1357084800000":true,"1357171200000":true,"1357257600000":true,"1357344000000":true}}

Поведение по умолчанию

Если JSON-сериализатор не может напрямую обработать содержимое контейнера, он откажется от обработки следующим образом:

  • если тип данных не поддерживается (например, np.complex_), то default_handler, если предоставлен, будет вызываться для каждого значения, в противном случае возникает исключение.

  • если объект не поддерживается, он предпримет следующие действия:

    • проверит, определен ли метод toDict объекта, и вызовет его. Метод toDict должен вернуть dict, который затем будет сериализован в JSON.

    • вызовет default_handler, если он был предоставлен.

    • преобразует объект в dict путем обхода его содержимого. Однако это часто приведет к OverflowError или даст неожиданные результаты.

В целом, лучшим подходом для недопустимых объектов или типов данных является предоставление default_handler. Например:

>>> DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json()  # raises
RuntimeError: Unhandled numpy dtype 15

может быть обработано, указав простой default_handler:

In [249]: pd.DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json(default_handler=str)
Out[249]: '{"0":{"0":"(1+0j)","1":"(2+0j)","2":"(1+2j)"}}'

Чтение JSON

Чтение JSON-строки в объект pandas может принимать ряд параметров. Парсер будет пытаться разобрать DataFrame , если typ не указан или равен None. Чтобы явно принудительно использовать Series парсинг, передайте typ=series

  • filepath_or_buffer : ВАЛИДНАЯ JSON-строка или дескриптор файла/объект StringIO. Строка может быть URL. Допустимые схемы URL включают http, ftp, S3 и file. Для URL-адресов файлов ожидается хост. Например, локальный файл может быть file://localhost/путь/к/таблице.json

  • typ : тип объекта для извлечения (series или frame), по умолчанию ‘frame’

  • orient :

    Series :
    • по умолчанию index

    • допустимые значения {split, records, index}

    DataFrame
    • по умолчанию columns

    • допустимые значения {split, records, index, columns, values, table}

    Формат JSON-строки

    split

    похожий на словарь {индекс -> [индекс], столбцы -> [столбцы], данные -> [значения]}

    records

    похожий на список [{столбец -> значение}, … , {столбец -> значение}]

    index

    похожий на словарь {индекс -> {столбец -> значение}}

    columns

    похожий на словарь {столбец -> {индекс -> значение}}

    values

    только массив значений

    table

    соответствует схеме таблицы JSON

  • dtype : если True, определить типы данных, если словарь столбец-тип данных, то использовать их, если False, тогда вообще не определять типы данных, по умолчанию True, применяется только к данным.

  • convert_axes : булево, попытаться преобразовать оси в соответствующие типы данных, по умолчанию True

  • convert_dates : список столбцов для парсинга дат; Если True, то попытаться разобрать столбцы, подобные дате, по умолчанию True.

  • keep_default_dates : булево, по умолчанию True. Если парсинг дат, то разобрать столбцы по умолчанию, подобные дате.

  • numpy : прямое декодирование в массивы NumPy. По умолчанию False; Поддерживаются только числовые данные, хотя метки могут быть не числовыми. Также обратите внимание, что порядок JSON ДОЛЖЕН быть одинаковым для каждого элемента, если numpy=True.

  • precise_float : булево, по умолчанию False. Установите, чтобы включить использование функции с большей точностью (strtod) при декодировании строк в двойные значения. По умолчанию (False) используется быстрая, но менее точная встроенная функциональность.

  • date_unit : строка, единица измерения временной метки для определения преобразования дат. По умолчанию None. По умолчанию точность временной метки будет определена, если это не нужно, передайте одно из ‘s’, ‘ms’, ‘us’ или ‘ns’, чтобы принудительно установить точность временной метки в секунды, миллисекунды, микросекунды или наносекунды соответственно.

  • lines : считывает файл как один JSON-объект на строку.

  • encoding : кодировка для декодирования py3 байтов.

  • chunksize : при использовании в сочетании с lines=True, возвращает JsonReader, который считывает chunksize строк за итерацию.

Парсер поднимет одну из ValueError/TypeError/AssertionError ошибок, если JSON не может быть распарсен.

Если использовалось нестандартное orient при кодировании в JSON, убедитесь, что вы передаёте тот же параметр здесь, чтобы декодирование давало осмысленные результаты. См. Параметры ориентирования для обзора.

Преобразование данных

Значения по умолчанию convert_axes=True, dtype=True, и convert_dates=True будут пытаться разобрать оси и все данные в соответствующие типы, включая даты. Если необходимо переопределить конкретные типы данных, передайте словарь в dtype. convert_axes следует установить в False только если необходимо сохранить числовые значения в виде строк (например, ‘1’, ‘2’) в осях.

Примечание

Большие целочисленные значения могут быть преобразованы в даты, если convert_dates=True и данные и/или метки столбцов имеют «похожий на дату» вид. Точный порог зависит от date_unit. «Похожий на дату» означает, что метка столбца соответствует одному из следующих критериев:

  • она оканчивается на '_at'

  • она оканчивается на '_time'

  • она начинается с 'timestamp'

  • она является 'modified'

  • она является 'date'

Предупреждение

При чтении JSON-данных автоматическое приведение типов имеет некоторые особенности:

  • индекс может быть восстановлен в другом порядке, чем при сериализации, то есть гарантируется, что возвращённый порядок будет таким же, как и раньше

  • столбец, содержащий данные float, будет преобразован в integer, если это можно сделать безопасно, например, столбец 1.

  • столбцы bool будут преобразованы в integer при восстановлении

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

Чтение из JSON-строки:

In [250]: pd.read_json(json)
Out[250]: 
        date         B         A
0 2013-01-01  0.403310  0.176444
1 2013-01-01  0.301624 -0.154951
2 2013-01-01 -1.369849 -2.179861
3 2013-01-01  1.462696 -0.954208
4 2013-01-01 -0.826591 -1.743161

Чтение из файла:

In [251]: pd.read_json("test.json")
Out[251]: 
                   A         B       date  ints  bools
2013-01-01 -0.121306 -0.097883 2013-01-01     0   True
2013-01-02  0.695775  0.341734 2013-01-01     1   True
2013-01-03  0.959726 -1.110336 2013-01-01     2   True
2013-01-04 -0.619976  0.149748 2013-01-01     3   True
2013-01-05 -0.732339  0.687738 2013-01-01     4   True

Не преобразовывать данные (но всё равно преобразовывать оси и даты):

In [252]: pd.read_json("test.json", dtype=object).dtypes
Out[252]: 
A        object
B        object
date     object
ints     object
bools    object
dtype: object

Указать типы данных для преобразования:

In [253]: pd.read_json("test.json", dtype={"A": "float32", "bools": "int8"}).dtypes
Out[253]: 
A               float32
B               float64
date     datetime64[ns]
ints              int64
bools              int8
dtype: object

Сохранить строковые индексы:

In [254]: si = pd.DataFrame(
   .....:     np.zeros((4, 4)), columns=list(range(4)), index=[str(i) for i in range(4)]
   .....: )
   .....: 

In [255]: si
Out[255]: 
     0    1    2    3
0  0.0  0.0  0.0  0.0
1  0.0  0.0  0.0  0.0
2  0.0  0.0  0.0  0.0
3  0.0  0.0  0.0  0.0

In [256]: si.index
Out[256]: Index(['0', '1', '2', '3'], dtype='object')

In [257]: si.columns
Out[257]: Int64Index([0, 1, 2, 3], dtype='int64')

In [258]: json = si.to_json()

In [259]: sij = pd.read_json(json, convert_axes=False)

In [260]: sij
Out[260]: 
   0  1  2  3
0  0  0  0  0
1  0  0  0  0
2  0  0  0  0
3  0  0  0  0

In [261]: sij.index
Out[261]: Index(['0', '1', '2', '3'], dtype='object')

In [262]: sij.columns
Out[262]: Index(['0', '1', '2', '3'], dtype='object')

Даты, записанные в наносекундах, необходимо читать обратно в наносекундах:

In [263]: json = dfj2.to_json(date_unit="ns")

# Try to parse timestamps as milliseconds -> Won't Work
In [264]: dfju = pd.read_json(json, date_unit="ms")

In [265]: dfju
Out[265]: 
                            A         B                 date  ints  bools
1356998400000000000 -0.121306 -0.097883  1356998400000000000     0   True
1357084800000000000  0.695775  0.341734  1356998400000000000     1   True
1357171200000000000  0.959726 -1.110336  1356998400000000000     2   True
1357257600000000000 -0.619976  0.149748  1356998400000000000     3   True
1357344000000000000 -0.732339  0.687738  1356998400000000000     4   True

# Let pandas detect the correct precision
In [266]: dfju = pd.read_json(json)

In [267]: dfju
Out[267]: 
                   A         B       date  ints  bools
2013-01-01 -0.121306 -0.097883 2013-01-01     0   True
2013-01-02  0.695775  0.341734 2013-01-01     1   True
2013-01-03  0.959726 -1.110336 2013-01-01     2   True
2013-01-04 -0.619976  0.149748 2013-01-01     3   True
2013-01-05 -0.732339  0.687738 2013-01-01     4   True

# Or specify that all timestamps are in nanoseconds
In [268]: dfju = pd.read_json(json, date_unit="ns")

In [269]: dfju
Out[269]: 
                   A         B       date  ints  bools
2013-01-01 -0.121306 -0.097883 2013-01-01     0   True
2013-01-02  0.695775  0.341734 2013-01-01     1   True
2013-01-03  0.959726 -1.110336 2013-01-01     2   True
2013-01-04 -0.619976  0.149748 2013-01-01     3   True
2013-01-05 -0.732339  0.687738 2013-01-01     4   True

Параметр Numpy

Примечание

Этот параметр устарел с версии 1.0.0 и будет вызывать FutureWarning.

Это поддерживает только числовые данные. Метки индекса и столбцов могут быть не числовыми, например, строками, датами и т. д.

Если numpy=True передаётся в read_json, будет сделана попытка определить подходящий тип данных во время десериализации и затем непосредственно декодировать в массивы NumPy, минуя промежуточные объекты Python.

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

In [270]: randfloats = np.random.uniform(-100, 1000, 10000)

In [271]: randfloats.shape = (1000, 10)

In [272]: dffloats = pd.DataFrame(randfloats, columns=list("ABCDEFGHIJ"))

In [273]: jsonfloats = dffloats.to_json()
In [274]: %timeit pd.read_json(jsonfloats)
11.6 ms +- 92.8 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [275]: %timeit pd.read_json(jsonfloats, numpy=True)
8.75 ms +- 107 us per loop (mean +- std. dev. of 7 runs, 100 loops each)

Ускорение менее заметно для меньших наборов данных:

In [276]: jsonfloats = dffloats.head(100).to_json()
In [277]: %timeit pd.read_json(jsonfloats)
7.27 ms +- 105 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [278]: %timeit pd.read_json(jsonfloats, numpy=True)
6.69 ms +- 117 us per loop (mean +- std. dev. of 7 runs, 100 loops each)

Предупреждение

Прямое декодирование NumPy делает ряд предположений и может завершиться ошибкой или выдать неожиданный результат, если эти предположения не выполнены:

  • данные являются числовыми.

  • данные однородные. Тип данных определяется из первого декодированного значения. Может быть выброшено ValueError, или могут быть получены некорректные результаты, если это условие не выполняется.

  • метки упорядочены. Метки считываются только из первого контейнера, предполагается, что каждая последующая строка/столбец закодированы в том же порядке. Это должно выполняться, если данные были закодированы с помощью to_json, но может не выполняться, если JSON получен из другого источника.

Нормализация

pandas предоставляет вспомогательную функцию для преобразования словаря или списка словарей в нормализованную плоскую таблицу.

In [279]: data = [
   .....:     {"id": 1, "name": {"first": "Coleen", "last": "Volk"}},
   .....:     {"name": {"given": "Mark", "family": "Regner"}},
   .....:     {"id": 2, "name": "Faye Raker"},
   .....: ]
   .....: 

In [280]: pd.json_normalize(data)
Out[280]: 
    id name.first name.last name.given name.family        name
0  1.0     Coleen      Volk        NaN         NaN         NaN
1  NaN        NaN       NaN       Mark      Regner         NaN
2  2.0        NaN       NaN        NaN         NaN  Faye Raker
In [281]: data = [
   .....:     {
   .....:         "state": "Florida",
   .....:         "shortname": "FL",
   .....:         "info": {"governor": "Rick Scott"},
   .....:         "county": [
   .....:             {"name": "Dade", "population": 12345},
   .....:             {"name": "Broward", "population": 40000},
   .....:             {"name": "Palm Beach", "population": 60000},
   .....:         ],
   .....:     },
   .....:     {
   .....:         "state": "Ohio",
   .....:         "shortname": "OH",
   .....:         "info": {"governor": "John Kasich"},
   .....:         "county": [
   .....:             {"name": "Summit", "population": 1234},
   .....:             {"name": "Cuyahoga", "population": 1337},
   .....:         ],
   .....:     },
   .....: ]
   .....: 

In [282]: pd.json_normalize(data, "county", ["state", "shortname", ["info", "governor"]])
Out[282]: 
         name  population    state shortname info.governor
0        Dade       12345  Florida        FL    Rick Scott
1     Broward       40000  Florida        FL    Rick Scott
2  Palm Beach       60000  Florida        FL    Rick Scott
3      Summit        1234     Ohio        OH   John Kasich
4    Cuyahoga        1337     Ohio        OH   John Kasich

Параметр max_level предоставляет больший контроль над уровнем завершения нормализации. При max_level=1 следующий фрагмент выполняет нормализацию до первого уровня вложенности предоставленного словаря.

In [283]: data = [
   .....:     {
   .....:         "CreatedBy": {"Name": "User001"},
   .....:         "Lookup": {
   .....:             "TextField": "Some text",
   .....:             "UserField": {"Id": "ID001", "Name": "Name001"},
   .....:         },
   .....:         "Image": {"a": "b"},
   .....:     }
   .....: ]
   .....: 

In [284]: pd.json_normalize(data, max_level=1)
Out[284]: 
  CreatedBy.Name Lookup.TextField                    Lookup.UserField Image.a
0        User001        Some text  {'Id': 'ID001', 'Name': 'Name001'}       b

JSON, разделитель — символ новой строки

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

Для файлов JSON, разделенных символом новой строки, pandas также может возвращать итератор, который считывает chunksize строк за раз. Это может быть полезно для больших файлов или для чтения из потока.

In [285]: jsonl = """
   .....:     {"a": 1, "b": 2}
   .....:     {"a": 3, "b": 4}
   .....: """
   .....: 

In [286]: df = pd.read_json(jsonl, lines=True)

In [287]: df
Out[287]: 
   a  b
0  1  2
1  3  4

In [288]: df.to_json(orient="records", lines=True)
Out[288]: '{"a":1,"b":2}\n{"a":3,"b":4}\n'

# reader is an iterator that returns ``chunksize`` lines each iteration
In [289]: with pd.read_json(StringIO(jsonl), lines=True, chunksize=1) as reader:
   .....:     reader
   .....:     for chunk in reader:
   .....:         print(chunk)
   .....: 
Empty DataFrame
Columns: []
Index: []
   a  b
0  1  2
   a  b
1  3  4

Схема таблицы

Схема таблицы — это спецификация для описания табличных наборов данных как JSON-объекта. JSON содержит информацию об именах полей, типах и других атрибутах. Вы можете использовать ориентацию table для создания JSON-строки с двумя полями, schema и data.

In [290]: df = pd.DataFrame(
   .....:     {
   .....:         "A": [1, 2, 3],
   .....:         "B": ["a", "b", "c"],
   .....:         "C": pd.date_range("2016-01-01", freq="d", periods=3),
   .....:     },
   .....:     index=pd.Index(range(3), name="idx"),
   .....: )
   .....: 

In [291]: df
Out[291]: 
     A  B          C
idx                 
0    1  a 2016-01-01
1    2  b 2016-01-02
2    3  c 2016-01-03

In [292]: df.to_json(orient="table", date_format="iso")
Out[292]: '{"schema":{"fields":[{"name":"idx","type":"integer"},{"name":"A","type":"integer"},{"name":"B","type":"string"},{"name":"C","type":"datetime"}],"primaryKey":["idx"],"pandas_version":"1.4.0"},"data":[{"idx":0,"A":1,"B":"a","C":"2016-01-01T00:00:00.000"},{"idx":1,"A":2,"B":"b","C":"2016-01-02T00:00:00.000"},{"idx":2,"A":3,"B":"c","C":"2016-01-03T00:00:00.000"}]}'

Поле schema содержит ключ fields, который в свою очередь содержит список пар «имя столбца — тип», включая Index или MultiIndex (см. список типов ниже). Поле schema также содержит поле primaryKey, если (мульти)индекс уникален.

Второе поле, data, содержит сериализованные данные с ориентацией records. Индекс включен, а все даты и времена форматируются в соответствии с ISO 8601, как требуется спецификацией Схемы таблицы.

Полный список поддерживаемых типов описан в спецификации Схемы таблицы. Эта таблица демонстрирует сопоставление с типами pandas:

Тип pandas

Тип Схемы таблицы

int64

целое число

float64

число

bool

логическое

datetime64[ns]

дата и время

timedelta64[ns]

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

categorical

любой

object

строка

Некоторые замечания по сгенерированной схеме таблицы:

  • Объект schema содержит поле pandas_version. Оно содержит версию диалекта pandas схемы и будет увеличиваться с каждым пересмотром.

  • Все даты преобразуются в UTC при сериализации. Даже значения с часовым поясом без указания часового пояса обрабатываются как UTC со смещением 0.

    In [293]: from pandas.io.json import build_table_schema
    
    In [294]: s = pd.Series(pd.date_range("2016", periods=4))
    
    In [295]: build_table_schema(s)
    Out[295]: 
    {'fields': [{'name': 'index', 'type': 'integer'},
      {'name': 'values', 'type': 'datetime'}],
     'primaryKey': ['index'],
     'pandas_version': '1.4.0'}
    
  • Даты и времена с часовым поясом (до сериализации) включают дополнительное поле tz с именем часового пояса (например, 'US/Central').

    In [296]: s_tz = pd.Series(pd.date_range("2016", periods=12, tz="US/Central"))
    
    In [297]: build_table_schema(s_tz)
    Out[297]: 
    {'fields': [{'name': 'index', 'type': 'integer'},
      {'name': 'values', 'type': 'datetime', 'tz': 'US/Central'}],
     'primaryKey': ['index'],
     'pandas_version': '1.4.0'}
    
  • Периоды преобразуются в отметки времени до сериализации и поэтому ведут себя аналогично, преобразуясь в UTC. Кроме того, периоды будут содержать дополнительное поле freq с частотой периода, например, 'A-DEC'.

    In [298]: s_per = pd.Series(1, index=pd.period_range("2016", freq="A-DEC", periods=4))
    
    In [299]: build_table_schema(s_per)
    Out[299]: 
    {'fields': [{'name': 'index', 'type': 'datetime', 'freq': 'A-DEC'},
      {'name': 'values', 'type': 'integer'}],
     'primaryKey': ['index'],
     'pandas_version': '1.4.0'}
    
  • Категории используют тип any и ограничение enum, перечисляющее множество возможных значений. Кроме того, включено поле ordered:

    In [300]: s_cat = pd.Series(pd.Categorical(["a", "b", "a"]))
    
    In [301]: build_table_schema(s_cat)
    Out[301]: 
    {'fields': [{'name': 'index', 'type': 'integer'},
      {'name': 'values',
       'type': 'any',
       'constraints': {'enum': ['a', 'b']},
       'ordered': False}],
     'primaryKey': ['index'],
     'pandas_version': '1.4.0'}
    
  • Поле primaryKey, содержащее массив меток, включается если индекс уникален:

    In [302]: s_dupe = pd.Series([1, 2], index=[1, 1])
    
    In [303]: build_table_schema(s_dupe)
    Out[303]: 
    {'fields': [{'name': 'index', 'type': 'integer'},
      {'name': 'values', 'type': 'integer'}],
     'pandas_version': '1.4.0'}
    
  • Поведение primaryKey аналогично с MultiIndex, но в этом случае primaryKey является массивом:

    In [304]: s_multi = pd.Series(1, index=pd.MultiIndex.from_product([("a", "b"), (0, 1)]))
    
    In [305]: build_table_schema(s_multi)
    Out[305]: 
    {'fields': [{'name': 'level_0', 'type': 'string'},
      {'name': 'level_1', 'type': 'integer'},
      {'name': 'values', 'type': 'integer'}],
     'primaryKey': FrozenList(['level_0', 'level_1']),
     'pandas_version': '1.4.0'}
    
  • Стандартные правила именования:

    • Для рядов используется object.name. Если он отсутствует, то используется имя values

    • Для столбцов используется строковое представление имени столбца

    • Для DataFrames, используется строковое представление имени столбца

    • Для Index (не MultiIndex), используется index.name, а в качестве резервного варианта используется index, если оно равно None.

    • Для MultiIndex, используется mi.names. Если у любого уровня нет имени, то используется level_<i>.

read_json также принимает orient='table' в качестве аргумента. Это позволяет сохранить метаданные, такие как dtypes и имена индексов, в обратимом формате.

In [306]: df = pd.DataFrame(
   .....:     {
   .....:         "foo": [1, 2, 3, 4],
   .....:         "bar": ["a", "b", "c", "d"],
   .....:         "baz": pd.date_range("2018-01-01", freq="d", periods=4),
   .....:         "qux": pd.Categorical(["a", "b", "c", "c"]),
   .....:     },
   .....:     index=pd.Index(range(4), name="idx"),
   .....: )
   .....: 

In [307]: df
Out[307]: 
     foo bar        baz qux
idx                        
0      1   a 2018-01-01   a
1      2   b 2018-01-02   b
2      3   c 2018-01-03   c
3      4   d 2018-01-04   c

In [308]: df.dtypes
Out[308]: 
foo             int64
bar            object
baz    datetime64[ns]
qux          category
dtype: object

In [309]: df.to_json("test.json", orient="table")

In [310]: new_df = pd.read_json("test.json", orient="table")

In [311]: new_df
Out[311]: 
     foo bar        baz qux
idx                        
0      1   a 2018-01-01   a
1      2   b 2018-01-02   b
2      3   c 2018-01-03   c
3      4   d 2018-01-04   c

In [312]: new_df.dtypes
Out[312]: 
foo             int64
bar            object
baz    datetime64[ns]
qux          category
dtype: object

Обратите внимание, что строка «index» в качестве имени Index не является обратимой, также как и любые имена, начинающиеся с 'level_' в MultiIndex. По умолчанию они используются в DataFrame.to_json() для обозначения пропущенных значений, и последующее чтение не может отличить намерение.

In [313]: df.index.name = "index"

In [314]: df.to_json("test.json", orient="table")

In [315]: new_df = pd.read_json("test.json", orient="table")

In [316]: print(new_df.index.name)
None

При использовании orient='table' вместе с пользовательскими ExtensionArray, сгенерированная схема будет содержать дополнительный ключ extDtype в соответствующем элементе fields. Этот дополнительный ключ не является стандартным, но он позволяет выполнять обратные преобразования в JSON для типов расширений (например, read_json(df.to_json(orient="table"), orient="table")).

Ключ extDtype содержит имя расширения. Если вы правильно зарегистрировали ExtensionDtype, pandas будет использовать это имя для поиска в реестре и повторно преобразует сериализованные данные в ваш пользовательский тип.

HTML

Чтение содержимого HTML

Предупреждение

Мы настоятельно рекомендуем прочитать Примечания к разбору таблиц HTML ниже, касающиеся проблем с парсерами BeautifulSoup4/html5lib/lxml.

Функция верхнего уровня read_html() может принимать строку/файл/URL HTML и парсить HTML-таблицы в список pandas DataFrames. Давайте рассмотрим несколько примеров.

Примечание

read_html возвращает list объектов DataFrame, даже если в содержимом HTML содержится только одна таблица.

Чтение URL без опций:

In [320]: "https://www.fdic.gov/resources/resolutions/bank-failures/failed-bank-list"
In [321]: pd.read_html(url)
Out[321]:
[                         Bank NameBank           CityCity StateSt  ...              Acquiring InstitutionAI Closing DateClosing FundFund
 0                    Almena State Bank             Almena      KS  ...                          Equity Bank    October 23, 2020    10538
 1           First City Bank of Florida  Fort Walton Beach      FL  ...            United Fidelity Bank, fsb    October 16, 2020    10537
 2                 The First State Bank      Barboursville      WV  ...                       MVB Bank, Inc.       April 3, 2020    10536
 3                   Ericson State Bank            Ericson      NE  ...           Farmers and Merchants Bank   February 14, 2020    10535
 4     City National Bank of New Jersey             Newark      NJ  ...                      Industrial Bank    November 1, 2019    10534
 ..                                 ...                ...     ...  ...                                  ...                 ...      ...
 558                 Superior Bank, FSB           Hinsdale      IL  ...                Superior Federal, FSB       July 27, 2001     6004
 559                Malta National Bank              Malta      OH  ...                    North Valley Bank         May 3, 2001     4648
 560    First Alliance Bank & Trust Co.         Manchester      NH  ...  Southern New Hampshire Bank & Trust    February 2, 2001     4647
 561  National State Bank of Metropolis         Metropolis      IL  ...              Banterra Bank of Marion   December 14, 2000     4646
 562                   Bank of Honolulu           Honolulu      HI  ...                   Bank of the Orient    October 13, 2000     4645

 [563 rows x 7 columns]]

Примечание

Данные с указанного выше URL меняются каждую понедельник, поэтому полученные данные могут немного отличаться.

Чтение содержимого файла из указанного выше URL и передача его read_html в качестве строки:

In [317]: html_str = """
   .....:          <table>
   .....:              <tr>
   .....:                  <th>A</th>
   .....:                  <th colspan="1">B</th>
   .....:                  <th rowspan="1">C</th>
   .....:              </tr>
   .....:              <tr>
   .....:                  <td>a</td>
   .....:                  <td>b</td>
   .....:                  <td>c</td>
   .....:              </tr>
   .....:          </table>
   .....:      """
   .....: 

In [318]: with open("tmp.html", "w") as f:
   .....:     f.write(html_str)
   .....: 

In [319]: df = pd.read_html("tmp.html")

In [320]: df[0]
Out[320]: 
   A  B  C
0  a  b  c

Вы также можете передать экземпляр StringIO, если хотите:

In [321]: dfs = pd.read_html(StringIO(html_str))

In [322]: dfs[0]
Out[322]: 
   A  B  C
0  a  b  c

Примечание

Следующие примеры не выполняются IPython-оценщиком, поскольку большое количество функций, обращающихся к сети, замедляет построение документации. Если вы обнаружите ошибку или пример, который не выполняется, не стесняйтесь сообщать об этом на странице проблем pandas в GitHub pandas GitHub issues page.

Чтение URL и поиск таблицы, содержащей определенный текст:

match = "Metcalf Bank"
df_list = pd.read_html(url, match=match)

Укажите строку заголовка (по умолчанию <th> или <td> элементы, расположенные в <thead>, используются для формирования индекса столбцов, если в <thead> содержатся несколько строк, создаётся MultiIndex); если указана, строка заголовка берётся из данных минус проанализированные элементы заголовка (<th> элементы).

dfs = pd.read_html(url, header=0)

Укажите столбец индекса:

dfs = pd.read_html(url, index_col=0)

Укажите количество строк для пропуска:

dfs = pd.read_html(url, skiprows=0)

Укажите количество строк для пропуска, используя список (range также работает):

dfs = pd.read_html(url, skiprows=range(2))

Укажите атрибут HTML:

dfs1 = pd.read_html(url, attrs={"id": "table"})
dfs2 = pd.read_html(url, attrs={"class": "sortable"})
print(np.array_equal(dfs1[0], dfs2[0]))  # Should be True

Укажите значения, которые должны быть преобразованы в NaN:

dfs = pd.read_html(url, na_values=["No Acquirer"])

Укажите, следует ли сохранить набор значений NaN по умолчанию:

dfs = pd.read_html(url, keep_default_na=False)

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

url_mcc = "https://en.wikipedia.org/wiki/Mobile_country_code"
dfs = pd.read_html(
    url_mcc,
    match="Telekom Albania",
    header=0,
    converters={"MNC": str},
)

Используйте некоторую комбинацию вышеперечисленного:

dfs = pd.read_html(url, match="Metcalf Bank", index_col=0)

Чтение выходных данных pandas to_html (с некоторыми потерями точности чисел с плавающей запятой):

df = pd.DataFrame(np.random.randn(2, 2))
s = df.to_html(float_format="{0:.40g}".format)
dfin = pd.read_html(s, index_col=0)

Бэкенд lxml вызовет ошибку при неудачном анализе, если это единственный парсер, который вы предоставляете. Если у вас только один парсер, вы можете передать просто строку, но считается хорошей практикой передавать список с одной строкой, если, например, функция ожидает последовательность строк. Вы можете использовать:

dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml"])

Или вы могли бы передать flavor='lxml' без списка:

dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor="lxml")

Однако, если у вас установлены bs4 и html5lib и вы передадите None или ['lxml', 'bs4'], то анализ, скорее всего, пройдет успешно. Обратите внимание, что как только анализ пройдёт успешно, функция вернёт значение.

dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml", "bs4"])

Ссылки могут быть извлечены из ячеек вместе с текстом, используя extract_links="all".

In [323]: html_table = """
   .....: <table>
   .....:   <tr>
   .....:     <th>GitHub</th>
   .....:   </tr>
   .....:   <tr>
   .....:     <td><a href="https://github.com/pandas-dev/pandas">pandas</a></td>
   .....:   </tr>
   .....: </table>
   .....: """
   .....: 

In [324]: df = pd.read_html(
   .....:     html_table,
   .....:     extract_links="all"
   .....: )[0]
   .....: 

In [325]: df
Out[325]: 
                                   (GitHub, None)
0  (pandas, https://github.com/pandas-dev/pandas)

In [326]: df[("GitHub", None)]
Out[326]: 
0    (pandas, https://github.com/pandas-dev/pandas)
Name: (GitHub, None), dtype: object

In [327]: df[("GitHub", None)].str[1]
Out[327]: 
0    https://github.com/pandas-dev/pandas
Name: (GitHub, None), dtype: object

Новое в версии 1.5.0.

Запись в файлы HTML

Объекты DataFrame имеют метод экземпляра to_html, который отображает содержимое DataFrame в виде HTML-таблицы. Аргументы функции такие же, как в методе to_string.

Примечание

Для краткости здесь не показаны все возможные параметры для DataFrame.to_html. См. to_html() для полного набора опций.

Примечание

В среде, поддерживающей отображение HTML, например, в Jupyter Notebook, display(HTML(...))` отобразит исходный HTML в этой среде.

In [328]: from IPython.display import display, HTML

In [329]: df = pd.DataFrame(np.random.randn(2, 2))

In [330]: df
Out[330]: 
          0         1
0  0.070319  1.773907
1  0.253908  0.414581

In [331]: html = df.to_html()

In [332]: print(html)  # raw html
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>0</th>
      <th>1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.070319</td>
      <td>1.773907</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.253908</td>
      <td>0.414581</td>
    </tr>
  </tbody>
</table>

In [333]: display(HTML(html))
<IPython.core.display.HTML object>

Аргумент columns ограничит отображаемые столбцы:

In [334]: html = df.to_html(columns=[0])

In [335]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>0</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.070319</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.253908</td>
    </tr>
  </tbody>
</table>

In [336]: display(HTML(html))
<IPython.core.display.HTML object>

float_format принимает вызываемый объект Python для управления точностью чисел с плавающей запятой:

In [337]: html = df.to_html(float_format="{0:.10f}".format)

In [338]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>0</th>
      <th>1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.0703192665</td>
      <td>1.7739074228</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.2539083433</td>
      <td>0.4145805920</td>
    </tr>
  </tbody>
</table>

In [339]: display(HTML(html))
<IPython.core.display.HTML object>

bold_rows сделает метки строк жирными по умолчанию, но вы можете это отключить:

In [340]: html = df.to_html(bold_rows=False)

In [341]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>0</th>
      <th>1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>0.070319</td>
      <td>1.773907</td>
    </tr>
    <tr>
      <td>1</td>
      <td>0.253908</td>
      <td>0.414581</td>
    </tr>
  </tbody>
</table>

In [342]: display(HTML(html))
<IPython.core.display.HTML object>

Аргумент classes предоставляет возможность присваивать результирующей HTML-таблице CSS-классы. Обратите внимание, что эти классы добавляются к существующему классу 'dataframe'.

In [343]: print(df.to_html(classes=["awesome_table_class", "even_more_awesome_class"]))
<table border="1" class="dataframe awesome_table_class even_more_awesome_class">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>0</th>
      <th>1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.070319</td>
      <td>1.773907</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.253908</td>
      <td>0.414581</td>
    </tr>
  </tbody>
</table>

Аргумент render_links предоставляет возможность добавлять гиперссылки в ячейки, содержащие URL.

In [344]: url_df = pd.DataFrame(
   .....:     {
   .....:         "name": ["Python", "pandas"],
   .....:         "url": ["https://www.python.org/", "https://pandas.pydata.org"],
   .....:     }
   .....: )
   .....: 

In [345]: html = url_df.to_html(render_links=True)

In [346]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>name</th>
      <th>url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Python</td>
      <td><a href="https://www.python.org/" target="_blank">https://www.python.org/</a></td>
    </tr>
    <tr>
      <th>1</th>
      <td>pandas</td>
      <td><a href="https://pandas.pydata.org" target="_blank">https://pandas.pydata.org</a></td>
    </tr>
  </tbody>
</table>

In [347]: display(HTML(html))
<IPython.core.display.HTML object>

Наконец, аргумент escape позволяет управлять тем, будут ли символы “<”, “>” и “&” экранированы в результирующем HTML (по умолчанию это True). Таким образом, чтобы получить HTML без экранированных символов, передайте escape=False

In [348]: df = pd.DataFrame({"a": list("&<>"), "b": np.random.randn(3)})

Экранированные:

In [349]: html = df.to_html()

In [350]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>&amp;</td>
      <td>0.842321</td>
    </tr>
    <tr>
      <th>1</th>
      <td>&lt;</td>
      <td>0.211337</td>
    </tr>
    <tr>
      <th>2</th>
      <td>&gt;</td>
      <td>-1.055427</td>
    </tr>
  </tbody>
</table>

In [351]: display(HTML(html))
<IPython.core.display.HTML object>

Не экранированные:

In [352]: html = df.to_html(escape=False)

In [353]: print(html)
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>&</td>
      <td>0.842321</td>
    </tr>
    <tr>
      <th>1</th>
      <td><</td>
      <td>0.211337</td>
    </tr>
    <tr>
      <th>2</th>
      <td>></td>
      <td>-1.055427</td>
    </tr>
  </tbody>
</table>

In [354]: display(HTML(html))
<IPython.core.display.HTML object>

Примечание

Некоторые браузеры могут не отображать разницу в представлении двух предыдущих HTML-таблиц.

Особенности разбора HTML-таблиц

Существуют некоторые проблемы с версионированием библиотек, используемых для разбора HTML-таблиц в функции pandas io верхнего уровня read_html.

Проблемы с lxml

  • Преимущества

    • lxml очень быстрый.

    • lxml требует корректной установки Cython.

  • Недостатки

    • lxml не гарантирует результаты своего разбора, если ему не предоставлен строго валидный разметка.

    • В свете вышесказанного, мы позволили вам, пользователю, использовать бэкенд lxml, но этот бэкенд будет использовать html5lib, если lxml не сможет обработать данные.

    • Поэтому настоятельно рекомендуется установить как BeautifulSoup4, так и html5lib, чтобы вы всё равно получали валидный результат (при условии, что всё остальное валидно), даже если lxml потерпит неудачу.

Проблемы с BeautifulSoup4 с использованием lxml в качестве бэкенда

  • Вышеупомянутые проблемы также актуальны здесь, поскольку BeautifulSoup4 по сути просто оболочка вокруг бэкенда парсера.

Проблемы с BeautifulSoup4 с использованием html5lib в качестве бэкенда

  • Преимущества

    • html5lib намного более лоялен, чем lxml, и, следовательно, работает с реальной разметкой намного разумнее, а не, например, просто удаляет элемент без уведомления.

    • html5lib автоматически генерирует валидную HTML5 разметку из невалидной. Это крайне важно для разбора HTML-таблиц, поскольку гарантирует валидный документ. Однако это не означает, что он «правильный», так как процесс исправления разметки не имеет единого определения.

    • html5lib является чистым Python и не требует дополнительных этапов сборки, помимо собственной установки.

  • Недостатки

    • Самый большой недостаток использования html5lib — его медлительность. Однако следует учесть, что многие таблицы в сети не настолько велики, чтобы время выполнения алгоритма разбора имело значение. Скорее всего, узким местом будет процесс чтения исходного текста из URL по сети, т. е. ввод-вывод. Для очень больших таблиц это может быть не так.

LaTeX

Новое в версии 1.3.0.

В настоящее время нет методов чтения из LaTeX, только методы вывода.

Запись в файлы LaTeX

Примечание

Объекты DataFrame и Styler в настоящее время имеют метод to_latex. Мы рекомендуем использовать метод Styler.to_latex() вместо DataFrame.to_latex() из-за большей гибкости первого с условным стилированием и возможного будущего устаревания второго.

Просмотрите документацию для Styler.to_latex, в которой приведены примеры условного форматирования и объяснено действие его ключевых аргументов.

Для простого применения достаточно следующего шаблона.

In [355]: df = pd.DataFrame([[1, 2], [3, 4]], index=["a", "b"], columns=["c", "d"])

In [356]: print(df.style.to_latex())
\begin{tabular}{lrr}
 & c & d \\
a & 1 & 2 \\
b & 3 & 4 \\
\end{tabular}

Чтобы отформатировать значения перед выводом, цепочкой вызовов метода Styler.format.

In [357]: print(df.style.format("€ {}").to_latex())
\begin{tabular}{lrr}
 & c & d \\
a & € 1 & € 2 \\
b & € 3 & € 4 \\
\end{tabular}

XML

Чтение XML

Новое в версии 1.3.0.

Функция верхнего уровня read_xml() может принимать строку/файл/URL XML и будет парсить узлы и атрибуты в пандас DataFrame.

Примечание

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

Давайте рассмотрим несколько примеров.

Чтение строки XML:

In [358]: xml = """<?xml version="1.0" encoding="UTF-8"?>
   .....: <bookstore>
   .....:   <book category="cooking">
   .....:     <title lang="en">Everyday Italian</title>
   .....:     <author>Giada De Laurentiis</author>
   .....:     <year>2005</year>
   .....:     <price>30.00</price>
   .....:   </book>
   .....:   <book category="children">
   .....:     <title lang="en">Harry Potter</title>
   .....:     <author>J K. Rowling</author>
   .....:     <year>2005</year>
   .....:     <price>29.99</price>
   .....:   </book>
   .....:   <book category="web">
   .....:     <title lang="en">Learning XML</title>
   .....:     <author>Erik T. Ray</author>
   .....:     <year>2003</year>
   .....:     <price>39.95</price>
   .....:   </book>
   .....: </bookstore>"""
   .....: 

In [359]: df = pd.read_xml(xml)

In [360]: df
Out[360]: 
   category             title               author  year  price
0   cooking  Everyday Italian  Giada De Laurentiis  2005  30.00
1  children      Harry Potter         J K. Rowling  2005  29.99
2       web      Learning XML          Erik T. Ray  2003  39.95

Чтение URL без опций:

In [361]: df = pd.read_xml("https://www.w3schools.com/xml/books.xml")

In [362]: df
Out[362]: 
   category              title                  author  year  price      cover
0   cooking   Everyday Italian     Giada De Laurentiis  2005  30.00       None
1  children       Harry Potter            J K. Rowling  2005  29.99       None
2       web  XQuery Kick Start  Vaidyanathan Nagarajan  2003  49.99       None
3       web       Learning XML             Erik T. Ray  2003  39.95  paperback

Чтение содержимого файла “books.xml” и передача его в read_xml в качестве строки:

In [363]: file_path = "books.xml"

In [364]: with open(file_path, "w") as f:
   .....:     f.write(xml)
   .....: 

In [365]: with open(file_path, "r") as f:
   .....:     df = pd.read_xml(f.read())
   .....: 

In [366]: df
Out[366]: 
   category             title               author  year  price
0   cooking  Everyday Italian  Giada De Laurentiis  2005  30.00
1  children      Harry Potter         J K. Rowling  2005  29.99
2       web      Learning XML          Erik T. Ray  2003  39.95

Чтение содержимого “books.xml” как экземпляра StringIO или BytesIO и передача его в read_xml:

In [367]: with open(file_path, "r") as f:
   .....:     sio = StringIO(f.read())
   .....: 

In [368]: df = pd.read_xml(sio)

In [369]: df
Out[369]: 
   category             title               author  year  price
0   cooking  Everyday Italian  Giada De Laurentiis  2005  30.00
1  children      Harry Potter         J K. Rowling  2005  29.99
2       web      Learning XML          Erik T. Ray  2003  39.95
In [370]: with open(file_path, "rb") as f:
   .....:     bio = BytesIO(f.read())
   .....: 

In [371]: df = pd.read_xml(bio)

In [372]: df
Out[372]: 
   category             title               author  year  price
0   cooking  Everyday Italian  Giada De Laurentiis  2005  30.00
1  children      Harry Potter         J K. Rowling  2005  29.99
2       web      Learning XML          Erik T. Ray  2003  39.95

Даже чтение XML из ведер AWS S3, таких как наборы данных статей NIH NCBI PMC, предоставляющие биомедицинские и научные журналы о жизни:

In [373]: df = pd.read_xml(
   .....:     "s3://pmc-oa-opendata/oa_comm/xml/all/PMC1236943.xml",
   .....:     xpath=".//journal-meta",
   .....: )
   .....: 

In [374]: df
Out[374]: 
              journal-id              journal-title       issn  publisher
0  Cardiovasc Ultrasound  Cardiovascular Ultrasound  1476-7120        NaN

С lxml по умолчанию parser, вы получаете доступ к полнофункциональной библиотеке XML, которая расширяет API ElementTree Python. Одним из мощных инструментов является возможность выборочного или условного запроса узлов с помощью более выразительного XPath:

In [375]: df = pd.read_xml(file_path, xpath="//book[year=2005]")

In [376]: df
Out[376]: 
   category             title               author  year  price
0   cooking  Everyday Italian  Giada De Laurentiis  2005  30.00
1  children      Harry Potter         J K. Rowling  2005  29.99

Укажите только элементы или только атрибуты для парсинга:

In [377]: df = pd.read_xml(file_path, elems_only=True)

In [378]: df
Out[378]: 
              title               author  year  price
0  Everyday Italian  Giada De Laurentiis  2005  30.00
1      Harry Potter         J K. Rowling  2005  29.99
2      Learning XML          Erik T. Ray  2003  39.95
In [379]: df = pd.read_xml(file_path, attrs_only=True)

In [380]: df
Out[380]: 
   category
0   cooking
1  children
2       web

Документы XML могут иметь пространства имен с префиксами и пространствами имен по умолчанию без префиксов, оба из которых обозначаются специальным атрибутом xmlns. Чтобы выполнить парсинг по узлу в контексте пространства имен, xpath должен ссылаться на префикс.

Например, XML ниже содержит пространство имен с префиксом, doc, и URI в https://example.com. Чтобы выполнить парсинг узлов doc:row, необходимо использовать namespaces.

In [381]: xml = """<?xml version='1.0' encoding='utf-8'?>
   .....: <doc:data xmlns:doc="https://example.com">
   .....:   <doc:row>
   .....:     <doc:shape>square</doc:shape>
   .....:     <doc:degrees>360</doc:degrees>
   .....:     <doc:sides>4.0</doc:sides>
   .....:   </doc:row>
   .....:   <doc:row>
   .....:     <doc:shape>circle</doc:shape>
   .....:     <doc:degrees>360</doc:degrees>
   .....:     <doc:sides/>
   .....:   </doc:row>
   .....:   <doc:row>
   .....:     <doc:shape>triangle</doc:shape>
   .....:     <doc:degrees>180</doc:degrees>
   .....:     <doc:sides>3.0</doc:sides>
   .....:   </doc:row>
   .....: </doc:data>"""
   .....: 

In [382]: df = pd.read_xml(xml,
   .....:                  xpath="//doc:row",
   .....:                  namespaces={"doc": "https://example.com"})
   .....: 

In [383]: df
Out[383]: 
      shape  degrees  sides
0    square      360    4.0
1    circle      360    NaN
2  triangle      180    3.0

Аналогично, документ XML может иметь пространство имен по умолчанию без префикса. Отсутствие присвоения временного префикса вернет нулевые узлы и приведет к ошибке ValueError. Но присвоение *любого* временного имени для правильного URI позволяет выполнить парсинг по узлам.

In [384]: xml = """<?xml version='1.0' encoding='utf-8'?>
   .....: <data xmlns="https://example.com">
   .....:  <row>
   .....:    <shape>square</shape>
   .....:    <degrees>360</degrees>
   .....:    <sides>4.0</sides>
   .....:  </row>
   .....:  <row>
   .....:    <shape>circle</shape>
   .....:    <degrees>360</degrees>
   .....:    <sides/>
   .....:  </row>
   .....:  <row>
   .....:    <shape>triangle</shape>
   .....:    <degrees>180</degrees>
   .....:    <sides>3.0</sides>
   .....:  </row>
   .....: </data>"""
   .....: 

In [385]: df = pd.read_xml(xml,
   .....:                  xpath="//pandas:row",
   .....:                  namespaces={"pandas": "https://example.com"})
   .....: 

In [386]: df
Out[386]: 
      shape  degrees  sides
0    square      360    4.0
1    circle      360    NaN
2  triangle      180    3.0

Однако, если XPath не ссылается на имена узлов, такие как по умолчанию, /*, то namespaces не требуется.

С lxml в качестве парсера вы можете сглаживать вложенные документы XML с помощью скрипта XSLT, который также может быть типами строки/файла/URL. В качестве справки, XSLT — это язык специального назначения, написанный в специальном файле XML, который может преобразовывать исходные документы XML в другие XML, HTML, даже текст (CSV, JSON и т. д.) с помощью процессора XSLT.

Например, рассмотрим эту несколько вложенную структуру поездов «L» Чикаго, где элементы станции и поезд содержат данные в собственных разделах. С помощью XSLT ниже, lxml может преобразовать исходный вложенный документ в более плоский вывод (как показано ниже для демонстрации) для более простого парсинга в DataFrame:

In [387]: xml = """<?xml version='1.0' encoding='utf-8'?>
   .....:  <response>
   .....:   <row>
   .....:     <station id="40850" name="Library"/>
   .....:     <month>2020-09-01T00:00:00</month>
   .....:     <rides>
   .....:       <avg_weekday_rides>864.2</avg_weekday_rides>
   .....:       <avg_saturday_rides>534</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides>
   .....:     </rides>
   .....:   </row>
   .....:   <row>
   .....:     <station id="41700" name="Washington/Wabash"/>
   .....:     <month>2020-09-01T00:00:00</month>
   .....:     <rides>
   .....:       <avg_weekday_rides>2707.4</avg_weekday_rides>
   .....:       <avg_saturday_rides>1909.8</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides>
   .....:     </rides>
   .....:   </row>
   .....:   <row>
   .....:     <station id="40380" name="Clark/Lake"/>
   .....:     <month>2020-09-01T00:00:00</month>
   .....:     <rides>
   .....:       <avg_weekday_rides>2949.6</avg_weekday_rides>
   .....:       <avg_saturday_rides>1657</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides>
   .....:     </rides>
   .....:   </row>
   .....:  </response>"""
   .....: 

In [388]: xsl = """<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   .....:    <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
   .....:    <xsl:strip-space elements="*"/>
   .....:    <xsl:template match="/response">
   .....:       <xsl:copy>
   .....:         <xsl:apply-templates select="row"/>
   .....:       </xsl:copy>
   .....:    </xsl:template>
   .....:    <xsl:template match="row">
   .....:       <xsl:copy>
   .....:         <station_id><xsl:value-of select="station/@id"/></station_id>
   .....:         <station_name><xsl:value-of select="station/@name"/></station_name>
   .....:         <xsl:copy-of select="month|rides/*"/>
   .....:       </xsl:copy>
   .....:    </xsl:template>
   .....:  </xsl:stylesheet>"""
   .....: 

In [389]: output = """<?xml version='1.0' encoding='utf-8'?>
   .....:  <response>
   .....:    <row>
   .....:       <station_id>40850</station_id>
   .....:       <station_name>Library</station_name>
   .....:       <month>2020-09-01T00:00:00</month>
   .....:       <avg_weekday_rides>864.2</avg_weekday_rides>
   .....:       <avg_saturday_rides>534</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides>
   .....:    </row>
   .....:    <row>
   .....:       <station_id>41700</station_id>
   .....:       <station_name>Washington/Wabash</station_name>
   .....:       <month>2020-09-01T00:00:00</month>
   .....:       <avg_weekday_rides>2707.4</avg_weekday_rides>
   .....:       <avg_saturday_rides>1909.8</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides>
   .....:    </row>
   .....:    <row>
   .....:       <station_id>40380</station_id>
   .....:       <station_name>Clark/Lake</station_name>
   .....:       <month>2020-09-01T00:00:00</month>
   .....:       <avg_weekday_rides>2949.6</avg_weekday_rides>
   .....:       <avg_saturday_rides>1657</avg_saturday_rides>
   .....:       <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides>
   .....:    </row>
   .....:  </response>"""
   .....: 

In [390]: df = pd.read_xml(xml, stylesheet=xsl)

In [391]: df
Out[391]: 
   station_id       station_name  ... avg_saturday_rides  avg_sunday_holiday_rides
0       40850            Library  ...              534.0                     417.2
1       41700  Washington/Wabash  ...             1909.8                    1438.6
2       40380         Clark/Lake  ...             1657.0                    1453.8

[3 rows x 6 columns]

Для очень больших файлов XML, которые могут иметь размер в сотни мегабайт до гигабайт, pandas.read_xml() поддерживает парсинг таких больших файлов с использованием lxml’s iterparse и etree’s iterparse, которые являются методами с низким потреблением памяти для итерации по дереву XML и извлечения определенных элементов и атрибутов без хранения всего дерева в памяти.

Новое в версии 1.5.0.

Чтобы использовать эту функцию, вы должны передать физический путь к файлу XML в read_xml и использовать аргумент iterparse. Файлы не должны быть сжаты или указывать на онлайн-источники, а храниться на локальном диске. Также, iterparse должен быть словарем, где ключ — это повторяющиеся узлы в документе (которые становятся строками), а значение — список любого элемента или атрибута, являющегося потомком (т. е. ребенком, внуком) повторяющегося узла. Поскольку XPath в этом методе не используется, потомки не обязательно должны иметь одинаковые отношения друг с другом. Ниже приведен пример чтения очень большого (более 12 ГБ) последнего выгруженного набора данных статей Википедии.

In [1]: df = pd.read_xml(
...         "/path/to/downloaded/enwikisource-latest-pages-articles.xml",
...         iterparse = {"page": ["title", "ns", "id"]}
...     )
...     df
Out[2]:
                                                     title   ns        id
0                                       Gettysburg Address    0     21450
1                                                Main Page    0     42950
2                            Declaration by United Nations    0      8435
3             Constitution of the United States of America    0      8435
4                     Declaration of Independence (Israel)    0     17858
...                                                    ...  ...       ...
3578760               Page:Black cat 1897 07 v2 n10.pdf/17  104    219649
3578761               Page:Black cat 1897 07 v2 n10.pdf/43  104    219649
3578762               Page:Black cat 1897 07 v2 n10.pdf/44  104    219649
3578763      The History of Tom Jones, a Foundling/Book IX    0  12084291
3578764  Page:Shakespeare of Stratford (1926) Yale.djvu/91  104     21450

[3578765 rows x 3 columns]

Запись XML

Новое в версии 1.3.0.

Объекты DataFrame имеют метод экземпляра to_xml, который отображает содержимое DataFrame как документ XML.

Примечание

Этот метод не поддерживает специальные свойства XML, включая DTD, CData, схемы XSD, инструкции обработки, комментарии и другие. Поддерживаются только пространства имен на уровне корня. Однако, stylesheet позволяет вносить изменения в дизайн после начального вывода.

Давайте рассмотрим несколько примеров.

Запись XML без опций:

In [392]: geom_df = pd.DataFrame(
   .....:     {
   .....:         "shape": ["square", "circle", "triangle"],
   .....:         "degrees": [360, 360, 180],
   .....:         "sides": [4, np.nan, 3],
   .....:     }
   .....: )
   .....: 

In [393]: print(geom_df.to_xml())
<?xml version='1.0' encoding='utf-8'?>
<data>
  <row>
    <index>0</index>
    <shape>square</shape>
    <degrees>360</degrees>
    <sides>4.0</sides>
  </row>
  <row>
    <index>1</index>
    <shape>circle</shape>
    <degrees>360</degrees>
    <sides/>
  </row>
  <row>
    <index>2</index>
    <shape>triangle</shape>
    <degrees>180</degrees>
    <sides>3.0</sides>
  </row>
</data>

Запись XML с новым корнем и именем строки:

In [394]: print(geom_df.to_xml(root_name="geometry", row_name="objects"))
<?xml version='1.0' encoding='utf-8'?>
<geometry>
  <objects>
    <index>0</index>
    <shape>square</shape>
    <degrees>360</degrees>
    <sides>4.0</sides>
  </objects>
  <objects>
    <index>1</index>
    <shape>circle</shape>
    <degrees>360</degrees>
    <sides/>
  </objects>
  <objects>
    <index>2</index>
    <shape>triangle</shape>
    <degrees>180</degrees>
    <sides>3.0</sides>
  </objects>
</geometry>

Запись XML, ориентированная на атрибуты:

In [395]: print(geom_df.to_xml(attr_cols=geom_df.columns.tolist()))
<?xml version='1.0' encoding='utf-8'?>
<data>
  <row index="0" shape="square" degrees="360" sides="4.0"/>
  <row index="1" shape="circle" degrees="360"/>
  <row index="2" shape="triangle" degrees="180" sides="3.0"/>
</data>

Запись смеси элементов и атрибутов:

In [396]: print(
   .....:     geom_df.to_xml(
   .....:         index=False,
   .....:         attr_cols=['shape'],
   .....:         elem_cols=['degrees', 'sides'])
   .....: )
   .....: 
<?xml version='1.0' encoding='utf-8'?>
<data>
  <row shape="square">
    <degrees>360</degrees>
    <sides>4.0</sides>
  </row>
  <row shape="circle">
    <degrees>360</degrees>
    <sides/>
  </row>
  <row shape="triangle">
    <degrees>180</degrees>
    <sides>3.0</sides>
  </row>
</data>

Любой DataFrames с иерархическими столбцами будет сглажен для имен элементов XML с уровнями, разделенными подчеркиванием:

In [397]: ext_geom_df = pd.DataFrame(
   .....:     {
   .....:         "type": ["polygon", "other", "polygon"],
   .....:         "shape": ["square", "circle", "triangle"],
   .....:         "degrees": [360, 360, 180],
   .....:         "sides": [4, np.nan, 3],
   .....:     }
   .....: )
   .....: 

In [398]: pvt_df = ext_geom_df.pivot_table(index='shape',
   .....:                                  columns='type',
   .....:                                  values=['degrees', 'sides'],
   .....:                                  aggfunc='sum')
   .....: 

In [399]: pvt_df
Out[399]: 
         degrees         sides        
type       other polygon other polygon
shape                                 
circle     360.0     NaN   0.0     NaN
square       NaN   360.0   NaN     4.0
triangle     NaN   180.0   NaN     3.0

In [400]: print(pvt_df.to_xml())
<?xml version='1.0' encoding='utf-8'?>
<data>
  <row>
    <shape>circle</shape>
    <degrees_other>360.0</degrees_other>
    <degrees_polygon/>
    <sides_other>0.0</sides_other>
    <sides_polygon/>
  </row>
  <row>
    <shape>square</shape>
    <degrees_other/>
    <degrees_polygon>360.0</degrees_polygon>
    <sides_other/>
    <sides_polygon>4.0</sides_polygon>
  </row>
  <row>
    <shape>triangle</shape>
    <degrees_other/>
    <degrees_polygon>180.0</degrees_polygon>
    <sides_other/>
    <sides_polygon>3.0</sides_polygon>
  </row>
</data>

Запись XML с пространством имен по умолчанию:

In [401]: print(geom_df.to_xml(namespaces={"": "https://example.com"}))
<?xml version='1.0' encoding='utf-8'?>
<data xmlns="https://example.com">
  <row>
    <index>0</index>
    <shape>square</shape>
    <degrees>360</degrees>
    <sides>4.0</sides>
  </row>
  <row>
    <index>1</index>
    <shape>circle</shape>
    <degrees>360</degrees>
    <sides/>
  </row>
  <row>
    <index>2</index>
    <shape>triangle</shape>
    <degrees>180</degrees>
    <sides>3.0</sides>
  </row>
</data>

Запись XML с префиксом пространства имен:

In [402]: print(
   .....:     geom_df.to_xml(namespaces={"doc": "https://example.com"},
   .....:                    prefix="doc")
   .....: )
   .....: 
<?xml version='1.0' encoding='utf-8'?>
<doc:data xmlns:doc="https://example.com">
  <doc:row>
    <doc:index>0</doc:index>
    <doc:shape>square</doc:shape>
    <doc:degrees>360</doc:degrees>
    <doc:sides>4.0</doc:sides>
  </doc:row>
  <doc:row>
    <doc:index>1</doc:index>
    <doc:shape>circle</doc:shape>
    <doc:degrees>360</doc:degrees>
    <doc:sides/>
  </doc:row>
  <doc:row>
    <doc:index>2</doc:index>
    <doc:shape>triangle</doc:shape>
    <doc:degrees>180</doc:degrees>
    <doc:sides>3.0</doc:sides>
  </doc:row>
</doc:data>

Запись XML без объявления или красивой печати:

In [403]: print(
   .....:     geom_df.to_xml(xml_declaration=False,
   .....:                    pretty_print=False)
   .....: )
   .....: 
<data><row><index>0</index><shape>square</shape><degrees>360</degrees><sides>4.0</sides></row><row><index>1</index><shape>circle</shape><degrees>360</degrees><sides/></row><row><index>2</index><shape>triangle</shape><degrees>180</degrees><sides>3.0</sides></row></data>

Запись XML и преобразование со стилем:

In [404]: xsl = """<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   .....:    <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
   .....:    <xsl:strip-space elements="*"/>
   .....:    <xsl:template match="/data">
   .....:      <geometry>
   .....:        <xsl:apply-templates select="row"/>
   .....:      </geometry>
   .....:    </xsl:template>
   .....:    <xsl:template match="row">
   .....:      <object index="{index}">
   .....:        <xsl:if test="shape!='circle'">
   .....:            <xsl:attribute name="type">polygon</xsl:attribute>
   .....:        </xsl:if>
   .....:        <xsl:copy-of select="shape"/>
   .....:        <property>
   .....:            <xsl:copy-of select="degrees|sides"/>
   .....:        </property>
   .....:      </object>
   .....:    </xsl:template>
   .....:  </xsl:stylesheet>"""
   .....: 

In [405]: print(geom_df.to_xml(stylesheet=xsl))
<?xml version="1.0"?>
<geometry>
  <object index="0" type="polygon">
    <shape>square</shape>
    <property>
      <degrees>360</degrees>
      <sides>4.0</sides>
    </property>
  </object>
  <object index="1">
    <shape>circle</shape>
    <property>
      <degrees>360</degrees>
      <sides/>
    </property>
  </object>
  <object index="2" type="polygon">
    <shape>triangle</shape>
    <property>
      <degrees>180</degrees>
      <sides>3.0</sides>
    </property>
  </object>
</geometry>

Заключительные заметки по XML

  • Все документы XML соответствуют спецификациям W3C. Парсеры как etree, так и lxml не смогут обработать какой-либо документ разметки, который не является правильно сформированным или не соответствует правилам синтаксиса XML. Имейте в виду, что HTML — это не документ XML, если он не соответствует спецификациям XHTML. Тем не менее, другие популярные типы разметки, включая KML, XAML, RSS, MusicML, MathML, соответствуют схемам XML.

  • По этой причине, если ваше приложение создает XML до операций pandas, используйте соответствующие библиотеки DOM, такие как etree и lxml, для построения необходимого документа, а не путем конкатенации строк или корректировки регулярных выражений. Всегда помните, что XML — это *специальный* текстовый файл с правилами разметки.

  • Для очень больших файлов XML (несколько сотен МБ до ГБ) XPath и XSLT могут стать ресурсоемкими операциями. Убедитесь, что у вас достаточно оперативной памяти для чтения и записи больших файлов XML (примерно в 5 раз больше, чем размер текста).

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

  • Парсер etree поддерживает все функции как read_xml, так и to_xml, за исключением сложных XPath и любых XSLT. Несмотря на ограниченные возможности, etree по-прежнему является надежным и способным парсером и создателем дерева. Его производительность может немного уступать lxml для больших файлов, но это практически незаметно для файлов небольшого и среднего размера.

Файлы Excel

Метод read_excel() может считывать файлы Excel 2007+ (.xlsx) с помощью модуля Python openpyxl. Файлы Excel 2003 (.xls) можно считывать с помощью xlrd. Бинарные файлы Excel (.xlsb) можно считывать с помощью pyxlsb. Метод экземпляра to_excel() используется для сохранения DataFrame в Excel. В целом, семантика аналогична работе с данными csv. См. раздел пособие для ознакомления с некоторыми продвинутыми стратегиями.

Предупреждение

Пакет xlwt для записи файлов Excel старого образца .xls больше не поддерживается. Пакет xlrd теперь используется только для чтения файлов Excel старого образца .xls.

До pandas 1.3.0, аргумент по умолчанию engine=None метода read_excel() в большинстве случаев приводил к использованию движка xlrd, включая новые файлы Excel 2007+ (.xlsx). Сейчас pandas по умолчанию использует движок openpyxl.

Настоятельно рекомендуется установить openpyxl для чтения файлов Excel 2007+ (.xlsx). Пожалуйста, не сообщайте об ошибках, возникающих при использовании ``xlrd`` для чтения файлов ``.xlsx``. Это больше не поддерживается, используйте вместо этого openpyxl.

Попытка использовать движок xlwt приведет к ошибке FutureWarning, если опция io.excel.xls.writer не установлена в "xlwt". Хотя эта опция теперь устарела и также вызовет ошибку FutureWarning, она может быть глобально установлена, и предупреждение подавлено. Пользователям рекомендуется писать файлы .xlsx с помощью движка openpyxl.

Чтение файлов Excel

В самом простом случае read_excel принимает путь к файлу Excel и sheet_name , указывающий, какой лист нужно проанализировать.

# Returns a DataFrame
pd.read_excel("path_to_file.xls", sheet_name="Sheet1")

ExcelFile класс

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

xlsx = pd.ExcelFile("path_to_file.xls")
df = pd.read_excel(xlsx, "Sheet1")

Класс ExcelFile также можно использовать в качестве контекстного менеджера.

with pd.ExcelFile("path_to_file.xls") as xls:
    df1 = pd.read_excel(xls, "Sheet1")
    df2 = pd.read_excel(xls, "Sheet2")

Свойство sheet_names сгенерирует список имён листов в файле.

Основной сценарий использования класса ExcelFile — это разбор нескольких листов с различными параметрами:

data = {}
# For when Sheet1's format differs from Sheet2
with pd.ExcelFile("path_to_file.xls") as xls:
    data["Sheet1"] = pd.read_excel(xls, "Sheet1", index_col=None, na_values=["NA"])
    data["Sheet2"] = pd.read_excel(xls, "Sheet2", index_col=1)

Обратите внимание, что если для всех листов используются одинаковые параметры разбора, список имён листов можно просто передать в read_excel без потери производительности.

# using the ExcelFile class
data = {}
with pd.ExcelFile("path_to_file.xls") as xls:
    data["Sheet1"] = pd.read_excel(xls, "Sheet1", index_col=None, na_values=["NA"])
    data["Sheet2"] = pd.read_excel(xls, "Sheet2", index_col=None, na_values=["NA"])

# equivalent using the read_excel function
data = pd.read_excel(
    "path_to_file.xls", ["Sheet1", "Sheet2"], index_col=None, na_values=["NA"]
)

ExcelFile также можно вызвать с объектом xlrd.book.Book в качестве параметра. Это позволяет пользователю контролировать способ чтения файла Excel. Например, листы можно загружать по требованию, вызывая xlrd.open_workbook() с on_demand=True.

import xlrd

xlrd_book = xlrd.open_workbook("path_to_file.xls", on_demand=True)
with pd.ExcelFile(xlrd_book) as xls:
    df1 = pd.read_excel(xls, "Sheet1")
    df2 = pd.read_excel(xls, "Sheet2")

Указание листов

Примечание

Второй аргумент — sheet_name, а не ExcelFile.sheet_names.

Примечание

Атрибут ExcelFile sheet_names предоставляет доступ к списку листов.

  • Аргументы sheet_name позволяют указать лист или листы для чтения.

  • Значение по умолчанию для sheet_name равно 0, что указывает на чтение первого листа.

  • Передайте строку, чтобы сослаться на имя конкретного листа в книге.

  • Передайте целое число, чтобы сослаться на индекс листа. Индексы следуют соглашениям Python, начиная с 0.

  • Передайте список строк или целых чисел, чтобы вернуть словарь указанных листов.

  • Передайте None , чтобы вернуть словарь всех доступных листов.

# Returns a DataFrame
pd.read_excel("path_to_file.xls", "Sheet1", index_col=None, na_values=["NA"])

Использование индекса листа:

# Returns a DataFrame
pd.read_excel("path_to_file.xls", 0, index_col=None, na_values=["NA"])

Использование всех значений по умолчанию:

# Returns a DataFrame
pd.read_excel("path_to_file.xls")

Использование None для получения всех листов:

# Returns a dictionary of DataFrames
pd.read_excel("path_to_file.xls", sheet_name=None)

Использование списка для получения нескольких листов:

# Returns the 1st and 4th sheet, as a dictionary of DataFrames.
pd.read_excel("path_to_file.xls", sheet_name=["Sheet1", 3])

read_excel может читать более одного листа, задавая sheet_name список имён листов, список позиций листов или None для чтения всех листов. Листы можно указывать по индексу листа или имени листа, используя целое число или строку соответственно.

Чтение многоуровневого индекса

read_excel может читать многоуровневый индекс, передавая список столбцов в index_col и список строк в header. Если у уровней многоуровневого индекса есть сериализованные имена уровней, они также будут считаны, указав строки/столбцы, составляющие уровни.

Например, для чтения многоуровневого индекса без имён:

In [406]: df = pd.DataFrame(
   .....:     {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]},
   .....:     index=pd.MultiIndex.from_product([["a", "b"], ["c", "d"]]),
   .....: )
   .....: 

In [407]: df.to_excel("path_to_file.xlsx")

In [408]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])

In [409]: df
Out[409]: 
     a  b
a c  1  5
  d  2  6
b c  3  7
  d  4  8

Если у индекса есть имена уровней, они также будут проанализированы, используя те же параметры.

In [410]: df.index = df.index.set_names(["lvl1", "lvl2"])

In [411]: df.to_excel("path_to_file.xlsx")

In [412]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])

In [413]: df
Out[413]: 
           a  b
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

Если исходный файл содержит многоуровневый индекс и столбцы, списки, определяющие каждый, должны быть переданы в index_col и header:

In [414]: df.columns = pd.MultiIndex.from_product([["a"], ["b", "d"]], names=["c1", "c2"])

In [415]: df.to_excel("path_to_file.xlsx")

In [416]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1], header=[0, 1])

In [417]: df
Out[417]: 
c1         a   
c2         b  d
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

Пропущенные значения в столбцах, указанных в index_col, будут заполнены вперёд, чтобы разрешить обратную загрузку с to_excel для merged_cells=True. Чтобы избежать заполнения пропущенных значений вперёд, используйте set_index после чтения данных вместо index_col.

Разбор определённых столбцов

Часто пользователи вставляют столбцы для временных вычислений в Excel, и вы можете не захотеть читать эти столбцы. read_excel принимает ключевое слово usecols , чтобы разрешить указание подмножества столбцов для разбора.

Изменено в версии 1.0.0.

Передача целого числа в usecols больше не будет работать. Пожалуйста, передайте вместо этого список целых чисел от 0 до usecols включительно.

Вы можете указать набор столбцов и диапазонов Excel через запятую в виде строки:

pd.read_excel("path_to_file.xls", "Sheet1", usecols="A,C:E")

Если usecols является списком целых чисел, предполагается, что это индексы столбцов файла для разбора.

pd.read_excel("path_to_file.xls", "Sheet1", usecols=[0, 2, 3])

Порядок элементов игнорируется, поэтому usecols=[0, 1] то же самое, что и [1, 0].

Если usecols является списком строк, предполагается, что каждая строка соответствует имени столбца, предоставленному пользователем в names или полученному из заголовков документа. Эти строки определяют, какие столбцы будут проанализированы.

pd.read_excel("path_to_file.xls", "Sheet1", usecols=["foo", "bar"])

Порядок элементов игнорируется, поэтому usecols=['baz', 'joe'] то же самое, что и ['joe', 'baz'].

Если usecols является вызываемым объектом, вызываемая функция будет оценена для имён столбцов, возвращая имена, где вызываемая функция оценивается как True.

pd.read_excel("path_to_file.xls", "Sheet1", usecols=lambda x: x.isalpha())

Разбор дат

Значения типа даты обычно автоматически преобразуются в соответствующий тип при чтении файла Excel. Но если у вас есть столбец строк, которые выглядят как даты (но на самом деле не отформатированы как даты в Excel), вы можете использовать ключевое слово parse_dates для разбора этих строк в значения типа datetime:

pd.read_excel("path_to_file.xls", "Sheet1", parse_dates=["date_strings"])

Преобразователи ячеек

Возможна трансформация содержимого ячеек Excel с помощью опции converters. Например, для преобразования столбца в булевы значения:

pd.read_excel("path_to_file.xls", "Sheet1", converters={"MyBools": bool})

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

def cfun(x):
    return int(x) if x else -1


pd.read_excel("path_to_file.xls", "Sheet1", converters={"MyInts": cfun})

Указание типов данных

В качестве альтернативы преобразователям, тип для целого столбца можно указать с помощью ключевого слова dtype , которое принимает словарь, сопоставляющий имена столбцов с типами. Для интерпретации данных без вывода типа используйте тип str или object.

pd.read_excel("path_to_file.xls", dtype={"MyInts": "int64", "MyText": str})

Запись файлов Excel

Запись файлов Excel на диск

Чтобы записать объект DataFrame в лист файла Excel, можно использовать метод экземпляра to_excel . Аргументы в значительной степени такие же, как и в to_csv , описанных выше, первым аргументом является имя файла Excel, а необязательным вторым аргументом — имя листа, в который следует записать DataFrame. Например:

df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")

Файлы с расширением .xls будут записаны с помощью xlwt, а файлы с расширением .xlsx будут записаны с помощью xlsxwriter (если доступно) или openpyxl.

DataFrame будет записан таким образом, чтобы попытаться имитировать вывод REPL. index_label будет помещён во вторую строку вместо первой. Вы можете поместить его в первую строку, установив опцию merge_cells в to_excel() на False:

df.to_excel("path_to_file.xlsx", index_label="label", merge_cells=False)

Чтобы записать отдельные DataFrames в отдельные листы одного файла Excel, можно передать ExcelWriter.

with pd.ExcelWriter("path_to_file.xlsx") as writer:
    df1.to_excel(writer, sheet_name="Sheet1")
    df2.to_excel(writer, sheet_name="Sheet2")

Запись файлов Excel в память

pandas поддерживает запись файлов Excel в объекты, похожие на буферы, такие как StringIO или BytesIO с помощью ExcelWriter.

from io import BytesIO

bio = BytesIO()

# By setting the 'engine' in the ExcelWriter constructor.
writer = pd.ExcelWriter(bio, engine="xlsxwriter")
df.to_excel(writer, sheet_name="Sheet1")

# Save the workbook
writer.save()

# Seek to the beginning and read to copy the workbook to a variable in memory
bio.seek(0)
workbook = bio.read()

Примечание

engine необязательно, но рекомендуется. Установка движка определяет версию создаваемой книги. Установка engine='xlrd' создаст книгу в формате Excel 2003 (xls). Использование 'openpyxl' или 'xlsxwriter' создаст книгу в формате Excel 2007 (xlsx). Если опущено, создается книга в формате Excel 2007.

Двигатели записи в Excel

Устарело начиная с версии 1.2.0: Так как пакет xlwt больше не поддерживается, движок xlwt будет удален из будущих версий pandas. Это единственный движок в pandas, который поддерживает запись в файлы .xls.

pandas выбирает движок записи в Excel двумя способами:

  1. ключевое слово engine

  2. расширение имени файла (через значение по умолчанию, указанное в параметрах конфигурации)

По умолчанию pandas использует XlsxWriter для .xlsx, openpyxl для .xlsm, и xlwt для .xls файлов. Если у вас установлено несколько движков, вы можете установить движок по умолчанию, изменив параметры конфигурации io.excel.xlsx.writer и io.excel.xls.writer. pandas будет использовать openpyxl для .xlsx файлов, если Xlsxwriter недоступен.

Чтобы указать, какой движок вы хотите использовать, вы можете передать ключевое слово engine в to_excel и ExcelWriter. Доступные встроенные движки:

  • openpyxl: требуется версия 2.4 или выше

  • xlsxwriter

  • xlwt

# By setting the 'engine' in the DataFrame 'to_excel()' methods.
df.to_excel("path_to_file.xlsx", sheet_name="Sheet1", engine="xlsxwriter")

# By setting the 'engine' in the ExcelWriter constructor.
writer = pd.ExcelWriter("path_to_file.xlsx", engine="xlsxwriter")

# Or via pandas configuration.
from pandas import options  # noqa: E402

options.io.excel.xlsx.writer = "xlsxwriter"

df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")

Стиль и форматирование

Внешний вид электронных таблиц Excel, созданных из pandas, можно изменить, используя следующие параметры в методе DataFrame’s to_excel.

  • float_format : Строка формата для чисел с плавающей точкой (по умолчанию None).

  • freeze_panes : Кортеж из двух целых чисел, представляющий последнюю строку и последний столбец для заморозки. Каждый из этих параметров индексируется с 1, поэтому (1, 1) заморозит первую строку и первый столбец (по умолчанию None).

Использование движка Xlsxwriter предоставляет множество опций для управления форматом электронной таблицы Excel, созданной методом to_excel. Отличные примеры можно найти в документации Xlsxwriter здесь: https://xlsxwriter.readthedocs.io/working_with_pandas.html

OpenDocument таблицы

Новое в версии 0.25.

Метод read_excel() также может читать таблицы OpenDocument, используя модуль odfpy. Семантика и возможности для чтения таблиц OpenDocument соответствуют тому, что можно сделать для файлов Excel с использованием engine='odf'.

# Returns a DataFrame
pd.read_excel("path_to_file.ods", engine="odf")

Примечание

В настоящее время pandas поддерживает только чтение таблиц OpenDocument. Запись не реализована.

Файлы двоичного формата Excel (.xlsb)

Новое в версии 1.0.0.

Метод read_excel() также может читать двоичные файлы Excel, используя модуль pyxlsb. Семантика и возможности для чтения двоичных файлов Excel в основном соответствуют тому, что можно сделать для файлов Excel, используя engine='pyxlsb'. pyxlsb не распознает типы дат в файлах и вместо этого вернет числа с плавающей точкой.

# Returns a DataFrame
pd.read_excel("path_to_file.xlsb", engine="pyxlsb")

Примечание

В настоящее время pandas поддерживает только чтение двоичных файлов Excel. Запись не реализована.

Буфер обмена

Удобный способ получить данные — использовать метод read_clipboard(), который получает содержимое буфера обмена и передает его методу read_csv. Например, вы можете скопировать следующий текст в буфер обмена (CTRL-C на многих операционных системах):

  A B C
x 1 4 p
y 2 5 q
z 3 6 r

Затем импортируйте данные непосредственно в DataFrame, вызвав:

>>> clipdf = pd.read_clipboard()
>>> clipdf
  A B C
x 1 4 p
y 2 5 q
z 3 6 r

Метод to_clipboard может использоваться для записи содержимого DataFrame в буфер обмена. После чего вы можете вставить содержимое буфера обмена в другие приложения (CTRL-V на многих операционных системах). Здесь мы продемонстрируем запись DataFrame в буфер обмена и чтение его обратно.

>>> df = pd.DataFrame(
...     {"A": [1, 2, 3], "B": [4, 5, 6], "C": ["p", "q", "r"]}, index=["x", "y", "z"]
... )

>>> df
  A B C
x 1 4 p
y 2 5 q
z 3 6 r
>>> df.to_clipboard()
>>> pd.read_clipboard()
  A B C
x 1 4 p
y 2 5 q
z 3 6 r

Мы видим, что получили тот же контент, который ранее записали в буфер обмена.

Примечание

Возможно, вам потребуется установить xclip или xsel (с PyQt5, PyQt4 или qtpy) в Linux, чтобы использовать эти методы.

Pickling

Все объекты pandas оснащены методами to_pickle, которые используют модуль Python cPickle для сохранения структур данных на диске в формате pickle.

In [418]: df
Out[418]: 
c1         a   
c2         b  d
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

In [419]: df.to_pickle("foo.pkl")

Функция read_pickle в пространстве имен pandas может использоваться для загрузки любого закодированного объекта pandas (или любого другого закодированного объекта) из файла:

In [420]: pd.read_pickle("foo.pkl")
Out[420]: 
c1         a   
c2         b  d
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

Предупреждение

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

См.: https://docs.python.org/3/library/pickle.html

Предупреждение

read_pickle() гарантирует обратную совместимость только до версии pandas 0.20.3

Сжатые файлы pickle

read_pickle(), DataFrame.to_pickle() и Series.to_pickle() могут читать и записывать сжатые файлы pickle. Поддерживаются типы сжатия gzip, bz2, xz, zstd для чтения и записи. Формат файла zip поддерживает только чтение и должен содержать только один файл данных для чтения.

Тип сжатия может быть явным параметром или определяться по расширению файла. Если ‘infer’, тогда используйте gzip, bz2, zip, xz, zstd если имя файла заканчивается на '.gz', '.bz2', '.zip', '.xz', или '.zst', соответственно.

Параметр сжатия также может быть dict для передачи опций протоколу сжатия. Он должен иметь ключ 'method', установленный в имя протокола сжатия, который должен быть одним из {'zip', 'gzip', 'bz2', 'xz', 'zstd'}. Все остальные пары ключ-значение передаются в подлежащую библиотеку сжатия.

In [421]: df = pd.DataFrame(
   .....:     {
   .....:         "A": np.random.randn(1000),
   .....:         "B": "foo",
   .....:         "C": pd.date_range("20130101", periods=1000, freq="s"),
   .....:     }
   .....: )
   .....: 

In [422]: df
Out[422]: 
            A    B                   C
0   -0.828876  foo 2013-01-01 00:00:00
1   -0.110383  foo 2013-01-01 00:00:01
2    2.357598  foo 2013-01-01 00:00:02
3   -1.620073  foo 2013-01-01 00:00:03
4    0.440903  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -1.177365  foo 2013-01-01 00:16:35
996  1.236988  foo 2013-01-01 00:16:36
997  0.743946  foo 2013-01-01 00:16:37
998 -0.533097  foo 2013-01-01 00:16:38
999 -0.140850  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

Использование явного типа сжатия:

In [423]: df.to_pickle("data.pkl.compress", compression="gzip")

In [424]: rt = pd.read_pickle("data.pkl.compress", compression="gzip")

In [425]: rt
Out[425]: 
            A    B                   C
0   -0.828876  foo 2013-01-01 00:00:00
1   -0.110383  foo 2013-01-01 00:00:01
2    2.357598  foo 2013-01-01 00:00:02
3   -1.620073  foo 2013-01-01 00:00:03
4    0.440903  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -1.177365  foo 2013-01-01 00:16:35
996  1.236988  foo 2013-01-01 00:16:36
997  0.743946  foo 2013-01-01 00:16:37
998 -0.533097  foo 2013-01-01 00:16:38
999 -0.140850  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

Определение типа сжатия по расширению:

In [426]: df.to_pickle("data.pkl.xz", compression="infer")

In [427]: rt = pd.read_pickle("data.pkl.xz", compression="infer")

In [428]: rt
Out[428]: 
            A    B                   C
0   -0.828876  foo 2013-01-01 00:00:00
1   -0.110383  foo 2013-01-01 00:00:01
2    2.357598  foo 2013-01-01 00:00:02
3   -1.620073  foo 2013-01-01 00:00:03
4    0.440903  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -1.177365  foo 2013-01-01 00:16:35
996  1.236988  foo 2013-01-01 00:16:36
997  0.743946  foo 2013-01-01 00:16:37
998 -0.533097  foo 2013-01-01 00:16:38
999 -0.140850  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

Значение по умолчанию — ‘infer’:

In [429]: df.to_pickle("data.pkl.gz")

In [430]: rt = pd.read_pickle("data.pkl.gz")

In [431]: rt
Out[431]: 
            A    B                   C
0   -0.828876  foo 2013-01-01 00:00:00
1   -0.110383  foo 2013-01-01 00:00:01
2    2.357598  foo 2013-01-01 00:00:02
3   -1.620073  foo 2013-01-01 00:00:03
4    0.440903  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -1.177365  foo 2013-01-01 00:16:35
996  1.236988  foo 2013-01-01 00:16:36
997  0.743946  foo 2013-01-01 00:16:37
998 -0.533097  foo 2013-01-01 00:16:38
999 -0.140850  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

In [432]: df["A"].to_pickle("s1.pkl.bz2")

In [433]: rt = pd.read_pickle("s1.pkl.bz2")

In [434]: rt
Out[434]: 
0     -0.828876
1     -0.110383
2      2.357598
3     -1.620073
4      0.440903
         ...   
995   -1.177365
996    1.236988
997    0.743946
998   -0.533097
999   -0.140850
Name: A, Length: 1000, dtype: float64

Передача опций протоколу сжатия для ускорения сжатия:

In [435]: df.to_pickle("data.pkl.gz", compression={"method": "gzip", "compresslevel": 1})

msgpack

Поддержка pandas для msgpack была удалена в версии 1.0.0. Рекомендуется использовать pickle вместо этого.

В качестве альтернативы, вы также можете использовать формат сериализации Arrow IPC для передачи по сети объектов pandas. Для получения документации по pyarrow, см. здесь.

END_OF_DOCUMENT_MARKER

HDF5 (PyTables)

HDFStore это объект, подобный словарю, который читает и записывает pandas в формате HDF5 с высокой производительностью, используя отличную библиотеку PyTables. См. справочник для некоторых продвинутых стратегий

Предупреждение

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

См.: https://docs.python.org/3/library/pickle.html для получения дополнительной информации.

In [436]: store = pd.HDFStore("store.h5")

In [437]: print(store)
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

Объекты могут быть записаны в файл, как добавление пар ключ-значение в словарь:

In [438]: index = pd.date_range("1/1/2000", periods=8)

In [439]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [440]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"])

# store.put('s', s) is an equivalent method
In [441]: store["s"] = s

In [442]: store["df"] = df

In [443]: store
Out[443]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

В текущей или последующей сессии Python вы можете получить сохранённые объекты:

# store.get('df') is an equivalent method
In [444]: store["df"]
Out[444]: 
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

# dotted (attribute) access provides get as well
In [445]: store.df
Out[445]: 
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

Удаление объекта, указанного ключом:

# store.remove('df') is an equivalent method
In [446]: del store["df"]

In [447]: store
Out[447]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

Закрытие хранилища и использование менеджера контекста:

In [448]: store.close()

In [449]: store
Out[449]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

In [450]: store.is_open
Out[450]: False

# Working with, and automatically closing the store using a context manager
In [451]: with pd.HDFStore("store.h5") as store:
   .....:     store.keys()
   .....: 

API чтения/записи

HDFStore поддерживает API верхнего уровня, используя read_hdf для чтения и to_hdf для записи, аналогично тому, как работают read_csv и to_csv.

In [452]: df_tl = pd.DataFrame({"A": list(range(5)), "B": list(range(5))})

In [453]: df_tl.to_hdf("store_tl.h5", "table", append=True)

In [454]: pd.read_hdf("store_tl.h5", "table", where=["index>2"])
Out[454]: 
   A  B
3  3  3
4  4  4

HDFStore по умолчанию не будет удалять строки, которые все отсутствуют. Это поведение можно изменить, установив dropna=True.

In [455]: df_with_missing = pd.DataFrame(
   .....:     {
   .....:         "col1": [0, np.nan, 2],
   .....:         "col2": [1, np.nan, np.nan],
   .....:     }
   .....: )
   .....: 

In [456]: df_with_missing
Out[456]: 
   col1  col2
0   0.0   1.0
1   NaN   NaN
2   2.0   NaN

In [457]: df_with_missing.to_hdf("file.h5", "df_with_missing", format="table", mode="w")

In [458]: pd.read_hdf("file.h5", "df_with_missing")
Out[458]: 
   col1  col2
0   0.0   1.0
1   NaN   NaN
2   2.0   NaN

In [459]: df_with_missing.to_hdf(
   .....:     "file.h5", "df_with_missing", format="table", mode="w", dropna=True
   .....: )
   .....: 

In [460]: pd.read_hdf("file.h5", "df_with_missing")
Out[460]: 
   col1  col2
0   0.0   1.0
2   2.0   NaN

Фиксированный формат

Приведённые выше примеры демонстрируют хранение с использованием put, которые записывают HDF5 в PyTables в формате фиксированного массива, называемом форматом fixed. Эти типы хранилищ не являются расширяемыми после записи (хотя вы можете их просто удалить и перезаписать). Они также не являются запросимыми; их необходимо извлекать целиком. Они также не поддерживают DataFrames с не уникальными именами столбцов. Формат fixed хранилища обеспечивает очень быструю запись и немного более быстрое чтение, чем хранилища table. Этот формат задаётся по умолчанию при использовании put или to_hdf или format='fixed' или format='f'.

Предупреждение

Формат fixed вызовет TypeError, если вы попытаетесь получить доступ с помощью where.

>>> pd.DataFrame(np.random.randn(10, 2)).to_hdf("test_fixed.h5", "df")
>>> pd.read_hdf("test_fixed.h5", "df", where="index>5")
TypeError: cannot pass a where specification when reading a fixed format.
           this store must be selected in its entirety

Формат таблицы

HDFStore поддерживает другой формат на диске, формат PyTables, формат table Концептуально table имеет форму, очень похожую на DataFrame, со строками и столбцами. К table можно добавлять данные в той же или другой сессии. Кроме того, поддерживаются операции удаления и запросов. Этот формат задаётся с помощью format='table' или format='t' для append или put или to_hdf.

Этот формат также может быть задан как опция pd.set_option('io.hdf.default_format','table') для включения put/append/to_hdf по умолчанию хранить в формате table.

In [461]: store = pd.HDFStore("store.h5")

In [462]: df1 = df[0:4]

In [463]: df2 = df[4:]

# append data (creates a table automatically)
In [464]: store.append("df", df1)

In [465]: store.append("df", df2)

In [466]: store
Out[466]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

# select the entire object
In [467]: store.select("df")
Out[467]: 
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

# the type of stored data
In [468]: store.root.df._v_attrs.pandas_type
Out[468]: 'frame_table'

Примечание

Вы также можете создать table, передав format='table' или format='t' в операцию put.

Иерархические ключи

Ключи к хранилищу могут быть заданы как строка. Они могут быть в формате иерархического пути (например, foo/bar/bah), который создаст иерархию подхранилищ (или Groups в терминологии PyTables). Ключи могут быть заданы без ведущего ‘/’ и всегда являются абсолютными (например, ‘foo’ относится к ‘/foo’). Операции удаления могут удалить всё в подхранилище и ниже, поэтому будьте осторожны.

In [469]: store.put("foo/bar/bah", df)

In [470]: store.append("food/orange", df)

In [471]: store.append("food/apple", df)

In [472]: store
Out[472]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

# a list of keys are returned
In [473]: store.keys()
Out[473]: ['/df', '/food/apple', '/food/orange', '/foo/bar/bah']

# remove all nodes under this level
In [474]: store.remove("food")

In [475]: store
Out[475]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

Вы можете проходить по иерархии групп, используя метод walk, который вернёт кортеж для каждого ключа группы вместе с относительными ключами её содержимого.

In [476]: for (path, subgroups, subkeys) in store.walk():
   .....:     for subgroup in subgroups:
   .....:         print("GROUP: {}/{}".format(path, subgroup))
   .....:     for subkey in subkeys:
   .....:         key = "/".join([path, subkey])
   .....:         print("KEY: {}".format(key))
   .....:         print(store.get(key))
   .....: 
GROUP: /foo
KEY: /df
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908
GROUP: /foo/bar
KEY: /foo/bar/bah
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

Предупреждение

Иерархические ключи не могут быть получены как доступ через точки (атрибут), как описано выше для элементов, сохранённых под узлом корня.

In [8]: store.foo.bar.bah
AttributeError: 'HDFStore' object has no attribute 'foo'

# you can directly access the actual PyTables node but using the root node
In [9]: store.root.foo.bar.bah
Out[9]:
/foo/bar/bah (Group) ''
  children := ['block0_items' (Array), 'block0_values' (Array), 'axis0' (Array), 'axis1' (Array)]

Вместо этого используйте явные ключи на основе строк:

In [477]: store["foo/bar/bah"]
Out[477]: 
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

Типы хранения

Хранение данных смешанных типов в таблице

Поддерживается хранение данных смешанного типа. Строки хранятся как фиксированной ширины, используя максимальный размер добавленного столбца. Попытки добавить более длинные строки приведут к ошибке ValueError.

Передача min_itemsize={`values`: size} в качестве параметра append установит больший минимум для столбцов строк. Хранение floats, strings, ints, bools, datetime64 в настоящее время поддерживается. Для столбцов строк передача nan_rep = 'nan' в append изменит представление nan на диске (которое преобразуется в/из np.nan), по умолчанию это nan.

In [478]: df_mixed = pd.DataFrame(
   .....:     {
   .....:         "A": np.random.randn(8),
   .....:         "B": np.random.randn(8),
   .....:         "C": np.array(np.random.randn(8), dtype="float32"),
   .....:         "string": "string",
   .....:         "int": 1,
   .....:         "bool": True,
   .....:         "datetime64": pd.Timestamp("20010102"),
   .....:     },
   .....:     index=list(range(8)),
   .....: )
   .....: 

In [479]: df_mixed.loc[df_mixed.index[3:5], ["A", "B", "string", "datetime64"]] = np.nan

In [480]: store.append("df_mixed", df_mixed, min_itemsize={"values": 50})

In [481]: df_mixed1 = store.select("df_mixed")

In [482]: df_mixed1
Out[482]: 
          A         B         C  string  int  bool datetime64
0  1.778161 -0.898283 -0.263043  string    1  True 2001-01-02
1 -0.913867 -0.218499 -0.639244  string    1  True 2001-01-02
2 -0.030004  1.408028 -0.866305  string    1  True 2001-01-02
3       NaN       NaN -0.225250     NaN    1  True        NaT
4       NaN       NaN -0.890978     NaN    1  True        NaT
5  0.081323  0.520995 -0.553839  string    1  True 2001-01-02
6 -0.268494  0.620028 -2.762875  string    1  True 2001-01-02
7  0.168016  0.159416 -1.244763  string    1  True 2001-01-02

In [483]: df_mixed1.dtypes.value_counts()
Out[483]: 
float64           2
float32           1
object            1
int64             1
bool              1
datetime64[ns]    1
dtype: int64

# we have provided a minimum string column size
In [484]: store.root.df_mixed.table
Out[484]: 
/df_mixed/table (Table(8,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Float64Col(shape=(2,), dflt=0.0, pos=1),
  "values_block_1": Float32Col(shape=(1,), dflt=0.0, pos=2),
  "values_block_2": StringCol(itemsize=50, shape=(1,), dflt=b'', pos=3),
  "values_block_3": Int64Col(shape=(1,), dflt=0, pos=4),
  "values_block_4": BoolCol(shape=(1,), dflt=False, pos=5),
  "values_block_5": Int64Col(shape=(1,), dflt=0, pos=6)}
  byteorder := 'little'
  chunkshape := (689,)
  autoindex := True
  colindexes := {
    "index": Index(6, mediumshuffle, zlib(1)).is_csi=False}

Хранение DataFrames с MultiIndex

Хранение MultiIndex DataFrames как таблиц очень похоже на хранение/выбор из однородного индекса DataFrames.

In [485]: index = pd.MultiIndex(
   .....:     levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]],
   .....:     codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]],
   .....:     names=["foo", "bar"],
   .....: )
   .....: 

In [486]: df_mi = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])

In [487]: df_mi
Out[487]: 
                  A         B         C
foo bar                                
foo one   -1.280289  0.692545 -0.536722
    two    1.005707  0.296917  0.139796
    three -1.083889  0.811865  1.648435
bar one   -0.164377 -0.402227  1.618922
    two   -1.424723 -0.023232  0.948196
baz two    0.183573  0.145277  0.308146
    three -1.043530 -0.708145  1.430905
qux one   -0.850136  0.813949  1.508891
    two   -1.556154  0.187597  1.176488
    three -1.246093 -0.002726 -0.444249

In [488]: store.append("df_mi", df_mi)

In [489]: store.select("df_mi")
Out[489]: 
                  A         B         C
foo bar                                
foo one   -1.280289  0.692545 -0.536722
    two    1.005707  0.296917  0.139796
    three -1.083889  0.811865  1.648435
bar one   -0.164377 -0.402227  1.618922
    two   -1.424723 -0.023232  0.948196
baz two    0.183573  0.145277  0.308146
    three -1.043530 -0.708145  1.430905
qux one   -0.850136  0.813949  1.508891
    two   -1.556154  0.187597  1.176488
    three -1.246093 -0.002726 -0.444249

# the levels are automatically included as data columns
In [490]: store.select("df_mi", "foo=bar")
Out[490]: 
                A         B         C
foo bar                              
bar one -0.164377 -0.402227  1.618922
    two -1.424723 -0.023232  0.948196

Примечание

Ключевое слово index зарезервировано и не может использоваться в качестве имени уровня.

Запросы

Запрос таблицы

select и delete операции имеют необязательный критерий, который может быть указан для выбора/удаления только подмножества данных. Это позволяет иметь очень большую таблицу на диске и извлекать только часть данных.

Запрос задаётся с использованием класса Term внутри, как булево выражение.

  • index и columns поддерживаются индексаторами DataFrames.

  • Если data_columns указаны, они могут использоваться в качестве дополнительных индексаторов.

  • имя уровня в MultiIndex, со значением по умолчанию level_0, level_1, … если не указано.

Действительные операторы сравнения:

=, ==, !=, >, >=, <, <=

Действительные булевы выражения объединяются с использованием:

  • | : или

  • & : и

  • ( и ) : для группировки

Эти правила аналогичны тому, как булевы выражения используются в pandas для индексирования.

Примечание

  • = будет автоматически расширен до оператора сравнения ==

  • ~ — оператор отрицания, но может использоваться только в очень ограниченных случаях

  • Если передаётся список/кортеж выражений, они будут объединены с использованием &

Следующие являются допустимыми выражениями:

  • 'index >= date'

  • "columns = ['A', 'D']"

  • "columns in ['A', 'D']"

  • 'columns = A'

  • 'columns == A'

  • "~(columns = ['A', 'B'])"

  • 'index > df.index[3] & string = "bar"'

  • '(index > df.index[3] & index <= df.index[6]) | string = "bar"'

  • "ts >= Timestamp('2012-02-01')"

  • "major_axis>=20130101"

indexers находятся слева от подвыражения:

columns, major_axis, ts

Правая часть подвыражения (после оператора сравнения) может быть:

  • функции, которые будут вычислены, например, Timestamp('2012-02-01')

  • строки, например, "bar"

  • данные типа даты, например, 20130101, или "20130101"

  • списки, например, "['A', 'B']"

  • переменные, определённые в локальном пространстве имён, например, date

Примечание

Не рекомендуется передавать строку в запрос путём интерполяции её в выражение запроса. Просто присвойте интересующую строку переменной и используйте эту переменную в выражении. Например, сделайте так

string = "HolyMoly'"
store.select("df", "index == string")

вместо этого

string = "HolyMoly'"
store.select('df', f'index == {string}')

Последний вариант не будет работать и вызовет SyntaxError. Обратите внимание, что в переменной string есть одинарная кавычка, за которой следует двойная.

Если вам необходимо выполнить интерполяцию, используйте спецификатор формата '%r'

store.select("df", "index == %r" % string)

который добавит кавычки к string.

Вот некоторые примеры:

In [491]: dfq = pd.DataFrame(
   .....:     np.random.randn(10, 4),
   .....:     columns=list("ABCD"),
   .....:     index=pd.date_range("20130101", periods=10),
   .....: )
   .....: 

In [492]: store.append("dfq", dfq, format="table", data_columns=True)

Используйте булевы выражения с вычислением функций в строке.

In [493]: store.select("dfq", "index>pd.Timestamp('20130104') & columns=['A', 'B']")
Out[493]: 
                   A         B
2013-01-05  1.366810  1.073372
2013-01-06  2.119746 -2.628174
2013-01-07  0.337920 -0.634027
2013-01-08  1.053434  1.109090
2013-01-09 -0.772942 -0.269415
2013-01-10  0.048562 -0.285920

Используйте встроенную ссылку на столбец.

In [494]: store.select("dfq", where="A>0 or C>0")
Out[494]: 
                   A         B         C         D
2013-01-01  0.856838  1.491776  0.001283  0.701816
2013-01-02 -1.097917  0.102588  0.661740  0.443531
2013-01-03  0.559313 -0.459055 -1.222598 -0.455304
2013-01-05  1.366810  1.073372 -0.994957  0.755314
2013-01-06  2.119746 -2.628174 -0.089460 -0.133636
2013-01-07  0.337920 -0.634027  0.421107  0.604303
2013-01-08  1.053434  1.109090 -0.367891 -0.846206
2013-01-10  0.048562 -0.285920  1.334100  0.194462

Ключевое слово columns может быть использовано для выбора списка столбцов, которые должны быть возвращены, что эквивалентно передаче 'columns=list_of_columns_to_filter':

In [495]: store.select("df", "columns=['A', 'B']")
Out[495]: 
                   A         B
2000-01-01 -0.398501 -0.677311
2000-01-02 -1.167564 -0.593353
2000-01-03 -0.131959  0.089012
2000-01-04  0.169405 -1.358046
2000-01-05  0.492195  0.076693
2000-01-06 -0.285283 -1.210529
2000-01-07  0.941577 -0.342447
2000-01-08  0.052607  2.093214

Параметры start и stop могут быть указаны для ограничения общего пространства поиска. Эти параметры выражаются в количестве строк в таблице.

Примечание

select вызовет ValueError если выражение запроса содержит ссылку на неизвестную переменную. Обычно это означает, что вы пытаетесь выбрать столбец, который не является столбцом данных.

select вызовет SyntaxError если выражение запроса не является допустимым.

Запрос timedelta64[ns]

Вы можете хранить и выполнять запросы, используя тип timedelta64[ns]. Условия могут быть заданы в формате: <float>(<unit>), где float может быть со знаком (и дробным), а unit может быть D,s,ms,us,ns для timedelta. Вот пример:

In [496]: from datetime import timedelta

In [497]: dftd = pd.DataFrame(
   .....:     {
   .....:         "A": pd.Timestamp("20130101"),
   .....:         "B": [
   .....:             pd.Timestamp("20130101") + timedelta(days=i, seconds=10)
   .....:             for i in range(10)
   .....:         ],
   .....:     }
   .....: )
   .....: 

In [498]: dftd["C"] = dftd["A"] - dftd["B"]

In [499]: dftd
Out[499]: 
           A                   B                  C
0 2013-01-01 2013-01-01 00:00:10  -1 days +23:59:50
1 2013-01-01 2013-01-02 00:00:10  -2 days +23:59:50
2 2013-01-01 2013-01-03 00:00:10  -3 days +23:59:50
3 2013-01-01 2013-01-04 00:00:10  -4 days +23:59:50
4 2013-01-01 2013-01-05 00:00:10  -5 days +23:59:50
5 2013-01-01 2013-01-06 00:00:10  -6 days +23:59:50
6 2013-01-01 2013-01-07 00:00:10  -7 days +23:59:50
7 2013-01-01 2013-01-08 00:00:10  -8 days +23:59:50
8 2013-01-01 2013-01-09 00:00:10  -9 days +23:59:50
9 2013-01-01 2013-01-10 00:00:10 -10 days +23:59:50

In [500]: store.append("dftd", dftd, data_columns=True)

In [501]: store.select("dftd", "C<'-3.5D'")
Out[501]: 
           A                   B                  C
4 2013-01-01 2013-01-05 00:00:10  -5 days +23:59:50
5 2013-01-01 2013-01-06 00:00:10  -6 days +23:59:50
6 2013-01-01 2013-01-07 00:00:10  -7 days +23:59:50
7 2013-01-01 2013-01-08 00:00:10  -8 days +23:59:50
8 2013-01-01 2013-01-09 00:00:10  -9 days +23:59:50
9 2013-01-01 2013-01-10 00:00:10 -10 days +23:59:50

Запрос MultiIndex

Выбор из MultiIndex может быть осуществлён с использованием имени уровня.

In [502]: df_mi.index.names
Out[502]: FrozenList(['foo', 'bar'])

In [503]: store.select("df_mi", "foo=baz and bar=two")
Out[503]: 
                A         B         C
foo bar                              
baz two  0.183573  0.145277  0.308146

Если у MultiIndex уровни имеют имена None, уровни автоматически становятся доступными через ключевое слово level_n с n требуемого уровня MultiIndex.

In [504]: index = pd.MultiIndex(
   .....:     levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]],
   .....:     codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]],
   .....: )
   .....: 

In [505]: df_mi_2 = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])

In [506]: df_mi_2
Out[506]: 
                  A         B         C
foo one   -0.646538  1.210676 -0.315409
    two    1.528366  0.376542  0.174490
    three  1.247943 -0.742283  0.710400
bar one    0.434128 -1.246384  1.139595
    two    1.388668 -0.413554 -0.666287
baz two    0.010150 -0.163820 -0.115305
    three  0.216467  0.633720  0.473945
qux one   -0.155446  1.287082  0.320201
    two   -1.256989  0.874920  0.765944
    three  0.025557 -0.729782 -0.127439

In [507]: store.append("df_mi_2", df_mi_2)

# the levels are automatically included as data columns with keyword level_n
In [508]: store.select("df_mi_2", "level_0=foo and level_1=two")
Out[508]: 
                A         B        C
foo two  1.528366  0.376542  0.17449

Индексирование

Вы можете создать/изменить индекс для таблицы с create_table_index после того, как данные уже находятся в таблице (после операции append/put). Создание индекса таблицы настоятельно рекомендуется. Это значительно ускорит ваши запросы, когда вы используете select с индексируемым измерением в качестве where.

Примечание

Индексы автоматически создаются для индексируемых столбцов и любых указанных столбцов данных. Это поведение можно отключить, передав index=False в append.

# we have automagically already created an index (in the first section)
In [509]: i = store.root.df.table.cols.index.index

In [510]: i.optlevel, i.kind
Out[510]: (6, 'medium')

# change an index by passing new parameters
In [511]: store.create_table_index("df", optlevel=9, kind="full")

In [512]: i = store.root.df.table.cols.index.index

In [513]: i.optlevel, i.kind
Out[513]: (9, 'full')

Часто при добавлении большого объёма данных в хранилище полезно отключить создание индекса для каждого добавления, а затем пересоздать его в конце.

In [514]: df_1 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [515]: df_2 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [516]: st = pd.HDFStore("appends.h5", mode="w")

In [517]: st.append("df", df_1, data_columns=["B"], index=False)

In [518]: st.append("df", df_2, data_columns=["B"], index=False)

In [519]: st.get_storer("df").table
Out[519]: 
/df/table (Table(20,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
  "B": Float64Col(shape=(), dflt=0.0, pos=2)}
  byteorder := 'little'
  chunkshape := (2730,)

Затем создайте индекс, когда закончите добавление.

In [520]: st.create_table_index("df", columns=["B"], optlevel=9, kind="full")

In [521]: st.get_storer("df").table
Out[521]: 
/df/table (Table(20,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
  "B": Float64Col(shape=(), dflt=0.0, pos=2)}
  byteorder := 'little'
  chunkshape := (2730,)
  autoindex := True
  colindexes := {
    "B": Index(9, fullshuffle, zlib(1)).is_csi=True}

In [522]: st.close()

См. здесь, чтобы узнать, как создать полностью отсортированный индекс (CSI) в существующем хранилище.

Запрос через столбцы данных

Вы можете назначить (и индексировать) определённые столбцы, для которых вы хотите выполнять запросы (кроме столбцов indexable, которые вы всегда можете запросить). Например, предположим, что вы хотите выполнить эту распространённую операцию на диске и вернуть только фрейм, соответствующий этому запросу. Вы можете указать data_columns = True для принудительного преобразования всех столбцов в data_columns.

In [523]: df_dc = df.copy()

In [524]: df_dc["string"] = "foo"

In [525]: df_dc.loc[df_dc.index[4:6], "string"] = np.nan

In [526]: df_dc.loc[df_dc.index[7:9], "string"] = "bar"

In [527]: df_dc["string2"] = "cool"

In [528]: df_dc.loc[df_dc.index[1:3], ["B", "C"]] = 1.0

In [529]: df_dc
Out[529]: 
                   A         B         C string string2
2000-01-01 -0.398501 -0.677311 -0.874991    foo    cool
2000-01-02 -1.167564  1.000000  1.000000    foo    cool
2000-01-03 -0.131959  1.000000  1.000000    foo    cool
2000-01-04  0.169405 -1.358046 -0.105563    foo    cool
2000-01-05  0.492195  0.076693  0.213685    NaN    cool
2000-01-06 -0.285283 -1.210529 -1.408386    NaN    cool
2000-01-07  0.941577 -0.342447  0.222031    foo    cool
2000-01-08  0.052607  2.093214  1.064908    bar    cool

# on-disk operations
In [530]: store.append("df_dc", df_dc, data_columns=["B", "C", "string", "string2"])

In [531]: store.select("df_dc", where="B > 0")
Out[531]: 
                   A         B         C string string2
2000-01-02 -1.167564  1.000000  1.000000    foo    cool
2000-01-03 -0.131959  1.000000  1.000000    foo    cool
2000-01-05  0.492195  0.076693  0.213685    NaN    cool
2000-01-08  0.052607  2.093214  1.064908    bar    cool

# getting creative
In [532]: store.select("df_dc", "B > 0 & C > 0 & string == foo")
Out[532]: 
                   A    B    C string string2
2000-01-02 -1.167564  1.0  1.0    foo    cool
2000-01-03 -0.131959  1.0  1.0    foo    cool

# this is in-memory version of this type of selection
In [533]: df_dc[(df_dc.B > 0) & (df_dc.C > 0) & (df_dc.string == "foo")]
Out[533]: 
                   A    B    C string string2
2000-01-02 -1.167564  1.0  1.0    foo    cool
2000-01-03 -0.131959  1.0  1.0    foo    cool

# we have automagically created this index and the B/C/string/string2
# columns are stored separately as ``PyTables`` columns
In [534]: store.root.df_dc.table
Out[534]: 
/df_dc/table (Table(8,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
  "B": Float64Col(shape=(), dflt=0.0, pos=2),
  "C": Float64Col(shape=(), dflt=0.0, pos=3),
  "string": StringCol(itemsize=3, shape=(), dflt=b'', pos=4),
  "string2": StringCol(itemsize=4, shape=(), dflt=b'', pos=5)}
  byteorder := 'little'
  chunkshape := (1680,)
  autoindex := True
  colindexes := {
    "index": Index(6, mediumshuffle, zlib(1)).is_csi=False,
    "B": Index(6, mediumshuffle, zlib(1)).is_csi=False,
    "C": Index(6, mediumshuffle, zlib(1)).is_csi=False,
    "string": Index(6, mediumshuffle, zlib(1)).is_csi=False,
    "string2": Index(6, mediumshuffle, zlib(1)).is_csi=False}

Есть некоторое снижение производительности при преобразовании большого количества столбцов в data columns, поэтому это зависит от пользователя. Кроме того, вы не можете изменять столбцы данных (или индексируемые столбцы) после первой операции добавления/записи (Конечно, вы можете просто считать данные и создать новую таблицу!).

Итератор

Вы можете передать iterator=True или chunksize=number_in_a_chunk в select и select_as_multiple для возвращения итератора по результатам. По умолчанию возвращается 50 000 строк в блоке.

In [535]: for df in store.select("df", chunksize=3):
   .....:     print(df)
   .....: 
                   A         B         C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353  0.146262
2000-01-03 -0.131959  0.089012  0.667450
                   A         B         C
2000-01-04  0.169405 -1.358046 -0.105563
2000-01-05  0.492195  0.076693  0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
                   A         B         C
2000-01-07  0.941577 -0.342447  0.222031
2000-01-08  0.052607  2.093214  1.064908

Примечание

Вы также можете использовать итератор с read_hdf, который откроет, а затем автоматически закроет хранилище после завершения итерации.

for df in pd.read_hdf("store.h5", "df", chunksize=3):
    print(df)

Обратите внимание, что ключевое слово chunksize относится к исходным строкам. Таким образом, если вы выполняете запрос, то chunksize разделит общее количество строк в таблице и применённый запрос, возвращая итератор по потенциально неравномерным блокам.

Вот рецепт для генерации запроса и использования его для создания равномерных блоков возвращаемых данных.

In [536]: dfeq = pd.DataFrame({"number": np.arange(1, 11)})

In [537]: dfeq
Out[537]: 
   number
0       1
1       2
2       3
3       4
4       5
5       6
6       7
7       8
8       9
9      10

In [538]: store.append("dfeq", dfeq, data_columns=["number"])

In [539]: def chunks(l, n):
   .....:     return [l[i: i + n] for i in range(0, len(l), n)]
   .....: 

In [540]: evens = [2, 4, 6, 8, 10]

In [541]: coordinates = store.select_as_coordinates("dfeq", "number=evens")

In [542]: for c in chunks(coordinates, 2):
   .....:     print(store.select("dfeq", where=c))
   .....: 
   number
1       2
3       4
   number
5       6
7       8
   number
9      10

Расширенные запросы

Выбор одного столбца

Для получения одного индексируемого или столбца данных используйте метод select_column. Это позволит, например, очень быстро получить индекс. Эти методы возвращают Series результата, индексированного по номеру строки. Эти методы в настоящее время не принимают селектор where.

In [543]: store.select_column("df_dc", "index")
Out[543]: 
0   2000-01-01
1   2000-01-02
2   2000-01-03
3   2000-01-04
4   2000-01-05
5   2000-01-06
6   2000-01-07
7   2000-01-08
Name: index, dtype: datetime64[ns]

In [544]: store.select_column("df_dc", "string")
Out[544]: 
0    foo
1    foo
2    foo
3    foo
4    NaN
5    NaN
6    foo
7    bar
Name: string, dtype: object
Выбор координат

Иногда вам нужно получить координаты (т.е. местоположения индексов) вашего запроса. Это возвращает Int64Index результирующих местоположений. Эти координаты также можно передавать в последующие операции where.

In [545]: df_coord = pd.DataFrame(
   .....:     np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
   .....: )
   .....: 

In [546]: store.append("df_coord", df_coord)

In [547]: c = store.select_as_coordinates("df_coord", "index > 20020101")

In [548]: c
Out[548]: 
Int64Index([732, 733, 734, 735, 736, 737, 738, 739, 740, 741,
            ...
            990, 991, 992, 993, 994, 995, 996, 997, 998, 999],
           dtype='int64', length=268)

In [549]: store.select("df_coord", where=c)
Out[549]: 
                   0         1
2002-01-02  0.009035  0.921784
2002-01-03 -1.476563 -1.376375
2002-01-04  1.266731  2.173681
2002-01-05  0.147621  0.616468
2002-01-06  0.008611  2.136001
...              ...       ...
2002-09-22  0.781169 -0.791687
2002-09-23 -0.764810 -2.000933
2002-09-24 -0.345662  0.393915
2002-09-25 -0.116661  0.834638
2002-09-26 -1.341780  0.686366

[268 rows x 2 columns]
Выбор с помощью маски where

Иногда ваш запрос может включать создание списка строк для выбора. Обычно эта mask будет результатом index операции индексирования. Этот пример выбирает месяцы datetimeindex, которые равны 5.

In [550]: df_mask = pd.DataFrame(
   .....:     np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
   .....: )
   .....: 

In [551]: store.append("df_mask", df_mask)

In [552]: c = store.select_column("df_mask", "index")

In [553]: where = c[pd.DatetimeIndex(c).month == 5].index

In [554]: store.select("df_mask", where=where)
Out[554]: 
                   0         1
2000-05-01 -0.386742 -0.977433
2000-05-02 -0.228819  0.471671
2000-05-03  0.337307  1.840494
2000-05-04  0.050249  0.307149
2000-05-05 -0.802947 -0.946730
...              ...       ...
2002-05-27  1.605281  1.741415
2002-05-28 -0.804450 -0.715040
2002-05-29 -0.874851  0.037178
2002-05-30 -0.161167 -1.294944
2002-05-31 -0.258463 -0.731969

[93 rows x 2 columns]
Объект Storer

Если вы хотите проверить сохранённый объект, получите его через get_storer. Вы можете использовать это программно, например, чтобы получить количество строк в объекте.

In [555]: store.get_storer("df_dc").nrows
Out[555]: 8

Запросы к нескольким таблицам

Методы append_to_multiple и select_as_multiple могут выполнять добавление/выбор из нескольких таблиц одновременно. Идея заключается в том, чтобы иметь одну таблицу (назовем её таблицей-селектором), в которой индексированы большинство/все столбцы, и выполнять запросы к ней. Другие таблицы являются таблицами данных с индексом, соответствующим индексу таблицы-селектора. Затем вы можете выполнить очень быстрый запрос к таблице-селектору, получив много данных. Этот метод похож на использование очень широкой таблицы, но позволяет более эффективно выполнять запросы.

Метод append_to_multiple разбивает заданный DataFrame на несколько таблиц в соответствии с d, словарем, сопоставляющим имена таблиц с списком «столбцов», которые вы хотите в этой таблице. Если None используется вместо списка, эта таблица будет содержать оставшиеся не указанные столбцы заданного DataFrame. Аргумент selector определяет, какая таблица является таблицей-селектором (из которой можно выполнять запросы). Аргумент dropna удалит строки из входного DataFrame, чтобы гарантировать синхронизацию таблиц. Это означает, что если строка для одной из таблиц, в которые осуществляется запись, полностью np.NaN, эта строка будет удалена из всех таблиц.

Если dropna имеет значение False, ПОЛЬЗОВАТЕЛЬ НЕСЁТ ОТВЕТСТВЕННОСТЬ ЗА СИНХРОНИЗАЦИЮ ТАБЛИЦ. Помните, что строки, полностью np.Nan, не записываются в HDFStore, поэтому, если вы вызовете dropna=False, некоторые таблицы могут иметь больше строк, чем другие, и поэтому select_as_multiple может не сработать или может вернуть неожиданные результаты.

In [556]: df_mt = pd.DataFrame(
   .....:     np.random.randn(8, 6),
   .....:     index=pd.date_range("1/1/2000", periods=8),
   .....:     columns=["A", "B", "C", "D", "E", "F"],
   .....: )
   .....: 

In [557]: df_mt["foo"] = "bar"

In [558]: df_mt.loc[df_mt.index[1], ("A", "B")] = np.nan

# you can also create the tables individually
In [559]: store.append_to_multiple(
   .....:     {"df1_mt": ["A", "B"], "df2_mt": None}, df_mt, selector="df1_mt"
   .....: )
   .....: 

In [560]: store
Out[560]: 
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5

# individual tables were created
In [561]: store.select("df1_mt")
Out[561]: 
                   A         B
2000-01-01  0.079529 -1.459471
2000-01-02       NaN       NaN
2000-01-03 -0.423113  2.314361
2000-01-04  0.756744 -0.792372
2000-01-05 -0.184971  0.170852
2000-01-06  0.678830  0.633974
2000-01-07  0.034973  0.974369
2000-01-08 -2.110103  0.243062

In [562]: store.select("df2_mt")
Out[562]: 
                   C         D         E         F  foo
2000-01-01 -0.596306 -0.910022 -1.057072 -0.864360  bar
2000-01-02  0.477849  0.283128 -2.045700 -0.338206  bar
2000-01-03 -0.033100 -0.965461 -0.001079 -0.351689  bar
2000-01-04 -0.513555 -1.484776 -0.796280 -0.182321  bar
2000-01-05 -0.872407 -1.751515  0.934334  0.938818  bar
2000-01-06 -1.398256  1.347142 -0.029520  0.082738  bar
2000-01-07 -0.755544  0.380786 -1.634116  1.293610  bar
2000-01-08  1.453064  0.500558 -0.574475  0.694324  bar

# as a multiple
In [563]: store.select_as_multiple(
   .....:     ["df1_mt", "df2_mt"],
   .....:     where=["A>0", "B>0"],
   .....:     selector="df1_mt",
   .....: )
   .....: 
Out[563]: 
                   A         B         C         D         E         F  foo
2000-01-06  0.678830  0.633974 -1.398256  1.347142 -0.029520  0.082738  bar
2000-01-07  0.034973  0.974369 -0.755544  0.380786 -1.634116  1.293610  bar

Удаление из таблицы

Вы можете выборочно удалить из таблицы, указав where. При удалении строк важно понимать, что PyTables удаляет строки, удаляя их, а затем перемещая последующие данные. Таким образом, удаление может быть очень дорогостоящей операцией в зависимости от ориентации ваших данных. Для достижения оптимальной производительности следует сделать так, чтобы измерение, которое вы удаляете, было первым в indexables.

Данные упорядочены (на диске) в соответствии с indexables. Вот простой пример использования. Вы храните данные типа панели, со значениями дат в major_axis и идентификаторами в minor_axis. Данные затем интерпретируются так:

  • date_1
    • id_1

    • id_2

    • .

    • id_n

  • date_2
    • id_1

    • .

    • id_n

Должно быть понятно, что операция удаления по major_axis будет довольно быстрой, так как один фрагмент удаляется, а последующие данные перемещаются. С другой стороны, операция удаления по minor_axis будет очень дорогостоящей. В этом случае было бы почти наверняка быстрее переписать таблицу, используя where , который выбирает все, кроме отсутствующих данных.

Предупреждение

Обратите внимание, что HDF5 НЕ ВОССТАНАВЛИВАЕТ ПРОСТРАНСТВО в файлах h5 автоматически. Таким образом, многократное удаление (или удаление узлов) и повторное добавление БУДЕТ ТЕНДЕНЦИЯ К УВЕЛИЧЕНИЮ РАЗМЕРА ФАЙЛА.

Чтобы упаковать и очистить файл, используйте ptrepack.

Примечания и замечания

Сжатие

PyTables позволяет сжимать хранимые данные. Это относится ко всем видам хранилищ, а не только к таблицам. Для управления сжатием используются два параметра: complevel и complib.

  • complevel указывает, как и насколько сильно сжимаются данные. complevel=0 и complevel=None отключают сжатие, а 0<complevel<10 включает сжатие.

  • complib указывает, какую библиотеку сжатия использовать. Если ничего не указано, используется библиотека по умолчанию zlib . Библиотека сжатия обычно оптимизирует либо скорость сжатия, либо скорость, и результаты будут зависеть от типа данных. Какой тип сжатия выбрать, зависит от ваших конкретных потребностей и данных. Список поддерживаемых библиотек сжатия:

    • zlib: Библиотека сжатия по умолчанию. Классика в плане сжатия, достигает хороших показателей сжатия, но несколько медленная.

    • lzo: Быстрое сжатие и распаковку.

    • bzip2: Хорошие показатели сжатия.

    • blosc: Быстрое сжатие и распаковку.

      Поддержка альтернативных сжимателей blosc:

      • blosc:blosclz Это сжиматель по умолчанию для blosc

      • blosc:lz4: Компактный, очень популярный и быстрый сжиматель.

      • blosc:lz4hc: Модифицированная версия LZ4, обеспечивает лучшие коэффициенты сжатия за счёт скорости.

      • blosc:snappy: Популярный сжиматель, используемый во многих местах.

      • blosc:zlib: Классика; несколько медленнее предыдущих, но достигает лучших коэффициентов сжатия.

      • blosc:zstd: Крайне сбалансированный кодек; он обеспечивает лучшие коэффициенты сжатия среди других вышеперечисленных, и с достаточно высокой скоростью.

    Если complib определено как что-то отличное от перечисленных библиотек, выдается исключение ValueError.

Примечание

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

Включить сжатие для всех объектов в файле:

store_compressed = pd.HDFStore(
    "store_compressed.h5", complevel=9, complib="blosc:blosclz"
)

Или сжатие в реальном времени (это относится только к таблицам) в хранилищах, где сжатие не включено:

store.append("df", df, complib="zlib", complevel=5)

ptrepack

PyTables предлагает лучшую производительность при записи, когда таблицы сжимаются после записи, а не при включении сжатия в самом начале. Вы можете использовать предоставленную утилиту PyTables ptrepack. Кроме того, ptrepack может изменять уровни сжатия после факта.

ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5

Кроме того, ptrepack in.h5 out.h5 упакует файл, чтобы вы могли повторно использовать ранее удалённое пространство. В качестве альтернативы, можно просто удалить файл и переписать его или использовать метод copy.

Ограничения

Предупреждение

HDFStore не потокобезопасен для записи. Базовое PyTables поддерживает только одновременные чтения (через потоки или процессы). Если вам нужно чтение и запись одновременно, вам нужно сериализовать эти операции в одном потоке в одном процессе. В противном случае вы повредите свои данные. Смотрите (GH2397) для получения дополнительной информации.

  • Если вы используете блокировки для управления доступом к записи между несколькими процессами, вы можете использовать fsync() перед освобождением блокировок записи. Для удобства вы можете использовать store.flush(fsync=True) , чтобы сделать это за вас.

  • После создания table столбцы (DataFrame) фиксируются; можно добавить только точно такие же столбцы.

  • Учитывайте, что часовые пояса (например, pytz.timezone('US/Eastern')) не обязательно совпадают с версиями библиотек часовых поясов. Таким образом, если данные локализованы в определённом часовом поясе в HDFStore с использованием одной версии библиотеки часовых поясов, и эти данные обновляются другой версией, данные будут преобразованы в UTC, так как эти часовые пояса не считаются равными. Либо используйте одну и ту же версию библиотеки часовых поясов, либо используйте tz_convert с обновлённым определением часового пояса.

Предупреждение

PyTables покажет NaturalNameWarning , если имя столбца нельзя использовать в качестве селектора атрибута. Естественные идентификаторы содержат только буквы, цифры и символы нижнего подчеркивания и не могут начинаться с цифры. Другие идентификаторы не могут быть использованы в where -оператор и, как правило, являются плохой практикой.

Типы данных

HDFStore будет отображать тип данных объекта на PyTables базовый тип данных. Это означает, что следующие типы работают:

Тип

Представляет пропущенные значения

с плавающей точкой : float64, float32, float16

np.nan

целый : int64, int32, int8, uint64,uint32, uint8

булево

datetime64[ns]

NaT

timedelta64[ns]

NaT

категориальный : см. раздел ниже

объект : strings

np.nan

unicode столбцы не поддерживаются и БУДУТ ВЫЗЫВАТЬ ОШИБКУ.

Категориальные данные

Вы можете записать данные, содержащие category типы данных в HDFStore. Запросы работают так же, как если бы это был массив объектов. Однако данные с типом category хранятся более эффективным способом.

In [564]: dfcat = pd.DataFrame(
   .....:     {"A": pd.Series(list("aabbcdba")).astype("category"), "B": np.random.randn(8)}
   .....: )
   .....: 

In [565]: dfcat
Out[565]: 
   A         B
0  a -1.608059
1  a  0.851060
2  b -0.736931
3  b  0.003538
4  c -1.422611
5  d  2.060901
6  b  0.993899
7  a -1.371768

In [566]: dfcat.dtypes
Out[566]: 
A    category
B     float64
dtype: object

In [567]: cstore = pd.HDFStore("cats.h5", mode="w")

In [568]: cstore.append("dfcat", dfcat, format="table", data_columns=["A"])

In [569]: result = cstore.select("dfcat", where="A in ['b', 'c']")

In [570]: result
Out[570]: 
   A         B
2  b -0.736931
3  b  0.003538
4  c -1.422611
6  b  0.993899

In [571]: result.dtypes
Out[571]: 
A    category
B     float64
dtype: object

Столбцы строк

min_itemsize

Внутренняя реализация HDFStore использует фиксированную ширину столбца (itemsize) для столбцов строк. Размер элемента столбца строк вычисляется как максимальная длина данных (для данного столбца), которые передаются в HDFStore, в первом добавлении. При последующих добавлениях может появиться строка для столбца, больше, чем может вместить столбец; будет поднято исключение (в противном случае у вас может быть неявное усечение этих столбцов, что приведет к потере информации). В будущем мы можем ослабить это и разрешить указание пользователем усечения.

Передайте min_itemsize при первом создании таблицы, чтобы задать минимальную длину определенного столбца строк. min_itemsize может быть целым числом или словарем, сопоставляющим имя столбца с целым числом. Вы можете передать values в качестве ключа, чтобы разрешить всем индексируемым или данным_столбцам иметь этот min_itemsize.

Передача словаря min_itemsize приведет к автоматическому созданию всех переданных столбцов в качестве данных_столбцов.

Примечание

Если вы не передаете data_columns, то min_itemsize будет максимальной длиной любой переданной строки

In [572]: dfs = pd.DataFrame({"A": "foo", "B": "bar"}, index=list(range(5)))

In [573]: dfs
Out[573]: 
     A    B
0  foo  bar
1  foo  bar
2  foo  bar
3  foo  bar
4  foo  bar

# A and B have a size of 30
In [574]: store.append("dfs", dfs, min_itemsize=30)

In [575]: store.get_storer("dfs").table
Out[575]: 
/dfs/table (Table(5,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": StringCol(itemsize=30, shape=(2,), dflt=b'', pos=1)}
  byteorder := 'little'
  chunkshape := (963,)
  autoindex := True
  colindexes := {
    "index": Index(6, mediumshuffle, zlib(1)).is_csi=False}

# A is created as a data_column with a size of 30
# B is size is calculated
In [576]: store.append("dfs2", dfs, min_itemsize={"A": 30})

In [577]: store.get_storer("dfs2").table
Out[577]: 
/dfs2/table (Table(5,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": StringCol(itemsize=3, shape=(1,), dflt=b'', pos=1),
  "A": StringCol(itemsize=30, shape=(), dflt=b'', pos=2)}
  byteorder := 'little'
  chunkshape := (1598,)
  autoindex := True
  colindexes := {
    "index": Index(6, mediumshuffle, zlib(1)).is_csi=False,
    "A": Index(6, mediumshuffle, zlib(1)).is_csi=False}

nan_rep

Столбцы строк будут сериализовать np.nan (пропущенное значение) с nan_rep строковым представлением. По умолчанию это строковое значение nan. Вы можете непреднамеренно преобразовать фактическое значение nan в пропущенное значение.

In [578]: dfss = pd.DataFrame({"A": ["foo", "bar", "nan"]})

In [579]: dfss
Out[579]: 
     A
0  foo
1  bar
2  nan

In [580]: store.append("dfss", dfss)

In [581]: store.select("dfss")
Out[581]: 
     A
0  foo
1  bar
2  NaN

# here you need to specify a different nan rep
In [582]: store.append("dfss2", dfss, nan_rep="_nan_")

In [583]: store.select("dfss2")
Out[583]: 
     A
0  foo
1  bar
2  nan

Внешняя совместимость

HDFStore записывает объекты в формате table в определенных форматах, подходящих для создания без потерь обратных преобразований в объекты pandas. Для внешней совместимости HDFStore может читать таблицы в формате PyTables.

Возможна запись объекта HDFStore , который легко можно импортировать в R с помощью библиотеки rhdf5 (Веб-сайт пакета). Создайте хранилище табличного формата так:

In [584]: df_for_r = pd.DataFrame(
   .....:     {
   .....:         "first": np.random.rand(100),
   .....:         "second": np.random.rand(100),
   .....:         "class": np.random.randint(0, 2, (100,)),
   .....:     },
   .....:     index=range(100),
   .....: )
   .....: 

In [585]: df_for_r.head()
Out[585]: 
      first    second  class
0  0.013480  0.504941      0
1  0.690984  0.898188      1
2  0.510113  0.618748      1
3  0.357698  0.004972      0
4  0.451658  0.012065      1

In [586]: store_export = pd.HDFStore("export.h5")

In [587]: store_export.append("df_for_r", df_for_r, data_columns=df_dc.columns)

In [588]: store_export
Out[588]: 
<class 'pandas.io.pytables.HDFStore'>
File path: export.h5

В R этот файл можно считать в объект data.frame с использованием библиотеки rhdf5. Следующая примерная функция считывает соответствующие имена столбцов и значения данных из значений и собирает их в data.frame.

# Load values and column names for all datasets from corresponding nodes and
# insert them into one data.frame object.

library(rhdf5)

loadhdf5data <- function(h5File) {

listing <- h5ls(h5File)
# Find all data nodes, values are stored in *_values and corresponding column
# titles in *_items
data_nodes <- grep("_values", listing$name)
name_nodes <- grep("_items", listing$name)
data_paths = paste(listing$group[data_nodes], listing$name[data_nodes], sep = "/")
name_paths = paste(listing$group[name_nodes], listing$name[name_nodes], sep = "/")
columns = list()
for (idx in seq(data_paths)) {
  # NOTE: matrices returned by h5read have to be transposed to obtain
  # required Fortran order!
  data <- data.frame(t(h5read(h5File, data_paths[idx])))
  names <- t(h5read(h5File, name_paths[idx]))
  entry <- data.frame(data)
  colnames(entry) <- names
  columns <- append(columns, entry)
}

data <- data.frame(columns)

return(data)
}

Теперь вы можете импортировать DataFrame в R:

> data = loadhdf5data("transfer.hdf5")
> head(data)
         first    second class
1 0.4170220047 0.3266449     0
2 0.7203244934 0.5270581     0
3 0.0001143748 0.8859421     1
4 0.3023325726 0.3572698     1
5 0.1467558908 0.9085352     1
6 0.0923385948 0.6233601     1

Примечание

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

Производительность

  • Формат tables сопровождается штрафом производительности при записи по сравнению с хранилищами fixed. Преимущество заключается в возможности добавлять/удалять и выполнять запросы (возможно, очень больших объемов данных). Время записи обычно больше по сравнению с обычными хранилищами. Время запроса может быть довольно быстрым, особенно по индексируемой оси.

  • Вы можете передать chunksize=<int> в append, указав размер блока записи (по умолчанию 50000). Это значительно снизит использование памяти при записи.

  • Вы можете передать expectedrows=<int> в первый append, чтобы установить ОБЩЕЕ количество строк, которые PyTables будет ожидать. Это позволит оптимизировать производительность чтения/записи.

  • Дублирующие строки могут быть записаны в таблицы, но отфильтровываются при выборе (последние элементы выбираются; таким образом, таблица уникальна по парам «главный, дополнительный»)

  • Будет поднято исключение PerformanceWarning, если вы пытаетесь сохранить типы, которые будут сериализованы PyTables (а не храниться как собственные типы). См. Здесь для получения дополнительной информации и некоторых решений.

Feather

Feather предоставляет двоичную столбцовую сериализацию для фреймов данных. Она разработана для повышения эффективности чтения и записи фреймов данных и для облегчения совместного использования данных между языками анализа данных.

Feather разработан для точной сериализации и десериализации DataFrames, поддерживая все типы данных pandas, включая расширенные типы данных, такие как категориальные и временные с часовым поясом.

Некоторые особенности:

  • Формат НЕ запишет Index, или MultiIndex для DataFrame и вызовет ошибку, если предоставлено отличное от стандартного значение. Вы можете .reset_index() для хранения индекса или .reset_index(drop=True) для игнорирования его.

  • Дублирование имен столбцов и имена столбцов, отличные от строк, не поддерживаются.

  • Фактические объекты Python в столбцах типа «объект» не поддерживаются. При попытке сериализации они вызовут полезное сообщение об ошибке.

См. Полную документацию.

In [589]: df = pd.DataFrame(
   .....:     {
   .....:         "a": list("abc"),
   .....:         "b": list(range(1, 4)),
   .....:         "c": np.arange(3, 6).astype("u1"),
   .....:         "d": np.arange(4.0, 7.0, dtype="float64"),
   .....:         "e": [True, False, True],
   .....:         "f": pd.Categorical(list("abc")),
   .....:         "g": pd.date_range("20130101", periods=3),
   .....:         "h": pd.date_range("20130101", periods=3, tz="US/Eastern"),
   .....:         "i": pd.date_range("20130101", periods=3, freq="ns"),
   .....:     }
   .....: )
   .....: 

In [590]: df
Out[590]: 
   a  b  c  ...          g                         h                             i
0  a  1  3  ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000
1  b  2  4  ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001
2  c  3  5  ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002

[3 rows x 9 columns]

In [591]: df.dtypes
Out[591]: 
a                        object
b                         int64
c                         uint8
d                       float64
e                          bool
f                      category
g                datetime64[ns]
h    datetime64[ns, US/Eastern]
i                datetime64[ns]
dtype: object

Запись в файл feather.

In [592]: df.to_feather("example.feather")

Чтение из файла feather.

In [593]: result = pd.read_feather("example.feather")

In [594]: result
Out[594]: 
   a  b  c  ...          g                         h                             i
0  a  1  3  ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000
1  b  2  4  ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001
2  c  3  5  ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002

[3 rows x 9 columns]

# we preserve dtypes
In [595]: result.dtypes
Out[595]: 
a                        object
b                         int64
c                         uint8
d                       float64
e                          bool
f                      category
g                datetime64[ns]
h    datetime64[ns, US/Eastern]
i                datetime64[ns]
dtype: object

Parquet

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

Parquet разработан для точного сериализации и десериализации DataFrame , поддерживая все типы данных pandas, включая расширенные типы данных, такие как datetime с часовым поясом.

Несколько замечаний.

  • Дублирование имён столбцов и имена столбцов, не являющиеся строками, не поддерживаются.

  • Двигатель pyarrow всегда записывает индекс в выходной файл, но fastparquet записывает только индексы, отличные от значений по умолчанию. Этот дополнительный столбец может вызвать проблемы для потребителей, не являющихся pandas, которые не ожидают его. Вы можете принудительно включить или исключить индексы с помощью аргумента index, независимо от базового двигателя.

  • Если указаны имена уровней индекса, они должны быть строками.

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

  • Двигатель pyarrow сохраняет флаг ordered категориальных типов данных со строковыми типами. fastparquet не сохраняет флаг ordered.

  • Неподдерживаемые типы включают Interval и фактические типы Python object. При попытке сериализации они будут генерировать полезное сообщение об ошибке. Тип Period поддерживается с pyarrow >= 0.16.0.

  • Двигатель pyarrow сохраняет расширенные типы данных, такие как целочисленный и строковый тип данных с возможностью NULL (требуется pyarrow >= 0.16.0, и требуется, чтобы расширенный тип реализовывал необходимые протоколы, см. документацию по расширенным типам).

Вы можете указать engine для управления сериализацией. Это может быть pyarrow, или fastparquet, или auto. Если двигатель НЕ указан, то проверяется опция pd.options.io.parquet.engine; если она также auto, то пробуется pyarrow, а в случае неудачи используется fastparquet.

См. документацию по pyarrow и fastparquet.

Примечание

Эти двигатели очень похожи и должны читать/записывать практически идентичные файлы формата parquet. pyarrow>=8.0.0 поддерживает данные timedelta, fastparquet>=0.1.4 поддерживает даты и время с учетом часового пояса. Эти библиотеки различаются своими различными базовыми зависимостями (fastparquet использует numba, в то время как pyarrow использует c-библиотеку).

In [596]: df = pd.DataFrame(
   .....:     {
   .....:         "a": list("abc"),
   .....:         "b": list(range(1, 4)),
   .....:         "c": np.arange(3, 6).astype("u1"),
   .....:         "d": np.arange(4.0, 7.0, dtype="float64"),
   .....:         "e": [True, False, True],
   .....:         "f": pd.date_range("20130101", periods=3),
   .....:         "g": pd.date_range("20130101", periods=3, tz="US/Eastern"),
   .....:         "h": pd.Categorical(list("abc")),
   .....:         "i": pd.Categorical(list("abc"), ordered=True),
   .....:     }
   .....: )
   .....: 

In [597]: df
Out[597]: 
   a  b  c    d      e          f                         g  h  i
0  a  1  3  4.0   True 2013-01-01 2013-01-01 00:00:00-05:00  a  a
1  b  2  4  5.0  False 2013-01-02 2013-01-02 00:00:00-05:00  b  b
2  c  3  5  6.0   True 2013-01-03 2013-01-03 00:00:00-05:00  c  c

In [598]: df.dtypes
Out[598]: 
a                        object
b                         int64
c                         uint8
d                       float64
e                          bool
f                datetime64[ns]
g    datetime64[ns, US/Eastern]
h                      category
i                      category
dtype: object

Запись в файл parquet.

In [599]: df.to_parquet("example_pa.parquet", engine="pyarrow")

In [600]: df.to_parquet("example_fp.parquet", engine="fastparquet")

Чтение из файла parquet.

In [601]: result = pd.read_parquet("example_fp.parquet", engine="fastparquet")

In [602]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow")

In [603]: result.dtypes
Out[603]: 
a                        object
b                         int64
c                         uint8
d                       float64
e                          bool
f                datetime64[ns]
g    datetime64[ns, US/Eastern]
h                      category
i                      category
dtype: object

Чтение только определенных столбцов из файла parquet.

In [604]: result = pd.read_parquet(
   .....:     "example_fp.parquet",
   .....:     engine="fastparquet",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [605]: result = pd.read_parquet(
   .....:     "example_pa.parquet",
   .....:     engine="pyarrow",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [606]: result.dtypes
Out[606]: 
a    object
b     int64
dtype: object

Обработка индексов

Сериализация DataFrame в parquet может включать неявный индекс как один или несколько столбцов в выходном файле. Таким образом, этот код:

In [607]: df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})

In [608]: df.to_parquet("test.parquet", engine="pyarrow")

создаёт файл parquet с тремя столбцами, если вы используете pyarrow для сериализации: a, b, и __index_level_0__. Если вы используете fastparquet, индекс может или не может быть записан в файл.

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

Если вы хотите исключить индексы фрейма данных при записи, передайте index=False в to_parquet():

In [609]: df.to_parquet("test.parquet", index=False)

Это создаёт файл parquet только с двумя ожидаемыми столбцами, a и b. Если ваш DataFrame имеет пользовательский индекс, вы не получите его обратно, загрузив этот файл в DataFrame.

Передача index=True всегда запишет индекс, даже если это не является стандартным поведением базового двигателя.

Разбиение файлов Parquet

Parquet поддерживает разбиение данных на основе значений одного или нескольких столбцов.

In [610]: df = pd.DataFrame({"a": [0, 0, 1, 1], "b": [0, 1, 0, 1]})

In [611]: df.to_parquet(path="test", engine="pyarrow", partition_cols=["a"], compression=None)

path указывает родительский каталог, в который будут сохранены данные. partition_cols — это имена столбцов, по которым будет производиться разбиение набора данных. Столбцы разбиваются в том порядке, в котором они указаны. Разбиения по столбцам определяются уникальными значениями в столбцах разбиения. Приведённый выше пример создаёт разбиение набора данных, которое может выглядеть так:

test
├── a=0
│   ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet
│   └──  ...
└── a=1
    ├── e6ab24a4f45147b49b54a662f0c412a3.parquet
    └── ...

ORC

Новый в версии 1.0.0.

Аналогично формату parquet, формат ORC — это бинарное разбиение по столбцам для фреймов данных. Он разработан для повышения эффективности чтения фреймов данных. pandas предоставляет как читатель, так и записыватель для формата ORC, read_orc() и to_orc(). Для этого требуется библиотека pyarrow.

Предупреждение

  • Сильно рекомендуется устанавливать pyarrow с помощью conda из-за проблем, возникающих при установке pyarrow.

  • to_orc() требует pyarrow>=7.0.0.

  • read_orc() и to_orc() пока не поддерживаются в Windows, вы можете найти подходящие среды на странице установки дополнительных зависимостей.

  • Список поддерживаемых типов данных см. в документации по поддерживаемым функциям ORC в Arrow.

  • В настоящее время часовые пояса в столбцах datetime не сохраняются при преобразовании фрейма данных в файлы ORC.

In [612]: df = pd.DataFrame(
   .....:     {
   .....:         "a": list("abc"),
   .....:         "b": list(range(1, 4)),
   .....:         "c": np.arange(4.0, 7.0, dtype="float64"),
   .....:         "d": [True, False, True],
   .....:         "e": pd.date_range("20130101", periods=3),
   .....:     }
   .....: )
   .....: 

In [613]: df
Out[613]: 
   a  b    c      d          e
0  a  1  4.0   True 2013-01-01
1  b  2  5.0  False 2013-01-02
2  c  3  6.0   True 2013-01-03

In [614]: df.dtypes
Out[614]: 
a            object
b             int64
c           float64
d              bool
e    datetime64[ns]
dtype: object

Запись в файл orc.

In [615]: df.to_orc("example_pa.orc", engine="pyarrow")

Чтение из файла orc.

In [616]: result = pd.read_orc("example_pa.orc")

In [617]: result.dtypes
Out[617]: 
a            object
b             int64
c           float64
d              bool
e    datetime64[ns]
dtype: object

Чтение только определённых столбцов из файла orc.

In [618]: result = pd.read_orc(
   .....:     "example_pa.orc",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [619]: result.dtypes
Out[619]: 
a    object
b     int64
dtype: object

SQL-запросы

Модуль pandas.io.sql предоставляет набор обёртки для запросов, что способствует извлечению данных и снижению зависимости от API конкретной СУБД. Абстракция базы данных предоставляется SQLAlchemy, если установлена. Кроме того, вам понадобится библиотека драйвера для вашей базы данных. Примерами таких драйверов являются psycopg2 для PostgreSQL или pymysql для MySQL. Для SQLite это включено в стандартной библиотеке Python по умолчанию. Вы можете найти обзор поддерживаемых драйверов для каждого SQL-диалекта в документации SQLAlchemy.

Если SQLAlchemy не установлена, кэширование предоставляется только для sqlite (и для mysql для обратной совместимости, но это устарело и будет удалено в будущей версии). Этот режим требует адаптер базы данных Python, который соответствует Python DB-API.

Также см. некоторые примеры кулинарной книги для некоторых продвинутых стратегий.

Основные функции:

read_sql_table(table_name, con[, schema, ...])

Чтение таблицы SQL базы данных в DataFrame.

read_sql_query(sql, con[, index_col, ...])

Чтение SQL-запроса в DataFrame.

read_sql(sql, con[, index_col, ...])

Чтение SQL-запроса или таблицы базы данных в DataFrame.

DataFrame.to_sql(name, con[, schema, ...])

Запись записей, хранящихся в DataFrame, в базу данных SQL.

Примечание

Функция read_sql() представляет собой удобную обёртку вокруг read_sql_table() и read_sql_query() (и для обратной совместимости) и будет делегировать конкретной функции в зависимости от предоставленного входного данных (имя таблицы базы данных или SQL-запрос). Имена таблиц не нужно заключать в кавычки, если они содержат специальные символы.

В следующем примере мы используем SQL-движок базы данных SQlite. Вы можете использовать временную базу данных SQLite, где данные хранятся в «памяти».

Для подключения с помощью SQLAlchemy используйте функцию create_engine() для создания объекта движка из URI базы данных. Вам нужно создать движок только один раз на каждую базу данных, к которой вы подключаетесь. Более подробную информацию о create_engine() и формате URI см. в примерах ниже и в документации SQLAlchemy.

In [620]: from sqlalchemy import create_engine

# Create your engine.
In [621]: engine = create_engine("sqlite:///:memory:")

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

with engine.connect() as conn, conn.begin():
    data = pd.read_sql_table("data", conn)

Предупреждение

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

Запись DataFrame

Предполагая, что следующие данные находятся в DataFrame data, мы можем вставить их в базу данных, используя to_sql().

id

Дата

Col_1

Col_2

Col_3

26

2012-10-18

X

25.7

True

42

2012-10-19

Y

-12.4

False

63

2012-10-20

Z

5.73

True

In [622]: import datetime

In [623]: c = ["id", "Date", "Col_1", "Col_2", "Col_3"]

In [624]: d = [
   .....:     (26, datetime.datetime(2010, 10, 18), "X", 27.5, True),
   .....:     (42, datetime.datetime(2010, 10, 19), "Y", -12.5, False),
   .....:     (63, datetime.datetime(2010, 10, 20), "Z", 5.73, True),
   .....: ]
   .....: 

In [625]: data = pd.DataFrame(d, columns=c)

In [626]: data
Out[626]: 
   id       Date Col_1  Col_2  Col_3
0  26 2010-10-18     X  27.50   True
1  42 2010-10-19     Y -12.50  False
2  63 2010-10-20     Z   5.73   True

In [627]: data.to_sql("data", engine)
Out[627]: 3

В некоторых базах данных запись больших DataFrame может привести к ошибкам из-за превышения ограничений размера пакета. Этому можно избежать, установив параметр chunksize при вызове to_sql. Например, следующее записывает data в базу данных партиями по 1000 строк за раз:

In [628]: data.to_sql("data_chunked", engine, chunksize=1000)
Out[628]: 3

Типы данных SQL

to_sql() попытается сопоставить ваши данные с соответствующим типом данных SQL на основе типа данных данных. Когда у вас есть столбцы типа object, pandas попытается определить тип данных.

Вы всегда можете переопределить тип по умолчанию, указав желаемый тип SQL любого из столбцов, используя аргумент dtype. Этот аргумент требует словарь, сопоставляющий имена столбцов с типами SQLAlchemy (или строки для режима кэширования sqlite3). Например, указание использования типа sqlalchemy String вместо типа по умолчанию Text для строковых столбцов:

In [629]: from sqlalchemy.types import String

In [630]: data.to_sql("data_dtype", engine, dtype={"Col_1": String})
Out[630]: 3

Примечание

Из-за ограниченной поддержки timedelta в различных вариантах баз данных столбцы с типом timedelta64 будут записаны как целые значения в наносекунды в базу данных, и будет выведено предупреждение.

Примечание

Столбцы типа category будут преобразованы в плотную форму представления, как вы получите с np.asarray(categorical) (например, для строковых категорий это даст массив строк). Из-за этого повторное чтение таблицы базы данных не генерирует категориальный.

Типы данных DateTime

Используя SQLAlchemy, to_sql() может записывать данные DateTime, которые являются локальными или с часовым поясом. Однако результирующие данные, хранящиеся в базе данных, в конечном итоге зависят от поддерживаемого типа данных для данных DateTime системы используемой базы данных.

В следующей таблице перечислены поддерживаемые типы данных для данных DateTime для некоторых распространённых баз данных. Другие диалекты баз данных могут иметь разные типы данных для данных DateTime.

База данных

Типы данных DateTime SQL

Поддержка часового пояса

SQLite

TEXT

Нет

MySQL

TIMESTAMP или DATETIME

Нет

PostgreSQL

TIMESTAMP или TIMESTAMP WITH TIME ZONE

Да

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

read_sql_table() также может считывать данные DateTime, которые являются осознанными или неосознанными в часовом поясе. При чтении TIMESTAMP WITH TIME ZONE типов pandas преобразует данные в UTC.

Метод вставки

Параметр method управляет SQL-командой вставки, используемой. Возможные значения:

  • None: Использование стандартной SQL-команды INSERT (одна на строку).

  • 'multi': Передача нескольких значений в одной команде INSERT . Она использует специальный синтаксис SQL, не поддерживаемый всеми бэкендами. Обычно это обеспечивает лучшую производительность для аналитических баз данных, таких как Presto и Redshift, но имеет худшую производительность для традиционного SQL бэкенда, если таблица содержит много столбцов. Для получения дополнительной информации ознакомьтесь с документацией SQLAlchemy .

  • функция с сигнатурой (pd_table, conn, keys, data_iter): Это можно использовать для реализации более эффективного метода вставки, основанного на функциях конкретного диалекта бэкенда.

Пример функции с использованием PostgreSQL команды COPY:

# Alternative to_sql() *method* for DBs that support COPY FROM
import csv
from io import StringIO

def psql_insert_copy(table, conn, keys, data_iter):
    """
    Execute SQL statement inserting data

    Parameters
    ----------
    table : pandas.io.sql.SQLTable
    conn : sqlalchemy.engine.Engine or sqlalchemy.engine.Connection
    keys : list of str
        Column names
    data_iter : Iterable that iterates the values to be inserted
    """
    # gets a DBAPI connection that can provide a cursor
    dbapi_conn = conn.connection
    with dbapi_conn.cursor() as cur:
        s_buf = StringIO()
        writer = csv.writer(s_buf)
        writer.writerows(data_iter)
        s_buf.seek(0)

        columns = ', '.join(['"{}"'.format(k) for k in keys])
        if table.schema:
            table_name = '{}.{}'.format(table.schema, table.name)
        else:
            table_name = table.name

        sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
            table_name, columns)
        cur.copy_expert(sql=sql, file=s_buf)

Чтение таблиц

read_sql_table() будет читать таблицу базы данных, заданную по имени таблицы и, необязательно, по подмножеству столбцов для чтения.

Примечание

Для использования read_sql_table(), необходимо установить необязательную зависимость SQLAlchemy.

In [631]: pd.read_sql_table("data", engine)
Out[631]: 
   index  id       Date Col_1  Col_2  Col_3
0      0  26 2010-10-18     X  27.50   True
1      1  42 2010-10-19     Y -12.50  False
2      2  63 2010-10-20     Z   5.73   True

Примечание

Обратите внимание, что pandas определяет типы данных столбцов из результатов запроса, а не из схемы физической базы данных. Например, предположим, что userid — это столбец целого типа данных в таблице. Тогда интуитивно select userid ... вернёт числовые серии, а select cast(userid as text) ... вернёт серии с типом данных объекта (строка). Соответственно, если результат запроса пустой, то все возвращаемые столбцы будут возвращены как серии с типом данных объекта (так как это наиболее общий случай). Если вы предполагаете, что ваш запрос иногда будет генерировать пустой результат, вы можете явно преобразовать типы данных впоследствии, чтобы обеспечить целостность типа данных.

Вы также можете указать имя столбца в качестве DataFrame индекса и указать подмножество столбцов для чтения.

In [632]: pd.read_sql_table("data", engine, index_col="id")
Out[632]: 
    index       Date Col_1  Col_2  Col_3
id                                      
26      0 2010-10-18     X  27.50   True
42      1 2010-10-19     Y -12.50  False
63      2 2010-10-20     Z   5.73   True

In [633]: pd.read_sql_table("data", engine, columns=["Col_1", "Col_2"])
Out[633]: 
  Col_1  Col_2
0     X  27.50
1     Y -12.50
2     Z   5.73

И вы можете явно принудительно преобразовать столбцы в даты:

In [634]: pd.read_sql_table("data", engine, parse_dates=["Date"])
Out[634]: 
   index  id       Date Col_1  Col_2  Col_3
0      0  26 2010-10-18     X  27.50   True
1      1  42 2010-10-19     Y -12.50  False
2      2  63 2010-10-20     Z   5.73   True

При необходимости вы можете явно указать строку формата или словарь аргументов для передачи в pandas.to_datetime():

pd.read_sql_table("data", engine, parse_dates={"Date": "%Y-%m-%d"})
pd.read_sql_table(
    "data",
    engine,
    parse_dates={"Date": {"format": "%Y-%m-%d %H:%M:%S"}},
)

Вы можете проверить, существует ли таблица, используя has_table()

Поддержка схем

Чтение из и запись в разные схемы поддерживаются через ключевое слово schema в функциях read_sql_table() и to_sql(). Однако обратите внимание, что это зависит от диалекта базы данных (sqlite не имеет схем). Например:

df.to_sql("table", engine, schema="other_schema")
pd.read_sql_table("table", engine, schema="other_schema")

Запросы

Вы можете выполнять запросы с помощью простого SQL в функции read_sql_query(). В этом случае необходимо использовать вариант SQL, соответствующий вашей базе данных. При использовании SQLAlchemy вы также можете передавать конструкции языка SQLAlchemy Expression, которые являются независимыми от базы данных.

In [635]: pd.read_sql_query("SELECT * FROM data", engine)
Out[635]: 
   index  id                        Date Col_1  Col_2  Col_3
0      0  26  2010-10-18 00:00:00.000000     X  27.50      1
1      1  42  2010-10-19 00:00:00.000000     Y -12.50      0
2      2  63  2010-10-20 00:00:00.000000     Z   5.73      1

Конечно, вы можете указать более «сложный» запрос.

In [636]: pd.read_sql_query("SELECT id, Col_1, Col_2 FROM data WHERE id = 42;", engine)
Out[636]: 
   id Col_1  Col_2
0  42     Y  -12.5

Функция read_sql_query() поддерживает аргумент chunksize. Указание этого аргумента вернёт итератор по частям результата запроса:

In [637]: df = pd.DataFrame(np.random.randn(20, 3), columns=list("abc"))

In [638]: df.to_sql("data_chunks", engine, index=False)
Out[638]: 20
In [639]: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5):
   .....:     print(chunk)
   .....: 
          a         b         c
0  0.070470  0.901320  0.937577
1  0.295770  1.420548 -0.005283
2 -1.518598 -0.730065  0.226497
3 -2.061465  0.632115  0.853619
4  2.719155  0.139018  0.214557
          a         b         c
0 -1.538924 -0.366973 -0.748801
1 -0.478137 -1.559153 -3.097759
2 -2.320335 -0.221090  0.119763
3  0.608228  1.064810 -0.780506
4 -2.736887  0.143539  1.170191
          a         b         c
0 -1.573076  0.075792 -1.722223
1 -0.774650  0.803627  0.221665
2  0.584637  0.147264  1.057825
3 -0.284136  0.912395  1.552808
4  0.189376 -0.109830  0.539341
          a         b         c
0  0.592591 -0.155407 -1.356475
1  0.833837  1.524249  1.606722
2 -0.029487 -0.051359  1.700152
3  0.921484 -0.926347  0.979818
4  0.182380 -0.186376  0.049820

Вы также можете выполнить простой запрос без создания DataFrame с помощью execute(). Это полезно для запросов, которые не возвращают значения, таких как INSERT. Это функционально эквивалентно вызову execute для объекта двигателя SQLAlchemy или объекта подключения к базе данных. Опять же, необходимо использовать вариант синтаксиса SQL, соответствующий вашей базе данных.

from pandas.io import sql

sql.execute("SELECT * FROM table_name", engine)
sql.execute(
    "INSERT INTO table_name VALUES(?, ?, ?)", engine, params=[("id", 1, 12.2, True)]
)

Примеры подключения к движку

Для подключения с помощью SQLAlchemy используйте функцию create_engine() для создания объекта двигателя из URI базы данных. Вам нужно создать двигатель только один раз на каждую базу данных, к которой вы подключаетесь.

from sqlalchemy import create_engine

engine = create_engine("postgresql://scott:tiger@localhost:5432/mydatabase")

engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")

engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")

engine = create_engine("mssql+pyodbc://mydsn")

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")

# or absolute, starting with a slash:
engine = create_engine("sqlite:////absolute/path/to/foo.db")

Дополнительную информацию см. в примерах документации SQLAlchemy documentation

Расширенные запросы SQLAlchemy

Вы можете использовать конструкции SQLAlchemy для описания вашего запроса.

Используйте sqlalchemy.text() для указания параметров запроса нейтральным по отношению к бэкенду способом

In [640]: import sqlalchemy as sa

In [641]: pd.read_sql(
   .....:     sa.text("SELECT * FROM data where Col_1=:col1"), engine, params={"col1": "X"}
   .....: )
   .....: 
Out[641]: 
   index  id                        Date Col_1  Col_2  Col_3
0      0  26  2010-10-18 00:00:00.000000     X   27.5      1

Если у вас есть описание вашей базы данных с использованием SQLAlchemy, вы можете выразить условия where с помощью выражений SQLAlchemy

In [642]: metadata = sa.MetaData()

In [643]: data_table = sa.Table(
   .....:     "data",
   .....:     metadata,
   .....:     sa.Column("index", sa.Integer),
   .....:     sa.Column("Date", sa.DateTime),
   .....:     sa.Column("Col_1", sa.String),
   .....:     sa.Column("Col_2", sa.Float),
   .....:     sa.Column("Col_3", sa.Boolean),
   .....: )
   .....: 

In [644]: pd.read_sql(sa.select([data_table]).where(data_table.c.Col_3 is True), engine)
Out[644]: 
Empty DataFrame
Columns: [index, Date, Col_1, Col_2, Col_3]
Index: []

Вы можете комбинировать выражения SQLAlchemy с параметрами, передаваемыми в read_sql() с помощью sqlalchemy.bindparam()

In [645]: import datetime as dt

In [646]: expr = sa.select([data_table]).where(data_table.c.Date > sa.bindparam("date"))

In [647]: pd.read_sql(expr, engine, params={"date": dt.datetime(2010, 10, 18)})
Out[647]: 
   index       Date Col_1  Col_2  Col_3
0      1 2010-10-19     Y -12.50  False
1      2 2010-10-20     Z   5.73   True

Падение на sqlite

Использование sqlite поддерживается без использования SQLAlchemy. Этот режим требует адаптера базы данных Python, который соблюдает Python DB-API.

Вы можете создать подключения следующим образом:

import sqlite3

con = sqlite3.connect(":memory:")

А затем выполнить следующие запросы:

data.to_sql("data", con)
pd.read_sql_query("SELECT * FROM data", con)

Google BigQuery

Предупреждение

Начиная с версии 0.20.0, pandas разделил поддержку Google BigQuery на отдельный пакет pandas-gbq. Вы можете pip install pandas-gbq , чтобы получить его.

Пакет pandas-gbq предоставляет функциональность для чтения/записи из Google BigQuery.

pandas интегрируется с этим внешним пакетом. Если pandas-gbq установлен, вы можете использовать методы pandas pd.read_gbq и DataFrame.to_gbq, которые вызовут соответствующие функции из pandas-gbq.

Полную документацию можно найти здесь.

Формат Stata

Запись в формате Stata

Метод to_stata() запишет DataFrame в файл .dta. Версия формата этого файла всегда 115 (Stata 12).

In [648]: df = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [649]: df.to_stata("stata.dta")

Файлы данных Stata имеют ограниченную поддержку типов данных; только строки длиной 244 символа или меньше, int8, int16, int32, float32 и float64 могут быть сохранены в файлах .dta. Кроме того, Stata зарезервировал определенные значения для представления пропущенных данных. Экспорт значения, отличного от пропущенного, которое выходит за пределы разрешенного диапазона в Stata для определенного типа данных, приведет к повторной типизации переменной до следующего большего размера. Например, значения int8 ограничены диапазоном от -127 до 100 в Stata, поэтому переменные со значениями выше 100 вызовут преобразование в int16. Значения nan в плавающей точке хранятся как базовый тип пропущенных данных (. в Stata).

Примечание

Экспорт пропущенных значений для целочисленных типов данных невозможен.

Писатель Stata корректно обрабатывает другие типы данных, включая int64, bool, uint8, uint16, uint32 путём приведения к наименьшему поддерживаемому типу, который может представить данные. Например, данные типа uint8 будут преобразованы к типу int8, если все значения меньше 100 (верхняя граница для значений, не являющихся пропущенными, int8 данных в Stata), или, если значения выходят за этот диапазон, переменная преобразуется в int16.

Предупреждение

Преобразование из int64 в float64 может привести к потере точности, если значения int64 больше, чем 2**53.

Предупреждение

StataWriter и to_stata() поддерживают только строки фиксированной ширины, содержащие до 244 символов, что является ограничением, наложенным форматом файла dta версии 115. Попытка записать файлы Stata dta со строками длиной более 244 символов вызывает ошибку ValueError.

Чтение из формата Stata

Функция верхнего уровня read_stata прочитает файл dta и вернёт либо DataFrame, либо StataReader, которые могут быть использованы для постепенного чтения файла.

In [650]: pd.read_stata("stata.dta")
Out[650]: 
   index         A         B
0      0 -1.690072  0.405144
1      1 -1.511309 -1.531396
2      2  0.572698 -1.106845
3      3 -1.185859  0.174564
4      4  0.603797 -1.796129
5      5 -0.791679  1.173795
6      6 -0.277710  1.859988
7      7 -0.258413  1.251808
8      8  1.443262  0.441553
9      9  1.168163 -2.054946

Указание chunksize даёт экземпляр StataReader, который может использоваться для чтения chunksize строк из файла за раз. Объект StataReader может использоваться как итератор.

In [651]: with pd.read_stata("stata.dta", chunksize=3) as reader:
   .....:     for df in reader:
   .....:         print(df.shape)
   .....: 
(3, 3)
(3, 3)
(3, 3)
(1, 3)

Для более тонкого управления используйте iterator=True и укажите chunksize в каждом вызове read().

In [652]: with pd.read_stata("stata.dta", iterator=True) as reader:
   .....:     chunk1 = reader.read(5)
   .....:     chunk2 = reader.read(5)
   .....: 

В настоящее время index извлекается в качестве столбца.

Параметр convert_categoricals указывает, следует ли читать метки значений и использовать их для создания переменной Categorical из них. Метки значений также можно получить с помощью функции value_labels, которая требует вызова read() перед использованием.

Параметр convert_missing указывает, следует ли сохранять представления пропущенных значений в Stata. Если False (по умолчанию), пропущенные значения представлены как np.nan. Если True, пропущенные значения представлены с использованием объектов StataMissingValue, а столбцы, содержащие пропущенные значения, будут иметь тип данных object.

Примечание

read_stata() и StataReader поддерживают форматы .dta 113-115 (Stata 10-12), 117 (Stata 13) и 118 (Stata 14).

Примечание

Установка preserve_dtypes=False приведет к повышению типов данных до стандартных типов данных pandas: int64 для всех целочисленных типов и float64 для данных с плавающей запятой. По умолчанию типы данных Stata сохраняются при импорте.

Данные категорий

Данные Categorical могут экспортироваться в файлы данных Stata как данные с метками значений. Экспортируемые данные состоят из кодов категорий в качестве целочисленных значений и категорий в качестве меток значений. В Stata нет явного эквивалента Categorical, и информация о том, является ли переменная упорядоченной, теряется при экспорте.

Предупреждение

Stata поддерживает только строковые метки значений, поэтому str вызывается для категорий при экспорте данных. Экспорт Categorical переменных с категориями, отличными от строк, вызывает предупреждение и может привести к потере информации, если представления категорий str не уникальны.

Данные с метками аналогичным образом можно импортировать из файлов данных Stata как Categorical переменные с использованием ключевого аргумента convert_categoricals (True по умолчанию). Ключевой аргумент order_categoricals (True по умолчанию) определяет, являются ли импортированные Categorical переменные упорядоченными.

Примечание

При импорте категориальных данных значения переменных в файле данных Stata не сохраняются, поскольку Categorical переменные всегда используют целочисленные типы данных между -1 и n-1, где n — количество категорий. Если исходные значения в файле данных Stata требуются, их можно импортировать, установив convert_categoricals=False, что импортирует исходные данные (но не метки переменных). Исходные значения можно сопоставить с импортированными категориальными данными, поскольку существует простое соответствие между исходными значениями данных Stata и кодами категорий импортированных категориальных переменных: пропущенным значениям присваивается код -1, а наименьшему исходному значению присваивается 0, второму наименьшему — 1 и так далее, пока наибольшему исходному значению не будет присвоен код n-1.

Примечание

Stata поддерживает частично помеченные ряды. Эти ряды имеют метки значений для некоторых, но не всех значений данных. Импорт частично помеченного ряда создаст Categorical со строковыми категориями для помеченных значений и числовыми категориями для значений без метки.

Форматы SAS

Функция верхнего уровня read_sas() может читать (но не записывать) файлы SAS XPORT (.xpt) и (с версии v0.18.0) SAS7BDAT (.sas7bdat).

Файлы SAS содержат только два типа значений: текстовые ASCII и значения с плавающей точкой (обычно 8 байт, но иногда усеченные). Для файлов xport автоматического преобразования типов к целым числам, датам или категориям нет. Для файлов SAS7BDAT коды формата могут позволить автоматически преобразовывать переменные дат в даты. По умолчанию весь файл читается и возвращается как DataFrame.

Укажите chunksize или используйте iterator=True для получения объектов-читателей (XportReader или SAS7BDATReader ) для постепенного чтения файла. Объекты-читатели также содержат атрибуты с дополнительной информацией о файле и его переменных.

Чтение файла SAS7BDAT:

df = pd.read_sas("sas_data.sas7bdat")

Получите итератор и прочитайте файл XPORT по 100 000 строк за раз:

def do_something(chunk):
    pass


with pd.read_sas("sas_xport.xpt", chunk=100000) as rdr:
    for chunk in rdr:
        do_something(chunk)

Спецификация формата файла xport доступна на веб-сайте SAS.

Для формата SAS7BDAT официальной документации нет.

Форматы SPSS

Новое в версии 0.25.0.

Функция верхнего уровня read_spss() может читать (но не записывать) файлы SPSS SAV (.sav) и ZSAV (.zsav).

Файлы SPSS содержат имена столбцов. По умолчанию весь файл считывается, категориальные столбцы преобразуются в pd.Categorical, и возвращается DataFrame со всеми столбцами.

Укажите параметр usecols для получения подмножества столбцов. Укажите convert_categoricals=False для предотвращения преобразования категориальных столбцов в pd.Categorical.

Чтение файла SPSS:

df = pd.read_spss("spss_data.sav")

Извлечение подмножества столбцов, содержащихся в usecols из файла SPSS, и предотвращение преобразования категориальных столбцов в pd.Categorical:

df = pd.read_spss(
    "spss_data.sav",
    usecols=["foo", "bar"],
    convert_categoricals=False,
)

Дополнительную информацию о форматах файлов SAV и ZSAV можно найти здесь.

Другие форматы файлов

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

netCDF

xarray предоставляет структуры данных, вдохновлённые pandas DataFrame, для работы с многомерными наборами данных, с уклоном в сторону формата файлов netCDF и удобным преобразованием в pandas и из него.

Соображения по производительности

Это неофициальное сравнение различных методов ввода/вывода, используя pandas 0.24.2. Время выполнения зависит от машины, и небольшие различия следует игнорировать.

In [1]: sz = 1000000
In [2]: df = pd.DataFrame({'A': np.random.randn(sz), 'B': [1] * sz})

In [3]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 2 columns):
A    1000000 non-null float64
B    1000000 non-null int64
dtypes: float64(1), int64(1)
memory usage: 15.3 MB

Ниже будут использоваться следующие тестовые функции для сравнения производительности нескольких методов ввода/вывода:

import numpy as np

import os

sz = 1000000
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})

sz = 1000000
np.random.seed(42)
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})


def test_sql_write(df):
    if os.path.exists("test.sql"):
        os.remove("test.sql")
    sql_db = sqlite3.connect("test.sql")
    df.to_sql(name="test_table", con=sql_db)
    sql_db.close()


def test_sql_read():
    sql_db = sqlite3.connect("test.sql")
    pd.read_sql_query("select * from test_table", sql_db)
    sql_db.close()


def test_hdf_fixed_write(df):
    df.to_hdf("test_fixed.hdf", "test", mode="w")


def test_hdf_fixed_read():
    pd.read_hdf("test_fixed.hdf", "test")


def test_hdf_fixed_write_compress(df):
    df.to_hdf("test_fixed_compress.hdf", "test", mode="w", complib="blosc")


def test_hdf_fixed_read_compress():
    pd.read_hdf("test_fixed_compress.hdf", "test")


def test_hdf_table_write(df):
    df.to_hdf("test_table.hdf", "test", mode="w", format="table")


def test_hdf_table_read():
    pd.read_hdf("test_table.hdf", "test")


def test_hdf_table_write_compress(df):
    df.to_hdf(
        "test_table_compress.hdf", "test", mode="w", complib="blosc", format="table"
    )


def test_hdf_table_read_compress():
    pd.read_hdf("test_table_compress.hdf", "test")


def test_csv_write(df):
    df.to_csv("test.csv", mode="w")


def test_csv_read():
    pd.read_csv("test.csv", index_col=0)


def test_feather_write(df):
    df.to_feather("test.feather")


def test_feather_read():
    pd.read_feather("test.feather")


def test_pickle_write(df):
    df.to_pickle("test.pkl")


def test_pickle_read():
    pd.read_pickle("test.pkl")


def test_pickle_write_compress(df):
    df.to_pickle("test.pkl.compress", compression="xz")


def test_pickle_read_compress():
    pd.read_pickle("test.pkl.compress", compression="xz")


def test_parquet_write(df):
    df.to_parquet("test.parquet")


def test_parquet_read():
    pd.read_parquet("test.parquet")

При записи, три самые быстрые функции — это test_feather_write, test_hdf_fixed_write и test_hdf_fixed_write_compress.

In [4]: %timeit test_sql_write(df)
3.29 s ± 43.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit test_hdf_fixed_write(df)
19.4 ms ± 560 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [6]: %timeit test_hdf_fixed_write_compress(df)
19.6 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [7]: %timeit test_hdf_table_write(df)
449 ms ± 5.61 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [8]: %timeit test_hdf_table_write_compress(df)
448 ms ± 11.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [9]: %timeit test_csv_write(df)
3.66 s ± 26.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [10]: %timeit test_feather_write(df)
9.75 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [11]: %timeit test_pickle_write(df)
30.1 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [12]: %timeit test_pickle_write_compress(df)
4.29 s ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [13]: %timeit test_parquet_write(df)
67.6 ms ± 706 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

При чтении, три самые быстрые функции — это test_feather_read, test_pickle_read и test_hdf_fixed_read.

In [14]: %timeit test_sql_read()
1.77 s ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [15]: %timeit test_hdf_fixed_read()
19.4 ms ± 436 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [16]: %timeit test_hdf_fixed_read_compress()
19.5 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [17]: %timeit test_hdf_table_read()
38.6 ms ± 857 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [18]: %timeit test_hdf_table_read_compress()
38.8 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [19]: %timeit test_csv_read()
452 ms ± 9.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [20]: %timeit test_feather_read()
12.4 ms ± 99.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [21]: %timeit test_pickle_read()
18.4 ms ± 191 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [22]: %timeit test_pickle_read_compress()
915 ms ± 7.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [23]: %timeit test_parquet_read()
24.4 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Файлы test.pkl.compress, test.parquet и test.feather заняли наименьшее место на диске (в байтах).

29519500 Oct 10 06:45 test.csv
16000248 Oct 10 06:45 test.feather
8281983  Oct 10 06:49 test.parquet
16000857 Oct 10 06:47 test.pkl
7552144  Oct 10 06:48 test.pkl.compress
34816000 Oct 10 06:42 test.sql
24009288 Oct 10 06:43 test_fixed.hdf
24009288 Oct 10 06:43 test_fixed_compress.hdf
24458940 Oct 10 06:44 test_table.hdf
24458940 Oct 10 06:44 test_table_compress.hdf

© 2008–2022, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/1.5.0/user_guide/io.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API