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

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

Тип формата

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

Reader

Writer

текст

CSV

read_csv

to_csv

текст

Файл с текстом фиксированной ширины

read_fwf

NA

текст

JSON

read_json

to_json

текст

HTML

read_html

to_html

текст

LaTeX

Styler.to_latex

NA

текст

XML

read_xml

to_xml

текст

Локальный буфер обмена

read_clipboard

to_clipboard

бинарный

MS Excel

read_excel

to_excel

бинарный

OpenDocument

read_excel

NA

бинарный

Формат 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

NA

бинарный

SPSS

read_spss

NA

бинарный

Формат Python Pickle

read_pickle

to_pickle

SQL

SQL

read_sql

to_sql

SQL

Google BigQuery;:ref:read_gbq;:ref:to_gbq

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

Примечание

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

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

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

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

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

Базовый#

filepath_or_bufferразличные

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

sepstr, по умолчанию ',' для read_csv(), \t для read_table()

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

разделительstr, по умолчанию None

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

delim_whitespaceboolean, по умолчанию False

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

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

headerint или список int, по умолчанию 'infer'

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

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

namesмассивоподобный, по умолчанию None

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

index_colint, str, последовательность int / str, или False, опционально, по умолчанию None

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

Примечание

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

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

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

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

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

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

Порядок элементов игнорируется, поэтому usecols=[0, 1] то же самое, что [1, 0]. Для создания DataFrame из data с сохранением порядка элементов используйте pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']] для столбцов в ['foo', 'bar'] порядок или pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']] для ['bar', 'foo'] порядок.

Если вызываемый объект, вызываемая функция будет оценена по именам столбцов, возвращая имена, где вызываемая функция оценивается как True:

In [1]: import pandas as pd

In [2]: from io import StringIO

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

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

In [5]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["COL1", "COL3"])
Out[5]: 
  col1  col3
0    a     1
1    a     2
2    c     3

Использование этого параметра значительно ускоряет время парсинга и снижает использование памяти при использовании движка c. Python-движок сначала загружает данные, а затем решает, какие столбцы удалить.

Общая конфигурация парсинга#

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

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

Добавлено в версии 1.5.0: Добавлена поддержка defaultdict. Укажите defaultdict в качестве входных данных, где значение по умолчанию определяет dtype столбцов, которые не указаны явно.

dtype_backend{"numpy_nullable", "pyarrow"}, по умолчанию DataFrame на основе NumPy

Какой dtype_backend использовать, например, должен ли DataFrame иметь массивы NumPy, нулевые типы данных используются для всех типов данных, которые имеют нулевую реализацию, когда установлено "numpy_nullable", pyarrow используется для всех типов данных, если установлено "pyarrow".

Параметры dtype_backends все еще являются экспериментальными.

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

движок{'c', 'python', 'pyarrow'}

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

Добавлено в версии 1.4.0: Движок "pyarrow" был добавлен как экспериментальный движок, и некоторые функции не поддерживаются или могут работать некорректно с этим движком.

конвертерыdict, по умолчанию None

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

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

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

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

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

skipinitialspaceboolean, по умолчанию False

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

skiprowsсписокоподобный или целое число, по умолчанию None

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

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

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

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

In [8]: pd.read_csv(StringIO(data), skiprows=lambda x: x % 2 != 0)
Out[8]: 
  col1 col2  col3
0    a    b     2
skipfooterint, по умолчанию 0

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

nrowsint, по умолчанию None

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

low_memoryboolean, по умолчанию True

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

memory_mapboolean, по умолчанию False

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

Обработка NA и пропущенных данных#

na_valuesскаляр, строка, список или словарь, по умолчанию None

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

keep_default_naboolean, по умолчанию 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_filterboolean, по умолчанию True

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

verboseboolean, по умолчанию False

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

skip_blank_linesboolean, по умолчанию True

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

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

parse_datesboolean или список int или имена или список списков или dict, по умолчанию False.
  • Если True -> попробовать разобрать индекс.

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

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

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

Примечание

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

infer_datetime_formatboolean, по умолчанию False

Если True игнорируется (

Устарело с версии 2.0.0: Строгая версия этого аргумента теперь используется по умолчанию, передача его не имеет эффекта.

keep_date_colboolean, по умолчанию False

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

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

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

Устарело с версии 2.0.0: Используйте date_format вместо этого, или считать как object и затем применить to_datetime() по мере необходимости.

date_formatstr или dict столбец -> формат, по умолчанию None

Если используется вместе с parse_dates, будет анализировать даты в соответствии с этим форматом. Для более сложных случаев, пожалуйста, читайте как object и затем применить to_datetime() по мере необходимости.

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

dayfirstboolean, по умолчанию False

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

cache_datesлогический, по умолчанию True

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

Итерация#

итераторboolean, по умолчанию False

Возвращает TextFileReader объект для итерации или получения блоков с get_chunk().

chunksizeint, по умолчанию None

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

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

compression{'infer', 'gzip', 'bz2', 'zip', 'xz', 'zstd', None, dict}, по умолчанию '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.2.0: Предыдущие версии передавали записи словаря для 'gzip' в gzip.open.

тысячиstr, по умолчанию None

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

десятичныйstr, по умолчанию '.'

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

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

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

lineterminatorstr (длина 1), по умолчанию None

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

quotecharstr (длина 1)

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

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

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

двойные кавычкиboolean, по умолчанию True

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

escapecharstr (длина 1), по умолчанию None

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

комментарийstr, по умолчанию None

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

кодировкаstr, по умолчанию None

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

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

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

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

on_bad_lines('error', 'warn', 'skip'), по умолчанию 'error'

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

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

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

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

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

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

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

In [9]: import numpy as np

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

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

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

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

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

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

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

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

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

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

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

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

In [20]: df["col_1"].apply(type).value_counts()
Out[20]: 
col_1
    4
Name: count, dtype: int64

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

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

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

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

In [24]: df2["col_1"].apply(type).value_counts()
Out[24]: 
col_1
    4
Name: count, dtype: int64

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

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

Примечание

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

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

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

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

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

In [29]: mixed_df["col_1"].apply(type).value_counts()
Out[29]: 
col_1
    737858
    262144
Name: count, dtype: int64

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

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

Установка dtype_backend="numpy_nullable" приведет к nullable типам данных для каждого столбца.

In [31]: data = """a,b,c,d,e,f,g,h,i,j
   ....: 1,2.5,True,a,,,,,12-31-2019,
   ....: 3,4.5,False,b,6,7.5,True,a,12-31-2019,
   ....: """
   ....: 

In [32]: df = pd.read_csv(StringIO(data), dtype_backend="numpy_nullable", parse_dates=["i"])

In [33]: df
Out[33]: 
   a    b      c  d     e     f     g     h          i     j
0  1  2.5   True  a         2019-12-31  
1  3  4.5  False  b     6   7.5  True     a 2019-12-31  

In [34]: df.dtypes
Out[34]: 
a             Int64
b           Float64
c           boolean
d    string[python]
e             Int64
f           Float64
g           boolean
h    string[python]
i    datetime64[ns]
j             Int64
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 чей categories являются уникальными значениями, наблюдаемыми в данных. Для большего контроля над категориями и порядком создайте 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', результирующие категории всегда будут анализироваться как строки (тип object). Если категории числовые, их можно преобразовать с помощью 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.

Парсинг дублирующихся имен#

Если файл или заголовок содержат повторяющиеся имена, 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

Дублирующихся данных больше нет, потому что дублирующиеся столбцы 'X', …, 'X' становятся 'X', 'X.1', …, 'X.N'.

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

The 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

The 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]: data = (
   ....:     "ID,level,category\n"
   ....:     "Patient1,123000,x # really unpleasant\n"
   ....:     "Patient2,23000,y # wouldn't take his medicine\n"
   ....:     "Patient3,1234018,z # awesome"
   ....: )
   ....: 

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

In [81]: 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 [82]: df = pd.read_csv("tmp.csv")

In [83]: df
Out[83]: 
         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 [84]: df = pd.read_csv("tmp.csv", comment="#")

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

Работа с Unicode данными#

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

In [86]: from io import BytesIO

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

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

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

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

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

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

Столбцы индекса и завершающие разделители#

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

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

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

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

Обычно этого поведения можно достичь с помощью index_col опция.

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

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

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

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

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

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

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

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

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

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

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

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

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

Самый простой случай — просто передать parse_dates=True:

In [104]: 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 [105]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True)

In [106]: df
Out[106]: 
            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 [107]: df.index
Out[107]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)

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

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

In [108]: 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 [109]: with open("tmp.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

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

In [111]: df
Out[111]: 
                  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 [112]: df = pd.read_csv(
   .....:     "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True
   .....: )
   .....: 

In [113]: df
Out[113]: 
                  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 [114]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

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

In [116]: df
Out[116]: 
              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 [117]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}

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

In [119]: df
Out[119]: 
                                 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

Примечание

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

Примечание

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

Устарело с версии 2.2.0: Объединение столбцов дат внутри read_csv устарело. Используйте pd.to_datetime на соответствующих результирующих столбцах.

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

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

  1. Если вы знаете формат, используйте date_format, например: date_format="%d/%m/%Y" или date_format={column_name: "%d/%m/%Y"}.

  2. Если у вас разные форматы для разных столбцов или вы хотите передать дополнительные параметры (такие как utc) в to_datetime, тогда вам следует прочитать ваши данные как object dtype, а затем используйте to_datetime.

Разбор CSV со смешанными часовыми поясами#

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

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))

In [122]: df["a"] = pd.to_datetime(df["a"], utc=True)

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

Вывод формата даты и времени#

Вот несколько примеров строк даты и времени, которые могут быть распознаны (все представляют 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/December/2011 00:00:00”

Обратите внимание, что определение формата чувствительно к dayfirst. С dayfirst=True, он предположит, что "01/12/2011" — это 1 декабря. С dayfirst=False (по умолчанию) он предположит, что "01/12/2011" — это 12 января.

Если попытаться разобрать столбец строк с датами, pandas попытается угадать формат по первому не-NaN элементу, а затем разберёт остальную часть столбца с этим форматом. Если pandas не удаётся угадать формат (например, если ваша первая строка — '01 December US/Pacific 2000'), тогда будет выдано предупреждение, и каждая строка будет проанализирована индивидуально с помощью dateutil.parser.parse. Самый безопасный способ разбора дат — явно установить format=.

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

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

В случае, если у вас смешанные форматы даты и времени в одном столбце, вы можете передать format='mixed'

In [126]: data = StringIO("date\n12 Jan 2000\n2000-01-13\n")

In [127]: df = pd.read_csv(data)

In [128]: df['date'] = pd.to_datetime(df['date'], format='mixed')

In [129]: df
Out[129]: 
        date
0 2000-01-12
1 2000-01-13

или, если все ваши форматы даты и времени соответствуют ISO8601 (возможно, не одинаково отформатированы):

In [130]: data = StringIO("date\n2020-01-01\n2020-01-01 03:00\n")

In [131]: df = pd.read_csv(data)

In [132]: df['date'] = pd.to_datetime(df['date'], format='ISO8601')

In [133]: df
Out[133]: 
                 date
0 2020-01-01 00:00:00
1 2020-01-01 03:00:00

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

Хотя американские форматы дат обычно имеют вид ММ/ДД/ГГГГ, многие международные форматы используют ДД/ММ/ГГГГ. Для удобства, dayfirst предоставлено ключевое слово:

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

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

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

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

In [138]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0])
Out[138]: 
        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 [139]: import io

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

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

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

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

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

In [143]: val = "0.3066101993807095471566981359501369297504425048828125"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

значения NA#

