IO 工具(文本、CSV、HDF5 等)#

pandas 的 I/O API 是一组顶级 reader 函数,通过 pandas.read_csv() 等方式访问,它们通常返回一个 pandas 对象。相应的 writer 函数是对象方法,通过 DataFrame.to_csv() 等方式访问。下表包含可用的 readerswriters

格式类型

数据描述

Reader

Writer

text

CSV

read_csv

to_csv

text

定宽文本文件

read_fwf

NA

text

JSON

read_json

to_json

text

HTML

read_html

to_html

text

LaTeX

Styler.to_latex

NA

text

XML

read_xml

to_xml

text

本地剪贴板

read_clipboard

to_clipboard

binary

MS Excel

read_excel

to_excel

binary

OpenDocument

read_excel

NA

binary

HDF5 Format

read_hdf

to_hdf

binary

Feather Format

read_feather

to_feather

binary

Parquet Format

read_parquet

to_parquet

binary

ORC Format

read_orc

to_orc

binary

Stata

read_stata

to_stata

binary

SAS

read_sas

NA

binary

SPSS

read_spss

NA

binary

Python Pickle Format

read_pickle

to_pickle

SQL

SQL

read_sql

to_sql

SQL

Google BigQuery;:ref:read_gbq<io.bigquery>;:ref:to_gbq<io.bigquery>

Here 是对其中一些 IO 方法的非正式性能比较。

备注

对于使用 StringIO 类的示例,请确保使用 from io import StringIO 来导入它(适用于 Python 3)。

CSV 和文本文件#

读取文本文件(也称为平面文件)的核心函数是 read_csv() 。有关一些高级策略,请参阅 cookbook

解析选项#

