Инструменты ввода-вывода (текст, CSV, HDF5 и т. д.)
API pandas для ввода-вывода — это набор основных reader функций, к которым обращаются так, как к pandas.read_csv(), и которые, как правило, возвращают объект pandas. Соответствующие writer функции — это методы объектов, к которым обращаются так, как к DataFrame.to_csv(). Ниже приведена таблица, содержащая доступные readers и writers.
Тип формата | Описание данных | Чтение | Запись |
|---|---|---|---|
текст | |||
текст | Файл с фиксированной шириной | ||
текст | |||
текст | |||
текст | |||
текст | |||
текст | Буфер обмена | ||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
двоичный | |||
SQL | |||
SQL |
Здесь представлено сравнение производительности некоторых из этих методов ввода-вывода.
Примечание
Для примеров, использующих класс StringIO, убедитесь, что вы импортировали его с помощью from io import StringIO для Python 3.
CSV & текстовые файлы
Функция для чтения текстовых файлов (также известных как плоские файлы) — это read_csv(). См. пособие для некоторых расширенных стратегий.
Параметры парсинга
read_csv() принимает следующие общие аргументы:
Основные
- filepath_or_buffer:разные
-
Путь к файлу (строка,
str,pathlib.Path, илиpy:py._path.local.LocalPath), URL (включая http, ftp и S3-локации) или любой объект с методомread()(таким как открытый файл илиStringIO). - sep:строка, по умолчанию ',' для read_csv(), \t для read_table()
-
Разделитель. Если sep равен
None, движок C не может автоматически определить разделитель, но движок парсинга Python может, что означает, что последний будет использован и автоматически определит разделитель с помощью встроенного инструмента определения разделителя Python,csv.Sniffer. Кроме того, разделители длиной более 1 символа и отличные от'\s+'будут интерпретироваться как регулярные выражения и также будут принудительно использовать движок парсинга Python. Обратите внимание, что разделители, основанные на регулярных выражениях, могут игнорировать данные в кавычках. Пример регулярного выражения:'\\r\\t'. - delimiter:строка, по умолчанию None
-
Альтернативное имя аргумента для sep.
- delim_whitespace:булево значение, по умолчанию False
-
Указывает, будет ли использоваться пробел (например,
' 'или'\t') в качестве разделителя. Эквивалентно установкеsep='\s+'. Если этот параметр установлен вTrue, для параметраdelimiterничего не следует передавать.
Расположение и имена столбцов и индексов
- header:целое число или список целых чисел, по умолчанию 'infer'
-
Номер(ы) строки, используемые в качестве имен столбцов и начала данных. По умолчанию имена столбцов вычисляются: если имена не переданы, поведение идентично
header=0, и имена столбцов вычисляются из первой строки файла; если имена столбцов явно переданы, поведение идентичноheader=None. Явно передайтеheader=0, чтобы заменить существующие имена.Заголовок может быть списком целых чисел, определяющих местоположения строк для MultiIndex столбцов, например,
[0,1,3]. Промежуточные строки, которые не указаны, будут пропущены (например, 2 в этом примере пропущен).Обратите внимание, что этот параметр игнорирует прокомментированные и пустые строки, если
skip_blank_lines=True, поэтому header=0 обозначает первую строку данных, а не первую строку файла. - names:последовательность, по умолчанию None
-
Список имен столбцов для использования. Если файл не содержит строки заголовка, то необходимо явно передать
header=None. Дубликаты в этом списке не допускаются. - index_col:целое число, строка, последовательность целых чисел/строк или False, необязательно, по умолчанию None
-
Столбец(ы) для использования в качестве меток строк DataFrame, заданный именем строки или индексом столбца. Если задана последовательность целых чисел/строк, используется MultiIndex.
Примечание
index_col=Falseможно использовать для принудительного отказа pandas от использования первого столбца в качестве индекса, например, при наличии поврежденного файла с разделителями в конце каждой строки.Значение по умолчанию
Noneуказывает pandas на угадывание. Если количество полей в строке заголовка столбцов равно количеству полей в теле файла данных, то используется индекс по умолчанию. Если оно больше, то используются первые столбцы в качестве индекса, чтобы оставшееся количество полей в теле было равно количеству полей в заголовке.Первая строка после заголовка используется для определения количества столбцов, которое войдет в индекс. Если последующие строки содержат меньше столбцов, чем первая строка, они заполняются
NaN.Этого можно избежать, используя
usecols. Это гарантирует, что столбцы берутся как есть, и отбрасываются последующие данные. - usecols:последовательность или вызываемый объект, по умолчанию None
-
Возвращает подмножество столбцов. Если это последовательность, все элементы должны быть либо позиционными (т. е. целочисленными индексами в столбцах документа), либо строками, соответствующими именам столбцов, предоставленными пользователем в
namesили выведенными из строки(строк) заголовка документа. Еслиnamesзаданы, строки(строки) заголовка документа не учитываются. Например, допустимым параметром последовательностиusecolsбыло бы[0, 1, 2]или['foo', 'bar', 'baz']. - squeeze:булево значение, по умолчанию False
-
Если проанализированные данные содержат только один столбец, возвращается
Series. - prefix:строка, по умолчанию None
-
Префикс, который нужно добавить к номерам столбцов, когда нет заголовка, например, «X» для X0, X1, …
- mangle_dupe_cols:булево значение, по умолчанию True
-
Дублирующие столбцы будут указаны как «X», «X.1»… «X.N», а не «X»…«X».
Общие параметры парсинга
- dtype:Имя типа или словарь столбец -> тип, по умолчанию None
-
Тип данных для данных или столбцов. Например,
{'a': np.float64, 'b': np.int32, 'c': 'Int64'}Используйтеstrилиobjectвместе с соответствующими параметрамиna_valuesдля сохранения и предотвращения интерпретации типа данных. Если заданы преобразователи, они будут применяться ВМЕСТО преобразования типа данных. - engine:{'c', 'python', 'pyarrow'}
-
Движок парсера для использования. Движки C и pyarrow быстрее, а движок python в настоящее время более функционален. Многопоточность в настоящее время поддерживается только движком pyarrow.
- converters:словарь, по умолчанию None
-
Словарь функций для преобразования значений в определенных столбцах. Ключи могут быть целыми числами или метками столбцов.
- true_values:список, по умолчанию None
-
Значения, которые следует рассматривать как
True. - false_values:список, по умолчанию None
-
Значения, которые следует рассматривать как
False. - skipinitialspace:булево значение, по умолчанию False
-
Пропустить пробелы после разделителя.
- skiprows:последовательность или целое число, по умолчанию None
-
Номера строк для пропуска (с индексом 0) или количество строк для пропуска (целое число) в начале файла.
- skipfooter:целое число, по умолчанию 0
-
Количество строк в конце файла, которые нужно пропустить (не поддерживается с engine='c').
- nrows:целое число, по умолчанию None
-
Количество строк файла для чтения. Полезно для чтения фрагментов больших файлов.
- low_memory:булево значение, по умолчанию True
-
Внутренняя обработка файла частями, что приводит к меньшему использованию памяти при парсинге, но, возможно, к смешанному выводу типа. Чтобы гарантировать отсутствие смешанных типов, установите
False, или укажите тип с параметромdtype. Обратите внимание, что весь файл считывается в одинDataFrameнезависимо, используйте параметрыchunksizeилиiteratorдля возврата данных частями. (Только с C парсером) - memory_map:булево значение, по умолчанию False
-
Если для
filepath_or_bufferзадан путь к файлу, отобразить объект файла непосредственно в памяти и получить доступ к данным непосредственно из него. Использование этого параметра может повысить производительность, так как больше нет накладных расходов ввода-вывода.
Обработка значений NA и отсутствующих данных
- na_values:скаляр, строка, список-подобный объект или словарь, по умолчанию None
-
Дополнительные строки для распознавания значений NA/NaN. Если передан словарь, значения NA специфичны для каждого столбца. См. на значения const ниже для списка значений, интерпретируемых как NaN по умолчанию.
- keep_default_na:булево значение, по умолчанию True
-
Включать ли значения NaN по умолчанию при разборе данных. В зависимости от того, что передано
na_values, поведение следующее:Если
keep_default_naравноTrue, аna_valuesзаданы,na_valuesдобавляется к значениям NaN по умолчанию, используемым для разбора.Если
keep_default_naравноTrue, аna_valuesне заданы, для разбора используются только значения NaN по умолчанию.Если
keep_default_naравноFalse, аna_valuesзаданы, для разбора используются только значения NaN, указанныеna_values.Если
keep_default_naравноFalse, аna_valuesне заданы, ни одна строка не будет интерпретироваться как NaN.
Обратите внимание, что если
na_filterпередано какFalse, параметрыkeep_default_naиna_valuesбудут игнорироваться. - na_filter:булево значение, по умолчанию True
-
Обнаруживать маркеры отсутствующих значений (пустые строки и значения na_values). В данных без значений NA передача
na_filter=Falseможет улучшить производительность чтения большого файла. - verbose:булево значение, по умолчанию False
-
Указывать количество значений NA, помещенных в нечисловые столбцы.
- skip_blank_lines:булево значение, по умолчанию True
-
Если
True, пропускать пустые строки вместо интерпретации их как значений NaN.
Обработка дат и времени
- parse_dates:булево значение или список целых чисел или имен или список списков или словарь, по умолчанию False.
-
Если
True-> попытка разбора индекса.Если
[1, 2, 3]-> попытка разбора столбцов 1, 2, 3 как отдельных столбцов дат.Если
[[1, 3]]-> объединение столбцов 1 и 3 и разбор как единого столбца дат.Если
{'foo': [1, 3]}-> разбор столбцов 1, 3 как дат и присвоение результату имени ‘foo’.
Примечание
Существует быстрый путь для дат в формате iso8601.
- infer_datetime_format:булево значение, по умолчанию False
-
Если
Trueи parse_dates включены для столбца, попробуйте определить формат даты и времени для ускорения обработки. - keep_date_col:булево значение, по умолчанию False
-
Если
Trueи parse_dates определяют объединение нескольких столбцов, сохранить исходные столбцы. - date_parser:функция, по умолчанию None
-
Функция для преобразования последовательности столбцов со строками в массив экземпляров даты и времени. По умолчанию используется
dateutil.parser.parserдля преобразования. pandas попытается вызвать date_parser тремя различными способами, переходя к следующему, если произойдет исключение: 1) Передать один или несколько массивов (как определено parse_dates) в качестве аргументов; 2) конкатенировать (по строкам) строковые значения из столбцов, определенных parse_dates, в один массив и передать его; и 3) вызвать date_parser один раз для каждой строки, используя одну или несколько строк (соответствующих столбцам, определенным parse_dates) в качестве аргументов. - dayfirst:булево значение, по умолчанию False
-
Даты в формате ДД/ММ, международный и европейский формат.
- cache_dates:булево значение, по умолчанию True
-
Если True, использовать кэш уникальных преобразованных дат для применения преобразования даты и времени. Может значительно ускорить разбор, если строки дат дублируются, особенно с часовыми поясами.
Новая функция в версии 0.25.0.
Итерация
- iterator:булево значение, по умолчанию False
-
Возвратить
TextFileReaderобъект для итерации или извлечения фрагментов сget_chunk(). - chunksize:целое число, по умолчанию None
-
Возвратить
TextFileReaderобъект для итерации. См. итерацию и фрагментацию ниже.
Кавычки, сжатие и формат файла
- compression:{'infer', 'gzip', 'bz2', 'zip', 'xz', 'zstd', None, dict}, default 'infer'
-
Для динамического распаковки данных на диске. Если ‘infer’, использовать gzip, bz2, zip, xz или zstandard, если
filepath_or_bufferявляется путём, заканчивающимся на ‘.gz’, ‘.bz2’, ‘.zip’, ‘.xz’, ‘.zst’, соответственно, и иначе - без распаковки. Если использовать ‘zip’, ZIP-файл должен содержать только один файл данных для чтения. Установите вNoneдля предотвращения распаковки. Также может быть словарь с ключом'method', установленным в одном из {'zip','gzip','bz2','zstd'} и другие пары ключ-значение передаютсяzipfile.ZipFile,gzip.GzipFile,bz2.BZ2File, илиzstandard.ZstdDecompressor. В качестве примера можно передать следующее для более быстрого сжатия и создания воспроизводимого gzip-архива:compression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1}.Изменено в версии 1.1.0: Расширение параметра dict для поддержки
gzipиbz2.Изменено в версии 1.2.0: Предыдущие версии передавали записи словаря для ‘gzip’ в
gzip.open. - thousands:строка, по умолчанию None
-
Разделитель тысяч.
- decimal:строка, по умолчанию '.'
-
Символ для распознавания десятичной точки. Например, используйте
','для европейских данных. - float_precision:строка, по умолчанию None
-
Указывает, какой конвертер C-движок должен использовать для значений с плавающей точкой. Варианты:
Noneдля обычного конвертера,highдля конвертера высокой точности иround_tripдля конвертера, обеспечивающего обратное преобразование. - lineterminator:строка (длина 1), по умолчанию None
-
Символ для разбиения файла на строки. Действителен только с C-парсером.
- quotechar:строка (длина 1)
-
Символ, используемый для обозначения начала и конца цитируемого элемента. Цитируемые элементы могут содержать разделитель, и он будет проигнорирован.
- quoting:целое число или экземпляр csv.QUOTE_*, по умолчанию 0
-
Управление поведением цитирования полей по
csv.QUOTE_*константам. Используйте один изQUOTE_MINIMAL(0),QUOTE_ALL(1),QUOTE_NONNUMERIC(2) илиQUOTE_NONE(3). - doublequote:булево значение, по умолчанию True
-
Когда
quotecharуказано, иquotingнеQUOTE_NONE, указывает, интерпретировать ли два последовательныхquotecharэлемента **внутри** поля как одинquotecharэлемент. - escapechar:строка (длина 1), по умолчанию None
-
Символ длиной в один символ, используемый для экранирования разделителя, когда цитирование
QUOTE_NONE. - comment:строка, по умолчанию None
-
Указывает, что остальная часть строки не должна анализироваться. Если найдена в начале строки, строка будет проигнорирована. Этот параметр должен быть символом. Как и пустые строки (пока
skip_blank_lines=True), полностью комментированные строки игнорируются параметромheader, но неskiprows. Например, еслиcomment='#', разбор ‘#empty\na,b,c\n1,2,3’ сheader=0приведет к тому, что ‘a,b,c’ будет рассматриваться как заголовок. - encoding:строка, по умолчанию None
-
Кодировка для использования с UTF при чтении/записи (например,
'utf-8'). Список стандартных кодировок Python. - dialect:строка или экземпляр csv.Dialect, по умолчанию None
-
Если предоставлено, этот параметр переопределит значения (по умолчанию или нет) следующих параметров:
delimiter,doublequote,escapechar,skipinitialspace,quotechar, иquoting. Если необходимо переопределить значения, будет выведено предупреждение ParserWarning. См.csv.Dialectдокументацию для получения дополнительной информации.
Обработка ошибок
- error_bad_lines:boolean, optional, default None
-
Строки с слишком большим количеством полей (например, строка CSV с слишком многими запятыми) по умолчанию вызывают исключение, и не будет возвращено
DataFrame. ЕслиFalse, то эти «плохие строки» будут удалены изDataFrame, которое возвращается. См. плохие строки ниже.Устарело начиная с версии 1.3.0: Вместо параметра
on_bad_linesследует использовать параметр для указания поведения при обнаружении плохой строки. - warn_bad_lines:boolean, optional, default None
-
Если error_bad_lines равно
False, и warn_bad_lines равноTrue, будет выводиться предупреждение для каждой «плохой строки».Устарело начиная с версии 1.3.0: Вместо параметра
on_bad_linesследует использовать параметр для указания поведения при обнаружении плохой строки. - on_bad_lines:(‘error’, ‘warn’, ‘skip’), default ‘error’
-
Указывает, что делать при обнаружении плохой строки (строки со слишком большим количеством полей). Допустимые значения:
‘error’, вызов ParserError при обнаружении плохой строки.
‘warn’, вывод предупреждения при обнаружении плохой строки и пропуск этой строки.
‘skip’, пропуск плохих строк без вывода предупреждений или исключений при их обнаружении.
Добавлена в версии 1.3.0.
Указание типов данных столбцов
Вы можете указать тип данных для всего DataFrame или отдельных столбцов:
In [13]: import numpy as np
In [14]: data = "a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11"
In [15]: print(data)
a,b,c,d
1,2,3,4
5,6,7,8
9,10,11
In [16]: df = pd.read_csv(StringIO(data), dtype=object)
In [17]: df
Out[17]:
a b c d
0 1 2 3 4
1 5 6 7 8
2 9 10 11 NaN
In [18]: df["a"][0]
Out[18]: '1'
In [19]: df = pd.read_csv(StringIO(data), dtype={"b": object, "c": np.float64, "d": "Int64"})
In [20]: df.dtypes
Out[20]:
a int64
b object
c float64
d Int64
dtype: object
К счастью, pandas предлагает более одного способа гарантировать, что ваши столбцы содержат только один dtype. Если вы не знакомы с этими понятиями, вы можете ознакомиться здесь, чтобы узнать больше о типах данных, и здесь, чтобы узнать больше о object преобразованиях в pandas.
Например, вы можете использовать аргумент converters метода read_csv():
In [21]: data = "col_1\n1\n2\n'A'\n4.22"
In [22]: df = pd.read_csv(StringIO(data), converters={"col_1": str})
In [23]: df
Out[23]:
col_1
0 1
1 2
2 'A'
3 4.22
In [24]: df["col_1"].apply(type).value_counts()
Out[24]:
<class 'str'> 4
Name: col_1, dtype: int64
Или вы можете использовать функцию to_numeric() для принудительного преобразования типов данных после чтения данных,
In [25]: df2 = pd.read_csv(StringIO(data))
In [26]: df2["col_1"] = pd.to_numeric(df2["col_1"], errors="coerce")
In [27]: df2
Out[27]:
col_1
0 1.00
1 2.00
2 NaN
3 4.22
In [28]: df2["col_1"].apply(type).value_counts()
Out[28]:
<class 'float'> 4
Name: col_1, dtype: int64
что преобразует все допустимые преобразования в числа с плавающей точкой, оставив недопустимые преобразования в виде NaN.
В конечном счёте, как вы будете обрабатывать чтение столбцов, содержащих смешанные типы данных, зависит от ваших конкретных потребностей. В приведенном выше примере, если вы хотите NaN выбросить аномалии данных, то to_numeric() , вероятно, является лучшим вариантом. Однако, если вам нужно, чтобы все данные были приведены к одному типу, независимо от типа, то использование аргумента converters метода read_csv() определённо стоит попробовать.
Примечание
В некоторых случаях чтение аномальных данных со столбцами, содержащими смешанные типы данных, приведет к несовместимому набору данных. Если вы полагаетесь на pandas для вывода типов данных ваших столбцов, движок парсинга будет определять типы данных для различных фрагментов данных, а не для всего набора данных сразу. В результате у вас могут получиться столбцы со смешанными типами данных. Например,
In [29]: col_1 = list(range(500000)) + ["a", "b"] + list(range(500000))
In [30]: df = pd.DataFrame({"col_1": col_1})
In [31]: df.to_csv("foo.csv")
In [32]: mixed_df = pd.read_csv("foo.csv")
In [33]: mixed_df["col_1"].apply(type).value_counts()
Out[33]:
<class 'int'> 737858
<class 'str'> 262144
Name: col_1, dtype: int64
In [34]: mixed_df["col_1"].dtype
Out[34]: dtype('O')
приведёт к mixed_df содержащему тип данных int для определённых фрагментов столбца и str для других из-за смешанных типов данных из считанных данных. Важно отметить, что весь столбец будет помечен типом dtype object, который используется для столбцов со смешанными типами данных.
Указание типа данных категорий
Столбцы Categorical могут быть распарсены напрямую, указав dtype='category' или dtype=CategoricalDtype(categories, ordered).
In [35]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3"
In [36]: pd.read_csv(StringIO(data))
Out[36]:
col1 col2 col3
0 a b 1
1 a b 2
2 c d 3
In [37]: pd.read_csv(StringIO(data)).dtypes
Out[37]:
col1 object
col2 object
col3 int64
dtype: object
In [38]: pd.read_csv(StringIO(data), dtype="category").dtypes
Out[38]:
col1 category
col2 category
col3 category
dtype: object
Отдельные столбцы могут быть распарсены как Categorical с помощью указания словаря:
In [39]: pd.read_csv(StringIO(data), dtype={"col1": "category"}).dtypes
Out[39]:
col1 category
col2 object
col3 int64
dtype: object
Указание dtype='category' приведёт к неотсортированной Categorical, категории которой будут уникальные значения, наблюдаемые в данных. Для большего контроля над категориями и порядком, создайте CategoricalDtype заранее и передайте его для dtype столбца.
In [40]: from pandas.api.types import CategoricalDtype
In [41]: dtype = CategoricalDtype(["d", "c", "b", "a"], ordered=True)
In [42]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).dtypes
Out[42]:
col1 category
col2 object
col3 int64
dtype: object
При использовании dtype=CategoricalDtype, «неожиданные» значения, которые не входят в dtype.categories, обрабатываются как пропущенные значения.
In [43]: dtype = CategoricalDtype(["a", "b", "d"]) # No 'c'
In [44]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).col1
Out[44]:
0 a
1 a
2 NaN
Name: col1, dtype: category
Categories (3, object): ['a', 'b', 'd']
Это соответствует поведению Categorical.set_categories().
Примечание
При использовании dtype='category', результирующие категории всегда будут распарсены как строки (объектный тип данных). Если категории являются числовыми, их можно преобразовать, используя функцию to_numeric(), или, как соответствующим образом, другой конвертер, например, to_datetime().
Когда dtype является CategoricalDtype с однородными categories (все числовые, все даты и т. д.), преобразование выполняется автоматически.
In [45]: df = pd.read_csv(StringIO(data), dtype="category")
In [46]: df.dtypes
Out[46]:
col1 category
col2 category
col3 category
dtype: object
In [47]: df["col3"]
Out[47]:
0 1
1 2
2 3
Name: col3, dtype: category
Categories (3, object): ['1', '2', '3']
In [48]: new_categories = pd.to_numeric(df["col3"].cat.categories)
In [49]: df["col3"] = df["col3"].cat.rename_categories(new_categories)
In [50]: df["col3"]
Out[50]:
0 1
1 2
2 3
Name: col3, dtype: category
Categories (3, int64): [1, 2, 3]
Именование и использование столбцов
Обработка имён столбцов
Файл может или не может иметь строку заголовка. pandas предполагает, что первая строка должна использоваться в качестве имён столбцов:
In [51]: data = "a,b,c\n1,2,3\n4,5,6\n7,8,9"
In [52]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9
In [53]: pd.read_csv(StringIO(data))
Out[53]:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
Указав аргумент names в сочетании с header, вы можете указать другие имена для использования и указать, удалять ли строку заголовка (если она есть):
In [54]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9
In [55]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=0)
Out[55]:
foo bar baz
0 1 2 3
1 4 5 6
2 7 8 9
In [56]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=None)
Out[56]:
foo bar baz
0 a b c
1 1 2 3
2 4 5 6
3 7 8 9
Если заголовок находится в строке, отличной от первой, передайте номер строки аргументу header. Это пропустит предшествующие строки:
In [57]: data = "skip this skip it\na,b,c\n1,2,3\n4,5,6\n7,8,9"
In [58]: pd.read_csv(StringIO(data), header=1)
Out[58]:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
Примечание
По умолчанию имена столбцов выводятся: если имена не передаются, поведение идентично header=0 , и имена столбцов выводятся из первой непустой строки файла; если имена столбцов переданы явно, поведение идентично header=None.
Обработка дублирующихся имён
Устарело начиная с версии 1.5.0:
mangle_dupe_colsникогда не реализовывался, и вместо него будет добавлен новый аргумент, где можно указать шаблон переименования.
Если файл или заголовок содержат дублирующиеся имена, pandas по умолчанию будет отличать их, чтобы предотвратить перезапись данных:
In [59]: data = "a,b,a\n0,1,2\n3,4,5"
In [60]: pd.read_csv(StringIO(data))
Out[60]:
a b a.1
0 0 1 2
1 3 4 5
Больше нет дублирующихся данных, потому что mangle_dupe_cols=True по умолчанию, что изменяет серию дублирующихся столбцов «X», …, «X» на «X», «X.1», …, «X.N».
Фильтрация столбцов (usecols)
Аргумент usecols позволяет вам выбрать любой подмножество столбцов в файле, используя имена столбцов, номера позиций или вызываемую функцию:
In [61]: data = "a,b,c,d\n1,2,3,foo\n4,5,6,bar\n7,8,9,baz"
In [62]: pd.read_csv(StringIO(data))
Out[62]:
a b c d
0 1 2 3 foo
1 4 5 6 bar
2 7 8 9 baz
In [63]: pd.read_csv(StringIO(data), usecols=["b", "d"])
Out[63]:
b d
0 2 foo
1 5 bar
2 8 baz
In [64]: pd.read_csv(StringIO(data), usecols=[0, 2, 3])
Out[64]:
a c d
0 1 3 foo
1 4 6 bar
2 7 9 baz
In [65]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["A", "C"])
Out[65]:
a c
0 1 3
1 4 6
2 7 9
Аргумент usecols также может использоваться для указания столбцов, которые не должны использоваться в конечном результате:
In [66]: pd.read_csv(StringIO(data), usecols=lambda x: x not in ["a", "c"])
Out[66]:
b d
0 2 foo
1 5 bar
2 8 baz
В этом случае вызываемая функция указывает, что мы исключаем столбцы «a» и «c» из результата.
Обработка данных Unicode
Аргумент encoding должен использоваться для закодированных данных Unicode, что приведет к декодированию байтовых строк в Unicode в результате:
In [84]: from io import BytesIO
In [85]: data = b"word,length\n" b"Tr\xc3\xa4umen,7\n" b"Gr\xc3\xbc\xc3\x9fe,5"
In [86]: data = data.decode("utf8").encode("latin-1")
In [87]: df = pd.read_csv(BytesIO(data), encoding="latin-1")
In [88]: df
Out[88]:
word length
0 Träumen 7
1 Grüße 5
In [89]: df["word"][1]
Out[89]: 'Grüße'
Некоторые форматы, которые кодируют все символы как несколько байтов, такие как UTF-16, не будут правильно парситься без указания кодировки. Полный список стандартных кодировок Python.
Индексные столбцы и разделители в конце
Если файл содержит на один столбец данных больше, чем количество названий столбцов, то первый столбец будет использован как имена строк DataFrame:
In [90]: data = "a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"
In [91]: pd.read_csv(StringIO(data))
Out[91]:
a b c
4 apple bat 5.7
8 orange cow 10.0
In [92]: data = "index,a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"
In [93]: pd.read_csv(StringIO(data), index_col=0)
Out[93]:
a b c
index
4 apple bat 5.7
8 orange cow 10.0
Обычно это поведение можно получить, используя опцию index_col.
Существуют некоторые исключительные случаи, когда файл был подготовлен с разделителями в конце каждой строки данных, что вводит в заблуждение парсер. Чтобы явно отключить вычисление индексного столбца и отбросить последний столбец, передайте index_col=False:
In [94]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"
In [95]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,
In [96]: pd.read_csv(StringIO(data))
Out[96]:
a b c
4 apple bat NaN
8 orange cow NaN
In [97]: pd.read_csv(StringIO(data), index_col=False)
Out[97]:
a b c
0 4 apple bat
1 8 orange cow
Если анализируется подмножество данных с помощью опции usecols, спецификация index_col основана на этом подмножестве, а не на исходных данных.
In [98]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"
In [99]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,
In [100]: pd.read_csv(StringIO(data), usecols=["b", "c"])
Out[100]:
b c
4 bat NaN
8 cow NaN
In [101]: pd.read_csv(StringIO(data), usecols=["b", "c"], index_col=0)
Out[101]:
b c
4 bat NaN
8 cow NaN
Обработка дат
Указание столбцов дат
Для лучшей работы с данными datetime, read_csv() использует ключевые аргументы parse_dates и date_parser для возможности указывать различные столбцы и форматы дат/времени, чтобы преобразовать входные текстовые данные в объекты datetime.
Простейший случай — передача parse_dates=True:
In [102]: with open("foo.csv", mode="w") as f:
.....: f.write("date,A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5")
.....:
# Use a column as an index, and parse it as dates.
In [103]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True)
In [104]: df
Out[104]:
A B C
date
2009-01-01 a 1 2
2009-01-02 b 3 4
2009-01-03 c 4 5
# These are Python datetime objects
In [105]: df.index
Out[105]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)
Часто требуется хранить данные даты и времени отдельно или хранить различные поля дат отдельно. Ключевое слово parse_dates может использоваться для указания комбинации столбцов для анализа дат и/или времени из них.
Вы можете указать список списков столбцов для parse_dates, результирующие столбцы дат будут добавлены в начало вывода (чтобы не влиять на существующий порядок столбцов), а новые имена столбцов будут результатом конкатенации имён исходных столбцов:
In [106]: data = (
.....: "KORD,19990127, 19:00:00, 18:56:00, 0.8100\n"
.....: "KORD,19990127, 20:00:00, 19:56:00, 0.0100\n"
.....: "KORD,19990127, 21:00:00, 20:56:00, -0.5900\n"
.....: "KORD,19990127, 21:00:00, 21:18:00, -0.9900\n"
.....: "KORD,19990127, 22:00:00, 21:56:00, -0.5900\n"
.....: "KORD,19990127, 23:00:00, 22:56:00, -0.5900"
.....: )
.....:
In [107]: with open("tmp.csv", "w") as fh:
.....: fh.write(data)
.....:
In [108]: df = pd.read_csv("tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]])
In [109]: df
Out[109]:
1_2 1_3 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
По умолчанию парсер удаляет столбцы компонент дат, но вы можете выбрать их сохранение с помощью ключевого слова keep_date_col:
In [110]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True
.....: )
.....:
In [111]: df
Out[111]:
1_2 1_3 0 ... 2 3 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD ... 19:00:00 18:56:00 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD ... 20:00:00 19:56:00 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD ... 21:00:00 20:56:00 -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD ... 21:00:00 21:18:00 -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD ... 22:00:00 21:56:00 -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD ... 23:00:00 22:56:00 -0.59
[6 rows x 7 columns]
Обратите внимание, что если вы хотите объединить несколько столбцов в один столбец дат, необходимо использовать вложенный список. Другими словами, parse_dates=[1, 2] указывает, что второй и третий столбцы должны быть проанализированы как отдельные столбцы дат, в то время как parse_dates=[[1, 2]] означает, что два столбца должны быть проанализированы в один столбец.
Вы также можете использовать словарь для указания пользовательских имён столбцов:
In [112]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}
In [113]: df = pd.read_csv("tmp.csv", header=None, parse_dates=date_spec)
In [114]: df
Out[114]:
nominal actual 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
Важно помнить, что если несколько текстовых столбцов должны быть проанализированы в один столбец дат, то новый столбец добавляется в данные. Спецификация index_col основана на этом новом наборе столбцов, а не на исходных столбцах данных:
In [115]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}
In [116]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=date_spec, index_col=0
.....: ) # index is the nominal column
.....:
In [117]: df
Out[117]:
actual 0 4
nominal
1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
Примечание
Если столбец или индекс содержит непроверяемую дату, весь столбец или индекс будут возвращены без изменений как тип данных объект. Для нестандартного анализа datetime используйте to_datetime() после pd.read_csv.
Примечание
read_csv имеет быстрый путь для парсинга строк datetime в формате iso8601, например “2000-01-01T00:01:02+00:00” и аналогичных вариаций. Если вы можете организовать хранение datetime в этом формате, время загрузки значительно сократится, было замечено ускорение ~20 раз.
Функции парсинга дат
Наконец, парсер позволяет указать пользовательскую функцию date_parser для полного использования гибкости API парсинга дат:
In [118]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=date_spec, date_parser=pd.to_datetime
.....: )
.....:
In [119]: df
Out[119]:
nominal actual 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
pandas попытается вызвать функцию date_parser тремя различными способами. Если возникнет исключение, будет выполнена следующая попытка:
date_parserсначала вызывается с одним или несколькими массивами в качестве аргументов, как определено с помощьюparse_dates(например,date_parser(['2013', '2013'], ['1', '2'])).Если #1 завершится неудачей,
date_parserвызывается со всеми столбцами, конкатенированными построчно в один массив (например,date_parser(['2013 1', '2013 2'])).
Обратите внимание, что с точки зрения производительности вам следует пытаться применять эти методы парсинга дат в таком порядке:
Попробуйте определить формат с помощью
infer_datetime_format=True(см. раздел ниже).Если вы знаете формат, используйте
pd.to_datetime():date_parser=lambda x: pd.to_datetime(x, format=...).Если у вас действительно нестандартный формат, используйте пользовательскую функцию
date_parser. Для оптимальной производительности она должна быть векторизованной, т.е. принимать массивы в качестве аргументов.
Парсинг CSV с различными часовыми поясами
pandas не может напрямую представлять столбец или индекс со смешанными часовыми поясами. Если ваш CSV-файл содержит столбцы с различными часовыми поясами, результат по умолчанию будет столбцом типа object со строками, даже с parse_dates.
In [120]: content = """\
.....: a
.....: 2000-01-01T00:00:00+05:00
.....: 2000-01-01T00:00:00+06:00"""
.....:
In [121]: df = pd.read_csv(StringIO(content), parse_dates=["a"])
In [122]: df["a"]
Out[122]:
0 2000-01-01 00:00:00+05:00
1 2000-01-01 00:00:00+06:00
Name: a, dtype: object
Чтобы проанализировать значения со смешанными часовыми поясами как столбец datetime, передайте частично применённую функцию to_datetime() с utc=True в качестве date_parser.
In [123]: df = pd.read_csv(
.....: StringIO(content),
.....: parse_dates=["a"],
.....: date_parser=lambda col: pd.to_datetime(col, utc=True),
.....: )
.....:
In [124]: df["a"]
Out[124]:
0 1999-12-31 19:00:00+00:00
1 1999-12-31 18:00:00+00:00
Name: a, dtype: datetime64[ns, UTC]
Определение формата datetime
Если для некоторых или всех ваших столбцов включена функция parse_dates, и все ваши строки дат имеют одинаковый формат, вы можете значительно ускорить процесс, установив infer_datetime_format=True. Если установлено, pandas попытается угадать формат ваших строк datetime и затем использовать более быстрый способ анализа строк. Было отмечено ускорение парсинга в 5-10 раз. pandas вернётся к обычному парсингу, если формат не может быть угадан или формат, который был угадан, не может правильно проанализировать весь столбец строк. Таким образом, в целом, infer_datetime_format не должно иметь негативных последствий при включении.
Вот некоторые примеры строк datetime, которые могут быть угаданы (все представляют 30 декабря 2011 года в 00:00:00):
“20111230”
“2011/12/30”
“20111230 00:00:00”
“12/30/2011 00:00:00”
“30/Dec/2011 00:00:00”
“30/Декабря/2011 00:00:00”
Обратите внимание, что infer_datetime_format чувствителен к dayfirst. С dayfirst=True, он угадает “01/12/2011” как 1 декабря. С dayfirst=False (по умолчанию) он угадает “01/12/2011” как 12 января.
# Try to infer the format for the index column
In [125]: df = pd.read_csv(
.....: "foo.csv",
.....: index_col=0,
.....: parse_dates=True,
.....: infer_datetime_format=True,
.....: )
.....:
In [126]: df
Out[126]:
A B C
date
2009-01-01 a 1 2
2009-01-02 b 3 4
2009-01-03 c 4 5
Международные форматы дат
В то время как американские форматы дат обычно используют MM/DD/YYYY, многие международные форматы используют DD/MM/YYYY вместо этого. Для удобства предоставлено ключевое слово dayfirst:
In [127]: data = "date,value,cat\n1/6/2000,5,a\n2/6/2000,10,b\n3/6/2000,15,c"
In [128]: print(data)
date,value,cat
1/6/2000,5,a
2/6/2000,10,b
3/6/2000,15,c
In [129]: with open("tmp.csv", "w") as fh:
.....: fh.write(data)
.....:
In [130]: pd.read_csv("tmp.csv", parse_dates=[0])
Out[130]:
date value cat
0 2000-01-06 5 a
1 2000-02-06 10 b
2 2000-03-06 15 c
In [131]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0])
Out[131]:
date value cat
0 2000-06-01 5 a
1 2000-06-02 10 b
2 2000-06-03 15 c
Запись CSV в двоичные объекты файлов
Добавлено в версии 1.2.0.
df.to_csv(..., mode="wb") позволяет записывать CSV в объект файла, открытый в двоичном режиме. В большинстве случаев нет необходимости указывать mode, так как Pandas автоматически определит, открыт ли объект файла в текстовом или двоичном режиме.
In [132]: import io
In [133]: data = pd.DataFrame([0, 1, 2])
In [134]: buffer = io.BytesIO()
In [135]: data.to_csv(buffer, encoding="utf-8", compression="gzip")
Указание метода преобразования чисел с плавающей точкой
Параметр float_precision может быть указан для использования определённого преобразователя чисел с плавающей точкой во время разбора с помощью движка C. Варианты — обычный преобразователь, преобразователь высокой точности и преобразователь обратного преобразования (который гарантирует обратное преобразование значений после записи в файл). Например:
In [136]: val = "0.3066101993807095471566981359501369297504425048828125"
In [137]: data = "a,b,c\n1,2,{0}".format(val)
In [138]: abs(
.....: pd.read_csv(
.....: StringIO(data),
.....: engine="c",
.....: float_precision=None,
.....: )["c"][0] - float(val)
.....: )
.....:
Out[138]: 5.551115123125783e-17
In [139]: abs(
.....: pd.read_csv(
.....: StringIO(data),
.....: engine="c",
.....: float_precision="high",
.....: )["c"][0] - float(val)
.....: )
.....:
Out[139]: 5.551115123125783e-17
In [140]: abs(
.....: pd.read_csv(StringIO(data), engine="c", float_precision="round_trip")["c"][0]
.....: - float(val)
.....: )
.....:
Out[140]: 0.0
Разделители тысяч
Для больших чисел, записанных с разделителями тысяч, вы можете установить ключевое слово thousands в строку длиной 1, чтобы целые числа анализировались корректно:
По умолчанию числа с разделителями тысяч будут анализироваться как строки:
In [141]: data = (
.....: "ID|level|category\n"
.....: "Patient1|123,000|x\n"
.....: "Patient2|23,000|y\n"
.....: "Patient3|1,234,018|z"
.....: )
.....:
In [142]: with open("tmp.csv", "w") as fh:
.....: fh.write(data)
.....:
In [143]: df = pd.read_csv("tmp.csv", sep="|")
In [144]: df
Out[144]:
ID level category
0 Patient1 123,000 x
1 Patient2 23,000 y
2 Patient3 1,234,018 z
In [145]: df.level.dtype
Out[145]: dtype('O')
Ключевое слово thousands позволяет корректно анализировать целые числа:
In [146]: df = pd.read_csv("tmp.csv", sep="|", thousands=",")
In [147]: df
Out[147]:
ID level category
0 Patient1 123000 x
1 Patient2 23000 y
2 Patient3 1234018 z
In [148]: df.level.dtype
Out[148]: dtype('int64')
Значения NA
Чтобы контролировать, какие значения анализируются как пропущенные значения (обозначаемые как NaN), укажите строку в na_values. Если вы указываете список строк, то все значения в нём считаются пропущенными значениями. Если вы указываете число (например, float, как 5.0 или integer, как 5 ), соответствующие эквивалентные значения также будут означать пропущенное значение (в этом случае эффективно [5.0, 5] распознаются как NaN).
Чтобы полностью переопределить значения по умолчанию, распознаваемые как пропущенные, укажите keep_default_na=False.
Рассмотрим несколько примеров:
pd.read_csv("path_to_file.csv", na_values=[5])
В примере выше 5 и 5.0 будут распознаны как NaN, в дополнение к значениям по умолчанию. Сначала строка будет интерпретироваться как числовое 5, затем как NaN.
pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=[""])
Выше только пустое поле будет распознано как NaN.
pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=["NA", "0"])
Выше, как строки, NA и 0 будут NaN.
pd.read_csv("path_to_file.csv", na_values=["Nope"])
Значения по умолчанию, кроме строки "Nope", распознаются как NaN.
Бесконечность
inf подобные значения будут анализироваться как np.inf (положительная бесконечность), а -inf как -np.inf (отрицательная бесконечность). При этом регистр значения игнорируется, что означает, что Inf, также будет анализироваться как np.inf.
Возвращение Series
Используя ключевое слово squeeze, парсер вернёт вывод с одним столбцом как Series.
Устарело начиная с версии 1.4.0: Пользователи должны добавить .squeeze("columns") к DataFrame, возвращённому read_csv.
In [149]: data = "level\nPatient1,123000\nPatient2,23000\nPatient3,1234018"
In [150]: with open("tmp.csv", "w") as fh:
.....: fh.write(data)
.....:
In [151]: print(open("tmp.csv").read())
level
Patient1,123000
Patient2,23000
Patient3,1234018
In [152]: output = pd.read_csv("tmp.csv", squeeze=True)
In [153]: output
Out[153]:
Patient1 123000
Patient2 23000
Patient3 1234018
Name: level, dtype: int64
In [154]: type(output)
Out[154]: pandas.core.series.Series
Булевы значения
Общие значения True, False, TRUE, и FALSE распознаются как булевы. Иногда вам может потребоваться распознать другие значения как булевы. Для этого используйте опции true_values и false_values следующим образом:
In [155]: data = "a,b,c\n1,Yes,2\n3,No,4"
In [156]: print(data)
a,b,c
1,Yes,2
3,No,4
In [157]: pd.read_csv(StringIO(data))
Out[157]:
a b c
0 1 Yes 2
1 3 No 4
In [158]: pd.read_csv(StringIO(data), true_values=["Yes"], false_values=["No"])
Out[158]:
a b c
0 1 True 2
1 3 False 4
Обработка «плохих» строк
Некоторые файлы могут содержать строки с неправильным форматом, с недостаточным или избыточным количеством полей. Строки с недостаточным количеством полей будут иметь значения NA, заполняющие недостающие поля. Строки с избыточным количеством полей по умолчанию вызовут ошибку:
In [159]: data = "a,b,c\n1,2,3\n4,5,6,7\n8,9,10"
In [160]: pd.read_csv(StringIO(data))
---------------------------------------------------------------------------
ParserError Traceback (most recent call last)
Cell In [160], line 1
----> 1 pd.read_csv(StringIO(data))
File ~/work/pandas/pandas/pandas/util/_decorators.py:211, in deprecate_kwarg.<locals>._deprecate_kwarg.<locals>.wrapper(*args, **kwargs)
209 else:
210 kwargs[new_arg_name] = new_arg_value
--> 211 return func(*args, **kwargs)
File ~/work/pandas/pandas/pandas/util/_decorators.py:317, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
311 if len(args) > num_allow_args:
312 warnings.warn(
313 msg.format(arguments=arguments),
314 FutureWarning,
315 stacklevel=find_stack_level(inspect.currentframe()),
316 )
--> 317 return func(*args, **kwargs)
File ~/work/pandas/pandas/pandas/io/parsers/readers.py:950, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)
935 kwds_defaults = _refine_defaults_read(
936 dialect,
937 delimiter,
(...)
946 defaults={"delimiter": ","},
947 )
948 kwds.update(kwds_defaults)
--> 950 return _read(filepath_or_buffer, kwds)
File ~/work/pandas/pandas/pandas/io/parsers/readers.py:611, in _read(filepath_or_buffer, kwds)
608 return parser
610 with parser:
--> 611 return parser.read(nrows)
File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1772, in TextFileReader.read(self, nrows)
1765 nrows = validate_integer("nrows", nrows)
1766 try:
1767 # error: "ParserBase" has no attribute "read"
1768 (
1769 index,
1770 columns,
1771 col_dict,
-> 1772 ) = self._engine.read( # type: ignore[attr-defined]
1773 nrows
1774 )
1775 except Exception:
1776 self.close()
File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:243, in CParserWrapper.read(self, nrows)
241 try:
242 if self.low_memory:
--> 243 chunks = self._reader.read_low_memory(nrows)
244 # destructive to chunks
245 data = _concatenate_chunks(chunks)
File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:808, in pandas._libs.parsers.TextReader.read_low_memory()
File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:866, in pandas._libs.parsers.TextReader._read_rows()
File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:852, in pandas._libs.parsers.TextReader._tokenize_rows()
File ~/work/pandas/pandas/pandas/_libs/parsers.pyx:1973, in pandas._libs.parsers.raise_parser_error()
ParserError: Error tokenizing data. C error: Expected 3 fields in line 3, saw 4
Вы можете выбрать пропуск плохих строк:
In [29]: pd.read_csv(StringIO(data), on_bad_lines="warn")
Skipping line 3: expected 3 fields, saw 4
Out[29]:
a b c
0 1 2 3
1 8 9 10
Или передать вызываемую функцию для обработки плохой строки, если engine="python". Плохая строка будет списком строк, разделённых sep.
In [29]: external_list = []
In [30]: def bad_lines_func(line):
...: external_list.append(line)
...: return line[-3:]
In [31]: pd.read_csv(StringIO(data), on_bad_lines=bad_lines_func, engine="python")
Out[31]:
a b c
0 1 2 3
1 5 6 7
2 8 9 10
In [32]: external_list
Out[32]: [4, 5, 6, 7]
.. versionadded:: 1.4.0
Вы также можете использовать параметр usecols для удаления лишних данных столбцов, которые появляются в некоторых строках, но не в других:
In [33]: pd.read_csv(StringIO(data), usecols=[0, 1, 2])
Out[33]:
a b c
0 1 2 3
1 4 5 6
2 8 9 10
В случае, если вы хотите сохранить все данные, включая строки с избыточным количеством полей, вы можете указать достаточное количество names. Это гарантирует, что строки с недостаточным количеством полей заполняются NaN.
In [34]: pd.read_csv(StringIO(data), names=['a', 'b', 'c', 'd'])
Out[34]:
a b c d
0 1 2 3 NaN
1 4 5 6 7
2 8 9 10 NaN
Диалект
Ключевое слово dialect предоставляет большую гибкость в указании формата файла. По умолчанию используется диалект Excel, но вы можете указать имя диалекта или экземпляр csv.Dialect.
Предположим, у вас были данные с необрамлёнными кавычками:
In [161]: data = "label1,label2,label3\n" 'index1,"a,c,e\n' "index2,b,d,f"
In [162]: print(data)
label1,label2,label3
index1,"a,c,e
index2,b,d,f
По умолчанию, read_csv использует диалект Excel и обрабатывает двойную кавычку как символ кавычки, что приводит к сбою, когда обнаруживается перевод строки до закрывающей двойной кавычки.
Мы можем обойти это, используя dialect:
In [163]: import csv
In [164]: dia = csv.excel()
In [165]: dia.quoting = csv.QUOTE_NONE
In [166]: pd.read_csv(StringIO(data), dialect=dia)
Out[166]:
label1 label2 label3
index1 "a c e
index2 b d f
Все опции диалекта можно указать отдельно в качестве ключевых аргументов:
In [167]: data = "a,b,c~1,2,3~4,5,6"
In [168]: pd.read_csv(StringIO(data), lineterminator="~")
Out[168]:
a b c
0 1 2 3
1 4 5 6
Ещё одна распространённая опция диалекта — skipinitialspace, чтобы пропустить любые пробелы после разделителя:
In [169]: data = "a, b, c\n1, 2, 3\n4, 5, 6"
In [170]: print(data)
a, b, c
1, 2, 3
4, 5, 6
In [171]: pd.read_csv(StringIO(data), skipinitialspace=True)
Out[171]:
a b c
0 1 2 3
1 4 5 6
Парсеры прилагают все усилия, чтобы «сделать правильно» и не быть хрупкими. Вывод типов — очень важная вещь. Если столбец может быть приведён к целочисленному типу без изменения содержимого, парсер это сделает. Любые нечисловые столбцы будут приходить как тип object, как и с остальными объектами pandas.
Кавычки и символы экранирования
Кавычки (и другие символы экранирования) в вложенных полях могут обрабатываться различными способами. Один из способов — использовать обратные слэши; чтобы правильно проанализировать эти данные, вы должны передать опцию escapechar:
In [172]: data = 'a,b\n"hello, \\"Bob\\", nice to see you",5'
In [173]: print(data)
a,b
"hello, \"Bob\", nice to see you",5
In [174]: pd.read_csv(StringIO(data), escapechar="\\")
Out[174]:
a b
0 hello, "Bob", nice to see you 5
Файлы с фиксированной шириной столбцов
В то время как read_csv() считывает данные с разделителями, функция read_fwf() работает с файлами данных, в которых известна и фиксирована ширина столбцов. Параметры функции read_fwf в основном такие же, как у read_csv с двумя дополнительными параметрами и другим использованием параметра delimiter:
colspecs: Список пар (кортежей), задающих границы полей фиксированной ширины каждой строки как полуоткрытых интервалов (т.е. [от, до[ ). Строковое значение ‘infer’ может быть использовано для указания парсеру попробовать определить спецификации столбцов из первых 100 строк данных. По умолчанию, если не указано, используется инференция.widths: Список ширины полей, которые могут быть использованы вместо ‘colspecs’, если интервалы являются непрерывными.delimiter: Символы, которые следует рассматривать как символы заполнителя в файле фиксированной ширины. Могут быть использованы для задания символа заполнителя полей, если он не пробелы (например, ‘~’).
Рассмотрим типичный файл данных фиксированной ширины:
In [175]: data1 = (
.....: "id8141 360.242940 149.910199 11950.7\n"
.....: "id1594 444.953632 166.985655 11788.4\n"
.....: "id1849 364.136849 183.628767 11806.2\n"
.....: "id1230 413.836124 184.375703 11916.8\n"
.....: "id1948 502.953953 173.237159 12468.3"
.....: )
.....:
In [176]: with open("bar.csv", "w") as f:
.....: f.write(data1)
.....:
Для того чтобы проанализировать этот файл в DataFrame, нам просто нужно предоставить спецификации столбцов функции read_fwf вместе с именем файла:
# Column specifications are a list of half-intervals
In [177]: colspecs = [(0, 6), (8, 20), (21, 33), (34, 43)]
In [178]: df = pd.read_fwf("bar.csv", colspecs=colspecs, header=None, index_col=0)
In [179]: df
Out[179]:
1 2 3
0
id8141 360.242940 149.910199 11950.7
id1594 444.953632 166.985655 11788.4
id1849 364.136849 183.628767 11806.2
id1230 413.836124 184.375703 11916.8
id1948 502.953953 173.237159 12468.3
Обратите внимание, как парсер автоматически выбирает имена столбцов X.<номер столбца>, когда указан аргумент header=None. В качестве альтернативы, вы можете указать только ширину столбцов для непрерывных столбцов:
# Widths are a list of integers
In [180]: widths = [6, 14, 13, 10]
In [181]: df = pd.read_fwf("bar.csv", widths=widths, header=None)
In [182]: df
Out[182]:
0 1 2 3
0 id8141 360.242940 149.910199 11950.7
1 id1594 444.953632 166.985655 11788.4
2 id1849 364.136849 183.628767 11806.2
3 id1230 413.836124 184.375703 11916.8
4 id1948 502.953953 173.237159 12468.3
Парсер позаботится о лишних пробелах вокруг столбцов, поэтому нет проблем с дополнительным разделителем между столбцами в файле.
По умолчанию read_fwf попытается вывести colspecs файла, используя первые 100 строк файла. Это возможно только в тех случаях, когда столбцы выровнены и корректно разделены предоставленным delimiter (разделитель по умолчанию — пробел).
In [183]: df = pd.read_fwf("bar.csv", header=None, index_col=0)
In [184]: df
Out[184]:
1 2 3
0
id8141 360.242940 149.910199 11950.7
id1594 444.953632 166.985655 11788.4
id1849 364.136849 183.628767 11806.2
id1230 413.836124 184.375703 11916.8
id1948 502.953953 173.237159 12468.3
read_fwf поддерживает параметр dtype для задания типов анализируемых столбцов, отличных от выведенного типа.
In [185]: pd.read_fwf("bar.csv", header=None, index_col=0).dtypes
Out[185]:
1 float64
2 float64
3 float64
dtype: object
In [186]: pd.read_fwf("bar.csv", header=None, dtype={2: "object"}).dtypes
Out[186]:
0 object
1 float64
2 object
3 float64
dtype: object
Индексы
Файлы с «неявным» столбцом индекса
Рассмотрим файл с на одну запись меньше в заголовке, чем количество столбцов данных:
In [187]: data = "A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5"
In [188]: print(data)
A,B,C
20090101,a,1,2
20090102,b,3,4
20090103,c,4,5
In [189]: with open("foo.csv", "w") as f:
.....: f.write(data)
.....:
В этом специальном случае read_csv предполагает, что первый столбец будет использоваться в качестве индекса DataFrame:
In [190]: pd.read_csv("foo.csv")
Out[190]:
A B C
20090101 a 1 2
20090102 b 3 4
20090103 c 4 5
Обратите внимание, что даты не были автоматически распарсены. В этом случае вам нужно сделать, как и прежде:
In [191]: df = pd.read_csv("foo.csv", parse_dates=True)
In [192]: df.index
Out[192]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', freq=None)
Чтение индекса с MultiIndex
Предположим, у вас есть данные, индексированные по двум столбцам:
In [193]: data = 'year,indiv,zit,xit\n1977,"A",1.2,.6\n1977,"B",1.5,.5'
In [194]: print(data)
year,indiv,zit,xit
1977,"A",1.2,.6
1977,"B",1.5,.5
In [195]: with open("mindex_ex.csv", mode="w") as f:
.....: f.write(data)
.....:
Аргумент index_col к read_csv может принимать список номеров столбцов, чтобы преобразовать несколько столбцов в MultiIndex для индекса возвращаемого объекта:
In [196]: df = pd.read_csv("mindex_ex.csv", index_col=[0, 1])
In [197]: df
Out[197]:
zit xit
year indiv
1977 A 1.2 0.6
B 1.5 0.5
In [198]: df.loc[1977]
Out[198]:
zit xit
indiv
A 1.2 0.6
B 1.5 0.5
Чтение столбцов с MultiIndex
Указав список расположений строк для аргумента header, вы можете считать MultiIndex для столбцов. Указание несмежных строк пропустит промежуточные строки.
In [199]: from pandas._testing import makeCustomDataframe as mkdf
In [200]: df = mkdf(5, 3, r_idx_nlevels=2, c_idx_nlevels=4)
In [201]: df.to_csv("mi.csv")
In [202]: print(open("mi.csv").read())
C0,,C_l0_g0,C_l0_g1,C_l0_g2
C1,,C_l1_g0,C_l1_g1,C_l1_g2
C2,,C_l2_g0,C_l2_g1,C_l2_g2
C3,,C_l3_g0,C_l3_g1,C_l3_g2
R0,R1,,,
R_l0_g0,R_l1_g0,R0C0,R0C1,R0C2
R_l0_g1,R_l1_g1,R1C0,R1C1,R1C2
R_l0_g2,R_l1_g2,R2C0,R2C1,R2C2
R_l0_g3,R_l1_g3,R3C0,R3C1,R3C2
R_l0_g4,R_l1_g4,R4C0,R4C1,R4C2
In [203]: pd.read_csv("mi.csv", header=[0, 1, 2, 3], index_col=[0, 1])
Out[203]:
C0 C_l0_g0 C_l0_g1 C_l0_g2
C1 C_l1_g0 C_l1_g1 C_l1_g2
C2 C_l2_g0 C_l2_g1 C_l2_g2
C3 C_l3_g0 C_l3_g1 C_l3_g2
R0 R1
R_l0_g0 R_l1_g0 R0C0 R0C1 R0C2
R_l0_g1 R_l1_g1 R1C0 R1C1 R1C2
R_l0_g2 R_l1_g2 R2C0 R2C1 R2C2
R_l0_g3 R_l1_g3 R3C0 R3C1 R3C2
R_l0_g4 R_l1_g4 R4C0 R4C1 R4C2
read_csv также может интерпретировать более распространённый формат индексов по нескольким столбцам.
In [204]: data = ",a,a,a,b,c,c\n,q,r,s,t,u,v\none,1,2,3,4,5,6\ntwo,7,8,9,10,11,12"
In [205]: print(data)
,a,a,a,b,c,c
,q,r,s,t,u,v
one,1,2,3,4,5,6
two,7,8,9,10,11,12
In [206]: with open("mi2.csv", "w") as fh:
.....: fh.write(data)
.....:
In [207]: pd.read_csv("mi2.csv", header=[0, 1], index_col=0)
Out[207]:
a b c
q r s t u v
one 1 2 3 4 5 6
two 7 8 9 10 11 12
Примечание
Если index_col не указан (например, у вас нет индекса или вы его написали с помощью df.to_csv(..., index=False)), то любые names на индексе столбцов будут утеряны.
Автоматическое «определениe» разделителя
read_csv может определять файлы с разделителями (не обязательно с запятыми), так как pandas использует класс csv.Sniffer из модуля csv. Для этого вам нужно указать sep=None.
In [208]: df = pd.DataFrame(np.random.randn(10, 4))
In [209]: df.to_csv("tmp.csv", sep="|")
In [210]: df.to_csv("tmp2.csv", sep=":")
In [211]: pd.read_csv("tmp2.csv", sep=None, engine="python")
Out[211]:
Unnamed: 0 0 1 2 3
0 0 0.469112 -0.282863 -1.509059 -1.135632
1 1 1.212112 -0.173215 0.119209 -1.044236
2 2 -0.861849 -2.104569 -0.494929 1.071804
3 3 0.721555 -0.706771 -1.039575 0.271860
4 4 -0.424972 0.567020 0.276232 -1.087401
5 5 -0.673690 0.113648 -1.478427 0.524988
6 6 0.404705 0.577046 -1.715002 -1.039268
7 7 -0.370647 -1.157892 -1.344312 0.844885
8 8 1.075770 -0.109050 1.643563 -1.469388
9 9 0.357021 -0.674600 -1.776904 -0.968914
Чтение нескольких файлов для создания одного DataFrame
Лучше использовать concat() для объединения нескольких файлов. См. справочник для примера.
Итерация по файлам по частям
Предположим, вы хотите итерироваться по файлу (возможно, очень большому) лениво, а не загружать весь файл в память, например, так:
In [212]: df = pd.DataFrame(np.random.randn(10, 4))
In [213]: df.to_csv("tmp.csv", sep="|")
In [214]: table = pd.read_csv("tmp.csv", sep="|")
In [215]: table
Out[215]:
Unnamed: 0 0 1 2 3
0 0 -1.294524 0.413738 0.276662 -0.472035
1 1 -0.013960 -0.362543 -0.006154 -0.923061
2 2 0.895717 0.805244 -1.206412 2.565646
3 3 1.431256 1.340309 -1.170299 -0.226169
4 4 0.410835 0.813850 0.132003 -0.827317
5 5 -0.076467 -1.187678 1.130127 -1.436737
6 6 -1.413681 1.607920 1.024180 0.569605
7 7 0.875906 -2.211372 0.974466 -2.006747
8 8 -0.410001 -0.078638 0.545952 -1.219217
9 9 -1.226825 0.769804 -1.281247 -0.727707
Указав chunksize для read_csv, возвращаемое значение будет итерируемым объектом типа TextFileReader:
In [216]: with pd.read_csv("tmp.csv", sep="|", chunksize=4) as reader:
.....: reader
.....: for chunk in reader:
.....: print(chunk)
.....:
Unnamed: 0 0 1 2 3
0 0 -1.294524 0.413738 0.276662 -0.472035
1 1 -0.013960 -0.362543 -0.006154 -0.923061
2 2 0.895717 0.805244 -1.206412 2.565646
3 3 1.431256 1.340309 -1.170299 -0.226169
Unnamed: 0 0 1 2 3
4 4 0.410835 0.813850 0.132003 -0.827317
5 5 -0.076467 -1.187678 1.130127 -1.436737
6 6 -1.413681 1.607920 1.024180 0.569605
7 7 0.875906 -2.211372 0.974466 -2.006747
Unnamed: 0 0 1 2 3
8 8 -0.410001 -0.078638 0.545952 -1.219217
9 9 -1.226825 0.769804 -1.281247 -0.727707
Изменено в версии 1.2: read_csv/json/sas возвращает менеджер контекста при итерации по файлу.
Указание iterator=True также вернёт TextFileReader объект:
In [217]: with pd.read_csv("tmp.csv", sep="|", iterator=True) as reader:
.....: reader.get_chunk(5)
.....:
Указание движка анализатора
Pandas в настоящее время поддерживает три движка: C-движок, Python-движок и экспериментальный движок pyarrow (требуется пакет pyarrow). Как правило, движок pyarrow быстрее при больших объёмах данных и эквивалентен по скорости C-движку в большинстве других случаев. Python-движок, как правило, медленнее, чем pyarrow и C-движки в большинстве случаев. Однако движок pyarrow намного менее надёжен, чем C-движок, которому не хватает нескольких функций по сравнению с Python-движком.
Когда это возможно, pandas использует C-анализатор (указанный как engine='c'), но может переключиться на Python, если указаны параметры, не поддерживаемые C.
В настоящее время параметры, не поддерживаемые C- и pyarrow-движками, включают:
sepкроме одного символа (например, разделители вида регулярного выражения)skipfootersep=Noneсdelim_whitespace=False
Указание любого из вышеперечисленных параметров приведёт к ParserWarning, если не выбран явно Python-движок с помощью engine='python'.
Параметры, не поддерживаемые pyarrow-движком, не охваченные списком выше, включают:
float_precisionchunksizecommentnrowsthousandsmemory_mapdialectwarn_bad_lineserror_bad_lineson_bad_linesdelim_whitespacequotinglineterminatorconvertersdecimaliteratordayfirstinfer_datetime_formatverboseskipinitialspacelow_memory
Указание этих параметров с engine='pyarrow' вызовет ValueError.
Чтение/запись удалённых файлов
Вы можете передать URL для чтения или записи удалённых файлов во многие функции ввода-вывода pandas — следующий пример демонстрирует чтение CSV-файла:
df = pd.read_csv("https://download.bls.gov/pub/time.series/cu/cu.item", sep="\t")
Добавлена в версии 1.3.0.
Пользовательский заголовок может быть отправлен вместе с HTTP(s)-запросами путём передачи словаря сопоставлений ключей и значений заголовков в аргумент storage_options как показано ниже:
headers = {"User-Agent": "pandas"}
df = pd.read_csv(
"https://download.bls.gov/pub/time.series/cu/cu.item",
sep="\t",
storage_options=headers
)
Все URL, которые не являются локальными файлами или HTTP(s), обрабатываются через fsspec, если установлен, и его различные реализации файловых систем (включая Amazon S3, Google Cloud, SSH, FTP, webHDFS…). Некоторые из этих реализаций потребуют установки дополнительных пакетов, например, URL-адреса S3 требуют библиотеки s3fs:
df = pd.read_json("s3://pandas-test/adatafile.json")
При работе с удалёнными системами хранения вам, возможно, потребуется дополнительная настройка с переменными окружения или конфигурационными файлами в специальных расположениях. Например, для доступа к данным в вашем ведре S3 вам необходимо определить учетные данные одним из нескольких способов, перечисленных в документации S3Fs. То же самое относится к нескольким бэкендам хранения, и вам следует перейти по ссылкам в fsimpl1 для реализаций, встроенных в fsspec и fsimpl2 для тех, что не входят в основное распределение fsspec.
Вы также можете передать параметры непосредственно драйверу бэкенда. Например, если у вас нет учетных данных S3, вы всё ещё можете получить доступ к общедоступным данным, указав анонимное подключение, например
Добавлена в версии 1.2.0.
pd.read_csv(
"s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/SaKe2013"
"-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv",
storage_options={"anon": True},
)
fsspec также позволяет использовать сложные URL для доступа к данным в сжатых архивах, кэширования файлов локально и многое другое. Чтобы кэшировать вышеприведенный пример локально, вы измените вызов на
pd.read_csv(
"simplecache::s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/"
"SaKe2013-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv",
storage_options={"s3": {"anon": True}},
)
где мы указываем, что параметр «anon» предназначен для части «s3» реализации, а не для реализации кэширования. Обратите внимание, что это кэширует только в временную директорию на время сессии, но вы также можете указать постоянное хранилище.
Вывод данных
Запись в формате CSV
Объекты Series и DataFrame имеют метод экземпляра to_csv, который позволяет сохранить содержимое объекта в виде файла со значениями, разделенными запятыми. Функция принимает несколько аргументов. Только первый является обязательным.
path_or_buf: Строковый путь к файлу для записи или объект файла. Если объект файла, он должен быть открыт с использованиемnewline=''sep: Разделитель полей для выходного файла (по умолчанию “,”)na_rep: Строковое представление пропущенного значения (по умолчанию ‘’)float_format: Форматная строка для чисел с плавающей точкойcolumns: Столбцы для записи (по умолчанию None)header: Выводить заголовки столбцов (по умолчанию True)index: Выводить имена строк (индексы) (по умолчанию True)index_label: Метки столбцов для столбцов индекса, если необходимо. Если None (по умолчанию), иheaderиindexравны True, то используются имена индексов. (Последовательность должна быть указана, еслиDataFrameиспользует MultiIndex).mode: Режим записи Python, по умолчанию ‘w’encoding: Строка, представляющая кодировку для использования, если содержимое не ASCII, для версий Python до 3lineterminator: Последовательность символов, обозначающая конец строки (по умолчаниюos.linesep)quoting: Установка правил цитирования, как в модуле csv (по умолчанию csv.QUOTE_MINIMAL). Обратите внимание, что если вы установилиfloat_format, то числа с плавающей точкой преобразуются в строки, и csv.QUOTE_NONNUMERIC будет рассматривать их как нечисловыеquotechar: Символ, используемый для цитирования полей (по умолчанию ‘”’)doublequote: Управление цитированиемquotecharв полях (по умолчанию True)escapechar: Символ, используемый для экранированияsepиquotechar, если необходимо (по умолчанию None)chunksize: Количество строк для записи за один разdate_format: Форматная строка для объектов datetime
Запись отформатированной строки
Объект DataFrame имеет метод экземпляра to_string, который позволяет управлять строковым представлением объекта. Все аргументы являются необязательными:
bufпо умолчанию None, например, объект StringIOcolumnsпо умолчанию None, которые столбцы записыватьcol_spaceпо умолчанию None, минимальная ширина каждого столбца.na_repпо умолчаниюNaN, представление значения NAformattersпо умолчанию None, словарь (по столбцам) функций, каждая из которых принимает один аргумент и возвращает отформатированную строкуfloat_formatпо умолчанию None, функция, которая принимает один аргумент (float) и возвращает отформатированную строку; должна применяться к числам с плавающей точкой вDataFrame.sparsifyпо умолчанию True, установить в False дляDataFrameс иерархическим индексом, чтобы вывести каждый ключ MultiIndex в каждой строке.index_namesпо умолчанию True, выведет имена индексовindexпо умолчанию True, выведет индекс (т.е., метки строк)headerпо умолчанию True, выведет метки столбцовjustifyпо умолчаниюleft, выведет заголовки столбцов выровненными влево или вправо
Объект Series также имеет метод to_string, но только с аргументами buf, na_rep, float_format. Также есть аргумент length, который, если установлен в True, дополнительно выведет длину Series.
JSON
Чтение и запись файлов и строк в формате JSON.
Запись JSON
Объект Series или DataFrame может быть преобразован в строку JSON. Используйте to_json с необязательными параметрами:
path_or_buf: путь к файлу или буфер для записи вывода. Может бытьNone, в этом случае возвращается строка JSON.-
orient:-
Series: -
по умолчанию
indexдопустимые значения {
split,records,index}
-
DataFrame: -
по умолчанию
columnsдопустимые значения {
split,records,index,columns,values,table}
Формат строки JSON
splitсловарь типа {индекс -> [индекс], столбцы -> [столбцы], данные -> [значения]}
recordsсписок типа [{столбец -> значение}, … , {столбец -> значение}]
indexсловарь типа {индекс -> {столбец -> значение}}
columnsсловарь типа {столбец -> {индекс -> значение}}
valuesтолько массив значений
tableсоответствие схеме таблицы JSON
-
date_format: строка, тип преобразования даты, ‘epoch’ для отметки времени, ‘iso’ для ISO8601.double_precision: количество десятичных знаков для кодирования чисел с плавающей точкой, по умолчанию 10.force_ascii: принудительно кодировать строку в ASCII, по умолчанию True.date_unit: единица времени для кодирования, управляет точностью отметки времени и ISO8601. Одно из значений ‘s’, ‘ms’, ‘us’ или ‘ns’ для секунд, миллисекунд, микросекунд и наносекунд соответственно. По умолчанию ‘ms’.default_handler: обработчик, вызываемый, если объект не может быть преобразован в подходящий для JSON формат. Принимает один аргумент — объект для преобразования и возвращает сериализуемый объект.lines: Еслиrecordsorient, то каждый запис будет записываться в новой строке как json.
Примечание: NaN, NaT и None будут преобразованы в null, а datetime объекты будут преобразованы на основе параметров date_format и date_unit.
In [218]: dfj = pd.DataFrame(np.random.randn(5, 2), columns=list("AB"))
In [219]: json = dfj.to_json()
In [220]: json
Out[220]: '{"A":{"0":-0.1213062281,"1":0.6957746499,"2":0.9597255933,"3":-0.6199759194,"4":-0.7323393705},"B":{"0":-0.0978826728,"1":0.3417343559,"2":-1.1103361029,"3":0.1497483186,"4":0.6877383895}}'
Варианты orient
Существует ряд вариантов формата результирующего файла/строки JSON. Рассмотрим следующие DataFrame и Series:
In [221]: dfjo = pd.DataFrame(
.....: dict(A=range(1, 4), B=range(4, 7), C=range(7, 10)),
.....: columns=list("ABC"),
.....: index=list("xyz"),
.....: )
.....:
In [222]: dfjo
Out[222]:
A B C
x 1 4 7
y 2 5 8
z 3 6 9
In [223]: sjo = pd.Series(dict(x=15, y=16, z=17), name="D")
In [224]: sjo
Out[224]:
x 15
y 16
z 17
Name: D, dtype: int64
Ориентированный по столбцам (по умолчанию для DataFrame ) сериализует данные в виде вложенных объектов JSON со столбцами в качестве основного индекса:
In [225]: dfjo.to_json(orient="columns")
Out[225]: '{"A":{"x":1,"y":2,"z":3},"B":{"x":4,"y":5,"z":6},"C":{"x":7,"y":8,"z":9}}'
# Not available for Series
Ориентированный по индексу (по умолчанию для Series ) аналогично ориентированному по столбцам, но метки индексов теперь являются основными:
In [226]: dfjo.to_json(orient="index")
Out[226]: '{"x":{"A":1,"B":4,"C":7},"y":{"A":2,"B":5,"C":8},"z":{"A":3,"B":6,"C":9}}'
In [227]: sjo.to_json(orient="index")
Out[227]: '{"x":15,"y":16,"z":17}'
Ориентированный по записям сериализует данные в массив JSON с записями столбец -> значение, метки индексов не включаются. Это полезно для передачи данных DataFrame в библиотеки визуализации, например, в JavaScript-библиотеку d3.js:
In [228]: dfjo.to_json(orient="records")
Out[228]: '[{"A":1,"B":4,"C":7},{"A":2,"B":5,"C":8},{"A":3,"B":6,"C":9}]'
In [229]: sjo.to_json(orient="records")
Out[229]: '[15,16,17]'
Ориентированный по значениям — базовый вариант, который сериализует данные в вложенные массивы JSON только со значениями, метки столбцов и индексов не включаются:
In [230]: dfjo.to_json(orient="values")
Out[230]: '[[1,4,7],[2,5,8],[3,6,9]]'
# Not available for Series
Ориентированный по разделу сериализует данные в JSON-объект, содержащий отдельные записи для значений, индексов и столбцов. Имя также включено для Series:
In [231]: dfjo.to_json(orient="split")
Out[231]: '{"columns":["A","B","C"],"index":["x","y","z"],"data":[[1,4,7],[2,5,8],[3,6,9]]}'
In [232]: sjo.to_json(orient="split")
Out[232]: '{"name":"D","index":["x","y","z"],"data":[15,16,17]}'
Ориентированный по таблице сериализует данные в JSON схему таблицы, позволяя сохранять метаданные, включая, но не ограничиваясь, типы данных и имена индексов.
Примечание
Любой вариант orient, который кодирует в JSON-объект, не сохранит порядок меток индексов и столбцов во время обратного сериализации. Если вы хотите сохранить порядок меток, используйте опцию split , так как она использует упорядоченные контейнеры.
Обработка дат
Запись в формате даты ISO:
In [233]: dfd = pd.DataFrame(np.random.randn(5, 2), columns=list("AB"))
In [234]: dfd["date"] = pd.Timestamp("20130101")
In [235]: dfd = dfd.sort_index(axis=1, ascending=False)
In [236]: json = dfd.to_json(date_format="iso")
In [237]: json
Out[237]: '{"date":{"0":"2013-01-01T00:00:00.000","1":"2013-01-01T00:00:00.000","2":"2013-01-01T00:00:00.000","3":"2013-01-01T00:00:00.000","4":"2013-01-01T00:00:00.000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'
Запись в формате даты ISO с микросекундами:
In [238]: json = dfd.to_json(date_format="iso", date_unit="us")
In [239]: json
Out[239]: '{"date":{"0":"2013-01-01T00:00:00.000000","1":"2013-01-01T00:00:00.000000","2":"2013-01-01T00:00:00.000000","3":"2013-01-01T00:00:00.000000","4":"2013-01-01T00:00:00.000000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'
Отметки времени epoch в секундах:
In [240]: json = dfd.to_json(date_format="epoch", date_unit="s")
In [241]: json
Out[241]: '{"date":{"0":1356998400,"1":1356998400,"2":1356998400,"3":1356998400,"4":1356998400},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'
Запись в файл с индексом даты и столбцом даты:
In [242]: dfj2 = dfj.copy()
In [243]: dfj2["date"] = pd.Timestamp("20130101")
In [244]: dfj2["ints"] = list(range(5))
In [245]: dfj2["bools"] = True
In [246]: dfj2.index = pd.date_range("20130101", periods=5)
In [247]: dfj2.to_json("test.json")
In [248]: with open("test.json") as fh:
.....: print(fh.read())
.....:
{"A":{"1356998400000":-0.1213062281,"1357084800000":0.6957746499,"1357171200000":0.9597255933,"1357257600000":-0.6199759194,"1357344000000":-0.7323393705},"B":{"1356998400000":-0.0978826728,"1357084800000":0.3417343559,"1357171200000":-1.1103361029,"1357257600000":0.1497483186,"1357344000000":0.6877383895},"date":{"1356998400000":1356998400000,"1357084800000":1356998400000,"1357171200000":1356998400000,"1357257600000":1356998400000,"1357344000000":1356998400000},"ints":{"1356998400000":0,"1357084800000":1,"1357171200000":2,"1357257600000":3,"1357344000000":4},"bools":{"1356998400000":true,"1357084800000":true,"1357171200000":true,"1357257600000":true,"1357344000000":true}}
Поведение по умолчанию
Если JSON-сериализатор не может напрямую обработать содержимое контейнера, он откажется от обработки следующим образом:
если тип данных не поддерживается (например,
np.complex_), тоdefault_handler, если предоставлен, будет вызываться для каждого значения, в противном случае возникает исключение.-
если объект не поддерживается, он предпримет следующие действия:
проверит, определен ли метод
toDictобъекта, и вызовет его. МетодtoDictдолжен вернутьdict, который затем будет сериализован в JSON.вызовет
default_handler, если он был предоставлен.преобразует объект в
dictпутем обхода его содержимого. Однако это часто приведет кOverflowErrorили даст неожиданные результаты.
В целом, лучшим подходом для недопустимых объектов или типов данных является предоставление default_handler. Например:
>>> DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json() # raises
RuntimeError: Unhandled numpy dtype 15
может быть обработано, указав простой default_handler:
In [249]: pd.DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json(default_handler=str)
Out[249]: '{"0":{"0":"(1+0j)","1":"(2+0j)","2":"(1+2j)"}}'
Чтение JSON
Чтение JSON-строки в объект pandas может принимать ряд параметров. Парсер будет пытаться разобрать DataFrame , если typ не указан или равен None. Чтобы явно принудительно использовать Series парсинг, передайте typ=series
filepath_or_buffer: ВАЛИДНАЯ JSON-строка или дескриптор файла/объект StringIO. Строка может быть URL. Допустимые схемы URL включают http, ftp, S3 и file. Для URL-адресов файлов ожидается хост. Например, локальный файл может быть file://localhost/путь/к/таблице.jsontyp: тип объекта для извлечения (series или frame), по умолчанию ‘frame’-
orient:- Series :
-
по умолчанию
indexдопустимые значения {
split,records,index}
- DataFrame
-
по умолчанию
columnsдопустимые значения {
split,records,index,columns,values,table}
Формат JSON-строки
splitпохожий на словарь {индекс -> [индекс], столбцы -> [столбцы], данные -> [значения]}
recordsпохожий на список [{столбец -> значение}, … , {столбец -> значение}]
indexпохожий на словарь {индекс -> {столбец -> значение}}
columnsпохожий на словарь {столбец -> {индекс -> значение}}
valuesтолько массив значений
tableсоответствует схеме таблицы JSON
dtype: если True, определить типы данных, если словарь столбец-тип данных, то использовать их, еслиFalse, тогда вообще не определять типы данных, по умолчанию True, применяется только к данным.convert_axes: булево, попытаться преобразовать оси в соответствующие типы данных, по умолчаниюTrueconvert_dates: список столбцов для парсинга дат; ЕслиTrue, то попытаться разобрать столбцы, подобные дате, по умолчаниюTrue.keep_default_dates: булево, по умолчаниюTrue. Если парсинг дат, то разобрать столбцы по умолчанию, подобные дате.numpy: прямое декодирование в массивы NumPy. По умолчаниюFalse; Поддерживаются только числовые данные, хотя метки могут быть не числовыми. Также обратите внимание, что порядок JSON ДОЛЖЕН быть одинаковым для каждого элемента, еслиnumpy=True.precise_float: булево, по умолчаниюFalse. Установите, чтобы включить использование функции с большей точностью (strtod) при декодировании строк в двойные значения. По умолчанию (False) используется быстрая, но менее точная встроенная функциональность.date_unit: строка, единица измерения временной метки для определения преобразования дат. По умолчанию None. По умолчанию точность временной метки будет определена, если это не нужно, передайте одно из ‘s’, ‘ms’, ‘us’ или ‘ns’, чтобы принудительно установить точность временной метки в секунды, миллисекунды, микросекунды или наносекунды соответственно.lines: считывает файл как один JSON-объект на строку.encoding: кодировка для декодирования py3 байтов.chunksize: при использовании в сочетании сlines=True, возвращает JsonReader, который считываетchunksizeстрок за итерацию.
Парсер поднимет одну из ValueError/TypeError/AssertionError ошибок, если JSON не может быть распарсен.
Если использовалось нестандартное orient при кодировании в JSON, убедитесь, что вы передаёте тот же параметр здесь, чтобы декодирование давало осмысленные результаты. См. Параметры ориентирования для обзора.
Преобразование данных
Значения по умолчанию convert_axes=True, dtype=True, и convert_dates=True будут пытаться разобрать оси и все данные в соответствующие типы, включая даты. Если необходимо переопределить конкретные типы данных, передайте словарь в dtype. convert_axes следует установить в False только если необходимо сохранить числовые значения в виде строк (например, ‘1’, ‘2’) в осях.
Примечание
Большие целочисленные значения могут быть преобразованы в даты, если convert_dates=True и данные и/или метки столбцов имеют «похожий на дату» вид. Точный порог зависит от date_unit. «Похожий на дату» означает, что метка столбца соответствует одному из следующих критериев:
она оканчивается на
'_at'она оканчивается на
'_time'она начинается с
'timestamp'она является
'modified'она является
'date'
Предупреждение
При чтении JSON-данных автоматическое приведение типов имеет некоторые особенности:
индекс может быть восстановлен в другом порядке, чем при сериализации, то есть гарантируется, что возвращённый порядок будет таким же, как и раньше
столбец, содержащий данные
float, будет преобразован вinteger, если это можно сделать безопасно, например, столбец1.столбцы bool будут преобразованы в
integerпри восстановлении
Поэтому бывают случаи, когда вам может потребоваться указать конкретные типы данных с помощью ключевого аргумента dtype.
Чтение из JSON-строки:
In [250]: pd.read_json(json)
Out[250]:
date B A
0 2013-01-01 0.403310 0.176444
1 2013-01-01 0.301624 -0.154951
2 2013-01-01 -1.369849 -2.179861
3 2013-01-01 1.462696 -0.954208
4 2013-01-01 -0.826591 -1.743161
Чтение из файла:
In [251]: pd.read_json("test.json")
Out[251]:
A B date ints bools
2013-01-01 -0.121306 -0.097883 2013-01-01 0 True
2013-01-02 0.695775 0.341734 2013-01-01 1 True
2013-01-03 0.959726 -1.110336 2013-01-01 2 True
2013-01-04 -0.619976 0.149748 2013-01-01 3 True
2013-01-05 -0.732339 0.687738 2013-01-01 4 True
Не преобразовывать данные (но всё равно преобразовывать оси и даты):
In [252]: pd.read_json("test.json", dtype=object).dtypes
Out[252]:
A object
B object
date object
ints object
bools object
dtype: object
Указать типы данных для преобразования:
In [253]: pd.read_json("test.json", dtype={"A": "float32", "bools": "int8"}).dtypes
Out[253]:
A float32
B float64
date datetime64[ns]
ints int64
bools int8
dtype: object
Сохранить строковые индексы:
In [254]: si = pd.DataFrame(
.....: np.zeros((4, 4)), columns=list(range(4)), index=[str(i) for i in range(4)]
.....: )
.....:
In [255]: si
Out[255]:
0 1 2 3
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0
In [256]: si.index
Out[256]: Index(['0', '1', '2', '3'], dtype='object')
In [257]: si.columns
Out[257]: Int64Index([0, 1, 2, 3], dtype='int64')
In [258]: json = si.to_json()
In [259]: sij = pd.read_json(json, convert_axes=False)
In [260]: sij
Out[260]:
0 1 2 3
0 0 0 0 0
1 0 0 0 0
2 0 0 0 0
3 0 0 0 0
In [261]: sij.index
Out[261]: Index(['0', '1', '2', '3'], dtype='object')
In [262]: sij.columns
Out[262]: Index(['0', '1', '2', '3'], dtype='object')
Даты, записанные в наносекундах, необходимо читать обратно в наносекундах:
In [263]: json = dfj2.to_json(date_unit="ns")
# Try to parse timestamps as milliseconds -> Won't Work
In [264]: dfju = pd.read_json(json, date_unit="ms")
In [265]: dfju
Out[265]:
A B date ints bools
1356998400000000000 -0.121306 -0.097883 1356998400000000000 0 True
1357084800000000000 0.695775 0.341734 1356998400000000000 1 True
1357171200000000000 0.959726 -1.110336 1356998400000000000 2 True
1357257600000000000 -0.619976 0.149748 1356998400000000000 3 True
1357344000000000000 -0.732339 0.687738 1356998400000000000 4 True
# Let pandas detect the correct precision
In [266]: dfju = pd.read_json(json)
In [267]: dfju
Out[267]:
A B date ints bools
2013-01-01 -0.121306 -0.097883 2013-01-01 0 True
2013-01-02 0.695775 0.341734 2013-01-01 1 True
2013-01-03 0.959726 -1.110336 2013-01-01 2 True
2013-01-04 -0.619976 0.149748 2013-01-01 3 True
2013-01-05 -0.732339 0.687738 2013-01-01 4 True
# Or specify that all timestamps are in nanoseconds
In [268]: dfju = pd.read_json(json, date_unit="ns")
In [269]: dfju
Out[269]:
A B date ints bools
2013-01-01 -0.121306 -0.097883 2013-01-01 0 True
2013-01-02 0.695775 0.341734 2013-01-01 1 True
2013-01-03 0.959726 -1.110336 2013-01-01 2 True
2013-01-04 -0.619976 0.149748 2013-01-01 3 True
2013-01-05 -0.732339 0.687738 2013-01-01 4 True
Параметр Numpy
Примечание
Этот параметр устарел с версии 1.0.0 и будет вызывать FutureWarning.
Это поддерживает только числовые данные. Метки индекса и столбцов могут быть не числовыми, например, строками, датами и т. д.
Если numpy=True передаётся в read_json, будет сделана попытка определить подходящий тип данных во время десериализации и затем непосредственно декодировать в массивы NumPy, минуя промежуточные объекты Python.
Это может ускорить процесс, если вы десериализуете большое количество числовых данных:
In [270]: randfloats = np.random.uniform(-100, 1000, 10000)
In [271]: randfloats.shape = (1000, 10)
In [272]: dffloats = pd.DataFrame(randfloats, columns=list("ABCDEFGHIJ"))
In [273]: jsonfloats = dffloats.to_json()
In [274]: %timeit pd.read_json(jsonfloats)
11.6 ms +- 92.8 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [275]: %timeit pd.read_json(jsonfloats, numpy=True)
8.75 ms +- 107 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
Ускорение менее заметно для меньших наборов данных:
In [276]: jsonfloats = dffloats.head(100).to_json()
In [277]: %timeit pd.read_json(jsonfloats)
7.27 ms +- 105 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
In [278]: %timeit pd.read_json(jsonfloats, numpy=True)
6.69 ms +- 117 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
Предупреждение
Прямое декодирование NumPy делает ряд предположений и может завершиться ошибкой или выдать неожиданный результат, если эти предположения не выполнены:
данные являются числовыми.
данные однородные. Тип данных определяется из первого декодированного значения. Может быть выброшено
ValueError, или могут быть получены некорректные результаты, если это условие не выполняется.метки упорядочены. Метки считываются только из первого контейнера, предполагается, что каждая последующая строка/столбец закодированы в том же порядке. Это должно выполняться, если данные были закодированы с помощью
to_json, но может не выполняться, если JSON получен из другого источника.
Нормализация
pandas предоставляет вспомогательную функцию для преобразования словаря или списка словарей в нормализованную плоскую таблицу.
In [279]: data = [
.....: {"id": 1, "name": {"first": "Coleen", "last": "Volk"}},
.....: {"name": {"given": "Mark", "family": "Regner"}},
.....: {"id": 2, "name": "Faye Raker"},
.....: ]
.....:
In [280]: pd.json_normalize(data)
Out[280]:
id name.first name.last name.given name.family name
0 1.0 Coleen Volk NaN NaN NaN
1 NaN NaN NaN Mark Regner NaN
2 2.0 NaN NaN NaN NaN Faye Raker
In [281]: data = [
.....: {
.....: "state": "Florida",
.....: "shortname": "FL",
.....: "info": {"governor": "Rick Scott"},
.....: "county": [
.....: {"name": "Dade", "population": 12345},
.....: {"name": "Broward", "population": 40000},
.....: {"name": "Palm Beach", "population": 60000},
.....: ],
.....: },
.....: {
.....: "state": "Ohio",
.....: "shortname": "OH",
.....: "info": {"governor": "John Kasich"},
.....: "county": [
.....: {"name": "Summit", "population": 1234},
.....: {"name": "Cuyahoga", "population": 1337},
.....: ],
.....: },
.....: ]
.....:
In [282]: pd.json_normalize(data, "county", ["state", "shortname", ["info", "governor"]])
Out[282]:
name population state shortname info.governor
0 Dade 12345 Florida FL Rick Scott
1 Broward 40000 Florida FL Rick Scott
2 Palm Beach 60000 Florida FL Rick Scott
3 Summit 1234 Ohio OH John Kasich
4 Cuyahoga 1337 Ohio OH John Kasich
Параметр max_level предоставляет больший контроль над уровнем завершения нормализации. При max_level=1 следующий фрагмент выполняет нормализацию до первого уровня вложенности предоставленного словаря.
In [283]: data = [
.....: {
.....: "CreatedBy": {"Name": "User001"},
.....: "Lookup": {
.....: "TextField": "Some text",
.....: "UserField": {"Id": "ID001", "Name": "Name001"},
.....: },
.....: "Image": {"a": "b"},
.....: }
.....: ]
.....:
In [284]: pd.json_normalize(data, max_level=1)
Out[284]:
CreatedBy.Name Lookup.TextField Lookup.UserField Image.a
0 User001 Some text {'Id': 'ID001', 'Name': 'Name001'} b
JSON, разделитель — символ новой строки
pandas может читать и записывать файлы JSON, разделенные символом новой строки, которые часто используются в обработке данных в конвейерах Hadoop или Spark.
Для файлов JSON, разделенных символом новой строки, pandas также может возвращать итератор, который считывает chunksize строк за раз. Это может быть полезно для больших файлов или для чтения из потока.
In [285]: jsonl = """
.....: {"a": 1, "b": 2}
.....: {"a": 3, "b": 4}
.....: """
.....:
In [286]: df = pd.read_json(jsonl, lines=True)
In [287]: df
Out[287]:
a b
0 1 2
1 3 4
In [288]: df.to_json(orient="records", lines=True)
Out[288]: '{"a":1,"b":2}\n{"a":3,"b":4}\n'
# reader is an iterator that returns ``chunksize`` lines each iteration
In [289]: with pd.read_json(StringIO(jsonl), lines=True, chunksize=1) as reader:
.....: reader
.....: for chunk in reader:
.....: print(chunk)
.....:
Empty DataFrame
Columns: []
Index: []
a b
0 1 2
a b
1 3 4
Схема таблицы
Схема таблицы — это спецификация для описания табличных наборов данных как JSON-объекта. JSON содержит информацию об именах полей, типах и других атрибутах. Вы можете использовать ориентацию table для создания JSON-строки с двумя полями, schema и data.
In [290]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2, 3],
.....: "B": ["a", "b", "c"],
.....: "C": pd.date_range("2016-01-01", freq="d", periods=3),
.....: },
.....: index=pd.Index(range(3), name="idx"),
.....: )
.....:
In [291]: df
Out[291]:
A B C
idx
0 1 a 2016-01-01
1 2 b 2016-01-02
2 3 c 2016-01-03
In [292]: df.to_json(orient="table", date_format="iso")
Out[292]: '{"schema":{"fields":[{"name":"idx","type":"integer"},{"name":"A","type":"integer"},{"name":"B","type":"string"},{"name":"C","type":"datetime"}],"primaryKey":["idx"],"pandas_version":"1.4.0"},"data":[{"idx":0,"A":1,"B":"a","C":"2016-01-01T00:00:00.000"},{"idx":1,"A":2,"B":"b","C":"2016-01-02T00:00:00.000"},{"idx":2,"A":3,"B":"c","C":"2016-01-03T00:00:00.000"}]}'
Поле schema содержит ключ fields, который в свою очередь содержит список пар «имя столбца — тип», включая Index или MultiIndex (см. список типов ниже). Поле schema также содержит поле primaryKey, если (мульти)индекс уникален.
Второе поле, data, содержит сериализованные данные с ориентацией records. Индекс включен, а все даты и времена форматируются в соответствии с ISO 8601, как требуется спецификацией Схемы таблицы.
Полный список поддерживаемых типов описан в спецификации Схемы таблицы. Эта таблица демонстрирует сопоставление с типами pandas:
Тип pandas | Тип Схемы таблицы |
|---|---|
int64 | целое число |
float64 | число |
bool | логическое |
datetime64[ns] | дата и время |
timedelta64[ns] | продолжительность |
categorical | любой |
object | строка |
Некоторые замечания по сгенерированной схеме таблицы:
Объект
schemaсодержит полеpandas_version. Оно содержит версию диалекта pandas схемы и будет увеличиваться с каждым пересмотром.-
Все даты преобразуются в UTC при сериализации. Даже значения с часовым поясом без указания часового пояса обрабатываются как UTC со смещением 0.
In [293]: from pandas.io.json import build_table_schema In [294]: s = pd.Series(pd.date_range("2016", periods=4)) In [295]: build_table_schema(s) Out[295]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'datetime'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} -
Даты и времена с часовым поясом (до сериализации) включают дополнительное поле
tzс именем часового пояса (например,'US/Central').In [296]: s_tz = pd.Series(pd.date_range("2016", periods=12, tz="US/Central")) In [297]: build_table_schema(s_tz) Out[297]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'datetime', 'tz': 'US/Central'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} -
Периоды преобразуются в отметки времени до сериализации и поэтому ведут себя аналогично, преобразуясь в UTC. Кроме того, периоды будут содержать дополнительное поле
freqс частотой периода, например,'A-DEC'.In [298]: s_per = pd.Series(1, index=pd.period_range("2016", freq="A-DEC", periods=4)) In [299]: build_table_schema(s_per) Out[299]: {'fields': [{'name': 'index', 'type': 'datetime', 'freq': 'A-DEC'}, {'name': 'values', 'type': 'integer'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} -
Категории используют тип
anyи ограничениеenum, перечисляющее множество возможных значений. Кроме того, включено полеordered:In [300]: s_cat = pd.Series(pd.Categorical(["a", "b", "a"])) In [301]: build_table_schema(s_cat) Out[301]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'any', 'constraints': {'enum': ['a', 'b']}, 'ordered': False}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} -
Поле
primaryKey, содержащее массив меток, включается если индекс уникален:In [302]: s_dupe = pd.Series([1, 2], index=[1, 1]) In [303]: build_table_schema(s_dupe) Out[303]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'integer'}], 'pandas_version': '1.4.0'} -
Поведение
primaryKeyаналогично с MultiIndex, но в этом случаеprimaryKeyявляется массивом:In [304]: s_multi = pd.Series(1, index=pd.MultiIndex.from_product([("a", "b"), (0, 1)])) In [305]: build_table_schema(s_multi) Out[305]: {'fields': [{'name': 'level_0', 'type': 'string'}, {'name': 'level_1', 'type': 'integer'}, {'name': 'values', 'type': 'integer'}], 'primaryKey': FrozenList(['level_0', 'level_1']), 'pandas_version': '1.4.0'} -
Стандартные правила именования:
Для рядов используется
object.name. Если он отсутствует, то используется имяvaluesДля столбцов используется строковое представление имени столбца
Для
DataFrames, используется строковое представление имени столбцаДля
Index(неMultiIndex), используетсяindex.name, а в качестве резервного варианта используетсяindex, если оно равно None.Для
MultiIndex, используетсяmi.names. Если у любого уровня нет имени, то используетсяlevel_<i>.
read_json также принимает orient='table' в качестве аргумента. Это позволяет сохранить метаданные, такие как dtypes и имена индексов, в обратимом формате.
In [306]: df = pd.DataFrame( .....: { .....: "foo": [1, 2, 3, 4], .....: "bar": ["a", "b", "c", "d"], .....: "baz": pd.date_range("2018-01-01", freq="d", periods=4), .....: "qux": pd.Categorical(["a", "b", "c", "c"]), .....: }, .....: index=pd.Index(range(4), name="idx"), .....: ) .....: In [307]: df Out[307]: foo bar baz qux idx 0 1 a 2018-01-01 a 1 2 b 2018-01-02 b 2 3 c 2018-01-03 c 3 4 d 2018-01-04 c In [308]: df.dtypes Out[308]: foo int64 bar object baz datetime64[ns] qux category dtype: object In [309]: df.to_json("test.json", orient="table") In [310]: new_df = pd.read_json("test.json", orient="table") In [311]: new_df Out[311]: foo bar baz qux idx 0 1 a 2018-01-01 a 1 2 b 2018-01-02 b 2 3 c 2018-01-03 c 3 4 d 2018-01-04 c In [312]: new_df.dtypes Out[312]: foo int64 bar object baz datetime64[ns] qux category dtype: object
Обратите внимание, что строка «index» в качестве имени Index не является обратимой, также как и любые имена, начинающиеся с 'level_' в MultiIndex. По умолчанию они используются в DataFrame.to_json() для обозначения пропущенных значений, и последующее чтение не может отличить намерение.
In [313]: df.index.name = "index"
In [314]: df.to_json("test.json", orient="table")
In [315]: new_df = pd.read_json("test.json", orient="table")
In [316]: print(new_df.index.name)
None
При использовании orient='table' вместе с пользовательскими ExtensionArray, сгенерированная схема будет содержать дополнительный ключ extDtype в соответствующем элементе fields. Этот дополнительный ключ не является стандартным, но он позволяет выполнять обратные преобразования в JSON для типов расширений (например, read_json(df.to_json(orient="table"), orient="table")).
Ключ extDtype содержит имя расширения. Если вы правильно зарегистрировали ExtensionDtype, pandas будет использовать это имя для поиска в реестре и повторно преобразует сериализованные данные в ваш пользовательский тип.
HTML
Чтение содержимого HTML
Предупреждение
Мы настоятельно рекомендуем прочитать Примечания к разбору таблиц HTML ниже, касающиеся проблем с парсерами BeautifulSoup4/html5lib/lxml.
Функция верхнего уровня read_html() может принимать строку/файл/URL HTML и парсить HTML-таблицы в список pandas DataFrames. Давайте рассмотрим несколько примеров.
Примечание
read_html возвращает list объектов DataFrame, даже если в содержимом HTML содержится только одна таблица.
Чтение URL без опций:
In [320]: "https://www.fdic.gov/resources/resolutions/bank-failures/failed-bank-list"
In [321]: pd.read_html(url)
Out[321]:
[ Bank NameBank CityCity StateSt ... Acquiring InstitutionAI Closing DateClosing FundFund
0 Almena State Bank Almena KS ... Equity Bank October 23, 2020 10538
1 First City Bank of Florida Fort Walton Beach FL ... United Fidelity Bank, fsb October 16, 2020 10537
2 The First State Bank Barboursville WV ... MVB Bank, Inc. April 3, 2020 10536
3 Ericson State Bank Ericson NE ... Farmers and Merchants Bank February 14, 2020 10535
4 City National Bank of New Jersey Newark NJ ... Industrial Bank November 1, 2019 10534
.. ... ... ... ... ... ... ...
558 Superior Bank, FSB Hinsdale IL ... Superior Federal, FSB July 27, 2001 6004
559 Malta National Bank Malta OH ... North Valley Bank May 3, 2001 4648
560 First Alliance Bank & Trust Co. Manchester NH ... Southern New Hampshire Bank & Trust February 2, 2001 4647
561 National State Bank of Metropolis Metropolis IL ... Banterra Bank of Marion December 14, 2000 4646
562 Bank of Honolulu Honolulu HI ... Bank of the Orient October 13, 2000 4645
[563 rows x 7 columns]]
Примечание
Данные с указанного выше URL меняются каждую понедельник, поэтому полученные данные могут немного отличаться.
Чтение содержимого файла из указанного выше URL и передача его read_html в качестве строки:
In [317]: html_str = """
.....: <table>
.....: <tr>
.....: <th>A</th>
.....: <th colspan="1">B</th>
.....: <th rowspan="1">C</th>
.....: </tr>
.....: <tr>
.....: <td>a</td>
.....: <td>b</td>
.....: <td>c</td>
.....: </tr>
.....: </table>
.....: """
.....:
In [318]: with open("tmp.html", "w") as f:
.....: f.write(html_str)
.....:
In [319]: df = pd.read_html("tmp.html")
In [320]: df[0]
Out[320]:
A B C
0 a b c
Вы также можете передать экземпляр StringIO, если хотите:
In [321]: dfs = pd.read_html(StringIO(html_str))
In [322]: dfs[0]
Out[322]:
A B C
0 a b c
Примечание
Следующие примеры не выполняются IPython-оценщиком, поскольку большое количество функций, обращающихся к сети, замедляет построение документации. Если вы обнаружите ошибку или пример, который не выполняется, не стесняйтесь сообщать об этом на странице проблем pandas в GitHub pandas GitHub issues page.
Чтение URL и поиск таблицы, содержащей определенный текст:
match = "Metcalf Bank"
df_list = pd.read_html(url, match=match)
Укажите строку заголовка (по умолчанию <th> или <td> элементы, расположенные в <thead>, используются для формирования индекса столбцов, если в <thead> содержатся несколько строк, создаётся MultiIndex); если указана, строка заголовка берётся из данных минус проанализированные элементы заголовка (<th> элементы).
dfs = pd.read_html(url, header=0)
Укажите столбец индекса:
dfs = pd.read_html(url, index_col=0)
Укажите количество строк для пропуска:
dfs = pd.read_html(url, skiprows=0)
Укажите количество строк для пропуска, используя список (range также работает):
dfs = pd.read_html(url, skiprows=range(2))
Укажите атрибут HTML:
dfs1 = pd.read_html(url, attrs={"id": "table"})
dfs2 = pd.read_html(url, attrs={"class": "sortable"})
print(np.array_equal(dfs1[0], dfs2[0])) # Should be True
Укажите значения, которые должны быть преобразованы в NaN:
dfs = pd.read_html(url, na_values=["No Acquirer"])
Укажите, следует ли сохранить набор значений NaN по умолчанию:
dfs = pd.read_html(url, keep_default_na=False)
Укажите преобразователи для столбцов. Это полезно для числовых текстовых данных, имеющих ведущие нули. По умолчанию столбцы, которые являются числовыми, преобразуются в числовые типы, и ведущие нули теряются. Чтобы этого избежать, мы можем преобразовать эти столбцы в строки.
url_mcc = "https://en.wikipedia.org/wiki/Mobile_country_code"
dfs = pd.read_html(
url_mcc,
match="Telekom Albania",
header=0,
converters={"MNC": str},
)
Используйте некоторую комбинацию вышеперечисленного:
dfs = pd.read_html(url, match="Metcalf Bank", index_col=0)
Чтение выходных данных pandas to_html (с некоторыми потерями точности чисел с плавающей запятой):
df = pd.DataFrame(np.random.randn(2, 2))
s = df.to_html(float_format="{0:.40g}".format)
dfin = pd.read_html(s, index_col=0)
Бэкенд lxml вызовет ошибку при неудачном анализе, если это единственный парсер, который вы предоставляете. Если у вас только один парсер, вы можете передать просто строку, но считается хорошей практикой передавать список с одной строкой, если, например, функция ожидает последовательность строк. Вы можете использовать:
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml"])
Или вы могли бы передать flavor='lxml' без списка:
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor="lxml")
Однако, если у вас установлены bs4 и html5lib и вы передадите None или ['lxml',
'bs4'], то анализ, скорее всего, пройдет успешно. Обратите внимание, что как только анализ пройдёт успешно, функция вернёт значение.
dfs = pd.read_html(url, "Metcalf Bank", index_col=0, flavor=["lxml", "bs4"])
Ссылки могут быть извлечены из ячеек вместе с текстом, используя extract_links="all".
In [323]: html_table = """
.....: <table>
.....: <tr>
.....: <th>GitHub</th>
.....: </tr>
.....: <tr>
.....: <td><a href="https://github.com/pandas-dev/pandas">pandas</a></td>
.....: </tr>
.....: </table>
.....: """
.....:
In [324]: df = pd.read_html(
.....: html_table,
.....: extract_links="all"
.....: )[0]
.....:
In [325]: df
Out[325]:
(GitHub, None)
0 (pandas, https://github.com/pandas-dev/pandas)
In [326]: df[("GitHub", None)]
Out[326]:
0 (pandas, https://github.com/pandas-dev/pandas)
Name: (GitHub, None), dtype: object
In [327]: df[("GitHub", None)].str[1]
Out[327]:
0 https://github.com/pandas-dev/pandas
Name: (GitHub, None), dtype: object
Новое в версии 1.5.0.
Запись в файлы HTML
Объекты DataFrame имеют метод экземпляра to_html, который отображает содержимое DataFrame в виде HTML-таблицы. Аргументы функции такие же, как в методе to_string.
Примечание
Для краткости здесь не показаны все возможные параметры для DataFrame.to_html. См. to_html() для полного набора опций.
Примечание
В среде, поддерживающей отображение HTML, например, в Jupyter Notebook, display(HTML(...))` отобразит исходный HTML в этой среде.
In [328]: from IPython.display import display, HTML
In [329]: df = pd.DataFrame(np.random.randn(2, 2))
In [330]: df
Out[330]:
0 1
0 0.070319 1.773907
1 0.253908 0.414581
In [331]: html = df.to_html()
In [332]: print(html) # raw html
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>0</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>0.070319</td>
<td>1.773907</td>
</tr>
<tr>
<th>1</th>
<td>0.253908</td>
<td>0.414581</td>
</tr>
</tbody>
</table>
In [333]: display(HTML(html))
<IPython.core.display.HTML object>
Аргумент columns ограничит отображаемые столбцы:
In [334]: html = df.to_html(columns=[0])
In [335]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>0</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>0.070319</td>
</tr>
<tr>
<th>1</th>
<td>0.253908</td>
</tr>
</tbody>
</table>
In [336]: display(HTML(html))
<IPython.core.display.HTML object>
float_format принимает вызываемый объект Python для управления точностью чисел с плавающей запятой:
In [337]: html = df.to_html(float_format="{0:.10f}".format)
In [338]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>0</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>0.0703192665</td>
<td>1.7739074228</td>
</tr>
<tr>
<th>1</th>
<td>0.2539083433</td>
<td>0.4145805920</td>
</tr>
</tbody>
</table>
In [339]: display(HTML(html))
<IPython.core.display.HTML object>
bold_rows сделает метки строк жирными по умолчанию, но вы можете это отключить:
In [340]: html = df.to_html(bold_rows=False)
In [341]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>0</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>0.070319</td>
<td>1.773907</td>
</tr>
<tr>
<td>1</td>
<td>0.253908</td>
<td>0.414581</td>
</tr>
</tbody>
</table>
In [342]: display(HTML(html))
<IPython.core.display.HTML object>
Аргумент classes предоставляет возможность присваивать результирующей HTML-таблице CSS-классы. Обратите внимание, что эти классы добавляются к существующему классу 'dataframe'.
In [343]: print(df.to_html(classes=["awesome_table_class", "even_more_awesome_class"]))
<table border="1" class="dataframe awesome_table_class even_more_awesome_class">
<thead>
<tr style="text-align: right;">
<th></th>
<th>0</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>0.070319</td>
<td>1.773907</td>
</tr>
<tr>
<th>1</th>
<td>0.253908</td>
<td>0.414581</td>
</tr>
</tbody>
</table>
Аргумент render_links предоставляет возможность добавлять гиперссылки в ячейки, содержащие URL.
In [344]: url_df = pd.DataFrame(
.....: {
.....: "name": ["Python", "pandas"],
.....: "url": ["https://www.python.org/", "https://pandas.pydata.org"],
.....: }
.....: )
.....:
In [345]: html = url_df.to_html(render_links=True)
In [346]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>name</th>
<th>url</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>Python</td>
<td><a href="https://www.python.org/" target="_blank">https://www.python.org/</a></td>
</tr>
<tr>
<th>1</th>
<td>pandas</td>
<td><a href="https://pandas.pydata.org" target="_blank">https://pandas.pydata.org</a></td>
</tr>
</tbody>
</table>
In [347]: display(HTML(html))
<IPython.core.display.HTML object>
Наконец, аргумент escape позволяет управлять тем, будут ли символы “<”, “>” и “&” экранированы в результирующем HTML (по умолчанию это True). Таким образом, чтобы получить HTML без экранированных символов, передайте escape=False
In [348]: df = pd.DataFrame({"a": list("&<>"), "b": np.random.randn(3)})
Экранированные:
In [349]: html = df.to_html()
In [350]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>&</td>
<td>0.842321</td>
</tr>
<tr>
<th>1</th>
<td><</td>
<td>0.211337</td>
</tr>
<tr>
<th>2</th>
<td>></td>
<td>-1.055427</td>
</tr>
</tbody>
</table>
In [351]: display(HTML(html))
<IPython.core.display.HTML object>
Не экранированные:
In [352]: html = df.to_html(escape=False)
In [353]: print(html)
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>&</td>
<td>0.842321</td>
</tr>
<tr>
<th>1</th>
<td><</td>
<td>0.211337</td>
</tr>
<tr>
<th>2</th>
<td>></td>
<td>-1.055427</td>
</tr>
</tbody>
</table>
In [354]: display(HTML(html))
<IPython.core.display.HTML object>
Примечание
Некоторые браузеры могут не отображать разницу в представлении двух предыдущих HTML-таблиц.
Особенности разбора HTML-таблиц
Существуют некоторые проблемы с версионированием библиотек, используемых для разбора HTML-таблиц в функции pandas io верхнего уровня read_html.
Проблемы с lxml
-
Преимущества
-
Недостатки
lxml не гарантирует результаты своего разбора, если ему не предоставлен строго валидный разметка.
В свете вышесказанного, мы позволили вам, пользователю, использовать бэкенд lxml, но этот бэкенд будет использовать html5lib, если lxml не сможет обработать данные.
Поэтому настоятельно рекомендуется установить как BeautifulSoup4, так и html5lib, чтобы вы всё равно получали валидный результат (при условии, что всё остальное валидно), даже если lxml потерпит неудачу.
Проблемы с BeautifulSoup4 с использованием lxml в качестве бэкенда
Вышеупомянутые проблемы также актуальны здесь, поскольку BeautifulSoup4 по сути просто оболочка вокруг бэкенда парсера.
Проблемы с BeautifulSoup4 с использованием html5lib в качестве бэкенда
-
Преимущества
html5lib намного более лоялен, чем lxml, и, следовательно, работает с реальной разметкой намного разумнее, а не, например, просто удаляет элемент без уведомления.
html5lib автоматически генерирует валидную HTML5 разметку из невалидной. Это крайне важно для разбора HTML-таблиц, поскольку гарантирует валидный документ. Однако это не означает, что он «правильный», так как процесс исправления разметки не имеет единого определения.
html5lib является чистым Python и не требует дополнительных этапов сборки, помимо собственной установки.
-
Недостатки
Самый большой недостаток использования html5lib — его медлительность. Однако следует учесть, что многие таблицы в сети не настолько велики, чтобы время выполнения алгоритма разбора имело значение. Скорее всего, узким местом будет процесс чтения исходного текста из URL по сети, т. е. ввод-вывод. Для очень больших таблиц это может быть не так.
LaTeX
Новое в версии 1.3.0.
В настоящее время нет методов чтения из LaTeX, только методы вывода.
Запись в файлы LaTeX
Примечание
Объекты DataFrame и Styler в настоящее время имеют метод to_latex. Мы рекомендуем использовать метод Styler.to_latex() вместо DataFrame.to_latex() из-за большей гибкости первого с условным стилированием и возможного будущего устаревания второго.
Просмотрите документацию для Styler.to_latex, в которой приведены примеры условного форматирования и объяснено действие его ключевых аргументов.
Для простого применения достаточно следующего шаблона.
In [355]: df = pd.DataFrame([[1, 2], [3, 4]], index=["a", "b"], columns=["c", "d"])
In [356]: print(df.style.to_latex())
\begin{tabular}{lrr}
& c & d \\
a & 1 & 2 \\
b & 3 & 4 \\
\end{tabular}
Чтобы отформатировать значения перед выводом, цепочкой вызовов метода Styler.format.
In [357]: print(df.style.format("€ {}").to_latex())
\begin{tabular}{lrr}
& c & d \\
a & € 1 & € 2 \\
b & € 3 & € 4 \\
\end{tabular}
XML
Чтение XML
Новое в версии 1.3.0.
Функция верхнего уровня read_xml() может принимать строку/файл/URL XML и будет парсить узлы и атрибуты в пандас DataFrame.
Примечание
Поскольку не существует стандартной структуры XML, где типы дизайна могут различаться многими способами, read_xml лучше всего работает с более плоскими, мелкоуровневыми версиями. Если документ XML сильно вложен, используйте функцию stylesheet, чтобы преобразовать XML в более плоскую версию.
Давайте рассмотрим несколько примеров.
Чтение строки XML:
In [358]: xml = """<?xml version="1.0" encoding="UTF-8"?>
.....: <bookstore>
.....: <book category="cooking">
.....: <title lang="en">Everyday Italian</title>
.....: <author>Giada De Laurentiis</author>
.....: <year>2005</year>
.....: <price>30.00</price>
.....: </book>
.....: <book category="children">
.....: <title lang="en">Harry Potter</title>
.....: <author>J K. Rowling</author>
.....: <year>2005</year>
.....: <price>29.99</price>
.....: </book>
.....: <book category="web">
.....: <title lang="en">Learning XML</title>
.....: <author>Erik T. Ray</author>
.....: <year>2003</year>
.....: <price>39.95</price>
.....: </book>
.....: </bookstore>"""
.....:
In [359]: df = pd.read_xml(xml)
In [360]: df
Out[360]:
category title author year price
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00
1 children Harry Potter J K. Rowling 2005 29.99
2 web Learning XML Erik T. Ray 2003 39.95
Чтение URL без опций:
In [361]: df = pd.read_xml("https://www.w3schools.com/xml/books.xml")
In [362]: df
Out[362]:
category title author year price cover
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 None
1 children Harry Potter J K. Rowling 2005 29.99 None
2 web XQuery Kick Start Vaidyanathan Nagarajan 2003 49.99 None
3 web Learning XML Erik T. Ray 2003 39.95 paperback
Чтение содержимого файла “books.xml” и передача его в read_xml в качестве строки:
In [363]: file_path = "books.xml"
In [364]: with open(file_path, "w") as f:
.....: f.write(xml)
.....:
In [365]: with open(file_path, "r") as f:
.....: df = pd.read_xml(f.read())
.....:
In [366]: df
Out[366]:
category title author year price
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00
1 children Harry Potter J K. Rowling 2005 29.99
2 web Learning XML Erik T. Ray 2003 39.95
Чтение содержимого “books.xml” как экземпляра StringIO или BytesIO и передача его в read_xml:
In [367]: with open(file_path, "r") as f:
.....: sio = StringIO(f.read())
.....:
In [368]: df = pd.read_xml(sio)
In [369]: df
Out[369]:
category title author year price
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00
1 children Harry Potter J K. Rowling 2005 29.99
2 web Learning XML Erik T. Ray 2003 39.95
In [370]: with open(file_path, "rb") as f:
.....: bio = BytesIO(f.read())
.....:
In [371]: df = pd.read_xml(bio)
In [372]: df
Out[372]:
category title author year price
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00
1 children Harry Potter J K. Rowling 2005 29.99
2 web Learning XML Erik T. Ray 2003 39.95
Даже чтение XML из ведер AWS S3, таких как наборы данных статей NIH NCBI PMC, предоставляющие биомедицинские и научные журналы о жизни:
In [373]: df = pd.read_xml(
.....: "s3://pmc-oa-opendata/oa_comm/xml/all/PMC1236943.xml",
.....: xpath=".//journal-meta",
.....: )
.....:
In [374]: df
Out[374]:
journal-id journal-title issn publisher
0 Cardiovasc Ultrasound Cardiovascular Ultrasound 1476-7120 NaN
С lxml по умолчанию parser, вы получаете доступ к полнофункциональной библиотеке XML, которая расширяет API ElementTree Python. Одним из мощных инструментов является возможность выборочного или условного запроса узлов с помощью более выразительного XPath:
In [375]: df = pd.read_xml(file_path, xpath="//book[year=2005]")
In [376]: df
Out[376]:
category title author year price
0 cooking Everyday Italian Giada De Laurentiis 2005 30.00
1 children Harry Potter J K. Rowling 2005 29.99
Укажите только элементы или только атрибуты для парсинга:
In [377]: df = pd.read_xml(file_path, elems_only=True)
In [378]: df
Out[378]:
title author year price
0 Everyday Italian Giada De Laurentiis 2005 30.00
1 Harry Potter J K. Rowling 2005 29.99
2 Learning XML Erik T. Ray 2003 39.95
In [379]: df = pd.read_xml(file_path, attrs_only=True)
In [380]: df
Out[380]:
category
0 cooking
1 children
2 web
Документы XML могут иметь пространства имен с префиксами и пространствами имен по умолчанию без префиксов, оба из которых обозначаются специальным атрибутом xmlns. Чтобы выполнить парсинг по узлу в контексте пространства имен, xpath должен ссылаться на префикс.
Например, XML ниже содержит пространство имен с префиксом, doc, и URI в https://example.com. Чтобы выполнить парсинг узлов doc:row, необходимо использовать namespaces.
In [381]: xml = """<?xml version='1.0' encoding='utf-8'?>
.....: <doc:data xmlns:doc="https://example.com">
.....: <doc:row>
.....: <doc:shape>square</doc:shape>
.....: <doc:degrees>360</doc:degrees>
.....: <doc:sides>4.0</doc:sides>
.....: </doc:row>
.....: <doc:row>
.....: <doc:shape>circle</doc:shape>
.....: <doc:degrees>360</doc:degrees>
.....: <doc:sides/>
.....: </doc:row>
.....: <doc:row>
.....: <doc:shape>triangle</doc:shape>
.....: <doc:degrees>180</doc:degrees>
.....: <doc:sides>3.0</doc:sides>
.....: </doc:row>
.....: </doc:data>"""
.....:
In [382]: df = pd.read_xml(xml,
.....: xpath="//doc:row",
.....: namespaces={"doc": "https://example.com"})
.....:
In [383]: df
Out[383]:
shape degrees sides
0 square 360 4.0
1 circle 360 NaN
2 triangle 180 3.0
Аналогично, документ XML может иметь пространство имен по умолчанию без префикса. Отсутствие присвоения временного префикса вернет нулевые узлы и приведет к ошибке ValueError. Но присвоение *любого* временного имени для правильного URI позволяет выполнить парсинг по узлам.
In [384]: xml = """<?xml version='1.0' encoding='utf-8'?>
.....: <data xmlns="https://example.com">
.....: <row>
.....: <shape>square</shape>
.....: <degrees>360</degrees>
.....: <sides>4.0</sides>
.....: </row>
.....: <row>
.....: <shape>circle</shape>
.....: <degrees>360</degrees>
.....: <sides/>
.....: </row>
.....: <row>
.....: <shape>triangle</shape>
.....: <degrees>180</degrees>
.....: <sides>3.0</sides>
.....: </row>
.....: </data>"""
.....:
In [385]: df = pd.read_xml(xml,
.....: xpath="//pandas:row",
.....: namespaces={"pandas": "https://example.com"})
.....:
In [386]: df
Out[386]:
shape degrees sides
0 square 360 4.0
1 circle 360 NaN
2 triangle 180 3.0
Однако, если XPath не ссылается на имена узлов, такие как по умолчанию, /*, то namespaces не требуется.
С lxml в качестве парсера вы можете сглаживать вложенные документы XML с помощью скрипта XSLT, который также может быть типами строки/файла/URL. В качестве справки, XSLT — это язык специального назначения, написанный в специальном файле XML, который может преобразовывать исходные документы XML в другие XML, HTML, даже текст (CSV, JSON и т. д.) с помощью процессора XSLT.
Например, рассмотрим эту несколько вложенную структуру поездов «L» Чикаго, где элементы станции и поезд содержат данные в собственных разделах. С помощью XSLT ниже, lxml может преобразовать исходный вложенный документ в более плоский вывод (как показано ниже для демонстрации) для более простого парсинга в DataFrame:
In [387]: xml = """<?xml version='1.0' encoding='utf-8'?>
.....: <response>
.....: <row>
.....: <station id="40850" name="Library"/>
.....: <month>2020-09-01T00:00:00</month>
.....: <rides>
.....: <avg_weekday_rides>864.2</avg_weekday_rides>
.....: <avg_saturday_rides>534</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides>
.....: </rides>
.....: </row>
.....: <row>
.....: <station id="41700" name="Washington/Wabash"/>
.....: <month>2020-09-01T00:00:00</month>
.....: <rides>
.....: <avg_weekday_rides>2707.4</avg_weekday_rides>
.....: <avg_saturday_rides>1909.8</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides>
.....: </rides>
.....: </row>
.....: <row>
.....: <station id="40380" name="Clark/Lake"/>
.....: <month>2020-09-01T00:00:00</month>
.....: <rides>
.....: <avg_weekday_rides>2949.6</avg_weekday_rides>
.....: <avg_saturday_rides>1657</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides>
.....: </rides>
.....: </row>
.....: </response>"""
.....:
In [388]: xsl = """<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.....: <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
.....: <xsl:strip-space elements="*"/>
.....: <xsl:template match="/response">
.....: <xsl:copy>
.....: <xsl:apply-templates select="row"/>
.....: </xsl:copy>
.....: </xsl:template>
.....: <xsl:template match="row">
.....: <xsl:copy>
.....: <station_id><xsl:value-of select="station/@id"/></station_id>
.....: <station_name><xsl:value-of select="station/@name"/></station_name>
.....: <xsl:copy-of select="month|rides/*"/>
.....: </xsl:copy>
.....: </xsl:template>
.....: </xsl:stylesheet>"""
.....:
In [389]: output = """<?xml version='1.0' encoding='utf-8'?>
.....: <response>
.....: <row>
.....: <station_id>40850</station_id>
.....: <station_name>Library</station_name>
.....: <month>2020-09-01T00:00:00</month>
.....: <avg_weekday_rides>864.2</avg_weekday_rides>
.....: <avg_saturday_rides>534</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>417.2</avg_sunday_holiday_rides>
.....: </row>
.....: <row>
.....: <station_id>41700</station_id>
.....: <station_name>Washington/Wabash</station_name>
.....: <month>2020-09-01T00:00:00</month>
.....: <avg_weekday_rides>2707.4</avg_weekday_rides>
.....: <avg_saturday_rides>1909.8</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>1438.6</avg_sunday_holiday_rides>
.....: </row>
.....: <row>
.....: <station_id>40380</station_id>
.....: <station_name>Clark/Lake</station_name>
.....: <month>2020-09-01T00:00:00</month>
.....: <avg_weekday_rides>2949.6</avg_weekday_rides>
.....: <avg_saturday_rides>1657</avg_saturday_rides>
.....: <avg_sunday_holiday_rides>1453.8</avg_sunday_holiday_rides>
.....: </row>
.....: </response>"""
.....:
In [390]: df = pd.read_xml(xml, stylesheet=xsl)
In [391]: df
Out[391]:
station_id station_name ... avg_saturday_rides avg_sunday_holiday_rides
0 40850 Library ... 534.0 417.2
1 41700 Washington/Wabash ... 1909.8 1438.6
2 40380 Clark/Lake ... 1657.0 1453.8
[3 rows x 6 columns]
Для очень больших файлов XML, которые могут иметь размер в сотни мегабайт до гигабайт, pandas.read_xml() поддерживает парсинг таких больших файлов с использованием lxml’s iterparse и etree’s iterparse, которые являются методами с низким потреблением памяти для итерации по дереву XML и извлечения определенных элементов и атрибутов без хранения всего дерева в памяти.
Новое в версии 1.5.0.
Чтобы использовать эту функцию, вы должны передать физический путь к файлу XML в read_xml и использовать аргумент iterparse. Файлы не должны быть сжаты или указывать на онлайн-источники, а храниться на локальном диске. Также, iterparse должен быть словарем, где ключ — это повторяющиеся узлы в документе (которые становятся строками), а значение — список любого элемента или атрибута, являющегося потомком (т. е. ребенком, внуком) повторяющегося узла. Поскольку XPath в этом методе не используется, потомки не обязательно должны иметь одинаковые отношения друг с другом. Ниже приведен пример чтения очень большого (более 12 ГБ) последнего выгруженного набора данных статей Википедии.
In [1]: df = pd.read_xml(
... "/path/to/downloaded/enwikisource-latest-pages-articles.xml",
... iterparse = {"page": ["title", "ns", "id"]}
... )
... df
Out[2]:
title ns id
0 Gettysburg Address 0 21450
1 Main Page 0 42950
2 Declaration by United Nations 0 8435
3 Constitution of the United States of America 0 8435
4 Declaration of Independence (Israel) 0 17858
... ... ... ...
3578760 Page:Black cat 1897 07 v2 n10.pdf/17 104 219649
3578761 Page:Black cat 1897 07 v2 n10.pdf/43 104 219649
3578762 Page:Black cat 1897 07 v2 n10.pdf/44 104 219649
3578763 The History of Tom Jones, a Foundling/Book IX 0 12084291
3578764 Page:Shakespeare of Stratford (1926) Yale.djvu/91 104 21450
[3578765 rows x 3 columns]
Запись XML
Новое в версии 1.3.0.
Объекты DataFrame имеют метод экземпляра to_xml, который отображает содержимое DataFrame как документ XML.
Примечание
Этот метод не поддерживает специальные свойства XML, включая DTD, CData, схемы XSD, инструкции обработки, комментарии и другие. Поддерживаются только пространства имен на уровне корня. Однако, stylesheet позволяет вносить изменения в дизайн после начального вывода.
Давайте рассмотрим несколько примеров.
Запись XML без опций:
In [392]: geom_df = pd.DataFrame(
.....: {
.....: "shape": ["square", "circle", "triangle"],
.....: "degrees": [360, 360, 180],
.....: "sides": [4, np.nan, 3],
.....: }
.....: )
.....:
In [393]: print(geom_df.to_xml())
<?xml version='1.0' encoding='utf-8'?>
<data>
<row>
<index>0</index>
<shape>square</shape>
<degrees>360</degrees>
<sides>4.0</sides>
</row>
<row>
<index>1</index>
<shape>circle</shape>
<degrees>360</degrees>
<sides/>
</row>
<row>
<index>2</index>
<shape>triangle</shape>
<degrees>180</degrees>
<sides>3.0</sides>
</row>
</data>
Запись XML с новым корнем и именем строки:
In [394]: print(geom_df.to_xml(root_name="geometry", row_name="objects"))
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<objects>
<index>0</index>
<shape>square</shape>
<degrees>360</degrees>
<sides>4.0</sides>
</objects>
<objects>
<index>1</index>
<shape>circle</shape>
<degrees>360</degrees>
<sides/>
</objects>
<objects>
<index>2</index>
<shape>triangle</shape>
<degrees>180</degrees>
<sides>3.0</sides>
</objects>
</geometry>
Запись XML, ориентированная на атрибуты:
In [395]: print(geom_df.to_xml(attr_cols=geom_df.columns.tolist()))
<?xml version='1.0' encoding='utf-8'?>
<data>
<row index="0" shape="square" degrees="360" sides="4.0"/>
<row index="1" shape="circle" degrees="360"/>
<row index="2" shape="triangle" degrees="180" sides="3.0"/>
</data>
Запись смеси элементов и атрибутов:
In [396]: print(
.....: geom_df.to_xml(
.....: index=False,
.....: attr_cols=['shape'],
.....: elem_cols=['degrees', 'sides'])
.....: )
.....:
<?xml version='1.0' encoding='utf-8'?>
<data>
<row shape="square">
<degrees>360</degrees>
<sides>4.0</sides>
</row>
<row shape="circle">
<degrees>360</degrees>
<sides/>
</row>
<row shape="triangle">
<degrees>180</degrees>
<sides>3.0</sides>
</row>
</data>
Любой DataFrames с иерархическими столбцами будет сглажен для имен элементов XML с уровнями, разделенными подчеркиванием:
In [397]: ext_geom_df = pd.DataFrame(
.....: {
.....: "type": ["polygon", "other", "polygon"],
.....: "shape": ["square", "circle", "triangle"],
.....: "degrees": [360, 360, 180],
.....: "sides": [4, np.nan, 3],
.....: }
.....: )
.....:
In [398]: pvt_df = ext_geom_df.pivot_table(index='shape',
.....: columns='type',
.....: values=['degrees', 'sides'],
.....: aggfunc='sum')
.....:
In [399]: pvt_df
Out[399]:
degrees sides
type other polygon other polygon
shape
circle 360.0 NaN 0.0 NaN
square NaN 360.0 NaN 4.0
triangle NaN 180.0 NaN 3.0
In [400]: print(pvt_df.to_xml())
<?xml version='1.0' encoding='utf-8'?>
<data>
<row>
<shape>circle</shape>
<degrees_other>360.0</degrees_other>
<degrees_polygon/>
<sides_other>0.0</sides_other>
<sides_polygon/>
</row>
<row>
<shape>square</shape>
<degrees_other/>
<degrees_polygon>360.0</degrees_polygon>
<sides_other/>
<sides_polygon>4.0</sides_polygon>
</row>
<row>
<shape>triangle</shape>
<degrees_other/>
<degrees_polygon>180.0</degrees_polygon>
<sides_other/>
<sides_polygon>3.0</sides_polygon>
</row>
</data>
Запись XML с пространством имен по умолчанию:
In [401]: print(geom_df.to_xml(namespaces={"": "https://example.com"}))
<?xml version='1.0' encoding='utf-8'?>
<data xmlns="https://example.com">
<row>
<index>0</index>
<shape>square</shape>
<degrees>360</degrees>
<sides>4.0</sides>
</row>
<row>
<index>1</index>
<shape>circle</shape>
<degrees>360</degrees>
<sides/>
</row>
<row>
<index>2</index>
<shape>triangle</shape>
<degrees>180</degrees>
<sides>3.0</sides>
</row>
</data>
Запись XML с префиксом пространства имен:
In [402]: print(
.....: geom_df.to_xml(namespaces={"doc": "https://example.com"},
.....: prefix="doc")
.....: )
.....:
<?xml version='1.0' encoding='utf-8'?>
<doc:data xmlns:doc="https://example.com">
<doc:row>
<doc:index>0</doc:index>
<doc:shape>square</doc:shape>
<doc:degrees>360</doc:degrees>
<doc:sides>4.0</doc:sides>
</doc:row>
<doc:row>
<doc:index>1</doc:index>
<doc:shape>circle</doc:shape>
<doc:degrees>360</doc:degrees>
<doc:sides/>
</doc:row>
<doc:row>
<doc:index>2</doc:index>
<doc:shape>triangle</doc:shape>
<doc:degrees>180</doc:degrees>
<doc:sides>3.0</doc:sides>
</doc:row>
</doc:data>
Запись XML без объявления или красивой печати:
In [403]: print(
.....: geom_df.to_xml(xml_declaration=False,
.....: pretty_print=False)
.....: )
.....:
<data><row><index>0</index><shape>square</shape><degrees>360</degrees><sides>4.0</sides></row><row><index>1</index><shape>circle</shape><degrees>360</degrees><sides/></row><row><index>2</index><shape>triangle</shape><degrees>180</degrees><sides>3.0</sides></row></data>
Запись XML и преобразование со стилем:
In [404]: xsl = """<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.....: <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
.....: <xsl:strip-space elements="*"/>
.....: <xsl:template match="/data">
.....: <geometry>
.....: <xsl:apply-templates select="row"/>
.....: </geometry>
.....: </xsl:template>
.....: <xsl:template match="row">
.....: <object index="{index}">
.....: <xsl:if test="shape!='circle'">
.....: <xsl:attribute name="type">polygon</xsl:attribute>
.....: </xsl:if>
.....: <xsl:copy-of select="shape"/>
.....: <property>
.....: <xsl:copy-of select="degrees|sides"/>
.....: </property>
.....: </object>
.....: </xsl:template>
.....: </xsl:stylesheet>"""
.....:
In [405]: print(geom_df.to_xml(stylesheet=xsl))
<?xml version="1.0"?>
<geometry>
<object index="0" type="polygon">
<shape>square</shape>
<property>
<degrees>360</degrees>
<sides>4.0</sides>
</property>
</object>
<object index="1">
<shape>circle</shape>
<property>
<degrees>360</degrees>
<sides/>
</property>
</object>
<object index="2" type="polygon">
<shape>triangle</shape>
<property>
<degrees>180</degrees>
<sides>3.0</sides>
</property>
</object>
</geometry>
Заключительные заметки по XML
Все документы XML соответствуют спецификациям W3C. Парсеры как
etree, так иlxmlне смогут обработать какой-либо документ разметки, который не является правильно сформированным или не соответствует правилам синтаксиса XML. Имейте в виду, что HTML — это не документ XML, если он не соответствует спецификациям XHTML. Тем не менее, другие популярные типы разметки, включая KML, XAML, RSS, MusicML, MathML, соответствуют схемам XML.По этой причине, если ваше приложение создает XML до операций pandas, используйте соответствующие библиотеки DOM, такие как
etreeиlxml, для построения необходимого документа, а не путем конкатенации строк или корректировки регулярных выражений. Всегда помните, что XML — это *специальный* текстовый файл с правилами разметки.Для очень больших файлов XML (несколько сотен МБ до ГБ) XPath и XSLT могут стать ресурсоемкими операциями. Убедитесь, что у вас достаточно оперативной памяти для чтения и записи больших файлов XML (примерно в 5 раз больше, чем размер текста).
Поскольку XSLT является языком программирования, используйте его с осторожностью, так как такие скрипты могут представлять угрозу безопасности в вашей среде и могут запускать большие или бесконечные рекурсивные операции. Всегда тестируйте скрипты на небольших фрагментах перед полным запуском.
Парсер etree поддерживает все функции как
read_xml, так иto_xml, за исключением сложных XPath и любых XSLT. Несмотря на ограниченные возможности,etreeпо-прежнему является надежным и способным парсером и создателем дерева. Его производительность может немного уступатьlxmlдля больших файлов, но это практически незаметно для файлов небольшого и среднего размера.
Файлы Excel
Метод read_excel() может считывать файлы Excel 2007+ (.xlsx) с помощью модуля Python openpyxl. Файлы Excel 2003 (.xls) можно считывать с помощью xlrd. Бинарные файлы Excel (.xlsb) можно считывать с помощью pyxlsb. Метод экземпляра to_excel() используется для сохранения DataFrame в Excel. В целом, семантика аналогична работе с данными csv. См. раздел пособие для ознакомления с некоторыми продвинутыми стратегиями.
Предупреждение
Пакет xlwt для записи файлов Excel старого образца .xls больше не поддерживается. Пакет xlrd теперь используется только для чтения файлов Excel старого образца .xls.
До pandas 1.3.0, аргумент по умолчанию engine=None метода read_excel() в большинстве случаев приводил к использованию движка xlrd, включая новые файлы Excel 2007+ (.xlsx). Сейчас pandas по умолчанию использует движок openpyxl.
Настоятельно рекомендуется установить openpyxl для чтения файлов Excel 2007+ (.xlsx). Пожалуйста, не сообщайте об ошибках, возникающих при использовании ``xlrd`` для чтения файлов ``.xlsx``. Это больше не поддерживается, используйте вместо этого openpyxl.
Попытка использовать движок xlwt приведет к ошибке FutureWarning, если опция io.excel.xls.writer не установлена в "xlwt". Хотя эта опция теперь устарела и также вызовет ошибку FutureWarning, она может быть глобально установлена, и предупреждение подавлено. Пользователям рекомендуется писать файлы .xlsx с помощью движка openpyxl.
Чтение файлов Excel
В самом простом случае read_excel принимает путь к файлу Excel и sheet_name , указывающий, какой лист нужно проанализировать.
# Returns a DataFrame
pd.read_excel("path_to_file.xls", sheet_name="Sheet1")
ExcelFile класс
Для работы с несколькими листами из одного файла можно использовать класс ExcelFile, который можно передать в read_excel. Будет заметен прирост производительности при чтении нескольких листов, так как файл считывается в память только один раз.
xlsx = pd.ExcelFile("path_to_file.xls")
df = pd.read_excel(xlsx, "Sheet1")
Класс ExcelFile также можно использовать в качестве контекстного менеджера.
with pd.ExcelFile("path_to_file.xls") as xls:
df1 = pd.read_excel(xls, "Sheet1")
df2 = pd.read_excel(xls, "Sheet2")
Свойство sheet_names сгенерирует список имён листов в файле.
Основной сценарий использования класса ExcelFile — это разбор нескольких листов с различными параметрами:
data = {}
# For when Sheet1's format differs from Sheet2
with pd.ExcelFile("path_to_file.xls") as xls:
data["Sheet1"] = pd.read_excel(xls, "Sheet1", index_col=None, na_values=["NA"])
data["Sheet2"] = pd.read_excel(xls, "Sheet2", index_col=1)
Обратите внимание, что если для всех листов используются одинаковые параметры разбора, список имён листов можно просто передать в read_excel без потери производительности.
# using the ExcelFile class
data = {}
with pd.ExcelFile("path_to_file.xls") as xls:
data["Sheet1"] = pd.read_excel(xls, "Sheet1", index_col=None, na_values=["NA"])
data["Sheet2"] = pd.read_excel(xls, "Sheet2", index_col=None, na_values=["NA"])
# equivalent using the read_excel function
data = pd.read_excel(
"path_to_file.xls", ["Sheet1", "Sheet2"], index_col=None, na_values=["NA"]
)
ExcelFile также можно вызвать с объектом xlrd.book.Book в качестве параметра. Это позволяет пользователю контролировать способ чтения файла Excel. Например, листы можно загружать по требованию, вызывая xlrd.open_workbook() с on_demand=True.
import xlrd
xlrd_book = xlrd.open_workbook("path_to_file.xls", on_demand=True)
with pd.ExcelFile(xlrd_book) as xls:
df1 = pd.read_excel(xls, "Sheet1")
df2 = pd.read_excel(xls, "Sheet2")
Указание листов
Примечание
Второй аргумент — sheet_name, а не ExcelFile.sheet_names.
Примечание
Атрибут ExcelFile sheet_names предоставляет доступ к списку листов.
Аргументы
sheet_nameпозволяют указать лист или листы для чтения.Значение по умолчанию для
sheet_nameравно 0, что указывает на чтение первого листа.Передайте строку, чтобы сослаться на имя конкретного листа в книге.
Передайте целое число, чтобы сослаться на индекс листа. Индексы следуют соглашениям Python, начиная с 0.
Передайте список строк или целых чисел, чтобы вернуть словарь указанных листов.
Передайте
None, чтобы вернуть словарь всех доступных листов.
# Returns a DataFrame
pd.read_excel("path_to_file.xls", "Sheet1", index_col=None, na_values=["NA"])
Использование индекса листа:
# Returns a DataFrame
pd.read_excel("path_to_file.xls", 0, index_col=None, na_values=["NA"])
Использование всех значений по умолчанию:
# Returns a DataFrame
pd.read_excel("path_to_file.xls")
Использование None для получения всех листов:
# Returns a dictionary of DataFrames
pd.read_excel("path_to_file.xls", sheet_name=None)
Использование списка для получения нескольких листов:
# Returns the 1st and 4th sheet, as a dictionary of DataFrames.
pd.read_excel("path_to_file.xls", sheet_name=["Sheet1", 3])
read_excel может читать более одного листа, задавая sheet_name список имён листов, список позиций листов или None для чтения всех листов. Листы можно указывать по индексу листа или имени листа, используя целое число или строку соответственно.
Чтение многоуровневого индекса
read_excel может читать многоуровневый индекс, передавая список столбцов в index_col и список строк в header. Если у уровней многоуровневого индекса есть сериализованные имена уровней, они также будут считаны, указав строки/столбцы, составляющие уровни.
Например, для чтения многоуровневого индекса без имён:
In [406]: df = pd.DataFrame(
.....: {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]},
.....: index=pd.MultiIndex.from_product([["a", "b"], ["c", "d"]]),
.....: )
.....:
In [407]: df.to_excel("path_to_file.xlsx")
In [408]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])
In [409]: df
Out[409]:
a b
a c 1 5
d 2 6
b c 3 7
d 4 8
Если у индекса есть имена уровней, они также будут проанализированы, используя те же параметры.
In [410]: df.index = df.index.set_names(["lvl1", "lvl2"])
In [411]: df.to_excel("path_to_file.xlsx")
In [412]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1])
In [413]: df
Out[413]:
a b
lvl1 lvl2
a c 1 5
d 2 6
b c 3 7
d 4 8
Если исходный файл содержит многоуровневый индекс и столбцы, списки, определяющие каждый, должны быть переданы в index_col и header:
In [414]: df.columns = pd.MultiIndex.from_product([["a"], ["b", "d"]], names=["c1", "c2"])
In [415]: df.to_excel("path_to_file.xlsx")
In [416]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1], header=[0, 1])
In [417]: df
Out[417]:
c1 a
c2 b d
lvl1 lvl2
a c 1 5
d 2 6
b c 3 7
d 4 8
Пропущенные значения в столбцах, указанных в index_col, будут заполнены вперёд, чтобы разрешить обратную загрузку с to_excel для merged_cells=True. Чтобы избежать заполнения пропущенных значений вперёд, используйте set_index после чтения данных вместо index_col.
Разбор определённых столбцов
Часто пользователи вставляют столбцы для временных вычислений в Excel, и вы можете не захотеть читать эти столбцы. read_excel принимает ключевое слово usecols , чтобы разрешить указание подмножества столбцов для разбора.
Изменено в версии 1.0.0.
Передача целого числа в usecols больше не будет работать. Пожалуйста, передайте вместо этого список целых чисел от 0 до usecols включительно.
Вы можете указать набор столбцов и диапазонов Excel через запятую в виде строки:
pd.read_excel("path_to_file.xls", "Sheet1", usecols="A,C:E")
Если usecols является списком целых чисел, предполагается, что это индексы столбцов файла для разбора.
pd.read_excel("path_to_file.xls", "Sheet1", usecols=[0, 2, 3])
Порядок элементов игнорируется, поэтому usecols=[0, 1] то же самое, что и [1, 0].
Если usecols является списком строк, предполагается, что каждая строка соответствует имени столбца, предоставленному пользователем в names или полученному из заголовков документа. Эти строки определяют, какие столбцы будут проанализированы.
pd.read_excel("path_to_file.xls", "Sheet1", usecols=["foo", "bar"])
Порядок элементов игнорируется, поэтому usecols=['baz', 'joe'] то же самое, что и ['joe', 'baz'].
Если usecols является вызываемым объектом, вызываемая функция будет оценена для имён столбцов, возвращая имена, где вызываемая функция оценивается как True.
pd.read_excel("path_to_file.xls", "Sheet1", usecols=lambda x: x.isalpha())
Разбор дат
Значения типа даты обычно автоматически преобразуются в соответствующий тип при чтении файла Excel. Но если у вас есть столбец строк, которые выглядят как даты (но на самом деле не отформатированы как даты в Excel), вы можете использовать ключевое слово parse_dates для разбора этих строк в значения типа datetime:
pd.read_excel("path_to_file.xls", "Sheet1", parse_dates=["date_strings"])
Преобразователи ячеек
Возможна трансформация содержимого ячеек Excel с помощью опции converters. Например, для преобразования столбца в булевы значения:
pd.read_excel("path_to_file.xls", "Sheet1", converters={"MyBools": bool})
Эта опция обрабатывает пропущенные значения и рассматривает исключения в преобразователях как пропущенные данные. Преобразования применяются к каждой ячейке по отдельности, а не к столбцу в целом, поэтому тип массива не гарантируется. Например, столбец целых чисел с пропущенными значениями не может быть преобразован в массив с целочисленным типом, поскольку NaN строго является плавающей точкой. Вы можете вручную маскировать пропущенные данные, чтобы восстановить целочисленный тип.
def cfun(x):
return int(x) if x else -1
pd.read_excel("path_to_file.xls", "Sheet1", converters={"MyInts": cfun})
Указание типов данных
В качестве альтернативы преобразователям, тип для целого столбца можно указать с помощью ключевого слова dtype , которое принимает словарь, сопоставляющий имена столбцов с типами. Для интерпретации данных без вывода типа используйте тип str или object.
pd.read_excel("path_to_file.xls", dtype={"MyInts": "int64", "MyText": str})
Запись файлов Excel
Запись файлов Excel на диск
Чтобы записать объект DataFrame в лист файла Excel, можно использовать метод экземпляра to_excel . Аргументы в значительной степени такие же, как и в to_csv , описанных выше, первым аргументом является имя файла Excel, а необязательным вторым аргументом — имя листа, в который следует записать DataFrame. Например:
df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")
Файлы с расширением .xls будут записаны с помощью xlwt, а файлы с расширением .xlsx будут записаны с помощью xlsxwriter (если доступно) или openpyxl.
DataFrame будет записан таким образом, чтобы попытаться имитировать вывод REPL. index_label будет помещён во вторую строку вместо первой. Вы можете поместить его в первую строку, установив опцию merge_cells в to_excel() на False:
df.to_excel("path_to_file.xlsx", index_label="label", merge_cells=False)
Чтобы записать отдельные DataFrames в отдельные листы одного файла Excel, можно передать ExcelWriter.
with pd.ExcelWriter("path_to_file.xlsx") as writer:
df1.to_excel(writer, sheet_name="Sheet1")
df2.to_excel(writer, sheet_name="Sheet2")
Запись файлов Excel в память
pandas поддерживает запись файлов Excel в объекты, похожие на буферы, такие как StringIO или BytesIO с помощью ExcelWriter.
from io import BytesIO
bio = BytesIO()
# By setting the 'engine' in the ExcelWriter constructor.
writer = pd.ExcelWriter(bio, engine="xlsxwriter")
df.to_excel(writer, sheet_name="Sheet1")
# Save the workbook
writer.save()
# Seek to the beginning and read to copy the workbook to a variable in memory
bio.seek(0)
workbook = bio.read()
Примечание
engine необязательно, но рекомендуется. Установка движка определяет версию создаваемой книги. Установка engine='xlrd' создаст книгу в формате Excel 2003 (xls). Использование 'openpyxl' или 'xlsxwriter' создаст книгу в формате Excel 2007 (xlsx). Если опущено, создается книга в формате Excel 2007.
Двигатели записи в Excel
Устарело начиная с версии 1.2.0: Так как пакет xlwt больше не поддерживается, движок xlwt будет удален из будущих версий pandas. Это единственный движок в pandas, который поддерживает запись в файлы .xls.
pandas выбирает движок записи в Excel двумя способами:
ключевое слово
engineрасширение имени файла (через значение по умолчанию, указанное в параметрах конфигурации)
По умолчанию pandas использует XlsxWriter для .xlsx, openpyxl для .xlsm, и xlwt для .xls файлов. Если у вас установлено несколько движков, вы можете установить движок по умолчанию, изменив параметры конфигурации io.excel.xlsx.writer и io.excel.xls.writer. pandas будет использовать openpyxl для .xlsx файлов, если Xlsxwriter недоступен.
Чтобы указать, какой движок вы хотите использовать, вы можете передать ключевое слово engine в to_excel и ExcelWriter. Доступные встроенные движки:
openpyxl: требуется версия 2.4 или вышеxlsxwriterxlwt
# By setting the 'engine' in the DataFrame 'to_excel()' methods.
df.to_excel("path_to_file.xlsx", sheet_name="Sheet1", engine="xlsxwriter")
# By setting the 'engine' in the ExcelWriter constructor.
writer = pd.ExcelWriter("path_to_file.xlsx", engine="xlsxwriter")
# Or via pandas configuration.
from pandas import options # noqa: E402
options.io.excel.xlsx.writer = "xlsxwriter"
df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")
Стиль и форматирование
Внешний вид электронных таблиц Excel, созданных из pandas, можно изменить, используя следующие параметры в методе DataFrame’s to_excel.
float_format: Строка формата для чисел с плавающей точкой (по умолчаниюNone).freeze_panes: Кортеж из двух целых чисел, представляющий последнюю строку и последний столбец для заморозки. Каждый из этих параметров индексируется с 1, поэтому (1, 1) заморозит первую строку и первый столбец (по умолчаниюNone).
Использование движка Xlsxwriter предоставляет множество опций для управления форматом электронной таблицы Excel, созданной методом to_excel. Отличные примеры можно найти в документации Xlsxwriter здесь: https://xlsxwriter.readthedocs.io/working_with_pandas.html
OpenDocument таблицы
Новое в версии 0.25.
Метод read_excel() также может читать таблицы OpenDocument, используя модуль odfpy. Семантика и возможности для чтения таблиц OpenDocument соответствуют тому, что можно сделать для файлов Excel с использованием engine='odf'.
# Returns a DataFrame
pd.read_excel("path_to_file.ods", engine="odf")
Примечание
В настоящее время pandas поддерживает только чтение таблиц OpenDocument. Запись не реализована.
Файлы двоичного формата Excel (.xlsb)
Новое в версии 1.0.0.
Метод read_excel() также может читать двоичные файлы Excel, используя модуль pyxlsb. Семантика и возможности для чтения двоичных файлов Excel в основном соответствуют тому, что можно сделать для файлов Excel, используя engine='pyxlsb'. pyxlsb не распознает типы дат в файлах и вместо этого вернет числа с плавающей точкой.
# Returns a DataFrame
pd.read_excel("path_to_file.xlsb", engine="pyxlsb")
Примечание
В настоящее время pandas поддерживает только чтение двоичных файлов Excel. Запись не реализована.
Буфер обмена
Удобный способ получить данные — использовать метод read_clipboard(), который получает содержимое буфера обмена и передает его методу read_csv. Например, вы можете скопировать следующий текст в буфер обмена (CTRL-C на многих операционных системах):
A B C
x 1 4 p
y 2 5 q
z 3 6 r
Затем импортируйте данные непосредственно в DataFrame, вызвав:
>>> clipdf = pd.read_clipboard()
>>> clipdf
A B C
x 1 4 p
y 2 5 q
z 3 6 r
Метод to_clipboard может использоваться для записи содержимого DataFrame в буфер обмена. После чего вы можете вставить содержимое буфера обмена в другие приложения (CTRL-V на многих операционных системах). Здесь мы продемонстрируем запись DataFrame в буфер обмена и чтение его обратно.
>>> df = pd.DataFrame(
... {"A": [1, 2, 3], "B": [4, 5, 6], "C": ["p", "q", "r"]}, index=["x", "y", "z"]
... )
>>> df
A B C
x 1 4 p
y 2 5 q
z 3 6 r
>>> df.to_clipboard()
>>> pd.read_clipboard()
A B C
x 1 4 p
y 2 5 q
z 3 6 r
Мы видим, что получили тот же контент, который ранее записали в буфер обмена.
Примечание
Возможно, вам потребуется установить xclip или xsel (с PyQt5, PyQt4 или qtpy) в Linux, чтобы использовать эти методы.
Pickling
Все объекты pandas оснащены методами to_pickle, которые используют модуль Python cPickle для сохранения структур данных на диске в формате pickle.
In [418]: df
Out[418]:
c1 a
c2 b d
lvl1 lvl2
a c 1 5
d 2 6
b c 3 7
d 4 8
In [419]: df.to_pickle("foo.pkl")
Функция read_pickle в пространстве имен pandas может использоваться для загрузки любого закодированного объекта pandas (или любого другого закодированного объекта) из файла:
In [420]: pd.read_pickle("foo.pkl")
Out[420]:
c1 a
c2 b d
lvl1 lvl2
a c 1 5
d 2 6
b c 3 7
d 4 8
Предупреждение
Загрузка закодированных данных, полученных из ненадежных источников, может быть небезопасной.
Предупреждение
read_pickle() гарантирует обратную совместимость только до версии pandas 0.20.3
Сжатые файлы pickle
read_pickle(), DataFrame.to_pickle() и Series.to_pickle() могут читать и записывать сжатые файлы pickle. Поддерживаются типы сжатия gzip, bz2, xz, zstd для чтения и записи. Формат файла zip поддерживает только чтение и должен содержать только один файл данных для чтения.
Тип сжатия может быть явным параметром или определяться по расширению файла. Если ‘infer’, тогда используйте gzip, bz2, zip, xz, zstd если имя файла заканчивается на '.gz', '.bz2', '.zip', '.xz', или '.zst', соответственно.
Параметр сжатия также может быть dict для передачи опций протоколу сжатия. Он должен иметь ключ 'method', установленный в имя протокола сжатия, который должен быть одним из {'zip', 'gzip', 'bz2', 'xz', 'zstd'}. Все остальные пары ключ-значение передаются в подлежащую библиотеку сжатия.
In [421]: df = pd.DataFrame(
.....: {
.....: "A": np.random.randn(1000),
.....: "B": "foo",
.....: "C": pd.date_range("20130101", periods=1000, freq="s"),
.....: }
.....: )
.....:
In [422]: df
Out[422]:
A B C
0 -0.828876 foo 2013-01-01 00:00:00
1 -0.110383 foo 2013-01-01 00:00:01
2 2.357598 foo 2013-01-01 00:00:02
3 -1.620073 foo 2013-01-01 00:00:03
4 0.440903 foo 2013-01-01 00:00:04
.. ... ... ...
995 -1.177365 foo 2013-01-01 00:16:35
996 1.236988 foo 2013-01-01 00:16:36
997 0.743946 foo 2013-01-01 00:16:37
998 -0.533097 foo 2013-01-01 00:16:38
999 -0.140850 foo 2013-01-01 00:16:39
[1000 rows x 3 columns]
Использование явного типа сжатия:
In [423]: df.to_pickle("data.pkl.compress", compression="gzip")
In [424]: rt = pd.read_pickle("data.pkl.compress", compression="gzip")
In [425]: rt
Out[425]:
A B C
0 -0.828876 foo 2013-01-01 00:00:00
1 -0.110383 foo 2013-01-01 00:00:01
2 2.357598 foo 2013-01-01 00:00:02
3 -1.620073 foo 2013-01-01 00:00:03
4 0.440903 foo 2013-01-01 00:00:04
.. ... ... ...
995 -1.177365 foo 2013-01-01 00:16:35
996 1.236988 foo 2013-01-01 00:16:36
997 0.743946 foo 2013-01-01 00:16:37
998 -0.533097 foo 2013-01-01 00:16:38
999 -0.140850 foo 2013-01-01 00:16:39
[1000 rows x 3 columns]
Определение типа сжатия по расширению:
In [426]: df.to_pickle("data.pkl.xz", compression="infer")
In [427]: rt = pd.read_pickle("data.pkl.xz", compression="infer")
In [428]: rt
Out[428]:
A B C
0 -0.828876 foo 2013-01-01 00:00:00
1 -0.110383 foo 2013-01-01 00:00:01
2 2.357598 foo 2013-01-01 00:00:02
3 -1.620073 foo 2013-01-01 00:00:03
4 0.440903 foo 2013-01-01 00:00:04
.. ... ... ...
995 -1.177365 foo 2013-01-01 00:16:35
996 1.236988 foo 2013-01-01 00:16:36
997 0.743946 foo 2013-01-01 00:16:37
998 -0.533097 foo 2013-01-01 00:16:38
999 -0.140850 foo 2013-01-01 00:16:39
[1000 rows x 3 columns]
Значение по умолчанию — ‘infer’:
In [429]: df.to_pickle("data.pkl.gz")
In [430]: rt = pd.read_pickle("data.pkl.gz")
In [431]: rt
Out[431]:
A B C
0 -0.828876 foo 2013-01-01 00:00:00
1 -0.110383 foo 2013-01-01 00:00:01
2 2.357598 foo 2013-01-01 00:00:02
3 -1.620073 foo 2013-01-01 00:00:03
4 0.440903 foo 2013-01-01 00:00:04
.. ... ... ...
995 -1.177365 foo 2013-01-01 00:16:35
996 1.236988 foo 2013-01-01 00:16:36
997 0.743946 foo 2013-01-01 00:16:37
998 -0.533097 foo 2013-01-01 00:16:38
999 -0.140850 foo 2013-01-01 00:16:39
[1000 rows x 3 columns]
In [432]: df["A"].to_pickle("s1.pkl.bz2")
In [433]: rt = pd.read_pickle("s1.pkl.bz2")
In [434]: rt
Out[434]:
0 -0.828876
1 -0.110383
2 2.357598
3 -1.620073
4 0.440903
...
995 -1.177365
996 1.236988
997 0.743946
998 -0.533097
999 -0.140850
Name: A, Length: 1000, dtype: float64
Передача опций протоколу сжатия для ускорения сжатия:
In [435]: df.to_pickle("data.pkl.gz", compression={"method": "gzip", "compresslevel": 1})
msgpack
Поддержка pandas для msgpack была удалена в версии 1.0.0. Рекомендуется использовать pickle вместо этого.
В качестве альтернативы, вы также можете использовать формат сериализации Arrow IPC для передачи по сети объектов pandas. Для получения документации по pyarrow, см. здесь.
HDF5 (PyTables)
HDFStore это объект, подобный словарю, который читает и записывает pandas в формате HDF5 с высокой производительностью, используя отличную библиотеку PyTables. См. справочник для некоторых продвинутых стратегий
Предупреждение
pandas использует PyTables для чтения и записи файлов HDF5, что позволяет сериализовать данные типа object с помощью pickle. Загрузка данных с pickle, полученных из ненадежных источников, может быть небезопасной.
См.: https://docs.python.org/3/library/pickle.html для получения дополнительной информации.
In [436]: store = pd.HDFStore("store.h5")
In [437]: print(store)
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
Объекты могут быть записаны в файл, как добавление пар ключ-значение в словарь:
In [438]: index = pd.date_range("1/1/2000", periods=8)
In [439]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [440]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"])
# store.put('s', s) is an equivalent method
In [441]: store["s"] = s
In [442]: store["df"] = df
In [443]: store
Out[443]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
В текущей или последующей сессии Python вы можете получить сохранённые объекты:
# store.get('df') is an equivalent method
In [444]: store["df"]
Out[444]:
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
# dotted (attribute) access provides get as well
In [445]: store.df
Out[445]:
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
Удаление объекта, указанного ключом:
# store.remove('df') is an equivalent method
In [446]: del store["df"]
In [447]: store
Out[447]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
Закрытие хранилища и использование менеджера контекста:
In [448]: store.close()
In [449]: store
Out[449]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
In [450]: store.is_open
Out[450]: False
# Working with, and automatically closing the store using a context manager
In [451]: with pd.HDFStore("store.h5") as store:
.....: store.keys()
.....:
API чтения/записи
HDFStore поддерживает API верхнего уровня, используя read_hdf для чтения и to_hdf для записи, аналогично тому, как работают read_csv и to_csv.
In [452]: df_tl = pd.DataFrame({"A": list(range(5)), "B": list(range(5))})
In [453]: df_tl.to_hdf("store_tl.h5", "table", append=True)
In [454]: pd.read_hdf("store_tl.h5", "table", where=["index>2"])
Out[454]:
A B
3 3 3
4 4 4
HDFStore по умолчанию не будет удалять строки, которые все отсутствуют. Это поведение можно изменить, установив dropna=True.
In [455]: df_with_missing = pd.DataFrame(
.....: {
.....: "col1": [0, np.nan, 2],
.....: "col2": [1, np.nan, np.nan],
.....: }
.....: )
.....:
In [456]: df_with_missing
Out[456]:
col1 col2
0 0.0 1.0
1 NaN NaN
2 2.0 NaN
In [457]: df_with_missing.to_hdf("file.h5", "df_with_missing", format="table", mode="w")
In [458]: pd.read_hdf("file.h5", "df_with_missing")
Out[458]:
col1 col2
0 0.0 1.0
1 NaN NaN
2 2.0 NaN
In [459]: df_with_missing.to_hdf(
.....: "file.h5", "df_with_missing", format="table", mode="w", dropna=True
.....: )
.....:
In [460]: pd.read_hdf("file.h5", "df_with_missing")
Out[460]:
col1 col2
0 0.0 1.0
2 2.0 NaN
Фиксированный формат
Приведённые выше примеры демонстрируют хранение с использованием put, которые записывают HDF5 в PyTables в формате фиксированного массива, называемом форматом fixed. Эти типы хранилищ не являются расширяемыми после записи (хотя вы можете их просто удалить и перезаписать). Они также не являются запросимыми; их необходимо извлекать целиком. Они также не поддерживают DataFrames с не уникальными именами столбцов. Формат fixed хранилища обеспечивает очень быструю запись и немного более быстрое чтение, чем хранилища table. Этот формат задаётся по умолчанию при использовании put или to_hdf или format='fixed' или format='f'.
Предупреждение
Формат fixed вызовет TypeError, если вы попытаетесь получить доступ с помощью where.
>>> pd.DataFrame(np.random.randn(10, 2)).to_hdf("test_fixed.h5", "df")
>>> pd.read_hdf("test_fixed.h5", "df", where="index>5")
TypeError: cannot pass a where specification when reading a fixed format.
this store must be selected in its entirety
Формат таблицы
HDFStore поддерживает другой формат на диске, формат PyTables, формат table Концептуально table имеет форму, очень похожую на DataFrame, со строками и столбцами. К table можно добавлять данные в той же или другой сессии. Кроме того, поддерживаются операции удаления и запросов. Этот формат задаётся с помощью format='table' или format='t' для append или put или to_hdf.
Этот формат также может быть задан как опция pd.set_option('io.hdf.default_format','table') для включения put/append/to_hdf по умолчанию хранить в формате table.
In [461]: store = pd.HDFStore("store.h5")
In [462]: df1 = df[0:4]
In [463]: df2 = df[4:]
# append data (creates a table automatically)
In [464]: store.append("df", df1)
In [465]: store.append("df", df2)
In [466]: store
Out[466]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
# select the entire object
In [467]: store.select("df")
Out[467]:
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
# the type of stored data
In [468]: store.root.df._v_attrs.pandas_type
Out[468]: 'frame_table'
Примечание
Вы также можете создать table, передав format='table' или format='t' в операцию put.
Иерархические ключи
Ключи к хранилищу могут быть заданы как строка. Они могут быть в формате иерархического пути (например, foo/bar/bah), который создаст иерархию подхранилищ (или Groups в терминологии PyTables). Ключи могут быть заданы без ведущего ‘/’ и всегда являются абсолютными (например, ‘foo’ относится к ‘/foo’). Операции удаления могут удалить всё в подхранилище и ниже, поэтому будьте осторожны.
In [469]: store.put("foo/bar/bah", df)
In [470]: store.append("food/orange", df)
In [471]: store.append("food/apple", df)
In [472]: store
Out[472]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
# a list of keys are returned
In [473]: store.keys()
Out[473]: ['/df', '/food/apple', '/food/orange', '/foo/bar/bah']
# remove all nodes under this level
In [474]: store.remove("food")
In [475]: store
Out[475]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
Вы можете проходить по иерархии групп, используя метод walk, который вернёт кортеж для каждого ключа группы вместе с относительными ключами её содержимого.
In [476]: for (path, subgroups, subkeys) in store.walk():
.....: for subgroup in subgroups:
.....: print("GROUP: {}/{}".format(path, subgroup))
.....: for subkey in subkeys:
.....: key = "/".join([path, subkey])
.....: print("KEY: {}".format(key))
.....: print(store.get(key))
.....:
GROUP: /foo
KEY: /df
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
GROUP: /foo/bar
KEY: /foo/bar/bah
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
Предупреждение
Иерархические ключи не могут быть получены как доступ через точки (атрибут), как описано выше для элементов, сохранённых под узлом корня.
In [8]: store.foo.bar.bah
AttributeError: 'HDFStore' object has no attribute 'foo'
# you can directly access the actual PyTables node but using the root node
In [9]: store.root.foo.bar.bah
Out[9]:
/foo/bar/bah (Group) ''
children := ['block0_items' (Array), 'block0_values' (Array), 'axis0' (Array), 'axis1' (Array)]
Вместо этого используйте явные ключи на основе строк:
In [477]: store["foo/bar/bah"]
Out[477]:
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
Типы хранения
Хранение данных смешанных типов в таблице
Поддерживается хранение данных смешанного типа. Строки хранятся как фиксированной ширины, используя максимальный размер добавленного столбца. Попытки добавить более длинные строки приведут к ошибке ValueError.
Передача min_itemsize={`values`: size} в качестве параметра append установит больший минимум для столбцов строк. Хранение floats,
strings, ints, bools, datetime64 в настоящее время поддерживается. Для столбцов строк передача nan_rep = 'nan' в append изменит представление nan на диске (которое преобразуется в/из np.nan), по умолчанию это nan.
In [478]: df_mixed = pd.DataFrame(
.....: {
.....: "A": np.random.randn(8),
.....: "B": np.random.randn(8),
.....: "C": np.array(np.random.randn(8), dtype="float32"),
.....: "string": "string",
.....: "int": 1,
.....: "bool": True,
.....: "datetime64": pd.Timestamp("20010102"),
.....: },
.....: index=list(range(8)),
.....: )
.....:
In [479]: df_mixed.loc[df_mixed.index[3:5], ["A", "B", "string", "datetime64"]] = np.nan
In [480]: store.append("df_mixed", df_mixed, min_itemsize={"values": 50})
In [481]: df_mixed1 = store.select("df_mixed")
In [482]: df_mixed1
Out[482]:
A B C string int bool datetime64
0 1.778161 -0.898283 -0.263043 string 1 True 2001-01-02
1 -0.913867 -0.218499 -0.639244 string 1 True 2001-01-02
2 -0.030004 1.408028 -0.866305 string 1 True 2001-01-02
3 NaN NaN -0.225250 NaN 1 True NaT
4 NaN NaN -0.890978 NaN 1 True NaT
5 0.081323 0.520995 -0.553839 string 1 True 2001-01-02
6 -0.268494 0.620028 -2.762875 string 1 True 2001-01-02
7 0.168016 0.159416 -1.244763 string 1 True 2001-01-02
In [483]: df_mixed1.dtypes.value_counts()
Out[483]:
float64 2
float32 1
object 1
int64 1
bool 1
datetime64[ns] 1
dtype: int64
# we have provided a minimum string column size
In [484]: store.root.df_mixed.table
Out[484]:
/df_mixed/table (Table(8,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": Float64Col(shape=(2,), dflt=0.0, pos=1),
"values_block_1": Float32Col(shape=(1,), dflt=0.0, pos=2),
"values_block_2": StringCol(itemsize=50, shape=(1,), dflt=b'', pos=3),
"values_block_3": Int64Col(shape=(1,), dflt=0, pos=4),
"values_block_4": BoolCol(shape=(1,), dflt=False, pos=5),
"values_block_5": Int64Col(shape=(1,), dflt=0, pos=6)}
byteorder := 'little'
chunkshape := (689,)
autoindex := True
colindexes := {
"index": Index(6, mediumshuffle, zlib(1)).is_csi=False}
Хранение DataFrames с MultiIndex
Хранение MultiIndex DataFrames как таблиц очень похоже на хранение/выбор из однородного индекса DataFrames.
In [485]: index = pd.MultiIndex(
.....: levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]],
.....: codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]],
.....: names=["foo", "bar"],
.....: )
.....:
In [486]: df_mi = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])
In [487]: df_mi
Out[487]:
A B C
foo bar
foo one -1.280289 0.692545 -0.536722
two 1.005707 0.296917 0.139796
three -1.083889 0.811865 1.648435
bar one -0.164377 -0.402227 1.618922
two -1.424723 -0.023232 0.948196
baz two 0.183573 0.145277 0.308146
three -1.043530 -0.708145 1.430905
qux one -0.850136 0.813949 1.508891
two -1.556154 0.187597 1.176488
three -1.246093 -0.002726 -0.444249
In [488]: store.append("df_mi", df_mi)
In [489]: store.select("df_mi")
Out[489]:
A B C
foo bar
foo one -1.280289 0.692545 -0.536722
two 1.005707 0.296917 0.139796
three -1.083889 0.811865 1.648435
bar one -0.164377 -0.402227 1.618922
two -1.424723 -0.023232 0.948196
baz two 0.183573 0.145277 0.308146
three -1.043530 -0.708145 1.430905
qux one -0.850136 0.813949 1.508891
two -1.556154 0.187597 1.176488
three -1.246093 -0.002726 -0.444249
# the levels are automatically included as data columns
In [490]: store.select("df_mi", "foo=bar")
Out[490]:
A B C
foo bar
bar one -0.164377 -0.402227 1.618922
two -1.424723 -0.023232 0.948196
Примечание
Ключевое слово index зарезервировано и не может использоваться в качестве имени уровня.
Запросы
Запрос таблицы
select и delete операции имеют необязательный критерий, который может быть указан для выбора/удаления только подмножества данных. Это позволяет иметь очень большую таблицу на диске и извлекать только часть данных.
Запрос задаётся с использованием класса Term внутри, как булево выражение.
indexиcolumnsподдерживаются индексаторамиDataFrames.Если
data_columnsуказаны, они могут использоваться в качестве дополнительных индексаторов.имя уровня в MultiIndex, со значением по умолчанию
level_0,level_1, … если не указано.
Действительные операторы сравнения:
=, ==, !=, >, >=, <, <=
Действительные булевы выражения объединяются с использованием:
|: или&: и(и): для группировки
Эти правила аналогичны тому, как булевы выражения используются в pandas для индексирования.
Примечание
=будет автоматически расширен до оператора сравнения==~— оператор отрицания, но может использоваться только в очень ограниченных случаяхЕсли передаётся список/кортеж выражений, они будут объединены с использованием
&
Следующие являются допустимыми выражениями:
'index >= date'"columns = ['A', 'D']""columns in ['A', 'D']"'columns = A''columns == A'"~(columns = ['A', 'B'])"'index > df.index[3] & string = "bar"''(index > df.index[3] & index <= df.index[6]) | string = "bar"'"ts >= Timestamp('2012-02-01')""major_axis>=20130101"
indexers находятся слева от подвыражения:
columns, major_axis, ts
Правая часть подвыражения (после оператора сравнения) может быть:
функции, которые будут вычислены, например,
Timestamp('2012-02-01')строки, например,
"bar"данные типа даты, например,
20130101, или"20130101"списки, например,
"['A', 'B']"переменные, определённые в локальном пространстве имён, например,
date
Примечание
Не рекомендуется передавать строку в запрос путём интерполяции её в выражение запроса. Просто присвойте интересующую строку переменной и используйте эту переменную в выражении. Например, сделайте так
string = "HolyMoly'"
store.select("df", "index == string")
вместо этого
string = "HolyMoly'"
store.select('df', f'index == {string}')
Последний вариант не будет работать и вызовет SyntaxError. Обратите внимание, что в переменной string есть одинарная кавычка, за которой следует двойная.
Если вам необходимо выполнить интерполяцию, используйте спецификатор формата '%r'
store.select("df", "index == %r" % string)
который добавит кавычки к string.
Вот некоторые примеры:
In [491]: dfq = pd.DataFrame(
.....: np.random.randn(10, 4),
.....: columns=list("ABCD"),
.....: index=pd.date_range("20130101", periods=10),
.....: )
.....:
In [492]: store.append("dfq", dfq, format="table", data_columns=True)
Используйте булевы выражения с вычислением функций в строке.
In [493]: store.select("dfq", "index>pd.Timestamp('20130104') & columns=['A', 'B']")
Out[493]:
A B
2013-01-05 1.366810 1.073372
2013-01-06 2.119746 -2.628174
2013-01-07 0.337920 -0.634027
2013-01-08 1.053434 1.109090
2013-01-09 -0.772942 -0.269415
2013-01-10 0.048562 -0.285920
Используйте встроенную ссылку на столбец.
In [494]: store.select("dfq", where="A>0 or C>0")
Out[494]:
A B C D
2013-01-01 0.856838 1.491776 0.001283 0.701816
2013-01-02 -1.097917 0.102588 0.661740 0.443531
2013-01-03 0.559313 -0.459055 -1.222598 -0.455304
2013-01-05 1.366810 1.073372 -0.994957 0.755314
2013-01-06 2.119746 -2.628174 -0.089460 -0.133636
2013-01-07 0.337920 -0.634027 0.421107 0.604303
2013-01-08 1.053434 1.109090 -0.367891 -0.846206
2013-01-10 0.048562 -0.285920 1.334100 0.194462
Ключевое слово columns может быть использовано для выбора списка столбцов, которые должны быть возвращены, что эквивалентно передаче 'columns=list_of_columns_to_filter':
In [495]: store.select("df", "columns=['A', 'B']")
Out[495]:
A B
2000-01-01 -0.398501 -0.677311
2000-01-02 -1.167564 -0.593353
2000-01-03 -0.131959 0.089012
2000-01-04 0.169405 -1.358046
2000-01-05 0.492195 0.076693
2000-01-06 -0.285283 -1.210529
2000-01-07 0.941577 -0.342447
2000-01-08 0.052607 2.093214
Параметры start и stop могут быть указаны для ограничения общего пространства поиска. Эти параметры выражаются в количестве строк в таблице.
Примечание
select вызовет ValueError если выражение запроса содержит ссылку на неизвестную переменную. Обычно это означает, что вы пытаетесь выбрать столбец, который не является столбцом данных.
select вызовет SyntaxError если выражение запроса не является допустимым.
Запрос timedelta64[ns]
Вы можете хранить и выполнять запросы, используя тип timedelta64[ns]. Условия могут быть заданы в формате: <float>(<unit>), где float может быть со знаком (и дробным), а unit может быть D,s,ms,us,ns для timedelta. Вот пример:
In [496]: from datetime import timedelta
In [497]: dftd = pd.DataFrame(
.....: {
.....: "A": pd.Timestamp("20130101"),
.....: "B": [
.....: pd.Timestamp("20130101") + timedelta(days=i, seconds=10)
.....: for i in range(10)
.....: ],
.....: }
.....: )
.....:
In [498]: dftd["C"] = dftd["A"] - dftd["B"]
In [499]: dftd
Out[499]:
A B C
0 2013-01-01 2013-01-01 00:00:10 -1 days +23:59:50
1 2013-01-01 2013-01-02 00:00:10 -2 days +23:59:50
2 2013-01-01 2013-01-03 00:00:10 -3 days +23:59:50
3 2013-01-01 2013-01-04 00:00:10 -4 days +23:59:50
4 2013-01-01 2013-01-05 00:00:10 -5 days +23:59:50
5 2013-01-01 2013-01-06 00:00:10 -6 days +23:59:50
6 2013-01-01 2013-01-07 00:00:10 -7 days +23:59:50
7 2013-01-01 2013-01-08 00:00:10 -8 days +23:59:50
8 2013-01-01 2013-01-09 00:00:10 -9 days +23:59:50
9 2013-01-01 2013-01-10 00:00:10 -10 days +23:59:50
In [500]: store.append("dftd", dftd, data_columns=True)
In [501]: store.select("dftd", "C<'-3.5D'")
Out[501]:
A B C
4 2013-01-01 2013-01-05 00:00:10 -5 days +23:59:50
5 2013-01-01 2013-01-06 00:00:10 -6 days +23:59:50
6 2013-01-01 2013-01-07 00:00:10 -7 days +23:59:50
7 2013-01-01 2013-01-08 00:00:10 -8 days +23:59:50
8 2013-01-01 2013-01-09 00:00:10 -9 days +23:59:50
9 2013-01-01 2013-01-10 00:00:10 -10 days +23:59:50
Запрос MultiIndex
Выбор из MultiIndex может быть осуществлён с использованием имени уровня.
In [502]: df_mi.index.names
Out[502]: FrozenList(['foo', 'bar'])
In [503]: store.select("df_mi", "foo=baz and bar=two")
Out[503]:
A B C
foo bar
baz two 0.183573 0.145277 0.308146
Если у MultiIndex уровни имеют имена None, уровни автоматически становятся доступными через ключевое слово level_n с n требуемого уровня MultiIndex.
In [504]: index = pd.MultiIndex(
.....: levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]],
.....: codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]],
.....: )
.....:
In [505]: df_mi_2 = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"])
In [506]: df_mi_2
Out[506]:
A B C
foo one -0.646538 1.210676 -0.315409
two 1.528366 0.376542 0.174490
three 1.247943 -0.742283 0.710400
bar one 0.434128 -1.246384 1.139595
two 1.388668 -0.413554 -0.666287
baz two 0.010150 -0.163820 -0.115305
three 0.216467 0.633720 0.473945
qux one -0.155446 1.287082 0.320201
two -1.256989 0.874920 0.765944
three 0.025557 -0.729782 -0.127439
In [507]: store.append("df_mi_2", df_mi_2)
# the levels are automatically included as data columns with keyword level_n
In [508]: store.select("df_mi_2", "level_0=foo and level_1=two")
Out[508]:
A B C
foo two 1.528366 0.376542 0.17449
Индексирование
Вы можете создать/изменить индекс для таблицы с create_table_index после того, как данные уже находятся в таблице (после операции append/put). Создание индекса таблицы настоятельно рекомендуется. Это значительно ускорит ваши запросы, когда вы используете select с индексируемым измерением в качестве where.
Примечание
Индексы автоматически создаются для индексируемых столбцов и любых указанных столбцов данных. Это поведение можно отключить, передав index=False в append.
# we have automagically already created an index (in the first section)
In [509]: i = store.root.df.table.cols.index.index
In [510]: i.optlevel, i.kind
Out[510]: (6, 'medium')
# change an index by passing new parameters
In [511]: store.create_table_index("df", optlevel=9, kind="full")
In [512]: i = store.root.df.table.cols.index.index
In [513]: i.optlevel, i.kind
Out[513]: (9, 'full')
Часто при добавлении большого объёма данных в хранилище полезно отключить создание индекса для каждого добавления, а затем пересоздать его в конце.
In [514]: df_1 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))
In [515]: df_2 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))
In [516]: st = pd.HDFStore("appends.h5", mode="w")
In [517]: st.append("df", df_1, data_columns=["B"], index=False)
In [518]: st.append("df", df_2, data_columns=["B"], index=False)
In [519]: st.get_storer("df").table
Out[519]:
/df/table (Table(20,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
"B": Float64Col(shape=(), dflt=0.0, pos=2)}
byteorder := 'little'
chunkshape := (2730,)
Затем создайте индекс, когда закончите добавление.
In [520]: st.create_table_index("df", columns=["B"], optlevel=9, kind="full")
In [521]: st.get_storer("df").table
Out[521]:
/df/table (Table(20,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
"B": Float64Col(shape=(), dflt=0.0, pos=2)}
byteorder := 'little'
chunkshape := (2730,)
autoindex := True
colindexes := {
"B": Index(9, fullshuffle, zlib(1)).is_csi=True}
In [522]: st.close()
См. здесь, чтобы узнать, как создать полностью отсортированный индекс (CSI) в существующем хранилище.
Запрос через столбцы данных
Вы можете назначить (и индексировать) определённые столбцы, для которых вы хотите выполнять запросы (кроме столбцов indexable, которые вы всегда можете запросить). Например, предположим, что вы хотите выполнить эту распространённую операцию на диске и вернуть только фрейм, соответствующий этому запросу. Вы можете указать data_columns = True для принудительного преобразования всех столбцов в data_columns.
In [523]: df_dc = df.copy()
In [524]: df_dc["string"] = "foo"
In [525]: df_dc.loc[df_dc.index[4:6], "string"] = np.nan
In [526]: df_dc.loc[df_dc.index[7:9], "string"] = "bar"
In [527]: df_dc["string2"] = "cool"
In [528]: df_dc.loc[df_dc.index[1:3], ["B", "C"]] = 1.0
In [529]: df_dc
Out[529]:
A B C string string2
2000-01-01 -0.398501 -0.677311 -0.874991 foo cool
2000-01-02 -1.167564 1.000000 1.000000 foo cool
2000-01-03 -0.131959 1.000000 1.000000 foo cool
2000-01-04 0.169405 -1.358046 -0.105563 foo cool
2000-01-05 0.492195 0.076693 0.213685 NaN cool
2000-01-06 -0.285283 -1.210529 -1.408386 NaN cool
2000-01-07 0.941577 -0.342447 0.222031 foo cool
2000-01-08 0.052607 2.093214 1.064908 bar cool
# on-disk operations
In [530]: store.append("df_dc", df_dc, data_columns=["B", "C", "string", "string2"])
In [531]: store.select("df_dc", where="B > 0")
Out[531]:
A B C string string2
2000-01-02 -1.167564 1.000000 1.000000 foo cool
2000-01-03 -0.131959 1.000000 1.000000 foo cool
2000-01-05 0.492195 0.076693 0.213685 NaN cool
2000-01-08 0.052607 2.093214 1.064908 bar cool
# getting creative
In [532]: store.select("df_dc", "B > 0 & C > 0 & string == foo")
Out[532]:
A B C string string2
2000-01-02 -1.167564 1.0 1.0 foo cool
2000-01-03 -0.131959 1.0 1.0 foo cool
# this is in-memory version of this type of selection
In [533]: df_dc[(df_dc.B > 0) & (df_dc.C > 0) & (df_dc.string == "foo")]
Out[533]:
A B C string string2
2000-01-02 -1.167564 1.0 1.0 foo cool
2000-01-03 -0.131959 1.0 1.0 foo cool
# we have automagically created this index and the B/C/string/string2
# columns are stored separately as ``PyTables`` columns
In [534]: store.root.df_dc.table
Out[534]:
/df_dc/table (Table(8,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
"B": Float64Col(shape=(), dflt=0.0, pos=2),
"C": Float64Col(shape=(), dflt=0.0, pos=3),
"string": StringCol(itemsize=3, shape=(), dflt=b'', pos=4),
"string2": StringCol(itemsize=4, shape=(), dflt=b'', pos=5)}
byteorder := 'little'
chunkshape := (1680,)
autoindex := True
colindexes := {
"index": Index(6, mediumshuffle, zlib(1)).is_csi=False,
"B": Index(6, mediumshuffle, zlib(1)).is_csi=False,
"C": Index(6, mediumshuffle, zlib(1)).is_csi=False,
"string": Index(6, mediumshuffle, zlib(1)).is_csi=False,
"string2": Index(6, mediumshuffle, zlib(1)).is_csi=False}
Есть некоторое снижение производительности при преобразовании большого количества столбцов в data columns, поэтому это зависит от пользователя. Кроме того, вы не можете изменять столбцы данных (или индексируемые столбцы) после первой операции добавления/записи (Конечно, вы можете просто считать данные и создать новую таблицу!).
Итератор
Вы можете передать iterator=True или chunksize=number_in_a_chunk в select и select_as_multiple для возвращения итератора по результатам. По умолчанию возвращается 50 000 строк в блоке.
In [535]: for df in store.select("df", chunksize=3):
.....: print(df)
.....:
A B C
2000-01-01 -0.398501 -0.677311 -0.874991
2000-01-02 -1.167564 -0.593353 0.146262
2000-01-03 -0.131959 0.089012 0.667450
A B C
2000-01-04 0.169405 -1.358046 -0.105563
2000-01-05 0.492195 0.076693 0.213685
2000-01-06 -0.285283 -1.210529 -1.408386
A B C
2000-01-07 0.941577 -0.342447 0.222031
2000-01-08 0.052607 2.093214 1.064908
Примечание
Вы также можете использовать итератор с read_hdf, который откроет, а затем автоматически закроет хранилище после завершения итерации.
for df in pd.read_hdf("store.h5", "df", chunksize=3):
print(df)
Обратите внимание, что ключевое слово chunksize относится к исходным строкам. Таким образом, если вы выполняете запрос, то chunksize разделит общее количество строк в таблице и применённый запрос, возвращая итератор по потенциально неравномерным блокам.
Вот рецепт для генерации запроса и использования его для создания равномерных блоков возвращаемых данных.
In [536]: dfeq = pd.DataFrame({"number": np.arange(1, 11)})
In [537]: dfeq
Out[537]:
number
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
In [538]: store.append("dfeq", dfeq, data_columns=["number"])
In [539]: def chunks(l, n):
.....: return [l[i: i + n] for i in range(0, len(l), n)]
.....:
In [540]: evens = [2, 4, 6, 8, 10]
In [541]: coordinates = store.select_as_coordinates("dfeq", "number=evens")
In [542]: for c in chunks(coordinates, 2):
.....: print(store.select("dfeq", where=c))
.....:
number
1 2
3 4
number
5 6
7 8
number
9 10
Расширенные запросы
Выбор одного столбца
Для получения одного индексируемого или столбца данных используйте метод select_column. Это позволит, например, очень быстро получить индекс. Эти методы возвращают Series результата, индексированного по номеру строки. Эти методы в настоящее время не принимают селектор where.
In [543]: store.select_column("df_dc", "index")
Out[543]:
0 2000-01-01
1 2000-01-02
2 2000-01-03
3 2000-01-04
4 2000-01-05
5 2000-01-06
6 2000-01-07
7 2000-01-08
Name: index, dtype: datetime64[ns]
In [544]: store.select_column("df_dc", "string")
Out[544]:
0 foo
1 foo
2 foo
3 foo
4 NaN
5 NaN
6 foo
7 bar
Name: string, dtype: object
Выбор координат
Иногда вам нужно получить координаты (т.е. местоположения индексов) вашего запроса. Это возвращает Int64Index результирующих местоположений. Эти координаты также можно передавать в последующие операции where.
In [545]: df_coord = pd.DataFrame(
.....: np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
.....: )
.....:
In [546]: store.append("df_coord", df_coord)
In [547]: c = store.select_as_coordinates("df_coord", "index > 20020101")
In [548]: c
Out[548]:
Int64Index([732, 733, 734, 735, 736, 737, 738, 739, 740, 741,
...
990, 991, 992, 993, 994, 995, 996, 997, 998, 999],
dtype='int64', length=268)
In [549]: store.select("df_coord", where=c)
Out[549]:
0 1
2002-01-02 0.009035 0.921784
2002-01-03 -1.476563 -1.376375
2002-01-04 1.266731 2.173681
2002-01-05 0.147621 0.616468
2002-01-06 0.008611 2.136001
... ... ...
2002-09-22 0.781169 -0.791687
2002-09-23 -0.764810 -2.000933
2002-09-24 -0.345662 0.393915
2002-09-25 -0.116661 0.834638
2002-09-26 -1.341780 0.686366
[268 rows x 2 columns]
Выбор с помощью маски where
Иногда ваш запрос может включать создание списка строк для выбора. Обычно эта mask будет результатом index операции индексирования. Этот пример выбирает месяцы datetimeindex, которые равны 5.
In [550]: df_mask = pd.DataFrame(
.....: np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000)
.....: )
.....:
In [551]: store.append("df_mask", df_mask)
In [552]: c = store.select_column("df_mask", "index")
In [553]: where = c[pd.DatetimeIndex(c).month == 5].index
In [554]: store.select("df_mask", where=where)
Out[554]:
0 1
2000-05-01 -0.386742 -0.977433
2000-05-02 -0.228819 0.471671
2000-05-03 0.337307 1.840494
2000-05-04 0.050249 0.307149
2000-05-05 -0.802947 -0.946730
... ... ...
2002-05-27 1.605281 1.741415
2002-05-28 -0.804450 -0.715040
2002-05-29 -0.874851 0.037178
2002-05-30 -0.161167 -1.294944
2002-05-31 -0.258463 -0.731969
[93 rows x 2 columns]
Объект Storer
Если вы хотите проверить сохранённый объект, получите его через get_storer. Вы можете использовать это программно, например, чтобы получить количество строк в объекте.
In [555]: store.get_storer("df_dc").nrows
Out[555]: 8
Запросы к нескольким таблицам
Методы append_to_multiple и select_as_multiple могут выполнять добавление/выбор из нескольких таблиц одновременно. Идея заключается в том, чтобы иметь одну таблицу (назовем её таблицей-селектором), в которой индексированы большинство/все столбцы, и выполнять запросы к ней. Другие таблицы являются таблицами данных с индексом, соответствующим индексу таблицы-селектора. Затем вы можете выполнить очень быстрый запрос к таблице-селектору, получив много данных. Этот метод похож на использование очень широкой таблицы, но позволяет более эффективно выполнять запросы.
Метод append_to_multiple разбивает заданный DataFrame на несколько таблиц в соответствии с d, словарем, сопоставляющим имена таблиц с списком «столбцов», которые вы хотите в этой таблице. Если None используется вместо списка, эта таблица будет содержать оставшиеся не указанные столбцы заданного DataFrame. Аргумент selector определяет, какая таблица является таблицей-селектором (из которой можно выполнять запросы). Аргумент dropna удалит строки из входного DataFrame, чтобы гарантировать синхронизацию таблиц. Это означает, что если строка для одной из таблиц, в которые осуществляется запись, полностью np.NaN, эта строка будет удалена из всех таблиц.
Если dropna имеет значение False, ПОЛЬЗОВАТЕЛЬ НЕСЁТ ОТВЕТСТВЕННОСТЬ ЗА СИНХРОНИЗАЦИЮ ТАБЛИЦ. Помните, что строки, полностью np.Nan, не записываются в HDFStore, поэтому, если вы вызовете dropna=False, некоторые таблицы могут иметь больше строк, чем другие, и поэтому select_as_multiple может не сработать или может вернуть неожиданные результаты.
In [556]: df_mt = pd.DataFrame(
.....: np.random.randn(8, 6),
.....: index=pd.date_range("1/1/2000", periods=8),
.....: columns=["A", "B", "C", "D", "E", "F"],
.....: )
.....:
In [557]: df_mt["foo"] = "bar"
In [558]: df_mt.loc[df_mt.index[1], ("A", "B")] = np.nan
# you can also create the tables individually
In [559]: store.append_to_multiple(
.....: {"df1_mt": ["A", "B"], "df2_mt": None}, df_mt, selector="df1_mt"
.....: )
.....:
In [560]: store
Out[560]:
<class 'pandas.io.pytables.HDFStore'>
File path: store.h5
# individual tables were created
In [561]: store.select("df1_mt")
Out[561]:
A B
2000-01-01 0.079529 -1.459471
2000-01-02 NaN NaN
2000-01-03 -0.423113 2.314361
2000-01-04 0.756744 -0.792372
2000-01-05 -0.184971 0.170852
2000-01-06 0.678830 0.633974
2000-01-07 0.034973 0.974369
2000-01-08 -2.110103 0.243062
In [562]: store.select("df2_mt")
Out[562]:
C D E F foo
2000-01-01 -0.596306 -0.910022 -1.057072 -0.864360 bar
2000-01-02 0.477849 0.283128 -2.045700 -0.338206 bar
2000-01-03 -0.033100 -0.965461 -0.001079 -0.351689 bar
2000-01-04 -0.513555 -1.484776 -0.796280 -0.182321 bar
2000-01-05 -0.872407 -1.751515 0.934334 0.938818 bar
2000-01-06 -1.398256 1.347142 -0.029520 0.082738 bar
2000-01-07 -0.755544 0.380786 -1.634116 1.293610 bar
2000-01-08 1.453064 0.500558 -0.574475 0.694324 bar
# as a multiple
In [563]: store.select_as_multiple(
.....: ["df1_mt", "df2_mt"],
.....: where=["A>0", "B>0"],
.....: selector="df1_mt",
.....: )
.....:
Out[563]:
A B C D E F foo
2000-01-06 0.678830 0.633974 -1.398256 1.347142 -0.029520 0.082738 bar
2000-01-07 0.034973 0.974369 -0.755544 0.380786 -1.634116 1.293610 bar
Удаление из таблицы
Вы можете выборочно удалить из таблицы, указав where. При удалении строк важно понимать, что PyTables удаляет строки, удаляя их, а затем перемещая последующие данные. Таким образом, удаление может быть очень дорогостоящей операцией в зависимости от ориентации ваших данных. Для достижения оптимальной производительности следует сделать так, чтобы измерение, которое вы удаляете, было первым в indexables.
Данные упорядочены (на диске) в соответствии с indexables. Вот простой пример использования. Вы храните данные типа панели, со значениями дат в major_axis и идентификаторами в minor_axis. Данные затем интерпретируются так:
-
- date_1
-
id_1
id_2
.
id_n
-
- date_2
-
id_1
.
id_n
Должно быть понятно, что операция удаления по major_axis будет довольно быстрой, так как один фрагмент удаляется, а последующие данные перемещаются. С другой стороны, операция удаления по minor_axis будет очень дорогостоящей. В этом случае было бы почти наверняка быстрее переписать таблицу, используя where , который выбирает все, кроме отсутствующих данных.
Предупреждение
Обратите внимание, что HDF5 НЕ ВОССТАНАВЛИВАЕТ ПРОСТРАНСТВО в файлах h5 автоматически. Таким образом, многократное удаление (или удаление узлов) и повторное добавление БУДЕТ ТЕНДЕНЦИЯ К УВЕЛИЧЕНИЮ РАЗМЕРА ФАЙЛА.
Чтобы упаковать и очистить файл, используйте ptrepack.
Примечания и замечания
Сжатие
PyTables позволяет сжимать хранимые данные. Это относится ко всем видам хранилищ, а не только к таблицам. Для управления сжатием используются два параметра: complevel и complib.
complevelуказывает, как и насколько сильно сжимаются данные.complevel=0иcomplevel=Noneотключают сжатие, а0<complevel<10включает сжатие.-
complibуказывает, какую библиотеку сжатия использовать. Если ничего не указано, используется библиотека по умолчаниюzlib. Библиотека сжатия обычно оптимизирует либо скорость сжатия, либо скорость, и результаты будут зависеть от типа данных. Какой тип сжатия выбрать, зависит от ваших конкретных потребностей и данных. Список поддерживаемых библиотек сжатия:zlib: Библиотека сжатия по умолчанию. Классика в плане сжатия, достигает хороших показателей сжатия, но несколько медленная.
lzo: Быстрое сжатие и распаковку.
bzip2: Хорошие показатели сжатия.
-
blosc: Быстрое сжатие и распаковку.
Поддержка альтернативных сжимателей blosc:
blosc:blosclz Это сжиматель по умолчанию для
bloscblosc:lz4: Компактный, очень популярный и быстрый сжиматель.
blosc:lz4hc: Модифицированная версия LZ4, обеспечивает лучшие коэффициенты сжатия за счёт скорости.
blosc:snappy: Популярный сжиматель, используемый во многих местах.
blosc:zlib: Классика; несколько медленнее предыдущих, но достигает лучших коэффициентов сжатия.
blosc:zstd: Крайне сбалансированный кодек; он обеспечивает лучшие коэффициенты сжатия среди других вышеперечисленных, и с достаточно высокой скоростью.
Если
complibопределено как что-то отличное от перечисленных библиотек, выдается исключениеValueError.
Примечание
Если библиотека, указанная с помощью параметра complib отсутствует на вашей платформе, сжатие по умолчанию zlib без дополнительных действий.
Включить сжатие для всех объектов в файле:
store_compressed = pd.HDFStore(
"store_compressed.h5", complevel=9, complib="blosc:blosclz"
)
Или сжатие в реальном времени (это относится только к таблицам) в хранилищах, где сжатие не включено:
store.append("df", df, complib="zlib", complevel=5)
ptrepack
PyTables предлагает лучшую производительность при записи, когда таблицы сжимаются после записи, а не при включении сжатия в самом начале. Вы можете использовать предоставленную утилиту PyTables ptrepack. Кроме того, ptrepack может изменять уровни сжатия после факта.
ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5
Кроме того, ptrepack in.h5 out.h5 упакует файл, чтобы вы могли повторно использовать ранее удалённое пространство. В качестве альтернативы, можно просто удалить файл и переписать его или использовать метод copy.
Ограничения
Предупреждение
HDFStore не потокобезопасен для записи. Базовое PyTables поддерживает только одновременные чтения (через потоки или процессы). Если вам нужно чтение и запись одновременно, вам нужно сериализовать эти операции в одном потоке в одном процессе. В противном случае вы повредите свои данные. Смотрите (GH2397) для получения дополнительной информации.
Если вы используете блокировки для управления доступом к записи между несколькими процессами, вы можете использовать
fsync()перед освобождением блокировок записи. Для удобства вы можете использоватьstore.flush(fsync=True), чтобы сделать это за вас.После создания
tableстолбцы (DataFrame) фиксируются; можно добавить только точно такие же столбцы.Учитывайте, что часовые пояса (например,
pytz.timezone('US/Eastern')) не обязательно совпадают с версиями библиотек часовых поясов. Таким образом, если данные локализованы в определённом часовом поясе в HDFStore с использованием одной версии библиотеки часовых поясов, и эти данные обновляются другой версией, данные будут преобразованы в UTC, так как эти часовые пояса не считаются равными. Либо используйте одну и ту же версию библиотеки часовых поясов, либо используйтеtz_convertс обновлённым определением часового пояса.
Предупреждение
PyTables покажет NaturalNameWarning , если имя столбца нельзя использовать в качестве селектора атрибута. Естественные идентификаторы содержат только буквы, цифры и символы нижнего подчеркивания и не могут начинаться с цифры. Другие идентификаторы не могут быть использованы в where -оператор и, как правило, являются плохой практикой.
Типы данных
HDFStore будет отображать тип данных объекта на PyTables базовый тип данных. Это означает, что следующие типы работают:
Тип | Представляет пропущенные значения |
|---|---|
с плавающей точкой : |
|
целый : | |
булево | |
|
|
|
|
категориальный : см. раздел ниже | |
объект : |
|
unicode столбцы не поддерживаются и БУДУТ ВЫЗЫВАТЬ ОШИБКУ.
Категориальные данные
Вы можете записать данные, содержащие category типы данных в HDFStore. Запросы работают так же, как если бы это был массив объектов. Однако данные с типом category хранятся более эффективным способом.
In [564]: dfcat = pd.DataFrame(
.....: {"A": pd.Series(list("aabbcdba")).astype("category"), "B": np.random.randn(8)}
.....: )
.....:
In [565]: dfcat
Out[565]:
A B
0 a -1.608059
1 a 0.851060
2 b -0.736931
3 b 0.003538
4 c -1.422611
5 d 2.060901
6 b 0.993899
7 a -1.371768
In [566]: dfcat.dtypes
Out[566]:
A category
B float64
dtype: object
In [567]: cstore = pd.HDFStore("cats.h5", mode="w")
In [568]: cstore.append("dfcat", dfcat, format="table", data_columns=["A"])
In [569]: result = cstore.select("dfcat", where="A in ['b', 'c']")
In [570]: result
Out[570]:
A B
2 b -0.736931
3 b 0.003538
4 c -1.422611
6 b 0.993899
In [571]: result.dtypes
Out[571]:
A category
B float64
dtype: object
Столбцы строк
min_itemsize
Внутренняя реализация HDFStore использует фиксированную ширину столбца (itemsize) для столбцов строк. Размер элемента столбца строк вычисляется как максимальная длина данных (для данного столбца), которые передаются в HDFStore, в первом добавлении. При последующих добавлениях может появиться строка для столбца, больше, чем может вместить столбец; будет поднято исключение (в противном случае у вас может быть неявное усечение этих столбцов, что приведет к потере информации). В будущем мы можем ослабить это и разрешить указание пользователем усечения.
Передайте min_itemsize при первом создании таблицы, чтобы задать минимальную длину определенного столбца строк. min_itemsize может быть целым числом или словарем, сопоставляющим имя столбца с целым числом. Вы можете передать values в качестве ключа, чтобы разрешить всем индексируемым или данным_столбцам иметь этот min_itemsize.
Передача словаря min_itemsize приведет к автоматическому созданию всех переданных столбцов в качестве данных_столбцов.
Примечание
Если вы не передаете data_columns, то min_itemsize будет максимальной длиной любой переданной строки
In [572]: dfs = pd.DataFrame({"A": "foo", "B": "bar"}, index=list(range(5)))
In [573]: dfs
Out[573]:
A B
0 foo bar
1 foo bar
2 foo bar
3 foo bar
4 foo bar
# A and B have a size of 30
In [574]: store.append("dfs", dfs, min_itemsize=30)
In [575]: store.get_storer("dfs").table
Out[575]:
/dfs/table (Table(5,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": StringCol(itemsize=30, shape=(2,), dflt=b'', pos=1)}
byteorder := 'little'
chunkshape := (963,)
autoindex := True
colindexes := {
"index": Index(6, mediumshuffle, zlib(1)).is_csi=False}
# A is created as a data_column with a size of 30
# B is size is calculated
In [576]: store.append("dfs2", dfs, min_itemsize={"A": 30})
In [577]: store.get_storer("dfs2").table
Out[577]:
/dfs2/table (Table(5,)) ''
description := {
"index": Int64Col(shape=(), dflt=0, pos=0),
"values_block_0": StringCol(itemsize=3, shape=(1,), dflt=b'', pos=1),
"A": StringCol(itemsize=30, shape=(), dflt=b'', pos=2)}
byteorder := 'little'
chunkshape := (1598,)
autoindex := True
colindexes := {
"index": Index(6, mediumshuffle, zlib(1)).is_csi=False,
"A": Index(6, mediumshuffle, zlib(1)).is_csi=False}
nan_rep
Столбцы строк будут сериализовать np.nan (пропущенное значение) с nan_rep строковым представлением. По умолчанию это строковое значение nan. Вы можете непреднамеренно преобразовать фактическое значение nan в пропущенное значение.
In [578]: dfss = pd.DataFrame({"A": ["foo", "bar", "nan"]})
In [579]: dfss
Out[579]:
A
0 foo
1 bar
2 nan
In [580]: store.append("dfss", dfss)
In [581]: store.select("dfss")
Out[581]:
A
0 foo
1 bar
2 NaN
# here you need to specify a different nan rep
In [582]: store.append("dfss2", dfss, nan_rep="_nan_")
In [583]: store.select("dfss2")
Out[583]:
A
0 foo
1 bar
2 nan
Внешняя совместимость
HDFStore записывает объекты в формате table в определенных форматах, подходящих для создания без потерь обратных преобразований в объекты pandas. Для внешней совместимости HDFStore может читать таблицы в формате PyTables.
Возможна запись объекта HDFStore , который легко можно импортировать в R с помощью библиотеки rhdf5 (Веб-сайт пакета). Создайте хранилище табличного формата так:
In [584]: df_for_r = pd.DataFrame(
.....: {
.....: "first": np.random.rand(100),
.....: "second": np.random.rand(100),
.....: "class": np.random.randint(0, 2, (100,)),
.....: },
.....: index=range(100),
.....: )
.....:
In [585]: df_for_r.head()
Out[585]:
first second class
0 0.013480 0.504941 0
1 0.690984 0.898188 1
2 0.510113 0.618748 1
3 0.357698 0.004972 0
4 0.451658 0.012065 1
In [586]: store_export = pd.HDFStore("export.h5")
In [587]: store_export.append("df_for_r", df_for_r, data_columns=df_dc.columns)
In [588]: store_export
Out[588]:
<class 'pandas.io.pytables.HDFStore'>
File path: export.h5
В R этот файл можно считать в объект data.frame с использованием библиотеки rhdf5. Следующая примерная функция считывает соответствующие имена столбцов и значения данных из значений и собирает их в data.frame.
# Load values and column names for all datasets from corresponding nodes and
# insert them into one data.frame object.
library(rhdf5)
loadhdf5data <- function(h5File) {
listing <- h5ls(h5File)
# Find all data nodes, values are stored in *_values and corresponding column
# titles in *_items
data_nodes <- grep("_values", listing$name)
name_nodes <- grep("_items", listing$name)
data_paths = paste(listing$group[data_nodes], listing$name[data_nodes], sep = "/")
name_paths = paste(listing$group[name_nodes], listing$name[name_nodes], sep = "/")
columns = list()
for (idx in seq(data_paths)) {
# NOTE: matrices returned by h5read have to be transposed to obtain
# required Fortran order!
data <- data.frame(t(h5read(h5File, data_paths[idx])))
names <- t(h5read(h5File, name_paths[idx]))
entry <- data.frame(data)
colnames(entry) <- names
columns <- append(columns, entry)
}
data <- data.frame(columns)
return(data)
}
Теперь вы можете импортировать DataFrame в R:
> data = loadhdf5data("transfer.hdf5")
> head(data)
first second class
1 0.4170220047 0.3266449 0
2 0.7203244934 0.5270581 0
3 0.0001143748 0.8859421 1
4 0.3023325726 0.3572698 1
5 0.1467558908 0.9085352 1
6 0.0923385948 0.6233601 1
Примечание
Функция R перечисляет содержимое всего файла HDF5 и собирает объект data.frame из всех совпадающих узлов, поэтому используйте это только как отправную точку, если вы сохранили несколько объектов DataFrame в один файл HDF5.
Производительность
Формат
tablesсопровождается штрафом производительности при записи по сравнению с хранилищамиfixed. Преимущество заключается в возможности добавлять/удалять и выполнять запросы (возможно, очень больших объемов данных). Время записи обычно больше по сравнению с обычными хранилищами. Время запроса может быть довольно быстрым, особенно по индексируемой оси.Вы можете передать
chunksize=<int>вappend, указав размер блока записи (по умолчанию 50000). Это значительно снизит использование памяти при записи.Вы можете передать
expectedrows=<int>в первыйappend, чтобы установить ОБЩЕЕ количество строк, которыеPyTablesбудет ожидать. Это позволит оптимизировать производительность чтения/записи.Дублирующие строки могут быть записаны в таблицы, но отфильтровываются при выборе (последние элементы выбираются; таким образом, таблица уникальна по парам «главный, дополнительный»)
Будет поднято исключение
PerformanceWarning, если вы пытаетесь сохранить типы, которые будут сериализованы PyTables (а не храниться как собственные типы). См. Здесь для получения дополнительной информации и некоторых решений.
Feather
Feather предоставляет двоичную столбцовую сериализацию для фреймов данных. Она разработана для повышения эффективности чтения и записи фреймов данных и для облегчения совместного использования данных между языками анализа данных.
Feather разработан для точной сериализации и десериализации DataFrames, поддерживая все типы данных pandas, включая расширенные типы данных, такие как категориальные и временные с часовым поясом.
Некоторые особенности:
Формат НЕ запишет
Index, илиMultiIndexдляDataFrameи вызовет ошибку, если предоставлено отличное от стандартного значение. Вы можете.reset_index()для хранения индекса или.reset_index(drop=True)для игнорирования его.Дублирование имен столбцов и имена столбцов, отличные от строк, не поддерживаются.
Фактические объекты Python в столбцах типа «объект» не поддерживаются. При попытке сериализации они вызовут полезное сообщение об ошибке.
См. Полную документацию.
In [589]: df = pd.DataFrame(
.....: {
.....: "a": list("abc"),
.....: "b": list(range(1, 4)),
.....: "c": np.arange(3, 6).astype("u1"),
.....: "d": np.arange(4.0, 7.0, dtype="float64"),
.....: "e": [True, False, True],
.....: "f": pd.Categorical(list("abc")),
.....: "g": pd.date_range("20130101", periods=3),
.....: "h": pd.date_range("20130101", periods=3, tz="US/Eastern"),
.....: "i": pd.date_range("20130101", periods=3, freq="ns"),
.....: }
.....: )
.....:
In [590]: df
Out[590]:
a b c ... g h i
0 a 1 3 ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000
1 b 2 4 ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001
2 c 3 5 ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002
[3 rows x 9 columns]
In [591]: df.dtypes
Out[591]:
a object
b int64
c uint8
d float64
e bool
f category
g datetime64[ns]
h datetime64[ns, US/Eastern]
i datetime64[ns]
dtype: object
Запись в файл feather.
In [592]: df.to_feather("example.feather")
Чтение из файла feather.
In [593]: result = pd.read_feather("example.feather")
In [594]: result
Out[594]:
a b c ... g h i
0 a 1 3 ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000
1 b 2 4 ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001
2 c 3 5 ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002
[3 rows x 9 columns]
# we preserve dtypes
In [595]: result.dtypes
Out[595]:
a object
b int64
c uint8
d float64
e bool
f category
g datetime64[ns]
h datetime64[ns, US/Eastern]
i datetime64[ns]
dtype: object
Parquet
Apache Parquet предоставляет разбиение по столбцам в бинарном формате для фреймов данных. Он разработан для повышения эффективности чтения и записи фреймов данных и упрощения совместного использования данных между языками аналитики данных. Parquet может использовать различные методы сжатия для минимизации размера файла, сохраняя при этом хорошую производительность чтения.
Parquet разработан для точного сериализации и десериализации DataFrame , поддерживая все типы данных pandas, включая расширенные типы данных, такие как datetime с часовым поясом.
Несколько замечаний.
Дублирование имён столбцов и имена столбцов, не являющиеся строками, не поддерживаются.
Двигатель
pyarrowвсегда записывает индекс в выходной файл, ноfastparquetзаписывает только индексы, отличные от значений по умолчанию. Этот дополнительный столбец может вызвать проблемы для потребителей, не являющихся pandas, которые не ожидают его. Вы можете принудительно включить или исключить индексы с помощью аргументаindex, независимо от базового двигателя.Если указаны имена уровней индекса, они должны быть строками.
В двигателе
pyarrowтипы данных категориальных значений для типов, не являющихся строками, могут быть сериализованы в parquet, но будут десериализованы как их примитивный тип данных.Двигатель
pyarrowсохраняет флагorderedкатегориальных типов данных со строковыми типами.fastparquetне сохраняет флагordered.Неподдерживаемые типы включают
Intervalи фактические типы Python object. При попытке сериализации они будут генерировать полезное сообщение об ошибке. ТипPeriodподдерживается с pyarrow >= 0.16.0.Двигатель
pyarrowсохраняет расширенные типы данных, такие как целочисленный и строковый тип данных с возможностью NULL (требуется pyarrow >= 0.16.0, и требуется, чтобы расширенный тип реализовывал необходимые протоколы, см. документацию по расширенным типам).
Вы можете указать engine для управления сериализацией. Это может быть pyarrow, или fastparquet, или auto. Если двигатель НЕ указан, то проверяется опция pd.options.io.parquet.engine; если она также auto, то пробуется pyarrow, а в случае неудачи используется fastparquet.
См. документацию по pyarrow и fastparquet.
Примечание
Эти двигатели очень похожи и должны читать/записывать практически идентичные файлы формата parquet. pyarrow>=8.0.0 поддерживает данные timedelta, fastparquet>=0.1.4 поддерживает даты и время с учетом часового пояса. Эти библиотеки различаются своими различными базовыми зависимостями (fastparquet использует numba, в то время как pyarrow использует c-библиотеку).
In [596]: df = pd.DataFrame(
.....: {
.....: "a": list("abc"),
.....: "b": list(range(1, 4)),
.....: "c": np.arange(3, 6).astype("u1"),
.....: "d": np.arange(4.0, 7.0, dtype="float64"),
.....: "e": [True, False, True],
.....: "f": pd.date_range("20130101", periods=3),
.....: "g": pd.date_range("20130101", periods=3, tz="US/Eastern"),
.....: "h": pd.Categorical(list("abc")),
.....: "i": pd.Categorical(list("abc"), ordered=True),
.....: }
.....: )
.....:
In [597]: df
Out[597]:
a b c d e f g h i
0 a 1 3 4.0 True 2013-01-01 2013-01-01 00:00:00-05:00 a a
1 b 2 4 5.0 False 2013-01-02 2013-01-02 00:00:00-05:00 b b
2 c 3 5 6.0 True 2013-01-03 2013-01-03 00:00:00-05:00 c c
In [598]: df.dtypes
Out[598]:
a object
b int64
c uint8
d float64
e bool
f datetime64[ns]
g datetime64[ns, US/Eastern]
h category
i category
dtype: object
Запись в файл parquet.
In [599]: df.to_parquet("example_pa.parquet", engine="pyarrow")
In [600]: df.to_parquet("example_fp.parquet", engine="fastparquet")
Чтение из файла parquet.
In [601]: result = pd.read_parquet("example_fp.parquet", engine="fastparquet")
In [602]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow")
In [603]: result.dtypes
Out[603]:
a object
b int64
c uint8
d float64
e bool
f datetime64[ns]
g datetime64[ns, US/Eastern]
h category
i category
dtype: object
Чтение только определенных столбцов из файла parquet.
In [604]: result = pd.read_parquet(
.....: "example_fp.parquet",
.....: engine="fastparquet",
.....: columns=["a", "b"],
.....: )
.....:
In [605]: result = pd.read_parquet(
.....: "example_pa.parquet",
.....: engine="pyarrow",
.....: columns=["a", "b"],
.....: )
.....:
In [606]: result.dtypes
Out[606]:
a object
b int64
dtype: object
Обработка индексов
Сериализация DataFrame в parquet может включать неявный индекс как один или несколько столбцов в выходном файле. Таким образом, этот код:
In [607]: df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
In [608]: df.to_parquet("test.parquet", engine="pyarrow")
создаёт файл parquet с тремя столбцами, если вы используете pyarrow для сериализации: a, b, и __index_level_0__. Если вы используете fastparquet, индекс может или не может быть записан в файл.
Этот неожиданный дополнительный столбец приводит к тому, что некоторые базы данных, например Amazon Redshift, отклоняют файл, потому что этот столбец не существует в целевой таблице.
Если вы хотите исключить индексы фрейма данных при записи, передайте index=False в to_parquet():
In [609]: df.to_parquet("test.parquet", index=False)
Это создаёт файл parquet только с двумя ожидаемыми столбцами, a и b. Если ваш DataFrame имеет пользовательский индекс, вы не получите его обратно, загрузив этот файл в DataFrame.
Передача index=True всегда запишет индекс, даже если это не является стандартным поведением базового двигателя.
Разбиение файлов Parquet
Parquet поддерживает разбиение данных на основе значений одного или нескольких столбцов.
In [610]: df = pd.DataFrame({"a": [0, 0, 1, 1], "b": [0, 1, 0, 1]})
In [611]: df.to_parquet(path="test", engine="pyarrow", partition_cols=["a"], compression=None)
path указывает родительский каталог, в который будут сохранены данные. partition_cols — это имена столбцов, по которым будет производиться разбиение набора данных. Столбцы разбиваются в том порядке, в котором они указаны. Разбиения по столбцам определяются уникальными значениями в столбцах разбиения. Приведённый выше пример создаёт разбиение набора данных, которое может выглядеть так:
test
├── a=0
│ ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet
│ └── ...
└── a=1
├── e6ab24a4f45147b49b54a662f0c412a3.parquet
└── ...
ORC
Новый в версии 1.0.0.
Аналогично формату parquet, формат ORC — это бинарное разбиение по столбцам для фреймов данных. Он разработан для повышения эффективности чтения фреймов данных. pandas предоставляет как читатель, так и записыватель для формата ORC, read_orc() и to_orc(). Для этого требуется библиотека pyarrow.
Предупреждение
Сильно рекомендуется устанавливать pyarrow с помощью conda из-за проблем, возникающих при установке pyarrow.
to_orc()требует pyarrow>=7.0.0.read_orc()иto_orc()пока не поддерживаются в Windows, вы можете найти подходящие среды на странице установки дополнительных зависимостей.Список поддерживаемых типов данных см. в документации по поддерживаемым функциям ORC в Arrow.
В настоящее время часовые пояса в столбцах datetime не сохраняются при преобразовании фрейма данных в файлы ORC.
In [612]: df = pd.DataFrame(
.....: {
.....: "a": list("abc"),
.....: "b": list(range(1, 4)),
.....: "c": np.arange(4.0, 7.0, dtype="float64"),
.....: "d": [True, False, True],
.....: "e": pd.date_range("20130101", periods=3),
.....: }
.....: )
.....:
In [613]: df
Out[613]:
a b c d e
0 a 1 4.0 True 2013-01-01
1 b 2 5.0 False 2013-01-02
2 c 3 6.0 True 2013-01-03
In [614]: df.dtypes
Out[614]:
a object
b int64
c float64
d bool
e datetime64[ns]
dtype: object
Запись в файл orc.
In [615]: df.to_orc("example_pa.orc", engine="pyarrow")
Чтение из файла orc.
In [616]: result = pd.read_orc("example_pa.orc")
In [617]: result.dtypes
Out[617]:
a object
b int64
c float64
d bool
e datetime64[ns]
dtype: object
Чтение только определённых столбцов из файла orc.
In [618]: result = pd.read_orc(
.....: "example_pa.orc",
.....: columns=["a", "b"],
.....: )
.....:
In [619]: result.dtypes
Out[619]:
a object
b int64
dtype: object
SQL-запросы
Модуль pandas.io.sql предоставляет набор обёртки для запросов, что способствует извлечению данных и снижению зависимости от API конкретной СУБД. Абстракция базы данных предоставляется SQLAlchemy, если установлена. Кроме того, вам понадобится библиотека драйвера для вашей базы данных. Примерами таких драйверов являются psycopg2 для PostgreSQL или pymysql для MySQL. Для SQLite это включено в стандартной библиотеке Python по умолчанию. Вы можете найти обзор поддерживаемых драйверов для каждого SQL-диалекта в документации SQLAlchemy.
Если SQLAlchemy не установлена, кэширование предоставляется только для sqlite (и для mysql для обратной совместимости, но это устарело и будет удалено в будущей версии). Этот режим требует адаптер базы данных Python, который соответствует Python DB-API.
Также см. некоторые примеры кулинарной книги для некоторых продвинутых стратегий.
Основные функции:
| Чтение таблицы SQL базы данных в DataFrame. |
| Чтение SQL-запроса в DataFrame. |
| Чтение SQL-запроса или таблицы базы данных в DataFrame. |
| Запись записей, хранящихся в DataFrame, в базу данных SQL. |
Примечание
Функция read_sql() представляет собой удобную обёртку вокруг read_sql_table() и read_sql_query() (и для обратной совместимости) и будет делегировать конкретной функции в зависимости от предоставленного входного данных (имя таблицы базы данных или SQL-запрос). Имена таблиц не нужно заключать в кавычки, если они содержат специальные символы.
В следующем примере мы используем SQL-движок базы данных SQlite. Вы можете использовать временную базу данных SQLite, где данные хранятся в «памяти».
Для подключения с помощью SQLAlchemy используйте функцию create_engine() для создания объекта движка из URI базы данных. Вам нужно создать движок только один раз на каждую базу данных, к которой вы подключаетесь. Более подробную информацию о create_engine() и формате URI см. в примерах ниже и в документации SQLAlchemy.
In [620]: from sqlalchemy import create_engine
# Create your engine.
In [621]: engine = create_engine("sqlite:///:memory:")
Если вы хотите управлять собственными подключениями, вы можете передать их вместо этого. В примере ниже открывается подключение к базе данных с помощью менеджера контекста Python, который автоматически закрывает подключение после завершения блока. См. документацию SQLAlchemy для объяснения того, как обрабатывается подключение к базе данных.
with engine.connect() as conn, conn.begin():
data = pd.read_sql_table("data", conn)
Предупреждение
При открытии подключения к базе данных вы также несете ответственность за его закрытие. Побочные эффекты оставления подключения открытым могут включать блокировку базы данных или другое разрушающее поведение.
Запись DataFrame
Предполагая, что следующие данные находятся в DataFrame data, мы можем вставить их в базу данных, используя to_sql().
id | Дата | Col_1 | Col_2 | Col_3 |
|---|---|---|---|---|
26 | 2012-10-18 | X | 25.7 | True |
42 | 2012-10-19 | Y | -12.4 | False |
63 | 2012-10-20 | Z | 5.73 | True |
In [622]: import datetime
In [623]: c = ["id", "Date", "Col_1", "Col_2", "Col_3"]
In [624]: d = [
.....: (26, datetime.datetime(2010, 10, 18), "X", 27.5, True),
.....: (42, datetime.datetime(2010, 10, 19), "Y", -12.5, False),
.....: (63, datetime.datetime(2010, 10, 20), "Z", 5.73, True),
.....: ]
.....:
In [625]: data = pd.DataFrame(d, columns=c)
In [626]: data
Out[626]:
id Date Col_1 Col_2 Col_3
0 26 2010-10-18 X 27.50 True
1 42 2010-10-19 Y -12.50 False
2 63 2010-10-20 Z 5.73 True
In [627]: data.to_sql("data", engine)
Out[627]: 3
В некоторых базах данных запись больших DataFrame может привести к ошибкам из-за превышения ограничений размера пакета. Этому можно избежать, установив параметр chunksize при вызове to_sql. Например, следующее записывает data в базу данных партиями по 1000 строк за раз:
In [628]: data.to_sql("data_chunked", engine, chunksize=1000)
Out[628]: 3
Типы данных SQL
to_sql() попытается сопоставить ваши данные с соответствующим типом данных SQL на основе типа данных данных. Когда у вас есть столбцы типа object, pandas попытается определить тип данных.
Вы всегда можете переопределить тип по умолчанию, указав желаемый тип SQL любого из столбцов, используя аргумент dtype. Этот аргумент требует словарь, сопоставляющий имена столбцов с типами SQLAlchemy (или строки для режима кэширования sqlite3). Например, указание использования типа sqlalchemy String вместо типа по умолчанию Text для строковых столбцов:
In [629]: from sqlalchemy.types import String
In [630]: data.to_sql("data_dtype", engine, dtype={"Col_1": String})
Out[630]: 3
Примечание
Из-за ограниченной поддержки timedelta в различных вариантах баз данных столбцы с типом timedelta64 будут записаны как целые значения в наносекунды в базу данных, и будет выведено предупреждение.
Примечание
Столбцы типа category будут преобразованы в плотную форму представления, как вы получите с np.asarray(categorical) (например, для строковых категорий это даст массив строк). Из-за этого повторное чтение таблицы базы данных не генерирует категориальный.
Типы данных DateTime
Используя SQLAlchemy, to_sql() может записывать данные DateTime, которые являются локальными или с часовым поясом. Однако результирующие данные, хранящиеся в базе данных, в конечном итоге зависят от поддерживаемого типа данных для данных DateTime системы используемой базы данных.
В следующей таблице перечислены поддерживаемые типы данных для данных DateTime для некоторых распространённых баз данных. Другие диалекты баз данных могут иметь разные типы данных для данных DateTime.
База данных | Типы данных DateTime SQL | Поддержка часового пояса |
|---|---|---|
SQLite |
| Нет |
MySQL |
| Нет |
PostgreSQL |
| Да |
При записи данных с часовым поясом в базы данных, которые не поддерживают часовые пояса, данные будут записаны как локальные временные метки без часового пояса, которые находятся в местном времени по отношению к часовому поясу.
read_sql_table() также может считывать данные DateTime, которые являются осознанными или неосознанными в часовом поясе. При чтении TIMESTAMP WITH TIME ZONE типов pandas преобразует данные в UTC.
Метод вставки
Параметр method управляет SQL-командой вставки, используемой. Возможные значения:
None: Использование стандартной SQL-командыINSERT(одна на строку).'multi': Передача нескольких значений в одной командеINSERT. Она использует специальный синтаксис SQL, не поддерживаемый всеми бэкендами. Обычно это обеспечивает лучшую производительность для аналитических баз данных, таких как Presto и Redshift, но имеет худшую производительность для традиционного SQL бэкенда, если таблица содержит много столбцов. Для получения дополнительной информации ознакомьтесь с документацией SQLAlchemy .функция с сигнатурой
(pd_table, conn, keys, data_iter): Это можно использовать для реализации более эффективного метода вставки, основанного на функциях конкретного диалекта бэкенда.
Пример функции с использованием PostgreSQL команды COPY:
# Alternative to_sql() *method* for DBs that support COPY FROM
import csv
from io import StringIO
def psql_insert_copy(table, conn, keys, data_iter):
"""
Execute SQL statement inserting data
Parameters
----------
table : pandas.io.sql.SQLTable
conn : sqlalchemy.engine.Engine or sqlalchemy.engine.Connection
keys : list of str
Column names
data_iter : Iterable that iterates the values to be inserted
"""
# gets a DBAPI connection that can provide a cursor
dbapi_conn = conn.connection
with dbapi_conn.cursor() as cur:
s_buf = StringIO()
writer = csv.writer(s_buf)
writer.writerows(data_iter)
s_buf.seek(0)
columns = ', '.join(['"{}"'.format(k) for k in keys])
if table.schema:
table_name = '{}.{}'.format(table.schema, table.name)
else:
table_name = table.name
sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
table_name, columns)
cur.copy_expert(sql=sql, file=s_buf)
Чтение таблиц
read_sql_table() будет читать таблицу базы данных, заданную по имени таблицы и, необязательно, по подмножеству столбцов для чтения.
Примечание
Для использования read_sql_table(), необходимо установить необязательную зависимость SQLAlchemy.
In [631]: pd.read_sql_table("data", engine)
Out[631]:
index id Date Col_1 Col_2 Col_3
0 0 26 2010-10-18 X 27.50 True
1 1 42 2010-10-19 Y -12.50 False
2 2 63 2010-10-20 Z 5.73 True
Примечание
Обратите внимание, что pandas определяет типы данных столбцов из результатов запроса, а не из схемы физической базы данных. Например, предположим, что userid — это столбец целого типа данных в таблице. Тогда интуитивно select userid ... вернёт числовые серии, а select cast(userid as text) ... вернёт серии с типом данных объекта (строка). Соответственно, если результат запроса пустой, то все возвращаемые столбцы будут возвращены как серии с типом данных объекта (так как это наиболее общий случай). Если вы предполагаете, что ваш запрос иногда будет генерировать пустой результат, вы можете явно преобразовать типы данных впоследствии, чтобы обеспечить целостность типа данных.
Вы также можете указать имя столбца в качестве DataFrame индекса и указать подмножество столбцов для чтения.
In [632]: pd.read_sql_table("data", engine, index_col="id")
Out[632]:
index Date Col_1 Col_2 Col_3
id
26 0 2010-10-18 X 27.50 True
42 1 2010-10-19 Y -12.50 False
63 2 2010-10-20 Z 5.73 True
In [633]: pd.read_sql_table("data", engine, columns=["Col_1", "Col_2"])
Out[633]:
Col_1 Col_2
0 X 27.50
1 Y -12.50
2 Z 5.73
И вы можете явно принудительно преобразовать столбцы в даты:
In [634]: pd.read_sql_table("data", engine, parse_dates=["Date"])
Out[634]:
index id Date Col_1 Col_2 Col_3
0 0 26 2010-10-18 X 27.50 True
1 1 42 2010-10-19 Y -12.50 False
2 2 63 2010-10-20 Z 5.73 True
При необходимости вы можете явно указать строку формата или словарь аргументов для передачи в pandas.to_datetime():
pd.read_sql_table("data", engine, parse_dates={"Date": "%Y-%m-%d"})
pd.read_sql_table(
"data",
engine,
parse_dates={"Date": {"format": "%Y-%m-%d %H:%M:%S"}},
)
Вы можете проверить, существует ли таблица, используя has_table()
Поддержка схем
Чтение из и запись в разные схемы поддерживаются через ключевое слово schema в функциях read_sql_table() и to_sql(). Однако обратите внимание, что это зависит от диалекта базы данных (sqlite не имеет схем). Например:
df.to_sql("table", engine, schema="other_schema")
pd.read_sql_table("table", engine, schema="other_schema")
Запросы
Вы можете выполнять запросы с помощью простого SQL в функции read_sql_query(). В этом случае необходимо использовать вариант SQL, соответствующий вашей базе данных. При использовании SQLAlchemy вы также можете передавать конструкции языка SQLAlchemy Expression, которые являются независимыми от базы данных.
In [635]: pd.read_sql_query("SELECT * FROM data", engine)
Out[635]:
index id Date Col_1 Col_2 Col_3
0 0 26 2010-10-18 00:00:00.000000 X 27.50 1
1 1 42 2010-10-19 00:00:00.000000 Y -12.50 0
2 2 63 2010-10-20 00:00:00.000000 Z 5.73 1
Конечно, вы можете указать более «сложный» запрос.
In [636]: pd.read_sql_query("SELECT id, Col_1, Col_2 FROM data WHERE id = 42;", engine)
Out[636]:
id Col_1 Col_2
0 42 Y -12.5
Функция read_sql_query() поддерживает аргумент chunksize. Указание этого аргумента вернёт итератор по частям результата запроса:
In [637]: df = pd.DataFrame(np.random.randn(20, 3), columns=list("abc"))
In [638]: df.to_sql("data_chunks", engine, index=False)
Out[638]: 20
In [639]: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5):
.....: print(chunk)
.....:
a b c
0 0.070470 0.901320 0.937577
1 0.295770 1.420548 -0.005283
2 -1.518598 -0.730065 0.226497
3 -2.061465 0.632115 0.853619
4 2.719155 0.139018 0.214557
a b c
0 -1.538924 -0.366973 -0.748801
1 -0.478137 -1.559153 -3.097759
2 -2.320335 -0.221090 0.119763
3 0.608228 1.064810 -0.780506
4 -2.736887 0.143539 1.170191
a b c
0 -1.573076 0.075792 -1.722223
1 -0.774650 0.803627 0.221665
2 0.584637 0.147264 1.057825
3 -0.284136 0.912395 1.552808
4 0.189376 -0.109830 0.539341
a b c
0 0.592591 -0.155407 -1.356475
1 0.833837 1.524249 1.606722
2 -0.029487 -0.051359 1.700152
3 0.921484 -0.926347 0.979818
4 0.182380 -0.186376 0.049820
Вы также можете выполнить простой запрос без создания DataFrame с помощью execute(). Это полезно для запросов, которые не возвращают значения, таких как INSERT. Это функционально эквивалентно вызову execute для объекта двигателя SQLAlchemy или объекта подключения к базе данных. Опять же, необходимо использовать вариант синтаксиса SQL, соответствующий вашей базе данных.
from pandas.io import sql
sql.execute("SELECT * FROM table_name", engine)
sql.execute(
"INSERT INTO table_name VALUES(?, ?, ?)", engine, params=[("id", 1, 12.2, True)]
)
Примеры подключения к движку
Для подключения с помощью SQLAlchemy используйте функцию create_engine() для создания объекта двигателя из URI базы данных. Вам нужно создать двигатель только один раз на каждую базу данных, к которой вы подключаетесь.
from sqlalchemy import create_engine
engine = create_engine("postgresql://scott:tiger@localhost:5432/mydatabase")
engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")
engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")
engine = create_engine("mssql+pyodbc://mydsn")
# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")
# or absolute, starting with a slash:
engine = create_engine("sqlite:////absolute/path/to/foo.db")
Дополнительную информацию см. в примерах документации SQLAlchemy documentation
Расширенные запросы SQLAlchemy
Вы можете использовать конструкции SQLAlchemy для описания вашего запроса.
Используйте sqlalchemy.text() для указания параметров запроса нейтральным по отношению к бэкенду способом
In [640]: import sqlalchemy as sa
In [641]: pd.read_sql(
.....: sa.text("SELECT * FROM data where Col_1=:col1"), engine, params={"col1": "X"}
.....: )
.....:
Out[641]:
index id Date Col_1 Col_2 Col_3
0 0 26 2010-10-18 00:00:00.000000 X 27.5 1
Если у вас есть описание вашей базы данных с использованием SQLAlchemy, вы можете выразить условия where с помощью выражений SQLAlchemy
In [642]: metadata = sa.MetaData()
In [643]: data_table = sa.Table(
.....: "data",
.....: metadata,
.....: sa.Column("index", sa.Integer),
.....: sa.Column("Date", sa.DateTime),
.....: sa.Column("Col_1", sa.String),
.....: sa.Column("Col_2", sa.Float),
.....: sa.Column("Col_3", sa.Boolean),
.....: )
.....:
In [644]: pd.read_sql(sa.select([data_table]).where(data_table.c.Col_3 is True), engine)
Out[644]:
Empty DataFrame
Columns: [index, Date, Col_1, Col_2, Col_3]
Index: []
Вы можете комбинировать выражения SQLAlchemy с параметрами, передаваемыми в read_sql() с помощью sqlalchemy.bindparam()
In [645]: import datetime as dt
In [646]: expr = sa.select([data_table]).where(data_table.c.Date > sa.bindparam("date"))
In [647]: pd.read_sql(expr, engine, params={"date": dt.datetime(2010, 10, 18)})
Out[647]:
index Date Col_1 Col_2 Col_3
0 1 2010-10-19 Y -12.50 False
1 2 2010-10-20 Z 5.73 True
Падение на sqlite
Использование sqlite поддерживается без использования SQLAlchemy. Этот режим требует адаптера базы данных Python, который соблюдает Python DB-API.
Вы можете создать подключения следующим образом:
import sqlite3
con = sqlite3.connect(":memory:")
А затем выполнить следующие запросы:
data.to_sql("data", con)
pd.read_sql_query("SELECT * FROM data", con)
Google BigQuery
Предупреждение
Начиная с версии 0.20.0, pandas разделил поддержку Google BigQuery на отдельный пакет pandas-gbq. Вы можете pip install pandas-gbq , чтобы получить его.
Пакет pandas-gbq предоставляет функциональность для чтения/записи из Google BigQuery.
pandas интегрируется с этим внешним пакетом. Если pandas-gbq установлен, вы можете использовать методы pandas pd.read_gbq и DataFrame.to_gbq, которые вызовут соответствующие функции из pandas-gbq.
Полную документацию можно найти здесь.
Формат Stata
Запись в формате Stata
Метод to_stata() запишет DataFrame в файл .dta. Версия формата этого файла всегда 115 (Stata 12).
In [648]: df = pd.DataFrame(np.random.randn(10, 2), columns=list("AB"))
In [649]: df.to_stata("stata.dta")
Файлы данных Stata имеют ограниченную поддержку типов данных; только строки длиной 244 символа или меньше, int8, int16, int32, float32 и float64 могут быть сохранены в файлах .dta. Кроме того, Stata зарезервировал определенные значения для представления пропущенных данных. Экспорт значения, отличного от пропущенного, которое выходит за пределы разрешенного диапазона в Stata для определенного типа данных, приведет к повторной типизации переменной до следующего большего размера. Например, значения int8 ограничены диапазоном от -127 до 100 в Stata, поэтому переменные со значениями выше 100 вызовут преобразование в int16. Значения nan в плавающей точке хранятся как базовый тип пропущенных данных (. в Stata).
Примечание
Экспорт пропущенных значений для целочисленных типов данных невозможен.
Писатель Stata корректно обрабатывает другие типы данных, включая int64, bool, uint8, uint16, uint32 путём приведения к наименьшему поддерживаемому типу, который может представить данные. Например, данные типа uint8 будут преобразованы к типу int8, если все значения меньше 100 (верхняя граница для значений, не являющихся пропущенными, int8 данных в Stata), или, если значения выходят за этот диапазон, переменная преобразуется в int16.
Предупреждение
Преобразование из int64 в float64 может привести к потере точности, если значения int64 больше, чем 2**53.
Предупреждение
StataWriter и to_stata() поддерживают только строки фиксированной ширины, содержащие до 244 символов, что является ограничением, наложенным форматом файла dta версии 115. Попытка записать файлы Stata dta со строками длиной более 244 символов вызывает ошибку ValueError.
Чтение из формата Stata
Функция верхнего уровня read_stata прочитает файл dta и вернёт либо DataFrame, либо StataReader, которые могут быть использованы для постепенного чтения файла.
In [650]: pd.read_stata("stata.dta")
Out[650]:
index A B
0 0 -1.690072 0.405144
1 1 -1.511309 -1.531396
2 2 0.572698 -1.106845
3 3 -1.185859 0.174564
4 4 0.603797 -1.796129
5 5 -0.791679 1.173795
6 6 -0.277710 1.859988
7 7 -0.258413 1.251808
8 8 1.443262 0.441553
9 9 1.168163 -2.054946
Указание chunksize даёт экземпляр StataReader, который может использоваться для чтения chunksize строк из файла за раз. Объект StataReader может использоваться как итератор.
In [651]: with pd.read_stata("stata.dta", chunksize=3) as reader:
.....: for df in reader:
.....: print(df.shape)
.....:
(3, 3)
(3, 3)
(3, 3)
(1, 3)
Для более тонкого управления используйте iterator=True и укажите chunksize в каждом вызове read().
In [652]: with pd.read_stata("stata.dta", iterator=True) as reader:
.....: chunk1 = reader.read(5)
.....: chunk2 = reader.read(5)
.....:
В настоящее время index извлекается в качестве столбца.
Параметр convert_categoricals указывает, следует ли читать метки значений и использовать их для создания переменной Categorical из них. Метки значений также можно получить с помощью функции value_labels, которая требует вызова read() перед использованием.
Параметр convert_missing указывает, следует ли сохранять представления пропущенных значений в Stata. Если False (по умолчанию), пропущенные значения представлены как np.nan. Если True, пропущенные значения представлены с использованием объектов StataMissingValue, а столбцы, содержащие пропущенные значения, будут иметь тип данных object.
Примечание
read_stata() и StataReader поддерживают форматы .dta 113-115 (Stata 10-12), 117 (Stata 13) и 118 (Stata 14).
Примечание
Установка preserve_dtypes=False приведет к повышению типов данных до стандартных типов данных pandas: int64 для всех целочисленных типов и float64 для данных с плавающей запятой. По умолчанию типы данных Stata сохраняются при импорте.
Данные категорий
Данные Categorical могут экспортироваться в файлы данных Stata как данные с метками значений. Экспортируемые данные состоят из кодов категорий в качестве целочисленных значений и категорий в качестве меток значений. В Stata нет явного эквивалента Categorical, и информация о том, является ли переменная упорядоченной, теряется при экспорте.
Предупреждение
Stata поддерживает только строковые метки значений, поэтому str вызывается для категорий при экспорте данных. Экспорт Categorical переменных с категориями, отличными от строк, вызывает предупреждение и может привести к потере информации, если представления категорий str не уникальны.
Данные с метками аналогичным образом можно импортировать из файлов данных Stata как Categorical переменные с использованием ключевого аргумента convert_categoricals (True по умолчанию). Ключевой аргумент order_categoricals (True по умолчанию) определяет, являются ли импортированные Categorical переменные упорядоченными.
Примечание
При импорте категориальных данных значения переменных в файле данных Stata не сохраняются, поскольку Categorical переменные всегда используют целочисленные типы данных между -1 и n-1, где n — количество категорий. Если исходные значения в файле данных Stata требуются, их можно импортировать, установив convert_categoricals=False, что импортирует исходные данные (но не метки переменных). Исходные значения можно сопоставить с импортированными категориальными данными, поскольку существует простое соответствие между исходными значениями данных Stata и кодами категорий импортированных категориальных переменных: пропущенным значениям присваивается код -1, а наименьшему исходному значению присваивается 0, второму наименьшему — 1 и так далее, пока наибольшему исходному значению не будет присвоен код n-1.
Примечание
Stata поддерживает частично помеченные ряды. Эти ряды имеют метки значений для некоторых, но не всех значений данных. Импорт частично помеченного ряда создаст Categorical со строковыми категориями для помеченных значений и числовыми категориями для значений без метки.
Форматы SAS
Функция верхнего уровня read_sas() может читать (но не записывать) файлы SAS XPORT (.xpt) и (с версии v0.18.0) SAS7BDAT (.sas7bdat).
Файлы SAS содержат только два типа значений: текстовые ASCII и значения с плавающей точкой (обычно 8 байт, но иногда усеченные). Для файлов xport автоматического преобразования типов к целым числам, датам или категориям нет. Для файлов SAS7BDAT коды формата могут позволить автоматически преобразовывать переменные дат в даты. По умолчанию весь файл читается и возвращается как DataFrame.
Укажите chunksize или используйте iterator=True для получения объектов-читателей (XportReader или SAS7BDATReader ) для постепенного чтения файла. Объекты-читатели также содержат атрибуты с дополнительной информацией о файле и его переменных.
Чтение файла SAS7BDAT:
df = pd.read_sas("sas_data.sas7bdat")
Получите итератор и прочитайте файл XPORT по 100 000 строк за раз:
def do_something(chunk):
pass
with pd.read_sas("sas_xport.xpt", chunk=100000) as rdr:
for chunk in rdr:
do_something(chunk)
Спецификация формата файла xport доступна на веб-сайте SAS.
Для формата SAS7BDAT официальной документации нет.
Форматы SPSS
Новое в версии 0.25.0.
Функция верхнего уровня read_spss() может читать (но не записывать) файлы SPSS SAV (.sav) и ZSAV (.zsav).
Файлы SPSS содержат имена столбцов. По умолчанию весь файл считывается, категориальные столбцы преобразуются в pd.Categorical, и возвращается DataFrame со всеми столбцами.
Укажите параметр usecols для получения подмножества столбцов. Укажите convert_categoricals=False для предотвращения преобразования категориальных столбцов в pd.Categorical.
Чтение файла SPSS:
df = pd.read_spss("spss_data.sav")
Извлечение подмножества столбцов, содержащихся в usecols из файла SPSS, и предотвращение преобразования категориальных столбцов в pd.Categorical:
df = pd.read_spss(
"spss_data.sav",
usecols=["foo", "bar"],
convert_categoricals=False,
)
Дополнительную информацию о форматах файлов SAV и ZSAV можно найти здесь.
Другие форматы файлов
Сам pandas поддерживает работу только с ограниченным набором форматов файлов, которые напрямую соответствуют его модели табличных данных. Для чтения и записи других форматов файлов в pandas и из pandas мы рекомендуем использовать пакеты из более широкой среды.
netCDF
xarray предоставляет структуры данных, вдохновлённые pandas DataFrame, для работы с многомерными наборами данных, с уклоном в сторону формата файлов netCDF и удобным преобразованием в pandas и из него.
Соображения по производительности
Это неофициальное сравнение различных методов ввода/вывода, используя pandas 0.24.2. Время выполнения зависит от машины, и небольшие различия следует игнорировать.
In [1]: sz = 1000000
In [2]: df = pd.DataFrame({'A': np.random.randn(sz), 'B': [1] * sz})
In [3]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 2 columns):
A 1000000 non-null float64
B 1000000 non-null int64
dtypes: float64(1), int64(1)
memory usage: 15.3 MB
Ниже будут использоваться следующие тестовые функции для сравнения производительности нескольких методов ввода/вывода:
import numpy as np
import os
sz = 1000000
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})
sz = 1000000
np.random.seed(42)
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})
def test_sql_write(df):
if os.path.exists("test.sql"):
os.remove("test.sql")
sql_db = sqlite3.connect("test.sql")
df.to_sql(name="test_table", con=sql_db)
sql_db.close()
def test_sql_read():
sql_db = sqlite3.connect("test.sql")
pd.read_sql_query("select * from test_table", sql_db)
sql_db.close()
def test_hdf_fixed_write(df):
df.to_hdf("test_fixed.hdf", "test", mode="w")
def test_hdf_fixed_read():
pd.read_hdf("test_fixed.hdf", "test")
def test_hdf_fixed_write_compress(df):
df.to_hdf("test_fixed_compress.hdf", "test", mode="w", complib="blosc")
def test_hdf_fixed_read_compress():
pd.read_hdf("test_fixed_compress.hdf", "test")
def test_hdf_table_write(df):
df.to_hdf("test_table.hdf", "test", mode="w", format="table")
def test_hdf_table_read():
pd.read_hdf("test_table.hdf", "test")
def test_hdf_table_write_compress(df):
df.to_hdf(
"test_table_compress.hdf", "test", mode="w", complib="blosc", format="table"
)
def test_hdf_table_read_compress():
pd.read_hdf("test_table_compress.hdf", "test")
def test_csv_write(df):
df.to_csv("test.csv", mode="w")
def test_csv_read():
pd.read_csv("test.csv", index_col=0)
def test_feather_write(df):
df.to_feather("test.feather")
def test_feather_read():
pd.read_feather("test.feather")
def test_pickle_write(df):
df.to_pickle("test.pkl")
def test_pickle_read():
pd.read_pickle("test.pkl")
def test_pickle_write_compress(df):
df.to_pickle("test.pkl.compress", compression="xz")
def test_pickle_read_compress():
pd.read_pickle("test.pkl.compress", compression="xz")
def test_parquet_write(df):
df.to_parquet("test.parquet")
def test_parquet_read():
pd.read_parquet("test.parquet")
При записи, три самые быстрые функции — это test_feather_write, test_hdf_fixed_write и test_hdf_fixed_write_compress.
In [4]: %timeit test_sql_write(df)
3.29 s ± 43.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [5]: %timeit test_hdf_fixed_write(df)
19.4 ms ± 560 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [6]: %timeit test_hdf_fixed_write_compress(df)
19.6 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [7]: %timeit test_hdf_table_write(df)
449 ms ± 5.61 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [8]: %timeit test_hdf_table_write_compress(df)
448 ms ± 11.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [9]: %timeit test_csv_write(df)
3.66 s ± 26.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [10]: %timeit test_feather_write(df)
9.75 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [11]: %timeit test_pickle_write(df)
30.1 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [12]: %timeit test_pickle_write_compress(df)
4.29 s ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [13]: %timeit test_parquet_write(df)
67.6 ms ± 706 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
При чтении, три самые быстрые функции — это test_feather_read, test_pickle_read и test_hdf_fixed_read.
In [14]: %timeit test_sql_read()
1.77 s ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [15]: %timeit test_hdf_fixed_read()
19.4 ms ± 436 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [16]: %timeit test_hdf_fixed_read_compress()
19.5 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [17]: %timeit test_hdf_table_read()
38.6 ms ± 857 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [18]: %timeit test_hdf_table_read_compress()
38.8 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [19]: %timeit test_csv_read()
452 ms ± 9.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [20]: %timeit test_feather_read()
12.4 ms ± 99.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [21]: %timeit test_pickle_read()
18.4 ms ± 191 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [22]: %timeit test_pickle_read_compress()
915 ms ± 7.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [23]: %timeit test_parquet_read()
24.4 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Файлы test.pkl.compress, test.parquet и test.feather заняли наименьшее место на диске (в байтах).
29519500 Oct 10 06:45 test.csv
16000248 Oct 10 06:45 test.feather
8281983 Oct 10 06:49 test.parquet
16000857 Oct 10 06:47 test.pkl
7552144 Oct 10 06:48 test.pkl.compress
34816000 Oct 10 06:42 test.sql
24009288 Oct 10 06:43 test_fixed.hdf
24009288 Oct 10 06:43 test_fixed_compress.hdf
24458940 Oct 10 06:44 test_table.hdf
24458940 Oct 10 06:44 test_table_compress.hdf
© 2008–2022, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/1.5.0/user_guide/io.html
Комментарии и пустые строки
Игнорирование комментариев и пустых строк
Если параметр
commentуказан, то полностью прокомментированные строки будут проигнорированы. По умолчанию, также будут проигнорированы полностью пустые строки.Если
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использует номера строк (включая прокомментированные/пустые строки):Если и
header, иskiprowsуказаны,headerбудет относиться к концуskiprows. Например:In [76]: data = ( ....: "# empty\n" ....: "# second empty line\n" ....: "# third emptyline\n" ....: "X,Y,Z\n" ....: "1,2,3\n" ....: "A,B,C\n" ....: "1,2.,4.\n" ....: "5.,NaN,10.0\n" ....: ) ....: In [77]: print(data) # empty # second empty line # third emptyline X,Y,Z 1,2,3 A,B,C 1,2.,4. 5.,NaN,10.0 In [78]: pd.read_csv(StringIO(data), comment="#", skiprows=4, header=1) Out[78]: A B C 0 1.0 2.0 4.0 1 5.0 NaN 10.0Комментарии
Иногда в файл могут быть включены комментарии или метаданные:
In [79]: print(open("tmp.csv").read()) ID,level,category Patient1,123000,x # really unpleasant Patient2,23000,y # wouldn't take his medicine Patient3,1234018,z # awesomeПо умолчанию, парсер включает комментарии в выходные данные:
In [80]: df = pd.read_csv("tmp.csv") In [81]: df Out[81]: ID level category 0 Patient1 123000 x # really unpleasant 1 Patient2 23000 y # wouldn't take his medicine 2 Patient3 1234018 z # awesomeМы можем подавить комментарии, используя ключевое слово
comment:In [82]: df = pd.read_csv("tmp.csv", comment="#") In [83]: df Out[83]: ID level category 0 Patient1 123000 x 1 Patient2 23000 y 2 Patient3 1234018 z