Чтобы контролировать, какие значения парсятся как пропущенные (которые обозначаются NaN), укажите строку в na_values. Если вы указываете список строк, то все значения в нем считаются пропущенными. Если вы указываете число (a 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', 'NULL', 'null', 'NaN', '-NaN', 'nan', '-nan', 'None', ''].

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

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.

Логические значения#

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

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

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

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

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

Обработка "плохих" строк#

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

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

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

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1026, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, 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, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
   1013 kwds_defaults = _refine_defaults_read(
   1014     dialect,
   1015     delimiter,
   (...)
   1022     dtype_backend=dtype_backend,
   1023 )
   1024 kwds.update(kwds_defaults)
-> 1026 return _read(filepath_or_buffer, kwds)

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

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1923, in TextFileReader.read(self, nrows)
   1916 nrows = validate_integer("nrows", nrows)
   1917 try:
   1918     # error: "ParserBase" has no attribute "read"
   1919     (
   1920         index,
   1921         columns,
   1922         col_dict,
-> 1923     ) = self._engine.read(  # type: ignore[attr-defined]
   1924         nrows
   1925     )
   1926 except Exception:
   1927     self.close()

File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:234, in CParserWrapper.read(self, nrows)
    232 try:
    233     if self.low_memory:
--> 234         chunks = self._reader.read_low_memory(nrows)
    235         # destructive to chunks
    236         data = _concatenate_chunks(chunks)

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

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

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

File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:891, in pandas._libs.parsers.TextReader._check_tokenize_status()

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

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

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

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

In [163]: pd.read_csv(StringIO(data), on_bad_lines="skip")
Out[163]: 
   a  b   c
0  1  2   3
1  8  9  10

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

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

In [164]: external_list = []

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

In [166]: external_list
Out[166]: []

Примечание

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

In [167]: bad_lines_func = lambda line: print(line)

In [168]: data = 'name,type\nname a,a is of type a\nname b,"b\" is of type b"'

In [169]: data
Out[169]: 'name,type\nname a,a is of type a\nname b,"b" is of type b"'

In [170]: pd.read_csv(StringIO(data), on_bad_lines=bad_lines_func, engine="python")
Out[170]: 
     name            type
0  name a  a is of type a

Строка не была обработана в этом случае, так как "плохая строка" здесь вызвана escape-символом.

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

In [171]: pd.read_csv(StringIO(data), usecols=[0, 1, 2])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[171], line 1
----> 1 pd.read_csv(StringIO(data), usecols=[0, 1, 2])

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1026, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, 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, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
   1013 kwds_defaults = _refine_defaults_read(
   1014     dialect,
   1015     delimiter,
   (...)
   1022     dtype_backend=dtype_backend,
   1023 )
   1024 kwds.update(kwds_defaults)
-> 1026 return _read(filepath_or_buffer, kwds)

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:620, in _read(filepath_or_buffer, kwds)
    617 _validate_names(kwds.get("names", None))
    619 # Create the parser.
--> 620 parser = TextFileReader(filepath_or_buffer, **kwds)
    622 if chunksize or iterator:
    623     return parser

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1620, in TextFileReader.__init__(self, f, engine, **kwds)
   1617     self.options["has_index_names"] = kwds["has_index_names"]
   1619 self.handles: IOHandles | None = None
-> 1620 self._engine = self._make_engine(f, self.engine)

File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1898, in TextFileReader._make_engine(self, f, engine)
   1895     raise ValueError(msg)
   1897 try:
-> 1898     return mapping[engine](f, **self.options)
   1899 except Exception:
   1900     if self.handles is not None:

File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:155, in CParserWrapper.__init__(self, src, **kwds)
    152     # error: Cannot determine type of 'names'
    153     if len(self.names) < len(usecols):  # type: ignore[has-type]
    154         # error: Cannot determine type of 'names'
--> 155         self._validate_usecols_names(
    156             usecols,
    157             self.names,  # type: ignore[has-type]
    158         )
    160 # error: Cannot determine type of 'names'
    161 self._validate_parse_dates_presence(self.names)  # type: ignore[has-type]

File ~/work/pandas/pandas/pandas/io/parsers/base_parser.py:988, in ParserBase._validate_usecols_names(self, usecols, names)
    986 missing = [c for c in usecols if c not in names]
    987 if len(missing) > 0:
--> 988     raise ValueError(
    989         f"Usecols do not match columns, columns expected but not found: "
    990         f"{missing}"
    991     )
    993 return usecols

ValueError: Usecols do not match columns, columns expected but not found: [0, 1, 2]

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

In [172]: pd.read_csv(StringIO(data), names=['a', 'b', 'c', 'd'])
Out[172]: 
        a                b   c   d
0    name             type NaN NaN
1  name a   a is of type a NaN NaN
2  name b  b is of type b" NaN NaN

Диалект#

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

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

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

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

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

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

In [175]: import csv

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

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

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

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

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

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

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

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

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

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

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

Кавычки и экранирующие символы#

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

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

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

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

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

В то время как read_csv() читает разделённые данные, то read_fwf() pandas.tseries.offsets.WeekOfMonth.is_anchored read_fwf в основном такие же, как read_csv с двумя дополнительными параметрами и другим использованием delimiter параметр:

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

  • widths: Список ширины полей, который можно использовать вместо 'colspecs', если интервалы непрерывны.

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

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

In [187]: 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 [188]: with open("bar.csv", "w") as f:
   .....:     f.write(data1)
   .....: 

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

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

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

In [191]: df
Out[191]: 
                 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 [192]: widths = [6, 14, 13, 10]

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

In [194]: df
Out[194]: 
        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 [195]: df = pd.read_fwf("bar.csv", header=None, index_col=0)

In [196]: df
Out[196]: 
                 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 [197]: pd.read_fwf("bar.csv", header=None, index_col=0).dtypes
Out[197]: 
1    float64
2    float64
3    float64
dtype: object

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

Индексы#

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

In [211]: mi_idx = pd.MultiIndex.from_arrays([[1, 2, 3, 4], list("abcd")], names=list("ab"))

In [212]: mi_col = pd.MultiIndex.from_arrays([[1, 2], list("ab")], names=list("cd"))

In [213]: df = pd.DataFrame(np.ones((4, 2)), index=mi_idx, columns=mi_col)

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

In [215]: print(open("mi.csv").read())
c,,1,2
d,,a,b
a,b,,
1,a,1.0,1.0
2,b,1.0,1.0
3,c,1.0,1.0
4,d,1.0,1.0


In [216]: pd.read_csv("mi.csv", header=[0, 1, 2, 3], index_col=[0, 1])
Out[216]: 
c                    1                  2
d                    a                  b
a   Unnamed: 2_level_2 Unnamed: 3_level_2
1                  1.0                1.0
2 b                1.0                1.0
3 c                1.0                1.0
4 d                1.0                1.0

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

In [217]: 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 [218]: 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 [219]: with open("mi2.csv", "w") as fh:
   .....:     fh.write(data)
   .....: 

In [220]: pd.read_csv("mi2.csv", header=[0, 1], index_col=0)
Out[220]: 
     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 в индексе столбцов будет потерян.

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

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

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

In [222]: df.to_csv("tmp2.csv", sep=":", index=False)

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

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

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

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

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

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

In [225]: df.to_csv("tmp.csv", index=False)

In [226]: table = pd.read_csv("tmp.csv")

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

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

In [228]: with pd.read_csv("tmp.csv", chunksize=4) as reader:
   .....:     print(reader)
   .....:     for chunk in reader:
   .....:         print(chunk)
   .....: 

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

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

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

In [229]: with pd.read_csv("tmp.csv", iterator=True) as reader:
   .....:     print(reader.get_chunk(5))
   .....: 
          0         1         2         3
0 -1.294524  0.413738  0.276662 -0.472035
1 -0.013960 -0.362543 -0.006154 -0.923061
2  0.895717  0.805244 -1.206412  2.565646
3  1.431256  1.340309 -1.170299 -0.226169
4  0.410835  0.813850  0.132003 -0.827317

Указание парсера#

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

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

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

  • sep отличный от одного символа (например, разделители regex)

  • skipfooter

  • sep=None с delim_whitespace=False

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

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

  • float_precision

  • chunksize

  • comment

  • nrows

  • thousands

  • memory_map

  • dialect

  • 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 распределение.

Вы также можете передавать параметры напрямую драйверу бэкенда. Поскольку fsspec не использует AWS_S3_HOST переменную окружения, мы можем напрямую определить словарь, содержащий endpoint_url, и передать объект в параметр storage option:

storage_options = {"client_kwargs": {"endpoint_url": "http://127.0.0.1:5555"}}}
df = pd.read_json("s3://pandas-test/test-1", storage_options=storage_options)

Дополнительные примеры конфигураций и документацию можно найти на Документация S3Fs.

Если вы делаете не имея учетные данные 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" реализации, а не для реализации кэширования. Обратите внимание, что это кэширует во временный каталог только на время сеанса, но вы также можете указать постоянное хранилище.

Запись данных#

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

The 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

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

The 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, будет печатать заголовки колонок выровненными по левому или правому краю

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

JSON#

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

Запись JSON#

A 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

    словарь вида {index -> [index]; columns -> [columns]; data -> [values]}

    records

    список вида [{column -> value}; … ]

    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 ориентация, затем будет записывать каждую запись в строку как json.

  • mode : строка, режим записи при записи в путь. 'w' для записи, 'a' для добавления. По умолчанию 'w'

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

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

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

In [232]: json
Out[232]: '{"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}}'

Параметры ориентации#

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

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

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

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

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

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

In [237]: dfjo.to_json(orient="columns")
Out[237]: '{"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 [238]: dfjo.to_json(orient="index")
Out[238]: '{"x":{"A":1,"B":4,"C":7},"y":{"A":2,"B":5,"C":8},"z":{"A":3,"B":6,"C":9}}'

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

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

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

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

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

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

# Not available for Series

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

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

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

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

Примечание

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

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

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

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

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

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

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

In [249]: json
Out[249]: '{"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 [250]: json = dfd.to_json(date_format="iso", date_unit="us")

In [251]: json
Out[251]: '{"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}}'

Метки времени эпохи, в секундах:

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

In [253]: json
Out[253]: '{"date":{"0":1,"1":1,"2":1,"3":1,"4":1},"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 [254]: dfj2 = dfj.copy()

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

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

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

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

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

In [260]: 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":1356,"1357084800000":1356,"1357171200000":1356,"1357257600000":1356,"1357344000000":1356},"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 метод и вызвать его. A 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 [261]: pd.DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json(default_handler=str)
Out[261]: '{"0":{"0":"(1+0j)","1":"(2+0j)","2":"(1+2j)"}}'

Чтение JSON#

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

  • filepath_or_buffer : a VALID Строка JSON или файловый дескриптор / StringIO. Строка может быть URL. Допустимые схемы URL включают http, ftp, S3 и file. Для URL файлов ожидается хост. Например, локальный файл может быть file://localhost/path/to/table.json

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

  • orient :

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

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

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

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

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

    split

    словарь вида {index -> [index]; columns -> [columns]; data -> [values]}

    records

    список вида [{column -> value} …]

    index

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

    columns

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

    values

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

    table

    соблюдая JSON Схема таблицы

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

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

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

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

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

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

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

  • encoding : Кодировка, используемая для декодирования байтов py3.

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

  • engine: Либо "ujson", встроенный парсер JSON, или "pyarrow" который передается в pyarrow pyarrow.json.read_json. "pyarrow" доступно только когда lines=True

Парсер вызовет один из 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 [262]: from io import StringIO

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

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

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

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

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

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

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

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

In [267]: from io import StringIO

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

In [269]: si
Out[269]: 
     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 [270]: si.index
Out[270]: Index(['0', '1', '2', '3'], dtype='object')

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

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

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

In [274]: sij
Out[274]: 
   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 [275]: sij.index
Out[275]: Index(['0', '1', '2', '3'], dtype='object')

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

Даты, записанные в наносекундах, должны считываться в наносекундах:

In [277]: from io import StringIO

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

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

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

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

In [282]: dfju
Out[282]: 
                   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 [283]: dfju = pd.read_json(StringIO(json), date_unit="ns")

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

Установкой dtype_backend аргумент позволяет управлять типами данных по умолчанию для результирующего DataFrame.

In [285]: data = (
   .....:  '{"a":{"0":1,"1":3},"b":{"0":2.5,"1":4.5},"c":{"0":true,"1":false},"d":{"0":"a","1":"b"},'
   .....:  '"e":{"0":null,"1":6.0},"f":{"0":null,"1":7.5},"g":{"0":null,"1":true},"h":{"0":null,"1":"a"},'
   .....:  '"i":{"0":"12-31-2019","1":"12-31-2019"},"j":{"0":null,"1":null}}'
   .....: )
   .....: 

In [286]: df = pd.read_json(StringIO(data), dtype_backend="pyarrow")

In [287]: df
Out[287]: 
   a    b      c  d     e     f     g     h           i     j
0  1  2.5   True  a          12-31-2019  None
1  3  4.5  False  b     6   7.5  True     a  12-31-2019  None

In [288]: df.dtypes
Out[288]: 
a     int64[pyarrow]
b    double[pyarrow]
c      bool[pyarrow]
d    string[pyarrow]
e     int64[pyarrow]
f    double[pyarrow]
g      bool[pyarrow]
h    string[pyarrow]
i    string[pyarrow]
j      null[pyarrow]
dtype: object

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

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

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

In [290]: pd.json_normalize(data)
Out[290]: 
    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 [291]: 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 [292]: pd.json_normalize(data, "county", ["state", "shortname", ["info", "governor"]])
Out[292]: 
         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 следующий фрагмент нормализует до 1-го уровня вложенности предоставленного словаря.

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

In [294]: pd.json_normalize(data, max_level=1)
Out[294]: 
  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 [295]: from io import StringIO

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

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

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

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

# reader is an iterator that returns ``chunksize`` lines each iteration
In [300]: 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 с ограничением строк также можно прочитать с помощью ридера pyarrow, указав engine="pyarrow".

In [301]: from io import BytesIO

In [302]: df = pd.read_json(BytesIO(jsonl.encode()), lines=True, engine="pyarrow")

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

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

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

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

In [304]: 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 [305]: df
Out[305]: 
     A  B          C
idx                 
0    1  a 2016-01-01
1    2  b 2016-01-02
2    3  c 2016-01-03

In [306]: df.to_json(orient="table", date_format="iso")
Out[306]: '{"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"}]}'

The schema поле содержит fields ключ, который сам содержит список пар имя столбца - тип, включая Index или MultiIndex (см. ниже список типов). schema поле также содержит primaryKey поля, если (Multi)index уникален.

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

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

тип pandas

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

int64

целое число

float64

число

bool

логический

datetime64[ns]

datetime

timedelta64[ns]

длительность

категориальный

любой

object

str

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

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

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

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

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

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

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

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

    In [318]: s_multi = pd.Series(1, index=pd.MultiIndex.from_product([("a", "b"), (0, 1)]))
    
    In [319]: build_table_schema(s_multi)
    Out[319]: 
    {'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_ используется.

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

In [320]: 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 [321]: df
Out[321]: 
     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 [322]: df.dtypes
Out[322]: 
foo             int64
bar            object
baz    datetime64[ns]
qux          category
dtype: object

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

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

In [325]: new_df
Out[325]: 
     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 [326]: new_df.dtypes
Out[326]: 
foo             int64
bar            object
baz    datetime64[ns]
qux          category
dtype: object

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

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

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

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

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

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

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

HTML#

Чтение HTML-контента#

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

Мы настоятельно рекомендуем вам прочитать Особенности парсинга HTML таблиц ниже относительно проблем с парсерами BeautifulSoup4/html5lib/lxml.

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

Примечание

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

Прочитать URL без опций:

In [320]: url = "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 с передачей заголовков вместе с HTTP-запросом:

In [322]: url = 'https://www.sump.org/notes/request/' # HTTP request reflector
In [323]: pd.read_html(url)
Out[323]:
[                   0                    1
 0     Remote Socket:  51.15.105.256:51760
 1  Protocol Version:             HTTP/1.1
 2    Request Method:                  GET
 3       Request URI:      /notes/request/
 4     Request Query:                  NaN,
 0   Accept-Encoding:             identity
 1              Host:         www.sump.org
 2        User-Agent:    Python-urllib/3.8
 3        Connection:                close]
In [324]: headers = {
In [325]:    'User-Agent':'Mozilla Firefox v14.0',
In [326]:    'Accept':'application/json',
In [327]:    'Connection':'keep-alive',
In [328]:    'Auth':'Bearer 2*/f3+fe68df*4'
In [329]: }
In [340]: pd.read_html(url, storage_options=headers)
Out[340]:
[                   0                    1
 0     Remote Socket:  51.15.105.256:51760
 1  Protocol Version:             HTTP/1.1
 2    Request Method:                  GET
 3       Request URI:      /notes/request/
 4     Request Query:                  NaN,
 0        User-Agent: Mozilla Firefox v14.0
 1    AcceptEncoding:   gzip,  deflate,  br
 2            Accept:      application/json
 3        Connection:             keep-alive
 4              Auth:  Bearer 2*/f3+fe68df*4]

Примечание

Мы видим выше, что переданные заголовки отражены в HTTP-запросе.

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

In [331]: html_str = """
   .....:             .....:                 .....:                     .....:                     .....:                     .....:                 .....:                 .....:                     .....:                     .....:                     .....:                 .....:          
A B C
a b c
.....: """ .....: In [332]: with open("tmp.html", "w") as f: .....: f.write(html_str) .....: In [333]: df = pd.read_html("tmp.html") In [334]: df[0] Out[334]: A B C 0 a b c

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

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

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

Примечание

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

Прочитать URL и найти таблицу, содержащую определённый текст:

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

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

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?oldid=899173761"
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)

The 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"])

Nathan Goldbaum + extract_links="all".