:func:`read_csv 接受以下常用参数:

基本#

filepath_or_buffer各种

可以是文件路径(str pathlib.Pathpy:py._path.local.LocalPath )、URL(包括 http, ftp,和 S3 位置),或任何具有 read() 方法的对象(例如,一个打开的文件或 StringIO )。

sepstr, 默认为

要使用的分隔符。如果 sep 是 None,C 引擎无法自动检测分隔符,但 Python 解析引擎可以,这意味着将使用后者,并通过 Python 内置的嗅探器 csv.Sniffer 自动检测分隔符。此外,长度大于 1 且与 '\s+' 不同的分隔符将被解释为正则表达式,并且也会强制使用 Python 解析引擎。请注意,正则表达式分隔符容易忽略带引号的数据。正则表达式示例:'\\r\\t'

delimiter : str, 默认为 Nonestr, 默认为

sep 的备用参数名称。

delim_whitespace布尔值, 默认为 False

指定是否将空白字符(例如 ' ''\t')用作分隔符。等价于设置 sep='\s+'。如果此选项设置为 True,则不应为 delimiter 参数传递任何内容。

列和索引位置及名称#

header : int 或 int 列表, 默认为 'infer'int 或 int 列表, 默认为

用作列名的行编号,以及数据的起始行。默认行为是推断列名:如果不传递任何名称,则行为与 header=0 相同,列名从文件的第一行推断;如果显式传递列名,则行为与 header=None 相同。显式传递 header=0 可以替换现有名称。

header 可以是整数列表,指定 MultiIndex 列的多行位置,例如 [0,1,3]。未指定的中间行将被跳过(例如,本例中的 2 将被跳过)。请注意,如果 skip_blank_lines=True,此参数将忽略注释行和空行,因此 header=0 表示数据的第一行,而不是文件的第一行。

names : 类数组, 默认为 None类数组, 默认为

要使用的列名列表。如果文件不包含标题行,则应显式传递 header=None。此列表中的重复项是不允许的。

index_col : int、str、int/str 序列或 False, 可选, 默认为 Noneint、str、int/str 序列或 False, 可选, 默认为

用作 DataFrame 行标签的列,可以是字符串名称或列索引。如果给出 int/str 序列,则使用 MultiIndex。

备注

index_col=False 可用于强制 pandas 将第一列用作索引,例如,当您有一个分隔符位于每行末尾的格式错误的文件的时。

默认值 None 指示 pandas 进行猜测。如果列标题行的字段数等于数据文件中数据行的字段数,则使用默认索引。如果大于,则使用靠前的列作为索引,使得数据行中剩余的字段数等于标题中的字段数。

使用标题后的第一行来确定列数,这些列将进入索引。如果后续行包含的列数少于第一行,则用 NaN 填充。

这可以通过 usecols 来避免。这可确保按原样获取列,并忽略尾部数据。

usecols : 类列表或可调用对象, 默认为 None类列表或可调用对象, 默认为

返回列的子集。如果类列表,则所有元素必须是位置的(即文档列中的整数索引)或字符串,对应于用户在 names 中提供的列名,或从文档标题行推断出的列名。如果给出了 names,则不考虑文档标题行。例如,有效的类列表 usecols 参数为 [0, 1, 2]['foo', 'bar', 'baz']

元素顺序将被忽略,因此 usecols=[0, 1][1, 0] 相同。要从 data 实例化一个保留元素顺序的 DataFrame,请为 ['foo', 'bar'] 顺序的列使用 pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']],或为 ['bar', 'foo'] 顺序的列使用 pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]

如果为可调用对象,则可调用函数将针对列名进行计算,返回可调用函数计算结果为 True 的名称:

使用此参数可显著提高解析速度并降低内存使用量(在使用 c engine 时)。Python engine 会先加载数据,然后再决定丢弃哪些列。

通用解析配置#

dtype : 类型名称或 dict of column -> type,默认为 None类型名称或 dict of column -> type,默认为

数据或列的数据类型。例如 {'a': np.float64, 'b': np.int32, 'c': 'Int64'}。使用 strobject 并结合适当的 na_values 设置来保留 dtype 而不进行解释。如果指定了 converters,它们将*代替* dtype 转换。

在 1.5.0 版本加入: 支持 defaultdict。可以指定 defaultdict 作为输入,其中 default 值决定未显式列出的列的 dtype。

dtype_backend{“numpy_nullable”, “pyarrow”},默认为 NumPy 支持的 DataFrame

要使用的 dtype_backend,例如,DataFrame 是否应具有 NumPy 数组,当设置为 “numpy_nullable” 时,可为空的 dtype 将用于所有具有可空实现的 dtype,如果设置为 “pyarrow”,则所有 dtype 都将使用 pyarrow。

dtype_backends 仍处于实验阶段。

在 2.0 版本加入.

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

要使用的解析引擎。C 和 pyarrow 引擎更快,而 python 引擎目前功能更完善。多线程目前仅受 pyarrow 引擎支持。

在 1.4.0 版本加入: “pyarrow” 引擎被添加为*实验性*引擎,某些功能对此引擎不被支持,或可能无法正常工作。

converters : dict,默认为 Nonedict,默认为

用于转换特定列中值的函数字典。键可以是整数或列标签。

true_values : list,默认为 Nonelist,默认为

被视为 True 的值。

false_values : list,默认为 Nonelist,默认为

被视为 False 的值。

skipinitialspace : boolean,默认为 Falseboolean,默认为

跳过分隔符后的空格。

skiprows : list-like or integer,默认为 Nonelist-like or integer,默认为

要跳过的行号(从 0 开始)或文件开头要跳过的行数(整数)。

如果为可调用对象,则可调用函数将针对行索引进行计算,返回 True(表示应跳过该行)或 False(表示不应跳过):

skipfooter : int,默认为 0int,默认为

要跳过文件底部的行数(使用 engine=’c’ 时不支持)。

nrows : int,默认为 Noneint,默认为

要读取的文件行数。读取大型文件的一部分时很有用。

low_memory : boolean,默认为 Trueboolean,默认为

将文件分块进行内部处理,从而在使用解析器时降低内存使用量,但可能会导致类型推断不一致。为确保没有类型不一致,请设置为 False,或使用 dtype 参数指定类型。请注意,整个文件仍会被读入单个 DataFrame 中,请使用 chunksizeiterator 参数以块的形式返回数据。(仅 C 解析器有效)

memory_map布尔值, 默认为 False

如果为 filepath_or_buffer 提供了文件路径,则将文件对象直接映射到内存中,并直接从中访问数据。使用此选项可以提高性能,因为不再存在 I/O 开销。

NA 和缺失数据处理#

na_values : scalar, str, list-like, or dict,默认为 Nonescalar, str, list-like, or dict,默认为

要识别为 NA/NaN 的附加字符串。如果传入 dict,则为特定于列的 NA 值。有关默认情况下被解释为 NaN 的值的列表,请参阅下面的 na values const

keep_default_na : boolean,默认为 Trueboolean,默认为

在解析数据时是否包含默认 NaN 值。根据是否传入 na_values,行为如下:

  • 如果 keep_default_naTrue,并且指定了 na_values,则会将 na_values 追加到用于解析的默认 NaN 值中。

  • 如果 keep_default_naTrue,并且未指定 na_values,则仅使用默认的 NaN 值进行解析。

  • 如果 keep_default_naFalse,并且指定了 na_values,则仅使用 na_values 中指定的 NaN 值进行解析。

  • 如果 keep_default_naFalse,并且未指定 na_values,则没有字符串会被解析为 NaN。

请注意,如果``na_filter``设置为``False``,则``keep_default_na``和``na_values``参数将被忽略。

na_filter : boolean, 默认 Trueboolean,默认为

检测缺失值标记(空字符串和``na_values``的值)。对于不包含任何 NA 的数据,设置``na_filter=False``可以提高读取大型文件的性能。

verbose : boolean, 默认 Falseboolean,默认为

指示在非数字列中放置的 NA 值的数量。

skip_blank_lines : boolean, 默认 Trueboolean,默认为

如果为 True,则跳过空白行,而不是将其解释为 NaN 值。

日期时间处理#

parse_dates : boolean 或 int 列表或名称列表或列表的列表或字典, 默认 Falseboolean 或 int 列表或名称列表或列表的列表或字典, 默认
  • 如果为 True -> 尝试解析索引。

  • 如果为 [1, 2, 3] -> 尝试将列 1、2、3 分别解析为日期列。

  • 如果为 [[1, 3]] -> 合并列 1 和 3 并解析为单个日期列。

  • 如果为 {'foo': [1, 3]} -> 将列 1、3 解析为日期并将其结果命名为 ‘foo’。

备注

对于 iso8601 格式的日期,存在一个快速路径。

infer_datetime_format : boolean, 默认 Falseboolean,默认为

如果为 True 并且为某列启用了 parse_dates,则尝试推断日期时间格式以加快处理速度。

自 2.0.0 版本弃用: 此参数的严格版本现在是默认设置,传递它没有效果。

keep_date_col : boolean, 默认 Falseboolean,默认为

如果为 True 并且 parse_dates 指定了合并多个列,则保留原始列。

date_parser : function, 默认 Nonefunction, 默认

用于将字符串列序列转换为日期时间实例数组的函数。默认使用 dateutil.parser.parser 进行转换。pandas 将尝试以三种不同的方式调用 date_parser,并在发生异常时前进到下一种:1)将一个或多个数组(由 parse_dates 定义)作为参数传递;2)将由 parse_dates 定义的列中的字符串值(逐行)连接成一个数组并传递;3)为每一行调用一次 date_parser,并将一个或多个字符串(对应于 parse_dates 定义的列)作为参数传递。

自 2.0.0 版本弃用: 请改用 date_format,或者读取为 object 然后根据需要应用 to_datetime()

date_format : str 或 dict of column -> format, 默认 Nonestr 或 dict of column -> format, 默认

如果与 parse_dates 结合使用,将根据此格式解析日期。对于更复杂的情况,请读取为 object 然后根据需要应用 to_datetime()

在 2.0.0 版本加入.

dayfirst : boolean, 默认 Falseboolean,默认为

DD/MM 格式的日期,国际和欧洲格式。

cache_datesboolean, 默认 True

如果为 True,则使用唯一的、已转换日期的缓存来应用日期时间转换。当解析重复的日期字符串(尤其是包含时区偏移的字符串)时,可能会显著加快速度。

迭代#

iterator : boolean, 默认 Falseboolean,默认为

返回 TextFileReader 对象以进行迭代或使用 get_chunk() 获取块。

chunksize : int, 默认 Noneint,默认为

返回 TextFileReader 对象以进行迭代。请参阅 iterating and chunking

引用、压缩和文件格式#

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

用于对磁盘数据进行即时解压缩。如果为 ‘infer’,则如果 filepath_or_buffer 是以 ‘.gz’, ‘.bz2’, ‘.zip’, ‘.xz’, ‘.zst’ 结尾的路径类,则使用 gzip, bz2, zip, xz, 或 zstandard,否则不进行解压缩。如果使用 ‘zip’,则 ZIP 文件必须只包含一个要读取的数据文件。设置为 None 表示不进行解压缩。也可以是一个字典,其中 ‘method’ 键设置为 {'zip', 'gzip', 'bz2', 'zstd'} 中的一个,其他键值对将被转发给 zipfile.ZipFile, gzip.GzipFile, bz2.BZ2File, 或 zstandard.ZstdDecompressor。例如,可以传递以下参数以实现更快的压缩并创建可重现的 gzip 存档:compression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1}

在 1.2.0 版本发生变更: 先前的版本会把 gzip 的字典条目传递给 gzip.open

thousands : str, 默认 Nonestr, 默认为

千位分隔符。

decimal : str, 默认 '.'str, 默认为

用于识别小数点的字符。例如,欧洲数据可使用 ','

float_precisionstring, 默认 None

指定 C 引擎用于浮点值的转换器。选项有:普通转换器的 None,高精度转换器的 high,以及往返转换器的 round_trip

lineterminator : str (长度为 1), 默认 Nonestr (长度为 1), 默认

用于将文件分割成行的字符。仅在使用 C 解析器时有效。

quotecharstr (长度为 1)

用于区分已引用的项的开始和结束的字符。已引用的项可能包含分隔符,但它们将被忽略。

quoting : int 或 csv.QUOTE_* 实例, 默认 0int 或

根据 csv.QUOTE_* 常量控制字段引用行为。使用 QUOTE_MINIMAL (0)、QUOTE_ALL (1)、QUOTE_NONNUMERIC (2) 或 QUOTE_NONE (3) 中的一个。

doublequote : boolean, 默认 Trueboolean,默认为

当指定了 quotecharquoting 不是 QUOTE_NONE 时,指示是否将字段 内部 的两个连续 quotechar 元素解释为单个 quotechar 元素。

escapechar : str (长度为 1), 默认 Nonestr (长度为 1), 默认

quotingQUOTE_NONE 时,用于转义分隔符的单字符字符串。

comment : str, 默认 Nonestr, 默认为

指示行的剩余部分不应被解析。如果出现在行首,则整行将被忽略。此参数必须是单个字符。与空行(只要 skip_blank_lines=True)类似,完全被注释的行会被 header 参数忽略,但不会被 skiprows 忽略。例如,如果 comment='#',那么使用 header=0 解析 ‘#empty\na,b,c\n1,2,3’ 将导致 ‘a,b,c’ 被视为标题行。

encoding : str, 默认 Nonestr, 默认为

读取/写入 UTF 时使用的编码(例如 'utf-8')。 List of Python standard encodings

dialect : str 或 csv.Dialect 实例, 默认 Nonestr 或

如果提供了此参数,它将覆盖以下参数的默认值(或非默认值):delimiterdoublequoteescapecharskipinitialspacequotecharquoting。如果需要覆盖这些值,将发出ParserWarning。有关更多详细信息,请参阅 csv.Dialect 文档。

错误处理#

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

指定遇到错误行(字段过多)时应采取的措施。允许的值为:

  • ‘error’,遇到错误行时引发 ParserError。

  • ‘warn’,遇到错误行时打印警告并跳过该行。

  • ‘skip’,遇到错误行时不引发错误或警告,直接跳过。

在 1.3.0 版本加入.

指定列数据类型#

您可以为整个 DataFrame 或单个列指定数据类型:

幸运的是,pandas 提供了不止一种方法来确保您的列只包含一种 dtype。如果您不熟悉这些概念,可以参阅 here 了解有关 dtype 的更多信息,以及 here 了解有关 pandas 中 object 转换的更多信息。

例如,您可以使用 read_csv()converters 参数:

或者,您可以在读取数据后使用 to_numeric() 函数强制转换 dtype,

这将把所有有效解析转换为浮点数,将无效解析保留为 NaN

最终,如何处理包含混合 dtype 的列取决于您的具体需求。在上述情况下,如果您想将数据异常值 NaN 化,那么 to_numeric() 可能是最佳选择。但是,如果您希望所有数据都被强制转换,无论类型如何,那么使用 read_csv()converters 参数绝对值得尝试。

备注

在某些情况下,读取包含混合 dtype 列的异常数据将导致数据集不一致。如果您依赖 pandas 来推断列的 dtype,解析引擎将为数据的不同块推断 dtype,而不是一次性为整个数据集推断。因此,您可能会得到包含混合 dtype 的列。例如,

这将导致 mixed_df 由于读取的数据中包含混合的数据类型,因此该列的部分块将具有 int 数据类型,而其他块将具有 str 数据类型。需要注意的是,整列将被标记为 dtypeobject,这是用于混合数据类型列的。

设置 dtype_backend="numpy_nullable" 将为所有列生成可为空的数据类型。

指定分类数据类型#

可以通过指定 dtype='category'dtype=CategoricalDtype(categories, ordered) 直接解析 Categorical 列。

可以使用字典规范将单个列解析为 Categorical

指定 dtype='category' 将生成无序的 Categorical,其 categories 是在数据中观察到的唯一值。为了更精细地控制类别和顺序,请提前创建一个 CategoricalDtype ,并将其传递给该列的 dtype

当使用 dtype=CategoricalDtype 时,“意外”的、不在 dtype.categories 之外的值将被视为缺失值。

这与 Categorical.set_categories() 的行为一致。

备注

使用 dtype='category' 时,生成的类别将始终作为字符串(object 类型)进行解析。如果类别是数值型的,可以使用 to_numeric() 函数或适当的另一个转换器(如 to_datetime() )进行转换。

dtype 是具有同质 categories``(全部为数值型、全部为日期时间型等)的 ``CategoricalDtype 时,转换会自动完成。

命名和使用列#

处理列名#

文件可能包含也可能不包含标题行。pandas 假设第一行应作为列名:

通过指定 names 参数并结合 header,可以指示使用其他名称以及是否丢弃标题行(如果有):

如果标题在第一行以外的某一行,请将行号传递给 header。这将跳过前面的行:

备注

默认行为是推断列名:如果未传递任何名称,则行为与 header=0 相同,并且列名是从文件的第一个非空白行推断出来的;如果显式传递了列名,则行为与 header=None 相同。

重复名称解析#

如果文件或标题包含重复的名称,pandas 默认会区分它们以防止数据被覆盖:

不再有重复数据,因为重复的列 ‘X’, …, ‘X’ 变成了 ‘X’, ‘X.1’, …, ‘X.N’。

筛选列(usecols#

usecols 参数允许您选择文件的任何列子集,可以使用列名、位置编号或可调用对象:

usecols 参数还可以用于指定最终结果中不使用的列:

在这种情况下,可调用对象指定了我们将“a”和“c”列排除在输出之外。

注释和空行#

忽略行注释和空行#

如果指定了 comment 参数,则将被忽略的完整注释行将被忽略。默认情况下,完全空白的行也会被忽略。

如果 skip_blank_lines=False,则 read_csv 将不会忽略空白行:

警告

被忽略的行的存在可能会在行号方面产生歧义;header 参数使用行号(忽略注释/空行),而 skiprows 使用行号(包括注释/空行):

如果同时指定了 headerskiprowsheader 将相对于 skiprows 的末尾计算。例如:

注释#

有时文件中可能包含注释或元数据:

默认情况下,解析器会将注释包含在输出中:

我们可以使用 comment 关键字来抑制注释:

处理 Unicode 数据#

对于编码后的 Unicode 数据,应使用 encoding 参数,这将导致字节字符串在结果中被解码为 Unicode:

某些以多字节对所有字符进行编码的格式(如 UTF-16)在不指定编码的情况下根本无法正确解析。Full list of Python standard encodings

索引列和尾部分隔符#

如果文件中的数据列比列名多一列,则第一列将被用作 DataFrame 的行名:

通常,您可以使用 index_col 选项来实现此行为。

当文件在每条数据行的末尾带有分隔符,从而混淆了解析器时,存在一些例外情况。要显式禁用索引列推断并丢弃最后一列,请传递 index_col=False

如果使用 usecols 选项解析数据子集,则 index_col 的指定将基于该子集,而不是原始数据。

日期处理#

指定日期列#

为了更好地处理 datetime 数据,read_csv() 使用关键字参数 parse_datesdate_format,允许用户指定各种列和日期/时间格式,将输入的文本数据转换为 datetime 对象。

最简单的情况是只传递 parse_dates=True

通常,我们可能希望单独存储日期和时间数据,或者单独存储各种日期字段。parse_dates 关键字可用于指定要从中解析日期和/或时间的列的组合。

您可以将列列表传递给 parse_dates,结果日期列将添加到输出的开头(以免影响现有的列顺序),新列名将是组件列名的连接:

默认情况下,解析器会删除组件日期列,但您可以通过 keep_date_col 关键字选择保留它们:

请注意,如果您想将多列合并为单个日期列,则必须使用嵌套列表。换句话说,parse_dates=[1, 2] 表示第二列和第三列应分别解析为单独的日期列,而 parse_dates=[[1, 2]] 表示这两列应解析为单个列。

您还可以使用字典来指定自定义名称列:

请务必记住,如果要将多个文本列解析为单个日期列,则会将一个新列添加到数据的开头。index_col 的指定是基于这个新的列集,而不是原始数据列:

备注

如果一列或索引包含无法解析的日期,则整个列或索引将以对象数据类型不变的形式返回。对于非标准日期时间解析,请在 pd.read_csv 之后使用 to_datetime()

备注

read_csv 为解析 iso8601 格式的日期时间字符串(例如 “2000-01-01T00:01:02+00:00” 及其变体)提供了一个快速路径。如果您能安排数据以这种格式存储日期时间,加载时间将显著加快,已观察到约 20 倍的提升。

自 2.2.0 版本弃用: 在 read_csv 内部合并日期列已被弃用。请改用相关结果列上的 pd.to_datetime

日期解析函数#

最后,解析器允许您指定自定义 date_format。在性能方面,您应按以下顺序尝试这些解析日期的方法:

  1. 如果您知道格式,请使用 date_format,例如:date_format="%d/%m/%Y"date_format={column_name: "%d/%m/%Y"}

  2. 如果您对不同列使用不同的格式,或者想将任何额外的选项(如 utc)传递给 to_datetime,则应将数据读取为 object dtype,然后使用 to_datetime

解析带有混合时区的 CSV#

pandas 无法原生表示带有混合时区的列或索引。如果您的 CSV 文件包含具有混合时区的列,即使使用了 parse_dates,默认结果也将是一个带有字符串的对象 dtype 列。要将混合时区的值解析为日期时间列,请将数据读取为 object dtype,然后使用 utc=True 调用 to_datetime()

推断日期时间格式#

以下是一些可以猜测的日期时间字符串示例(均表示 2011 年 12 月 30 日 00:00:00):

  • “20111230”

  • “2011/12/30”

  • “20111230 00:00:00”

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

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

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

请注意,格式推断对 dayfirst 很敏感。使用 dayfirst=True 时,它会猜测 “01/12/2011” 为 12 月 1 日。使用 ``dayfirst=False``(默认值)时,它会猜测 “01/12/2011” 为 1 月 12 日。

如果你尝试解析一个字符串日期列,pandas 会尝试从第一个非 NaN 元素中猜测格式,然后用该格式解析该列的其余部分。如果 pandas 无法猜测格式(例如,如果你的第一个字符串是 '01 December US/Pacific 2000'),则会引发警告,并且每一行将单独由 dateutil.parser.parse 进行解析。解析日期的最安全方法是显式设置 format=

如果同一列中存在混合的日期时间格式,你可以传递 format='mixed'

或者,如果你的日期时间格式都是 ISO8601(可能格式不完全相同):

国际日期格式#

虽然美国的日期格式倾向于 MM/DD/YYYY,但许多国际格式则使用 DD/MM/YYYY。为了方便起见,提供了一个 dayfirst 关键字:

将 CSV 写入二进制文件对象#

在 1.2.0 版本加入.

df.to_csv(..., mode="wb") 允许以二进制模式将 CSV 写入文件对象。在大多数情况下,无需指定 mode,因为 Pandas 会自动检测文件对象是已以文本模式还是二进制模式打开。

指定浮点数转换方法#

可以通过指定 float_precision 参数来使用特定的浮点数转换器,以便在使用 C 引擎进行解析时。选项包括普通转换器、高精度转换器和往返转换器(保证在写入文件后能够往返转换值)。例如:

千位分隔符#

对于带有千位分隔符的大数字,你可以将 thousands 关键字设置为长度为 1 的字符串,以便正确解析整数:

默认情况下,带有千位分隔符的数字将被解析为字符串:

thousands 关键字允许正确解析整数:

NA 值#

要控制哪些值被解析为缺失值(由 NaN 表示),请在 na_values 中指定一个字符串。如果你指定一个字符串列表,那么列表中的所有值都将被视为缺失值。如果你指定一个数字(一个 float,如 5.0 或一个 integer5),相应的等效值也将表示缺失值(在这种情况下,effectively [5.0, 5] 被识别为 NaN)。

要完全覆盖默认的被识别为缺失的值,请指定 keep_default_na=False

默认识别的 NaN 值包括 ['-1.#IND', '1.#QNAN', '1.#IND', '-1.#QNAN', '#N/A N/A', '#N/A', 'N/A', 'n/a', 'NA', '<NA>', '#NA', 'NULL', 'null', 'NaN', '-NaN', 'nan', '-nan', 'None', '']

让我们看一些例子:

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

在上面的示例中,除了默认值之外,55.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"])

上面,字符串 NA0 都是 NaN

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

默认值以及字符串 "Nope" 都被识别为 NaN

无穷大#

inf 类值将被解析为 np.inf``(正无穷大),-inf`` 将解析为 -np.inf``(负无穷大)。这些值会忽略大小写,这意味着 ``Inf 也将被解析为 np.inf

布尔值#

常见的 TrueFalseTRUEFALSE 值都被识别为布尔值。偶尔你可能希望识别其他值作为布尔值。为此,使用 true_valuesfalse_values 选项,如下所示:

处理“坏”行#

一些文件可能包含格式不正确的行,字段过多或过少。字段过少的行将在尾部字段中填充 NA 值。默认情况下,字段过多的行将引发错误:

你可以选择跳过坏行:

在 1.4.0 版本加入.

或者,如果 engine="python",可以将一个可调用函数传递给处理坏行。坏行将是一个被 sep 分割的字符串列表:

备注

可调用函数只会处理字段过多的行。由其他错误引起的坏行将被静默跳过。

在这种情况下,该行未被处理,因为这里的“坏行”是由转义字符引起的。

你还可以使用 usecols 参数来消除某些行但不出现在其他行中的多余列数据:

如果您想保留所有数据,包括字段过多的行,可以指定足够多的 names。这可确保字段不足的行用 NaN 填充。

方言#

dialect 关键字为指定文件格式提供了更大的灵活性。默认情况下,它使用 Excel 方言,但您可以指定方言名称或 csv.Dialect 实例。

假设您有未加引号的数据:

默认情况下,read_csv 使用 Excel 方言并将双引号视为引号字符,这会导致在找到匹配的双引号之前遇到换行符时出错。

我们可以使用 dialect 来解决这个问题:

所有方言选项都可以通过关键字参数单独指定:

另一个常见的方言选项是 skipinitialspace,它会跳过分隔符后的任何空格:

解析器会尽力“做正确的事”并且不脆弱。类型推断是一个非常重要的问题。如果一个列可以在不更改内容的情况下强制转换为 integer dtype,解析器就会这样做。任何非数字列都将像 pandas 对象一样以 object dtype 显示。

引号和转义字符#

嵌入字段中的引号(和其他转义字符)可以通过多种方式处理。一种方法是使用反斜杠;为了正确解析此数据,您应该传递 escapechar 选项:

固定宽度列的文件#

虽然 read_csv() 读取分隔数据,但 read_fwf() 函数处理具有已知固定列宽度的文件。read_fwf 的函数参数与 read_csv 大致相同,但增加了两个参数,并且 delimiter 参数的用法不同:

  • colspecs:一对(元组)列表,表示每行固定宽度字段的范围,为半开区间(即 [from, to[)。字符串值 ‘infer’ 可用于指示解析器从数据的前 100 行尝试检测列规范。如果未指定,则默认行为是推断。

  • widths:一组字段宽度,如果区间是连续的,则可代替 ‘colspecs’ 使用。

  • delimiter:要视为固定宽度文件中填充字符的字符。如果填充字符不是空格(例如 ‘~’),则可用于指定字段的填充字符。

考虑一个典型的固定宽度数据文件:

为了将此文件解析到 DataFrame 中,我们只需将列规范与文件名一起提供给 read_fwf 函数:

请注意,当指定 header=None 参数时,解析器会自动选择列名 X.<column number>。或者,您也可以仅为连续的列提供列宽度:

解析器会处理列周围的额外空格,因此文件中两列之间的额外间隔是可以接受的。

默认情况下,read_fwf 会尝试使用文件的前 100 行来推断文件的 colspecs。这仅在列对齐并通过提供的 ``delimiter``(默认分隔符为空白)正确分隔的情况下才有效。

read_fwf 支持 dtype 参数,用于指定解析后的列类型与推断的类型不同。

索引#

具有“隐式”索引列的文件#

考虑一个文件,其标题中的条目比数据列的数量少一个:

在这种特殊情况下,read_csv 假定第一列将用作 DataFrame 的索引:

请注意,日期未自动解析。在这种情况下,您需要像以前一样进行操作:

读取带有 MultiIndex 的索引#

假设您有由两列索引的数据:

read_csvindex_col 参数可以接受一个列号列表,将多个列转换为返回对象的索引的 MultiIndex

读取带有 MultiIndex 的列#

通过为 header 参数指定行位置列表,您可以读取列的 MultiIndex。指定非连续的行将跳过其中的行。

read_csv 还能解释更常见的多列索引格式。

备注

如果未指定 index_col``(例如,您没有索引,或者使用 ``df.to_csv(..., index=False) 写入),则列索引上的任何 names 都将*丢失*。

自动“嗅探”分隔符#

read_csv 能够推断分隔符(不一定是逗号分隔)文件,因为 pandas 使用 csv 模块的 csv.Sniffer 类。为此,您必须指定 sep=None

将多个文件读取到一个 DataFrame#

最好使用 concat() 来合并多个文件。请参阅 cookbook 中的示例。

分块迭代文件#

假设您希望惰性地迭代一个(可能非常大)的文件,而不是将整个文件读入内存,例如:

通过为 read_csv 指定 chunksize,返回值将是一个类型为 TextFileReader 的可迭代对象:

在 1.2 版本发生变更: read_csv/json/sas 在迭代文件时返回一个上下文管理器。

指定 iterator=True 也将返回 TextFileReader 对象:

指定解析器引擎#

Pandas 目前支持三种引擎:C 引擎、Python 引擎和实验性的 pyarrow 引擎(需要 pyarrow 包)。通常,pyarrow 引擎在较大的工作负载上速度最快,而在大多数其他工作负载上速度与 C 引擎相当。Python 引擎在大多数工作负载上往往比 pyarrow 和 C 引擎慢。然而,pyarrow 引擎的健壮性远不如 C 引擎,而 C 引擎缺少一些 Python 引擎的功能。

在可能的情况下,pandas 会使用 C 解析器(指定为 engine='c'),但如果指定了 C 不支持的选项,它可能会回退到 Python。

目前,C 和 pyarrow 引擎不支持的选项包括:

  • sep 非单个字符(例如,正则表达式分隔符)

  • skipfooter

  • sep=Nonedelim_whitespace=False

指定以上任何选项都将产生一个 ParserWarning,除非使用 engine='python' 显式选择 Python 引擎。

pyarrow 引擎不支持的、未包含在上述列表中的选项包括:

  • float_precision

  • chunksize

  • comment

  • nrows

  • thousands

  • memory_map

  • dialect

  • on_bad_lines

  • delim_whitespace

  • quoting

  • lineterminator

  • converters

  • decimal

  • iterator

  • dayfirst

  • infer_datetime_format

  • verbose

  • skipinitialspace

  • low_memory

使用 engine='pyarrow' 指定这些选项将引发 ValueError

读取/写入远程文件#

您可以将 URL 传递给 pandas 的许多 IO 函数来读取或写入远程文件 - 以下示例演示了读取 CSV 文件:

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

在 1.3.0 版本加入.

可以通过将标头键值映射字典传递给 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
)

所有非本地文件或 HTTP(s) 的 URL 都由 fsspec 处理(如果已安装),以及其各种文件系统实现(包括 Amazon S3、Google Cloud、SSH、FTP、webHDFS…)。其中一些实现需要安装额外的包,例如 S3 URL 需要 s3fs 库:

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

在处理远程存储系统时,您可能需要在环境变量或特殊位置的配置文件中进行额外的配置。例如,要访问 S3 存储桶中的数据,您需要在 S3Fs documentation 中列出的几种方式之一中定义凭证。许多存储后端也是如此,您应该遵循 fsimpl1 中指向 fsspec 中内置实现的链接,以及 fsimpl2 中指向未包含在主 fsspec 分发版中的实现的链接。

您还可以将参数直接传递给后端驱动程序。由于 fsspec 不使用 AWS_S3_HOST 环境变量,我们可以直接定义一个包含 endpoint_url 的字典,并将该对象传入 storage option 参数:

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

更多示例配置和文档可以在 S3Fs documentation 中找到。

如果您*不*具有 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 格式#

SeriesDataFrame 对象都有一个实例方法 to_csv,该方法允许将对象的内容存储为逗号分隔值文件。该函数接受多个参数。只有第一个是必需的。

  • path_or_buf:要写入的文件的字符串路径或文件对象。如果是文件对象,则必须使用 newline='' 打开

  • sep:输出文件的字段分隔符(默认为“,”)

  • na_rep:缺失值的字符串表示(默认为‘’)

  • float_format:浮点数的格式字符串

  • columns:要写入的列(默认为 None)

  • header:是否写入列名(默认为 True)

  • index:是否写入行(索引)名称(默认为 True)

  • index_label:如果需要,则为索引列的列标签。如果为 None(默认),并且 headerindex 为 True,则使用索引名称。(如果 DataFrame 使用 MultiIndex,则应给出序列)。

  • mode:Python 写入模式,默认为 ‘w’

  • encoding:在 Python 3 之前的版本中,如果内容不是 ASCII,则用于表示编码的字符串

  • lineterminator:表示行结束的字符序列(默认为 os.linesep

  • quoting:根据 csv 模块设置引用规则(默认为 csv.QUOTE_MINIMAL)。请注意,如果您设置了 float_format,则浮点数会转换为字符串,csv.QUOTE_NONNUMERIC 会将它们视为非数字

  • quotechar:用于引用字段的字符(默认为 ‘"’)

  • doublequote:控制字段中 quotechar 的引用(默认为 True)

  • escapechar:在适当的时候用于转义 sepquotechar 的字符(默认为 None)

  • chunksize:一次写入的行数

  • date_format:日期时间对象的格式字符串

写入格式化字符串#

DataFrame 对象有一个实例方法 to_string,该方法允许控制对象的字符串表示形式。所有参数都是可选的:

  • buf 默认为 None,例如 StringIO 对象

  • columns 默认为 None,要写入的列

  • col_space 默认为 None,每列的最小宽度。

  • na_rep 默认为 NaN,NA 值的表示形式

  • formatters 默认为 None,一个字典(按列)由函数组成,每个函数接受一个参数并返回一个格式化字符串

  • float_format 默认为 None,一个函数,它接受单个(浮点数)参数并返回一个格式化字符串;将应用于 DataFrame 中的浮点数。

  • sparsify 默认为 True,对于具有分层索引的 DataFrame,设置为 False 以在每行打印每个 MultiIndex 键。

  • index_names 默认为 True,将打印索引的名称

  • index 默认为 True,将打印索引(即行标签)

  • header 默认为 True,将打印列标签

  • justify 默认为 left,将左对齐或右对齐打印列标题

Series 对象也有一个 to_string 方法,但只有 bufna_repfloat_format 参数。还有一个 length 参数,如果设置为 True,还将输出 Series 的长度。

JSON#

读写 JSON 格式的文件和字符串。

写入 JSON#

SeriesDataFrame 可以转换为有效的 JSON 字符串。使用 to_json 和可选参数:

  • path_or_buf:用于写入输出的路径名或缓冲区。这可以是 None,在这种情况下将返回 JSON 字符串。

  • orient

    Series
    • 默认为 index

    • 允许的值为 {split, records, index}

    DataFrame
    • 默认为 columns

    • 允许的值为 {split, records, index, columns, values, table}

    JSON 字符串的格式

    split

    类似 {index -> [index]; columns -> [columns]; data -> [values]} 的字典

    records

    类似 [{column -> value}; … ] 的列表

    index

    类似 {index -> {column -> value}} 的字典

    columns

    类似 {column -> {index -> value}} 的字典

    values

    只有值的数组

    table

    遵循 JSON Table Schema

  • date_format : string, 日期转换的类型, ‘epoch’ 表示时间戳, ‘iso’ 表示 ISO8601。

  • double_precision : 编码浮点数值时使用的十进制位数, 默认为 10。

  • force_ascii : 强制编码字符串为 ASCII, 默认为 True。

  • date_unit : 编码到什么时间单位, 决定时间戳和 ISO8601 的精度。可以是 ‘s’, ‘ms’, ‘us’ 或 ‘ns’ (分别表示秒, 毫秒, 微秒和纳秒)。默认为 ‘ms’。

  • default_handler : 如果一个对象无法转换为适合 JSON 的格式时调用的处理器。接受单个参数, 即要转换的对象, 并返回一个可序列化的对象。

  • lines : 如果是 records 格式, 则将每条记录写入为单独的 JSON 行。

  • mode : string, 写入路径时的模式。’w’ 表示写入, ‘a’ 表示追加。默认为 ‘w’

注意 NaN, NaTNone 将被转换为 null, 而 datetime 对象将根据 date_formatdate_unit 参数进行转换。

格式选项#

生成的 JSON 文件/字符串的格式有多种选项。考虑以下 DataFrameSeries:

按列 (Column oriented) (DataFrame 的默认格式) 将数据序列化为嵌套的 JSON 对象, 以列标签作为主索引:

按索引 (Index oriented) (Series 的默认格式) 类似于按列格式, 但现在以索引标签为主:

按记录 (Record oriented) 将数据序列化为列 -> 值的记录 JSON 数组, 不包含索引标签。这对于将 DataFrame 数据传递给绘图库 (例如 JavaScript 库 d3.js) 非常有用:

按值 (Value oriented) 是一种基础选项, 序列化为仅包含值的嵌套 JSON 数组, 不包含列和索引标签:

按拆分 (Split oriented) 序列化为包含值、索引和列的独立条目的 JSON 对象。对于 Series 还会包含名称:

按表 (Table oriented) 序列化为 JSON Table Schema 允许保留包括但不限于 dtypes 和索引名称的元数据。

备注

任何编码为 JSON 对象的 orient 选项在进行往返序列化时都不会保留索引和列标签的顺序。如果您希望保留标签顺序, 请使用 split 选项,因为它使用有序容器。

日期处理#

以 ISO 日期格式写入:

以 ISO 日期格式写入, 包含微秒:

Epoch 时间戳, 以秒为单位:

写入文件, 包含日期索引和日期列:

回退行为#

如果 JSON 序列化器无法直接处理容器内容,它将按以下方式回退:

  • 如果 dtype 不受支持 (例如 np.complex_), 则调用提供的 default_handler 来处理每个值, 否则将引发异常。

  • 如果对象不受支持,它将尝试以下方法:

    • 检查对象是否定义了 toDict 方法并调用它。toDict 方法应返回一个 dict, 然后该 dict 将被 JSON 序列化。

    • 如果提供了 default_handler, 则调用它。

    • 通过遍历其内容将对象转换为 dict。但这通常会因 OverflowError 而失败, 或产生意外的结果。

总的来说, 对于不受支持的对象或 dtypes, 最佳方法是提供一个 default_handler。例如:

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

可以通过指定一个简单的 default_handler 来处理:

读取 JSON#

将 JSON 字符串读取到 pandas 对象可以接受多个参数。如果未提供 typtypNone, 解析器将尝试解析 DataFrame。要显式强制解析为 Series, 请传递 typ=series

  • filepath_or_buffer : 一个 有效 的 JSON 字符串或文件句柄/StringIO。该字符串可以是 URL。有效的 URL 方案包括 http、ftp、S3 和 file。对于文件 URL, 需要主机名。例如, 本地文件可以是 file ://localhost/path/to/table.json

  • typ : 要恢复的对象类型(系列或帧),默认为 ‘frame’

  • orient

    Series :
    • 默认为 index

    • 允许的值为 {split, records, index}

    DataFrame
    • 默认为 columns

    • 允许的值为 {split, records, index, columns, values, table}

    JSON 字符串的格式

    split

    类似 {index -> [index]; columns -> [columns]; data -> [values]} 的字典

    records

    类似列表的 [{column -> value} …]

    index

    类似 {index -> {column -> value}} 的字典

    columns

    类似 {column -> {index -> value}} 的字典

    values

    只有值的数组

    table

    遵循 JSON Table Schema

  • dtype : 如果为 True,则推断 dtypes;如果为列到 dtype 的字典,则使用这些 dtypes;如果为 False,则根本不推断 dtypes,默认为 True,仅应用于数据。

  • convert_axes : 布尔值,尝试将轴转换为正确的 dtype,默认为 True

  • convert_dates : 用于解析日期的列列表;如果为 True,则尝试解析日期类列,默认为 True

  • keep_default_dates : 布尔值,默认为 True。如果解析日期,则解析默认的日期类列。

  • precise_float : 布尔值,默认为 False。设置为 True 可在将字符串解码为双精度值时使用更高精度的 (strtod) 函数。默认值(False)是使用快速但不那么精确的内置功能。

  • date_unit : 字符串,用于检测日期转换时的戳单位。默认为 None。默认情况下将检测时间戳精度,如果不希望这样,可以传入 ‘s’、’ms’、’us’ 或 ‘ns’ 中的一个,以分别强制时间戳精度为秒、毫秒、微秒或纳秒。

  • lines : 将文件读取为每行一个 JSON 对象。

  • encoding : 用于解码 py3 字节的编码。

  • chunksize : 当与 lines=True 结合使用时,返回一个 pandas.api.typing.JsonReader,该读取器在每次迭代时读取 chunksize 行。

  • engine: 可以是 "ujson"``(内置 JSON 解析器)或 ``"pyarrow"``(将分派到 pyarrow ``pyarrow.json.read_json)。"pyarrow" 仅在 lines=True 时可用。

如果 JSON 无法解析,解析器将引发 ValueError/TypeError/AssertionError 之一。

如果编码为 JSON 时使用了非默认的 orient,请务必在此处也传递相同的选项,以便解码产生有意义的结果,请参阅 Orient Options 以获取概述。

数据转换#

convert_axes=Truedtype=Trueconvert_dates=True 的默认值将尝试将轴和所有数据解析为适当的类型,包括日期。如果需要覆盖特定的 dtype,请将字典传递给 dtype。仅当需要保留轴中的类字符串数字(例如 ‘1’、’2’)时,才应将 convert_axes 设置为 False

备注

如果 convert_dates=True,并且数据和/或列标签看起来是“日期类”的,那么较大的整数值可能会被转换为日期。确切的阈值取决于指定的 date_unit。’date-like’ 表示列标签满足以下任一条件:

  • 它以 '_at' 结尾

  • 它以 '_time' 结尾

  • 它以 'timestamp' 开头

  • 它是 'modified'

  • 它是 'date'

警告

读取 JSON 数据时,自动强制转换为 dtype 有一些怪癖:

  • 索引可以以不同于序列化的顺序重建,也就是说,返回的顺序不能保证与序列化之前相同。

  • 如果可以安全地进行转换,则 float 类型的数据列将被转换为 integer,例如,值为 1. 的列。

  • 布尔列在重建时将被转换为 integer

因此,有时您可能希望通过 dtype 关键字参数指定特定的 dtype。

从 JSON 字符串读取:

从文件读取:

不转换任何数据(但仍转换轴和日期):

为转换指定 dtype:

保留字符串索引:

以纳秒为单位写入的日期需要以纳秒为单位读回:

通过设置 dtype_backend 参数,您可以控制生成的 DataFrame 使用的默认 dtype。

归一化#

pandas 提供了一个实用函数,用于将字典或字典列表 归一化 为扁平表。

max_level 参数可提供对归一化结束级别的更多控制。使用 max_level=1,以下代码段将归一化直到提供的字典的第一个嵌套级别。

逐行分隔的 JSON#

pandas 能够读取和写入在数据处理管道(如 Hadoop 或 Spark)中常见的逐行分隔的 JSON 文件。

对于逐行分隔的 JSON 文件,pandas 还可以返回一个迭代器,该迭代器一次读取 chunksize 行。这对于大文件或从流中读取很有用。

通过指定 engine="pyarrow",也可以使用 pyarrow 读取器读取行限制的 json。

在 2.0.0 版本加入.

表格模式#

Table Schema 是一个描述表格数据集为 JSON 对象的规范。该 JSON 包含字段名、类型和其他属性的信息。您可以使用 orienttable 来构建一个包含 schemadata 两个字段的 JSON 字符串。

schema 字段包含 fields 键,该键本身包含一个列名到类型的配对列表,包括 IndexMultiIndex``(类型列表请参见下文)。如果(Multi)索引是唯一的,``schema 字段还包含一个 primaryKey 字段。

第二个字段 data 包含以 records orient 序列化的数据。索引包含在内,并且所有日期时间都按照 Table Schema 规范的要求进行 ISO 8601 格式化。

支持的类型完整列表在 Table Schema 规范中有描述。下表显示了与 pandas 类型的映射关系:

pandas 类型

Table Schema 类型

int64

integer

float64

number

bool

boolean

datetime64[ns]

datetime

timedelta64[ns]

duration

categorical

any

object

str

关于生成的表格模式有几点说明:

  • schema 对象包含一个 pandas_version 字段。该字段包含 pandas 模式方言的版本,每次修订都会递增。

  • 所有日期在序列化时都会转换为 UTC。即使是时区不敏感的值,也会被视为 UTC,偏移量为 0。

  • 带有(序列化前的)时区的日期时间会包含一个额外的 tz 字段,其中包含时区名称(例如 'US/Central')。

  • Period(周期)在序列化前被转换为时间戳,因此具有转换为 UTC 的相同行为。此外,Period还将包含一个额外的 freq 字段,其中包含周期的频率,例如 'A-DEC'

  • Categorical(分类)使用 any 类型和 enum 约束,该约束列出了所有可能值的集合。此外,还包含一个 ordered 字段:

  • 如果索引是唯一的,则会包含一个包含标签数组的 primaryKey 字段:

  • 对于 MultiIndexes,primaryKey 的行为相同,但在此情况下 primaryKey 是一个数组:

  • 默认命名大致遵循以下规则:

    • 对于 Series,使用 object.name。如果该值为 none,则名称为 values

    • 对于 DataFrame,使用列名的字符串化版本。

    • 对于 Index``(非 ``MultiIndex),使用 index.name,如果该值为 None,则回退到 index

    • 对于 MultiIndex,使用 mi.names。如果任何层没有名称,则使用 level_<i>

read_json 也接受 orient='table' 作为参数。这允许以可往返的方式保留 dtype 和索引名称等元数据。

请注意,作为 Index 名称的字面字符串 ‘index’ 是不可往返的,同样,MultiIndex 中任何以 'level_' 开头的名称也是不可往返的。这些名称在 DataFrame.to_json() 中被用作默认值来指示缺失值,随后的读取无法区分其意图。

当使用 orient='table' 以及用户定义的 ExtensionArray 时,生成的 schema 将在相应的 fields 元素中包含一个额外的 extDtype 键。这个额外的键不是标准的,但确实支持扩展类型的 JSON 往返(例如,read_json(df.to_json(orient="table"), orient="table"))。

extDtype 键携带扩展的名称,如果您已正确注册了 ExtensionDtype,pandas 将使用该名称在注册表中查找并将其序列化数据重新转换为自定义 dtype。

HTML#

读取 HTML 内容#

警告

我们**强烈建议**您阅读下面的 HTML Table Parsing gotchas ,了解围绕 BeautifulSoup4/html5lib/lxml 解析器的相关问题。

顶级的 read_html() 函数可以接受 HTML 字符串/文件/URL,并将 HTML 表格解析为 pandas DataFrame 的列表。让我们看几个例子。

备注

read_json 返回一个 list 对象,即使 HTML 内容中只包含一个表格。

读取一个没有选项的 URL:

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

 [563 rows x 7 columns]]

备注

上方的 URL 中的数据每周一都会更改,因此上方显示的结果数据可能会略有不同。

在发送 HTTP 请求时,可以传递标头来读取 URL:

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

备注

如上所示,我们传递的标头已反映在 HTTP 请求中。

读取上方 URL 中文件的内容,并将其作为字符串传递给 read_html

如果需要,您甚至可以传递 StringIO 的实例:

备注

以下示例未由 IPython 评估器运行,因为有太多网络访问函数会减慢文档构建速度。如果您发现错误或示例无法运行,请随时在 pandas GitHub issues page 上报告。

读取 URL 并匹配包含特定文本的表格:

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

指定标题行(默认情况下,<thead> 内的 <th><td> 元素用于构成列索引,如果 <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?oldid=899173761"
dfs = pd.read_html(
    url_mcc,
    match="Telekom Albania",
    header=0,
    converters={"MNC": str},
)

组合使用上述选项:

dfs = pd.read_html(url, match="Metcalf Bank", index_col=0)

读取 pandas to_html 输出(可能损失一些浮点精度):

df = pd.DataFrame(np.random.randn(2, 2))
s = df.to_html(float_format="{0:.40g}".format)
dfin = pd.read_html(s, index_col=0)

如果未提供其他解析器,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" 可以从单元格中提取链接及其文本。

在 1.5.0 版本加入.

写入 HTML 文件#

DataFrame 对象有一个实例方法 to_html,该方法将 DataFrame 的内容渲染为 HTML 表格。该函数参数与上面描述的 to_string 方法相同。

备注

为简洁起见,此处未显示 DataFrame.to_html 的所有可能选项。请参阅 DataFrame.to_html() 了解完整选项。

备注

在支持 HTML 渲染的环境(如 Jupyter Notebook)中,display(HTML(...))` 会将原始 HTML 渲染到环境中。

