IO 工具(文本、CSV、HDF5...)# pandas I/O API 是一组顶级reader函数,访问方式 pandas.read_csv()通常返回 pandas 对象。相应的 writer函数是对象方法,可以像 DataFrame.to_csv().下面是一个包含可用readers和 的 表writers。 格式类型 数据说明 读者 作家 文本 CSV 读取csv 到_csv 文本 固定宽度文本文件 读_fwf 文本 JSON 读取json to_json 文本 超文本标记语言 读取html to_html 文本 乳胶 Styler.to_latex 文本 XML 读取XML to_xml 文本 本地剪贴板 读取剪贴板 到剪贴板 二进制 微软Excel 读取Excel 脱颖而出 二进制 开放文档 读取Excel 二进制 HDF5 格式 读取hdf to_hdf 二进制 羽毛格式 读羽毛 羽毛 二进制 实木复合地板格式 读镶木地板 镶木地板 二进制 兽人格式 读兽人 to_orc 二进制 斯塔塔 读stata to_stata 二进制 SAS 读_sas 二进制 统计软件 读spss 二进制 Python Pickle 格式 读取pickle to_pickle SQL SQL 读sql 到_sql SQL 谷歌大查询 读GBQ 到_GBQ 以下是其中一些 IO 方法的非正式性能比较。 笔记 对于使用该类的示例StringIO,请确保使用Python 3 导入它。from io import StringIO CSV 和文本文件# 读取文本文件(又名平面文件)的主力函数是 read_csv().请参阅食谱了解一些高级策略。 解析选项# read_csv()接受以下常见参数: 基本的# filepath_or_buffer各种文件的路径(str、pathlib.Path或py:py._path.local.LocalPath)、URL(包括 http、ftp 和 S3 位置)或具有read()方法的任何对象(例如打开的文件 或 StringIO)。 sep str,默认为','for read_csv(), \tforread_table()要使用的分隔符。如果 sep 为None,则 C 引擎无法自动检测分隔符,但 Python 解析引擎可以,这意味着将使用后者并通过 Python 内置的嗅探工具 自动检测分隔符 csv.Sniffer。另外,长度超过1个字符且不同于的分隔符'\s+'将被解释为正则表达式,也会强制使用Python解析引擎。请注意,正则表达式分隔符很容易忽略引用的数据。正则表达式示例:'\\r\\t'. 分隔符str,默认Nonesep 的替代参数名称。 delim_whitespace布尔值,默认 False指定是否使用空格(例如或)作为分隔符。相当于设置。如果此选项设置为,则不应为该参数传递任何内容 。' ''\t'sep='\s+'Truedelimiter 列和索引位置和名称# header int 或 int 列表,默认'infer'用作列名称的行号以及数据的开头。默认行为是推断列名称:如果未传递名称,则行为header=0与从文件的第一行推断列名称相同,如果显式传递列名称,则行为与 相同 header=None。显式传递header=0以能够替换现有名称。 标头可以是一个整数列表,用于指定列上 MultiIndex 的行位置,例如[0,1,3]。未指定的中间行将被跳过(例如,本例中的 2 行将被跳过)。请注意,如果 ,此参数会忽略注释行和空行skip_blank_lines=True,因此 header=0 表示数据的第一行而不是文件的第一行。 名称类似数组,默认None要使用的列名称列表。如果文件不包含标题行,那么您应该显式传递header=None.此列表中不允许有重复项。 index_col int、str、int / str 序列或 False,可选,默认None用作 的行标签的列DataFrame,以字符串名称或列索引的形式给出。如果给出 int / str 序列,则使用 MultiIndex。 笔记 index_col=False可用于强制 pandas 不使用第一列作为索引,例如,当您有一个格式错误的文件,每行末尾都有分隔符时。 默认值None指示 pandas 进行猜测。如果列标题行中的字段数等于数据文件正文中的字段数,则使用默认索引。如果较大,则将第一列用作索引,以便正文中的剩余字段数等于标头中的字段数。 标题后的第一行用于确定将进入索引的列数。如果后续行包含的列数少于第一行,则它们将填充为NaN。 通过 可以避免这种情况usecols。这可确保按原样获取列并忽略尾随数据。 usecols类似列表或可调用,默认None返回列的子集。如果是类似列表,则所有元素必须是位置元素(即文档列中的整数索引)或者是与用户在文档names标题行中提供的或从文档标题行推断出的列名称相对应的字符串。如果names给出,则不考虑文档标题行。例如,有效的类似列表的 usecols参数是or 。[0, 1, 2]['foo', 'bar', 'baz'] 元素顺序被忽略,因此与 相同。要从保留元素顺序的 DataFrame 实例化,请使用按顺序排列的列或 按 顺序排列的列。usecols=[0, 1][1, 0]datapd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']]['foo', 'bar']pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]['bar', 'foo'] 如果可调用,则将根据列名称评估可调用函数,返回可调用函数计算结果为 True 的名称: In [1]: import pandas as pd In [2]: from io import StringIO In [3]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [4]: pd.read_csv(StringIO(data)) Out[4]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [5]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["COL1", "COL3"]) Out[5]: col1 col3 0 a 1 1 a 2 2 c 3 使用 c 引擎时,使用此参数会导致更快的解析时间和更低的内存使用量。 Python 引擎首先加载数据,然后再决定删除哪些列。 通用解析配置# dtype列的类型名称或字典 -> 类型,默认None数据或列的数据类型。例如, 使用或与适当的设置一起保留而不是解释数据类型。如果指定了转换器,则将应用它们而不是数据类型转换。{'a': np.float64, 'b': np.int32, 'c': 'Int64'}strobjectna_values 1.5.0 版新增功能:添加了对 defaultdict 的支持。指定一个 defaultdict 作为输入,其中默认值确定未显式列出的列的数据类型。 dtype_backend {“numpy_nullable”, “pyarrow”},默认为 NumPy 支持的 DataFrame使用哪个 dtype_backend,例如 DataFrame 是否应该具有 NumPy 数组,当设置“numpy_nullable”时,可空数据类型用于所有具有可空实现的数据类型,如果设置“pyarrow”,则 pyarrow 用于所有数据类型。 dtype_backends 仍处于实验阶段。 2.0版本中的新增功能。 引擎{ 'c','python','pyarrow'}要使用的解析器引擎。 C 和 pyarrow 引擎速度更快,而 python 引擎目前功能更完整。目前仅 pyarrow 引擎支持多线程。 版本 1.4.0 中的新增功能:添加了“pyarrow”引擎作为实验引擎,该引擎不支持某些功能,或者可能无法正常工作。 转换器字典,默认None用于转换某些列中的值的函数字典。键可以是整数或列标签。 true_values列表,默认None考虑的值为True。 false_values列表,默认值None考虑的值为False。 跳过初始空间布尔值,默认False跳过分隔符后的空格。 跳过行列表或整数,默认None文件开头要跳过的行号(从 0 开始索引)或要跳过的行数 (int)。 如果可调用,则将根据行索引评估可调用函数,如果应跳过该行则返回 True,否则返回 False: In [6]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [7]: pd.read_csv(StringIO(data)) Out[7]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [8]: pd.read_csv(StringIO(data), skiprows=lambda x: x % 2 != 0) Out[8]: col1 col2 col3 0 a b 2 Skipfooter int,默认0文件底部要跳过的行数(engine='c' 不支持)。 nrows整数,默认None要读取的文件行数。对于读取大文件很有用。 low_memory布尔值,默认值True在内部以块的形式处理文件,从而在解析时减少内存使用,但可能会混合类型推断。为了确保没有混合类型,请设置False或使用参数指定类型dtype。请注意,无论如何,整个文件都会读入单个文件DataFrame,请使用chunksizeoriterator参数以块的形式返回数据。 (仅对 C 解析器有效) memory_map布尔值,默认 False如果提供了文件路径filepath_or_buffer,则将文件对象直接映射到内存并直接从那里访问数据。使用此选项可以提高性能,因为不再有任何 I/O 开销。 NA 和缺失数据处理# na_values标量、str、类列表或字典,默认值None识别为 NA/NaN 的其他字符串。如果 dict 通过,则特定的每列 NA 值。有关默认情况下解释为 NaN 的值的列表,请参阅下面的na 值 const 。 keep_default_na布尔值,默认值True解析数据时是否包含默认 NaN 值。根据是否na_values传入,行为如下: 如果指定keep_default_na了True和, 则附加到用于解析的默认 NaN 值。na_valuesna_values 如果keep_default_na未指定True和na_values,则仅使用默认的 NaN 值进行解析。 如果指定了keep_default_naisFalse和na_values,则仅使用指定的 NaN 值na_values进行解析。 如果未指定keep_default_naisFalse和,则不会将任何字符串解析为 NaN。na_values 请注意,如果na_filter作为 传入False,则keep_default_na和 na_values参数将被忽略。 na_filter布尔值,默认True检测缺失值标记(空字符串和 na_values 的值)。在没有任何NA的数据中,传递na_filter=False可以提高读取大文件的性能。 详细布尔值,默认False指示放置在非数字列中的 NA 值的数量。 Skip_blank_lines布尔值,默认值True如果True,则跳过空白行而不是解释为 NaN 值。 日期时间处理# parse_dates布尔值、整数列表、名称列表、列表列表或字典列表,默认值False。 如果True-> 尝试解析索引。 如果-> 尝试将第 1、2、3 列分别解析为单独的日期列。[1, 2, 3] 如果-> 合并第 1 列和第 3 列并解析为单个日期列。[[1, 3]] If -> 将第 1、3 列解析为日期并调用结果“foo”。{'foo': [1, 3]} 笔记 iso8601 格式的日期存在快速路径。 infer_datetime_format布尔值,默认False如果True为列启用了 parse_dates,则尝试推断日期时间格式以加快处理速度。 自 version 2.0.0 起已弃用:此参数的严格版本现在是默认值,传递它没有任何效果。 keep_date_col布尔值,默认False如果True和 parse_dates 指定组合多个列,则保留原始列。 date_parser函数,默认None用于将字符串列序列转换为日期时间实例数组的函数。默认用于dateutil.parser.parser进行转换。 pandas 将尝试以三种不同的方式调用 date_parser,如果发生异常,则前进到下一个: 1) 传递一个或多个数组(由 parse_dates 定义)作为参数; 2) 将 parse_dates 定义的列中的字符串值连接(按行)到单个数组中并传递; 3) 使用一个或多个字符串(对应于 parse_dates 定义的列)作为参数,为每一行调用一次 date_parser 。 自版本 2.0.0 起已弃用:使用date_format替代方案,或读入 as object,然后 to_datetime()根据需要应用。 date_format列的 str 或 dict -> 格式,默认None如果与 一起使用parse_dates,将根据此格式解析日期。对于更复杂的内容,请阅读 asobject并to_datetime()根据需要应用。 2.0.0 版本中的新增内容。 dayfirst布尔值,默认FalseDD/MM 格式日期、国际和欧洲格式。 cache_dates布尔值,默认 True如果为 True,则使用唯一的已转换日期的缓存来应用日期时间转换。解析重复的日期字符串时,尤其是具有时区偏移量的字符串时,可能会产生显着的加速。 迭代# 迭代器布尔值,默认False返回TextFileReader用于迭代的对象或使用 获取块 get_chunk()。 chunksize int,默认None返回TextFileReader迭代对象。请参阅下面的迭代和分块。 引用、压缩和文件格式# 压缩{ 'infer', 'gzip', 'bz2', 'zip', 'xz', 'zstd', None, dict}, 默认'infer'用于磁盘数据的即时解压缩。如果是 'infer',则filepath_or_buffer分别以 '.gz'、'.bz2'、'.zip'、'.xz'、'.zst' 结尾的类似路径,则使用 gzip、bz2、zip、xz 或 zstandard ,否则不解压。如果使用“zip”,ZIP 文件必须仅包含一个要读入的数据文件。设置为None则不解压缩。也可以是一个字典,其键设置为 { , , , }'method' 之一,其他键值对转发到, , , 或。例如,可以传递以下内容以加快压缩速度并创建可重现的 gzip 存档: .'zip''gzip''bz2''zstd'zipfile.ZipFilegzip.GzipFilebz2.BZ2Filezstandard.ZstdDecompressorcompression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1} 版本 1.2.0 中的更改:以前的版本将“gzip”的字典条目转发到gzip.open. 千str,默认None千位分隔符。 十进制str,默认'.'识别为小数点的字符。例如用于','欧洲数据。 float_ precision字符串,默认 None指定 C 引擎应使用哪个转换器来处理浮点值。选项有None普通转换器、high高精度转换器和round_trip往返转换器。 行终止符str(长度1),默认None将文件分成行的字符。仅对 C 解析器有效。 quotechar str(长度1)用于表示引用项的开始和结束的字符。引用的项目可以包含分隔符,它将被忽略。 引用int 或csv.QUOTE_*实例,默认0控制每个常量的字段引用行为csv.QUOTE_*。使用QUOTE_MINIMAL(0)、QUOTE_ALL(1)、QUOTE_NONNUMERIC(2) 或 QUOTE_NONE(3)之一 。 双引号布尔值,默认True当quotechar指定 且quoting不是 时QUOTE_NONE,指示是否将字段内的quotechar两个连续元素 解释为单个元素。quotechar escapechar str(长度1),默认None当引用为 时,用于转义分隔符的单字符字符串QUOTE_NONE。 注释str,默认None指示不应解析该行的其余部分。如果在行的开头找到,则该行将被完全忽略。该参数必须是单个字符。与空行(只要skip_blank_lines=True)一样,完全注释的行会被参数忽略header,但不会被 忽略skiprows。例如,如果comment='#',用 解析 '#empty\na,b,c\n1,2,3' header=0将导致 'a,b,c' 被视为标头。 编码str,默认None读/写时用于 UTF 的编码(例如'utf-8')。Python 标准编码列表。 方言str 或csv.Dialect实例,默认None如果提供,此参数将覆盖以下参数的值(默认或非默认):delimiter、doublequote、escapechar、 skipinitialspace、quotechar和quoting。如果需要覆盖值,将发出 ParserWarning。请参阅csv.Dialect 文档了解更多详细信息。 错误处理# on_bad_lines ('错误', '警告', '跳过'), 默认'错误'指定遇到错误行(包含太多字段的行)时要执行的操作。允许的值为: 'error',当遇到坏行时引发 ParserError。 'warn',当遇到坏行时打印警告并跳过该行。 'skip',跳过坏行,遇到坏行时不引发或警告。 1.3.0 版本中的新增功能。 指定列数据类型# DataFrame您可以指定整个列或单个列的数据类型: In [9]: import numpy as np In [10]: data = "a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11" In [11]: print(data) a,b,c,d 1,2,3,4 5,6,7,8 9,10,11 In [12]: df = pd.read_csv(StringIO(data), dtype=object) In [13]: df Out[13]: a b c d 0 1 2 3 4 1 5 6 7 8 2 9 10 11 NaN In [14]: df["a"][0] Out[14]: '1' In [15]: df = pd.read_csv(StringIO(data), dtype={"b": object, "c": np.float64, "d": "Int64"}) In [16]: df.dtypes Out[16]: a int64 b object c float64 d Int64 dtype: object 幸运的是,pandas 提供了不止一种方法来确保您的列仅包含一个dtype.如果您不熟悉这些概念,可以参阅此处了解有关 dtypes 的更多信息,并 了解object有关pandas 中转换的更多信息。 例如,您可以使用converters以下参数read_csv(): In [17]: data = "col_1\n1\n2\n'A'\n4.22" In [18]: df = pd.read_csv(StringIO(data), converters={"col_1": str}) In [19]: df Out[19]: col_1 0 1 1 2 2 'A' 3 4.22 In [20]: df["col_1"].apply(type).value_counts() Out[20]: col_1 <class 'str'> 4 Name: count, dtype: int64 或者您可以to_numeric()在读入数据后使用该函数强制转换数据类型, In [21]: df2 = pd.read_csv(StringIO(data)) In [22]: df2["col_1"] = pd.to_numeric(df2["col_1"], errors="coerce") In [23]: df2 Out[23]: col_1 0 1.00 1 2.00 2 NaN 3 4.22 In [24]: df2["col_1"].apply(type).value_counts() Out[24]: col_1 <class 'float'> 4 Name: count, dtype: int64 这会将所有有效解析转换为浮点数,将无效解析保留为NaN. 最终,如何处理包含混合数据类型的列的读取取决于您的特定需求。在上述情况下,如果您想NaN排除数据异常,那么to_numeric()可能是您的最佳选择。但是,如果您希望强制所有数据,无论类型如何,那么使用 参数converters肯定read_csv()值得尝试。 笔记 在某些情况下,读取包含混合数据类型的列的异常数据将导致数据集不一致。如果您依靠 pandas 来推断列的 dtypes,则解析引擎将go推断不同数据块的 dtypes,而不是立即推断整个数据集。因此,您最终可能会得到具有混合数据类型的列。例如, In [25]: col_1 = list(range(500000)) + ["a", "b"] + list(range(500000)) In [26]: df = pd.DataFrame({"col_1": col_1}) In [27]: df.to_csv("foo.csv") In [28]: mixed_df = pd.read_csv("foo.csv") In [29]: mixed_df["col_1"].apply(type).value_counts() Out[29]: col_1 <class 'int'> 737858 <class 'str'> 262144 Name: count, dtype: int64 In [30]: mixed_df["col_1"].dtype Out[30]: dtype('O') 由于读入数据的混合数据类型,将导致列的某些块mixed_df包含数据类型,而其他块则包含数据类型。需要注意的是,整个列将用 a of标记,用于列具有混合数据类型。intstrdtypeobject 设置dtype_backend="numpy_nullable"将导致每列的数据类型可为空。 In [31]: data = """a,b,c,d,e,f,g,h,i,j ....: 1,2.5,True,a,,,,,12-31-2019, ....: 3,4.5,False,b,6,7.5,True,a,12-31-2019, ....: """ ....: In [32]: df = pd.read_csv(StringIO(data), dtype_backend="numpy_nullable", parse_dates=["i"]) In [33]: df Out[33]: a b c d e f g h i j 0 1 2.5 True a <NA> <NA> <NA> <NA> 2019-12-31 <NA> 1 3 4.5 False b 6 7.5 True a 2019-12-31 <NA> In [34]: df.dtypes Out[34]: a Int64 b Float64 c boolean d string[python] e Int64 f Float64 g boolean h string[python] i datetime64[ns] j Int64 dtype: object 指定分类数据类型# Categoricaldtype='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可以使用 dict 规范将各个列解析为: In [39]: pd.read_csv(StringIO(data), dtype={"col1": "category"}).dtypes Out[39]: col1 category col2 object col3 int64 dtype: object 指定dtype='category'将导致无序,Categorical 其categories是数据中观察到的唯一值。为了更好地控制类别和顺序,请 CategoricalDtype提前创建一个,并将其传递给该列的dtype. In [40]: from pandas.api.types import CategoricalDtype In [41]: dtype = CategoricalDtype(["d", "c", "b", "a"], ordered=True) In [42]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).dtypes Out[42]: col1 category col2 object col3 int64 dtype: object 使用 时dtype=CategoricalDtype,超出范围的“意外”值 dtype.categories将被视为缺失值。 In [43]: dtype = CategoricalDtype(["a", "b", "d"]) # No 'c' In [44]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).col1 Out[44]: 0 a 1 a 2 NaN Name: col1, dtype: category Categories (3, object): ['a', 'b', 'd'] 这符合 的行为Categorical.set_categories()。 笔记 使用dtype='category',结果类别将始终被解析为字符串(对象数据类型)。如果类别是数字,则可以使用该to_numeric()函数进行转换,或者根据情况使用其他转换器,例如to_datetime(). 当dtypeaCategoricalDtype具有同构categories(所有数字、所有日期时间等)时,转换会自动完成。 In [45]: df = pd.read_csv(StringIO(data), dtype="category") In [46]: df.dtypes Out[46]: col1 category col2 category col3 category dtype: object In [47]: df["col3"] Out[47]: 0 1 1 2 2 3 Name: col3, dtype: category Categories (3, object): ['1', '2', '3'] In [48]: new_categories = pd.to_numeric(df["col3"].cat.categories) In [49]: df["col3"] = df["col3"].cat.rename_categories(new_categories) In [50]: df["col3"] Out[50]: 0 1 1 2 2 3 Name: col3, dtype: category Categories (3, int64): [1, 2, 3] 命名和使用列# 处理列名# 文件可能有也可能没有标题行。 pandas 假设第一行应用作列名称: In [51]: data = "a,b,c\n1,2,3\n4,5,6\n7,8,9" In [52]: print(data) a,b,c 1,2,3 4,5,6 7,8,9 In [53]: pd.read_csv(StringIO(data)) Out[53]: a b c 0 1 2 3 1 4 5 6 2 7 8 9 通过names结合指定参数,header您可以指示要使用的其他名称以及是否丢弃标题行(如果有): In [54]: print(data) a,b,c 1,2,3 4,5,6 7,8,9 In [55]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=0) Out[55]: foo bar baz 0 1 2 3 1 4 5 6 2 7 8 9 In [56]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=None) Out[56]: foo bar baz 0 a b c 1 1 2 3 2 4 5 6 3 7 8 9 如果标题位于第一行以外的行中,则将行号传递给 header。这将跳过前面的行: In [57]: data = "skip this skip it\na,b,c\n1,2,3\n4,5,6\n7,8,9" In [58]: pd.read_csv(StringIO(data), header=1) Out[58]: a b c 0 1 2 3 1 4 5 6 2 7 8 9 笔记 默认行为是推断列名:如果未传递任何名称,则行为与 相同header=0,并且从文件的第一个非空行推断列名,如果显式传递列名,则行为与 相同 header=None。 重复名称解析# 如果文件或头包含重复的名称,pandas 默认情况下会区分它们,以防止覆盖数据: In [59]: data = "a,b,a\n0,1,2\n3,4,5" In [60]: pd.read_csv(StringIO(data)) Out[60]: a b a.1 0 0 1 2 1 3 4 5 不再有重复数据,因为重复列“X”、...、“X”变为“X”、“X.1”、...、“X.N”。 过滤列 ( usecols) # 该usecols参数允许您使用列名称、位置号或可调用函数来选择文件中列的任何子集: In [61]: data = "a,b,c,d\n1,2,3,foo\n4,5,6,bar\n7,8,9,baz" In [62]: pd.read_csv(StringIO(data)) Out[62]: a b c d 0 1 2 3 foo 1 4 5 6 bar 2 7 8 9 baz In [63]: pd.read_csv(StringIO(data), usecols=["b", "d"]) Out[63]: b d 0 2 foo 1 5 bar 2 8 baz In [64]: pd.read_csv(StringIO(data), usecols=[0, 2, 3]) Out[64]: a c d 0 1 3 foo 1 4 6 bar 2 7 9 baz In [65]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["A", "C"]) Out[65]: a c 0 1 3 1 4 6 2 7 9 该usecols参数还可以用于指定在最终结果中不使用哪些列: In [66]: pd.read_csv(StringIO(data), usecols=lambda x: x not in ["a", "c"]) Out[66]: b d 0 2 foo 1 5 bar 2 8 baz 在这种情况下,可调用指定我们从输出中排除“a”和“c”列。 注释和空行# 忽略行注释和空行# 如果comment指定了该参数,则完全注释的行将被忽略。默认情况下,完全空白的行也将被忽略。 In [67]: data = "\na,b,c\n \n# commented line\n1,2,3\n\n4,5,6" In [68]: print(data) a,b,c # commented line 1,2,3 4,5,6 In [69]: pd.read_csv(StringIO(data), comment="#") Out[69]: a b c 0 1 2 3 1 4 5 6 如果skip_blank_lines=False,则read_csv不会忽略空行: In [70]: data = "a,b,c\n\n1,2,3\n\n\n4,5,6" In [71]: pd.read_csv(StringIO(data), skip_blank_lines=False) Out[71]: a b c 0 NaN NaN NaN 1 1.0 2.0 3.0 2 NaN NaN NaN 3 NaN NaN NaN 4 4.0 5.0 6.0 警告 被忽略的行的存在可能会产生涉及行号的歧义;参数header使用行号(忽略注释/空行),而skiprows使用行号(包括注释/空行): In [72]: data = "#comment\na,b,c\nA,B,C\n1,2,3" In [73]: pd.read_csv(StringIO(data), comment="#", header=1) Out[73]: A B C 0 1 2 3 In [74]: data = "A,B,C\n#comment\na,b,c\n1,2,3" In [75]: pd.read_csv(StringIO(data), comment="#", skiprows=2) Out[75]: a b c 0 1 2 3 如果同时指定了header和,则将相对于 的结尾。例如:skiprowsheaderskiprows In [76]: data = ( ....: "# empty\n" ....: "# second empty line\n" ....: "# third emptyline\n" ....: "X,Y,Z\n" ....: "1,2,3\n" ....: "A,B,C\n" ....: "1,2.,4.\n" ....: "5.,NaN,10.0\n" ....: ) ....: In [77]: print(data) # empty # second empty line # third emptyline X,Y,Z 1,2,3 A,B,C 1,2.,4. 5.,NaN,10.0 In [78]: pd.read_csv(StringIO(data), comment="#", skiprows=4, header=1) Out[78]: A B C 0 1.0 2.0 4.0 1 5.0 NaN 10.0 评论# 有时,注释或元数据可能包含在文件中: In [79]: data = ( ....: "ID,level,category\n" ....: "Patient1,123000,x # really unpleasant\n" ....: "Patient2,23000,y # wouldn't take his medicine\n" ....: "Patient3,1234018,z # awesome" ....: ) ....: In [80]: with open("tmp.csv", "w") as fh: ....: fh.write(data) ....: In [81]: print(open("tmp.csv").read()) ID,level,category Patient1,123000,x # really unpleasant Patient2,23000,y # wouldn't take his medicine Patient3,1234018,z # awesome 默认情况下,解析器在输出中包含注释: In [82]: df = pd.read_csv("tmp.csv") In [83]: df Out[83]: ID level category 0 Patient1 123000 x # really unpleasant 1 Patient2 23000 y # wouldn't take his medicine 2 Patient3 1234018 z # awesome 我们可以使用关键字来抑制评论comment: In [84]: df = pd.read_csv("tmp.csv", comment="#") In [85]: df Out[85]: ID level category 0 Patient1 123000 x 1 Patient2 23000 y 2 Patient3 1234018 z 处理 Unicode 数据# 该encoding参数应用于编码的 unicode 数据,这将导致字节字符串在结果中被解码为 unicode: In [86]: from io import BytesIO In [87]: data = b"word,length\n" b"Tr\xc3\xa4umen,7\n" b"Gr\xc3\xbc\xc3\x9fe,5" In [88]: data = data.decode("utf8").encode("latin-1") In [89]: df = pd.read_csv(BytesIO(data), encoding="latin-1") In [90]: df Out[90]: word length 0 Träumen 7 1 Grüße 5 In [91]: df["word"][1] Out[91]: 'Grüße' 某些将所有字符编码为多个字节的格式(例如 UTF-16)在不指定编码的情况下根本无法正确解析。Python 标准编码的完整列表。 索引列和尾随分隔符# 如果文件的数据列多于列名称的数量,则第一列将用作 的DataFrame行名称: In [92]: data = "a,b,c\n4,apple,bat,5.7\n8,orange,cow,10" In [93]: pd.read_csv(StringIO(data)) Out[93]: a b c 4 apple bat 5.7 8 orange cow 10.0 In [94]: data = "index,a,b,c\n4,apple,bat,5.7\n8,orange,cow,10" In [95]: pd.read_csv(StringIO(data), index_col=0) Out[95]: a b c index 4 apple bat 5.7 8 orange cow 10.0 通常,您可以使用该index_col选项来实现此行为。 在某些例外情况下,文件在每个数据行末尾都准备有分隔符,这会使解析器感到困惑。要显式禁用索引列推断并丢弃最后一列,请传递index_col=False: In [96]: data = "a,b,c\n4,apple,bat,\n8,orange,cow," In [97]: print(data) a,b,c 4,apple,bat, 8,orange,cow, In [98]: pd.read_csv(StringIO(data)) Out[98]: a b c 4 apple bat NaN 8 orange cow NaN In [99]: pd.read_csv(StringIO(data), index_col=False) Out[99]: a b c 0 4 apple bat 1 8 orange cow 如果使用该选项解析数据子集usecols,则 index_col规范基于该子集,而不是原始数据。 In [100]: data = "a,b,c\n4,apple,bat,\n8,orange,cow," In [101]: print(data) a,b,c 4,apple,bat, 8,orange,cow, In [102]: pd.read_csv(StringIO(data), usecols=["b", "c"]) Out[102]: b c 4 bat NaN 8 cow NaN In [103]: pd.read_csv(StringIO(data), usecols=["b", "c"], index_col=0) Out[103]: b c 4 bat NaN 8 cow NaN 日期处理# 指定日期列# 为了更好地方便处理日期时间数据,read_csv() 请使用关键字参数parse_dates并date_format 允许用户指定各种列和日期/时间格式以将输入文本数据转换为datetime对象。 最简单的情况就是直接传入parse_dates=True: In [104]: with open("foo.csv", mode="w") as f: .....: f.write("date,A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5") .....: # Use a column as an index, and parse it as dates. In [105]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True) In [106]: df Out[106]: A B C date 2009-01-01 a 1 2 2009-01-02 b 3 4 2009-01-03 c 4 5 # These are Python datetime objects In [107]: df.index Out[107]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None) 通常情况下,我们可能希望单独存储日期和时间数据,或者单独存储各种日期字段。关键字parse_dates可用于指定用于解析日期和/或时间的列的组合。 您可以将列列表指定为parse_dates,生成的日期列将被添加到输出之前(以免影响现有的列顺序),并且新列名称将是组件列名称的串联: In [108]: data = ( .....: "KORD,19990127, 19:00:00, 18:56:00, 0.8100\n" .....: "KORD,19990127, 20:00:00, 19:56:00, 0.0100\n" .....: "KORD,19990127, 21:00:00, 20:56:00, -0.5900\n" .....: "KORD,19990127, 21:00:00, 21:18:00, -0.9900\n" .....: "KORD,19990127, 22:00:00, 21:56:00, -0.5900\n" .....: "KORD,19990127, 23:00:00, 22:56:00, -0.5900" .....: ) .....: In [109]: with open("tmp.csv", "w") as fh: .....: fh.write(data) .....: In [110]: df = pd.read_csv("tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]]) In [111]: df Out[111]: 1_2 1_3 0 4 0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81 1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01 2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59 3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99 4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59 5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59 默认情况下,解析器会删除组件日期列,但您可以选择通过关键字保留它们keep_date_col: In [112]: df = pd.read_csv( .....: "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True .....: ) .....: In [113]: df Out[113]: 1_2 1_3 0 ... 2 3 4 0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD ... 19:00:00 18:56:00 0.81 1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD ... 20:00:00 19:56:00 0.01 2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD ... 21:00:00 20:56:00 -0.59 3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD ... 21:00:00 21:18:00 -0.99 4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD ... 22:00:00 21:56:00 -0.59 5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD ... 23:00:00 22:56:00 -0.59 [6 rows x 7 columns] 请注意,如果您希望将多个列合并为单个日期列,则必须使用嵌套列表。换句话说,表示第二列和第三列应分别解析为单独的日期列,而表示这两列应解析为单个列。parse_dates=[1, 2]parse_dates=[[1, 2]] 您还可以使用字典来指定自定义名称列: In [114]: date_spec = {"nominal": [1, 2], "actual": [1, 3]} In [115]: df = pd.read_csv("tmp.csv", header=None, parse_dates=date_spec) In [116]: df Out[116]: nominal actual 0 4 0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81 1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01 2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59 3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99 4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59 5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59 请务必记住,如果要将多个文本列解析为单个日期列,则会在数据前面添加一个新列。该index_col 规范基于这组新列而不是原始数据列: In [117]: date_spec = {"nominal": [1, 2], "actual": [1, 3]} In [118]: df = pd.read_csv( .....: "tmp.csv", header=None, parse_dates=date_spec, index_col=0 .....: ) # index is the nominal column .....: In [119]: df Out[119]: actual 0 4 nominal 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59 笔记 如果列或索引包含无法解析的日期,则整个列或索引将作为对象数据类型原样返回。对于非标准日期时间解析,请使用to_datetime()after pd.read_csv。 笔记 read_csv 有一个 fast_path 用于解析 iso8601 格式的日期时间字符串,例如“2000-01-01T00:01:02+00:00”和类似的变体。如果您可以安排数据以这种格式存储日期时间,加载时间将显着加快,已观察到大约 20 倍。 自版本 2.2.0 起已弃用:已弃用在 read_csv 中组合日期列。pd.to_datetime 请改为在相关结果列上使用。 日期解析函数# 最后,解析器允许您指定自定义date_format.就性能而言,您应该按顺序尝试以下解析日期的方法: 如果您知道格式,请使用date_format,例如: date_format="%d/%m/%Y"或。date_format={column_name: "%d/%m/%Y"} 如果您对不同的列使用不同的格式,或者想要将任何额外的选项(例如utc)传递给to_datetime,那么您应该将数据作为objectdtype 读入,然后使用to_datetime。 解析具有混合时区的 CSV # pandas 本身无法表示具有混合时区的列或索引。如果您的 CSV 文件包含混合时区的列,则默认结果将是带有字符串的 object-dtype 列,即使是parse_dates.要将混合时区值解析为日期时间列,请以objectdtype 读入,然后使用to_datetime()调用utc=True。 In [120]: content = """\ .....: a .....: 2000-01-01T00:00:00+05:00 .....: 2000-01-01T00:00:00+06:00""" .....: In [121]: df = pd.read_csv(StringIO(content)) In [122]: df["a"] = pd.to_datetime(df["a"], utc=True) In [123]: df["a"] Out[123]: 0 1999-12-31 19:00:00+00:00 1 1999-12-31 18:00:00+00:00 Name: a, dtype: datetime64[ns, UTC] 推断日期时间格式# 以下是一些可以猜测的日期时间字符串示例(全部代表 2011 年 12 月 30 日 00:00:00): “20111230” “2011/12/30” “20111230 00:00:00” “2011 年 12 月 30 日 00:00:00” “2011 年 12 月 30 日 00:00:00” “2011 年 12 月 30 日 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.parseformat= In [124]: df = pd.read_csv( .....: "foo.csv", .....: index_col=0, .....: parse_dates=True, .....: ) .....: In [125]: df Out[125]: A B C date 2009-01-01 a 1 2 2009-01-02 b 3 4 2009-01-03 c 4 5 如果同一列中有混合的日期时间格式,您可以传递format='mixed' In [126]: data = StringIO("date\n12 Jan 2000\n2000-01-13\n") In [127]: df = pd.read_csv(data) In [128]: df['date'] = pd.to_datetime(df['date'], format='mixed') In [129]: df Out[129]: date 0 2000-01-12 1 2000-01-13 或者,如果您的日期时间格式均为 ISO8601(格式可能不相同): In [130]: data = StringIO("date\n2020-01-01\n2020-01-01 03:00\n") In [131]: df = pd.read_csv(data) In [132]: df['date'] = pd.to_datetime(df['date'], format='ISO8601') In [133]: df Out[133]: date 0 2020-01-01 00:00:00 1 2020-01-01 03:00:00 国际日期格式# 虽然美国日期格式往往是 MM/DD/YYYY,但许多国际格式使用 DD/MM/YYYY。为了方便起见,dayfirst提供了一个关键字: In [134]: data = "date,value,cat\n1/6/2000,5,a\n2/6/2000,10,b\n3/6/2000,15,c" In [135]: print(data) date,value,cat 1/6/2000,5,a 2/6/2000,10,b 3/6/2000,15,c In [136]: with open("tmp.csv", "w") as fh: .....: fh.write(data) .....: In [137]: pd.read_csv("tmp.csv", parse_dates=[0]) Out[137]: date value cat 0 2000-01-06 5 a 1 2000-02-06 10 b 2 2000-03-06 15 c In [138]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0]) Out[138]: date value cat 0 2000-06-01 5 a 1 2000-06-02 10 b 2 2000-06-03 15 c 将 CSV 写入二进制文件对象# 1.2.0 版本中的新增内容。 df.to_csv(..., mode="wb")允许将 CSV 写入打开二进制模式的文件对象。在大多数情况下,不需要指定, mode因为 Pandas 会自动检测文件对象是以文本模式还是二进制模式打开。 In [139]: import io In [140]: data = pd.DataFrame([0, 1, 2]) In [141]: buffer = io.BytesIO() In [142]: data.to_csv(buffer, encoding="utf-8", compression="gzip") 指定浮点转换方法# 可以指定该参数float_precision,以便在使用 C 引擎进行解析期间使用特定的浮点转换器。选项包括普通转换器、高精度转换器和往返转换器(保证写入文件后值往返)。例如: In [143]: val = "0.3066101993807095471566981359501369297504425048828125" In [144]: data = "a,b,c\n1,2,{0}".format(val) In [145]: abs( .....: pd.read_csv( .....: StringIO(data), .....: engine="c", .....: float_precision=None, .....: )["c"][0] - float(val) .....: ) .....: Out[145]: 5.551115123125783e-17 In [146]: abs( .....: pd.read_csv( .....: StringIO(data), .....: engine="c", .....: float_precision="high", .....: )["c"][0] - float(val) .....: ) .....: Out[146]: 5.551115123125783e-17 In [147]: abs( .....: pd.read_csv(StringIO(data), engine="c", float_precision="round_trip")["c"][0] .....: - float(val) .....: ) .....: Out[147]: 0.0 千位分隔符# 对于使用千位分隔符编写的大数字,可以将关键字设置thousands为长度为 1 的字符串,以便正确解析整数: 默认情况下,带有千位分隔符的数字将被解析为字符串: In [148]: data = ( .....: "ID|level|category\n" .....: "Patient1|123,000|x\n" .....: "Patient2|23,000|y\n" .....: "Patient3|1,234,018|z" .....: ) .....: In [149]: with open("tmp.csv", "w") as fh: .....: fh.write(data) .....: In [150]: df = pd.read_csv("tmp.csv", sep="|") In [151]: df Out[151]: ID level category 0 Patient1 123,000 x 1 Patient2 23,000 y 2 Patient3 1,234,018 z In [152]: df.level.dtype Out[152]: dtype('O') 该thousands关键字允许正确解析整数: In [153]: df = pd.read_csv("tmp.csv", sep="|", thousands=",") In [154]: df Out[154]: ID level category 0 Patient1 123000 x 1 Patient2 23000 y 2 Patient3 1234018 z In [155]: df.level.dtype Out[155]: dtype('int64') NA值# 要控制将哪些值解析为缺失值(用 表示 NaN),请在 中指定一个字符串na_values。如果指定字符串列表,则其中的所有值都将被视为缺失值。如果您指定一个数字( a float、 like5.0或 an integerlike 5),相应的等效值也将意味着缺失值(在这种情况下实际上被识别为)。[5.0, 5]NaN 要完全覆盖被识别为缺失的默认值,请指定keep_default_na=False。 默认NaN识别值为。['-1.#IND', '1.#QNAN', '1.#IND', '-1.#QNAN', '#N/A N/A', '#N/A', 'N/A', 'n/a', 'NA', '<NA>', '#NA', 'NULL', 'null', 'NaN', '-NaN', 'nan', '-nan', 'None', ''] 让我们考虑一些例子: pd.read_csv("path_to_file.csv", na_values=[5]) 在上面的示例中,除了默认值之外,5和5.0还将被识别为。NaN字符串首先会被解释为 numeric 5,然后解释为 a NaN。 pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=[""]) 上面,只有空字段才会被识别为NaN。 pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=["NA", "0"]) 上面,NA和0作为字符串都是NaN。 pd.read_csv("path_to_file.csv", na_values=["Nope"]) 除了字符串之外,默认值也"Nope"被识别为 NaN。 无穷# inf类似的值将被解析为np.inf(正无穷大)和-inf(-np.inf负无穷大)。这些将忽略值的大小写,即Inf,也将被解析为np.inf。 布尔值# 常见值True、False、TRUE和FALSE都被识别为布尔值。有时您可能想将其他值识别为布尔值。为此,请使用true_values和false_values 选项,如下所示: In [156]: data = "a,b,c\n1,Yes,2\n3,No,4" In [157]: print(data) a,b,c 1,Yes,2 3,No,4 In [158]: pd.read_csv(StringIO(data)) Out[158]: a b c 0 1 Yes 2 1 3 No 4 In [159]: pd.read_csv(StringIO(data), true_values=["Yes"], false_values=["No"]) Out[159]: a b c 0 1 True 2 1 3 False 4 处理“坏”行# 某些文件可能存在格式错误的行,其中字段太少或太多。字段太少的行将在尾随字段中填充 NA 值。默认情况下,包含太多字段的行会引发错误: In [160]: data = "a,b,c\n1,2,3\n4,5,6,7\n8,9,10" In [161]: pd.read_csv(StringIO(data)) --------------------------------------------------------------------------- ParserError Traceback (most recent call last) Cell In[161], line 1 ----> 1 pd.read_csv(StringIO(data)) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1026, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend) 1013 kwds_defaults = _refine_defaults_read( 1014 dialect, 1015 delimiter, (...) 1022 dtype_backend=dtype_backend, 1023 ) 1024 kwds.update(kwds_defaults) -> 1026 return _read(filepath_or_buffer, kwds) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:626, in _read(filepath_or_buffer, kwds) 623 return parser 625 with parser: --> 626 return parser.read(nrows) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1923, in TextFileReader.read(self, nrows) 1916 nrows = validate_integer("nrows", nrows) 1917 try: 1918 # error: "ParserBase" has no attribute "read" 1919 ( 1920 index, 1921 columns, 1922 col_dict, -> 1923 ) = self._engine.read( # type: ignore[attr-defined] 1924 nrows 1925 ) 1926 except Exception: 1927 self.close() File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:234, in CParserWrapper.read(self, nrows) 232 try: 233 if self.low_memory: --> 234 chunks = self._reader.read_low_memory(nrows) 235 # destructive to chunks 236 data = _concatenate_chunks(chunks) File parsers.pyx:838, in pandas._libs.parsers.TextReader.read_low_memory() File parsers.pyx:905, in pandas._libs.parsers.TextReader._read_rows() File parsers.pyx:874, in pandas._libs.parsers.TextReader._tokenize_rows() File parsers.pyx:891, in pandas._libs.parsers.TextReader._check_tokenize_status() File parsers.pyx:2061, in pandas._libs.parsers.raise_parser_error() ParserError: Error tokenizing data. C error: Expected 3 fields in line 3, saw 4 您可以选择跳过坏行: In [162]: data = "a,b,c\n1,2,3\n4,5,6,7\n8,9,10" In [163]: pd.read_csv(StringIO(data), on_bad_lines="skip") Out[163]: a b c 0 1 2 3 1 8 9 10 1.4.0 版本中的新增功能。 或者传递一个可调用函数来处理坏行 if engine="python"。错误的行将是由 分割的字符串列表sep: In [164]: external_list = [] In [165]: def bad_lines_func(line): .....: external_list.append(line) .....: return line[-3:] .....: In [166]: external_list Out[166]: [] 笔记 可调用函数将仅处理包含太多字段的行。由其他错误引起的坏行将被默默地跳过。 In [167]: bad_lines_func = lambda line: print(line) In [168]: data = 'name,type\nname a,a is of type a\nname b,"b\" is of type b"' In [169]: data Out[169]: 'name,type\nname a,a is of type a\nname b,"b" is of type b"' In [170]: pd.read_csv(StringIO(data), on_bad_lines=bad_lines_func, engine="python") Out[170]: name type 0 name a a is of type a 在这种情况下,该行未被处理,因为这里的“坏行”是由转义字符引起的。 您还可以使用该usecols参数来消除出现在某些行中而不是其他行中的无关列数据: In [171]: pd.read_csv(StringIO(data), usecols=[0, 1, 2]) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[171], line 1 ----> 1 pd.read_csv(StringIO(data), usecols=[0, 1, 2]) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1026, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend) 1013 kwds_defaults = _refine_defaults_read( 1014 dialect, 1015 delimiter, (...) 1022 dtype_backend=dtype_backend, 1023 ) 1024 kwds.update(kwds_defaults) -> 1026 return _read(filepath_or_buffer, kwds) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:620, in _read(filepath_or_buffer, kwds) 617 _validate_names(kwds.get("names", None)) 619 # Create the parser. --> 620 parser = TextFileReader(filepath_or_buffer, **kwds) 622 if chunksize or iterator: 623 return parser File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1620, in TextFileReader.__init__(self, f, engine, **kwds) 1617 self.options["has_index_names"] = kwds["has_index_names"] 1619 self.handles: IOHandles | None = None -> 1620 self._engine = self._make_engine(f, self.engine) File ~/work/pandas/pandas/pandas/io/parsers/readers.py:1898, in TextFileReader._make_engine(self, f, engine) 1895 raise ValueError(msg) 1897 try: -> 1898 return mapping[engine](f, **self.options) 1899 except Exception: 1900 if self.handles is not None: File ~/work/pandas/pandas/pandas/io/parsers/c_parser_wrapper.py:155, in CParserWrapper.__init__(self, src, **kwds) 152 # error: Cannot determine type of 'names' 153 if len(self.names) < len(usecols): # type: ignore[has-type] 154 # error: Cannot determine type of 'names' --> 155 self._validate_usecols_names( 156 usecols, 157 self.names, # type: ignore[has-type] 158 ) 160 # error: Cannot determine type of 'names' 161 self._validate_parse_dates_presence(self.names) # type: ignore[has-type] File ~/work/pandas/pandas/pandas/io/parsers/base_parser.py:979, in ParserBase._validate_usecols_names(self, usecols, names) 977 missing = [c for c in usecols if c not in names] 978 if len(missing) > 0: --> 979 raise ValueError( 980 f"Usecols do not match columns, columns expected but not found: " 981 f"{missing}" 982 ) 984 return usecols ValueError: Usecols do not match columns, columns expected but not found: [0, 1, 2] 如果您想保留所有数据,包括具有太多字段的行,您可以指定足够数量的names.这可确保字段不足的行被填充NaN。 In [172]: pd.read_csv(StringIO(data), names=['a', 'b', 'c', 'd']) Out[172]: a b c d 0 name type NaN NaN 1 name a a is of type a NaN NaN 2 name b b is of type b" NaN NaN 方言# 该dialect关键字在指定文件格式方面提供了更大的灵活性。默认情况下,它使用 Excel 方言,但您可以指定方言名称或csv.Dialect实例。 假设您有未包含引号的数据: In [173]: data = "label1,label2,label3\n" 'index1,"a,c,e\n' "index2,b,d,f" In [174]: print(data) label1,label2,label3 index1,"a,c,e index2,b,d,f 默认情况下,read_csv使用 Excel 方言并将双引号视为引号字符,这会导致在找到结束双引号之前找到换行符时失败。 我们可以使用以下方法来解决这个问题dialect: In [175]: import csv In [176]: dia = csv.excel() In [177]: dia.quoting = csv.QUOTE_NONE In [178]: pd.read_csv(StringIO(data), dialect=dia) Out[178]: label1 label2 label3 index1 "a c e index2 b d f 所有方言选项都可以通过关键字参数单独指定: In [179]: data = "a,b,c~1,2,3~4,5,6" In [180]: pd.read_csv(StringIO(data), lineterminator="~") Out[180]: a b c 0 1 2 3 1 4 5 6 另一个常见的方言选项是skipinitialspace,跳过分隔符后的任何空格: In [181]: data = "a, b, c\n1, 2, 3\n4, 5, 6" In [182]: print(data) a, b, c 1, 2, 3 4, 5, 6 In [183]: pd.read_csv(StringIO(data), skipinitialspace=True) Out[183]: a b c 0 1 2 3 1 4 5 6 解析器尽一切努力“做正确的事”而不是脆弱的。类型推断是一件非常重要的事情。如果可以将列强制为整数数据类型而不更改内容,则解析器将这样做。任何非数字列都将作为对象 dtype 与其余 pandas 对象一样通过。 引用和转义字符# 嵌入字段中的引号(和其他转义字符)可以通过多种方式处理。一种方法是使用反斜杠;要正确解析此数据,您应该传递escapechar选项: In [184]: data = 'a,b\n"hello, \\"Bob\\", nice to see you",5' In [185]: print(data) a,b "hello, \"Bob\", nice to see you",5 In [186]: pd.read_csv(StringIO(data), escapechar="\\") Out[186]: a b 0 hello, "Bob", nice to see you 5 具有固定宽度列的文件# 在read_csv()读取分隔数据时,该read_fwf()函数适用于具有已知且固定列宽的数据文件。函数参数 toread_fwf与两个额外参数基本相同read_csv,并且参数的用法不同delimiter: colspecs:以半开间隔形式给出每行固定宽度字段范围的对(元组)列表(即 [from, to[ )。字符串值“infer”可用于指示解析器尝试从数据的前 100 行中检测列规范。如果未指定,默认行为是推断。 widths:如果间隔是连续的,则可以使用字段宽度列表来代替“colspecs”。 delimiter:在固定宽度文件中视为填充字符的字符。如果不是空格(例如“~”),可用于指定字段的填充字符。 考虑一个典型的固定宽度数据文件: In [187]: data1 = ( .....: "id8141 360.242940 149.910199 11950.7\n" .....: "id1594 444.953632 166.985655 11788.4\n" .....: "id1849 364.136849 183.628767 11806.2\n" .....: "id1230 413.836124 184.375703 11916.8\n" .....: "id1948 502.953953 173.237159 12468.3" .....: ) .....: In [188]: with open("bar.csv", "w") as f: .....: f.write(data1) .....: 为了将此文件解析为DataFrame,我们只需向函数提供列规范read_fwf以及文件名: # Column specifications are a list of half-intervals In [189]: colspecs = [(0, 6), (8, 20), (21, 33), (34, 43)] In [190]: df = pd.read_fwf("bar.csv", colspecs=colspecs, header=None, index_col=0) In [191]: df Out[191]: 1 2 3 0 id8141 360.242940 149.910199 11950.7 id1594 444.953632 166.985655 11788.4 id1849 364.136849 183.628767 11806.2 id1230 413.836124 184.375703 11916.8 id1948 502.953953 173.237159 12468.3 header=None请注意指定参数时解析器如何自动选择列名称 X.<列号> 。或者,您可以仅提供连续列的列宽: # Widths are a list of integers In [192]: widths = [6, 14, 13, 10] In [193]: df = pd.read_fwf("bar.csv", widths=widths, header=None) In [194]: df Out[194]: 0 1 2 3 0 id8141 360.242940 149.910199 11950.7 1 id1594 444.953632 166.985655 11788.4 2 id1849 364.136849 183.628767 11806.2 3 id1230 413.836124 184.375703 11916.8 4 id1948 502.953953 173.237159 12468.3 解析器将处理列周围的额外空白,因此可以在文件中的列之间进行额外的分隔。 默认情况下,将尝试使用文件的前 100 行来read_fwf推断文件。colspecs仅当列对齐并按提供的正确分隔delimiter(默认分隔符为空格)时,它才可以执行此操作。 In [195]: df = pd.read_fwf("bar.csv", header=None, index_col=0) In [196]: df Out[196]: 1 2 3 0 id8141 360.242940 149.910199 11950.7 id1594 444.953632 166.985655 11788.4 id1849 364.136849 183.628767 11806.2 id1230 413.836124 184.375703 11916.8 id1948 502.953953 173.237159 12468.3 read_fwf支持dtype用于指定解析列类型与推断类型不同的参数。 In [197]: pd.read_fwf("bar.csv", header=None, index_col=0).dtypes Out[197]: 1 float64 2 float64 3 float64 dtype: object In [198]: pd.read_fwf("bar.csv", header=None, dtype={2: "object"}).dtypes Out[198]: 0 object 1 float64 2 object 3 float64 dtype: object 索引# 具有“隐式”索引列的文件# 考虑一个文件,标题中的条目比数据列的数量少一个: In [199]: data = "A,B,C\n20090101,a,1,2\n20090102,b,3,4\n20090103,c,4,5" In [200]: print(data) A,B,C 20090101,a,1,2 20090102,b,3,4 20090103,c,4,5 In [201]: with open("foo.csv", "w") as f: .....: f.write(data) .....: 在这种特殊情况下,read_csv假设第一列将用作 的索引DataFrame: In [202]: pd.read_csv("foo.csv") Out[202]: A B C 20090101 a 1 2 20090102 b 3 4 20090103 c 4 5 请注意,日期不会自动解析。在这种情况下,您需要像以前一样执行以下操作: In [203]: df = pd.read_csv("foo.csv", parse_dates=True) In [204]: df.index Out[204]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', freq=None) 读取带有#的索引MultiIndex 假设您有由两列索引的数据: In [205]: data = 'year,indiv,zit,xit\n1977,"A",1.2,.6\n1977,"B",1.5,.5' In [206]: print(data) year,indiv,zit,xit 1977,"A",1.2,.6 1977,"B",1.5,.5 In [207]: with open("mindex_ex.csv", mode="w") as f: .....: f.write(data) .....: 的参数index_col可以read_csv采用列号列表将多个列转换MultiIndex为返回对象的索引: In [208]: df = pd.read_csv("mindex_ex.csv", index_col=[0, 1]) In [209]: df Out[209]: zit xit year indiv 1977 A 1.2 0.6 B 1.5 0.5 In [210]: df.loc[1977] Out[210]: zit xit indiv A 1.2 0.6 B 1.5 0.5 读取带有# 的列MultiIndex 通过指定参数的行位置列表header,您可以读取MultiIndex列的 a 。指定不连续的行将跳过中间的行。 In [211]: mi_idx = pd.MultiIndex.from_arrays([[1, 2, 3, 4], list("abcd")], names=list("ab")) In [212]: mi_col = pd.MultiIndex.from_arrays([[1, 2], list("ab")], names=list("cd")) In [213]: df = pd.DataFrame(np.ones((4, 2)), index=mi_idx, columns=mi_col) In [214]: df.to_csv("mi.csv") In [215]: print(open("mi.csv").read()) c,,1,2 d,,a,b a,b,, 1,a,1.0,1.0 2,b,1.0,1.0 3,c,1.0,1.0 4,d,1.0,1.0 In [216]: pd.read_csv("mi.csv", header=[0, 1, 2, 3], index_col=[0, 1]) Out[216]: c 1 2 d a b a Unnamed: 2_level_2 Unnamed: 3_level_2 1 1.0 1.0 2 b 1.0 1.0 3 c 1.0 1.0 4 d 1.0 1.0 read_csv还能够解释更常见的多列索引格式。 In [217]: data = ",a,a,a,b,c,c\n,q,r,s,t,u,v\none,1,2,3,4,5,6\ntwo,7,8,9,10,11,12" In [218]: print(data) ,a,a,a,b,c,c ,q,r,s,t,u,v one,1,2,3,4,5,6 two,7,8,9,10,11,12 In [219]: with open("mi2.csv", "w") as fh: .....: fh.write(data) .....: In [220]: pd.read_csv("mi2.csv", header=[0, 1], index_col=0) Out[220]: a b c q r s t u v one 1 2 3 4 5 6 two 7 8 9 10 11 12 笔记 如果index_col未指定(例如,您没有索引,或使用 编写索引,则列上的任何索引都将丢失。df.to_csv(..., index=False)names 自动“嗅探”分隔符# read_csv能够推断分隔(不一定是逗号分隔)文件,因为 pandas 使用csv.Sniffer csv 模块的类。为此,您必须指定sep=None. In [221]: df = pd.DataFrame(np.random.randn(10, 4)) In [222]: df.to_csv("tmp2.csv", sep=":", index=False) In [223]: pd.read_csv("tmp2.csv", sep=None, engine="python") Out[223]: 0 1 2 3 0 0.469112 -0.282863 -1.509059 -1.135632 1 1.212112 -0.173215 0.119209 -1.044236 2 -0.861849 -2.104569 -0.494929 1.071804 3 0.721555 -0.706771 -1.039575 0.271860 4 -0.424972 0.567020 0.276232 -1.087401 5 -0.673690 0.113648 -1.478427 0.524988 6 0.404705 0.577046 -1.715002 -1.039268 7 -0.370647 -1.157892 -1.344312 0.844885 8 1.075770 -0.109050 1.643563 -1.469388 9 0.357021 -0.674600 -1.776904 -0.968914 读取多个文件以创建单个 DataFrame # 最好用于concat()合并多个文件。请参阅食谱示例。 逐块迭代文件# 假设您希望延迟遍历一个(可能非常大)文件,而不是将整个文件读入内存,如下所示: In [224]: df = pd.DataFrame(np.random.randn(10, 4)) In [225]: df.to_csv("tmp.csv", index=False) In [226]: table = pd.read_csv("tmp.csv") In [227]: table Out[227]: 0 1 2 3 0 -1.294524 0.413738 0.276662 -0.472035 1 -0.013960 -0.362543 -0.006154 -0.923061 2 0.895717 0.805244 -1.206412 2.565646 3 1.431256 1.340309 -1.170299 -0.226169 4 0.410835 0.813850 0.132003 -0.827317 5 -0.076467 -1.187678 1.130127 -1.436737 6 -1.413681 1.607920 1.024180 0.569605 7 0.875906 -2.211372 0.974466 -2.006747 8 -0.410001 -0.078638 0.545952 -1.219217 9 -1.226825 0.769804 -1.281247 -0.727707 通过指定chunksizeto read_csv,返回值将是类型的可迭代对象TextFileReader: In [228]: with pd.read_csv("tmp.csv", chunksize=4) as reader: .....: print(reader) .....: for chunk in reader: .....: print(chunk) .....: <pandas.io.parsers.readers.TextFileReader object at 0x7ff2e5421db0> 0 1 2 3 0 -1.294524 0.413738 0.276662 -0.472035 1 -0.013960 -0.362543 -0.006154 -0.923061 2 0.895717 0.805244 -1.206412 2.565646 3 1.431256 1.340309 -1.170299 -0.226169 0 1 2 3 4 0.410835 0.813850 0.132003 -0.827317 5 -0.076467 -1.187678 1.130127 -1.436737 6 -1.413681 1.607920 1.024180 0.569605 7 0.875906 -2.211372 0.974466 -2.006747 0 1 2 3 8 -0.410001 -0.078638 0.545952 -1.219217 9 -1.226825 0.769804 -1.281247 -0.727707 版本 1.2 中的更改:read_csv/json/sas迭代文件时返回上下文管理器。 指定iterator=True还将返回该TextFileReader对象: In [229]: with pd.read_csv("tmp.csv", iterator=True) as reader: .....: print(reader.get_chunk(5)) .....: 0 1 2 3 0 -1.294524 0.413738 0.276662 -0.472035 1 -0.013960 -0.362543 -0.006154 -0.923061 2 0.895717 0.805244 -1.206412 2.565646 3 1.431256 1.340309 -1.170299 -0.226169 4 0.410835 0.813850 0.132003 -0.827317 指定解析器引擎# Pandas 目前支持三种引擎,C 引擎、python 引擎和实验性 pyarrow 引擎(需要软件包pyarrow)。一般来说,pyarrow 引擎在较大工作负载上速度最快,并且在大多数其他工作负载上速度与 C 引擎相当。在大多数工作负载上,Python 引擎往往比 pyarrow 和 C 引擎慢。然而,pyarrow 引擎的鲁棒性远不如 C 引擎,与 Python 引擎相比,C 引擎缺乏一些功能。 在可能的情况下,pandas 使用 C 解析器(指定为engine='c'),但如果指定了 C 不支持的选项,它可能会回退到 Python。 目前,C 和 pyarrow 引擎不支持的选项包括: sep除了单个字符(例如正则表达式分隔符) skipfooter sep=None和delim_whitespace=False ParserWarning除非使用 显式选择 python 引擎,否则指定上述任何选项都会生成engine='python'。 pyarrow 引擎不支持且未包含在上面列表中的选项包括: float_precision chunksize comment nrows thousands memory_map dialect on_bad_lines delim_whitespace quoting lineterminator converters decimal iterator dayfirst infer_datetime_format verbose skipinitialspace low_memory 指定这些选项engine='pyarrow'将引发ValueError. 读/写远程文件# 您可以传入 URL 来读取或写入远程文件到 pandas 的许多 IO 函数 - 以下示例显示读取 CSV 文件: df = pd.read_csv("https://download.bls.gov/pub/time.series/cu/cu.item", sep="\t") 1.3.0 版本中的新增功能。 通过将标头键值映射字典传递到storage_options关键字参数,可以将自定义标头与 HTTP(s) 请求一起发送,如下所示: headers = {"User-Agent": "pandas"} df = pd.read_csv( "https://download.bls.gov/pub/time.series/cu/cu.item", sep="\t", storage_options=headers ) 所有非本地文件或 HTTP 的 URL 均由 fsspec(如果已安装)及其各种文件系统实现(包括 Amazon S3、Google Cloud、SSH、FTP、webHDFS...)处理。其中一些实现需要安装额外的软件包,例如 S3 URL 需要s3fs库: df = pd.read_json("s3://pandas-test/adatafile.json") 在处理远程存储系统时,您可能需要使用环境变量或特殊位置的配置文件进行额外配置。例如,要访问 S3 存储桶中的数据,您需要使用S3Fs 文档中列出的几种方式之一定义凭据。对于几个存储后端来说也是如此,您应该点击fsimpl1上的链接来获取内置的实现,fsspec并点击 fsimpl2上的链接 来获取主发行fsspec 版中未包含的实现。 您还可以直接将参数传递给后端驱动程序。由于fsspec不使用AWS_S3_HOST环境变量,我们可以直接定义一个包含endpoint_url的字典并将该对象传递给存储选项参数: storage_options = {"client_kwargs": {"endpoint_url": "http://127.0.0.1:5555"}}} df = pd.read_json("s3://pandas-test/test-1", storage_options=storage_options) 更多示例配置和文档可以在S3Fs 文档中找到。 如果您没有S3凭证,您仍然可以通过指定匿名连接来访问公共数据,例如 1.2.0 版本中的新增内容。 pd.read_csv( "s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/SaKe2013" "-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv", storage_options={"anon": True}, ) fsspec还允许复杂的 URL,用于访问压缩档案中的数据、文件的本地缓存等。要在本地缓存上面的示例,您可以将调用修改为 pd.read_csv( "simplecache::s3://ncei-wcsd-archive/data/processed/SH1305/18kHz/" "SaKe2013-D20130523-T080854_to_SaKe2013-D20130523-T085643.csv", storage_options={"s3": {"anon": True}}, ) 我们指定“anon”参数用于实现的“s3”部分,而不是用于缓存实现。请注意,这仅在会话期间缓存到临时目录,但您也可以指定永久存储。 写出数据# 写入 CSV 格式# Series和对象DataFrame有一个实例方法to_csv,允许将对象的内容存储为逗号分隔值文件。该函数接受多个参数。仅需要第一个。 path_or_buf:要写入的文件或文件对象的字符串路径。如果文件对象必须用以下命令打开newline='' sep:输出文件的字段分隔符(默认“,”) na_rep:缺失值的字符串表示形式(默认“”) float_format:浮点数的格式字符串 columns:要写入的列(默认无) header:是否写出列名(默认True) index:是否写入行(索引)名称(默认True) index_label:索引列的列标签(如果需要)。如果无(默认),并且header和index为 True,则使用索引名称。 (如果使用多索引,则应给出序列DataFrame)。 mode:Python写入模式,默认'w' encoding:表示内容为非 ASCII 时要使用的编码的字符串,适用于 3 之前的 Python 版本 lineterminator:表示行结束的字符序列(默认os.linesep) quoting:设置 csv 模块中的引用规则(默认 csv.QUOTE_MINIMAL)。请注意,如果您设置了 a float_format,则浮点数将转换为字符串,并且 csv.QUOTE_NONNUMERIC 会将它们视为非数字 quotechar:用于引用字段的字符(默认“”) doublequote:控制 in 字段的引用quotechar(默认 True) escapecharsep: 适当时用于转义的字符quotechar(默认无) chunksize:一次写入的行数 date_format:日期时间对象的格式字符串 写入格式化字符串# 该DataFrame对象有一个实例方法to_string,可以控制该对象的字符串表示形式。所有参数都是可选的: buf默认 None,例如 StringIO 对象 columns默认 None,写入哪些列 col_space默认无,每列的最小宽度。 na_repdefault NaN, NA值的表示 formatters默认 None,函数的字典(按列),每个函数采用单个参数并返回格式化字符串 float_format默认 None,该函数采用单个(浮点)参数并返回格式化字符串;应用于 中的浮动 DataFrame。 sparsifyDataFrame默认为 True,对于具有分层索引的情况,设置为 False以打印每行的每个 MultiIndex 键。 index_names默认 True,将打印索引的名称 index默认 True,将打印索引(即行标签) header默认 True,将打印列标签 justify默认left,将左对齐或右对齐打印列标题 该Series对象还有一个to_string方法,但只有buf, na_rep,float_format参数。还有一个length参数,如果设置为True,将另外输出系列的长度。 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 类似于 {index -> [index], columns -> [columns], data -> [values]} 的字典 records 列表如 [{列 -> 值}, … , {列 -> 值}] index 像 {index -> {column -> value}} 这样的字典 columns 像 {column -> {index -> value}} 这样的字典 values 只是值数组 table 遵守 JSON表架构 date_format:字符串,日期转换类型,'epoch' 为时间戳,'iso' 为 ISO8601。 double_precision:编码浮点值时使用的小数位数,默认 10。 force_ascii:强制编码字符串为ASCII,默认True。 date_unit:编码的时间单位,控制时间戳和 ISO8601 精度。 “s”、“ms”、“us”或“ns”之一分别表示秒、毫秒、微秒和纳秒。默认“毫秒”。 default_handler:如果无法将对象转换为合适的 JSON 格式,则调用处理程序。接受一个参数,即要转换的对象,并返回一个可序列化的对象。 lines:如果是recordsorient,则将每行的每条记录写为 json。 mode:字符串,写入路径时的写入器模式。 'w' 表示写入,'a' 表示追加。默认“w” 注意NaN's, NaT's 和None会被转换为null和datetime对象,并且会根据date_format和date_unit参数进行转换。 In [230]: dfj = pd.DataFrame(np.random.randn(5, 2), columns=list("AB")) In [231]: json = dfj.to_json() In [232]: json Out[232]: '{"A":{"0":-0.1213062281,"1":0.6957746499,"2":0.9597255933,"3":-0.6199759194,"4":-0.7323393705},"B":{"0":-0.0978826728,"1":0.3417343559,"2":-1.1103361029,"3":0.1497483186,"4":0.6877383895}}' 方向选项# 生成的 JSON 文件/字符串的格式有多种不同的选项。考虑以下几点DataFrame和Series: In [233]: dfjo = pd.DataFrame( .....: dict(A=range(1, 4), B=range(4, 7), C=range(7, 10)), .....: columns=list("ABC"), .....: index=list("xyz"), .....: ) .....: In [234]: dfjo Out[234]: A B C x 1 4 7 y 2 5 8 z 3 6 9 In [235]: sjo = pd.Series(dict(x=15, y=16, z=17), name="D") In [236]: sjo Out[236]: x 15 y 16 z 17 Name: D, dtype: int64 面向列(默认DataFrame)将数据序列化为嵌套 JSON 对象,其中列标签充当主索引: In [237]: dfjo.to_json(orient="columns") Out[237]: '{"A":{"x":1,"y":2,"z":3},"B":{"x":4,"y":5,"z":6},"C":{"x":7,"y":8,"z":9}}' # Not available for Series 面向索引(默认Series)与面向列类似,但索引标签现在是主要的: In [238]: dfjo.to_json(orient="index") Out[238]: '{"x":{"A":1,"B":4,"C":7},"y":{"A":2,"B":5,"C":8},"z":{"A":3,"B":6,"C":9}}' In [239]: sjo.to_json(orient="index") Out[239]: '{"x":15,"y":16,"z":17}' 面向记录将数据序列化为列 -> 值记录的 JSON 数组,不包括索引标签。这对于将DataFrame数据传递到绘图库非常有用,例如 JavaScript 库d3.js: In [240]: dfjo.to_json(orient="records") Out[240]: '[{"A":1,"B":4,"C":7},{"A":2,"B":5,"C":8},{"A":3,"B":6,"C":9}]' In [241]: sjo.to_json(orient="records") Out[241]: '[15,16,17]' 面向值是一个简单的选项,它仅序列化为嵌套的 JSON 值数组,不包括列和索引标签: In [242]: dfjo.to_json(orient="values") Out[242]: '[[1,4,7],[2,5,8],[3,6,9]]' # Not available for Series 面向拆分序列化为包含值、索引和列的单独条目的 JSON 对象。名称还包括Series: In [243]: dfjo.to_json(orient="split") Out[243]: '{"columns":["A","B","C"],"index":["x","y","z"],"data":[[1,4,7],[2,5,8],[3,6,9]]}' In [244]: sjo.to_json(orient="split") Out[244]: '{"name":"D","index":["x","y","z"],"data":[15,16,17]}' 面向表的序列化为 JSON Table Schema,允许保存元数据,包括但不限于数据类型和索引名称。 笔记 任何编码为 JSON 对象的 orient 选项在往返序列化期间都不会保留索引和列标签的顺序。如果您希望保留标签排序,请使用该split选项,因为它使用有序容器。 日期处理# 以 ISO 日期格式写入: In [245]: dfd = pd.DataFrame(np.random.randn(5, 2), columns=list("AB")) In [246]: dfd["date"] = pd.Timestamp("20130101") In [247]: dfd = dfd.sort_index(axis=1, ascending=False) In [248]: json = dfd.to_json(date_format="iso") In [249]: json Out[249]: '{"date":{"0":"2013-01-01T00:00:00.000","1":"2013-01-01T00:00:00.000","2":"2013-01-01T00:00:00.000","3":"2013-01-01T00:00:00.000","4":"2013-01-01T00:00:00.000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}' 以 ISO 日期格式写入,以微秒为单位: In [250]: json = dfd.to_json(date_format="iso", date_unit="us") In [251]: json Out[251]: '{"date":{"0":"2013-01-01T00:00:00.000000","1":"2013-01-01T00:00:00.000000","2":"2013-01-01T00:00:00.000000","3":"2013-01-01T00:00:00.000000","4":"2013-01-01T00:00:00.000000"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}' 纪元时间戳,以秒为单位: In [252]: json = dfd.to_json(date_format="epoch", date_unit="s") In [253]: json Out[253]: '{"date":{"0":1,"1":1,"2":1,"3":1,"4":1},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}' 写入带有日期索引和日期列的文件: In [254]: dfj2 = dfj.copy() In [255]: dfj2["date"] = pd.Timestamp("20130101") In [256]: dfj2["ints"] = list(range(5)) In [257]: dfj2["bools"] = True In [258]: dfj2.index = pd.date_range("20130101", periods=5) In [259]: dfj2.to_json("test.json") In [260]: with open("test.json") as fh: .....: print(fh.read()) .....: {"A":{"1356998400000":-0.1213062281,"1357084800000":0.6957746499,"1357171200000":0.9597255933,"1357257600000":-0.6199759194,"1357344000000":-0.7323393705},"B":{"1356998400000":-0.0978826728,"1357084800000":0.3417343559,"1357171200000":-1.1103361029,"1357257600000":0.1497483186,"1357344000000":0.6877383895},"date":{"1356998400000":1356,"1357084800000":1356,"1357171200000":1356,"1357257600000":1356,"1357344000000":1356},"ints":{"1356998400000":0,"1357084800000":1,"1357171200000":2,"1357257600000":3,"1357344000000":4},"bools":{"1356998400000":true,"1357084800000":true,"1357171200000":true,"1357257600000":true,"1357344000000":true}} 回退行为# 如果 JSON 序列化程序无法直接处理容器内容,它将按以下方式回退: 如果 dtype 不受支持(例如np.complex_)default_handler,则将为每个值调用 (如果提供),否则会引发异常。 如果一个对象不受支持,它将尝试以下操作: 检查对象是否定义了toDict方法并调用它。方法toDict应该返回一个dict,然后将其进行 JSON 序列化。 default_handler如果提供了则调用。 dict通过遍历对象的内容将对象转换为 a 。然而,这通常会失败OverflowError并产生意想不到的结果。 一般来说,针对不受支持的对象或数据类型的最佳方法是提供default_handler.例如: >>> DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json() # raises RuntimeError: Unhandled numpy dtype 15 可以通过指定一个简单的方法来处理default_handler: In [261]: pd.DataFrame([1.0, 2.0, complex(1.0, 2.0)]).to_json(default_handler=str) Out[261]: '{"0":{"0":"(1+0j)","1":"(2+0j)","2":"(1+2j)"}}' 读取 JSON # 将 JSON 字符串读取到 pandas 对象可以采用多个参数。解析器将尝试解析DataFrameif typis not displayed 或 is None。要显式强制Series解析,请传递typ=series filepath_or_buffer:有效的JSON 字符串或文件句柄/StringIO。该字符串可以是 URL。有效的 URL 方案包括 http、ftp、S3 和 file。对于文件 URL,需要一个主机。例如,本地文件可以是 file://localhost/path/to/table.json typ:要恢复的对象类型(系列或帧),默认“帧” orient: 系列 : 默认是index 允许的值为 { split, records, index} 数据框 默认是columns 允许的值为 { split, records, index, columns, values, table} JSON 字符串的格式 split 类似于 {index -> [index], columns -> [columns], data -> [values]} 的字典 records 列表如 [{列 -> 值}, … , {列 -> 值}] index 像 {index -> {column -> value}} 这样的字典 columns 像 {column -> {index -> value}} 这样的字典 values 只是值数组 table 遵守 JSON表架构 dtype:如果为 True,则推断 dtypes,如果将列的字典转换为 dtype,则使用这些,如果False,则根本不推断 dtypes,默认为 True,仅适用于数据。 convert_axes:布尔值,尝试将轴转换为正确的数据类型,默认为True convert_dates:要解析日期的列列表;如果True,则尝试解析类似日期的列,默认为True。 keep_default_dates:布尔值,默认值True。如果解析日期,则解析默认的类似日期的列。 precise_float:布尔值,默认值False。设置为在将字符串解码为双精度值时启用更高精度 (strtod) 函数。默认 ( False) 是使用快速但不太精确的内置功能。 date_unit:字符串,检测是否转换日期的时间戳单位。默认无。默认情况下,将检测时间戳精度,如果不需要,则传递“s”、“ms”、“us”或“ns”之一以分别将时间戳精度强制为秒、毫秒、微秒或纳秒。 lines:将文件读取为每行一个 json 对象。 encoding:用于解码 py3 字节的编码。 chunksize:与 结合使用时lines=True,返回每次迭代pandas.api.typing.JsonReader读取chunksize行数的 a。 engine:要么是"ujson"内置的 JSON 解析器,要么"pyarrow"分派到 pyarrow 的pyarrow.json.read_json.仅"pyarrow"当以下情况下才可用lines=True ValueError/TypeError/AssertionError如果 JSON 不可解析,解析器将引发其中之一。 如果在编码为 JSON 时使用非默认值,orient请务必在此处传递相同的选项,以便解码产生合理的结果,请参阅定向选项以获取概述。 数据转换# convert_axes=True、dtype=True、 和的默认值convert_dates=True 将尝试将轴和所有数据解析为适当的类型,包括日期。如果您需要覆盖特定的数据类型,请将字典传递给 dtype.仅当您需要在轴中保留类似字符串的数字(例如“1”、“2”)时才convert_axes应设置为。False 笔记 convert_dates=True如果数据和/或列标签显示为“类似日期”,则大整数值可以转换为日期。确切的阈值取决于date_unit指定的。 “类似日期”意味着列标签满足以下条件之一: 它结束于'_at' 它结束于'_time' 它开始于'timestamp' 这是'modified' 这是'date' 警告 读取 JSON 数据时,自动强制转换为 dtypes 有一些怪癖: 索引可以按照与序列化不同的顺序重建,即不保证返回的顺序与序列化之前相同 如果可以安全地完成,float将转换为数据列,例如一列integer1. integerbool 列将在重建时转换为 因此,有时您可能希望通过dtype关键字参数指定特定的数据类型。 从 JSON 字符串读取: In [262]: from io import StringIO In [263]: pd.read_json(StringIO(json)) Out[263]: date B A 0 1 0.403310 0.176444 1 1 0.301624 -0.154951 2 1 -1.369849 -2.179861 3 1 1.462696 -0.954208 4 1 -0.826591 -1.743161 从文件中读取: In [264]: pd.read_json("test.json") Out[264]: A B date ints bools 2013-01-01 -0.121306 -0.097883 1356 0 True 2013-01-02 0.695775 0.341734 1356 1 True 2013-01-03 0.959726 -1.110336 1356 2 True 2013-01-04 -0.619976 0.149748 1356 3 True 2013-01-05 -0.732339 0.687738 1356 4 True 不转换任何数据(但仍转换轴和日期): In [265]: pd.read_json("test.json", dtype=object).dtypes Out[265]: A object B object date object ints object bools object dtype: object 指定转换的数据类型: In [266]: pd.read_json("test.json", dtype={"A": "float32", "bools": "int8"}).dtypes Out[266]: A float32 B float64 date int64 ints int64 bools int8 dtype: object 保留字符串索引: In [267]: from io import StringIO In [268]: si = pd.DataFrame( .....: np.zeros((4, 4)), columns=list(range(4)), index=[str(i) for i in range(4)] .....: ) .....: In [269]: si Out[269]: 0 1 2 3 0 0.0 0.0 0.0 0.0 1 0.0 0.0 0.0 0.0 2 0.0 0.0 0.0 0.0 3 0.0 0.0 0.0 0.0 In [270]: si.index Out[270]: Index(['0', '1', '2', '3'], dtype='object') In [271]: si.columns Out[271]: Index([0, 1, 2, 3], dtype='int64') In [272]: json = si.to_json() In [273]: sij = pd.read_json(StringIO(json), convert_axes=False) In [274]: sij Out[274]: 0 1 2 3 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 3 0 0 0 0 In [275]: sij.index Out[275]: Index(['0', '1', '2', '3'], dtype='object') In [276]: sij.columns Out[276]: Index(['0', '1', '2', '3'], dtype='object') 以纳秒为单位写入的日期需要以纳秒为单位读回: In [277]: from io import StringIO In [278]: json = dfj2.to_json(date_unit="ns") # Try to parse timestamps as milliseconds -> Won't Work In [279]: dfju = pd.read_json(StringIO(json), date_unit="ms") In [280]: dfju Out[280]: A B date ints bools 1356998400000000000 -0.121306 -0.097883 1356998400 0 True 1357084800000000000 0.695775 0.341734 1356998400 1 True 1357171200000000000 0.959726 -1.110336 1356998400 2 True 1357257600000000000 -0.619976 0.149748 1356998400 3 True 1357344000000000000 -0.732339 0.687738 1356998400 4 True # Let pandas detect the correct precision In [281]: dfju = pd.read_json(StringIO(json)) In [282]: dfju Out[282]: A B date ints bools 2013-01-01 -0.121306 -0.097883 2013-01-01 0 True 2013-01-02 0.695775 0.341734 2013-01-01 1 True 2013-01-03 0.959726 -1.110336 2013-01-01 2 True 2013-01-04 -0.619976 0.149748 2013-01-01 3 True 2013-01-05 -0.732339 0.687738 2013-01-01 4 True # Or specify that all timestamps are in nanoseconds In [283]: dfju = pd.read_json(StringIO(json), date_unit="ns") In [284]: dfju Out[284]: A B date ints bools 2013-01-01 -0.121306 -0.097883 1356998400 0 True 2013-01-02 0.695775 0.341734 1356998400 1 True 2013-01-03 0.959726 -1.110336 1356998400 2 True 2013-01-04 -0.619976 0.149748 1356998400 3 True 2013-01-05 -0.732339 0.687738 1356998400 4 True 通过设置dtype_backend参数,您可以控制用于生成的 DataFrame 的默认数据类型。 In [285]: data = ( .....: '{"a":{"0":1,"1":3},"b":{"0":2.5,"1":4.5},"c":{"0":true,"1":false},"d":{"0":"a","1":"b"},' .....: '"e":{"0":null,"1":6.0},"f":{"0":null,"1":7.5},"g":{"0":null,"1":true},"h":{"0":null,"1":"a"},' .....: '"i":{"0":"12-31-2019","1":"12-31-2019"},"j":{"0":null,"1":null}}' .....: ) .....: In [286]: df = pd.read_json(StringIO(data), dtype_backend="pyarrow") In [287]: df Out[287]: a b c d e f g h i j 0 1 2.5 True a <NA> <NA> <NA> <NA> 12-31-2019 None 1 3 4.5 False b 6 7.5 True a 12-31-2019 None In [288]: df.dtypes Out[288]: a int64[pyarrow] b double[pyarrow] c bool[pyarrow] d string[pyarrow] e int64[pyarrow] f double[pyarrow] g bool[pyarrow] h string[pyarrow] i string[pyarrow] j null[pyarrow] dtype: object 标准化# pandas 提供了一个实用函数来获取字典或字典列表并将此半结构化数据规范化为平面表。 In [289]: data = [ .....: {"id": 1, "name": {"first": "Coleen", "last": "Volk"}}, .....: {"name": {"given": "Mark", "family": "Regner"}}, .....: {"id": 2, "name": "Faye Raker"}, .....: ] .....: In [290]: pd.json_normalize(data) Out[290]: id name.first name.last name.given name.family name 0 1.0 Coleen Volk NaN NaN NaN 1 NaN NaN NaN Mark Regner NaN 2 2.0 NaN NaN NaN NaN Faye Raker In [291]: data = [ .....: { .....: "state": "Florida", .....: "shortname": "FL", .....: "info": {"governor": "Rick Scott"}, .....: "county": [ .....: {"name": "Dade", "population": 12345}, .....: {"name": "Broward", "population": 40000}, .....: {"name": "Palm Beach", "population": 60000}, .....: ], .....: }, .....: { .....: "state": "Ohio", .....: "shortname": "OH", .....: "info": {"governor": "John Kasich"}, .....: "county": [ .....: {"name": "Summit", "population": 1234}, .....: {"name": "Cuyahoga", "population": 1337}, .....: ], .....: }, .....: ] .....: In [292]: pd.json_normalize(data, "county", ["state", "shortname", ["info", "governor"]]) Out[292]: name population state shortname info.governor 0 Dade 12345 Florida FL Rick Scott 1 Broward 40000 Florida FL Rick Scott 2 Palm Beach 60000 Florida FL Rick Scott 3 Summit 1234 Ohio OH John Kasich 4 Cuyahoga 1337 Ohio OH John Kasich max_level 参数提供了对结束标准化的级别的更多控制。当 max_level=1 时,以下代码片段将标准化,直到提供的字典的第一个嵌套级别。 In [293]: data = [ .....: { .....: "CreatedBy": {"Name": "User001"}, .....: "Lookup": { .....: "TextField": "Some text", .....: "UserField": {"Id": "ID001", "Name": "Name001"}, .....: }, .....: "Image": {"a": "b"}, .....: } .....: ] .....: In [294]: pd.json_normalize(data, max_level=1) Out[294]: CreatedBy.Name Lookup.TextField Lookup.UserField Image.a 0 User001 Some text {'Id': 'ID001', 'Name': 'Name001'} b 行分隔 json # pandas 能够读取和写入行分隔的 json 文件,这些文件在使用 Hadoop 或 Spark 的数据处理管道中很常见。 chunksize对于行分隔的 json 文件,pandas 还可以返回一个迭代器,一次读取行。这对于大文件或从流中读取非常有用。 In [295]: from io import StringIO In [296]: jsonl = """ .....: {"a": 1, "b": 2} .....: {"a": 3, "b": 4} .....: """ .....: In [297]: df = pd.read_json(StringIO(jsonl), lines=True) In [298]: df Out[298]: a b 0 1 2 1 3 4 In [299]: df.to_json(orient="records", lines=True) Out[299]: '{"a":1,"b":2}\n{"a":3,"b":4}\n' # reader is an iterator that returns ``chunksize`` lines each iteration In [300]: with pd.read_json(StringIO(jsonl), lines=True, chunksize=1) as reader: .....: reader .....: for chunk in reader: .....: print(chunk) .....: Empty DataFrame Columns: [] Index: [] a b 0 1 2 a b 1 3 4 还可以通过指定 来使用 pyarrow reader 读取行限制的 json engine="pyarrow"。 In [301]: from io import BytesIO In [302]: df = pd.read_json(BytesIO(jsonl.encode()), lines=True, engine="pyarrow") In [303]: df Out[303]: a b 0 1 2 1 3 4 2.0.0 版本中的新增内容。 表架构# 表架构是将表格数据集描述为 JSON 对象的规范。 JSON 包含有关字段名称、类型和其他属性的信息。您可以使用 orienttable构建包含两个字段schema和 的JSON 字符串data。 In [304]: df = pd.DataFrame( .....: { .....: "A": [1, 2, 3], .....: "B": ["a", "b", "c"], .....: "C": pd.date_range("2016-01-01", freq="d", periods=3), .....: }, .....: index=pd.Index(range(3), name="idx"), .....: ) .....: In [305]: df Out[305]: A B C idx 0 1 a 2016-01-01 1 2 b 2016-01-02 2 3 c 2016-01-03 In [306]: df.to_json(orient="table", date_format="iso") Out[306]: '{"schema":{"fields":[{"name":"idx","type":"integer"},{"name":"A","type":"integer"},{"name":"B","type":"string"},{"name":"C","type":"datetime"}],"primaryKey":["idx"],"pandas_version":"1.4.0"},"data":[{"idx":0,"A":1,"B":"a","C":"2016-01-01T00:00:00.000"},{"idx":1,"A":2,"B":"b","C":"2016-01-02T00:00:00.000"},{"idx":2,"A":3,"B":"c","C":"2016-01-03T00:00:00.000"}]}' 该schema字段包含fields键,键本身包含列名到类型对的列表,包括Indexor MultiIndex (请参阅下面的类型列表)。如果(多)索引是唯一的,则该schema字段还包含一个字段。primaryKey 第二个字段data包含带有方向的序列化数据records 。索引已包含在内,并且任何日期时间均采用 ISO 8601 格式,符合表架构规范的要求。 表架构规范中描述了支持的类型的完整列表。下表显示了 pandas 类型的映射: 熊猫型 表架构类型 整型64 整数 浮动64 数字 布尔值 布尔值 日期时间64[ns] 约会时间 时间增量64[ns] 期间 绝对的 任何 目的 斯特 关于生成的表架构的一些注释: 该schema对象包含一个pandas_version字段。这包含 pandas 模式方言的版本,并将随着每次修订而增加。 序列化时所有日期都会转换为 UTC。即使是时区原始值,也被视为偏移量为 0 的 UTC。 In [307]: from pandas.io.json import build_table_schema In [308]: s = pd.Series(pd.date_range("2016", periods=4)) In [309]: build_table_schema(s) Out[309]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'datetime'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} tz带有时区的日期时间(序列化之前),包含一个带有时区名称的附加字段 (例如'US/Central')。 In [310]: s_tz = pd.Series(pd.date_range("2016", periods=12, tz="US/Central")) In [311]: build_table_schema(s_tz) Out[311]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'datetime', 'tz': 'US/Central'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} 周期在序列化之前转换为时间戳,因此具有与转换为 UTC 相同的行为。此外,周期将包含freq带有周期频率的附加字段,例如'A-DEC'。 In [312]: s_per = pd.Series(1, index=pd.period_range("2016", freq="Y-DEC", periods=4)) In [313]: build_table_schema(s_per) Out[313]: {'fields': [{'name': 'index', 'type': 'datetime', 'freq': 'YE-DEC'}, {'name': 'values', 'type': 'integer'}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} 分类使用any类型和enum列出可能值集的约束。此外,还ordered包括一个字段: In [314]: s_cat = pd.Series(pd.Categorical(["a", "b", "a"])) In [315]: build_table_schema(s_cat) Out[315]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'any', 'constraints': {'enum': ['a', 'b']}, 'ordered': False}], 'primaryKey': ['index'], 'pandas_version': '1.4.0'} 如果索引是唯一的primaryKey,则包含包含标签数组的 字段: In [316]: s_dupe = pd.Series([1, 2], index=[1, 1]) In [317]: build_table_schema(s_dupe) Out[317]: {'fields': [{'name': 'index', 'type': 'integer'}, {'name': 'values', 'type': 'integer'}], 'pandas_version': '1.4.0'} 其primaryKey行为与 MultiIndexes 相同,但在本例中primaryKey是一个数组: In [318]: s_multi = pd.Series(1, index=pd.MultiIndex.from_product([("a", "b"), (0, 1)])) In [319]: build_table_schema(s_multi) Out[319]: {'fields': [{'name': 'level_0', 'type': 'string'}, {'name': 'level_1', 'type': 'integer'}, {'name': 'values', 'type': 'integer'}], 'primaryKey': FrozenList(['level_0', 'level_1']), 'pandas_version': '1.4.0'} 默认命名大致遵循以下规则: 对于系列,object.name使用 。如果没有,那么名字就是values 对于DataFrames,使用列名的字符串化版本 For Index(not MultiIndex),index.name使用,index如果为 None 则回退。 对于MultiIndex,mi.names使用。如果任何级别没有名称,则level_<i>使用该级别。 read_json也接受orient='table'作为参数。这允许以可往返的方式保存元数据,例如数据类型和索引名称。 In [320]: df = pd.DataFrame( .....: { .....: "foo": [1, 2, 3, 4], .....: "bar": ["a", "b", "c", "d"], .....: "baz": pd.date_range("2018-01-01", freq="d", periods=4), .....: "qux": pd.Categorical(["a", "b", "c", "c"]), .....: }, .....: index=pd.Index(range(4), name="idx"), .....: ) .....: In [321]: df Out[321]: foo bar baz qux idx 0 1 a 2018-01-01 a 1 2 b 2018-01-02 b 2 3 c 2018-01-03 c 3 4 d 2018-01-04 c In [322]: df.dtypes Out[322]: foo int64 bar object baz datetime64[ns] qux category dtype: object In [323]: df.to_json("test.json", orient="table") In [324]: new_df = pd.read_json("test.json", orient="table") In [325]: new_df Out[325]: foo bar baz qux idx 0 1 a 2018-01-01 a 1 2 b 2018-01-02 b 2 3 c 2018-01-03 c 3 4 d 2018-01-04 c In [326]: new_df.dtypes Out[326]: foo int64 bar object baz datetime64[ns] qux category dtype: object 请注意,作为 an 名称的文字字符串“index”Index 不可往返,也不能以'level_'a 开头的 任何名称MultiIndex。默认情况下,它们用于DataFrame.to_json()指示缺失值,并且后续读取无法区分意图。 In [327]: df.index.name = "index" In [328]: df.to_json("test.json", orient="table") In [329]: new_df = pd.read_json("test.json", orient="table") In [330]: print(new_df.index.name) None orient='table'与 user-defined 一起使用时ExtensionArray,生成的架构将extDtype在相应 fields元素中包含一个附加键。这个额外的键不是标准的,但确实为扩展类型启用了 JSON 往返(例如)。read_json(df.to_json(orient="table"), orient="table") 该extDtype密钥带有扩展名,如果您已正确注册ExtensionDtype,pandas 将使用该名称在注册表中执行查找并将序列化数据重新转换为您的自定义数据类型。 HTML # 读取 HTML 内容# 警告 我们强烈建议您阅读 下面有关 BeautifulSoup4/html5lib/lxml 解析器问题的HTML 表解析陷阱。 顶级read_html()函数可以接受 HTML 字符串/文件/URL 并将 HTML 表解析为 pandas 列表DataFrames。让我们看几个例子。 笔记 read_html返回一个list对象DataFrame,即使 HTML 内容中只包含一个表。 读取没有选项的 URL: In [320]: url = "https://www.fdic.gov/resources/resolutions/bank-failures/failed-bank-list" In [321]: pd.read_html(url) Out[321]: [ Bank NameBank CityCity StateSt ... Acquiring InstitutionAI Closing DateClosing FundFund 0 Almena State Bank Almena KS ... Equity Bank October 23, 2020 10538 1 First City Bank of Florida Fort Walton Beach FL ... United Fidelity Bank, fsb October 16, 2020 10537 2 The First State Bank Barboursville WV ... MVB Bank, Inc. April 3, 2020 10536 3 Ericson State Bank Ericson NE ... Farmers and Merchants Bank February 14, 2020 10535 4 City National Bank of New Jersey Newark NJ ... Industrial Bank November 1, 2019 10534 .. ... ... ... ... ... ... ... 558 Superior Bank, FSB Hinsdale IL ... Superior Federal, FSB July 27, 2001 6004 559 Malta National Bank Malta OH ... North Valley Bank May 3, 2001 4648 560 First Alliance Bank & Trust Co. Manchester NH ... Southern New Hampshire Bank & Trust February 2, 2001 4647 561 National State Bank of Metropolis Metropolis IL ... Banterra Bank of Marion December 14, 2000 4646 562 Bank of Honolulu Honolulu HI ... Bank of the Orient October 13, 2000 4645 [563 rows x 7 columns]] 笔记 上述 URL 的数据每周一都会发生变化,因此上面的结果数据可能会略有不同。 在与 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 作为字符串传递给: In [331]: 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 [332]: with open("tmp.html", "w") as f: .....: f.write(html_str) .....: In [333]: df = pd.read_html("tmp.html") In [334]: df[0] Out[334]: A B C 0 a b c StringIO如果您愿意,您甚至可以传递一个实例: In [335]: dfs = pd.read_html(StringIO(html_str)) In [336]: dfs[0] Out[336]: A B C 0 a b c 笔记 以下示例不是由 IPython 评估器运行,因为拥有如此多的网络访问函数会减慢文档构建速度。如果您发现错误或示例无法运行,请立即在pandas GitHub 问题页面上报告。 读取 URL 并匹配包含特定文本的表: match = "Metcalf Bank" df_list = pd.read_html(url, match=match) 指定标题行(默认情况下<th>或<td>位于 a 中的元素 <thead>用于形成列索引,如果其中包含多行, <thead>则创建 MultiIndex);如果指定,则标题行取自数据减go已解析的标题元素(<th>elements)。 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) 读取 pandasto_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 [337]: html_table = """ .....: <table> .....: <tr> .....: <th>GitHub</th> .....: </tr> .....: <tr> .....: <td><a href="https://github.com/pandas-dev/pandas">pandas</a></td> .....: </tr> .....: </table> .....: """ .....: In [338]: df = pd.read_html( .....: StringIO(html_table), .....: extract_links="all" .....: )[0] .....: In [339]: df Out[339]: (GitHub, None) 0 (pandas, https://github.com/pandas-dev/pandas) In [340]: df[("GitHub", None)] Out[340]: 0 (pandas, https://github.com/pandas-dev/pandas) Name: (GitHub, None), dtype: object In [341]: df[("GitHub", None)].str[1] Out[341]: 0 https://github.com/pandas-dev/pandas Name: (GitHub, None), dtype: object 1.5.0 版本中的新增功能。 写入 HTML 文件# DataFrame对象有一个实例方法to_html,它将对象的内容呈现DataFrame为 HTML 表。函数参数与to_string上述方法中的相同。 笔记 DataFrame.to_html为了简洁起见,此处并未显示所有可能的选项。请参阅DataFrame.to_html()参考资料 获取完整的选项集。 笔记 在支持 HTML 渲染的环境(例如 Jupyter Notebook)中,display(HTML(...))` 会将原始 HTML 渲染到环境中。 In [342]: from IPython.display import display, HTML In [343]: df = pd.DataFrame(np.random.randn(2, 2)) In [344]: df Out[344]: 0 1 0 -0.345352 1.314232 1 0.690579 0.995761 In [345]: html = df.to_html() In [346]: print(html) # raw html <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.345352</td> <td>1.314232</td> </tr> <tr> <th>1</th> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table> In [347]: display(HTML(html)) <IPython.core.display.HTML object> 该columns参数将限制显示的列: In [348]: html = df.to_html(columns=[0]) In [349]: 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.345352</td> </tr> <tr> <th>1</th> <td>0.690579</td> </tr> </tbody> </table> In [350]: display(HTML(html)) <IPython.core.display.HTML object> float_format采用 Python 可调用来控制浮点值的精度: In [351]: html = df.to_html(float_format="{0:.10f}".format) In [352]: 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.3453521949</td> <td>1.3142323796</td> </tr> <tr> <th>1</th> <td>0.6905793352</td> <td>0.9957609037</td> </tr> </tbody> </table> In [353]: display(HTML(html)) <IPython.core.display.HTML object> bold_rows默认情况下,行标签会变为粗体,但您可以将其关闭: In [354]: html = df.to_html(bold_rows=False) In [355]: 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.345352</td> <td>1.314232</td> </tr> <tr> <td>1</td> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table> In [356]: display(HTML(html)) <IPython.core.display.HTML object> 该classes参数提供了给出生成的 HTML 表 CSS 类的能力。请注意,这些类将附加到现有 'dataframe'类中。 In [357]: 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.345352</td> <td>1.314232</td> </tr> <tr> <th>1</th> <td>0.690579</td> <td>0.995761</td> </tr> </tbody> </table> 该render_links参数提供了向包含 URL 的单元格添加超链接的功能。 In [358]: url_df = pd.DataFrame( .....: { .....: "name": ["Python", "pandas"], .....: "url": ["https://www.python.org/", "https://pandas.pydata.org"], .....: } .....: ) .....: In [359]: html = url_df.to_html(render_links=True) In [360]: print(html) <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 [361]: display(HTML(html)) <IPython.core.display.HTML object> 最后,该escape参数允许您控制“<”、“>”和“&”字符是否在生成的 HTML 中转义(默认情况下为 True)。因此,要获取没有转义字符的 HTML,请通过escape=False In [362]: df = pd.DataFrame({"a": list("&<>"), "b": np.random.randn(3)}) 逃脱: In [363]: html = df.to_html() In [364]: print(html) <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>2.396780</td> </tr> <tr> <th>1</th> <td><</td> <td>0.014871</td> </tr> <tr> <th>2</th> <td>></td> <td>3.357427</td> </tr> </tbody> </table> In [365]: display(HTML(html)) <IPython.core.display.HTML object> 没有逃脱: In [366]: html = df.to_html(escape=False) In [367]: 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>2.396780</td> </tr> <tr> <th>1</th> <td><</td> <td>0.014871</td> </tr> <tr> <th>2</th> <td>></td> <td>3.357427</td> </tr> </tbody> </table> In [368]: display(HTML(html)) <IPython.core.display.HTML object> 笔记 某些浏览器可能不会显示前两个 HTML 表的呈现差异。 HTML 表解析陷阱# 顶级 pandas io 函数中用于解析 HTML 表的库存在一些版本控制问题read_html。 lxml的问题 好处 lxml非常快。 lxml需要 Cython 正确安装。 缺点 lxml对其解析结果不做任何保证, 除非给出严格有效的标记。 鉴于上述情况,我们选择允许您(用户)使用 lxml后端,但如果lxml 解析失败,该后端将使用 html5lib 因此,强烈建议您同时安装 BeautifulSoup4和html5lib,这样即使lxml失败,您仍然可以获得有效的结果(假设其他一切都有效)。 使用lxml作为后端的BeautifulSoup4 的问题 上述问题在这里也存在,因为BeautifulSoup4本质上只是解析器后端的包装器。 使用html5lib作为后端的BeautifulSoup4 的问题 好处 html5lib比lxml宽松得多,因此以更明智的方式处理现实生活中的标记,而不仅仅是在不通知您的情况下删除元素。 html5lib 自动从无效标记生成有效的 HTML5 标记。这对于解析 HTML 表非常重要,因为它保证了有效的文档。然而,这并不意味着它是“正确的”,因为修复标记的过程没有单一的定义。 html5lib是纯Python,除了其自身的安装之外不需要额外的构建步骤。 缺点 使用html5lib的最大缺点是速度慢得像糖蜜。然而,请考虑以下事实:网络上的许多表都不够大,不足以影响解析算法的运行时间。瓶颈更有可能出现在通过网络从 URL 读取原始文本的过程中,即 IO(输入-输出)。对于非常大的表,这可能不正确。 乳胶# 1.3.0 版本中的新增功能。 目前没有从 LaTeX 读取的方法,只有输出方法。 写入 LaTeX 文件# 笔记 DataFrame和Styler 对象当前有一个to_latex方法。我们建议使用Styler.to_latex()方法而不是DataFrame.to_latex(),因为前者在条件样式方面具有更大的灵活性,而后者将来可能会被弃用。 查看Styler.to_latex的文档,其中提供了条件样式的示例并解释了其关键字参数的操作。 对于简单的应用,以下模式就足够了。 In [369]: df = pd.DataFrame([[1, 2], [3, 4]], index=["a", "b"], columns=["c", "d"]) In [370]: print(df.style.to_latex()) \begin{tabular}{lrr} & c & d \\ a & 1 & 2 \\ b & 3 & 4 \\ \end{tabular} 要在输出之前格式化值,请链接Styler.format 方法。 In [371]: print(df.style.format("€ {}").to_latex()) \begin{tabular}{lrr} & c & d \\ a & € 1 & € 2 \\ b & € 3 & € 4 \\ \end{tabular} XML # 读取 XML # 1.3.0 版本中的新增功能。 顶级read_xml()函数可以接受 XML 字符串/文件/URL 并将节点和属性解析为 pandas DataFrame。 笔记 由于没有标准的 XML 结构,其中设计类型可以在很多方面有所不同,因此read_xml最适合扁平化、浅层的版本。如果 XML 文档嵌套很深,请使用该stylesheet功能将 XML 转换为更扁平的版本。 让我们看几个例子。 读取 XML 字符串: In [372]: from io import StringIO In [373]: xml = """<?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 [374]: df = pd.read_xml(StringIO(xml)) In [375]: df Out[375]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95 读取没有选项的 URL: In [376]: df = pd.read_xml("https://www.w3schools.com/xml/books.xml") In [377]: df Out[377]: category title author year price cover 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 None 1 children Harry Potter J K. Rowling 2005 29.99 None 2 web XQuery Kick Start Vaidyanathan Nagarajan 2003 49.99 None 3 web Learning XML Erik T. Ray 2003 39.95 paperback 读取“books.xml”文件的内容并将其read_xml 作为字符串传递给: In [378]: file_path = "books.xml" In [379]: with open(file_path, "w") as f: .....: f.write(xml) .....: In [380]: with open(file_path, "r") as f: .....: df = pd.read_xml(StringIO(f.read())) .....: In [381]: df Out[381]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95 读入“books.xml”的内容作为StringIOor 的实例BytesIO并将其传递给read_xml: In [382]: with open(file_path, "r") as f: .....: sio = StringIO(f.read()) .....: In [383]: df = pd.read_xml(sio) In [384]: df Out[384]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95 In [385]: with open(file_path, "rb") as f: .....: bio = BytesIO(f.read()) .....: In [386]: df = pd.read_xml(bio) In [387]: df Out[387]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 2 web Learning XML Erik T. Ray 2003 39.95 甚至可以从 AWS S3 存储桶(例如提供生物医学和生命科学期刊的 NIH NCBI PMC 文章数据集)读取 XML: In [388]: df = pd.read_xml( .....: "s3://pmc-oa-opendata/oa_comm/xml/all/PMC1236943.xml", .....: xpath=".//journal-meta", .....: ) .....: In [389]: df Out[389]: journal-id journal-title issn publisher 0 Cardiovasc Ultrasound Cardiovascular Ultrasound 1476-7120 NaN 使用lxml作为默认值parser,您可以访问扩展 Python ElementTree API 的全功能 XML 库。一种强大的工具是能够使用更具表现力的 XPath 有选择地或有条件地查询节点: In [390]: df = pd.read_xml(file_path, xpath="//book[year=2005]") In [391]: df Out[391]: category title author year price 0 cooking Everyday Italian Giada De Laurentiis 2005 30.00 1 children Harry Potter J K. Rowling 2005 29.99 仅指定要解析的元素或属性: In [392]: df = pd.read_xml(file_path, elems_only=True) In [393]: df Out[393]: title author year price 0 Everyday Italian Giada De Laurentiis 2005 30.00 1 Harry Potter J K. Rowling 2005 29.99 2 Learning XML Erik T. Ray 2003 39.95 In [394]: df = pd.read_xml(file_path, attrs_only=True) In [395]: df Out[395]: category 0 cooking 1 children 2 web XML 文档可以具有带前缀的命名空间和不带前缀的默认命名空间,这两种命名空间都用特殊属性表示xmlns。为了在命名空间上下文下按节点进行解析,xpath必须引用前缀。 例如,下面的 XML 包含一个带有前缀doc和 URI 的 命名空间https://example.com。为了解析doc:row节点, namespaces必须使用。 In [396]: xml = """<?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 [397]: df = pd.read_xml(StringIO(xml), .....: xpath="//doc:row", .....: namespaces={"doc": "https://example.com"}) .....: In [398]: df Out[398]: shape degrees sides 0 square 360 4.0 1 circle 360 NaN 2 triangle 180 3.0 类似地,XML 文档可以有一个没有前缀的默认命名空间。未能分配临时前缀将不会返回任何节点并引发ValueError.但是将任何临时名称分配给正确的 URI 都允许节点进行解析。 In [399]: xml = """<?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 [400]: df = pd.read_xml(StringIO(xml), .....: xpath="//pandas:row", .....: namespaces={"pandas": "https://example.com"}) .....: In [401]: df Out[401]: shape degrees sides 0 square 360 4.0 1 circle 360 NaN 2 triangle 180 3.0 但是,如果 XPath 不引用节点名称(例如 default、 )/*,则 namespaces不需要。 笔记 由于xpath标识了要解析的内容的父级,因此仅解析包括子节点或当前属性的直接后代。因此,read_xml不会解析孙子或其他后代的文本,也不会解析任何后代的属性。要检索较低级别的内容,请将 xpath 调整为较低级别。例如, In [402]: xml = """ .....: <data> .....: <row> .....: <shape sides="4">square</shape> .....: <degrees>360</degrees> .....: </row> .....: <row> .....: <shape sides="0">circle</shape> .....: <degrees>360</degrees> .....: </row> .....: <row> .....: <shape sides="3">triangle</shape> .....: <degrees>180</degrees> .....: </row> .....: </data>""" .....: In [403]: df = pd.read_xml(StringIO(xml), xpath="./row") In [404]: df Out[404]: shape degrees 0 square 360 1 circle 360 2 triangle 180 sides显示元素上的属性shape未按预期进行解析,因为该属性驻留在row元素的子元素上而不是row元素本身。换句话说,sides属性是元素的孙级后代row。但是,xpath 目标row元素仅涵盖其子元素和属性。 使用lxml作为解析器,您可以使用 XSLT 脚本展平嵌套的 XML 文档,该脚本也可以是字符串/文件/URL 类型。作为背景,XSLT是一种用特殊 XML 文件编写的专用语言,可以使用 XSLT 处理器将原始 XML 文档转换为其他 XML、HTML,甚至文本(CSV、JSON 等)。 例如,考虑芝加哥“L”游乐设施的这种有点嵌套的结构,其中车站和游乐设施元素将数据封装在自己的部分中。使用下面的 XSLT,lxml可以将原始嵌套文档转换为更扁平的输出(如下所示的演示),以便更容易解析为DataFrame: In [405]: 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 [406]: 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 [407]: 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 [408]: df = pd.read_xml(StringIO(xml), stylesheet=xsl) In [409]: df Out[409]: station_id station_name ... avg_saturday_rides avg_sunday_holiday_rides 0 40850 Library ... 534.0 417.2 1 41700 Washington/Wabash ... 1909.8 1438.6 2 40380 Clark/Lake ... 1657.0 1453.8 [3 rows x 6 columns] 对于数百兆字节到千兆字节的非常大的 XML 文件,pandas.read_xml() 支持使用lxml 的 iterparse和etree 的 iterparse来解析如此大的文件 ,这些方法是内存高效的方法,用于迭代 XML 树并提取特定元素和属性。无需将整个树保存在内存中。 1.5.0 版本中的新增功能。 要使用此功能,您必须将物理 XML 文件路径传递到read_xml并使用该iterparse参数。文件不应被压缩或指向在线源,而应存储在本地磁盘上。另外,iterparse应该是一个字典,其中键是文档中的重复节点(成为行),值是重复节点的后代(即子节点、孙子节点)的任何元素或属性的列表。由于此方法中未使用 XPath,因此后代不需要彼此共享相同的关系。下面显示了阅读维基百科非常大(12 GB+)最新文章数据转储的示例。 In [1]: df = pd.read_xml( ... "/path/to/downloaded/enwikisource-latest-pages-articles.xml", ... iterparse = {"page": ["title", "ns", "id"]} ... ) ... df Out[2]: title ns id 0 Gettysburg Address 0 21450 1 Main Page 0 42950 2 Declaration by United Nations 0 8435 3 Constitution of the United States of America 0 8435 4 Declaration of Independence (Israel) 0 17858 ... ... ... ... 3578760 Page:Black cat 1897 07 v2 n10.pdf/17 104 219649 3578761 Page:Black cat 1897 07 v2 n10.pdf/43 104 219649 3578762 Page:Black cat 1897 07 v2 n10.pdf/44 104 219649 3578763 The History of Tom Jones, a Foundling/Book IX 0 12084291 3578764 Page:Shakespeare of Stratford (1926) Yale.djvu/91 104 21450 [3578765 rows x 3 columns] 编写 XML # 1.3.0 版本中的新增功能。 DataFrame对象有一个实例方法to_xml,它将对象的内容呈现DataFrame为 XML 文档。 笔记 此方法不支持 XML 的特殊属性,包括 DTD、CData、XSD 架构、处理指令、注释等。仅支持根级别的命名空间。但是,stylesheet 允许在初始输出后更改设计。 让我们看几个例子。 编写不带选项的 XML: In [410]: geom_df = pd.DataFrame( .....: { .....: "shape": ["square", "circle", "triangle"], .....: "degrees": [360, 360, 180], .....: "sides": [4, np.nan, 3], .....: } .....: ) .....: In [411]: print(geom_df.to_xml()) <?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 [412]: 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 [413]: 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 [414]: 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 [415]: ext_geom_df = pd.DataFrame( .....: { .....: "type": ["polygon", "other", "polygon"], .....: "shape": ["square", "circle", "triangle"], .....: "degrees": [360, 360, 180], .....: "sides": [4, np.nan, 3], .....: } .....: ) .....: In [416]: pvt_df = ext_geom_df.pivot_table(index='shape', .....: columns='type', .....: values=['degrees', 'sides'], .....: aggfunc='sum') .....: In [417]: pvt_df Out[417]: degrees sides type other polygon other polygon shape circle 360.0 NaN 0.0 NaN square NaN 360.0 NaN 4.0 triangle NaN 180.0 NaN 3.0 In [418]: print(pvt_df.to_xml()) <?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 [419]: 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 [420]: 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 [421]: 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 [422]: 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 [423]: 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 架构。 由于上述原因,如果您的应用程序在 pandas 操作之前构建 XML,请使用适当的 DOM 库(例如etree和 )lxml来构建必要的文档,而不是通过字符串连接或正则表达式调整。永远记住 XML 是一种带有标记规则的特殊文本文件。 对于非常大的 XML 文件(数百 MB 到 GB),XPath 和 XSLT 可能会成为内存密集型操作。确保有足够的可用 RAM 来读取和写入大型 XML 文件(大约是文本大小的 5 倍)。 由于 XSLT 是一种编程语言,因此请谨慎使用它,因为此类脚本可能会给您的环境带来安全风险,并且可能运行大型或无限的递归操作。在完全运行之前,始终在小片段上测试脚本。 etree解析器支持read_xml除 to_xml复杂 XPath 和任何 XSLT 之外的所有功能。尽管功能有限,但 etree仍然是一个可靠且功能强大的解析器和树构建器。对于较大的文件,其性能可能会有所lxml下降,但对于中小型文件,其性能相对不明显。 Excel 文件# 该read_excel()方法可以使用Python模块读取Excel 2007+ ( .xlsx) 文件openpyxl。 Excel 2003( .xls) 文件可以使用xlrd..xlsb可以使用 读取二进制 Excel( ) 文件pyxlsb。所有格式都可以使用炉甘石引擎读取。实例to_excel()方法用于将数据保存DataFrame到 Excel。一般来说,语义与处理csv数据类似。请参阅食谱了解一些高级策略。 笔记 当 时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)文件。 对于炉甘石引擎,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属性将生成文件中工作表名称的列表。 an 的主要用例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_name0,表示读取第一张 传递一个字符串来引用工作簿中特定工作表的名称。 传递一个整数来引用工作表的索引。索引遵循 Python 约定,从 0 开始。 传递字符串或整数的列表,以返回指定工作表的字典。 传递 aNone返回所有可用工作表的字典。 # 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_excelsheet_name通过设置为工作表名称列表、工作表位置列表或None读取所有工作表,可以读取多个工作表。工作表可以通过工作表索引或工作表名称分别使用整数或字符串来指定。 正在读MultiIndex# read_excelMultiIndex可以通过将列列表传递给 来读取索引,index_col 也可以MultiIndex通过将行列表传递给 来读取列header。如果index 或columns具有序列化的级别名称,也将通过指定构成级别的行/列来读入这些名称。 例如,要读入MultiIndex没有名称的索引: In [424]: df = pd.DataFrame( .....: {"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}, .....: index=pd.MultiIndex.from_product([["a", "b"], ["c", "d"]]), .....: ) .....: In [425]: df.to_excel("path_to_file.xlsx") In [426]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1]) In [427]: df Out[427]: a b a c 1 5 d 2 6 b c 3 7 d 4 8 如果索引具有级别名称,它们也会使用相同的参数进行解析。 In [428]: df.index = df.index.set_names(["lvl1", "lvl2"]) In [429]: df.to_excel("path_to_file.xlsx") In [430]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1]) In [431]: df Out[431]: a b lvl1 lvl2 a c 1 5 d 2 6 b c 3 7 d 4 8 如果源文件同时具有MultiIndex索引和列,则指定每个的列表应传递给index_coland header: In [432]: df.columns = pd.MultiIndex.from_product([["a"], ["b", "d"]], names=["c1", "c2"]) In [433]: df.to_excel("path_to_file.xlsx") In [434]: df = pd.read_excel("path_to_file.xlsx", index_col=[0, 1], header=[0, 1]) In [435]: df Out[435]: c1 a c2 b d lvl1 lvl2 a c 1 5 d 2 6 b c 3 7 d 4 8 中指定的列中的缺失值index_col将向前填充,以允许使用to_excelfor进行往返merged_cells=True。为了避免前向填充缺失值,请使用set_indexafter read the data 而不是 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}) D类型规格# 作为转换器的替代方案,可以使用关键字指定整个列的类型dtype,该关键字采用将列名映射到类型的字典。要在不进行类型推断的情况下解释数据,请使用类型str或object。 pd.read_excel("path_to_file.xls", dtype={"MyInts": "int64", "MyText": str}) 写入 Excel 文件# 将 Excel 文件写入磁盘# 要将DataFrame对象写入 Excel 文件的工作表,可以使用 to_excel实例方法。这些参数与上面描述的基本相同to_csv ,第一个参数是 Excel 文件的名称,可选的第二个参数是应DataFrame写入的工作表的名称。例如: df.to_excel("path_to_file.xlsx", sheet_name="Sheet1") 具有扩展名的文件 .xlsx将使用xlsxwriter(如果可用)或 openpyxl. 将以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") 当使用engine_kwargs参数时,pandas 会将这些参数传递给引擎。为此,了解 pandas 内部使用哪个函数非常重要。 对于引擎 openpyxl,pandas 用于openpyxl.Workbook()创建新工作表并将openpyxl.load_workbook()数据附加到现有工作表。 openpyxl 引擎写入 ( .xlsx) 和 ( .xlsm) 文件。 对于 xlsxwriter 引擎,pandas 用于xlsxwriter.Workbook()写入 ( .xlsx) 文件。 对于 odf 引擎,pandas 使用odf.opendocument.OpenDocumentSpreadsheet()写入 ( .ods) 文件。 将 Excel 文件写入内存# pandas 支持将 Excel 文件写入类似缓冲区的对象,例如StringIO或 BytesIO使用ExcelWriter. from io import BytesIO bio = BytesIO() # By setting the 'engine' in the ExcelWriter constructor. writer = pd.ExcelWriter(bio, engine="xlsxwriter") df.to_excel(writer, sheet_name="Sheet1") # Save the workbook writer.save() # Seek to the beginning and read to copy the workbook to a variable in memory bio.seek(0) workbook = bio.read() 笔记 engine是可选的,但推荐。设置引擎决定了生成的工作簿的版本。设置engine='xlrd'后将生成 Excel 2003 格式的工作簿 (xls)。使用'openpyxl'或 'xlsxwriter'将生成 Excel 2007 格式的工作簿 (xlsx)。如果省略,将生成 Excel 2007 格式的工作簿。 Excel 编写引擎# pandas 通过两种方法选择 Excel 编写器: 关键字engine参数 文件扩展名(通过配置选项中指定的默认值) 默认情况下,pandas 使用XlsxWriter for .xlsx,openpyxl for .xlsm。如果您安装了多个引擎,您可以通过设置配置选项 io.excel.xlsx.writer和 来设置默认引擎io.excel.xls.writer。 如果Xlsxwriter不可用,pandas 将依靠openpyxl来获取文件。.xlsx 要指定要使用哪个编写器,您可以将引擎关键字参数传递给to_excel和ExcelWriter。内置引擎有: openpyxl:需要2.4或更高版本 xlsxwriter # By setting the 'engine' in the DataFrame 'to_excel()' methods. df.to_excel("path_to_file.xlsx", sheet_name="Sheet1", engine="xlsxwriter") # By setting the 'engine' in the ExcelWriter constructor. writer = pd.ExcelWriter("path_to_file.xlsx", engine="xlsxwriter") # Or via pandas configuration. from pandas import options # noqa: E402 options.io.excel.xlsx.writer = "xlsxwriter" df.to_excel("path_to_file.xlsx", sheet_name="Sheet1") 样式和格式# 可以使用DataFrame的to_excel方法中的以下参数修改从 pandas 创建的 Excel 工作表的外观和风格。 float_format:浮点数的格式字符串(默认None)。 freeze_panes:两个整数的元组,表示要冻结的最底行和最右边的列。这些参数中的每一个都是基于 1 的,因此 (1, 1) 将冻结第一行和第一列(默认None)。 使用Xlsxwriter引擎提供了许多选项来控制使用该方法创建的 Excel 工作表的格式to_excel。优秀的示例可以在 Xlsxwriter文档中找到:https://xlsxwriter.readthedocs.io/working_with_pandas.html OpenDocument 电子表格# Excel 文件的 io 方法还支持使用odfpy模块读取和写入 OpenDocument 电子表格。读写 OpenDocument 电子表格的语义和功能与使用 engine='odf'.需要安装可选依赖项“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()方法还可以使用该模块读取二进制 Excel 文件pyxlsb。读取二进制 Excel文件的语义和功能大部分与使用 engine='pyxlsb'.pyxlsb不识别文件中的日期时间类型,而是返回浮点数( 如果需要识别日期时间类型,可以使用炉甘石)。 # Returns a DataFrame pd.read_excel("path_to_file.xlsb", engine="pyxlsb") 笔记 目前pandas仅支持读取二进制Excel文件。写入没有实现。 炉甘石(Excel 和 ODS 文件)# 该方法可以使用该模块read_excel()读取 Excel 文件 ( .xlsx、.xlsm、.xls、.xlsb) 和 OpenDocument 电子表格 ( ) 。该模块是 Rust 库calamine的绑定 ,在大多数情况下比其他引擎更快。需要安装可选依赖项“python-calamine”。.odspython-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然后通过调用将数据直接导入到a : >>> clipdf = pd.read_clipboard() >>> clipdf A B C x 1 4 p y 2 5 q z 3 6 r 该to_clipboard方法可用于将 a 的内容写入DataFrame剪贴板。接下来,您可以将剪贴板内容粘贴到其他应用程序中(在许多操作系统上为 CTRL-V)。这里我们演示将 a 写入 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)才能使用这些方法。 酸洗# 所有 pandas 对象都配备了to_pickle使用 Python cPickle模块以 pickle 格式将数据结构保存到磁盘的方法。 In [436]: df Out[436]: c1 a c2 b d lvl1 lvl2 a c 1 5 d 2 6 b c 3 7 d 4 8 In [437]: df.to_pickle("foo.pkl") read_pickle命名空间中的函数可pandas用于从文件加载任何 pickled pandas 对象(或任何其他 pickled 对象): In [438]: pd.read_pickle("foo.pkl") Out[438]: c1 a c2 b d lvl1 lvl2 a c 1 5 d 2 6 b c 3 7 d 4 8 警告 加载从不受信任的来源收到的腌制数据可能不安全。 请参阅:https://docs.python.org/3/library/pickle.html 警告 read_pickle()仅保证向后兼容到一些次要版本。 压缩的pickle文件# read_pickle(),DataFrame.to_pickle()并且Series.to_pickle()可以读取和写入压缩的pickle文件。读写支持gzip、bz2、xz、压缩类型。zstd该zip文件格式仅支持读取,并且只能包含一个要读取的数据文件。 压缩类型可以是显式参数,也可以从文件扩展名推断出来。如果是“推断”,则如果文件名分别以、、、 或结尾,则使用gzip、bz2、zip、xz。zstd'.gz''.bz2''.zip''.xz''.zst' 压缩参数也可以是 a,dict以便将选项传递给压缩协议。它必须有一个'method'设置为压缩协议名称的键,该名称必须是 { 'zip', 'gzip', 'bz2', 'xz', 'zstd'} 之一。所有其他键值对都传递到底层压缩库。 In [439]: df = pd.DataFrame( .....: { .....: "A": np.random.randn(1000), .....: "B": "foo", .....: "C": pd.date_range("20130101", periods=1000, freq="s"), .....: } .....: ) .....: In [440]: df Out[440]: A B C 0 -0.317441 foo 2013-01-01 00:00:00 1 -1.236269 foo 2013-01-01 00:00:01 2 0.896171 foo 2013-01-01 00:00:02 3 -0.487602 foo 2013-01-01 00:00:03 4 -0.082240 foo 2013-01-01 00:00:04 .. ... ... ... 995 -0.171092 foo 2013-01-01 00:16:35 996 1.786173 foo 2013-01-01 00:16:36 997 -0.575189 foo 2013-01-01 00:16:37 998 0.820750 foo 2013-01-01 00:16:38 999 -1.256530 foo 2013-01-01 00:16:39 [1000 rows x 3 columns] 使用显式压缩类型: In [441]: df.to_pickle("data.pkl.compress", compression="gzip") In [442]: rt = pd.read_pickle("data.pkl.compress", compression="gzip") In [443]: rt Out[443]: A B C 0 -0.317441 foo 2013-01-01 00:00:00 1 -1.236269 foo 2013-01-01 00:00:01 2 0.896171 foo 2013-01-01 00:00:02 3 -0.487602 foo 2013-01-01 00:00:03 4 -0.082240 foo 2013-01-01 00:00:04 .. ... ... ... 995 -0.171092 foo 2013-01-01 00:16:35 996 1.786173 foo 2013-01-01 00:16:36 997 -0.575189 foo 2013-01-01 00:16:37 998 0.820750 foo 2013-01-01 00:16:38 999 -1.256530 foo 2013-01-01 00:16:39 [1000 rows x 3 columns] 从扩展推断压缩类型: In [444]: df.to_pickle("data.pkl.xz", compression="infer") In [445]: rt = pd.read_pickle("data.pkl.xz", compression="infer") In [446]: rt Out[446]: A B C 0 -0.317441 foo 2013-01-01 00:00:00 1 -1.236269 foo 2013-01-01 00:00:01 2 0.896171 foo 2013-01-01 00:00:02 3 -0.487602 foo 2013-01-01 00:00:03 4 -0.082240 foo 2013-01-01 00:00:04 .. ... ... ... 995 -0.171092 foo 2013-01-01 00:16:35 996 1.786173 foo 2013-01-01 00:16:36 997 -0.575189 foo 2013-01-01 00:16:37 998 0.820750 foo 2013-01-01 00:16:38 999 -1.256530 foo 2013-01-01 00:16:39 [1000 rows x 3 columns] 默认是“推断”: In [447]: df.to_pickle("data.pkl.gz") In [448]: rt = pd.read_pickle("data.pkl.gz") In [449]: rt Out[449]: A B C 0 -0.317441 foo 2013-01-01 00:00:00 1 -1.236269 foo 2013-01-01 00:00:01 2 0.896171 foo 2013-01-01 00:00:02 3 -0.487602 foo 2013-01-01 00:00:03 4 -0.082240 foo 2013-01-01 00:00:04 .. ... ... ... 995 -0.171092 foo 2013-01-01 00:16:35 996 1.786173 foo 2013-01-01 00:16:36 997 -0.575189 foo 2013-01-01 00:16:37 998 0.820750 foo 2013-01-01 00:16:38 999 -1.256530 foo 2013-01-01 00:16:39 [1000 rows x 3 columns] In [450]: df["A"].to_pickle("s1.pkl.bz2") In [451]: rt = pd.read_pickle("s1.pkl.bz2") In [452]: rt Out[452]: 0 -0.317441 1 -1.236269 2 0.896171 3 -0.487602 4 -0.082240 ... 995 -0.171092 996 1.786173 997 -0.575189 998 0.820750 999 -1.256530 Name: A, Length: 1000, dtype: float64 将选项传递给压缩协议以加速压缩: In [453]: df.to_pickle("data.pkl.gz", compression={"method": "gzip", "compresslevel": 1}) 消息包# pandas 对 的支持msgpack已在 1.0.0 版本中删除。建议使用pickle代替。 或者,您还可以使用 Arrow IPC 序列化格式进行 pandas 对象的在线传输。有关 pyarrow 的文档,请参阅 此处。 HDF5 (PyTables) # HDFStore是一个类似字典的对象,它使用优秀的PyTables库使用高性能 HDF5 格式读取和写入 pandas 。请参阅食谱 了解一些高级策略 警告 pandas 使用 PyTables 读取和写入 HDF5 文件,它允许使用 pickle 序列化对象数据类型数据。加载从不受信任的来源收到的腌制数据可能不安全。 有关更多信息,请参阅:https://docs.python.org/3/library/pickle.html。 In [454]: store = pd.HDFStore("store.h5") In [455]: print(store) <class 'pandas.io.pytables.HDFStore'> File path: store.h5 可以将对象写入文件,就像将键值对添加到字典中一样: In [456]: index = pd.date_range("1/1/2000", periods=8) In [457]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"]) In [458]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"]) # store.put('s', s) is an equivalent method In [459]: store["s"] = s In [460]: store["df"] = df In [461]: store Out[461]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 在当前或以后的 Python 会话中,您可以检索存储的对象: # store.get('df') is an equivalent method In [462]: store["df"] Out[462]: A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 # dotted (attribute) access provides get as well In [463]: store.df Out[463]: A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 删除key指定的对象: # store.remove('df') is an equivalent method In [464]: del store["df"] In [465]: store Out[465]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 关闭 Store 并使用上下文管理器: In [466]: store.close() In [467]: store Out[467]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 In [468]: store.is_open Out[468]: False # Working with, and automatically closing the store using a context manager In [469]: with pd.HDFStore("store.h5") as store: .....: store.keys() .....: 读/写API # HDFStoreread_hdf支持用于读取和写入的顶级 API to_hdf,类似于 howread_csv和to_csvwork。 In [470]: df_tl = pd.DataFrame({"A": list(range(5)), "B": list(range(5))}) In [471]: df_tl.to_hdf("store_tl.h5", key="table", append=True) In [472]: pd.read_hdf("store_tl.h5", "table", where=["index>2"]) Out[472]: A B 3 3 3 4 4 4 默认情况下,HDFStore 不会删除全部丢失的行。可以通过设置更改此行为dropna=True。 In [473]: df_with_missing = pd.DataFrame( .....: { .....: "col1": [0, np.nan, 2], .....: "col2": [1, np.nan, np.nan], .....: } .....: ) .....: In [474]: df_with_missing Out[474]: col1 col2 0 0.0 1.0 1 NaN NaN 2 2.0 NaN In [475]: df_with_missing.to_hdf("file.h5", key="df_with_missing", format="table", mode="w") In [476]: pd.read_hdf("file.h5", "df_with_missing") Out[476]: col1 col2 0 0.0 1.0 1 NaN NaN 2 2.0 NaN In [477]: df_with_missing.to_hdf( .....: "file.h5", key="df_with_missing", format="table", mode="w", dropna=True .....: ) .....: In [478]: pd.read_hdf("file.h5", "df_with_missing") Out[478]: col1 col2 0 0.0 1.0 2 2.0 NaN 固定格式# 上面的示例显示了使用 进行存储put,它将 HDF5 写入PyTables固定数组格式,称为fixed格式。这些类型的存储一旦写入就不可追加(尽管您可以简单地删除它们并重写)。它们也不可查询;必须将它们全部收回。它们也不支持具有非唯一列名的数据框。格式fixed存储提供比存储非常快的写入速度和稍快的读取速度table。当使用putorto_hdf或 byformat='fixed'或 时,默认指定此格式format='f'。 警告 如果您尝试使用 a 进行检索,格式fixed将引发 a :TypeErrorwhere In [479]: pd.DataFrame(np.random.randn(10, 2)).to_hdf("test_fixed.h5", key="df") In [480]: pd.read_hdf("test_fixed.h5", "df", where="index>5") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[480], line 1 ----> 1 pd.read_hdf("test_fixed.h5", "df", where="index>5") File ~/work/pandas/pandas/pandas/io/pytables.py:452, in read_hdf(path_or_buf, key, mode, errors, where, start, stop, columns, iterator, chunksize, **kwargs) 447 raise ValueError( 448 "key must be provided when HDF5 " 449 "file contains multiple datasets." 450 ) 451 key = candidate_only_group._v_pathname --> 452 return store.select( 453 key, 454 where=where, 455 start=start, 456 stop=stop, 457 columns=columns, 458 iterator=iterator, 459 chunksize=chunksize, 460 auto_close=auto_close, 461 ) 462 except (ValueError, TypeError, LookupError): 463 if not isinstance(path_or_buf, HDFStore): 464 # if there is an error, close the store if we opened it. File ~/work/pandas/pandas/pandas/io/pytables.py:906, in HDFStore.select(self, key, where, start, stop, columns, iterator, chunksize, auto_close) 892 # create the iterator 893 it = TableIterator( 894 self, 895 s, (...) 903 auto_close=auto_close, 904 ) --> 906 return it.get_result() File ~/work/pandas/pandas/pandas/io/pytables.py:2029, in TableIterator.get_result(self, coordinates) 2026 where = self.where 2028 # directly return the result -> 2029 results = self.func(self.start, self.stop, where) 2030 self.close() 2031 return results File ~/work/pandas/pandas/pandas/io/pytables.py:890, in HDFStore.select.<locals>.func(_start, _stop, _where) 889 def func(_start, _stop, _where): --> 890 return s.read(start=_start, stop=_stop, where=_where, columns=columns) File ~/work/pandas/pandas/pandas/io/pytables.py:3278, in BlockManagerFixed.read(self, where, columns, start, stop) 3270 def read( 3271 self, 3272 where=None, (...) 3276 ) -> DataFrame: 3277 # start, stop applied to rows, so 0th axis only -> 3278 self.validate_read(columns, where) 3279 select_axis = self.obj_type()._get_block_manager_axis(0) 3281 axes = [] File ~/work/pandas/pandas/pandas/io/pytables.py:2922, in GenericFixed.validate_read(self, columns, where) 2917 raise TypeError( 2918 "cannot pass a column specification when reading " 2919 "a Fixed format store. this store must be selected in its entirety" 2920 ) 2921 if where is not None: -> 2922 raise TypeError( 2923 "cannot pass a where specification when reading " 2924 "from a Fixed format store. this store must be selected in its entirety" 2925 ) TypeError: cannot pass a where specification when reading from a Fixed format store. this store must be selected in its entirety 表格格式# HDFStore支持PyTables磁盘上的另一种格式,table 格式。从概念上讲,a 的table形状非常像 DataFrame,具有行和列。 Atable可以附加到同一会话或其他会话中。另外还支持删除和查询类型的操作。该格式由format='table'或指定format='t' 为append或put或to_hdf。 此格式也可以设置为选项,pd.set_option('io.hdf.default_format','table')以便默认put/append/to_hdf以该table格式存储。 In [481]: store = pd.HDFStore("store.h5") In [482]: df1 = df[0:4] In [483]: df2 = df[4:] # append data (creates a table automatically) In [484]: store.append("df", df1) In [485]: store.append("df", df2) In [486]: store Out[486]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 # select the entire object In [487]: store.select("df") Out[487]: A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 # the type of stored data In [488]: store.root.df._v_attrs.pandas_type Out[488]: 'frame_table' 笔记 table您还可以通过将format='table'or传递format='t'给操作来创建put。 分层键# 商店的密钥可以指定为字符串。这些可以采用分层路径名格式(例如foo/bar/bah),这将生成子存储的层次结构(或Groups用 PyTables 的说法)。键可以在没有前导“/”的情况下指定,并且始终是 绝对的(例如“foo”指的是“/foo”)。删除操作可以删除子商店及以下的所有内容,所以要小心。 In [489]: store.put("foo/bar/bah", df) In [490]: store.append("food/orange", df) In [491]: store.append("food/apple", df) In [492]: store Out[492]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 # a list of keys are returned In [493]: store.keys() Out[493]: ['/df', '/food/apple', '/food/orange', '/foo/bar/bah'] # remove all nodes under this level In [494]: store.remove("food") In [495]: store Out[495]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 您可以使用该方法遍历组层次结构,walk该方法将为每个组键及其内容的相关键生成一个元组。 In [496]: for (path, subgroups, subkeys) in store.walk(): .....: for subgroup in subgroups: .....: print("GROUP: {}/{}".format(path, subgroup)) .....: for subkey in subkeys: .....: key = "/".join([path, subkey]) .....: print("KEY: {}".format(key)) .....: print(store.get(key)) .....: GROUP: /foo KEY: /df A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 GROUP: /foo/bar KEY: /foo/bar/bah A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 警告 如上所述,对于存储在根节点下的项目,无法将分层键作为点(属性)访问进行检索。 In [497]: store.foo.bar.bah --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[497], line 1 ----> 1 store.foo.bar.bah File ~/work/pandas/pandas/pandas/io/pytables.py:613, in HDFStore.__getattr__(self, name) 611 """allow attribute access to get stores""" 612 try: --> 613 return self.get(name) 614 except (KeyError, ClosedFileError): 615 pass File ~/work/pandas/pandas/pandas/io/pytables.py:813, in HDFStore.get(self, key) 811 if group is None: 812 raise KeyError(f"No object named {key} in the file") --> 813 return self._read_group(group) File ~/work/pandas/pandas/pandas/io/pytables.py:1878, in HDFStore._read_group(self, group) 1877 def _read_group(self, group: Node): -> 1878 s = self._create_storer(group) 1879 s.infer_axes() 1880 return s.read() File ~/work/pandas/pandas/pandas/io/pytables.py:1752, in HDFStore._create_storer(self, group, format, value, encoding, errors) 1750 tt = "generic_table" 1751 else: -> 1752 raise TypeError( 1753 "cannot create a storer if the object is not existing " 1754 "nor a value are passed" 1755 ) 1756 else: 1757 if isinstance(value, Series): TypeError: cannot create a storer if the object is not existing nor a value are passed # you can directly access the actual PyTables node but using the root node In [498]: store.root.foo.bar.bah Out[498]: /foo/bar/bah (Group) '' children := ['axis0' (Array), 'axis1' (Array), 'block0_items' (Array), 'block0_values' (Array)] 相反,使用显式的基于字符串的键: In [499]: store["foo/bar/bah"] Out[499]: A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 存储类型# 在表中存储混合类型# 支持存储混合数据类型数据。使用附加列的最大大小将字符串存储为固定宽度。随后尝试附加更长的字符串将引发ValueError. 作为参数传递给追加将为字符串列设置更大的最小值。目前支持存储。对于字符串列,传递给append将更改磁盘上的默认nan表示(转换为/从),默认为.min_itemsize={`values`: size}floats, strings, ints, bools, datetime64nan_rep = 'nan'np.nannan In [500]: df_mixed = pd.DataFrame( .....: { .....: "A": np.random.randn(8), .....: "B": np.random.randn(8), .....: "C": np.array(np.random.randn(8), dtype="float32"), .....: "string": "string", .....: "int": 1, .....: "bool": True, .....: "datetime64": pd.Timestamp("20010102"), .....: }, .....: index=list(range(8)), .....: ) .....: In [501]: df_mixed.loc[df_mixed.index[3:5], ["A", "B", "string", "datetime64"]] = np.nan In [502]: store.append("df_mixed", df_mixed, min_itemsize={"values": 50}) In [503]: df_mixed1 = store.select("df_mixed") In [504]: df_mixed1 Out[504]: A B C ... int bool datetime64 0 0.013747 -1.166078 -1.292080 ... 1 True 1970-01-01 00:00:00.978393600 1 -0.712009 0.247572 1.526911 ... 1 True 1970-01-01 00:00:00.978393600 2 -0.645096 1.687406 0.288504 ... 1 True 1970-01-01 00:00:00.978393600 3 NaN NaN 0.097771 ... 1 True NaT 4 NaN NaN 1.536408 ... 1 True NaT 5 -0.023202 0.043702 0.926790 ... 1 True 1970-01-01 00:00:00.978393600 6 2.359782 0.088224 -0.676448 ... 1 True 1970-01-01 00:00:00.978393600 7 -0.143428 -0.813360 -0.179724 ... 1 True 1970-01-01 00:00:00.978393600 [8 rows x 7 columns] In [505]: df_mixed1.dtypes.value_counts() Out[505]: float64 2 float32 1 object 1 int64 1 bool 1 datetime64[ns] 1 Name: count, dtype: int64 # we have provided a minimum string column size In [506]: store.root.df_mixed.table Out[506]: /df_mixed/table (Table(8,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": Float64Col(shape=(2,), dflt=0.0, pos=1), "values_block_1": Float32Col(shape=(1,), dflt=0.0, pos=2), "values_block_2": StringCol(itemsize=50, shape=(1,), dflt=b'', pos=3), "values_block_3": Int64Col(shape=(1,), dflt=0, pos=4), "values_block_4": BoolCol(shape=(1,), dflt=False, pos=5), "values_block_5": Int64Col(shape=(1,), dflt=0, pos=6)} byteorder := 'little' chunkshape := (689,) autoindex := True colindexes := { "index": Index(6, mediumshuffle, zlib(1)).is_csi=False} 存储多索引数据帧# 将 MultiIndex 存储DataFrames为表与从同类索引中存储/选择非常相似DataFrames。 In [507]: index = pd.MultiIndex( .....: levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]], .....: codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]], .....: names=["foo", "bar"], .....: ) .....: In [508]: df_mi = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"]) In [509]: df_mi Out[509]: A B C foo bar foo one -1.303456 -0.642994 -0.649456 two 1.012694 0.414147 1.950460 three 1.094544 -0.802899 -0.583343 bar one 0.410395 0.618321 0.560398 two 1.434027 -0.033270 0.343197 baz two -1.646063 -0.695847 -0.429156 three -0.244688 -1.428229 -0.138691 qux one 1.866184 -1.446617 0.036660 two -1.660522 0.929553 -1.298649 three 3.565769 0.682402 1.041927 In [510]: store.append("df_mi", df_mi) In [511]: store.select("df_mi") Out[511]: A B C foo bar foo one -1.303456 -0.642994 -0.649456 two 1.012694 0.414147 1.950460 three 1.094544 -0.802899 -0.583343 bar one 0.410395 0.618321 0.560398 two 1.434027 -0.033270 0.343197 baz two -1.646063 -0.695847 -0.429156 three -0.244688 -1.428229 -0.138691 qux one 1.866184 -1.446617 0.036660 two -1.660522 0.929553 -1.298649 three 3.565769 0.682402 1.041927 # the levels are automatically included as data columns In [512]: store.select("df_mi", "foo=bar") Out[512]: A B C foo bar bar one 0.410395 0.618321 0.560398 two 1.434027 -0.033270 0.343197 笔记 该index关键字是保留关键字,不能用作级别名称。 查询# 查询表# select并且delete操作具有可选标准,可以指定该标准来仅选择/删除数据的子集。这允许拥有一个非常大的磁盘表并仅检索一部分数据。 查询是使用Term底层的类作为布尔表达式来指定的。 index并columns受支持的索引器DataFrames。 如果data_columns指定,它们可以用作附加索引器。 MultiIndex 中的级别名称,如果未提供,则使用默认名称level_0, , ... 。level_1 有效的比较运算符是: =, ==, !=, >, >=, <, <= 有效的布尔表达式与: |: 或者 &: 和 (和): 用于分组 这些规则类似于 pandas 中使用布尔表达式进行索引的方式。 笔记 =将自动扩展为比较运算符== ~是 not 运算符,但只能在非常有限的情况下使用 如果传递了表达式列表/元组,它们将通过以下方式组合& 以下是有效的表达式: 'index >= date' "columns = ['A', 'D']" "columns in ['A', 'D']" 'columns = A' 'columns == A' "~(columns = ['A', 'B'])" 'index > df.index[3] & string = "bar"' '(index > df.index[3] & index <= df.index[6]) | string = "bar"' "ts >= Timestamp('2012-02-01')" "major_axis>=20130101" 它们indexers位于子表达式的左侧: columns, major_axis,ts 子表达式的右侧(在比较运算符之后)可以是: 将被评估的函数,例如Timestamp('2012-02-01') 字符串,例如"bar" 类似日期的,例如20130101, 或"20130101" 列表,例如"['A', 'B']" 在本地名称空间中定义的变量,例如date 笔记 不建议通过将字符串插入到查询表达式中来将字符串传递给查询。只需将感兴趣的字符串分配给变量并在表达式中使用该变量即可。例如,这样做 string = "HolyMoly'" store.select("df", "index == string") 而不是这个 string = "HolyMoly'" store.select('df', f'index == {string}') 后者不起作用,并且会引发一个SyntaxError。请注意,变量中有一个单引号,后跟一个双引号string 。 如果必须插值,请使用'%r'格式说明符 store.select("df", "index == %r" % string) 这将引用string. 这里有些例子: In [513]: dfq = pd.DataFrame( .....: np.random.randn(10, 4), .....: columns=list("ABCD"), .....: index=pd.date_range("20130101", periods=10), .....: ) .....: In [514]: store.append("dfq", dfq, format="table", data_columns=True) 使用布尔表达式和内联函数求值。 In [515]: store.select("dfq", "index>pd.Timestamp('20130104') & columns=['A', 'B']") Out[515]: A B 2013-01-05 -0.830545 -0.457071 2013-01-06 0.431186 1.049421 2013-01-07 0.617509 -0.811230 2013-01-08 0.947422 -0.671233 2013-01-09 -0.183798 -1.211230 2013-01-10 0.361428 0.887304 使用内联列引用。 In [516]: store.select("dfq", where="A>0 or C>0") Out[516]: A B C D 2013-01-02 0.658179 0.362814 -0.917897 0.010165 2013-01-03 0.905122 1.848731 -1.184241 0.932053 2013-01-05 -0.830545 -0.457071 1.565581 1.148032 2013-01-06 0.431186 1.049421 0.383309 0.595013 2013-01-07 0.617509 -0.811230 -2.088563 -1.393500 2013-01-08 0.947422 -0.671233 -0.847097 -1.187785 2013-01-10 0.361428 0.887304 0.266457 -0.399641 可以提供关键字columns来选择要返回的列列表,这相当于传递一个 'columns=list_of_columns_to_filter': In [517]: store.select("df", "columns=['A', 'B']") Out[517]: A B 2000-01-01 0.858644 -0.851236 2000-01-02 -0.080372 -1.268121 2000-01-03 0.816983 1.965656 2000-01-04 0.712795 -0.062433 2000-01-05 -0.298721 -1.988045 2000-01-06 1.103675 1.382242 2000-01-07 -0.729161 -0.142928 2000-01-08 -1.005977 0.465222 start并且stop可以指定参数来限制总搜索空间。这些是表中的总行数。 笔记 selectValueError如果查询表达式具有未知的变量引用,则会引发 a 。通常这意味着您尝试选择不是data_column的列。 selectSyntaxError如果查询表达式无效,将引发 a 。 查询timedelta64[ns] # 您可以使用timedelta64[ns]类型来存储和查询。术语可以按以下格式指定:<float>(<unit>),其中浮点数可以带符号(和小数),单位可以 D,s,ms,us,ns表示时间增量。这是一个例子: In [518]: from datetime import timedelta In [519]: dftd = pd.DataFrame( .....: { .....: "A": pd.Timestamp("20130101"), .....: "B": [ .....: pd.Timestamp("20130101") + timedelta(days=i, seconds=10) .....: for i in range(10) .....: ], .....: } .....: ) .....: In [520]: dftd["C"] = dftd["A"] - dftd["B"] In [521]: dftd Out[521]: A B C 0 2013-01-01 2013-01-01 00:00:10 -1 days +23:59:50 1 2013-01-01 2013-01-02 00:00:10 -2 days +23:59:50 2 2013-01-01 2013-01-03 00:00:10 -3 days +23:59:50 3 2013-01-01 2013-01-04 00:00:10 -4 days +23:59:50 4 2013-01-01 2013-01-05 00:00:10 -5 days +23:59:50 5 2013-01-01 2013-01-06 00:00:10 -6 days +23:59:50 6 2013-01-01 2013-01-07 00:00:10 -7 days +23:59:50 7 2013-01-01 2013-01-08 00:00:10 -8 days +23:59:50 8 2013-01-01 2013-01-09 00:00:10 -9 days +23:59:50 9 2013-01-01 2013-01-10 00:00:10 -10 days +23:59:50 In [522]: store.append("dftd", dftd, data_columns=True) In [523]: store.select("dftd", "C<'-3.5D'") Out[523]: A B C 4 1970-01-01 00:00:01.356998400 2013-01-05 00:00:10 -5 days +23:59:50 5 1970-01-01 00:00:01.356998400 2013-01-06 00:00:10 -6 days +23:59:50 6 1970-01-01 00:00:01.356998400 2013-01-07 00:00:10 -7 days +23:59:50 7 1970-01-01 00:00:01.356998400 2013-01-08 00:00:10 -8 days +23:59:50 8 1970-01-01 00:00:01.356998400 2013-01-09 00:00:10 -9 days +23:59:50 9 1970-01-01 00:00:01.356998400 2013-01-10 00:00:10 -10 days +23:59:50 查询多重索引# MultiIndex可以通过使用级别名称来实现从 a 中进行选择。 In [524]: df_mi.index.names Out[524]: FrozenList(['foo', 'bar']) In [525]: store.select("df_mi", "foo=baz and bar=two") Out[525]: A B C foo bar baz two -1.646063 -0.695847 -0.429156 如果MultiIndex级别名称为,则级别将通过关键字以及您要从中选择的级别None自动可用。level_nnMultiIndex In [526]: index = pd.MultiIndex( .....: levels=[["foo", "bar", "baz", "qux"], ["one", "two", "three"]], .....: codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3, 3], [0, 1, 2, 0, 1, 1, 2, 0, 1, 2]], .....: ) .....: In [527]: df_mi_2 = pd.DataFrame(np.random.randn(10, 3), index=index, columns=["A", "B", "C"]) In [528]: df_mi_2 Out[528]: A B C foo one -0.219582 1.186860 -1.437189 two 0.053768 1.872644 -1.469813 three -0.564201 0.876341 0.407749 bar one -0.232583 0.179812 0.922152 two -1.820952 -0.641360 2.133239 baz two -0.941248 -0.136307 -1.271305 three -0.099774 -0.061438 -0.845172 qux one 0.465793 0.756995 -0.541690 two -0.802241 0.877657 -2.553831 three 0.094899 -2.319519 0.293601 In [529]: store.append("df_mi_2", df_mi_2) # the levels are automatically included as data columns with keyword level_n In [530]: store.select("df_mi_2", "level_0=foo and level_1=two") Out[530]: A B C foo two 0.053768 1.872644 -1.469813 索引# create_table_index 您可以在数据已存在于表中之后(在 andappend/put 操作之后)为表创建/修改索引。强烈鼓励创建表索引。当您使用select索引维度作为 时,这将大大加快您的查询速度where。 笔记 索引是在可索引和您指定的任何数据列上自动创建的。可以通过传递index=False到 来关闭此行为 append。 # we have automagically already created an index (in the first section) In [531]: i = store.root.df.table.cols.index.index In [532]: i.optlevel, i.kind Out[532]: (6, 'medium') # change an index by passing new parameters In [533]: store.create_table_index("df", optlevel=9, kind="full") In [534]: i = store.root.df.table.cols.index.index In [535]: i.optlevel, i.kind Out[535]: (9, 'full') 通常,当将大量数据附加到存储时,关闭每个附加的索引创建,然后在最后重新创建是很有用的。 In [536]: df_1 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB")) In [537]: df_2 = pd.DataFrame(np.random.randn(10, 2), columns=list("AB")) In [538]: st = pd.HDFStore("appends.h5", mode="w") In [539]: st.append("df", df_1, data_columns=["B"], index=False) In [540]: st.append("df", df_2, data_columns=["B"], index=False) In [541]: st.get_storer("df").table Out[541]: /df/table (Table(20,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1), "B": Float64Col(shape=(), dflt=0.0, pos=2)} byteorder := 'little' chunkshape := (2730,) 然后在完成追加后创建索引。 In [542]: st.create_table_index("df", columns=["B"], optlevel=9, kind="full") In [543]: st.get_storer("df").table Out[543]: /df/table (Table(20,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1), "B": Float64Col(shape=(), dflt=0.0, pos=2)} byteorder := 'little' chunkshape := (2730,) autoindex := True colindexes := { "B": Index(9, fullshuffle, zlib(1)).is_csi=True} In [544]: st.close() 请参阅此处,了解如何在现有存储上创建完全排序索引 (CSI)。 通过数据列查询# 您可以指定(并索引)某些您希望能够执行查询的列(除了indexable您始终可以查询的列)。例如,假设您想要在磁盘上执行此常见操作,并仅返回与此查询匹配的帧。您可以指定强制所有列为.data_columns = Truedata_columns In [545]: df_dc = df.copy() In [546]: df_dc["string"] = "foo" In [547]: df_dc.loc[df_dc.index[4:6], "string"] = np.nan In [548]: df_dc.loc[df_dc.index[7:9], "string"] = "bar" In [549]: df_dc["string2"] = "cool" In [550]: df_dc.loc[df_dc.index[1:3], ["B", "C"]] = 1.0 In [551]: df_dc Out[551]: A B C string string2 2000-01-01 0.858644 -0.851236 1.058006 foo cool 2000-01-02 -0.080372 1.000000 1.000000 foo cool 2000-01-03 0.816983 1.000000 1.000000 foo cool 2000-01-04 0.712795 -0.062433 0.736755 foo cool 2000-01-05 -0.298721 -1.988045 1.475308 NaN cool 2000-01-06 1.103675 1.382242 -0.650762 NaN cool 2000-01-07 -0.729161 -0.142928 -1.063038 foo cool 2000-01-08 -1.005977 0.465222 -0.094517 bar cool # on-disk operations In [552]: store.append("df_dc", df_dc, data_columns=["B", "C", "string", "string2"]) In [553]: store.select("df_dc", where="B > 0") Out[553]: A B C string string2 2000-01-02 -0.080372 1.000000 1.000000 foo cool 2000-01-03 0.816983 1.000000 1.000000 foo cool 2000-01-06 1.103675 1.382242 -0.650762 NaN cool 2000-01-08 -1.005977 0.465222 -0.094517 bar cool # getting creative In [554]: store.select("df_dc", "B > 0 & C > 0 & string == foo") Out[554]: A B C string string2 2000-01-02 -0.080372 1.0 1.0 foo cool 2000-01-03 0.816983 1.0 1.0 foo cool # this is in-memory version of this type of selection In [555]: df_dc[(df_dc.B > 0) & (df_dc.C > 0) & (df_dc.string == "foo")] Out[555]: A B C string string2 2000-01-02 -0.080372 1.0 1.0 foo cool 2000-01-03 0.816983 1.0 1.0 foo cool # we have automagically created this index and the B/C/string/string2 # columns are stored separately as ``PyTables`` columns In [556]: store.root.df_dc.table Out[556]: /df_dc/table (Table(8,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1), "B": Float64Col(shape=(), dflt=0.0, pos=2), "C": Float64Col(shape=(), dflt=0.0, pos=3), "string": StringCol(itemsize=3, shape=(), dflt=b'', pos=4), "string2": StringCol(itemsize=4, shape=(), dflt=b'', pos=5)} byteorder := 'little' chunkshape := (1680,) autoindex := True colindexes := { "index": Index(6, mediumshuffle, zlib(1)).is_csi=False, "B": Index(6, mediumshuffle, zlib(1)).is_csi=False, "C": Index(6, mediumshuffle, zlib(1)).is_csi=False, "string": Index(6, mediumshuffle, zlib(1)).is_csi=False, "string2": Index(6, mediumshuffle, zlib(1)).is_csi=False} 将大量列放入 中会导致性能下降 ,因此由用户来指定这些列。此外,在第一次追加/放置操作后,您不能更改数据列(也不能更改索引)(当然您可以简单地读入数据并创建一个新表!)。data columns 迭代器# 您可以将iterator=True或传递chunksize=number_in_a_chunk 给select和select_as_multiple来返回结果的迭代器。默认值是在一个块中返回 50,000 行。 In [557]: for df in store.select("df", chunksize=3): .....: print(df) .....: A B C 2000-01-01 0.858644 -0.851236 1.058006 2000-01-02 -0.080372 -1.268121 1.561967 2000-01-03 0.816983 1.965656 -1.169408 A B C 2000-01-04 0.712795 -0.062433 0.736755 2000-01-05 -0.298721 -1.988045 1.475308 2000-01-06 1.103675 1.382242 -0.650762 A B C 2000-01-07 -0.729161 -0.142928 -1.063038 2000-01-08 -1.005977 0.465222 -0.094517 笔记 您还可以使用read_hdf将打开存储的迭代器,然后在完成迭代时自动关闭存储。 for df in pd.read_hdf("store.h5", "df", chunksize=3): print(df) 请注意,chunksize 关键字适用于源行。因此,如果您正在执行查询,那么 chunksize 将细分表中的总行和应用的查询,并返回可能大小不等的块上的迭代器。 以下是生成查询并使用它来创建相同大小的返回块的方法。 In [558]: dfeq = pd.DataFrame({"number": np.arange(1, 11)}) In [559]: dfeq Out[559]: number 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 In [560]: store.append("dfeq", dfeq, data_columns=["number"]) In [561]: def chunks(l, n): .....: return [l[i: i + n] for i in range(0, len(l), n)] .....: In [562]: evens = [2, 4, 6, 8, 10] In [563]: coordinates = store.select_as_coordinates("dfeq", "number=evens") In [564]: for c in chunks(coordinates, 2): .....: print(store.select("dfeq", where=c)) .....: number 1 2 3 4 number 5 6 7 8 number 9 10 高级查询# 选择单列# 要检索单个可索引列或数据列,请使用方法select_column。例如,这将使您能够非常快速地获取索引。它们返回Series结果,按行号索引。这些当前不接受where选择器。 In [565]: store.select_column("df_dc", "index") Out[565]: 0 2000-01-01 1 2000-01-02 2 2000-01-03 3 2000-01-04 4 2000-01-05 5 2000-01-06 6 2000-01-07 7 2000-01-08 Name: index, dtype: datetime64[ns] In [566]: store.select_column("df_dc", "string") Out[566]: 0 foo 1 foo 2 foo 3 foo 4 NaN 5 NaN 6 foo 7 bar Name: string, dtype: object 选择坐标# 有时您想要获取查询的坐标(也称为索引位置)。这将返回 Index结果位置之一。这些坐标也可以传递给后续 where操作。 In [567]: df_coord = pd.DataFrame( .....: np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000) .....: ) .....: In [568]: store.append("df_coord", df_coord) In [569]: c = store.select_as_coordinates("df_coord", "index > 20020101") In [570]: c Out[570]: Index([732, 733, 734, 735, 736, 737, 738, 739, 740, 741, ... 990, 991, 992, 993, 994, 995, 996, 997, 998, 999], dtype='int64', length=268) In [571]: store.select("df_coord", where=c) Out[571]: 0 1 2002-01-02 0.007717 1.168386 2002-01-03 0.759328 -0.638934 2002-01-04 -1.154018 -0.324071 2002-01-05 -0.804551 -1.280593 2002-01-06 -0.047208 1.260503 ... ... ... 2002-09-22 -1.139583 0.344316 2002-09-23 -0.760643 -1.306704 2002-09-24 0.059018 1.775482 2002-09-25 1.242255 -0.055457 2002-09-26 0.410317 2.194489 [268 rows x 2 columns] 使用 where 掩码进行选择# 有时,您的查询可能涉及创建要选择的行列表。通常这mask是index索引操作的结果。此示例选择 datetimeindex 的月份,即 5。 In [572]: df_mask = pd.DataFrame( .....: np.random.randn(1000, 2), index=pd.date_range("20000101", periods=1000) .....: ) .....: In [573]: store.append("df_mask", df_mask) In [574]: c = store.select_column("df_mask", "index") In [575]: where = c[pd.DatetimeIndex(c).month == 5].index In [576]: store.select("df_mask", where=where) Out[576]: 0 1 2000-05-01 1.479511 0.516433 2000-05-02 -0.334984 -1.493537 2000-05-03 0.900321 0.049695 2000-05-04 0.614266 -1.077151 2000-05-05 0.233881 0.493246 ... ... ... 2002-05-27 0.294122 0.457407 2002-05-28 -1.102535 1.215650 2002-05-29 -0.432911 0.753606 2002-05-30 -1.105212 2.311877 2002-05-31 2.567296 2.610691 [93 rows x 2 columns] 存储对象# 如果您想检查存储的对象,请通过 检索 get_storer。您可以以编程方式使用它来获取对象中的行数。 In [577]: store.get_storer("df_dc").nrows Out[577]: 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 [578]: df_mt = pd.DataFrame( .....: np.random.randn(8, 6), .....: index=pd.date_range("1/1/2000", periods=8), .....: columns=["A", "B", "C", "D", "E", "F"], .....: ) .....: In [579]: df_mt["foo"] = "bar" In [580]: df_mt.loc[df_mt.index[1], ("A", "B")] = np.nan # you can also create the tables individually In [581]: store.append_to_multiple( .....: {"df1_mt": ["A", "B"], "df2_mt": None}, df_mt, selector="df1_mt" .....: ) .....: In [582]: store Out[582]: <class 'pandas.io.pytables.HDFStore'> File path: store.h5 # individual tables were created In [583]: store.select("df1_mt") Out[583]: A B 2000-01-01 0.162291 -0.430489 2000-01-02 NaN NaN 2000-01-03 0.429207 -1.099274 2000-01-04 1.869081 -1.466039 2000-01-05 0.092130 -1.726280 2000-01-06 0.266901 -0.036854 2000-01-07 -0.517871 -0.990317 2000-01-08 -0.231342 0.557402 In [584]: store.select("df2_mt") Out[584]: C D E F foo 2000-01-01 -2.502042 0.668149 0.460708 1.834518 bar 2000-01-02 0.130441 -0.608465 0.439872 0.506364 bar 2000-01-03 -1.069546 1.236277 0.116634 -1.772519 bar 2000-01-04 0.137462 0.313939 0.748471 -0.943009 bar 2000-01-05 0.836517 2.049798 0.562167 0.189952 bar 2000-01-06 1.112750 -0.151596 1.503311 0.939470 bar 2000-01-07 -0.294348 0.335844 -0.794159 1.495614 bar 2000-01-08 0.860312 -0.538674 -0.541986 -1.759606 bar # as a multiple In [585]: store.select_as_multiple( .....: ["df1_mt", "df2_mt"], .....: where=["A>0", "B>0"], .....: selector="df1_mt", .....: ) .....: Out[585]: Empty DataFrame Columns: [A, B, C, D, E, F, foo] Index: [] 从表中删除# 您可以通过指定 来有选择地从表中删除where。在删除行时,重要的是要了解PyTables通过擦除行然后移动后续数据来删除行。因此,根据数据的方向,删除可能是一项非常昂贵的操作。为了获得最佳性能,值得将要删除的维度作为 indexables. 数据(在磁盘上)按照indexables.这是一个简单的用例。您存储面板类型数据,日期在 中, major_axisID 在 中minor_axis。然后数据将像这样交错: 日期_1 id_1 id_2 。 id_n 日期_2 id_1 。 id_n 应该清楚的是,删除操作将major_axis相当快,因为删除一个块,然后移动后续数据。另一方面,删除操作将minor_axis非常昂贵。在这种情况下,使用where选择除缺失数据之外的所有数据的a 来重写表几乎肯定会更快。 警告 请注意,HDF5不会自动回收 h5 文件中的空间。因此,重复删除(或删除节点)并再次添加,往往会增加文件大小。 要重新打包并清理文件,请使用ptrepack。 注意事项和警告# 压缩# PyTables允许压缩存储的数据。这适用于所有类型的商店,而不仅仅是桌子。有两个参数用于控制压缩:complevel和complib。 complevel指定是否压缩数据以及压缩程度。 complevel=0并complevel=None禁用压缩和 0<complevel<10启用压缩。 complib指定要使用的压缩库。如果未指定任何内容zlib,则使用默认库。压缩库通常会针对良好的压缩率或速度进行优化,结果将取决于数据的类型。选择哪种类型的压缩取决于您的具体需求和数据。支持的压缩库列表: zlib:默认压缩库。压缩方面的经典,实现了良好的压缩率,但速度有点慢。 lzo:快速压缩和解压。 bzip2:良好的压缩率。 blosc:快速压缩和解压。 支持替代 blosc 压缩机: blosc:blosclz这是默认压缩器blosc blosc:lz4:一个紧凑、非常流行且快速的压缩器。 blosc:lz4hc:LZ4 的调整版本,以牺牲速度为代价产生更好的压缩比。 blosc:snappy:一种在许多地方使用的流行压缩器。 blosc:zlib:经典;比以前的速度稍慢,但实现了更好的压缩比。 blosc:zstd:一个非常平衡的编解码器;它提供了上述其他压缩比中最好的压缩比,并且速度相当快。 如果complib定义为除列出的库之外的其他内容, ValueError则会发出异常。 笔记 complib如果您的平台上缺少使用该选项指定的库,则默认压缩,zlib无需多言。 为文件中的所有对象启用压缩: store_compressed = pd.HDFStore( "store_compressed.h5", complevel=9, complib="blosc:blosclz" ) 或者在未启用压缩的存储中进行即时压缩(这只适用于表): store.append("df", df, complib="zlib", complevel=5) ptrepack # PyTables当表在写入后进行压缩时,可以提供更好的写入性能,而不是在开始时就打开压缩。您可以使用提供的PyTables实用程序 ptrepack。此外,还ptrepack可以事后更改压缩级别。 ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5 此外,将重新打包文件以允许您重用以前删除的空间。或者,可以简单地删除该文件并重新写入,或者使用该方法。ptrepack in.h5 out.h5copy 注意事项# 警告 HDFStore对于写入来说不是线程安全的。底层 PyTables仅支持并发读取(通过线程或进程)。如果需要同时进行读取和写入,则需要将这些操作序列化在单个进程的单个线程中。否则您将损坏您的数据。请参阅 ( GH 2397 ) 了解更多信息。 如果您使用锁来管理多个进程之间的写访问,您可能需要fsync()在释放写锁之前使用。为了方便起见,您可以使用store.flush(fsync=True)它来为您执行此操作。 一旦table创建了列(DataFrame)就固定了;只能附加完全相同的列 请注意,时区(例如,pytz.timezone('US/Eastern'))在时区版本之间不一定相同。因此,如果使用一个版本的时区库将数据本地化到 HDFStore 中的特定时区,并且使用另一版本更新该数据,则数据将转换为 UTC,因为这些时区不被视为相等。使用相同版本的时区库或使用tz_convert更新的时区定义。 警告 PyTablesNaturalNameWarning如果列名不能用作属性选择器, 将显示 a 。自然标识符仅包含字母、数字和下划线,并且不能以数字开头。其他标识符不能在子句中使用where,通常是一个坏主意。 数据类型# HDFStore将对象数据类型映射到PyTables底层数据类型。这意味着以下类型已知可以工作: 类型 代表缺失值 漂浮的 :float64, float32, float16 np.nan 整数 :int64, int32, int8, uint64,uint32, uint8 布尔值 datetime64[ns] NaT timedelta64[ns] NaT 分类:请参阅下面的部分 目的 :strings np.nan unicode不支持列,并且会失败。 分类数据# 您可以将包含category数据类型的数据写入HDFStore.查询的工作方式与对象数组相同。然而,categorydtyped 数据以更有效的方式存储。 In [586]: dfcat = pd.DataFrame( .....: {"A": pd.Series(list("aabbcdba")).astype("category"), "B": np.random.randn(8)} .....: ) .....: In [587]: dfcat Out[587]: A B 0 a -1.520478 1 a -1.069391 2 b -0.551981 3 b 0.452407 4 c 0.409257 5 d 0.301911 6 b -0.640843 7 a -2.253022 In [588]: dfcat.dtypes Out[588]: A category B float64 dtype: object In [589]: cstore = pd.HDFStore("cats.h5", mode="w") In [590]: cstore.append("dfcat", dfcat, format="table", data_columns=["A"]) In [591]: result = cstore.select("dfcat", where="A in ['b', 'c']") In [592]: result Out[592]: A B 2 b -0.551981 3 b 0.452407 4 c 0.409257 6 b -0.640843 In [593]: result.dtypes Out[593]: A category B float64 dtype: object 字符串列# 最小项目大小 的底层实现HDFStore对字符串列使用固定的列宽(itemsize)。字符串列 itemsize 计算为传递到第一个追加中的HDFStore,的数据(针对该列)的最大长度。后续追加可能会为列引入一个大于该列可以容纳的字符串,将引发异常(否则您可能会静默截断这些列,从而导致信息丢失)。将来我们可能会放宽这一点并允许用户指定的截断发生。 将第一个表创建传递min_itemsize给先验指定特定字符串列的最小长度。 min_itemsize可以是一个整数,也可以是一个将列名映射到整数的字典。您可以values作为键传递,以允许所有可索引或data_columns具有此 min_itemsize。 传递min_itemsize字典将导致所有传递的列自动创建为data_columns。 笔记 如果您没有传递 any data_columns,那么min_itemsize将会是传递的任何字符串的最大长度 In [594]: dfs = pd.DataFrame({"A": "foo", "B": "bar"}, index=list(range(5))) In [595]: dfs Out[595]: A B 0 foo bar 1 foo bar 2 foo bar 3 foo bar 4 foo bar # A and B have a size of 30 In [596]: store.append("dfs", dfs, min_itemsize=30) In [597]: store.get_storer("dfs").table Out[597]: /dfs/table (Table(5,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": StringCol(itemsize=30, shape=(2,), dflt=b'', pos=1)} byteorder := 'little' chunkshape := (963,) autoindex := True colindexes := { "index": Index(6, mediumshuffle, zlib(1)).is_csi=False} # A is created as a data_column with a size of 30 # B is size is calculated In [598]: store.append("dfs2", dfs, min_itemsize={"A": 30}) In [599]: store.get_storer("dfs2").table Out[599]: /dfs2/table (Table(5,)) '' description := { "index": Int64Col(shape=(), dflt=0, pos=0), "values_block_0": StringCol(itemsize=3, shape=(1,), dflt=b'', pos=1), "A": StringCol(itemsize=30, shape=(), dflt=b'', pos=2)} byteorder := 'little' chunkshape := (1598,) autoindex := True colindexes := { "index": Index(6, mediumshuffle, zlib(1)).is_csi=False, "A": Index(6, mediumshuffle, zlib(1)).is_csi=False} 南代表 字符串列将np.nan使用字符串表示形式序列化 a (缺失值)nan_rep。默认为字符串值nan。您可能会无意中将实际nan值变成缺失值。 In [600]: dfss = pd.DataFrame({"A": ["foo", "bar", "nan"]}) In [601]: dfss Out[601]: A 0 foo 1 bar 2 nan In [602]: store.append("dfss", dfss) In [603]: store.select("dfss") Out[603]: A 0 foo 1 bar 2 NaN # here you need to specify a different nan rep In [604]: store.append("dfss2", dfss, nan_rep="_nan_") In [605]: store.select("dfss2") Out[605]: A 0 foo 1 bar 2 nan 表现# tables与商店相比,格式会带来写入性能损失 fixed。好处是能够追加/删除和查询(可能是大量数据)。与常规存储相比,写入时间通常更长。查询时间可能非常快,尤其是在索引轴上。 您可以传递chunksize=<int>给append,指定写入块大小(默认为 50000)。这将显着降低写入时的内存使用量。 您可以传递expectedrows=<int>给第一个, 以设置预期的append总行数。PyTables这将优化读/写性能。 重复的行可以写入表中,但会在选择中被过滤掉(选择最后一项;因此表在主要、次要对上是唯一的) PerformanceWarning如果您尝试存储将由 PyTables 腌制的类型(而不是存储为特有类型),则会引发A 。请参阅 此处 了解更多信息和一些解决方案。 羽毛# Feather 为数据帧提供二进制柱状序列化。它旨在提高数据帧的读写效率,并使跨数据分析语言共享数据变得容易。 Feather 旨在忠实地序列化和反序列化 DataFrame,支持所有 pandas 数据类型,包括扩展数据类型,例如带有 tz 的分类和日期时间。 几个注意事项: 该格式不会为 写入Index, 或 ,如果提供非默认值,则会引发错误。您可以存储索引或忽略它。MultiIndexDataFrame.reset_index().reset_index(drop=True) 不支持重复的列名和非字符串列名 不支持对象数据类型列中的实际 Python 对象。这些将在尝试序列化时引发有用的错误消息。 请参阅完整文档。 In [606]: df = pd.DataFrame( .....: { .....: "a": list("abc"), .....: "b": list(range(1, 4)), .....: "c": np.arange(3, 6).astype("u1"), .....: "d": np.arange(4.0, 7.0, dtype="float64"), .....: "e": [True, False, True], .....: "f": pd.Categorical(list("abc")), .....: "g": pd.date_range("20130101", periods=3), .....: "h": pd.date_range("20130101", periods=3, tz="US/Eastern"), .....: "i": pd.date_range("20130101", periods=3, freq="ns"), .....: } .....: ) .....: In [607]: df Out[607]: a b c ... g h i 0 a 1 3 ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000 1 b 2 4 ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001 2 c 3 5 ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002 [3 rows x 9 columns] In [608]: df.dtypes Out[608]: a object b int64 c uint8 d float64 e bool f category g datetime64[ns] h datetime64[ns, US/Eastern] i datetime64[ns] dtype: object 写入羽毛文件。 In [609]: df.to_feather("example.feather") 从羽毛文件中读取。 In [610]: result = pd.read_feather("example.feather") In [611]: result Out[611]: a b c ... g h i 0 a 1 3 ... 2013-01-01 2013-01-01 00:00:00-05:00 2013-01-01 00:00:00.000000000 1 b 2 4 ... 2013-01-02 2013-01-02 00:00:00-05:00 2013-01-01 00:00:00.000000001 2 c 3 5 ... 2013-01-03 2013-01-03 00:00:00-05:00 2013-01-01 00:00:00.000000002 [3 rows x 9 columns] # we preserve dtypes In [612]: result.dtypes Out[612]: a object b int64 c uint8 d float64 e bool f category g datetime64[ns] h datetime64[ns, US/Eastern] i datetime64[ns] dtype: object 实木复合地板# Apache Parquet为数据帧提供分区二进制列序列化。它旨在提高数据帧的读写效率,并使跨数据分析语言共享数据变得容易。 Parquet 可以使用多种压缩技术来尽可能缩小文件大小,同时仍保持良好的读取性能。 Parquet 旨在忠实地序列化和反序列化DataFrame,支持所有 pandas 数据类型,包括扩展数据类型,例如带有 tz 的日期时间。 几个警告。 不支持重复的列名和非字符串列名。 引擎pyarrow始终将索引写入输出,但fastparquet仅写入非默认索引。这个额外的列可能会给那些没有预料到的非 pandas 消费者带来问题。index无论底层引擎如何,您都可以使用参数强制包含或省略索引。 索引级别名称(如果指定)必须是字符串。 在pyarrow引擎中,非字符串类型的分类数据类型可以序列化为镶木地板,但将反序列化为其原始数据类型。 引擎pyarrow保留ordered字符串类型的分类数据类型的标志。fastparquet不保留ordered标志。 不支持的类型包括Interval实际的 Python 对象类型。这些将在尝试序列化时引发有用的错误消息。Periodpyarrow >= 0.16.0 支持类型。 引擎pyarrow保留扩展数据类型,例如可为空的整数和字符串数据类型(需要 pyarrow >= 0.16.0,并且需要扩展类型来实现所需的协议,请参阅扩展类型文档)。 您可以指定 anengine来指导序列化。这可以是pyarrow、 或fastparquet、 或之一auto。如果未指定引擎,则pd.options.io.parquet.engine选中该选项;如果这也是auto,则pyarrow尝试并回退到fastparquet。 请参阅pyarrow和fastparquet的文档。 笔记 这些引擎非常相似,并且应该读取/写入几乎相同的 parquet 格式文件。 pyarrow>=8.0.0支持 timedelta 数据,fastparquet>=0.1.4支持时区感知日期时间。这些库的不同之处在于具有不同的底层依赖关系(fastparquet通过使用numba,而pyarrow使用 c 库)。 In [613]: df = pd.DataFrame( .....: { .....: "a": list("abc"), .....: "b": list(range(1, 4)), .....: "c": np.arange(3, 6).astype("u1"), .....: "d": np.arange(4.0, 7.0, dtype="float64"), .....: "e": [True, False, True], .....: "f": pd.date_range("20130101", periods=3), .....: "g": pd.date_range("20130101", periods=3, tz="US/Eastern"), .....: "h": pd.Categorical(list("abc")), .....: "i": pd.Categorical(list("abc"), ordered=True), .....: } .....: ) .....: In [614]: df Out[614]: a b c d e f g h i 0 a 1 3 4.0 True 2013-01-01 2013-01-01 00:00:00-05:00 a a 1 b 2 4 5.0 False 2013-01-02 2013-01-02 00:00:00-05:00 b b 2 c 3 5 6.0 True 2013-01-03 2013-01-03 00:00:00-05:00 c c In [615]: df.dtypes Out[615]: a object b int64 c uint8 d float64 e bool f datetime64[ns] g datetime64[ns, US/Eastern] h category i category dtype: object 写入镶木地板文件。 In [616]: df.to_parquet("example_pa.parquet", engine="pyarrow") In [617]: df.to_parquet("example_fp.parquet", engine="fastparquet") 从镶木地板文件中读取。 In [618]: result = pd.read_parquet("example_fp.parquet", engine="fastparquet") In [619]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow") In [620]: result.dtypes Out[620]: a object b int64 c uint8 d float64 e bool f datetime64[ns] g datetime64[ns, US/Eastern] h category i category dtype: object 通过设置dtype_backend参数,您可以控制用于生成的 DataFrame 的默认数据类型。 In [621]: result = pd.read_parquet("example_pa.parquet", engine="pyarrow", dtype_backend="pyarrow") In [622]: result.dtypes Out[622]: a string[pyarrow] b int64[pyarrow] c uint8[pyarrow] d double[pyarrow] e bool[pyarrow] f timestamp[ns][pyarrow] g timestamp[ns, tz=US/Eastern][pyarrow] h dictionary<values=string, indices=int32, order... i dictionary<values=string, indices=int32, order... dtype: object 笔记 请注意,这不支持fastparquet. 只读取 parquet 文件的某些列。 In [623]: result = pd.read_parquet( .....: "example_fp.parquet", .....: engine="fastparquet", .....: columns=["a", "b"], .....: ) .....: In [624]: result = pd.read_parquet( .....: "example_pa.parquet", .....: engine="pyarrow", .....: columns=["a", "b"], .....: ) .....: In [625]: result.dtypes Out[625]: a object b int64 dtype: object 处理索引# 将 a 序列化为DataFrameparquet 可能会将隐式索引包含为输出文件中的一列或多列。因此,这段代码: In [626]: df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) In [627]: df.to_parquet("test.parquet", engine="pyarrow") 如果您用于序列化,则会创建一个包含三列的 parquet 文件pyarrow: a、b和__index_level_0__。如果您使用fastparquet,索引可能会也可能不会 写入文件。 此意外的额外列会导致某些数据库(例如 Amazon Redshift)拒绝该文件,因为目标表中不存在该列。 如果您想在写入时省略数据帧的索引,请传递index=False给 to_parquet(): In [628]: df.to_parquet("test.parquet", index=False) 这将创建一个仅包含两个预期列的 parquet 文件,a并且b.如果您DataFrame有自定义索引,则当您将此文件加载到DataFrame. 传递index=True将始终写入索引,即使这不是底层引擎的默认行为。 对 Parquet 文件进行分区# Parquet 支持根据一列或多列的值对数据进行分区。 In [629]: df = pd.DataFrame({"a": [0, 0, 1, 1], "b": [0, 1, 0, 1]}) In [630]: df.to_parquet(path="test", engine="pyarrow", partition_cols=["a"], compression=None) 指定path数据将保存到的父目录。它们partition_cols是对数据集进行分区的列名称。列按照给定的顺序进行分区。分区分割由分区列中的唯一值确定。上面的示例创建了一个分区数据集,可能如下所示: test ├── a=0 │ ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet │ └── ... └── a=1 ├── e6ab24a4f45147b49b54a662f0c412a3.parquet └── ... 兽人# 与parquet格式类似,ORC 格式是数据帧的二进制柱状序列化。它旨在提高读取数据帧的效率。 pandas 为 ORC 格式提供了读取器和写入器,read_orc()并且to_orc().这需要pyarrow库。 警告 由于 pyarrow 出现一些问题,强烈建议使用conda 安装 pyarrow。 to_orc()需要 pyarrow>=7.0.0。 read_orc()并且to_orc()在 Windows 上尚不支持,您可以在安装可选依赖项上找到有效的环境。 有关支持的数据类型,请参阅Arrow 中支持的 ORC 功能。 目前,当数据帧转换为 ORC 文件时,日期时间列中的时区不会保留。 In [631]: df = pd.DataFrame( .....: { .....: "a": list("abc"), .....: "b": list(range(1, 4)), .....: "c": np.arange(4.0, 7.0, dtype="float64"), .....: "d": [True, False, True], .....: "e": pd.date_range("20130101", periods=3), .....: } .....: ) .....: In [632]: df Out[632]: a b c d e 0 a 1 4.0 True 2013-01-01 1 b 2 5.0 False 2013-01-02 2 c 3 6.0 True 2013-01-03 In [633]: df.dtypes Out[633]: a object b int64 c float64 d bool e datetime64[ns] dtype: object 写入 orc 文件。 In [634]: df.to_orc("example_pa.orc", engine="pyarrow") 从 orc 文件中读取。 In [635]: result = pd.read_orc("example_pa.orc") In [636]: result.dtypes Out[636]: a object b int64 c float64 d bool e datetime64[ns] dtype: object 只读取 orc 文件的某些列。 In [637]: result = pd.read_orc( .....: "example_pa.orc", .....: columns=["a", "b"], .....: ) .....: In [638]: result.dtypes Out[638]: a object b int64 dtype: object SQL 查询# 该pandas.io.sql模块提供了一组查询包装器,以方便数据检索并减少对特定于数据库的 API 的依赖。 如果可用,用户可能首先希望选择Apache Arrow ADBC驱动程序。这些驱动程序应提供最佳性能、空处理和类型检测。 2.2.0 版新增功能:添加了对 ADBC 驱动程序的本机支持 有关 ADBC 驱动程序及其开发状态的完整列表,请参阅ADBC 驱动程序实现状态 文档。 如果 ADBC 驱动程序不可用或可能缺少功能,用户应选择将 SQLAlchemy 与数据库驱动程序库一起安装。此类驱动程序的示例包括用于 PostgreSQL 的psycopg2 或用于 MySQL 的pymysql。对于SQLite,它默认包含在 Python 的标准库中。您可以在SQLAlchemy 文档中找到每种 SQL 方言支持的驱动程序的概述 。 如果未安装 SQLAlchemy,您可以使用sqlite3.Connection代替 SQLAlchemy 引擎、连接或 URI 字符串。 另请参阅一些食谱示例以了解一些高级策略。 主要功能是: read_sql_table(表名, con[, 架构, ...]) 将 SQL 数据库表读入 DataFrame。 read_sql_query(sql, con[, index_col, ...]) 将 SQL 查询读入 DataFrame。 read_sql(sql, con[, index_col, ...]) 将 SQL 查询或数据库表读入 DataFrame。 DataFrame.to_sql(名称、con、*[、模式、...]) 将存储在 DataFrame 中的记录写入 SQL 数据库。 笔记 该函数是and 的read_sql()便捷包装器 (并且为了向后兼容),并将根据提供的输入(数据库表名称或 sql 查询)委托给特定函数。如果表名有特殊字符,则不需要用引号引起来。read_sql_table()read_sql_query() 在以下示例中,我们使用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 创建引擎对象。您只需为要连接的每个数据库创建一次引擎。有关 URI 格式的更多信息create_engine(),请参阅下面的示例和 SQLAlchemy文档 In [639]: from sqlalchemy import create_engine # Create your engine. In [640]: engine = create_engine("sqlite:///:memory:") 如果您想管理自己的连接,您可以传递其中之一。下面的示例使用 Python 上下文管理器打开与数据库的连接,该管理器在块完成后自动关闭连接。 有关如何处理数据库连接的说明,请参阅SQLAlchemy 文档。 with engine.connect() as conn, conn.begin(): data = pd.read_sql_table("data", conn) 警告 当您打开与数据库的连接时,您还负责关闭它。保持连接打开的副作用可能包括锁定数据库或其他破坏行为。 编写数据帧# 假设a中有以下数据DataFrame data,我们可以使用将其插入数据库to_sql()。 ID 日期 列_1 列_2 列_3 26 2012-10-18 X 25.7 真的 42 2012-10-19 是 -12.4 错误的 63 2012-10-20 Z 5.73 真的 In [641]: import datetime In [642]: c = ["id", "Date", "Col_1", "Col_2", "Col_3"] In [643]: d = [ .....: (26, datetime.datetime(2010, 10, 18), "X", 27.5, True), .....: (42, datetime.datetime(2010, 10, 19), "Y", -12.5, False), .....: (63, datetime.datetime(2010, 10, 20), "Z", 5.73, True), .....: ] .....: In [644]: data = pd.DataFrame(d, columns=c) In [645]: data Out[645]: id Date Col_1 Col_2 Col_3 0 26 2010-10-18 X 27.50 True 1 42 2010-10-19 Y -12.50 False 2 63 2010-10-20 Z 5.73 True In [646]: data.to_sql("data", con=engine) Out[646]: 3 对于某些数据库,写入大型 DataFrame 可能会由于超出数据包大小限制而导致错误。可以通过chunksize调用时设置参数来避免这种情况 to_sql。例如,以下内容data一次批量写入 1000 行数据库: In [647]: data.to_sql("data_chunked", con=engine, chunksize=1000) Out[647]: 3 SQL 数据类型# 确保跨 SQL 数据库的数据类型管理一致具有挑战性。并非每个 SQL 数据库都提供相同的类型,即使它们提供了相同的类型,给定类型的实现也可能会有所不同,这会对类型的保留方式产生微妙的影响。 为了最大程度地保留数据库类型,建议用户在可用时使用 ADBC 驱动程序。 Arrow 类型系统提供了更广泛的类型,比历史上的 pandas/NumPy 类型系统更接近地匹配数据库类型。为了说明这一点,请注意不同数据库和 pandas 后端中可用类型的(非详尽)列表: numpy/熊猫 箭 postgres sqlite 整型16/整型16 整型16 小智 整数 int32/Int32 整型32 整数 整数 int64/Int64 整型64 BIGINT 整数 浮动32 浮动32 真实的 真实的 浮动64 浮动64 双精度 真实的 目的 细绳 文本 文本 布尔值 bool_ 布尔值 日期时间64[ns] 时间戳(我们) 时间戳 日期时间64[ns,tz] 时间戳(我们,tz) 时间戳 日期32 日期 月日纳米间隔 间隔 二进制 二进制 BLOB 十进制128 小数[ 1 ] 列表 阵列[ 1 ] 结构体 复合型[ 1 ] 脚注 如果您有兴趣在 DataFrame 的整个生命周期中尽可能保留数据库类型,则鼓励用户利用dtype_backend="pyarrow"以下论点:read_sql() # for roundtripping with pg_dbapi.connect(uri) as conn: df2 = pd.read_sql("pandas_table", conn, dtype_backend="pyarrow") 这将阻止您的数据转换为传统的 pandas/NumPy 类型系统,该系统通常以无法往返的方式转换 SQL 类型。 如果 ADBC 驱动程序不可用,to_sql() 将尝试根据数据的数据类型将数据映射到适当的 SQL 数据类型。当您有 dtype 列时object,pandas 将尝试推断数据类型。 您始终可以通过使用参数指定任何列的所需 SQL 类型来覆盖默认类型dtype。此参数需要一个将列名称映射到 SQLAlchemy 类型(或 sqlite3 后备模式的字符串)的字典。例如,指定使用 sqlalchemyString类型而不是Text字符串列的默认类型: In [648]: from sqlalchemy.types import String In [649]: data.to_sql("data_dtype", con=engine, dtype={"Col_1": String}) Out[649]: 3 笔记 由于不同数据库风格中对 timedelta 的支持有限,具有类型的列timedelta64将作为纳秒的整数值写入数据库,并会引发警告。唯一的例外是使用 ADBC PostgreSQL 驱动程序时,在这种情况下,时间增量将作为INTERVAL 笔记 dtype的列category将被转换为密集表示,就像您所得到的那样np.asarray(categorical)(例如,对于字符串类别,这给出了一个字符串数组)。因此,读回数据库表不会生成分类。 日期时间数据类型# 使用 ADBC 或 SQLAlchemy,to_sql()能够编写时区天然或时区感知的日期时间数据。但是,存储在数据库中的结果数据最终取决于所使用的数据库系统的日期时间数据支持的数据类型。 下表列出了一些常见数据库支持的日期时间数据的数据类型。其他数据库方言可能具有不同的日期时间数据数据类型。 数据库 SQL 日期时间类型 时区支持 SQLite TEXT 不 MySQL TIMESTAMP或者DATETIME 不 PostgreSQL TIMESTAMP或者TIMESTAMP WITH TIME ZONE 是的 将时区感知数据写入不支持时区的数据库时,数据将被写入时区原生时间戳,该时间戳是相对于时区的本地时间。 read_sql_table()还能够读取时区感知或天真的日期时间数据。当读取类型时,pandas 会将数据转换为 UTC。TIMESTAMP WITH TIME ZONE 插入方法# 该参数method控制使用的 SQL 插入子句。可能的值为: None:使用标准 SQLINSERT子句(每行一个)。 'multi':在单个INSERT子句中传递多个值。它使用并非所有后端都支持的特殊SQL 语法。这通常为Presto和Redshift等分析数据库提供更好的性能,但如果表包含许多列,则传统 SQL 后端的性能会较差。有关更多信息,请查看 SQLAlchemy文档。 可调用带签名:这可用于基于特定后端方言功能实现性能更高的插入方法。(pd_table, conn, keys, data_iter) 使用 PostgreSQL COPY 子句的可调用示例: # Alternative to_sql() *method* for DBs that support COPY FROM import csv from io import StringIO def psql_insert_copy(table, conn, keys, data_iter): """ Execute SQL statement inserting data Parameters ---------- table : pandas.io.sql.SQLTable conn : sqlalchemy.engine.Engine or sqlalchemy.engine.Connection keys : list of str Column names data_iter : Iterable that iterates the values to be inserted """ # gets a DBAPI connection that can provide a cursor dbapi_conn = conn.connection with dbapi_conn.cursor() as cur: s_buf = StringIO() writer = csv.writer(s_buf) writer.writerows(data_iter) s_buf.seek(0) columns = ', '.join(['"{}"'.format(k) for k in keys]) if table.schema: table_name = '{}.{}'.format(table.schema, table.name) else: table_name = table.name sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format( table_name, columns) cur.copy_expert(sql=sql, file=s_buf) 读表# read_sql_table()将读取给定表名和可选的要读取的列子集的数据库表。 笔记 为了使用read_sql_table(),您必须安装 ADBC 驱动程序或 SQLAlchemy 可选依赖项。 In [650]: pd.read_sql_table("data", engine) Out[650]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 X 27.50 True 1 1 42 2010-10-19 Y -12.50 False 2 2 63 2010-10-20 Z 5.73 True 笔记 ADBC 驱动程序会将数据库类型直接映射回箭头类型。对于其他驱动程序,请注意 pandas 从查询输出推断列数据类型,而不是通过在物理数据库模式中查找数据类型。例如,假设userid 是表中的整数列。然后,直观地,将返回整数值系列,而将返回对象值(str)系列。因此,如果查询输出为空,则所有结果列都将作为对象值返回(因为它们是最通用的)。如果您预见查询有时会生成空结果,您可能需要随后显式进行类型转换以确保数据类型的完整性。select userid ...select cast(userid as text) ... 您还可以指定列的名称作为索引DataFrame,并指定要读取的列的子集。 In [651]: pd.read_sql_table("data", engine, index_col="id") Out[651]: index Date Col_1 Col_2 Col_3 id 26 0 2010-10-18 X 27.50 True 42 1 2010-10-19 Y -12.50 False 63 2 2010-10-20 Z 5.73 True In [652]: pd.read_sql_table("data", engine, columns=["Col_1", "Col_2"]) Out[652]: Col_1 Col_2 0 X 27.50 1 Y -12.50 2 Z 5.73 您可以显式强制将列解析为日期: In [653]: pd.read_sql_table("data", engine, parse_dates=["Date"]) Out[653]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 X 27.50 True 1 1 42 2010-10-19 Y -12.50 False 2 2 63 2010-10-20 Z 5.73 True 如果需要,您可以显式指定格式字符串或要传递给的参数字典pandas.to_datetime(): pd.read_sql_table("data", engine, parse_dates={"Date": "%Y-%m-%d"}) pd.read_sql_table( "data", engine, parse_dates={"Date": {"format": "%Y-%m-%d %H:%M:%S"}}, ) 您可以使用检查表是否存在has_table() 架构支持# 通过and函数schema 中的关键字支持读取和写入不同的模式 。但请注意,这取决于数据库风格(sqlite 没有模式)。例如:read_sql_table()to_sql() df.to_sql(name="table", con=engine, schema="other_schema") pd.read_sql_table("table", engine, schema="other_schema") 查询# 您可以在函数中使用原始 SQL 进行查询read_sql_query()。在这种情况下,您必须使用适合您的数据库的 SQL 变体。使用 SQLAlchemy 时,您还可以传递与数据库无关的 SQLAlchemy 表达式语言构造。 In [654]: pd.read_sql_query("SELECT * FROM data", engine) Out[654]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 00:00:00.000000 X 27.50 1 1 1 42 2010-10-19 00:00:00.000000 Y -12.50 0 2 2 63 2010-10-20 00:00:00.000000 Z 5.73 1 当然,您可以指定更“复杂”的查询。 In [655]: pd.read_sql_query("SELECT id, Col_1, Col_2 FROM data WHERE id = 42;", engine) Out[655]: id Col_1 Col_2 0 42 Y -12.5 该read_sql_query()函数支持一个chunksize参数。指定此项将返回一个遍历查询结果块的迭代器: In [656]: df = pd.DataFrame(np.random.randn(20, 3), columns=list("abc")) In [657]: df.to_sql(name="data_chunks", con=engine, index=False) Out[657]: 20 In [658]: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5): .....: print(chunk) .....: a b c 0 -0.395347 -0.822726 -0.363777 1 1.676124 -0.908102 -1.391346 2 -1.094269 0.278380 1.205899 3 1.503443 0.932171 -0.709459 4 -0.645944 -1.351389 0.132023 a b c 0 0.210427 0.192202 0.661949 1 1.690629 -1.046044 0.618697 2 -0.013863 1.314289 1.951611 3 -1.485026 0.304662 1.194757 4 -0.446717 0.528496 -0.657575 a b c 0 -0.876654 0.336252 0.172668 1 0.337684 -0.411202 -0.828394 2 -0.244413 1.094948 0.087183 3 1.125934 -1.480095 1.205944 4 -0.451849 0.452214 -2.208192 a b c 0 -2.061019 0.044184 -0.017118 1 1.248959 -0.675595 -1.908296 2 -0.125934 1.491974 0.648726 3 0.391214 0.438609 1.634248 4 1.208707 -1.535740 1.620399 引擎连接示例# 要与 SQLAlchemy 连接,您可以使用该create_engine()函数从数据库 URI 创建引擎对象。您只需为要连接的每个数据库创建一次引擎。 from sqlalchemy import create_engine engine = create_engine("postgresql://scott:tiger@localhost:5432/mydatabase") engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo") engine = create_engine("oracle://scott:[email protected]: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文档中的示例 高级 SQLAlchemy 查询# 您可以使用 SQLAlchemy 构造来描述您的查询。 用于sqlalchemy.text()以与后端无关的方式指定查询参数 In [659]: import sqlalchemy as sa In [660]: pd.read_sql( .....: sa.text("SELECT * FROM data where Col_1=:col1"), engine, params={"col1": "X"} .....: ) .....: Out[660]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 00:00:00.000000 X 27.5 1 如果您有数据库的 SQLAlchemy 描述,您可以使用 SQLAlchemy 表达式表达 where 条件 In [661]: metadata = sa.MetaData() In [662]: data_table = sa.Table( .....: "data", .....: metadata, .....: sa.Column("index", sa.Integer), .....: sa.Column("Date", sa.DateTime), .....: sa.Column("Col_1", sa.String), .....: sa.Column("Col_2", sa.Float), .....: sa.Column("Col_3", sa.Boolean), .....: ) .....: In [663]: pd.read_sql(sa.select(data_table).where(data_table.c.Col_3 is True), engine) Out[663]: Empty DataFrame Columns: [index, Date, Col_1, Col_2, Col_3] Index: [] read_sql()您可以将 SQLAlchemy 表达式与传递给using 的参数结合起来sqlalchemy.bindparam() In [664]: import datetime as dt In [665]: expr = sa.select(data_table).where(data_table.c.Date > sa.bindparam("date")) In [666]: pd.read_sql(expr, engine, params={"date": dt.datetime(2010, 10, 18)}) Out[666]: index Date Col_1 Col_2 Col_3 0 1 2010-10-19 Y -12.50 False 1 2 2010-10-20 Z 5.73 True SQLite 后备# 支持在不使用 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) 谷歌大查询# 该pandas-gbq包提供了从 Google BigQuery 读取/写入的功能。 pandas 与这个外部包集成。如果pandas-gbq安装了,您可以使用 pandas 方法pd.read_gbq和DataFrame.to_gbq,它将调用 中的相应函数pandas-gbq。 完整的文档可以在这里找到。 统计数据格式# 写入 stata 格式# 该方法DataFrame.to_stata()会将 DataFrame 写入 .dta 文件。该文件的格式版本始终为 115 (Stata 12)。 In [667]: df = pd.DataFrame(np.random.randn(10, 2), columns=list("AB")) In [668]: df.to_stata("stata.dta") Stata数据文件的数据类型支持有限;仅包含 244 个或更少字符的字符串 、int8、int16、int32和float32 可以float64存储在.dta文件中。此外, Stata保留某些值来表示缺失的数据。导出超出 Stata 中特定数据类型允许范围的非缺失值会将变量重新键入为下一个更大的大小。例如,int8Stata 中的值被限制在 -127 到 100 之间,因此值大于 100 的变量将触发到int16.nan浮点数据类型中的值存储为基本缺失数据类型(.在Stata中)。 笔记 无法导出整数数据类型的缺失数据值。 Stata编写器通过转换为可表示数据的最小受支持类型来优雅地处理其他数据类型,包括int64、 bool、uint8、uint16。uint32例如,如果所有值都小于 100( Stata中非缺失数据的上限),则类型为 的数据uint8将被转换为,或者,如果值超出此范围,则变量将被转换为。int8int8int16 警告 如果值大于 2**53,则从int64到 的转换float64可能会导致精度损失。int64 警告 StataWriter并且 DataFrame.to_stata()仅支持最多包含 244 个字符的固定宽度字符串,这是版本 115 dta 文件格式所施加的限制。尝试写入字符串长度超过 244 个字符的StataValueError dta 文件会引发. 从 Stata 格式读取# 顶层函数read_stata将读取 dta 文件并返回 aDataFrame或 apandas.api.typing.StataReader可用于增量读取文件。 In [669]: pd.read_stata("stata.dta") Out[669]: index A B 0 0 -0.165614 0.490482 1 1 -0.637829 0.067091 2 2 -0.242577 1.348038 3 3 0.647699 -0.644937 4 4 0.625771 0.918376 5 5 0.401781 -1.488919 6 6 -0.981845 -0.046882 7 7 -0.306796 0.877025 8 8 -0.336606 0.624747 9 9 -1.582600 0.806340 指定 achunksize会生成一个 pandas.api.typing.StataReader可用于chunksize一次从文件中读取行的实例。该StataReader 对象可以用作迭代器。 In [670]: with pd.read_stata("stata.dta", chunksize=3) as reader: .....: for df in reader: .....: print(df.shape) .....: (3, 3) (3, 3) (3, 3) (1, 3) 要进行更细粒度的控制,请在每次调用时 使用iterator=True并指定 。chunksizeread() In [671]: with pd.read_stata("stata.dta", iterator=True) as reader: .....: chunk1 = reader.read(5) .....: chunk2 = reader.read(5) .....: 目前,它index是以列的形式检索的。 该参数convert_categoricals指示是否应读取值标签并使用Categorical它们来创建变量。值标签也可以通过函数检索value_labels,需要read() 在使用前调用。 该参数convert_missing指示是否应保留 Stata 中的缺失值表示。如果False(默认),缺失值表示为np.nan。如果True,则使用对象表示缺失值StataMissingValue,并且包含缺失值的列将具有object数据类型。 笔记 read_stata()并 StataReader支持 .dta 格式 113-115 (Stata 10-12)、117 (Stata 13) 和 118 (Stata 14)。 笔记 设置preserve_dtypes=False将向上转换为标准 pandas 数据类型: int64适用于所有整数类型和float64浮点数据。默认情况下,导入时会保留 Stata 数据类型。 笔记 所有StataReader对象,无论是通过(使用orread_stata() 时)创建还是手动实例化,都必须用作上下文管理器(例如语句)。虽然该方法可用,但不支持其使用。它不是公共 API 的一部分,将来会在没有警告的情况下被删除。iterator=Truechunksizewithclose() 分类数据# Categorical数据可以作为值标记数据导出到Stata数据文件。导出的数据由作为整数数据值的基础类别代码和作为值标签的类别组成。Stata没有与 a 等价的显式变量,并且有关变量是否Categorical有序的信息在导出时会丢失。 警告 Stata仅支持字符串值标签,因此str在导出数据时会在类别上调用。导出具有非字符串类别的变量会产生警告,并且如果类别的表示形式不唯一,Categorical可能会导致信息丢失。str 类似地,可以使用关键字参数(默认情况下)从Stata数据文件中将标记数据作为变量导入。关键字参数(默认情况下)确定导入的变量是否有序。Categoricalconvert_categoricalsTrueorder_categoricalsTrueCategorical 笔记 导入分类数据时, Stata数据文件中的变量值 不会保留,因为Categorical变量始终使用整数数据类型,其中和-1是类别数。如果需要Stata数据文件中的原始值,可以通过设置导入这些值,这将导入原始数据(但不导入变量标签)。原始值可以与导入的分类数据进行匹配,因为原始Stata数据值和导入的分类变量的类别代码之间存在简单的映射:缺失值分配为 code ,分配最小的原始值,第二小的值是依此类推 ,直到最大的原始值被分配给代码。n-1nconvert_categoricals=False-101n-1 笔记 Stata支持部分标记系列。这些系列具有某些但不是所有数据值的值标签。导入部分标记的系列将为Categorical带标记的值生成带字符串类别的值,为不带标签的值生成数字类别。 SAS 格式# 顶层函数read_sas()可以读取(但不能写入)SAS XPORT (.xpt) 和 SAS7BDAT (.sas7bdat) 格式文件。 SAS 文件仅包含两种值类型:ASCII 文本和浮点值(通常为 8 个字节,但有时会被截断)。对于 xport 文件,没有到整数、日期或分类的自动类型转换。对于 SAS7BDAT 文件,格式代码可以允许日期变量自动转换为日期。默认情况下,整个文件被读取并作为DataFrame. 指定chunksize或 用于iterator=True获取读取器对象(XportReader或SAS7BDATReader)以增量读取文件。读取器对象还具有包含有关文件及其变量的附加信息的属性。 读取 SAS7BDAT 文件: df = pd.read_sas("sas_data.sas7bdat") 获取一个迭代器并一次读取 XPORT 文件 100,000 行: def do_something(chunk): pass with pd.read_sas("sas_xport.xpt", chunk=100000) as rdr: for chunk in rdr: do_something(chunk) xport 文件格式的规范可从 SAS 网站获取。 没有针对 SAS7BDAT 格式的官方文档。 SPSS 格式# 顶层函数read_spss()可以读取(但不能写入)SPSS SAV (.sav) 和 ZSAV (.zsav) 格式文件。 SPSS 文件包含列名称。默认情况下,会读取整个文件,将分类列转换为pd.Categorical,并DataFrame返回包含所有列的 a 。 指定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 本身仅支持一组有限的文件格式的 IO,这些文件格式干净地映射到其表格数据模型。为了在 pandas 中读取和写入其他文件格式,我们推荐来自更广泛社区的这些软件包。 网络CDF # xarray提供受 pandas 启发的数据结构,DataFrame用于处理多维数据集,重点关注 netCDF 文件格式以及与 pandas 之间的轻松转换。 性能考虑# 这是对各种 IO 方法的非正式比较,使用 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 下面将使用以下测试函数来比较几种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_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