In [337]: html_table = """
   .....:    .....:      .....:        .....:      .....:      .....:        .....:      .....: 
GitHub
pandas
.....: """ .....: In [338]: df = pd.read_html( .....: StringIO(html_table), .....: extract_links="all" .....: )[0] .....: In [339]: df Out[339]: (GitHub, None) 0 (pandas, https://github.com/pandas-dev/pandas) In [340]: df[("GitHub", None)] Out[340]: 0 (pandas, https://github.com/pandas-dev/pandas) Name: (GitHub, None), dtype: object In [341]: df[("GitHub", None)].str[1] Out[341]: 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 показаны здесь для краткости. См. DataFrame.to_html() для полного набора опций.

Примечание

В среде с поддержкой HTML-рендеринга, такой как Jupyter Notebook, display(HTML(...))` отобразит необработанный HTML в окружении.

In [342]: from IPython.display import display, HTML

In [343]: df = pd.DataFrame(np.random.randn(2, 2))

In [344]: df
Out[344]: 
          0         1
0 -0.345352  1.314232
1  0.690579  0.995761

In [345]: html = df.to_html()

In [346]: print(html)  # raw html
                                                                                      
0 1
0 -0.345352 1.314232
1 0.690579 0.995761
In [347]: display(HTML(html))

The columns аргумент ограничит отображаемые столбцы:

In [348]: html = df.to_html(columns=[0])

In [349]: print(html)
                                                                    
0
0 -0.345352
1 0.690579
In [350]: display(HTML(html))

float_format принимает вызываемый объект Python для управления точностью значений с плавающей точкой:

In [351]: html = df.to_html(float_format="{0:.10f}".format)

In [352]: print(html)
                                                                                      
0 1
0 -0.3453521949 1.3142323796
1 0.6905793352 0.9957609037
In [353]: display(HTML(html))

bold_rows будет делать метки строк жирными по умолчанию, но это можно отключить:

In [354]: html = df.to_html(bold_rows=False)

In [355]: print(html)
                                                                                      
0 1
0 -0.345352 1.314232
1 0.690579 0.995761
In [356]: display(HTML(html))

The classes аргумент предоставляет возможность задать результирующей HTML-таблице CSS-классы. Обратите внимание, что эти классы добавлен к существующему 'dataframe' класс.

In [357]: print(df.to_html(classes=["awesome_table_class", "even_more_awesome_class"]))
                                                                                      
0 1
0 -0.345352 1.314232
1 0.690579 0.995761

The render_links аргумент предоставляет возможность добавлять гиперссылки в ячейки, содержащие URL-адреса.

In [358]: url_df = pd.DataFrame(
   .....:     {
   .....:         "name": ["Python", "pandas"],
   .....:         "url": ["https://www.python.org/", "https://pandas.pydata.org"],
   .....:     }
   .....: )
   .....: 

In [359]: html = url_df.to_html(render_links=True)

In [360]: print(html)
                                                                                      
name url
0 Python https://www.python.org/
1 pandas https://pandas.pydata.org
In [361]: display(HTML(html))

Наконец, escape аргумент позволяет контролировать, экранируются ли символы "<", ">" и "&" в результирующем HTML (по умолчанию True). Поэтому чтобы получить HTML без экранированных символов, передайте escape=False

In [362]: df = pd.DataFrame({"a": list("&<>"), "b": np.random.randn(3)})

Экранировано:

In [363]: html = df.to_html()

In [364]: print(html)
                                                                                                                
a b
0 & 2.396780
1 < 0.014871
2 > 3.357427
In [365]: display(HTML(html))

Не экранировано:

In [366]: html = df.to_html(escape=False)

In [367]: print(html)
                                                                                                                
a b
0 & 2.396780
1 < 0.014871
2 > 3.357427
In [368]: display(HTML(html))

Примечание

Некоторые браузеры могут не показывать разницу в отображении двух предыдущих HTML-таблиц.

Особенности парсинга HTML таблиц#

Существуют некоторые проблемы с версиями библиотек, используемых для парсинга HTML-таблиц в функции ввода-вывода верхнего уровня pandas read_html.

Проблемы с lxml

  • Преимущества

    • lxml очень быстрый.

    • lxml требует Cython для правильной установки.

  • Недостатки

    • lxml делает не не даёт никаких гарантий относительно результатов своего разбора если только если задано строго допустимая разметка.

    • В свете вышеизложенного, мы решили позволить вам, пользователю, использовать lxml бэкенд, но этот бэкенд будет использовать html5lib if lxml не удается разобрать

    • Поэтому настоятельно рекомендуется что вы устанавливаете оба BeautifulSoup4 и html5lib, так что вы всё равно получите корректный результат (при условии, что всё остальное корректно), даже если lxml не удается.

Проблемы с BeautifulSoup4 используя lxml в качестве бэкенда

  • Указанные выше проблемы также актуальны здесь, поскольку BeautifulSoup4 по сути просто обёртка вокруг парсерного бэкенда.

Проблемы с BeautifulSoup4 используя html5lib в качестве бэкенда

  • Преимущества

    • html5lib гораздо более снисходителен, чем lxml и, следовательно, работает с реальная разметка гораздо более разумным способом, а не просто, например, удаляя элемент без уведомления.

    • html5lib автоматически генерирует валидную HTML5-разметку из невалидной разметки. Это чрезвычайно важно для разбора HTML-таблиц, поскольку гарантирует валидный документ. Однако это НЕ означает, что он «корректен», поскольку процесс исправления разметки не имеет единого определения.

    • html5lib является чистым Python и не требует дополнительных шагов сборки, кроме собственной установки.

  • Недостатки

    • Самый большой недостаток использования html5lib заключается в том, что он медленный как патока. Однако учтите, что многие таблицы в интернете недостаточно велики, чтобы время выполнения алгоритма разбора имело значение. Скорее всего, узким местом будет процесс чтения необработанного текста из URL через интернет, т.е., ввод-вывод (IO). Для очень больших таблиц это может быть не так.

LaTeX#

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

В настоящее время нет методов для чтения из LaTeX, только методы вывода.

Запись в файлы LaTeX#

Примечание

DataFrame и Объекты Styler в настоящее время имеют to_latex метод. Мы рекомендуем использовать Styler.to_latex() метод вместо DataFrame.to_latex() из-за большей гибкости первого с условным форматированием и возможной будущей устарелости второго.

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

Для простого применения достаточно следующего шаблона.

In [369]: df = pd.DataFrame([[1, 2], [3, 4]], index=["a", "b"], columns=["c", "d"])

In [370]: print(df.style.to_latex())
\begin{tabular}{lrr}
 & c & d \\
a & 1 & 2 \\
b & 3 & 4 \\
\end{tabular}

Для форматирования значений перед выводом, объедините Styler.format метод.

In [371]: 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() функция может принимать XML-строку/файл/URL и будет разбирать узлы и атрибуты в pandas DataFrame.

Примечание

Поскольку нет стандартной структуры XML, где типы дизайна могут различаться многими способами, read_xml работает лучше с более плоскими, неглубокими версиями. Если XML-документ глубоко вложен, используйте stylesheet функция для преобразования XML в более плоскую версию.

Давайте рассмотрим несколько примеров.

Чтение XML-строки:

In [372]: from io import StringIO

In [373]: xml = """
   .....: 
   .....:   
   .....:     Everyday Italian
   .....:     Giada De Laurentiis
   .....:     2005
   .....:     30.00
   .....:   
   .....:   
   .....:     Harry Potter
   .....:     J K. Rowling
   .....:     2005
   .....:     29.99
   .....:   
   .....:   
   .....:     Learning XML
   .....:     Erik T. Ray
   .....:     2003
   .....:     39.95
   .....:   
   .....: """
   .....: 

In [374]: df = pd.read_xml(StringIO(xml))

In [375]: df
Out[375]: 
   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 [376]: df = pd.read_xml("https://www.w3schools.com/xml/books.xml")

In [377]: df
Out[377]: 
   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 [378]: file_path = "books.xml"

In [379]: with open(file_path, "w") as f:
   .....:     f.write(xml)
   .....: 

In [380]: with open(file_path, "r") as f:
   .....:     df = pd.read_xml(StringIO(f.read()))
   .....: 

In [381]: df
Out[381]: 
   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 [382]: with open(file_path, "r") as f:
   .....:     sio = StringIO(f.read())
   .....: 

In [383]: df = pd.read_xml(sio)

In [384]: df
Out[384]: 
   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 [385]: with open(file_path, "rb") as f:
   .....:     bio = BytesIO(f.read())
   .....: 

In [386]: df = pd.read_xml(bio)

In [387]: df
Out[387]: 
   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 [388]: df = pd.read_xml(
   .....:     "s3://pmc-oa-opendata/oa_comm/xml/all/PMC1236943.xml",
   .....:     xpath=".//journal-meta",
   .....: )
   .....: 

In [389]: df
Out[389]: 
              journal-id              journal-title       issn  publisher
0  Cardiovasc Ultrasound  Cardiovascular Ultrasound  1476-7120        NaN

С lxml по умолчанию parser, вы получаете доступ к полнофункциональной XML-библиотеке, которая расширяет API ElementTree Python. Одним из мощных инструментов является возможность запрашивать узлы выборочно или условно с более выразительным XPath:

In [390]: df = pd.read_xml(file_path, xpath="//book[year=2005]")

In [391]: df
Out[391]: 
   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 [392]: df = pd.read_xml(file_path, elems_only=True)

In [393]: df
Out[393]: 
              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 [394]: df = pd.read_xml(file_path, attrs_only=True)

In [395]: df
Out[395]: 
   category
0   cooking
1  children
2       web

XML-документы могут иметь пространства имён с префиксами и пространства имён по умолчанию без префиксов, оба из которых обозначаются специальным атрибутом xmlnsДля парсинга по узлу в контексте пространства имен, xpath должен ссылаться на префикс.

Например, приведенный ниже XML содержит пространство имен с префиксом, doc, и URI на https://example.com. Чтобы разобрать doc:row узлы, namespaces должен использоваться.

In [396]: xml = """
   .....: 
   .....:   
   .....:     square
   .....:     360
   .....:     4.0
   .....:   
   .....:   
   .....:     circle
   .....:     360
   .....:     
   .....:   
   .....:   
   .....:     triangle
   .....:     180
   .....:     3.0
   .....:   
   .....: """
   .....: 

In [397]: df = pd.read_xml(StringIO(xml),
   .....:                  xpath="//doc:row",
   .....:                  namespaces={"doc": "https://example.com"})
   .....: 

In [398]: df
Out[398]: 
      shape  degrees  sides
0    square      360    4.0
1    circle      360    NaN
2  triangle      180    3.0

Аналогично, XML-документ может иметь пространство имен по умолчанию без префикса. Не назначение временного префикса вернет пустые узлы и вызовет ValueError. Но присваивание любой временное имя для исправления URI позволяет парсинг узлами.

In [399]: xml = """
   .....: 
   .....:  
   .....:    square
   .....:    360
   .....:    4.0
   .....:  
   .....:  
   .....:    circle
   .....:    360
   .....:    
   .....:  
   .....:  
   .....:    triangle
   .....:    180
   .....:    3.0
   .....:  
   .....: """
   .....: 

In [400]: df = pd.read_xml(StringIO(xml),
   .....:                  xpath="//pandas:row",
   .....:                  namespaces={"pandas": "https://example.com"})
   .....: 

In [401]: df
Out[401]: 
      shape  degrees  sides
0    square      360    4.0
1    circle      360    NaN
2  triangle      180    3.0

Однако, если XPath не ссылается на имена узлов, такие как default, /*, затем namespaces не требуется.

Примечание

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

In [402]: xml = """
   .....: 
   .....:   
   .....:     square
   .....:     360
   .....:   
   .....:   
   .....:     circle
   .....:     360
   .....:   
   .....:   
   .....:     triangle
   .....:     180
   .....:   
   .....: """
   .....: 

In [403]: df = pd.read_xml(StringIO(xml), xpath="./row")

In [404]: df
Out[404]: 
      shape  degrees
0    square      360
1    circle      360
2  triangle      180

показывает атрибут sides на shape элемент не был разобран как ожидалось, поскольку этот атрибут находится на дочернем элементе row элемент а не row сам элемент. Другими словами, sides атрибут является потомком уровня внука для row элемент. Однако, xpath targets row элемент, который охватывает только его дочерние элементы и атрибуты.

С lxml в качестве парсера, вы можете сгладить вложенные XML-документы с помощью XSLT скрипта, который также может быть строкой/файлом/URL. В качестве фона, XSLT является специализированным языком, написанным в специальном XML-файле, который может преобразовывать исходные XML-документы в другие XML, HTML, даже текст (CSV, JSON и т.д.) с использованием процессора XSLT.

Например, рассмотрим эту несколько вложенную структуру поездок Chicago 'L', где элементы станции и поездок инкапсулируют данные в своих собственных разделах. С помощью XSLT ниже, lxml может преобразовать исходный вложенный документ в более плоский вывод (как показано ниже для демонстрации) для более простого разбора в DataFrame:

In [405]: xml = """
   .....:  
   .....:   
   .....:     
   .....:     2020-09-01T00:00:00
   .....:     
   .....:       864.2
   .....:       534
   .....:       417.2
   .....:     
   .....:   
   .....:   
   .....:     
   .....:     2020-09-01T00:00:00
   .....:     
   .....:       2707.4
   .....:       1909.8
   .....:       1438.6
   .....:     
   .....:   
   .....:   
   .....:     
   .....:     2020-09-01T00:00:00
   .....:     
   .....:       2949.6
   .....:       1657
   .....:       1453.8
   .....:     
   .....:   
   .....:  """
   .....: 

In [406]: xsl = """
   .....:    
   .....:    
   .....:    
   .....:       
   .....:         
   .....:       
   .....:    
   .....:    
   .....:       
   .....:         
   .....:         
   .....:         
   .....:       
   .....:    
   .....:  """
   .....: 

In [407]: output = """
   .....:  
   .....:    
   .....:       40850
   .....:       Library
   .....:       2020-09-01T00:00:00
   .....:       864.2
   .....:       534
   .....:       417.2
   .....:    
   .....:    
   .....:       41700
   .....:       Washington/Wabash
   .....:       2020-09-01T00:00:00
   .....:       2707.4
   .....:       1909.8
   .....:       1438.6
   .....:    
   .....:    
   .....:       40380
   .....:       Clark/Lake
   .....:       2020-09-01T00:00:00
   .....:       2949.6
   .....:       1657
   .....:       1453.8
   .....:    
   .....:  """
   .....: 

In [408]: df = pd.read_xml(StringIO(xml), stylesheet=xsl)

In [409]: df
Out[409]: 
   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 и iterparse из etree которые являются эффективными по памяти методами для итерации по 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 [410]: geom_df = pd.DataFrame(
   .....:     {
   .....:         "shape": ["square", "circle", "triangle"],
   .....:         "degrees": [360, 360, 180],
   .....:         "sides": [4, np.nan, 3],
   .....:     }
   .....: )
   .....: 

In [411]: print(geom_df.to_xml())


  
    0
    square
    360
    4.0
  
  
    1
    circle
    360
    
  
  
    2
    triangle
    180
    3.0
  

Записать XML с новым корневым элементом и именем строки:

In [412]: print(geom_df.to_xml(root_name="geometry", row_name="objects"))


  
    0
    square
    360
    4.0
  
  
    1
    circle
    360
    
  
  
    2
    triangle
    180
    3.0
  

Записать XML с атрибутами:

In [413]: print(geom_df.to_xml(attr_cols=geom_df.columns.tolist()))


  
  
  

Напишите смесь элементов и атрибутов:

In [414]: print(
   .....:     geom_df.to_xml(
   .....:         index=False,
   .....:         attr_cols=['shape'],
   .....:         elem_cols=['degrees', 'sides'])
   .....: )
   .....: 


  
    360
    4.0
  
  
    360
    
  
  
    180
    3.0
  

Любой DataFrames с иерархическими столбцами будут сглажены для имен XML элементов с уровнями, разделенными подчеркиваниями:

In [415]: ext_geom_df = pd.DataFrame(
   .....:     {
   .....:         "type": ["polygon", "other", "polygon"],
   .....:         "shape": ["square", "circle", "triangle"],
   .....:         "degrees": [360, 360, 180],
   .....:         "sides": [4, np.nan, 3],
   .....:     }
   .....: )
   .....: 

In [416]: pvt_df = ext_geom_df.pivot_table(index='shape',
   .....:                                  columns='type',
   .....:                                  values=['degrees', 'sides'],
   .....:                                  aggfunc='sum')
   .....: 

In [417]: pvt_df
Out[417]: 
         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 [418]: print(pvt_df.to_xml())


  
    circle
    360.0
    
    0.0
    
  
  
    square
    
    360.0
    
    4.0
  
  
    triangle
    
    180.0
    
    3.0
  

Запись XML с пространством имен по умолчанию:

In [419]: print(geom_df.to_xml(namespaces={"": "https://example.com"}))


  
    0
    square
    360
    4.0
  
  
    1
    circle
    360
    
  
  
    2
    triangle
    180
    3.0
  

Запись XML с префиксом пространства имён:

In [420]: print(
   .....:     geom_df.to_xml(namespaces={"doc": "https://example.com"},
   .....:                    prefix="doc")
   .....: )
   .....: 


  
    0
    square
    360
    4.0
  
  
    1
    circle
    360
    
  
  
    2
    triangle
    180
    3.0
  

Записать XML без объявления или с красивым выводом:

In [421]: print(
   .....:     geom_df.to_xml(xml_declaration=False,
   .....:                    pretty_print=False)
   .....: )
   .....: 
0square3604.01circle3602triangle1803.0

Написать XML и преобразовать с помощью таблицы стилей:

In [422]: xsl = """
   .....:    
   .....:    
   .....:    
   .....:      
   .....:        
   .....:      
   .....:    
   .....:    
   .....:      {index}">
   .....:        
   .....:            polygon
   .....:        
   .....:        
   .....:        
   .....:            
   .....:        
   .....:      
   .....:    
   .....:  """
   .....: 

In [423]: print(geom_df.to_xml(stylesheet=xsl))


  
    square
    
      360
      4.0
    
  
  
    circle
    
      360
      
    
  
  
    triangle
    
      180
      3.0
    
  

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 является языком программирования, используйте его с осторожностью, так как такие скрипты могут представлять угрозу безопасности в вашей среде и могут запускать большие или бесконечные рекурсивные операции. Всегда тестируйте скрипты на небольших фрагментах перед полным запуском.

  • The etree парсер поддерживает всю функциональность как read_xml и to_xml за исключением сложных XPath и любых XSLT. Хотя ограниченный в возможностях, etree всё ещё является надёжным и способным парсером и построителем деревьев. Его производительность может отставать lxml до определенной степени для больших файлов, но относительно незаметно для файлов малого и среднего размера.

Файлы Excel#

The read_excel() метод может читать Excel 2007+ (.xlsx) файлы с использованием openpyxl Модуль Python. Excel 2003 (.xls) файлы можно читать с помощью xlrd. Двоичный Excel (.xlsb) файлы можно читать с помощью pyxlsbВсе форматы могут быть прочитаны с использованием calamine движок. В to_excel() метод экземпляра используется для сохранения DataFrame в Excel. Обычно семантика работы похожа на работу с csv данные. См. cookbook для некоторых продвинутых стратегий.

Примечание

Когда engine=None, следующая логика будет использоваться для определения движка:

  • Если path_or_buffer является форматом OpenDocument (.odf, .ods, .odt), тогда odf будет использоваться.

  • В противном случае, если path_or_buffer является форматом xls, xlrd будет использоваться.

  • В противном случае, если path_or_buffer имеет формат xlsb, pyxlsb будет использоваться.

  • В противном случае openpyxl будет использоваться.

Чтение файлов Excel#

В самом простом случае использования, read_excel принимает путь к файлу Excel, и sheet_name указывающий, какой лист разбирать.

При использовании engine_kwargs параметр, pandas передаст эти аргументы в движок. Для этого важно знать, какую функцию pandas использует внутренне.

  • Для движка openpyxl pandas использует openpyxl.load_workbook() для чтения (.xlsx) и (.xlsm) файлы.

  • Для движка xlrd, pandas использует xlrd.open_workbook() для чтения (.xls) файлы.

  • Для движка pyxlsb, pandas использует pyxlsb.open_workbook() для чтения (.xlsb) файлы.

  • Для движка odf, pandas использует odf.opendocument.load() для чтения (.ods) файлы.

  • Для движка calamine, pandas использует python_calamine.load_workbook() для чтения (.xlsx), (.xlsm), (.xls), (.xlsb), (.ods) файлы.

# 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")

The ExcelFile класс также может использоваться как контекстный менеджер.

with pd.ExcelFile("path_to_file.xls") as xls:
    df1 = pd.read_excel(xls, "Sheet1")
    df2 = pd.read_excel(xls, "Sheet2")

The 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, что указывает на чтение первого листа

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

  • Передайте целое число для ссылки на индекс листа. Индексы следуют 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 чтобы прочитать все листы. Листы могут быть указаны по индексу или имени листа, используя целое число или строку, соответственно.

Чтение MultiIndex#

read_excel может прочитать MultiIndex индекса, передавая список столбцов в index_col и MultiIndex столбец, передавая список строк в header. Если любой из index или columns если сериализованы имена уровней, они также будут прочитаны путем указания строк/столбцов, составляющих уровни.

Например, для чтения MultiIndex индекс без имён:

In [424]: df = pd.DataFrame(
   .....:     {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]},
   .....:     index=pd.MultiIndex.from_product([["a", "b"], ["c", "d"]]),
   .....: )
   .....: 

In [425]: df.to_excel("path_to_file.xlsx")

In [426]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])

In [427]: df
Out[427]: 
     a  b
a c  1  5
  d  2  6
b c  3  7
  d  4  8

Если индекс имеет имена уровней, они также будут разобраны с использованием тех же параметров.

In [428]: df.index = df.index.set_names(["lvl1", "lvl2"])

In [429]: df.to_excel("path_to_file.xlsx")

In [430]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])

In [431]: df
Out[431]: 
           a  b
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

Если исходный файл содержит оба MultiIndex индекс и столбцы, списки, указывающие каждый должны быть переданы в index_col и header:

In [432]: df.columns = pd.MultiIndex.from_product([["a"], ["b", "d"]], names=["c1", "c2"])

In [433]: df.to_excel("path_to_file.xlsx")

In [434]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1], header=[0, 1])

In [435]: df
Out[435]: 
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 takes a 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())

Разбор дат#

Значения, похожие на дату и время, обычно автоматически преобразуются в соответствующий dtype при чтении файла Excel. Но если у вас есть столбец строк, которые смотри например, даты (но фактически не отформатированы как даты в Excel), вы можете использовать parse_dates ключевое слово для преобразования этих строк в даты и время:

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")

Файлы с .xlsx расширение будет записано с использованием xlsxwriter (если доступно) или openpyxl.

The DataFrame будет записан таким образом, чтобы попытаться имитировать вывод REPL. index_label будет помещён во вторую строку вместо первой. Вы можете поместить его в первую строку, установив merge_cells опция в to_excel() to 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")

При использовании engine_kwargs параметр, pandas передаст эти аргументы в движок. Для этого важно знать, какую функцию pandas использует внутри.

  • Для движка openpyxl pandas использует openpyxl.Workbook() для создания нового листа и openpyxl.load_workbook() для добавления данных в существующий лист. Движок openpyxl записывает в (.xlsx) и (.xlsm) файлы.

  • Для движка xlsxwriter pandas использует xlsxwriter.Workbook() для записи в (.xlsx) файлы.

  • Для движка odf, pandas использует odf.opendocument.OpenDocumentSpreadsheet() для записи в (.ods) файлы.

Запись файлов 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#

pandas выбирает писатель Excel двумя способами:

  1. the engine аргумент ключевого слова

  2. расширение имени файла (через значение по умолчанию, указанное в параметрах конфигурации)

По умолчанию pandas использует XlsxWriter для .xlsx, openpyxl для .xlsm. Если у вас установлено несколько движков, вы можете установить движок по умолчанию через установка параметров конфигурации io.excel.xlsx.writer и io.excel.xls.writer. pandas вернется к openpyxl для .xlsx файлы, если Xlsxwriter недоступно.

Чтобы указать, какой модуль записи вы хотите использовать, вы можете передать аргумент ключевого слова engine в to_excel и для ExcelWriter. Встроенные движки:

  • openpyxl: требуется версия 2.4 или выше

  • xlsxwriter

# 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) закрепит первую строку и первый столбец (по умолчанию None).

Используя Xlsxwriter движок предоставляет множество опций для управления форматом листа Excel, созданного с помощью to_excel метод. Отличные примеры можно найти в Xlsxwriter документация здесь: https://xlsxwriter.readthedocs.io/working_with_pandas.html

OpenDocument Spreadsheets#

Методы ввода-вывода для Файлы Excel также поддерживает чтение и запись электронных таблиц OpenDocument с использованием odfpy модуль. Семантика и возможности для чтения и записи электронных таблиц OpenDocument соответствуют тому, что можно сделать для Файлы Excel используя engine='odf'. Необходимо установить опциональную зависимость 'odfpy'.

The read_excel() метод может читать электронные таблицы OpenDocument

# Returns a DataFrame
pd.read_excel("path_to_file.ods", engine="odf")

Аналогично, to_excel() метод может записывать электронные таблицы OpenDocument

# Writes DataFrame to a .ods file
df.to_excel("path_to_file.ods", engine="odf")

Бинарные файлы Excel (.xlsb)#

The read_excel() метод также может читать двоичные файлы Excel с помощью pyxlsb модуль. Семантика и возможности для чтения двоичных файлов Excel в основном соответствуют тому, что можно сделать для Файлы Excel используя engine='pyxlsb'. pyxlsb не распознает типы datetime в файлах и будет возвращать числа с плавающей точкой вместо них (можно использовать calamine если вам нужно распознавать типы datetime).

# Returns a DataFrame
pd.read_excel("path_to_file.xlsb", engine="pyxlsb")

Примечание

В настоящее время pandas поддерживает только чтение двоичные файлы Excel. Запись не реализована.

Calamine (файлы Excel и ODS)#

The read_excel() метод может читать файл Excel (.xlsx, .xlsm, .xls, .xlsb) и электронные таблицы OpenDocument (.ods) с использованием python-calamine xlsm calamine и работает быстрее других движков в большинстве случаев. Необходимо установить опциональную зависимость 'python-calamine'.

# Returns a DataFrame
pd.read_excel("path_to_file.xlsb", engine="calamine")

Буфер обмена#

Удобный способ получения данных — использовать 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

The 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 [436]: df
Out[436]: 
c1         a   
c2         b  d
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

In [437]: df.to_pickle("foo.pkl")

The read_pickle функция в pandas пространство имен может использоваться для загрузки любого сохраненного объекта pandas (или любого другого сохраненного объекта) из файла:

In [438]: pd.read_pickle("foo.pkl")
Out[438]: 
c1         a   
c2         b  d
lvl1 lvl2      
a    c     1  5
     d     2  6
b    c     3  7
     d     4  8

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

Загрузка данных из pickle-файлов, полученных из ненадежных источников, может быть небезопасной.

См.: https://docs.python.org/3/library/pickle.html

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

read_pickle() гарантируется обратная совместимость только на несколько минорных релизов назад.

Сжатые файлы 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 [439]: df = pd.DataFrame(
   .....:     {
   .....:         "A": np.random.randn(1000),
   .....:         "B": "foo",
   .....:         "C": pd.date_range("20130101", periods=1000, freq="s"),
   .....:     }
   .....: )
   .....: 

In [440]: df
Out[440]: 
            A    B                   C
0   -0.317441  foo 2013-01-01 00:00:00
1   -1.236269  foo 2013-01-01 00:00:01
2    0.896171  foo 2013-01-01 00:00:02
3   -0.487602  foo 2013-01-01 00:00:03
4   -0.082240  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -0.171092  foo 2013-01-01 00:16:35
996  1.786173  foo 2013-01-01 00:16:36
997 -0.575189  foo 2013-01-01 00:16:37
998  0.820750  foo 2013-01-01 00:16:38
999 -1.256530  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

Использование явного типа сжатия:

In [441]: df.to_pickle("data.pkl.compress", compression="gzip")

In [442]: rt = pd.read_pickle("data.pkl.compress", compression="gzip")

In [443]: rt
Out[443]: 
            A    B                   C
0   -0.317441  foo 2013-01-01 00:00:00
1   -1.236269  foo 2013-01-01 00:00:01
2    0.896171  foo 2013-01-01 00:00:02
3   -0.487602  foo 2013-01-01 00:00:03
4   -0.082240  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -0.171092  foo 2013-01-01 00:16:35
996  1.786173  foo 2013-01-01 00:16:36
997 -0.575189  foo 2013-01-01 00:16:37
998  0.820750  foo 2013-01-01 00:16:38
999 -1.256530  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

Определение типа сжатия по расширению:

In [444]: df.to_pickle("data.pkl.xz", compression="infer")

In [445]: rt = pd.read_pickle("data.pkl.xz", compression="infer")

In [446]: rt
Out[446]: 
            A    B                   C
0   -0.317441  foo 2013-01-01 00:00:00
1   -1.236269  foo 2013-01-01 00:00:01
2    0.896171  foo 2013-01-01 00:00:02
3   -0.487602  foo 2013-01-01 00:00:03
4   -0.082240  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -0.171092  foo 2013-01-01 00:16:35
996  1.786173  foo 2013-01-01 00:16:36
997 -0.575189  foo 2013-01-01 00:16:37
998  0.820750  foo 2013-01-01 00:16:38
999 -1.256530  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

По умолчанию используется 'infer':

In [447]: df.to_pickle("data.pkl.gz")

In [448]: rt = pd.read_pickle("data.pkl.gz")

In [449]: rt
Out[449]: 
            A    B                   C
0   -0.317441  foo 2013-01-01 00:00:00
1   -1.236269  foo 2013-01-01 00:00:01
2    0.896171  foo 2013-01-01 00:00:02
3   -0.487602  foo 2013-01-01 00:00:03
4   -0.082240  foo 2013-01-01 00:00:04
..        ...  ...                 ...
995 -0.171092  foo 2013-01-01 00:16:35
996  1.786173  foo 2013-01-01 00:16:36
997 -0.575189  foo 2013-01-01 00:16:37
998  0.820750  foo 2013-01-01 00:16:38
999 -1.256530  foo 2013-01-01 00:16:39

[1000 rows x 3 columns]

In [450]: df["A"].to_pickle("s1.pkl.bz2")

In [451]: rt = pd.read_pickle("s1.pkl.bz2")

In [452]: rt
Out[452]: 
0     -0.317441
1     -1.236269
2      0.896171
3     -0.487602
4     -0.082240
         ...   
995   -0.171092
996    1.786173
997   -0.575189
998    0.820750
999   -1.256530
Name: A, Length: 1000, dtype: float64

Передача опций протоколу сжатия для ускорения сжатия:

In [453]: df.to_pickle("data.pkl.gz", compression={"method": "gzip", "compresslevel": 1})

msgpack#

поддержка pandas для msgpack был удален в версии 1.0.0. Рекомендуется использовать pickle вместо этого.

В качестве альтернативы, вы также можете использовать формат сериализации Arrow IPC для передачи объектов pandas по сети. Документацию по pyarrow см. в здесь.

HDF5 (PyTables)#

HDFStore является объектом, похожим на словарь, который читает и записывает pandas с использованием высокопроизводительного формата HDF5 с помощью отличного PyTables библиотеки. См. cookbook для некоторых продвинутых стратегий

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

pandas использует PyTables для чтения и записи файлов HDF5, что позволяет сериализовать данные типа object с помощью pickle. Загрузка данных pickle, полученных из ненадежных источников, может быть небезопасной.

См.: https://docs.python.org/3/library/pickle.html подробнее.

In [454]: store = pd.HDFStore("store.h5")

In [455]: print(store)

File path: store.h5

Объекты могут быть записаны в файл так же, как добавление пар ключ-значение в словарь:

In [456]: index = pd.date_range("1/1/2000", periods=8)

In [457]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [458]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"])

# store.put('s', s) is an equivalent method
In [459]: store["s"] = s

In [460]: store["df"] = df

In [461]: store
Out[461]: 

File path: store.h5

В текущей или последующей сессии Python вы можете извлечь сохранённые объекты:

# store.get('df') is an equivalent method
In [462]: store["df"]
Out[462]: 
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

# dotted (attribute) access provides get as well
In [463]: store.df
Out[463]: 
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

Удаление объекта, указанного ключом:

# store.remove('df') is an equivalent method
In [464]: del store["df"]

In [465]: store
Out[465]: 

File path: store.h5

Закрытие Store и использование контекстного менеджера:

In [466]: store.close()

In [467]: store
Out[467]: 

File path: store.h5

In [468]: store.is_open
Out[468]: False

# Working with, and automatically closing the store using a context manager
In [469]: with pd.HDFStore("store.h5") as store:
   .....:     store.keys()
   .....: 

API чтения/записи#

HDFStore поддерживает API верхнего уровня с использованием read_hdf для чтения и to_hdf для записи, аналогично тому, как read_csv и to_csv работа.

In [470]: df_tl = pd.DataFrame({"A": list(range(5)), "B": list(range(5))})

In [471]: df_tl.to_hdf("store_tl.h5", key="table", append=True)

In [472]: pd.read_hdf("store_tl.h5", "table", where=["index>2"])
Out[472]: 
   A  B
3  3  3
4  4  4

HDFStore по умолчанию не удаляет строки, которые полностью отсутствуют. Это поведение можно изменить, установив dropna=True.

In [473]: df_with_missing = pd.DataFrame(
   .....:     {
   .....:         "col1": [0, np.nan, 2],
   .....:         "col2": [1, np.nan, np.nan],
   .....:     }
   .....: )
   .....: 

In [474]: df_with_missing
Out[474]: 
   col1  col2
0   0.0   1.0
1   NaN   NaN
2   2.0   NaN

In [475]: df_with_missing.to_hdf("file.h5", key="df_with_missing", format="table", mode="w")

In [476]: pd.read_hdf("file.h5", "df_with_missing")
Out[476]: 
   col1  col2
0   0.0   1.0
1   NaN   NaN
2   2.0   NaN

In [477]: df_with_missing.to_hdf(
   .....:     "file.h5", key="df_with_missing", format="table", mode="w", dropna=True
   .....: )
   .....: 

In [478]: pd.read_hdf("file.h5", "df_with_missing")
Out[478]: 
   col1  col2
0   0.0   1.0
2   2.0   NaN

Фиксированный формат#

Приведенные выше примеры показывают сохранение с использованием put, который записывает HDF5 в PyTables в фиксированном формате массива, называемом fixed формат. Эти типы хранилищ не дополняемыми после записи (хотя вы можете просто удалить их и перезаписать). Также они не являются queryable; они должны быть извлечены полностью. Они также не поддерживают датафреймы с неуникальными именами столбцов. fixed форматы хранилищ обеспечивают очень быструю запись и немного более быстрое чтение, чем table хранит. Этот формат задается по умолчанию при использовании put или to_hdf или с помощью format='fixed' или format='f'.

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

A fixed формат вызовет TypeError если вы попытаетесь извлечь с помощью where:

In [479]: pd.DataFrame(np.random.randn(10, 2)).to_hdf("test_fixed.h5", key="df")

In [480]: pd.read_hdf("test_fixed.h5", "df", where="index>5")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[480], line 1
----> 1 pd.read_hdf("test_fixed.h5", "df", where="index>5")

File ~/work/pandas/pandas/pandas/io/pytables.py:465, in read_hdf(path_or_buf, key, mode, errors, where, start, stop, columns, iterator, chunksize, **kwargs)
    460                 raise ValueError(
    461                     "key must be provided when HDF5 "
    462                     "file contains multiple datasets."
    463                 )
    464         key = candidate_only_group._v_pathname
--> 465     return store.select(
    466         key,
    467         where=where,
    468         start=start,
    469         stop=stop,
    470         columns=columns,
    471         iterator=iterator,
    472         chunksize=chunksize,
    473         auto_close=auto_close,
    474     )
    475 except (ValueError, TypeError, LookupError):
    476     if not isinstance(path_or_buf, HDFStore):
    477         # if there is an error, close the store if we opened it.

File ~/work/pandas/pandas/pandas/io/pytables.py:919, in HDFStore.select(self, key, where, start, stop, columns, iterator, chunksize, auto_close)
    905 # create the iterator
    906 it = TableIterator(
    907     self,
    908     s,
   (...)
    916     auto_close=auto_close,
    917 )
--> 919 return it.get_result()

File ~/work/pandas/pandas/pandas/io/pytables.py:2042, in TableIterator.get_result(self, coordinates)
   2039     where = self.where
   2041 # directly return the result
-> 2042 results = self.func(self.start, self.stop, where)
   2043 self.close()
   2044 return results

File ~/work/pandas/pandas/pandas/io/pytables.py:903, in HDFStore.select..func(_start, _stop, _where)
    902 def func(_start, _stop, _where):
--> 903     return s.read(start=_start, stop=_stop, where=_where, columns=columns)

File ~/work/pandas/pandas/pandas/io/pytables.py:3345, in BlockManagerFixed.read(self, where, columns, start, stop)
   3337 def read(
   3338     self,
   3339     where=None,
   (...)
   3343 ) -> DataFrame:
   3344     # start, stop applied to rows, so 0th axis only
-> 3345     self.validate_read(columns, where)
   3346     select_axis = self.obj_type()._get_block_manager_axis(0)
   3348     axes = []

File ~/work/pandas/pandas/pandas/io/pytables.py:2949, in GenericFixed.validate_read(self, columns, where)
   2944     raise TypeError(
   2945         "cannot pass a column specification when reading "
   2946         "a Fixed format store. this store must be selected in its entirety"
   2947     )
   2948 if where is not None:
-> 2949     raise TypeError(
   2950         "cannot pass a where specification when reading "
   2951         "from a Fixed format store. this store must be selected in its entirety"
   2952     )

TypeError: cannot pass a where specification when reading from a Fixed format store. this store must be selected in its entirety

Формат таблицы#

HDFStore поддерживает другой PyTables формат на диске, table формат. Концептуально table имеет структуру, очень похожую на DataFrame, с строками и столбцами. A table может быть дополнен в той же или других сессиях. Кроме того, поддерживаются операции удаления и запросов. Этот формат задается format='table' или format='t' to append или put или to_hdf.

Этот формат также может быть установлен как опция pd.set_option('io.hdf.default_format','table') для включения put/append/to_hdf по умолчанию сохранять в table формат.

In [481]: store = pd.HDFStore("store.h5")

In [482]: df1 = df[0:4]

In [483]: df2 = df[4:]

# append data (creates a table automatically)
In [484]: store.append("df", df1)

In [485]: store.append("df", df2)

In [486]: store
Out[486]: 

File path: store.h5

# select the entire object
In [487]: store.select("df")
Out[487]: 
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

# the type of stored data
In [488]: store.root.df._v_attrs.pandas_type
Out[488]: 'frame_table'

Примечание

Вы также можете создать table передавая format='table' или format='t' в put операция.

Иерархические ключи#

Ключи к хранилищу могут быть указаны в виде строки. Они могут быть в иерархическом формате пути (например, foo/bar/bah), который будет генерировать иерархию подхранилищ (или Groups в терминологии PyTables ). Ключи могут быть указаны без ведущего '/' и всегда абсолютный (например, 'foo' ссылается на '/foo'). Операции удаления могут удалить всё в подхранилище и ниже, поэтому будьте осторожно.

In [489]: store.put("foo/bar/bah", df)

In [490]: store.append("food/orange", df)

In [491]: store.append("food/apple", df)

In [492]: store
Out[492]: 

File path: store.h5

# a list of keys are returned
In [493]: store.keys()
Out[493]: ['/df', '/food/apple', '/food/orange', '/foo/bar/bah']

# remove all nodes under this level
In [494]: store.remove("food")

In [495]: store
Out[495]: 

File path: store.h5

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

In [496]: 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.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517
GROUP: /foo/bar
KEY: /foo/bar/bah
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

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

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

In [497]: store.foo.bar.bah
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[497], line 1
----> 1 store.foo.bar.bah

File ~/work/pandas/pandas/pandas/io/pytables.py:626, in HDFStore.__getattr__(self, name)
    624 """allow attribute access to get stores"""
    625 try:
--> 626     return self.get(name)
    627 except (KeyError, ClosedFileError):
    628     pass

File ~/work/pandas/pandas/pandas/io/pytables.py:826, in HDFStore.get(self, key)
    824 if group is None:
    825     raise KeyError(f"No object named {key} in the file")
--> 826 return self._read_group(group)

File ~/work/pandas/pandas/pandas/io/pytables.py:1891, in HDFStore._read_group(self, group)
   1890 def _read_group(self, group: Node):
-> 1891     s = self._create_storer(group)
   1892     s.infer_axes()
   1893     return s.read()

File ~/work/pandas/pandas/pandas/io/pytables.py:1765, in HDFStore._create_storer(self, group, format, value, encoding, errors)
   1763         tt = "generic_table"
   1764     else:
-> 1765         raise TypeError(
   1766             "cannot create a storer if the object is not existing "
   1767             "nor a value are passed"
   1768         )
   1769 else:
   1770     if isinstance(value, Series):

TypeError: cannot create a storer if the object is not existing nor a value are passed
# you can directly access the actual PyTables node but using the root node
In [498]: store.root.foo.bar.bah
Out[498]: 
/foo/bar/bah (Group) ''
  children := ['axis0' (Array), 'axis1' (Array), 'block0_items' (Array), 'block0_values' (Array)]

Вместо этого используйте явные строковые ключи:

In [499]: store["foo/bar/bah"]
Out[499]: 
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

Хранение типов#

Хранение смешанных типов в таблице#

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

Передача min_itemsize={`values`: size} в качестве параметра для append установит большее минимальное значение для строковых столбцов. Хранение floats, strings, ints, bools, datetime64 в настоящее время поддерживаются. Для строковых столбцов, передача nan_rep = 'nan' для добавления изменит представление nan по умолчанию на диске (которое преобразуется в/из np.nan), по умолчанию устанавливается в nan.

In [500]: 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 [501]: df_mixed.loc[df_mixed.index[3:5], ["A", "B", "string", "datetime64"]] = np.nan

In [502]: store.append("df_mixed", df_mixed, min_itemsize={"values": 50})

In [503]: df_mixed1 = store.select("df_mixed")

In [504]: df_mixed1
Out[504]: 
          A         B         C  ... int  bool                    datetime64
0  0.013747 -1.166078 -1.292080  ...   1  True 1970-01-01 00:00:00.978393600
1 -0.712009  0.247572  1.526911  ...   1  True 1970-01-01 00:00:00.978393600
2 -0.645096  1.687406  0.288504  ...   1  True 1970-01-01 00:00:00.978393600
3       NaN       NaN  0.097771  ...   1  True                           NaT
4       NaN       NaN  1.536408  ...   1  True                           NaT
5 -0.023202  0.043702  0.926790  ...   1  True 1970-01-01 00:00:00.978393600
6  2.359782  0.088224 -0.676448  ...   1  True 1970-01-01 00:00:00.978393600
7 -0.143428 -0.813360 -0.179724  ...   1  True 1970-01-01 00:00:00.978393600

[8 rows x 7 columns]

In [505]: df_mixed1.dtypes.value_counts()
Out[505]: 
float64           2
float32           1
object            1
int64             1
bool              1
datetime64[ns]    1
Name: count, dtype: int64

# we have provided a minimum string column size
In [506]: store.root.df_mixed.table
Out[506]: 
/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}

Хранение DataFrame с MultiIndex#

Хранение MultiIndex DataFrames хранение/выборка из таблиц очень похожа на хранение/выборку из однородного индекса DataFrames.

In [507]: 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 [508]: df_mi = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])

In [509]: df_mi
Out[509]: 
                  A         B         C
foo bar                                
foo one   -1.303456 -0.642994 -0.649456
    two    1.012694  0.414147  1.950460
    three  1.094544 -0.802899 -0.583343
bar one    0.410395  0.618321  0.560398
    two    1.434027 -0.033270  0.343197
baz two   -1.646063 -0.695847 -0.429156
    three -0.244688 -1.428229 -0.138691
qux one    1.866184 -1.446617  0.036660
    two   -1.660522  0.929553 -1.298649
    three  3.565769  0.682402  1.041927

In [510]: store.append("df_mi", df_mi)

In [511]: store.select("df_mi")
Out[511]: 
                  A         B         C
foo bar                                
foo one   -1.303456 -0.642994 -0.649456
    two    1.012694  0.414147  1.950460
    three  1.094544 -0.802899 -0.583343
bar one    0.410395  0.618321  0.560398
    two    1.434027 -0.033270  0.343197
baz two   -1.646063 -0.695847 -0.429156
    three -0.244688 -1.428229 -0.138691
qux one    1.866184 -1.446617  0.036660
    two   -1.660522  0.929553 -1.298649
    three  3.565769  0.682402  1.041927

# the levels are automatically included as data columns
In [512]: store.select("df_mi", "foo=bar")
Out[512]: 
                A         B         C
foo bar                              
bar one  0.410395  0.618321  0.560398
    two  1.434027 -0.033270  0.343197

Примечание

The index ключевое слово зарезервировано и не может использоваться как имя уровня.

Запросы#

Запрос к таблице#

select и delete операции имеют необязательный критерий, который можно указать для выбора/удаления только части данных. Это позволяет иметь очень большую таблицу на диске и извлекать только часть данных.

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

  • index и columns являются поддерживаемыми индексаторами для DataFrames.

  • if data_columns указаны, они могут использоваться как дополнительные индексаторы.

  • имя уровня в MultiIndex, с именем по умолчанию level_0, level_1, … если не предоставлено.

Допустимые операторы сравнения:

=, ==, !=, >, >=, <, <=

Допустимые логические выражения объединяются с:

  • | : или

  • & : и

  • ( и ) : для группировки

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

Примечание

  • = будет автоматически расширен до оператора сравнения ==

  • ~ — это оператор not, но может использоваться только в очень ограниченных обстоятельствах.

  • Если передается список/кортеж выражений, они будут объединены через &

Следующие выражения являются допустимыми:

  • '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"

The 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 [513]: dfq = pd.DataFrame(
   .....:     np.random.randn(10, 4),
   .....:     columns=list("ABCD"),
   .....:     index=pd.date_range("20130101", periods=10),
   .....: )
   .....: 

In [514]: store.append("dfq", dfq, format="table", data_columns=True)

Используйте логические выражения с встроенной оценкой функций.

In [515]: store.select("dfq", "index>pd.Timestamp('20130104') & columns=['A', 'B']")
Out[515]: 
                   A         B
2013-01-05 -0.830545 -0.457071
2013-01-06  0.431186  1.049421
2013-01-07  0.617509 -0.811230
2013-01-08  0.947422 -0.671233
2013-01-09 -0.183798 -1.211230
2013-01-10  0.361428  0.887304

Используйте встроенную ссылку на столбец.

In [516]: store.select("dfq", where="A>0 or C>0")
Out[516]: 
                   A         B         C         D
2013-01-02  0.658179  0.362814 -0.917897  0.010165
2013-01-03  0.905122  1.848731 -1.184241  0.932053
2013-01-05 -0.830545 -0.457071  1.565581  1.148032
2013-01-06  0.431186  1.049421  0.383309  0.595013
2013-01-07  0.617509 -0.811230 -2.088563 -1.393500
2013-01-08  0.947422 -0.671233 -0.847097 -1.187785
2013-01-10  0.361428  0.887304  0.266457 -0.399641

The columns ключевое слово может быть предоставлено для выбора списка столбцов, которые будут возвращены, это эквивалентно передаче 'columns=list_of_columns_to_filter':

In [517]: store.select("df", "columns=['A', 'B']")
Out[517]: 
                   A         B
2000-01-01  0.858644 -0.851236
2000-01-02 -0.080372 -1.268121
2000-01-03  0.816983  1.965656
2000-01-04  0.712795 -0.062433
2000-01-05 -0.298721 -1.988045
2000-01-06  1.103675  1.382242
2000-01-07 -0.729161 -0.142928
2000-01-08 -1.005977  0.465222

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

Примечание

select вызовет ValueError если выражение запроса содержит неизвестную ссылку на переменную. Обычно это означает, что вы пытаетесь выбрать столбец, который не столбец данных.

select вызовет SyntaxError если выражение запроса недействительно.

Запрос timedelta64[нс]#

Вы можете хранить и запрашивать с использованием timedelta64[ns] тип. Условия могут быть указаны в формате: (), где float может быть знаковым (и дробным), а unit может быть D,s,ms,us,ns для timedelta. Вот пример:

In [518]: from datetime import timedelta

In [519]: dftd = pd.DataFrame(
   .....:     {
   .....:         "A": pd.Timestamp("20130101"),
   .....:         "B": [
   .....:             pd.Timestamp("20130101") + timedelta(days=i, seconds=10)
   .....:             for i in range(10)
   .....:         ],
   .....:     }
   .....: )
   .....: 

In [520]: dftd["C"] = dftd["A"] - dftd["B"]

In [521]: dftd
Out[521]: 
           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 [522]: store.append("dftd", dftd, data_columns=True)

In [523]: store.select("dftd", "C<'-3.5D'")
Out[523]: 
                              A                   B                  C
4 1970-01-01 00:00:01.356998400 2013-01-05 00:00:10  -5 days +23:59:50
5 1970-01-01 00:00:01.356998400 2013-01-06 00:00:10  -6 days +23:59:50
6 1970-01-01 00:00:01.356998400 2013-01-07 00:00:10  -7 days +23:59:50
7 1970-01-01 00:00:01.356998400 2013-01-08 00:00:10  -8 days +23:59:50
8 1970-01-01 00:00:01.356998400 2013-01-09 00:00:10  -9 days +23:59:50
9 1970-01-01 00:00:01.356998400 2013-01-10 00:00:10 -10 days +23:59:50

Запрос MultiIndex#

Выбор из MultiIndex может быть достигнуто с использованием имени уровня.

In [524]: df_mi.index.names
Out[524]: FrozenList(['foo', 'bar'])

In [525]: store.select("df_mi", "foo=baz and bar=two")
Out[525]: 
                A         B         C
foo bar                              
baz two -1.646063 -0.695847 -0.429156

Если MultiIndex имена уровней None, уровни автоматически становятся доступными через level_n ключевое слово с n уровень MultiIndex из которых вы хотите выбрать.

In [526]: 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 [527]: df_mi_2 = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])

In [528]: df_mi_2
Out[528]: 
                  A         B         C
foo one   -0.219582  1.186860 -1.437189
    two    0.053768  1.872644 -1.469813
    three -0.564201  0.876341  0.407749
bar one   -0.232583  0.179812  0.922152
    two   -1.820952 -0.641360  2.133239
baz two   -0.941248 -0.136307 -1.271305
    three -0.099774 -0.061438 -0.845172
qux one    0.465793  0.756995 -0.541690
    two   -0.802241  0.877657 -2.553831
    three  0.094899 -2.319519  0.293601

In [529]: store.append("df_mi_2", df_mi_2)

# the levels are automatically included as data columns with keyword level_n
In [530]: store.select("df_mi_2", "level_0=foo and level_1=two")
Out[530]: 
                A         B         C
foo two  0.053768  1.872644 -1.469813

Индексирование#

Вы можете создать/изменить индекс для таблицы с помощью create_table_index после того, как данные уже в таблице (после и append/put операцию). Создание индекса таблицы — это высоко рекомендуется. Это значительно ускорит ваши запросы при использовании select с индексированным измерением в качестве where.

Примечание

Индексы автоматически создаются для индексируемых объектов и любых указанных столбцов данных. Это поведение можно отключить, передав index=False to append.

# we have automagically already created an index (in the first section)
In [531]: i = store.root.df.table.cols.index.index

In [532]: i.optlevel, i.kind
Out[532]: (6, 'medium')

# change an index by passing new parameters
In [533]: store.create_table_index("df", optlevel=9, kind="full")

In [534]: i = store.root.df.table.cols.index.index

In [535]: i.optlevel, i.kind
Out[535]: (9, 'full')

Часто при добавлении больших объемов данных в хранилище полезно отключать создание индекса для каждого добавления, а затем воссоздавать его в конце.

In [536]: df_1 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [537]: df_2 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [538]: st = pd.HDFStore("appends.h5", mode="w")

In [539]: st.append("df", df_1, data_columns=["B"], index=False)

In [540]: st.append("df", df_2, data_columns=["B"], index=False)

In [541]: st.get_storer("df").table
Out[541]: 
/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 [542]: st.create_table_index("df", columns=["B"], optlevel=9, kind="full")

In [543]: st.get_storer("df").table
Out[543]: 
/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 [544]: st.close()

См. здесь о том, как создать полностью отсортированный индекс (CSI) в существующем хранилище.

Запрос через столбцы данных#

Вы можете назначить (и проиндексировать) определенные столбцы, которые хотите использовать для выполнения запросов (кроме indexable столбцы, которые вы всегда можете запросить). Например, предположим, вы хотите выполнить эту распространённую операцию на диске и вернуть только фрейм, соответствующий этому запросу. Вы можете указать data_columns = True чтобы принудительно сделать все столбцы data_columns.

In [545]: df_dc = df.copy()

In [546]: df_dc["string"] = "foo"

In [547]: df_dc.loc[df_dc.index[4:6], "string"] = np.nan

In [548]: df_dc.loc[df_dc.index[7:9], "string"] = "bar"

In [549]: df_dc["string2"] = "cool"

In [550]: df_dc.loc[df_dc.index[1:3], ["B", "C"]] = 1.0

In [551]: df_dc
Out[551]: 
                   A         B         C string string2
2000-01-01  0.858644 -0.851236  1.058006    foo    cool
2000-01-02 -0.080372  1.000000  1.000000    foo    cool
2000-01-03  0.816983  1.000000  1.000000    foo    cool
2000-01-04  0.712795 -0.062433  0.736755    foo    cool
2000-01-05 -0.298721 -1.988045  1.475308    NaN    cool
2000-01-06  1.103675  1.382242 -0.650762    NaN    cool
2000-01-07 -0.729161 -0.142928 -1.063038    foo    cool
2000-01-08 -1.005977  0.465222 -0.094517    bar    cool

# on-disk operations
In [552]: store.append("df_dc", df_dc, data_columns=["B", "C", "string", "string2"])

In [553]: store.select("df_dc", where="B > 0")
Out[553]: 
                   A         B         C string string2
2000-01-02 -0.080372  1.000000  1.000000    foo    cool
2000-01-03  0.816983  1.000000  1.000000    foo    cool
2000-01-06  1.103675  1.382242 -0.650762    NaN    cool
2000-01-08 -1.005977  0.465222 -0.094517    bar    cool

# getting creative
In [554]: store.select("df_dc", "B > 0 & C > 0 & string == foo")
Out[554]: 
                   A    B    C string string2
2000-01-02 -0.080372  1.0  1.0    foo    cool
2000-01-03  0.816983  1.0  1.0    foo    cool

# this is in-memory version of this type of selection
In [555]: df_dc[(df_dc.B > 0) & (df_dc.C > 0) & (df_dc.string == "foo")]
Out[555]: 
                   A    B    C string string2
2000-01-02 -0.080372  1.0  1.0    foo    cool
2000-01-03  0.816983  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 [556]: store.root.df_dc.table
Out[556]: 
/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 to select и select_as_multiple чтобы возвращать итератор по результатам. По умолчанию возвращается 50 000 строк в чанке.

In [557]: for df in store.select("df", chunksize=3):
   .....:     print(df)
   .....: 
                   A         B         C
2000-01-01  0.858644 -0.851236  1.058006
2000-01-02 -0.080372 -1.268121  1.561967
2000-01-03  0.816983  1.965656 -1.169408
                   A         B         C
2000-01-04  0.712795 -0.062433  0.736755
2000-01-05 -0.298721 -1.988045  1.475308
2000-01-06  1.103675  1.382242 -0.650762
                   A         B         C
2000-01-07 -0.729161 -0.142928 -1.063038
2000-01-08 -1.005977  0.465222 -0.094517

Примечание

Вы также можете использовать итератор с read_hdf который откроет, а затем автоматически закроет хранилище после завершения итерации.

for df in pd.read_hdf("store.h5", "df", chunksize=3):
    print(df)

Обратите внимание, что ключевое слово chunksize применяется к источник строк. Таким образом, если вы выполняете запрос, то chunksize разделит общее количество строк в таблице и применит запрос, возвращая итератор по потенциально неравным по размеру фрагментам.

Вот рецепт для генерации запроса и его использования для создания возвращаемых частей равного размера.

In [558]: dfeq = pd.DataFrame({"number": np.arange(1, 11)})

In [559]: dfeq
Out[559]: 
   number
0       1
1       2
2       3
3       4
4       5
5       6
6       7
7       8
8       9
9      10

In [560]: store.append("dfeq", dfeq, data_columns=["number"])

In [561]: def chunks(l, n):
   .....:     return [l[i: i + n] for i in range(0, len(l), n)]
   .....: 

In [562]: evens = [2, 4, 6, 8, 10]

In [563]: coordinates = store.select_as_coordinates("dfeq", "number=evens")

In [564]: 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 [565]: store.select_column("df_dc", "index")
Out[565]: 
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 [566]: store.select_column("df_dc", "string")
Out[566]: 
0    foo
1    foo
2    foo
3    foo
4    NaN
5    NaN
6    foo
7    bar
Name: string, dtype: object
Выбор координат#

Иногда нужно получить координаты (т.е. позиции индексов) вашего запроса. Это возвращает Index результирующих позиций. Эти координаты также можно передать в последующие where операции.

In [567]: df_coord = pd.DataFrame(
   .....:     np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
   .....: )
   .....: 

In [568]: store.append("df_coord", df_coord)

In [569]: c = store.select_as_coordinates("df_coord", "index > 20020101")

In [570]: c
Out[570]: 
Index([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 [571]: store.select("df_coord", where=c)
Out[571]: 
                   0         1
2002-01-02  0.007717  1.168386
2002-01-03  0.759328 -0.638934
2002-01-04 -1.154018 -0.324071
2002-01-05 -0.804551 -1.280593
2002-01-06 -0.047208  1.260503
...              ...       ...
2002-09-22 -1.139583  0.344316
2002-09-23 -0.760643 -1.306704
2002-09-24  0.059018  1.775482
2002-09-25  1.242255 -0.055457
2002-09-26  0.410317  2.194489

[268 rows x 2 columns]
Выбор с использованием маски where#

Иногда ваш запрос может включать создание списка строк для выбора. Обычно это mask будет результирующим index из операции индексирования. Этот пример выбирает месяцы datetimeindex, которые равны 5.

In [572]: df_mask = pd.DataFrame(
   .....:     np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
   .....: )
   .....: 

In [573]: store.append("df_mask", df_mask)

In [574]: c = store.select_column("df_mask", "index")

In [575]: where = c[pd.DatetimeIndex(c).month == 5].index

In [576]: store.select("df_mask", where=where)
Out[576]: 
                   0         1
2000-05-01  1.479511  0.516433
2000-05-02 -0.334984 -1.493537
2000-05-03  0.900321  0.049695
2000-05-04  0.614266 -1.077151
2000-05-05  0.233881  0.493246
...              ...       ...
2002-05-27  0.294122  0.457407
2002-05-28 -1.102535  1.215650
2002-05-29 -0.432911  0.753606
2002-05-30 -1.105212  2.311877
2002-05-31  2.567296  2.610691

[93 rows x 2 columns]
Объект Storer#

Если вы хотите проверить сохраненный объект, получите его через get_storer. Вы можете использовать это программно, чтобы, например, получить количество строк в объекте.

In [577]: store.get_storer("df_dc").nrows
Out[577]: 8

Запросы к нескольким таблицам#

Методы append_to_multiple и select_as_multiple может выполнять добавление/выборку из нескольких таблиц одновременно. Идея состоит в том, чтобы иметь одну таблицу (назовем ее таблицей-селектором), в которой индексируется большинство/все столбцы, и выполнять запросы по ней. Другие таблицы — это таблицы данных с индексом, соответствующим индексу таблицы-селектора. Затем можно выполнить очень быстрый запрос к таблице-селектору, но получить много данных. Этот метод похож на использование очень широкой таблицы, но позволяет выполнять более эффективные запросы.

The append_to_multiple метод разделяет заданный DataFrame на несколько таблиц в соответствии с d, словарь, который сопоставляет имена таблиц со списком 'столбцов', которые вы хотите в этой таблице. Если None используется вместо списка, эта таблица будет содержать оставшиеся неуказанные столбцы данного DataFrame. Аргумент selector определяет, какая таблица является таблицей-селектором (из которой можно делать запросы). Аргумент dropna будет удалять строки из входных данных DataFrame чтобы обеспечить синхронизацию таблиц. Это означает, что если строка для одной из записываемых таблиц полностью np.nan, эта строка будет удалена из всех таблиц.

Если dropna равно False, ПОЛЬЗОВАТЕЛЬ ОТВЕЧАЕТ ЗА СИНХРОНИЗАЦИЮ ТАБЛИЦ. Помните, что полностью np.Nan строки не записываются в HDFStore, поэтому если вы решите вызвать dropna=False, некоторые таблицы могут иметь больше строк, чем другие, и поэтому select_as_multiple может не работать или возвращать неожиданные результаты.

In [578]: 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 [579]: df_mt["foo"] = "bar"

In [580]: df_mt.loc[df_mt.index[1], ("A", "B")] = np.nan

# you can also create the tables individually
In [581]: store.append_to_multiple(
   .....:     {"df1_mt": ["A", "B"], "df2_mt": None}, df_mt, selector="df1_mt"
   .....: )
   .....: 

In [582]: store
Out[582]: 

File path: store.h5

# individual tables were created
In [583]: store.select("df1_mt")
Out[583]: 
                   A         B
2000-01-01  0.162291 -0.430489
2000-01-02       NaN       NaN
2000-01-03  0.429207 -1.099274
2000-01-04  1.869081 -1.466039
2000-01-05  0.092130 -1.726280
2000-01-06  0.266901 -0.036854
2000-01-07 -0.517871 -0.990317
2000-01-08 -0.231342  0.557402

In [584]: store.select("df2_mt")
Out[584]: 
                   C         D         E         F  foo
2000-01-01 -2.502042  0.668149  0.460708  1.834518  bar
2000-01-02  0.130441 -0.608465  0.439872  0.506364  bar
2000-01-03 -1.069546  1.236277  0.116634 -1.772519  bar
2000-01-04  0.137462  0.313939  0.748471 -0.943009  bar
2000-01-05  0.836517  2.049798  0.562167  0.189952  bar
2000-01-06  1.112750 -0.151596  1.503311  0.939470  bar
2000-01-07 -0.294348  0.335844 -0.794159  1.495614  bar
2000-01-08  0.860312 -0.538674 -0.541986 -1.759606  bar

# as a multiple
In [585]: store.select_as_multiple(
   .....:     ["df1_mt", "df2_mt"],
   .....:     where=["A>0", "B>0"],
   .....:     selector="df1_mt",
   .....: )
   .....: 
Out[585]: 
Empty DataFrame
Columns: [A, B, C, D, E, F, foo]
Index: []

Удалить из таблицы#

Вы можете удалять из таблицы выборочно, указав 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 включает сжатие.

  • 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 поддерживает только параллельное чтение (через потоки или процессы). Если требуется чтение и запись одновременно, вам нужно сериализовать эти операции в одном потоке в одном процессе. В противном случае вы повредите свои данные. Смотрите (GH 2397) для получения дополнительной информации.

  • Если вы используете блокировки для управления доступом на запись между несколькими процессами, вы можете захотеть использовать fsync() перед снятием блокировок записи. Для удобства вы можете использовать store.flush(fsync=True) сделать это за вас.

  • Как только table создаются столбцы (DataFrame) фиксированными; можно добавлять только точно такие же столбцы

  • Обратите внимание, что часовые пояса (например, pytz.timezone('US/Eastern')) не обязательно равны между версиями часовых поясов. Поэтому если данные локализованы в определенный часовой пояс в HDFStore с использованием одной версии библиотеки часовых поясов, и эти данные обновляются другой версией, данные будут преобразованы в UTC, поскольку эти часовые пояса не считаются равными. Используйте либо ту же версию библиотеки часовых поясов, либо используйте tz_convert с обновленным определением часового пояса.

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

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

Типы данных#

HDFStore будет сопоставлять тип данных object с PyTables базовый dtype. Это означает, что следующие типы известны как работающие:

Тип

Представляет отсутствующие значения

floating : float64, float32, float16

np.nan

целое число : int64, int32, int8, uint64,uint32, uint8

логический

datetime64[ns]

NaT

timedelta64[ns]

NaT

categorical : см. раздел ниже

объект : strings

np.nan

unicode столбцы не поддерживаются, и НЕ СРАБОТАЕТ.

Категориальные данные#

Вы можете записывать данные, содержащие category типы данных в HDFStore. Запросы работают так же, как если бы это был массив объектов. Однако category данные с типом dtype хранятся более эффективным способом.

In [586]: dfcat = pd.DataFrame(
   .....:     {"A": pd.Series(list("aabbcdba")).astype("category"), "B": np.random.randn(8)}
   .....: )
   .....: 

In [587]: dfcat
Out[587]: 
   A         B
0  a -1.520478
1  a -1.069391
2  b -0.551981
3  b  0.452407
4  c  0.409257
5  d  0.301911
6  b -0.640843
7  a -2.253022

In [588]: dfcat.dtypes
Out[588]: 
A    category
B     float64
dtype: object

In [589]: cstore = pd.HDFStore("cats.h5", mode="w")

In [590]: cstore.append("dfcat", dfcat, format="table", data_columns=["A"])

In [591]: result = cstore.select("dfcat", where="A in ['b', 'c']")

In [592]: result
Out[592]: 
   A         B
2  b -0.551981
3  b  0.452407
4  c  0.409257
6  b -0.640843

In [593]: result.dtypes
Out[593]: 
A    category
B     float64
dtype: object

Строковые столбцы#

min_itemsize

Базовая реализация HDFStore использует фиксированную ширину столбца (размер элемента) для строковых столбцов. Размер элемента строкового столбца вычисляется как максимум длины данных (для этого столбца), которые передаются в HDFStore, в первом добавлении. Последующие добавления могут ввести строку для столбца больше чем может вместить столбец, будет вызвано исключение (в противном случае вы можете получить тихое усечение этих столбцов, приводящее к потере информации). В будущем мы можем ослабить это и разрешить пользовательское усечение.

Передать min_itemsize при первом создании таблицы, чтобы заранее указать минимальную длину конкретного строкового столбца. min_itemsize может быть целым числом или словарём, сопоставляющим имя столбца с целым числом. Вы можете передать values в качестве ключа для разрешения всех индексируемые объекты или data_columns для установки этого min_itemsize.

Передача min_itemsize dict приведет к созданию всех переданных столбцов как data_columns автоматически.

Примечание

Если вы не передаете никакие data_columns, тогда min_itemsize будет максимальной длиной любой переданной строки

In [594]: dfs = pd.DataFrame({"A": "foo", "B": "bar"}, index=list(range(5)))

In [595]: dfs
Out[595]: 
     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 [596]: store.append("dfs", dfs, min_itemsize=30)

In [597]: store.get_storer("dfs").table
Out[597]: 
/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 [598]: store.append("dfs2", dfs, min_itemsize={"A": 30})

In [599]: store.get_storer("dfs2").table
Out[599]: 
/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 [600]: dfss = pd.DataFrame({"A": ["foo", "bar", "nan"]})

In [601]: dfss
Out[601]: 
     A
0  foo
1  bar
2  nan

In [602]: store.append("dfss", dfss)

In [603]: store.select("dfss")
Out[603]: 
     A
0  foo
1  bar
2  NaN

# here you need to specify a different nan rep
In [604]: store.append("dfss2", dfss, nan_rep="_nan_")

In [605]: store.select("dfss2")
Out[605]: 
     A
0  foo
1  bar
2  nan

Производительность#

  • tables формат имеет штраф производительности при записи по сравнению с fixed хранилища. Преимущество - возможность добавления/удаления и запроса (потенциально очень больших объемов данных). Время записи обычно больше по сравнению с обычными хранилищами. Время запроса может быть довольно быстрым, особенно по индексированной оси.

  • Вы можете передать chunksize= to append, указание размера чанков для записи (по умолчанию 50000). Это значительно снизит использование памяти при записи.

  • Вы можете передать expectedrows= к первому append, чтобы установить ОБЩЕЕ количество строк, которые PyTables будет ожидать. Это оптимизирует производительность чтения/записи.

  • Дублирующиеся строки могут быть записаны в таблицы, но отфильтровываются при выборе (с выбором последних элементов; таким образом, таблица уникальна по парам major, minor)

  • A PerformanceWarning будет вызвано, если вы пытаетесь сохранить типы, которые будут сериализованы PyTables (а не сохранены как встроенные типы). См. Здесь для получения дополнительной информации и некоторых решений.

Feather#

Feather предоставляет бинарную колоночную сериализацию для фреймов данных. Он разработан для эффективного чтения и записи фреймов данных и для легкого обмена данными между языками анализа данных.

Feather предназначен для точной сериализации и десериализации DataFrame, поддерживая все типы данных pandas, включая расширенные типы, такие как категориальные и datetime с tz.

Несколько предостережений:

  • Формат НЕ будет записывать Index, или MultiIndex для DataFrame и вызовет ошибку, если предоставлен нестандартный. Вы можете .reset_index() для хранения индекса или .reset_index(drop=True) чтобы игнорировать это.

  • Дублирующиеся имена столбцов и нестроковые имена столбцов не поддерживаются

  • Фактические объекты Python в столбцах типа object не поддерживаются. Они вызовут полезное сообщение об ошибке при попытке сериализации.

См. Полная документация.

In [606]: 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 [607]: df
Out[607]: 
   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 [608]: df.dtypes
Out[608]: 
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 [609]: df.to_feather("example.feather")

Чтение из файла feather.

In [610]: result = pd.read_feather("example.feather")

In [611]: result
Out[611]: 
   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 [612]: result.dtypes
Out[612]: 
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 s, поддерживающий все типы данных pandas, включая расширенные типы, такие как datetime с часовым поясом.

Несколько предостережений.

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

  • The pyarrow движок всегда записывает индекс в вывод, но fastparquet записывает только нестандартные индексы. Этот дополнительный столбец может вызывать проблемы для потребителей, не использующих pandas, которые его не ожидают. Вы можете принудительно включать или исключать индексы с помощью index аргумент, независимо от базового движка.

  • Имена уровней индекса, если указаны, должны быть строками.

  • В pyarrow движок, категориальные типы данных для нестроковых типов могут быть сериализованы в parquet, но будут десериализованы как их примитивный тип данных.

  • The pyarrow движок сохраняет ordered флаг категориальных dtypes со строковыми типами. fastparquet не сохраняет ordered флаг.

  • Неподдерживаемые типы включают Interval и фактические типы объектов Python. Они вызовут полезное сообщение об ошибке при попытке сериализации. Period тип поддерживается с pyarrow >= 0.16.0.

  • The pyarrow движок сохраняет расширенные типы данных, такие как nullable integer и string data type (требуется 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 [613]: 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 [614]: df
Out[614]: 
   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 [615]: df.dtypes
Out[615]: 
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 [616]: df.to_parquet("example_pa.parquet", engine="pyarrow")

In [617]: df.to_parquet("example_fp.parquet", engine="fastparquet")

Чтение из файла parquet.

In [618]: result = pd.read_parquet("example_fp.parquet", engine="fastparquet")

In [619]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow")

In [620]: result.dtypes
Out[620]: 
a                        object
b                         int64
c                         uint8
d                       float64
e                          bool
f                datetime64[ns]
g    datetime64[ns, US/Eastern]
h                      category
i                      category
dtype: object

Установкой dtype_backend аргумент позволяет управлять типами данных по умолчанию для результирующего DataFrame.

In [621]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow", dtype_backend="pyarrow")

In [622]: result.dtypes
Out[622]: 
a                                      string[pyarrow]
b                                       int64[pyarrow]
c                                       uint8[pyarrow]
d                                      double[pyarrow]
e                                        bool[pyarrow]
f                               timestamp[ns][pyarrow]
g                timestamp[ns, tz=US/Eastern][pyarrow]
h    dictionary
i    dictionary
dtype: object

Примечание

Обратите внимание, что это не поддерживается для fastparquet.

Чтение только определённых столбцов файла parquet.

In [623]: result = pd.read_parquet(
   .....:     "example_fp.parquet",
   .....:     engine="fastparquet",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [624]: result = pd.read_parquet(
   .....:     "example_pa.parquet",
   .....:     engine="pyarrow",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [625]: result.dtypes
Out[625]: 
a    object
b     int64
dtype: object

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

Сериализация DataFrame При сохранении в формат parquet неявный индекс может быть включен как одна или несколько колонок в выходном файле. Таким образом, этот код:

In [626]: df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})

In [627]: df.to_parquet("test.parquet", engine="pyarrow")

создает файл parquet с три столбцы, если вы используете pyarrow для сериализации: a, b, и __index_level_0__. Если вы используете fastparquet, индекс может или не может будет записано в файл.

Этот неожиданный дополнительный столбец заставляет некоторые базы данных, такие как Amazon Redshift, отклонять файл, потому что этот столбец не существует в целевой таблице.

Если вы хотите опустить индексы датафрейма при записи, передайте index=False to to_parquet():

In [628]: df.to_parquet("test.parquet", index=False)

Это создаёт файл parquet только с двумя ожидаемыми столбцами, a и b. Если ваш DataFrame имеет пользовательский индекс, вы не получите его обратно при загрузке этого файла в DataFrame.

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

Разделение файлов Parquet#

Parquet поддерживает разделение данных на основе значений одного или нескольких столбцов.

In [629]: df = pd.DataFrame({"a": [0, 0, 1, 1], "b": [0, 1, 0, 1]})

In [630]: df.to_parquet(path="test", engine="pyarrow", partition_cols=["a"], compression=None)

The path указывает родительский каталог, в который будут сохранены данные. partition_cols являются именами столбцов, по которым набор данных будет разделён. Столбцы разделяются в порядке их указания. Разделы разделов определяются уникальными значениями в столбцах разделов. Приведённый выше пример создаёт разделённый набор данных, который может выглядеть так:

test
├── a=0
│   ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet
│   └──  ...
└── a=1
    ├── e6ab24a4f45147b49b54a662f0c412a3.parquet
    └── ...

ORC#

Аналогично 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 [631]: 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 [632]: df
Out[632]: 
   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 [633]: df.dtypes
Out[633]: 
a            object
b             int64
c           float64
d              bool
e    datetime64[ns]
dtype: object

Запись в файл orc.

In [634]: df.to_orc("example_pa.orc", engine="pyarrow")

Чтение из файла orc.

In [635]: result = pd.read_orc("example_pa.orc")

In [636]: result.dtypes
Out[636]: 
a            object
b             int64
c           float64
d              bool
e    datetime64[ns]
dtype: object

Прочитать только определённые столбцы файла orc.

In [637]: result = pd.read_orc(
   .....:     "example_pa.orc",
   .....:     columns=["a", "b"],
   .....: )
   .....: 

In [638]: result.dtypes
Out[638]: 
a    object
b     int64
dtype: object

SQL-запросы#

The pandas.io.sql модуль предоставляет набор обёрток запросов для облегчения извлечения данных и уменьшения зависимости от API конкретной базы данных.

При наличии возможности пользователям следует сначала рассмотреть Apache Arrow ADBC драйверы. Эти драйверы должны обеспечивать наилучшую производительность, обработку null и определение типов.

Добавлено в версии 2.2.0: Добавлена нативная поддержка драйверов ADBC

Полный список драйверов ADBC и их статус разработки см. в Статус реализации драйвера ADBC документация.

Если драйвер ADBC недоступен или может не хватать функциональности, пользователям следует установить SQLAlchemy вместе с библиотекой драйвера базы данных. Примеры таких драйверов: psycopg2 для PostgreSQL или pymysql для MySQL. Для SQLite это включено в стандартную библиотеку Python по умолчанию. Обзор поддерживаемых драйверов для каждого SQL-диалекта можно найти в документация SQLAlchemy.

Если SQLAlchemy не установлен, вы можете использовать sqlite3.Connection вместо движка, соединения или строки URI SQLAlchemy.

См. также некоторые примеры из кулинарной книги для некоторых продвинутых стратегий.

Ключевые функции:

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-запрос). Имена таблиц не нужно заключать в кавычки, если они содержат специальные символы.

В следующем примере мы используем SQlite Движок базы данных SQL. Можно использовать временную базу данных SQLite, где данные хранятся в «памяти».

Для подключения с использованием драйвера ADBC вам потребуется установить adbc_driver_sqlite используя ваш менеджер пакетов. После установки вы можете использовать интерфейс DBAPI, предоставляемый драйвером ADBC, для подключения к вашей базе данных.

import adbc_driver_sqlite.dbapi as sqlite_dbapi

# Create the connection
with sqlite_dbapi.connect("sqlite:///:memory:") as conn:
     df = pd.read_sql_table("data", conn)

Для подключения к SQLAlchemy используйте create_engine() функция для создания объекта движка из URI базы данных. Вам нужно создать движок только один раз для каждой базы данных, к которой вы подключаетесь. Для получения дополнительной информации о create_engine() и форматирование URI, см. примеры ниже и SQLAlchemy документация

In [639]: from sqlalchemy import create_engine

# Create your engine.
In [640]: engine = create_engine("sqlite:///:memory:")

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

with engine.connect() as conn, conn.begin():
    data = pd.read_sql_table("data", conn)

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

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

Запись DataFrames#

Предполагая, что следующие данные находятся в 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 [641]: import datetime

In [642]: c = ["id", "Date", "Col_1", "Col_2", "Col_3"]

In [643]: 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 [644]: data = pd.DataFrame(d, columns=c)

In [645]: data
Out[645]: 
   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 [646]: data.to_sql("data", con=engine)
Out[646]: 3

С некоторыми базами данных запись больших DataFrames может вызывать ошибки из-за превышения ограничений на размер пакета. Этого можно избежать, установив chunksize параметр при вызове to_sql. Например, следующее записывает data в базу данных партиями по 1000 строк за раз:

In [647]: data.to_sql("data_chunked", con=engine, chunksize=1000)
Out[647]: 3

Типы данных SQL#

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

Для наилучших шансов сохранения типов базы данных пользователям рекомендуется использовать драйверы ADBC, когда они доступны. Система типов Arrow предлагает более широкий набор типов, которые более точно соответствуют типам баз данных, чем историческая система типов pandas/NumPy. Для иллюстрации обратите внимание на этот (неисчерпывающий) список типов, доступных в различных базах данных и бэкендах pandas:

numpy/pandas

arrow

postgres

sqlite

int16/Int16

int16

SMALLINT

INTEGER

int32/Int32

int32

INTEGER

INTEGER

int64/Int64

int64

BIGINT

INTEGER

float32

float32

REAL

REAL

float64

float64

DOUBLE PRECISION

REAL

object

string

TEXT

TEXT

bool

bool_

BOOLEAN

datetime64[ns]

timestamp(us)

ВРЕМЕННАЯ МЕТКА

datetime64[ns,tz]

timestamp(us,tz)

TIMESTAMPTZ

date32

ДАТА

month_day_nano_interval

INTERVAL

бинарный

BINARY

BLOB

decimal128

DECIMAL [1]

list

ARRAY [1]

struct

СОСТАВНОЙ ТИП

[1]

Сноски

Если вы заинтересованы в максимально возможном сохранении типов базы данных на протяжении всего жизненного цикла вашего DataFrame, пользователям рекомендуется использовать dtype_backend="pyarrow" аргумент read_sql()

# for roundtripping
with pg_dbapi.connect(uri) as conn:
    df2 = pd.read_sql("pandas_table", conn, dtype_backend="pyarrow")

Это предотвратит преобразование ваших данных в традиционную систему типов pandas/NumPy, которая часто преобразует SQL-типы таким образом, что делает невозможным их обратное преобразование.

В случае, если драйвер ADBC недоступен, to_sql() попытается сопоставить ваши данные с подходящим типом данных SQL на основе dtype данных. Когда у вас есть столбцы с dtype object, pandas попытается вывести тип данных.

Вы всегда можете переопределить тип по умолчанию, указав желаемый SQL-тип для любого из столбцов, используя dtype аргумент. Этот аргумент требует словарь, сопоставляющий имена столбцов с типами SQLAlchemy (или строками для режима отката sqlite3). Например, указание использовать sqlalchemy String тип вместо стандартного Text тип для строковых столбцов:

In [648]: from sqlalchemy.types import String

In [649]: data.to_sql("data_dtype", con=engine, dtype={"Col_1": String})
Out[649]: 3

Примечание

Из-за ограниченной поддержки timedelta в различных типах баз данных, столбцы с типом timedelta64 будет записано как целочисленные значения в наносекундах в базу данных, и будет выдано предупреждение. Единственное исключение — при использовании драйвера ADBC PostgreSQL, в этом случае интервал времени будет записан в базу данных как INTERVAL

Примечание

Столбцы category тип данных будет преобразован в плотное представление, как вы получили бы с np.asarray(categorical) (например, для строковых категорий это даёт массив строк). Из-за этого чтение таблицы базы данных обратно не создать категориальный тип.

Типы данных даты и времени#

Используя ADBC или SQLAlchemy, to_sql() способен записывать данные datetime, которые не учитывают часовой пояс или учитывают его. Однако итоговые данные, хранящиеся в базе данных, в конечном счете зависят от поддерживаемого типа данных для данных datetime в используемой системе баз данных.

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

База данных

Типы даты и времени SQL

Поддержка часовых поясов

SQLite

TEXT

Нет

MySQL

TIMESTAMP или DATETIME

Нет

PostgreSQL

TIMESTAMP или TIMESTAMP WITH TIME ZONE

Да

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

read_sql_table() также способен читать данные даты и времени, которые учитывают часовой пояс или не учитывают. При чтении 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(), вы должен иметь драйвер ADBC или установленную опциональную зависимость SQLAlchemy.

In [650]: pd.read_sql_table("data", engine)
Out[650]: 
   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

Примечание

Драйверы ADBC будут напрямую сопоставлять типы базы данных с типами arrow. Для других драйверов обратите внимание, что pandas выводит типы столбцов из результатов запроса, а не путем поиска типов данных в физической схеме базы данных. Например, предположим userid является целочисленным столбцом в таблице. Тогда, интуитивно, select userid ... будет возвращать Series с целочисленными значениями, в то время как select cast(userid as text) ... будет возвращать Series со значениями типа object (str). Соответственно, если результат запроса пуст, то все результирующие столбцы будут возвращены как object-valued (поскольку они являются наиболее общими). Если вы предполагаете, что ваш запрос иногда будет генерировать пустой результат, вы можете явно привести типы после этого, чтобы обеспечить целостность dtype.

Вы также можете указать имя столбца как DataFrame индекс, и укажите подмножество столбцов для чтения.

In [651]: pd.read_sql_table("data", engine, index_col="id")
Out[651]: 
    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 [652]: pd.read_sql_table("data", engine, columns=["Col_1", "Col_2"])
Out[652]: 
  Col_1  Col_2
0     X  27.50
1     Y -12.50
2     Z   5.73

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

In [653]: pd.read_sql_table("data", engine, parse_dates=["Date"])
Out[653]: 
   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(name="table", con=engine, schema="other_schema")
pd.read_sql_table("table", engine, schema="other_schema")

Запросы#

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

In [654]: pd.read_sql_query("SELECT * FROM data", engine)
Out[654]: 
   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 [655]: pd.read_sql_query("SELECT id, Col_1, Col_2 FROM data WHERE id = 42;", engine)
Out[655]: 
   id Col_1  Col_2
0  42     Y  -12.5

The read_sql_query() функция поддерживает chunksize аргумент. Указание этого вернет итератор по частям результата запроса:

In [656]: df = pd.DataFrame(np.random.randn(20, 3), columns=list("abc"))

In [657]: df.to_sql(name="data_chunks", con=engine, index=False)
Out[657]: 20
In [658]: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5):
   .....:     print(chunk)
   .....: 
          a         b         c
0 -0.395347 -0.822726 -0.363777
1  1.676124 -0.908102 -1.391346
2 -1.094269  0.278380  1.205899
3  1.503443  0.932171 -0.709459
4 -0.645944 -1.351389  0.132023
          a         b         c
0  0.210427  0.192202  0.661949
1  1.690629 -1.046044  0.618697
2 -0.013863  1.314289  1.951611
3 -1.485026  0.304662  1.194757
4 -0.446717  0.528496 -0.657575
          a         b         c
0 -0.876654  0.336252  0.172668
1  0.337684 -0.411202 -0.828394
2 -0.244413  1.094948  0.087183
3  1.125934 -1.480095  1.205944
4 -0.451849  0.452214 -2.208192
          a         b         c
0 -2.061019  0.044184 -0.017118
1  1.248959 -0.675595 -1.908296
2 -0.125934  1.491974  0.648726
3  0.391214  0.438609  1.634248
4  1.208707 -1.535740  1.620399

Примеры подключения движка#

Для подключения к 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:///
# where  is relative:
engine = create_engine("sqlite:///foo.db")

# or absolute, starting with a slash:
engine = create_engine("sqlite:////absolute/path/to/foo.db")

Для получения дополнительной информации см. примеры SQLAlchemy документация

Расширенные запросы SQLAlchemy#

Вы можете использовать конструкции SQLAlchemy для описания вашего запроса.

Используйте sqlalchemy.text() для указания параметров запроса независимо от бэкенда

In [659]: import sqlalchemy as sa

In [660]: pd.read_sql(
   .....:     sa.text("SELECT * FROM data where Col_1=:col1"), engine, params={"col1": "X"}
   .....: )
   .....: 
Out[660]: 
   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 [661]: metadata = sa.MetaData()

In [662]: 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 [663]: pd.read_sql(sa.select(data_table).where(data_table.c.Col_3 is True), engine)
Out[663]: 
Empty DataFrame
Columns: [index, Date, Col_1, Col_2, Col_3]
Index: []

Вы можете комбинировать выражения SQLAlchemy с параметрами, переданными в read_sql() используя sqlalchemy.bindparam()

In [664]: import datetime as dt

In [665]: expr = sa.select(data_table).where(data_table.c.Date > sa.bindparam("date"))

In [666]: pd.read_sql(expr, engine, params={"date": dt.datetime(2010, 10, 18)})
Out[666]: 
   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#

The pandas-gbq пакет предоставляет функциональность для чтения/записи из Google BigQuery.

pandas интегрируется с этим внешним пакетом. если pandas-gbq установлен, вы можете использовать методы pandas pd.read_gbq и DataFrame.to_gbq, который вызовет соответствующие функции из pandas-gbq.

Полную документацию можно найти здесь.

Формат Stata#

Запись в формат stata#

Метод DataFrame.to_stata() запишет DataFrame в файл .dta. Версия формата этого файла всегда 115 (Stata 12).

In [667]: df = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))

In [668]: df.to_stata("stata.dta")

Stata файлы данных имеют ограниченную поддержку типов данных; только строки с 244 или менее символами, int8, int16, int32, float32 и float64 может храниться в .dta файлы. Кроме того, Stata резервирует определенные значения для представления отсутствующих данных. Экспорт неотсутствующего значения, которое находится вне допустимого диапазона в Stata для определенного типа данных, изменит тип переменной на следующий больший размер. Например, int8 значения ограничены диапазоном от -127 до 100 в Stata, поэтому переменные со значениями выше 100 вызовут преобразование в int16. nan значения в типах данных с плавающей точкой хранятся как базовый тип отсутствующих данных (. в Stata).

Примечание

Невозможно экспортировать пропущенные значения для целочисленных типов данных.

The Stata записыватель корректно обрабатывает другие типы данных, включая int64, bool, uint8, uint16, uint32 путем приведения к наименьшему поддерживаемому типу, который может представлять данные. Например, данные с типом uint8 будет приведено к int8 если все значения меньше 100 (верхняя граница для не-пропущенных int8 данные в Stata), или, если значения выходят за пределы этого диапазона, переменная приводится к int16.

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

Преобразование из int64 to float64 может привести к потере точности если int64 значения больше 2**53.

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

StataWriter и DataFrame.to_stata() поддерживают только строки фиксированной ширины длиной до 244 символов, ограничение, наложенное форматом файла dta версии 115. Попытка записать Stata файлы dta со строками длиной более 244 символов вызывают ValueError.

Чтение из формата Stata#

Функция верхнего уровня read_stata будет читать файл dta и возвращать либо DataFrame или pandas.api.typing.StataReader который можно использовать для инкрементального чтения файла.

In [669]: pd.read_stata("stata.dta")
Out[669]: 
   index         A         B
0      0 -0.165614  0.490482
1      1 -0.637829  0.067091
2      2 -0.242577  1.348038
3      3  0.647699 -0.644937
4      4  0.625771  0.918376
5      5  0.401781 -1.488919
6      6 -0.981845 -0.046882
7      7 -0.306796  0.877025
8      8 -0.336606  0.624747
9      9 -1.582600  0.806340

Указание chunksize возвращает pandas.api.typing.StataReader экземпляр, который можно использовать для чтения chunksize строк из файла за раз. StataReader объект может использоваться как итератор.

In [670]: 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 [671]: 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 сохраняются при импорте.

Примечание

Все StataReader объекты, созданные ли read_stata() (при использовании iterator=True или chunksize) или созданный вручную, должен использоваться как контекстный менеджер (например, the with оператор). В то время как close() метод доступен, его использование не поддерживается. Он не является частью публичного API и будет удален в будущем без предупреждения.

Категориальные данные#

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) и 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)

The спецификация для формата файла xport доступен на веб-сайте SAS.

Официальная документация для формата SAS7BDAT недоступна.

Форматы SPSS#

Функция верхнего уровня 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()

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", key="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", key="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", key="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", key="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