columns 参数将限制显示的列:

float_format 接受一个 Python 可调用对象来控制浮点值的精度:

bold_rows 默认会使行标签变粗,但您可以将其关闭:

classes 参数允许为生成的 HTML 表格提供 CSS 类。请注意,这些类会**附加**到现有的 'dataframe' 类上。

render_links 参数允许为包含 URL 的单元格添加超链接。

最后,escape 参数允许您控制是否在生成的 HTML 中转义 “<”、”>” 和 “&” 字符(默认为 True)。因此,要获取未转义字符的 HTML,请传递 escape=False

转义的:

未转义的:

备注

某些浏览器可能不会显示前两个 HTML 表格渲染的差异。

HTML 表格解析的注意事项#

用于解析 pandas io 顶层函数 read_html 中 HTML 表格的库存在一些版本问题。

**lxml 的问题**|_

  • 优点

    • lxml 速度非常快。

    • lxml 需要 Cython 才能正确安装。

  • 缺点

    • lxml **不**保证其解析结果,除非提供了 svm

    • 鉴于上述情况,我们选择允许您(用户)使用 lxml 后端,但**该后端将在 lxml 无法解析时使用** html5lib

    • 因此,*强烈建议*您同时安装 BeautifulSoup4html5lib, 这样即使 lxml 失败,您仍然会得到有效的结果(前提是其他一切都有效)。

使用 lxml 作为后端处理 BeautifulSoup4 时遇到的问题

  • 上述问题在这里同样适用,因为 BeautifulSoup4 本质上只是一个解析器后端的包装器。

使用 html5lib 作为后端处理 BeautifulSoup4 时遇到的问题

  • 优点

    • html5liblxml 更加宽松,因此能够以更合理的方式处理*实际的标记*,而不是仅仅丢弃一个元素而不通知您。

    • html5lib 会*自动从无效标记生成有效的 HTML5 标记*。这对于解析 HTML 表格来说非常重要,因为它保证了一个有效的文档。但是,这*并不意味着*它是“正确”的,因为修复标记的过程没有单一的定义。

    • html5lib 是纯 Python 实现的,除了其自身的安装之外,不需要额外的构建步骤。

  • 缺点

    • 使用 html5lib 的最大缺点是它非常慢。然而,考虑到网络上的许多表格并不大,解析算法的运行时间不太重要。更可能成为瓶颈的是从网络 URL 读取原始文本的过程,即 IO(输入-输出)。对于非常大的表格,情况可能并非如此。

LaTeX#

在 1.3.0 版本加入.

目前没有从 LaTeX 读取的方法,只有输出方法。

写入 LaTeX 文件#

备注

DataFrame Styler 对象目前都有一个 to_latex 方法。我们建议使用 Styler.to_latex() 方法而不是 DataFrame.to_latex() 方法,因为前者在条件样式方面具有更大的灵活性,而后者可能会在将来被弃用。

请查阅 Styler.to_latex 的文档,其中提供了条件样式的示例,并解释了其关键字参数的操作。

对于简单的应用,以下模式就足够了。

要在输出前格式化值,请链接 Styler.format 方法。

XML#

读取 XML#

在 1.3.0 版本加入.

顶级的 read_xml() 函数可以接受 XML 字符串/文件/URL,并将节点和属性解析为 pandas DataFrame

备注

由于 XML 结构没有标准,并且设计类型可能以多种方式变化,read_xml 对于更扁平、浅层的版本效果最好。如果 XML 文档嵌套很深,请使用 stylesheet 功能将 XML 转换为更扁平的版本。

我们来看几个例子。

读取 XML 字符串:

读取一个没有选项的 URL:

读取 “books.xml” 文件的内容,并将其作为字符串传递给 read_xml

将 “books.xml” 的内容作为 StringIOBytesIO 的实例读取,并将其传递给 read_xml

甚至可以从 AWS S3 存储桶读取 XML,例如 NIH NCBI PMC Article Datasets,它提供了生物医学和生命科学期刊:

使用 lxml 作为默认的 parser,您可以访问功能齐全的 XML 库,该库扩展了 Python 的 ElementTree API。一个强大的工具是能够使用更具表现力的 XPath 选择性或有条件地查询节点:

指定仅解析元素或仅解析属性:

XML 文档可以包含带有前缀的命名空间,以及不带前缀的默认命名空间,这两者都用特殊的 xmlns 属性表示。为了在命名空间上下文中按节点解析,xpath 必须引用一个前缀。

例如,下面的 XML 包含一个带有前缀 doc 和 URI https://example.com 的命名空间。为了解析 doc:row 节点,必须使用 namespaces

同样,XML 文档可以有一个不带前缀的默认命名空间。如果未能分配临时前缀,将不会返回任何节点并引发 ValueError。但为正确的 URI 分配*任何*临时名称后,即可按节点进行解析。

然而,如果 XPath 不引用节点名称,例如默认的 /*,则不需要 namespaces

备注

由于 xpath 标识了要解析的内容的父级,因此只解析直接子项(包括子节点或当前属性)。因此,read_xml 不会解析孙子节点或其他后代节点的文本,也不会解析任何后代节点的属性。要检索更低级别的內容,请将 xpath 调整到更低的级别。例如:

显示 shape 元素上的 sides 属性未能按预期解析,因为该属性位于 row 元素的子元素上,而不是 row 元素本身。换句话说,sides 属性是 row 元素的孙子级后代。然而,xpath 目标是 row 元素,仅包含其子元素和属性。

使用 lxml 作为解析器,您可以使用 XSLT 脚本来展平嵌套的 XML 文档,XSLT 脚本也可以是字符串/文件/URL 类型。作为背景知识,XSLT 是一种特殊的语言,写在特殊的 XML 文件中,它可以使用 XSLT 处理器将原始 XML 文档转换为其他的 XML、HTML,甚至是文本(CSV、JSON 等)。

例如,考虑以下芝加哥 “L” 线路的结构,其中 station 和 rides 元素在各自的 section 中封装数据。通过下面的 XSLT,lxml 可以将原始的嵌套文档转换为更平坦的输出(如下所示以作演示),以便更容易地解析到 DataFrame 中:

对于可能达到数百兆字节到千兆字节的非常大的 XML 文件,pandas.read_xml() 支持使用 lxml’s iterparseetree’s iterparse 来解析这些庞大的文件,这些是内存高效的方法,可以迭代 XML 树并提取特定元素和属性,而无需将整个树保留在内存中。

在 1.5.0 版本加入.

要使用此功能,您必须将物理 XML 文件路径传递给 read_xml 并使用 iterparse 参数。文件不应被压缩或指向在线源,而应存储在本地磁盘上。此外,iterparse 应为一个字典,其中键是文档中的重复节点(将成为行),值是重复节点(即子节点、孙子节点)的任何元素或属性的列表。由于此方法不使用 XPath,因此后代不需要彼此之间共享相同的关系。下面显示了读取维基百科非常大(12GB+)的最新文章数据转储的示例。

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:

写入带有新的根和行名称的 XML:

写入面向属性的 XML:

写入元素和属性的混合:

任何具有分层列的 DataFrame 都将被展平,XML 元素名称的级别将用下划线分隔:

写入带有默认命名空间的 XML:

写入带有命名空间前缀的 XML:

写入没有声明或美化打印的 XML:

写入 XML 并使用样式表进行转换:

XML 最终说明#

  • 所有 XML 文档都符合 W3C specificationsetreelxml 解析器都将无法解析任何格式不正确或不遵循 XML 语法规则的标记文档。请注意,HTML 不是 XML 文档,除非它遵循 XHTML 规范。然而,包括 KML、XAML、RSS、MusicML、MathML 在内的其他流行的标记类型都符合 XML schemas

  • 因此,如果您的应用程序在进行 Pandas 操作之前构建 XML,请使用适当的 DOM 库,如 etreelxml 来构建必要的文档,而不是通过字符串连接或正则表达式调整。始终记住 XML 是一个*特殊*的文本文件,有其标记规则。

  • 对于非常大的 XML 文件(几百兆字节到千兆字节),XPath 和 XSLT 可能会成为内存密集型操作。请确保您有足够的可用 RAM 来读取和写入大型 XML 文件(大约是文本大小的 5 倍)。

  • 由于 XSLT 是一种编程语言,请谨慎使用它,因为此类脚本可能会对您的环境造成安全风险,并可能运行大型或无限递归操作。始终在小片段上测试脚本,然后再全面运行。

  • The etree parser supports all functionality of both read_xml and to_xml except for complex XPath and any XSLT. Though limited in features, etree is still a reliable and capable parser and tree builder. Its performance may trail lxml to a certain degree for larger files but relatively unnoticeable on small to medium size files.

Excel 文件#

read_excel() 方法可以使用 openpyxl Python 模块读取 Excel 2007+ (.xlsx) 文件。Excel 2003 (.xls) 文件可以使用 xlrd 读取。二进制 Excel (.xlsb) 文件可以使用 pyxlsb 读取。所有格式都可以使用 calamine 引擎读取。 to_excel() 实例方法用于将 DataFrame 保存到 Excel。通常其语义与处理 csv 数据类似。有关一些高级策略,请参阅 cookbook

备注

engine=None 时,将使用以下逻辑来确定引擎:

  • 如果 path_or_buffer 是 OpenDocument 格式 (.odf, .ods, .odt),则将使用 odf

  • 否则,如果 path_or_buffer 是 xls 格式,则将使用 xlrd

  • 否则,如果 path_or_buffer 是 xlsb 格式,则将使用 pyxlsb

  • 否则,将使用 openpyxl

读取 Excel 文件#

在最基本的使用场景中,read_excel 接受一个 Excel 文件的路径,以及指示要解析哪个工作表的 sheet_name

使用 engine_kwargs 参数时,pandas 会将这些参数传递给引擎。因此,了解 pandas 在内部使用哪个引擎非常重要。

  • 对于 openpyxl 引擎,pandas 使用 openpyxl.load_workbook() 读取 (.xlsx) 和 (.xlsm) 文件。

  • 对于 xlrd 引擎,pandas 使用 xlrd.open_workbook() 读取 (.xls) 文件。

  • 对于 pyxlsb 引擎,pandas 使用 pyxlsb.open_workbook() 读取 (.xlsb) 文件。

  • 对于 odf 引擎,pandas 使用 odf.opendocument.load() 读取 (.ods) 文件。

  • 对于 calamine 引擎,pandas 使用 python_calamine.load_workbook() 读取 (.xlsx)、(.xlsm)、(.xls)、(.xlsb)、(.ods) 文件。

# Returns a DataFrame
pd.read_excel("path_to_file.xls", sheet_name="Sheet1")

ExcelFile#

为了方便地处理同一文件中的多个工作表,可以使用 ExcelFile 类来包装文件,并将其传递给 read_excel。由于文件只读取一次到内存中,这对于读取多个工作表可以带来性能上的提升。

xlsx = pd.ExcelFile("path_to_file.xls")
df = pd.read_excel(xlsx, "Sheet1")

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

通过将 sheet_name 设置为工作表名称列表、工作表位置列表或 None``(读取所有工作表),``read_excel 可以读取多个工作表。可以使用整数或字符串分别通过工作表索引或工作表名称来指定工作表。

读取 MultiIndex#

read_excel 可以通过向 index_col 传递列列表来读取 MultiIndex 索引,并通过向 header 传递行列表来读取 MultiIndex 列。如果 indexcolumns 具有序列化的级别名称,则通过指定构成级别的行/列来读取它们。

例如,读取未命名的 MultiIndex 索引:

如果索引具有级别名称,它们也将被解析,使用相同的参数。

如果源文件同时包含 MultiIndex 索引和列,应将指定每个的列表传递给 index_colheader

index_col 指定的列中的缺失值将被向前填充,以允许与 merged_cells=Trueto_excel 进行往返。要避免向前填充缺失值,请在读取数据后使用 set_index 而不是 index_col

解析特定列#

用户经常会在 Excel 中插入列进行临时计算,而您可能不想读取这些列。read_excel 接受一个 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 关键字将这些字符串解析为日期时间:

pd.read_excel("path_to_file.xls", "Sheet1", parse_dates=["date_strings"])

单元格转换器#

可以通过 converters 选项转换 Excel 单元格的内容。例如,将一列转换为布尔值:

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 关键字指定整列的类型,该关键字接受一个映射列名到类型的字典。要解释无类型推断的数据,请使用 strobject 类型。

pd.read_excel("path_to_file.xls", dtype={"MyInts": "int64", "MyText": str})

写入 Excel 文件#

将 Excel 文件写入磁盘#

要将 DataFrame 对象写入 Excel 文件的某个工作表,您可以使用 to_excel 实例方法。参数大部分与上面描述的 to_csv 相同,第一个参数是 Excel 文件的名称,可选的第二个参数是要写入 DataFrame 的工作表的名称。例如:

df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")

扩展名为 .xlsx 的文件将使用 xlsxwriter``(如果可用)或 ``openpyxl 写入。

DataFrame 的写入方式会尽量模仿 REPL 输出。index_label 将放置在第二行而不是第一行。通过在 to_excel() 中将 merge_cells 选项设置为 False,您可以将其放置在第一行:

df.to_excel("path_to_file.xlsx", index_label="label", merge_cells=False)

为了将不同的 DataFrame 写入单个 Excel 文件中的不同工作表,可以使用 ExcelWriter

with pd.ExcelWriter("path_to_file.xlsx") as writer:
    df1.to_excel(writer, sheet_name="Sheet1")
    df2.to_excel(writer, sheet_name="Sheet2")

使用 engine_kwargs 参数时,pandas 会将这些参数传递给引擎。因此,了解 pandas 在内部使用哪个引擎非常重要。

  • 对于 openpyxl 引擎,pandas 使用 openpyxl.Workbook() 创建新工作表,使用 openpyxl.load_workbook() 向现有工作表追加数据。openpyxl 引擎写入 (.xlsx) 和 (.xlsm) 文件。

  • 对于 xlsxwriter 引擎,pandas 使用 xlsxwriter.Workbook() 写入 (.xlsx) 文件。

  • 对于 odf 引擎,pandas 使用 odf.opendocument.OpenDocumentSpreadsheet() 写入 (.ods) 文件。

将 Excel 文件写入内存#

pandas 支持使用 ExcelWriter 将 Excel 文件写入缓冲区对象,例如 StringIOBytesIO

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 会决定生成工作簿的版本。设置 engine='xlrd' 会生成 Excel 2003 格式的工作簿 (xls)。使用 'openpyxl''xlsxwriter' 都会生成 Excel 2007 格式的工作簿 (xlsx)。如果省略,则会生成 Excel 2007 格式的工作簿。

Excel 写入器引擎#

pandas 通过两种方法选择 Excel 写入器:

  1. engine 关键字参数

  2. 文件名扩展名(通过配置文件中指定的默认值)

By default, pandas uses the XlsxWriter for .xlsx, openpyxl for .xlsm. If you have multiple engines installed, you can set the default engine through setting the config options io.excel.xlsx.writer and io.excel.xls.writer. pandas will fall back on openpyxl for .xlsx files if Xlsxwriter is not available.

要指定要使用的写入器,您可以将 engine 关键字参数传递给 to_excelExcelWriter。内置引擎有:

  • openpyxl:需要 2.4 或更高版本

  • xlsxwriter

# By setting the 'engine' in the DataFrame 'to_excel()' methods.
df.to_excel("path_to_file.xlsx", sheet_name="Sheet1", engine="xlsxwriter")

# By setting the 'engine' in the ExcelWriter constructor.
writer = pd.ExcelWriter("path_to_file.xlsx", engine="xlsxwriter")

# Or via pandas configuration.
from pandas import options  # noqa: E402

options.io.excel.xlsx.writer = "xlsxwriter"

df.to_excel("path_to_file.xlsx", sheet_name="Sheet1")

样式和格式#

可以使用 DataFrameto_excel 方法中的以下参数来修改从 pandas 创建的 Excel 工作表的观感。

  • float_format:浮点数的格式字符串(默认为 None)。

  • freeze_panes:一个由两个整数组成的元组,表示要冻结的最后一行和最后一列。这些参数都以 1 为基数,所以 (1, 1) 将冻结第一行和第一列(默认为 None)。

使用 Xlsxwriter 引擎提供了许多控制 to_excel 方法创建的 Excel 工作表格式的选项。您可以在 Xlsxwriter 文档的此处找到出色的示例:https://xlsxwriter.readthedocs.io/working_with_pandas.html

OpenDocument 电子表格#

Excel files 的 io 方法也支持使用 odfpy 模块读写 OpenDocument 电子表格。读写 OpenDocument 电子表格的语义和功能与使用 engine='odf' 处理 Excel files 所能实现的一致。需要安装可选的依赖项 ‘odfpy’。

read_excel() 方法可以读取 OpenDocument 电子表格

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

同样,to_excel() 方法可以写入 OpenDocument 电子表格

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

二进制 Excel (.xlsb) 文件#

read_excel() 方法还可以使用 pyxlsb 模块读取二进制 Excel 文件。读取二进制 Excel 文件的语义和功能与使用 engine='pyxlsb' 处理 Excel files 所能实现的大部分一致。pyxlsb 不识别文件中的日期时间类型,而是返回浮点数(如果您需要识别日期时间类型,可以使用 calamine )。

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

备注

目前 pandas 只支持*读取*二进制 Excel 文件。写入功能尚未实现。

Calamine (Excel 和 ODS 文件)#

read_excel() 方法可以使用 python-calamine 模块读取 Excel 文件(.xlsx, .xlsm, .xls, .xlsb)和 OpenDocument 电子表格(.ods)。该模块是 Rust 库 calamine 的绑定,在大多数情况下比其他引擎更快。需要安装可选的依赖项 ‘python-calamine’。

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

剪贴板#

获取数据的一个便捷方法是使用 read_clipboard() 方法,它会获取剪贴板缓冲区的内容并将其传递给 read_csv 方法。例如,您可以将以下文本复制到剪贴板(在许多操作系统上按 CTRL-C):

  A B C
x 1 4 p
y 2 5 q
z 3 6 r

然后可以通过调用以下命令将数据直接导入到 DataFrame 中:

>>> clipdf = pd.read_clipboard()
>>> clipdf
  A B C
x 1 4 p
y 2 5 q
z 3 6 r

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

我们可以看到,我们得到了与之前写入剪贴板的内容相同的内容。

备注

在 Linux 上,您可能需要安装 xclip 或 xsel(与 PyQt5、PyQt4 或 qtpy 一起)才能使用这些方法。

Pickling#

所有 pandas 对象都配备了 to_pickle 方法,该方法使用 Python 的 cPickle 模块将数据结构以 pickle 格式保存到磁盘。

pandas 命名空间中的 read_pickle 函数可用于从文件加载任何 pickled pandas 对象(或任何其他 pickled 对象):

警告

从不受信任的来源加载 pickled 数据可能不安全。

参见:https://docs.python.org/3/library/pickle.html

警告

read_pickle() 仅能保证与少数次要版本向后兼容。

压缩的 pickle 文件#

read_pickle()DataFrame.to_pickle()Series.to_pickle() 可以读写压缩的 pickle 文件。支持 gzipbz2xzzstd 的压缩类型进行读写。zip 文件格式仅支持读取,且必须只包含一个数据文件才能被读取。

压缩类型可以是一个显式参数,也可以从文件扩展名推断。如果为 ‘infer’,则当文件名以 '.gz''.bz2''.zip''.xz''.zst' 结尾时,分别使用 gzipbz2zipxzzstd

压缩参数也可以是一个 dict,以便将选项传递给压缩协议。它必须有一个名为压缩协议名称的 'method' 键,该名称必须是 {'zip', 'gzip', 'bz2', 'xz', 'zstd'} 中的一个。所有其他键值对都将传递给底层的压缩库。

使用显式的压缩类型:

从扩展名推断压缩类型:

默认是 ‘infer’:

将选项传递给压缩协议以加快压缩速度:

msgpack#

pandas 对 msgpack 的支持已在 1.0.0 版本中移除。建议使用 pickle 代替。

或者,您也可以使用 Arrow IPC 序列化格式进行 pandas 对象的在线传输。有关 pyarrow 的文档,请参阅 here

HDF5 (PyTables)#

HDFStore 是一个类似 dict 的对象,它使用出色的 PyTables 库以高性能 HDF5 格式读写 pandas。有关一些高级策略,请参阅 cookbook

警告

pandas 使用 PyTables 来读写 HDF5 文件,这允许使用 pickle 序列化 object-dtype 数据。从不受信任的来源加载 pickled 数据可能不安全。

有关更多信息,请参阅:https://docs.python.org/3/library/pickle.html

对象可以像向 dict 添加键值对一样写入文件:

在当前或后续的 Python 会话中,您可以检索已存储的对象:

删除由键指定的对象:

关闭 Store 并使用上下文管理器:

读/写 API#

HDFStore 支持使用 read_hdf 进行读取和 to_hdf 进行写入的顶级 API,这与 read_csvto_csv 的工作方式类似。

HDFStore 默认不会删除所有值都缺失的行。可以通过设置 dropna=True 来更改此行为。

固定格式#

上面的示例展示了使用 put 进行存储,这会将 HDF5 以称为 fixed 格式的固定数组格式写入 PyTables。这些类型的存储一旦写入就**不可追加**(尽管您可以简单地删除它们并重新写入)。它们也**不可查询**;它们必须被完整检索。它们也不支持具有非唯一列名的 DataFrame。fixed 格式的存储提供了非常快的写入速度,并且读取速度略快于 table 存储。当使用 putto_hdfformat='fixed'format='f' 时,此格式是默认指定的。

警告

fixed 格式在尝试使用 where 进行检索时会引发 TypeError

表格式#

HDFStore 在磁盘上还支持另一种 PyTables 格式,即 table 格式。概念上,一个 table 的形状与 DataFrame 非常相似,具有行和列。可以在同一会话或其他会话中追加 table。此外,还支持删除和查询类型的操作。此格式通过 appendputto_hdf 指定为 format='table'format='t'

此格式也可以设置为选项 pd.set_option('io.hdf.default_format','table'),以使 put/append/to_hdf 默认以 table 格式进行存储。

备注

您也可以通过向 put 操作传递 format='table'format='t' 来创建一个“表”。

分层键#

存储键可以指定为字符串。它们可以采用类似层级路径名的格式(例如 foo/bar/bah),这将生成一个子存储的层级结构(在 PyTables 的术语中称为“组”)。键可以不带前导 ‘/’ 指定,并且**总是**绝对的(例如 ‘foo’ 指的是 ‘/foo’)。删除操作可以删除子存储中**及之下**的所有内容,所以要*小心*。

您可以使用 walk 方法遍历组层级结构,该方法将为每个组键 along with its contents 的相对键生成一个元组。

警告

分层键不能像上面为存储在根节点下的项所描述的那样,以点(属性)访问方式检索。

而是使用显式的基于字符串的键:

存储类型#

在表中存储混合类型#

支持存储混合数据类型。字符串以固定宽度存储,使用追加列的最大大小。后续尝试追加更长字符串会导致 ValueError

min_itemsize={`values`: size} 作为参数传递给 append 将为字符串列设置更大的最小值。目前支持存储 floats, strings, ints, bools, datetime64。对于字符串列,将 nan_rep = 'nan' 传递给 append 会更改磁盘上的默认 nan 表示(在 np.nan 之间转换),默认为 nan

存储 MultiIndex DataFrame#

将 MultiIndex DataFrame 存储为表与存储/选择同质索引 DataFrame 非常相似。

备注

index 关键字是保留的,不能用作级别名称。

查询#

查询表#

selectdelete 操作有一个可选的条件,可以指定该条件来选择/删除数据的子集。这使得可以拥有一个非常大的磁盘表并只检索其中的一部分数据。

查询在底层使用 Term 类指定,作为布尔表达式。

  • indexcolumnsDataFrame 支持的索引器。

  • 如果指定了 data_columns,这些可以作为额外的索引器使用。

  • MultiIndex 中的级别名称,如果未提供,则默认为 level_0level_1

有效的比较运算符是:

=, ==, !=, >, >=, <, <=

有效的布尔表达式通过以下方式组合:

  • | : or

  • & : and

  • () : 用于分组

这些规则与 pandas 中用于索引的布尔表达式的使用方式类似。

备注

  • = 将被自动扩展为比较运算符 ==

  • ~ 是 not 运算符,但只能在非常有限的情况下使用

  • 如果传递了表达式列表/元组,它们将通过 & 组合

以下是有效的表达式:

  • 'index >= date'

  • "columns = ['A', 'D']"

  • "columns in ['A', 'D']"

  • 'columns = A'

  • 'columns == A'

  • "~(columns = ['A', 'B'])"

  • 'index > df.index[3] & string = "bar"'

  • '(index > df.index[3] & index <= df.index[6]) | string = "bar"'

  • "ts >= Timestamp('2012-02-01')"

  • "major_axis>=20130101"

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 加上引号。

以下是一些示例:

使用布尔表达式,并进行内联函数求值。

使用内联列引用。

columns 关键字可以提供给选择要返回的列列表,这等同于传递 'columns=list_of_columns_to_filter'

startstop 参数可以被指定来限制总体的搜索空间。它们是以表格中总行数来计算的。

备注

如果查询表达式存在一个未知的变量引用,select 将会抛出 ValueError。通常这意味着您正尝试选择一个 不是 data_column 的列。

如果查询表达式无效,select 将会抛出 SyntaxError

查询 timedelta64[ns]#

您可以使用 timedelta64[ns] 类型进行存储和查询。项可以按如下格式指定:<float>(<unit>),其中 float 可以是带符号的(并且可以是小数),unit 可以是 D,s,ms,us,ns 来表示时间差。例如:

查询 MultiIndex#

通过使用级别的名称可以实现从 MultiIndex 中进行选择。

如果 MultiIndex 的级别名称是 None,那么级别将通过 level_n 关键字自动提供,其中 n 是您想从中选择的 MultiIndex 的级别。

索引#

您可以在数据已经存在于表格中之后(在执行 append/put 操作之后),使用 create_table_index 来创建/修改表格的索引。强烈建议创建表格索引。这将极大地加快您在 select 中使用索引维度作为 where 条件时的查询速度。

备注

索引将自动创建在可索引列 (indexables) 和您指定的任何数据列上。可以通过将 index=False 传递给 append 来关闭此行为。

当向存储添加大量数据时,通常的做法是关闭每次添加时的索引创建,然后在最后重新创建索引。

然后,在添加完成时创建索引。

请参阅 here ,了解如何为现有存储创建完全排序索引 (CSI)。

通过数据列进行查询#

您可以指定(并索引)某些列,以便执行查询(除了 indexable 列,这些列始终可以查询)。例如,假设您想在磁盘上执行此常见操作,并仅返回匹配此查询的帧。您可以指定 data_columns = True 来强制所有列都成为 data_columns

将大量列设置为 data columns 会带来一些性能下降,因此应由用户来指定这些。此外,在第一次 append/put 操作之后,您无法更改数据列(也不能更改 indexables)(当然,您可以简单地读取数据并创建一个新表!)。

迭代器#

您可以将 iterator=Truechunksize=number_in_a_chunk 传递给 selectselect_as_multiple 来返回结果的迭代器。默认情况下,每个块返回 50,000 行。

备注

您也可以将迭代器与 read_hdf 一起使用,它会打开存储,并在迭代完成后自动关闭。

for df in pd.read_hdf("store.h5", "df", chunksize=3):
    print(df)

请注意, chunksize 关键字适用于 行。因此,如果您正在进行查询,那么 chunksize 将会细分表格中的总行数并应用查询,返回可能大小不等的块的迭代器。

以下是生成查询并使用它来创建大小相等的返回块的方法。

高级查询#

选择单个列#

要检索单个可索引列或数据列,请使用 select_column 方法。例如,这将使您能够非常快速地获取索引。这些方法返回结果的 Series,并由行号索引。目前它们不接受 where 选择器。

选择坐标#

有时您想获取查询的坐标(也称为索引位置)。这将返回结果位置的 Index。这些坐标也可以传递给后续的 where 操作。

使用 where 掩码进行选择#

有时您的查询可能涉及创建一个要选择的行列表。通常这个 mask 会是索引操作的结果 index。这个例子选择了 datetimeindex 中月份为 5 的行。

Storer 对象#

如果您想检查存储的对象,可以通过 get_storer 进行检索。您可以以编程方式使用它来获取对象中的行数。

多表查询#

append_to_multipleselect_as_multiple 方法可以一次性对多个表执行追加/选择操作。其思想是有一个表(称之为选择器表),您为其索引大部分/所有列,并执行查询。其他表是数据表,其索引与选择器表的索引匹配。然后,您可以对选择器表执行非常快速的查询,同时获得大量数据。此方法类似于拥有一个非常宽的表,但可以实现更高效的查询。

append_to_multiple 方法根据 d (一个将表名映射到您希望在该表中包含的“列”列表的字典) 将给定的单个 DataFrame 分割成多个表。如果使用 None 代替列表,则该表将包含给定 DataFrame 中其余未指定的列。参数 selector 定义了哪个表是选择器表 (您可以从中进行查询)。参数 dropna 将从输入的 DataFrame 中删除行以确保表同步。这意味着,如果被写入的某个表的一行全是 np.nan,那么该行将从所有表中删除。

如果 dropna 为 False,用户负责同步表。请记住,全是 np.Nan 的行不会写入 HDFStore,因此如果您选择调用 dropna=False,某些表可能比其他表包含更多行,因此 select_as_multiple 可能无法正常工作或返回意外结果。

从表中删除#

您可以通过指定 where 来选择性地从表中删除。在删除行时,理解 PyTables 的删除行方式很重要,它会删除行,然后**移动**后面的数据。因此,删除可能是一项非常耗费资源的操作,具体取决于数据的方向。为了获得最佳性能,最好将要删除的维度设置为 indexables 的第一个。

数据在磁盘上的顺序是根据 indexables 的。这是一个简单的用例。您存储面板类型的数据,日期在 major_axis 中,ID 在 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 允许对存储的数据进行压缩。这适用于所有类型的存储,不仅仅是表。有两个参数用于控制压缩:complevelcomplib

  • complevel 指定是否以及如何对数据进行压缩。complevel=0complevel=None 禁用压缩,0<complevel<10 启用压缩。

  • complib 指定使用哪个压缩库。如果未指定任何内容,则使用默认库 zlib。压缩库通常针对良好的压缩率或速度进行优化,结果将取决于数据类型。选择哪种类型的压缩取决于您的具体需求和数据。支持的压缩库列表:

    • zlib :默认的压缩库。经典的压缩算法,压缩率好但速度较慢。

    • lzo :压缩和解压缩速度快。

    • bzip2 :压缩率好。

    • blosc :压缩和解压缩速度快。

      对 blosc 压缩器的支持:

      • blosc:blosclz 这是 blosc 的默认压缩器。

      • blosc:lz4 :紧凑、非常流行且快速的压缩器。

      • blosc:lz4hc :LZ4 的一个调整版本,压缩率更好,但速度较慢。

      • blosc:snappy :一个在许多地方使用的流行压缩器。

      • blosc:zlib :经典的压缩器;比前面的速度稍慢,但压缩率更好。

      • blosc:zstd :一个极度均衡的编解码器;它提供比上述其他编解码器更好的压缩率,并且速度相当快。

    如果 complib 被定义为除列出的库之外的其他内容,则会引发 ValueError 异常。

备注

如果 complib 选项指定的库在您的平台上缺失,压缩将直接默认为 zlib

为文件中的所有对象启用压缩:

store_compressed = pd.HDFStore(
    "store_compressed.h5", complevel=9, complib="blosc:blosclz"
)

或者在未启用压缩的存储中启用即时压缩(这仅适用于表):

store.append("df", df, complib="zlib", complevel=5)

ptrepack#

PyTables 在表编写完成后再对其进行压缩,而不是在开始时就启用压缩,这样可以获得更好的写入性能。您可以使用提供的 PyTables 工具 ptrepack。此外,ptrepack 还可以事后更改压缩级别。

ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5

此外,ptrepack in.h5 out.h5 将会*重新打包*文件,以允许您重新使用先前删除的空间。或者,您可以简单地删除文件然后重新写入,或使用 copy 方法。

注意事项#

警告

HDFStore 在写入时不是线程安全的。底层的 PyTables 只支持并发读取(通过线程或进程)。如果您需要在*同时*进行读取和写入,您需要在单个进程的单个线程中将这些操作串行化。否则,您的数据将会损坏。有关更多信息,请参阅 (GH 2397

  • 如果您使用锁来管理多个进程之间的写入访问,您可能希望在释放写入锁之前使用 fsync() 。为了方便起见,您可以使用 store.flush(fsync=True) 来完成此操作。

  • 一旦创建了 table,列(DataFrame)就是固定的;只能追加完全相同的列。

  • 请注意,时区(例如 pytz.timezone('US/Eastern'))在不同时区版本之间不一定相等。因此,如果数据在一个时区库版本中使用 HDFStore 进行了特定时区本地化,然后使用另一个版本更新了这些数据,则数据将被转换为 UTC,因为这些时区不被认为是相等的。要么使用相同版本的时区库,要么在更新时区定义时使用 tz_convert

警告

如果列名不能用作属性选择器,PyTables 会显示 NaturalNameWarning*自然*标识符只包含字母、数字和下划线,并且不能以数字开头。其他标识符不能用在 where 子句中,并且通常不推荐使用。

数据类型#

HDFStore 会将 object dtype 映射到底层的 PyTables dtype。这意味着以下类型已知可以正常工作:

类型

代表缺失值

浮点数 : float64, float32, float16

np.nan

整数 : int64, int32, int8, uint64,uint32, uint8

boolean

datetime64[ns]

NaT

timedelta64[ns]

NaT

分类 : 请参阅下面的部分

对象 : 字符串

np.nan

不支持 unicode 列,并且**将会失败**。

类别数据#

您可以将包含 category dtypes 的数据写入 HDFStore。查询的工作方式与对象数组相同。但是,category dtyped 的数据以更有效的方式存储。

字符串列#

min_itemsize

HDFStore 的底层实现对字符串列使用固定列宽(itemsize)。字符串列的 itemsize 计算方法是 HDFStore 在*第一次追加*时传递给它的(该列的)数据长度的最大值。后续的追加可能会引入一个比列宽度*更大*的字符串,此时将引发异常(否则可能会导致这些列被静默截断,从而丢失信息)。未来我们可能会放宽此限制,允许用户指定截断行为。

在首次创建表时传递 min_itemsize,以预先指定特定字符串列的最小长度。min_itemsize 可以是一个整数,也可以是一个映射列名到整数的字典。您可以将 values 作为键传递,以允许所有*可索引*或*数据列*具有此 min_itemsize。

传递 min_itemsize 字典将导致所有传递的列自动创建为*数据列*。

备注

如果您没有传递任何 data_columns,那么 min_itemsize 将是传递的任何字符串长度的最大值。

nan_rep

字符串列将使用 nan_rep 字符串表示法序列化 np.nan``(缺失值)。默认值为字符串 ``nan。您可能会无意中将实际的 nan 值转换为缺失值。

性能#

  • fixed 存储相比,tables 格式在写入性能上有所损失。其优点是能够追加/删除并查询(可能非常大量的数据)。与常规存储相比,写入时间通常更长。查询时间可以非常快,尤其是在索引轴上。

  • 你可以将 chunksize=<int> 传递给 append,指定写入的块大小(默认为 50000)。这将显著降低写入时的内存使用量。

  • 你可以将 expectedrows=<int> 传递给第一个 append,以设置 PyTables 期望的总行数。这将优化读写性能。

  • 可以向表中写入重复行,但在选择时会被过滤掉(选择最后出现的行;因此,表在主键、次键对上是唯一的)。

  • 如果尝试存储 PyTables 将进行 pickling(而不是存储为原生类型)的类型,将引发 PerformanceWarning。有关更多信息和一些解决方案,请参见 Here

Feather#

Feather 提供数据框的二进制列式序列化。它旨在提高数据框读写效率,以及跨数据分析语言轻松共享数据。

Feather 旨在忠实地序列化和反序列化 DataFrames,支持所有 pandas 的 dtype,包括分类和带时区的 datetime 等扩展 dtype。

有几点需要注意:

  • 该格式不会写入 DataFrameIndexMultiIndex,如果提供了非默认索引,则会引发错误。你可以通过 .reset_index() 来存储索引,或者通过 .reset_index(drop=True) 来忽略它。

  • 不支持重复的列名和非字符串列名。

  • 不支持 object dtype 列中的实际 Python 对象。尝试序列化这些对象时会引发一个有用的错误消息。

请参阅 Full Documentation

写入 feather 文件。

从 feather 文件读取。

Parquet#

Apache Parquet 提供数据框的分布式二进制列式序列化。它旨在提高数据框读写效率,以及跨数据分析语言轻松共享数据。Parquet 可以使用各种压缩技术,在尽可能缩小文件大小的同时保持良好的读取性能。

Parquet 旨在忠实地序列化和反序列化 DataFrame,支持所有 pandas 的 dtype,包括带时区的 datetime 等扩展 dtype。

有几点需要注意。

  • 不支持重复的列名和非字符串列名。

  • pyarrow 引擎始终将索引写入输出,但 fastparquet 只写入非默认索引。这个额外的列可能会给不期望它的非 pandas 消费者带来问题。你可以通过 index 参数强制包含或省略索引,而与底层引擎无关。

  • 如果指定了索引级别名称,则必须是字符串。

  • pyarrow 引擎中,非字符串类型的分类 dtype 可以序列化为 parquet,但会反序列化为它们的原始 dtype。

  • pyarrow 引擎会保留字符串类型分类 dtype 的 ordered 标志。fastparquet 不会保留 ordered 标志。

  • 不支持的类型包括 Interval 和实际的 Python 对象类型。尝试序列化这些类型会引发一个有用的错误消息。Period 类型在 pyarrow >= 0.16.0 中受支持。

  • pyarrow 引擎会保留扩展数据类型,例如可空的整数和字符串数据类型(需要 pyarrow >= 0.16.0,并要求扩展类型实现所需的协议,请参阅 extension types documentation )。

你可以指定 engine 来控制序列化。它可以是 pyarrowfastparquetauto。如果未指定引擎,则会检查 pd.options.io.parquet.engine 选项;如果该选项也为 auto,则会尝试 pyarrow,然后回退到 fastparquet

See the documentation for pyarrow and fastparquet.

备注

这些引擎非常相似,应该能够读/写几乎相同的 parquet 格式文件。pyarrow>=8.0.0 支持 timedelta 数据,fastparquet>=0.1.4 支持带时区的 datetime。这些库的不同之处在于它们有不同的底层依赖(fastparquet 使用 numba,而 pyarrow 使用 C 库)。

写入 parquet 文件。

从 parquet 文件读取。

通过设置 dtype_backend 参数,您可以控制生成的 DataFrame 使用的默认 dtype。

备注

请注意,这不适用于 fastparquet

仅读取 parquet 文件的某些列。

处理索引#

DataFrame 序列化为 parquet 时,可能包含隐式索引作为输出文件中的一个或多个列。因此,这段代码:

如果你使用 pyarrow 进行序列化,将创建一个包含 列的 parquet 文件:ab__index_level_0__。如果你使用 fastparquet,索引 may or may not

这个意外的额外列会导致某些数据库(如 Amazon Redshift)拒绝该文件,因为该列在目标表中不存在。

如果你想在写入时省略 DataFrame 的索引,请将 index=False 传递给 to_parquet()

这将创建一个只包含预期的两列 a 和 b 的 parquet 文件。如果你的 DataFrame 有自定义索引,当你将此文件加载到 DataFrame 时,将无法恢复该索引。

传递 index=True总是 写入索引,即使这不是底层引擎的默认行为。

分区 Parquet 文件#

Parquet 支持基于一个或多个列的值对数据进行分区。

path 指定数据将被保存的父目录。partition_cols 是将用于分区的列名。列将按照它们给出的顺序进行分区。分区是通过分区列中的唯一值确定的。上面的例子创建了一个分区数据集,它可能看起来像:

test
├── a=0
│   ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet
│   └──  ...
└── a=1
    ├── e6ab24a4f45147b49b54a662f0c412a3.parquet
    └── ...

ORC#

parquet 格式类似,ORC Format 是用于数据帧的二进制列式序列化格式。它旨在使读取数据帧的效率更高。pandas 提供了 ORC 格式的读取器和写入器 read_orc()to_orc() 。这需要 pyarrow 库。

警告

写入 orc 文件。

从 orc 文件读取。

仅读取 orc 文件的某些列。

SQL 查询#

The pandas.io.sql module provides a collection of query wrappers to both facilitate data retrieval and to reduce dependency on DB-specific API.

在可能的情况下,用户可以优先选择 Apache Arrow ADBC 驱动程序。这些驱动程序应该提供最佳的性能、NULL 值处理和类型检测。

在 2.2.0 版本加入: 添加了对 ADBC 驱动程序的本地支持

有关 ADBC 驱动程序的完整列表及其开发状态,请参阅 ADBC Driver Implementation Status 文档。

当 ADBC 驱动程序不可用或缺少某些功能时,用户应选择与数据库驱动程序库一起安装 SQLAlchemy。这类驱动程序的示例包括 PostgreSQL 的 psycopg2 或 MySQL 的 pymysql 。对于 SQLite ,Python 标准库已默认包含。你可以在 SQLAlchemy docs 中找到支持的 SQL 方言驱动程序的概述。

如果未安装 SQLAlchemy,可以使用 sqlite3.Connection 代替 SQLAlchemy 的 engine、connection 或 URI 字符串。

另请参阅一些 cookbook examples ,了解一些高级策略。

主要功能包括:

read_sql_table (table_name, con[, schema, ...])

将 SQL 数据库表读取到 DataFrame 中。

read_sql_query (sql, con[, index_col, ...])

将 SQL 查询读取到 DataFrame 中。

read_sql (sql, con[, index_col, ...])

将 SQL 查询或数据库表读取到 DataFrame 中。

DataFrame.to_sql (name, con, *[, schema, ...])

将 DataFrame 中存储的记录写入 SQL 数据库。

备注

read_sql() 函数是 read_sql_table()read_sql_query() (以及为了向后兼容)的便捷包装器,它将根据提供的输入(数据库表名或 SQL 查询)委托给特定函数。如果表名包含特殊字符,则无需加引号。

在下面的示例中,我们使用 SQlite SQL 数据库引擎。您可以使用临时 SQLite 数据库,其中数据存储在“内存”中。

要使用 ADBC 驱动程序连接,请使用您的包管理器安装 adbc_driver_sqlite。安装完成后,您可以使用 ADBC 驱动程序提供的 DBAPI 接口连接到数据库。

import adbc_driver_sqlite.dbapi as sqlite_dbapi

# Create the connection
with sqlite_dbapi.connect("sqlite:///:memory:") as conn:
     df = pd.read_sql_table("data", conn)

要使用 SQLAlchemy 连接,请使用 create_engine() 函数从数据库 URI 创建一个 engine 对象。您只需要为要连接的每个数据库创建一个 engine 对象。有关 create_engine() 和 URI 格式的更多信息,请参阅下面的示例和 SQLAlchemy 的 documentation

如果您想管理自己的连接,可以改用其中一个。下面的示例使用 Python 上下文管理器打开数据库连接,该管理器会在块完成后自动关闭连接。有关数据库连接如何处理的说明,请参阅 SQLAlchemy docs

with engine.connect() as conn, conn.begin():
    data = pd.read_sql_table("data", conn)

警告

打开数据库连接后,您还负责关闭它。保持连接打开的副作用可能包括锁定数据库或其他破坏性行为。

写入 DataFrame#

假设 DataFrame data 中包含以下数据,我们可以使用 to_sql() 将其插入数据库。

id

Date

Col_1

Col_2

Col_3

26

2012-10-18

X

25.7

True

2012-10-19

Y

-12.4

63

False

2012-10-20

Z

5.73

对于某些数据库,写入大型 DataFrame 可能会因为超出数据包大小限制而导致错误。这可以通过在调用 to_sql 时设置 chunksize 参数来避免。例如,以下代码以每次 1000 行的批次将 data 写入数据库:

True

SQL 数据类型

确保 SQL 数据库之间数据类型的一致性是一个挑战。并非所有 SQL 数据库都提供相同的类型,即使它们提供相同的类型,其实现方式也可能有所不同,从而对类型的保留方式产生微妙的影响。#

为了最大限度地保留数据库类型,建议用户在有 ADBC 驱动程序可用时使用它们。Arrow 类型系统提供了比 pandas/NumPy 历史类型系统更广泛的类型,这些类型与数据库类型更匹配。为了说明这一点,请注意以下不同数据库和 pandas 后端中可用类型的列表(非详尽):

numpy/pandas

arrow

postgres

sqlite

int16/Int16

int16

SMALLINT

INTEGER

int32/Int32

int32

int64/Int64

int32/Int32

int32/Int32

BIGINT

int64

float32

int32/Int32

REAL

REAL

DOUBLE PRECISION

DOUBLE PRECISION

float64

float64

string

DOUBLE PRECISION

object

TEXT

bool_

bool_

bool

BOOLEAN

timestamp(us)

datetime64[ns]

TIMESTAMP

datetime64[ns,tz]

timestamp(us,tz)

TIMESTAMPTZ

date32

DATE

month_day_nano_interval

INTERVAL

BINARY

binary

BLOB

decimal128

DECIMAL

list

ARRAY

struct

COMPOSITE TYPE

脚注

撰写本文时未实现,但理论上可行

If you are interested in preserving database types as best as possible throughout the lifecycle of your DataFrame, users are encouraged to leverage the dtype_backend="pyarrow" argument of read_sql()

# for roundtripping
with pg_dbapi.connect(uri) as conn:
    df2 = pd.read_sql("pandas_table", conn, dtype_backend="pyarrow")

这可以防止您的数据被转换为传统的 pandas/NumPy 类型系统,该系统通常会以无法正确往返(round-trip)的方式转换 SQL 类型。

如果 ADBC 驱动程序不可用,to_sql() 将尝试根据数据的 dtype 将您的数据映射到适当的 SQL 数据类型。当您有 dtype 为 object 的列时,pandas 会尝试推断数据类型。

您可以通过使用 dtype 参数为任何列指定所需的 SQL 类型来覆盖默认类型。此参数需要一个字典,将列名映射到 SQLAlchemy 类型(或用于 sqlite3 回退模式的字符串)。例如,指定将 SQLAlchemy 的 String 类型用于字符串列,而不是默认的 Text 类型:

备注

由于不同数据库种类对 timedelta 的支持有限,类型为 timedelta64 的列将以纳秒为单位的整数值写入数据库,并会引发警告。唯一的例外是使用 ADBC PostgreSQL 驱动程序时,在这种情况下,timedelta 将作为 INTERVAL 写入数据库。

备注

类型为 category 的列将被转换为密集表示,就像您通过 np.asarray(categorical) 获得的那样(例如,对于字符串类别,这将得到一个字符串数组)。因此,将数据库表读回时**不会**生成分类数据。

日期时间数据类型#

使用 ADBC 或 SQLAlchemy,to_sql() 能够写入时区无关或时区相关的日期时间数据。但是,最终存储在数据库中的数据取决于所使用的数据库系统对日期时间数据的支持类型。

下表列出了一些常用数据库对日期时间数据的支持类型。其他数据库方言可能具有不同的日期时间数据类型。

数据库

SQL 日期时间类型

时区支持

SQLite

TEXT

MySQL

TIMESTAMPDATETIME

PostgreSQL

TIMESTAMPTIMESTAMP WITH TIME ZONE

当将时区相关的日期时间数据写入不支持时区的数据库时,数据将被写入为本地时间的时区无关时间戳。

read_sql_table() 也能够读取时区相关或时区无关的日期时间数据。读取 TIMESTAMP WITH TIME ZONE 类型时,pandas 会将数据转换为 UTC。

插入方法#

method 参数控制使用的 SQL INSERT 子句。可能的值为:

  • None:使用标准的 SQL INSERT 子句(每行一个)。

  • 'multi':在一个 INSERT 子句中传递多个值。它使用 一种特殊的 SQL 语法,并非所有后端都支持。这通常能为 PrestoRedshift 等分析数据库提供更好的性能,但如果表包含许多列,则对于传统 SQL 后端性能较差。有关更多信息,请参阅 SQLAlchemy 的 documentation

  • 签名是 (pd_table, conn, keys, data_iter) 的可调用对象:这可以用于实现基于特定后端方言特性的更高效的插入方法。

使用 PostgreSQL COPY clause 的可调用对象示例::

# Alternative to_sql() *method* for DBs that support COPY FROM
import csv
from io import StringIO

def psql_insert_copy(table, conn, keys, data_iter):
    """
    Execute SQL statement inserting data

    Parameters
    ----------
    table : pandas.io.sql.SQLTable
    conn : sqlalchemy.engine.Engine or sqlalchemy.engine.Connection
    keys : list of str
        Column names
    data_iter : Iterable that iterates the values to be inserted
    """
    # gets a DBAPI connection that can provide a cursor
    dbapi_conn = conn.connection
    with dbapi_conn.cursor() as cur:
        s_buf = StringIO()
        writer = csv.writer(s_buf)
        writer.writerows(data_iter)
        s_buf.seek(0)

        columns = ', '.join(['"{}"'.format(k) for k in keys])
        if table.schema:
            table_name = '{}.{}'.format(table.schema, table.name)
        else:
            table_name = table.name

        sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
            table_name, columns)
        cur.copy_expert(sql=sql, file=s_buf)

读取表#

read_sql_table() 将读取给定的表名,并可选择读取列的子集。

备注

为了使用 read_sql_table() ,您**必须**安装 ADBC 驱动程序或 SQLAlchemy 可选依赖项。

备注

ADBC 驱动程序会将数据库类型直接映射回 arrow 类型。对于其他驱动程序,请注意 pandas 根据查询输出来推断列 dtype,而不是通过查询物理数据库架构中的数据类型来推断。例如,假设 userid 是表中的一个整数列。那么,直观地说,select userid ... 将返回整数值的 series,而 select cast(userid as text) ... 将返回对象值(str)的 series。因此,如果查询结果为空,那么所有生成的列都将作为对象值返回(因为它们是最通用的)。如果您预计查询有时会生成空结果,您可能需要稍后显式进行类型转换以确保 dtype 的完整性。

你也可以指定列名作为 DataFrame 的索引,并指定要读取的列子集。

并且你可以显式强制将列解析为日期:

如果需要,你可以显式指定一个格式字符串,或者一个传递给 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() 函数中的 schema 关键字,支持读写不同的 schema。但请注意,这取决于数据库类型(sqlite 没有 schema)。例如:

df.to_sql(name="table", con=engine, schema="other_schema")
pd.read_sql_table("table", engine, schema="other_schema")

查询#

你可以在 read_sql_query() 函数中使用原始 SQL 进行查询。在这种情况下,你必须使用适合你的数据库的 SQL 变体。当使用 SQLAlchemy 时,你也可以传递 SQLAlchemy Expression 语言构造,这些是数据库无关的。

当然,你也可以指定一个更“复杂”的查询。

read_sql_query() 函数支持 chunksize 参数。指定此参数将返回一个查询结果块的迭代器:

Engine 连接示例#

要使用 SQLAlchemy 连接، 你可以使用 create_engine() 函数从数据库 URI 创建一个 engine 对象。你只需要为每个要连接的数据库创建一次 engine。

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() 以后端无关的方式指定查询参数。

如果你有数据库的 SQLAlchemy 描述,你可以使用 SQLAlchemy 表达式来表示 where 条件。

你可以使用 read_sql() 将 SQLAlchemy 表达式与传递给 sqlalchemy.bindparam() 的参数结合起来。

Sqlite 后备#

如果不使用 SQLAlchemy,也支持使用 sqlite。此模式需要一个遵循 Python DB-API 的 Python 数据库适配器。

你可以这样创建连接:

import sqlite3

con = sqlite3.connect(":memory:")

然后可以执行以下查询:

data.to_sql("data", con)
pd.read_sql_query("SELECT * FROM data", con)

Google BigQuery#

pandas-gbq 包提供了从 Google BigQuery 读取/写入的功能。

pandas 与该外部包集成。如果安装了 pandas-gbq,你可以使用 pandas 方法 pd.read_gbqDataFrame.to_gbq,它们将调用 pandas-gbq 中相应的函数。

完整的文档可以在 here 找到。

Stata 格式#

写入 Stata 格式#

DataFrame.to_stata() 方法会将 DataFrame 写入 .dta 文件。该文件的格式版本始终是 115 (Stata 12)。

Stata 数据文件支持的数据类型有限;只有长度小于等于 244 个字符的字符串、int8int16int32float32float64 可以存储在 .dta 文件中。此外,Stata 保留某些值来表示缺失数据。导出超出 Stata 对于特定数据类型的允许范围的非缺失值时,变量将被重新键入为更大的尺寸。例如,int8 的值在 Stata 中被限制在 -127 到 100 之间,因此值大于 100 的变量将触发转换为 int16。浮点数据类型中的 nan 值存储为基本缺失数据类型(在 Stata 中是 .)。

备注

无法为整数数据类型导出缺失数据值。

Stata 写入器会优雅地处理其他数据类型,包括 int64booluint8uint16uint32,方法是将它们转换为能够表示数据的最小支持类型。例如,类型为 uint8 的数据如果所有值都小于 100(Stata 中非缺失 int8 数据的上限),则会被转换为 int8;或者,如果值超出此范围,则该变量会被转换为 int16

警告

int64 转换为 float64 可能会导致精度损失,如果 int64 值大于 2**53。

警告

StataWriterDataFrame.to_stata() 只支持固定宽度字符串,长度最多为 244 个字符,这是由 115 版本 dta 文件格式强加的限制。尝试写入长度超过 244 个字符的 Stata dta 文件会引发 ValueError

从 Stata 格式读取#

顶层函数 read_stata 将读取 dta 文件并返回一个 DataFrame 或一个 pandas.api.typing.StataReader ,后者可用于增量读取文件。

指定 chunksize 可得到一个 pandas.api.typing.StataReader 实例,该实例可用于一次读取文件中的 chunksize 行。StataReader 对象可用作迭代器。

为了更精细地控制,请使用 iterator=True 并在每次调用 read() 时指定 chunksize

当前 index 被检索为一个列。

参数 convert_categoricals 指示是否应读取值标签并使用它们来创建 Categorical 变量。值标签也可以通过 value_labels 函数检索,该函数需要先调用 read() 才能使用。

参数 convert_missing 指示是否应保留 Stata 中的缺失值表示。如果为 False``(默认值),则缺失值表示为 ``np.nan。如果为 True,则缺失值使用 StataMissingValue 对象表示,包含缺失值的列将具有 object 数据类型。

备注

read_stata()StataReader 支持 .dta 格式 113-115(Stata 10-12)、117(Stata 13)和 118(Stata 14)。

备注

设置为 preserve_dtypes=False 将向上转换为标准的 pandas 数据类型:所有整数类型为 int64,浮点数据为 float64。默认情况下,导入时会保留 Stata 数据类型。

备注

所有 StataReader 对象,无论是通过 read_stata() (在使用 iterator=Truechunksize 时)创建还是手动实例化,都必须用作上下文管理器(例如 with 语句)。虽然 close() 方法可用,但其使用不受支持。它不是公共 API 的一部分,将在未来版本中移除,恕不另行通知。

类别数据#

Category 数据可以导出到 Stata 数据文件中作为值标记数据。导出的数据由底层类别代码作为整数数据值组成,类别作为值标签。Stata 没有与 Category 显式等价的东西,并且在导出时关于变量*是否*有序的信息会丢失。

警告

Stata 只支持字符串值标签,因此在导出数据时会对类别调用 str。导出具有非字符串类别的 Categorical 变量会产生警告,并且如果类别的 str 表示形式不唯一,则可能导致信息丢失。

标记数据可以类似地从 Stata 数据文件中导入为 Categorical 变量,使用关键字参数 convert_categoricals``(默认为 ``True)。关键字参数 order_categoricals``(默认为 ``True)决定导入的 Categorical 变量是否是有序的。

备注

导入分类数据时,Stata 数据文件中的变量值不会被保留,因为 Categorical 变量始终使用介于 -1n-1 之间的整数数据类型,其中 n 是类别的数量。如果需要 Stata 数据文件中的原始值,可以通过设置 convert_categoricals=False 来导入这些值,这将导入原始数据(但不是变量标签)。由于原始 Stata 数据值和导入的 Categorical 变量的类别代码之间存在简单的映射,因此可以将原始值与导入的分类数据匹配:缺失值被分配代码 -1,最小的原始值被分配 0,第二小的分配 1,依此类推,直到最大的原始值被分配代码 n-1

备注

Stata 支持部分标记序列。这些序列对某些数据值具有值标签,但并非全部。导入部分标记的序列将生成一个 Categorical,其中标记的值具有字符串类别,没有标签的值具有数值类别。

SAS 格式#

顶层函数 read_sas() 可以读取(但不能写入)SAS XPORT (.xpt) 和 SAS7BDAT (.sas7bdat) 格式的文件。

SAS 文件只包含两种值类型:ASCII 文本和浮点值(通常为 8 字节,但有时会被截断)。对于 xport 文件,没有自动类型转换成整数、日期或分类变量的功能。对于 SAS7BDAT 文件,格式代码可能允许日期变量自动转换为日期。默认情况下,整个文件会被读取并返回为一个 DataFrame

指定 chunksize 或使用 iterator=True 来获取 reader 对象(XportReaderSAS7BDATReader),以便逐块读取文件。reader 对象还具有包含文件及其变量的附加信息的属性。

读取 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 格式#

顶层函数 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")

从 SPSS 文件中提取 usecols 包含的列子集,并避免将分类列转换为 pd.Categorical

df = pd.read_spss(
    "spss_data.sav",
    usecols=["foo", "bar"],
    convert_categoricals=False,
)

有关 SAV 和 ZSAV 文件格式的更多信息可在此处 获取。

其他文件格式#

pandas 本身仅支持与一套有限的文件格式的 IO 操作,这些文件格式可以清晰地映射到其表格数据模型。要将其他文件格式读写到 pandas 中,我们推荐使用来自更广泛社区的以下包。

netCDF#

xarray 提供了受 pandas DataFrame 启发的用于处理多维数据集的数据结构,专注于 netCDF 文件格式,并可轻松转换为 pandas 对象以及从 pandas 对象进行转换。

性能考虑#

这是使用 pandas 0.24.2 对各种 IO 方法进行的非正式比较。计时结果取决于机器,应忽略微小差异。

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

以下测试函数将在下面用于比较几种 IO 方法的性能:

import numpy as np

import os

sz = 1000000
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})

sz = 1000000
np.random.seed(42)
df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz})


def test_sql_write(df):
    if os.path.exists("test.sql"):
        os.remove("test.sql")
    sql_db = sqlite3.connect("test.sql")
    df.to_sql(name="test_table", con=sql_db)
    sql_db.close()


def test_sql_read():
    sql_db = sqlite3.connect("test.sql")
    pd.read_sql_query("select * from test_table", sql_db)
    sql_db.close()


def test_hdf_fixed_write(df):
    df.to_hdf("test_fixed.hdf", key="test", mode="w")


def test_hdf_fixed_read():
    pd.read_hdf("test_fixed.hdf", "test")


def test_hdf_fixed_write_compress(df):
    df.to_hdf("test_fixed_compress.hdf", key="test", mode="w", complib="blosc")


def test_hdf_fixed_read_compress():
    pd.read_hdf("test_fixed_compress.hdf", "test")


def test_hdf_table_write(df):
    df.to_hdf("test_table.hdf", key="test", mode="w", format="table")


def test_hdf_table_read():
    pd.read_hdf("test_table.hdf", "test")


def test_hdf_table_write_compress(df):
    df.to_hdf(
        "test_table_compress.hdf", key="test", mode="w", complib="blosc", format="table"
    )


def test_hdf_table_read_compress():
    pd.read_hdf("test_table_compress.hdf", "test")


def test_csv_write(df):
    df.to_csv("test.csv", mode="w")


def test_csv_read():
    pd.read_csv("test.csv", index_col=0)


def test_feather_write(df):
    df.to_feather("test.feather")


def test_feather_read():
    pd.read_feather("test.feather")


def test_pickle_write(df):
    df.to_pickle("test.pkl")


def test_pickle_read():
    pd.read_pickle("test.pkl")


def test_pickle_write_compress(df):
    df.to_pickle("test.pkl.compress", compression="xz")


def test_pickle_read_compress():
    pd.read_pickle("test.pkl.compress", compression="xz")


def test_parquet_write(df):
    df.to_parquet("test.parquet")


def test_parquet_read():
    pd.read_parquet("test.parquet")

在写入方面,速度最快的前三个函数是 test_feather_writetest_hdf_fixed_writetest_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_readtest_pickle_readtest_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.compresstest.parquettest.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