IO 工具(文本,CSV,HDF5,…)
pandas I/O API 是一组顶级reader函数,如pandas.read_csv()通常返回一个 pandas 对象。相应的writer函数是对象方法,如DataFrame.to_csv()。下面是包含可用reader和writer的表格。
| 格式类型 | 数据描述 | 读取器 | 写入器 |
| 文本 | CSV | read_csv | to_csv |
| 文本 | 定宽文本文件 | read_fwf | |
| 文本 | JSON | read_json | to_json |
| 文本 | HTML | read_html | to_html |
| 文本 | LaTeX | Styler.to_latex | |
| 文本 | XML | read_xml | to_xml |
| 文本 | 本地剪贴板 | read_clipboard | to_clipboard |
| 二进制 | MS Excel | read_excel | to_excel |
| 二进制 | OpenDocument | read_excel | |
| 二进制 | HDF5 格式 | read_hdf | to_hdf |
| 二进制 | Feather 格式 | read_feather | to_feather |
| 二进制 | Parquet 格式 | read_parquet | to_parquet |
| 二进制 | ORC 格式 | read_orc | to_orc |
| 二进制 | Stata | read_stata | to_stata |
| 二进制 | SAS | read_sas | |
| 二进制 | SPSS | read_spss | |
| 二进制 | Python Pickle 格式 | read_pickle | to_pickle |
| SQL | SQL | read_sql | to_sql |
| SQL | Google BigQuery | read_gbq | to_gbq |
这里是一些 IO 方法的非正式性能比较。
注意
对于使用StringIO类的示例,请确保在 Python 3 中导入它时使用from io import StringIO。
CSV & 文本文件
用于读取文本文件(也称为平面文件)的主要函数是 read_csv()。查看食谱以获取一些高级策略。
解析选项
read_csv() 接受以下常见参数:
基本
filepath_or_buffervarious
要么是文件的路径(str,pathlib.Path,或 py:py._path.local.LocalPath),URL(包括 http、ftp 和 S3 地址),或具有 read() 方法的任何对象(例如打开的文件或 StringIO)。
sepstr,默认为 read_csv() 的 ',',read_table() 的 \t
要使用的分隔符。如果 sep 为 None,则 C 引擎无法自动检测分隔符,但 Python 解析引擎可以,这意味着将使用后者,并通过 Python 的内置嗅探工具 csv.Sniffer 自动检测分隔符。此外,长度大于 1 且不同于 '\s+' 的分隔符将被解释为正则表达式,并且还将强制使用 Python 解析引擎。请注意,正则表达式分隔符容易忽略带引号的数据。正则表达式示例:'\\r\\t'。
delimiterstr,默认为 None
sep 的替代参数名称。
delim_whitespaceboolean,默认为 False
指定是否使用空格(例如 ' ' 或 '\t')作为分隔符。等同于设置 sep='\s+'。如果此选项设置为 True,则不应为 delimiter 参数传递任何内容。
列和索引位置及名称
headerint 或整数列表,默认为 'infer'
用作列名和数据起始位置的行号。默认行为是推断列名:如果没有传递名称,则行为与 header=0 相同,并且列名从文件的第一行推断出来,如果显式传递列名,则行为与 header=None 相同。显式传递 header=0 以能够替换现有名称。
头部可以是指定列的 MultiIndex 的行位置的整数列表,例如 [0,1,3]。未指定的中间行将被跳过(例如在此示例中跳过了 2)。请注意,如果 skip_blank_lines=True,此参数将忽略注释行和空行,因此 header=0 表示数据的第一行而不是文件的第一行。
namesarray-like,默认为 None
要使用的列名列表。如果文件不包含标题行,则应明确传递header=None。此列表中不允许重复项。
index_colint,str,int/str 序列或 False,可选,默认为None
用作DataFrame行标签的列,可以作为字符串名称或列索引给出。如果给出 int/str 序列,则使用 MultiIndex。
注意
可以使用index_col=False来强制 pandas不使用第一列作为索引,例如当您有一个每行末尾都有分隔符的格式错误文件时。
None的默认值指示 pandas 进行猜测。如果列标题行中的字段数等于数据文件主体中的字段数,则使用默认索引。如果大于此数,则使用前几列作为索引,以使数据主体中的剩余字段数等于标题中的字段数。
在标题之后的第一行用于确定要放入索引的列数。如果后续行的列数少于第一行,则用NaN填充。
可以通过usecols来避免这种情况。这确保了列按原样获取,而尾随数据被忽略。
usecols 类似列表或可调用对象,默认为None
返回列的子集。如果类似列表,则所有元素必须是位置的(即整数索引到文档列)或与用户在names中提供的列名对应的字符串。如果给出了names,则不考虑文档标题行。例如,一个有效的类似列表usecols参数可以是[0, 1, 2]或['foo', 'bar', 'baz']。
元素顺序被忽略,因此usecols=[0, 1]与[1, 0]相同。要从具有保留元素顺序的data实例化数据帧,请使用pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']]以['foo', 'bar']顺序或pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]以['bar', 'foo']顺序。
如果可调用,则将对列名评估可调用函数,返回可调用函数评估为 True 的名称:
In [1]: import pandas as pd In [2]: from io import StringIO In [3]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [4]: pd.read_csv(StringIO(data)) Out[4]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [5]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["COL1", "COL3"]) Out[5]: col1 col3 0 a 1 1 a 2 2 c 3
使用此参数可在使用 c 引擎时获得更快的解析时间和更低的内存使用率。Python 引擎在决定要删除哪些列之前首先加载数据。
通用解析配置
dtype 类型名称或列->类型的字典,默认为None
数据或列的数据类型。例如{'a': np.float64, 'b': np.int32, 'c': 'Int64'} 使用str或object与适当的na_values设置一起使用以保留并不解释数据类型。如果指定了转换器,则将应用转换器,而不是数据类型转换。
1.5.0 版本中的新功能:添加了对 defaultdict 的支持。指定一个 defaultdict 作为输入,其中默认值确定未明确列出的列的数据类型。
dtype_backend{“numpy_nullable”,“pyarrow”},默认为 NumPy 支持的数据帧
要使用的 dtype_backend,例如 DataFrame 是否应具有 NumPy 数组,当设置“numpy_nullable”时,所有具有可为空实现的 dtype 都使用可为空 dtype,如果设置“pyarrow”,则所有 dtype 都使用 pyarrow。
dtype_backends 仍处于实验阶段。
2.0 版本中新增。
engine{'c', 'python', 'pyarrow'}
使用的解析引擎。C 和 pyarrow 引擎速度更快,而 python 引擎目前功能更完整。目前只有 pyarrow 引擎支持多线程。
1.4.0 版本中新增:添加了“pyarrow”引擎作为实验性引擎,并且某些功能不受支持,或者可能无法正常工作。
转换器字典,默认为None
用于转换某些列中值的函数字典。键可以是整数或列标签。
true_values 列表,默认为None
要视为True的值。
false_values 列表,默认为None
要视为False的值。
skipinitialspace 布尔值,默认为False
在分隔符后跳过空格。
skiprows 类似列表或整数,默认为None
要跳过的行号(从 0 开始计数)或要在文件开头跳过的行数(整数)。
如果可调用,则将针对行索引评估可调用函数,如果应跳过该行则返回 True,否则返回 False:
In [6]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [7]: pd.read_csv(StringIO(data)) Out[7]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [8]: pd.read_csv(StringIO(data), skiprows=lambda x: x % 2 != 0) Out[8]: col1 col2 col3 0 a b 2
skipfooterint,默认为0
要跳过文件底部的行数(与 engine=’c’ 不兼容)。
nrows 整数,默认为None
要读取的文件行数。用于读取大文件的片段。
low_memory 布尔值,默认为True
在块中内部处理文件,导致解析时使用更少的内存,但可能混合类型推断。为确保没有混合类型,要么设置为False,要么使用dtype参数指定类型。请注意,无论如何整个文件都会读入单个DataFrame,使用chunksize或iterator参数以返回分块数据。 (仅适用于 C 解析器)
memory_map 布尔值,默认为 False
如果为filepath_or_buffer提供了文件路径,则直接将文件对象映射到内存,并直接从那里访问数据。使用此选项可以提高性能,因为不再有任何 I/O 开销。
NA 和缺失数据处理
na_values 标量、字符串、类似列表或字典,默认为None
附加字符串识别为 NA/NaN。如果传递了字典,则为每列指定特定的 NA 值。请参见下面的 na values const 以获取默认情况下解释为 NaN 的值列表。
keep_default_na 布尔值,默认为True
是否在解析数据时包括默认的 NaN 值。根据是否传递了na_values,行为如下:
- 如果
keep_default_na为True,并且指定了na_values,则na_values将附加到用于解析的默认 NaN 值。 - 如果
keep_default_na为True,并且未指定na_values,则仅使用默认 NaN 值进行解析。 - 如果
keep_default_na为False,且指定了na_values,则只使用指定的 NaN 值na_values进行解析。 - 如果
keep_default_na为False,且未指定na_values,则不会将任何字符串解析为 NaN。
请注意,如果传递na_filter为False,则keep_default_na和na_values参数将被忽略。
na_filter 布尔值,默认为True
检测缺失值标记(空字符串和 na_values 的值)。在没有任何 NA 的数据中,传递na_filter=False可以提高读取大文件的性能。
verbose 布尔值,默认为False
指示放置在非数字列中的 NA 值的数量。
skip_blank_lines 布尔值,默认为True
如果为True,则跳过空行而不解释为 NaN 值。
日期时间处理
parse_dates 布尔值或整数列表或名称列表或列表列表或字典,默认为False。
- 如果为
True-> 尝试解析索引。 - 如果
[1, 2, 3]-> 尝试将列 1、2、3 分别解析为单独的日期列。 - 如果
[[1, 3]]-> 合并列 1 和 3 并解析为单个日期列。 - 如果
{'foo': [1, 3]}-> 解析列 1、3 为日期,并将结果命名为‘foo’。
注意
存在用于 iso8601 格式日期的快速路径。
infer_datetime_format 布尔值,默认为False
如果为True并且启用了 parse_dates 用于某一列,则尝试推断日期时间格式以加快处理速度。
自 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,或者读取为object,然后根据需要应用to_datetime()。
date_format 字符串或列->格式字典,默认为None
如果与parse_dates一起使用,将根据此格式解析日期。对于更复杂的情况,请按照object读取,然后根据需要应用to_datetime()。
2.0.0 版本中的新功能。
dayfirst 布尔值,默认为False
DD/MM 格式日期,国际和欧洲格式。
cache_dates 布尔值,默认为 True
如果为 True,则使用唯一的转换日期缓存来应用日期时间转换。在解析重复日期字符串时可能会产生显著的加速,特别是带有时区偏移的日期字符串。
迭代
迭代器布尔值,默认为False
返回用于迭代或使用get_chunk()获取块的TextFileReader对象。
块大小整数,默认为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'设置为其中之一{'zip', 'gzip', 'bz2', 'zstd},其他键值对转发到zipfile.ZipFile、gzip.GzipFile、bz2.BZ2File或zstandard.ZstdDecompressor。例如,可以传递以下内容以获得更快的压缩和创建可重现的 gzip 存档:compression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1}。
从版本 1.2.0 更改:以前的版本将‘gzip’的字典条目转发到gzip.open。
千位分隔符字符串,默认为None
千位分隔符。
十进制字符串,默认为'.'
用于识别为小数点的字符。例如,对于欧洲数据使用','。
浮点精度字符串,默认为 None
指定 C 引擎应使用哪个转换器处理浮点值。选项为None表示普通转换器,high表示高精度转换器,round_trip表示往返转换器。
行终止符字符串(长度为 1),默认为None
用于将文件分成行的字符。仅与 C 解析器有效。
引用字符字符串(长度为 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元素。
转义字符字符串(长度为 1),默认为None
在引用方式为QUOTE_NONE时用于转义分隔符的单字符字符串。
注释字符串,默认为None
指示不应解析行的其余部分。如果在行的开头找到,整行将被完全忽略。此参数必须是单个字符。与空行一样(只要skip_blank_lines=True),完全注释的行由参数header忽略,但不由skiprows忽略。例如,如果comment='#',使用header=0解析‘#empty\na,b,c\n1,2,3’将导致���a,b,c’被视为标题。
encodingstr,默认为None
读取/写入 UTF 时要使用的编码(例如,'utf-8')。Python 标准编码列表。
dialectstr 或csv.Dialect实例,默认为None
如果提供,此参数将覆盖以下参数的值(默认或非默认):delimiter、doublequote、escapechar、skipinitialspace、quotechar和quoting。如果需要覆盖值,将发出 ParserWarning。有关更多详细信息,请参阅csv.Dialect文档。
错误处理
on_bad_lines(‘error’、‘warn’、‘skip’),默认为‘error’
指定在遇到坏行(字段过多的行)时要执行的操作。允许的值为:
- ‘error’,遇到坏行时引发 ParserError。
- ‘warn’,遇到坏行时打印警告并跳过该行。
- ‘skip’,遇到坏行时跳过而不引发或警告。
1.3.0 版中的新功能。
指定列数据类型
您可以指示整个DataFrame或单独的列的数据类型:
In [9]: import numpy as np In [10]: data = "a,b,c,d\n1,2,3,4\n5,6,7,8\n9,10,11" In [11]: print(data) a,b,c,d 1,2,3,4 5,6,7,8 9,10,11 In [12]: df = pd.read_csv(StringIO(data), dtype=object) In [13]: df Out[13]: a b c d 0 1 2 3 4 1 5 6 7 8 2 9 10 11 NaN In [14]: df["a"][0] Out[14]: '1' In [15]: df = pd.read_csv(StringIO(data), dtype={"b": object, "c": np.float64, "d": "Int64"}) In [16]: df.dtypes Out[16]: a int64 b object c float64 d Int64 dtype: object
幸运的是,pandas 提供了多种方法来确保您的列只包含一个dtype。如果您对这些概念不熟悉,可以查看这里了解有关 dtypes 的更多信息,以及这里了解有关 pandas 中object转换的更多信息。
例如,您可以使用read_csv()的converters参数:
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()函数强制转换 dtypes,
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。
最终,如何处理包含混合 dtypes 的列取决于您的具体需求。在上面的情况下,如果您想要将数据异常值设置为NaN,那么to_numeric()可能是您最好的选择。然而,如果您希望所有数据被强制转换,无论类型如何,那么使用read_csv()的converters参数肯定值得一试。
注意
在某些情况下,读取包含混合 dtype 列的异常数据将导致数据集不一致。如果依赖 pandas 推断列的 dtype,解析引擎将会推断数据的不同块的 dtype,而不是一次推断整个数据集。因此,可能会出现具有混合 dtype 的列。例如,
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包含某些列块的int dtype,以及由于读取的数据中混合 dtype 而导致其他列块的str。重要的是要注意,整体列将被标记为object的dtype,用于具有混合 dtype 的列。
设置dtype_backend="numpy_nullable"将导致每列具有可空 dtype。
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 ```### 指定分类 dtype `Categorical`列可以直接通过指定`dtype='category'`或`dtype=CategoricalDtype(categories, ordered)`来解析。 ```py In [35]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [36]: pd.read_csv(StringIO(data)) Out[36]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [37]: pd.read_csv(StringIO(data)).dtypes Out[37]: col1 object col2 object col3 int64 dtype: object In [38]: pd.read_csv(StringIO(data), dtype="category").dtypes Out[38]: col1 category col2 category col3 category dtype: object
可以使用字典规范将单独的列解析为Categorical:
In [39]: pd.read_csv(StringIO(data), dtype={"col1": "category"}).dtypes Out[39]: col1 category col2 object col3 int64 dtype: object
指定dtype='category'将导致一个无序的Categorical,其categories是数据中观察到的唯一值。要对类别和顺序进行更多控制,预先创建一个CategoricalDtype,并将其传递给该列的dtype。
In [40]: from pandas.api.types import CategoricalDtype In [41]: dtype = CategoricalDtype(["d", "c", "b", "a"], ordered=True) In [42]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).dtypes Out[42]: col1 category col2 object col3 int64 dtype: object
使用dtype=CategoricalDtype时,dtype.categories之外的“意外”值被视为缺失值。
In [43]: dtype = CategoricalDtype(["a", "b", "d"]) # No 'c' In [44]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).col1 Out[44]: 0 a 1 a 2 NaN Name: col1, dtype: category Categories (3, object): ['a', 'b', 'd']
这与Categorical.set_categories()的行为相匹配。
注意
使用dtype='category',生成的类别将始终被解析为字符串(对象 dtype)。如果类别是数字的,可以使用to_numeric()函数进行转换,或者根据需要使用另一个转换器,如to_datetime()。
当dtype是具有同质categories(全部是数字,全部是日期时间等)的CategoricalDtype时,转换会自动完成。
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
通过在header中与names参数结合使用,可以指示要使用的其他名称以及是否丢弃标题行(如果有):
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和skiprows,header将相对于skiprows的末尾。例如:
In [76]: data = ( ....: "# empty\n" ....: "# second empty line\n" ....: "# third emptyline\n" ....: "X,Y,Z\n" ....: "1,2,3\n" ....: "A,B,C\n" ....: "1,2.,4.\n" ....: "5.,NaN,10.0\n" ....: ) ....: In [77]: print(data) # empty # second empty line # third emptyline X,Y,Z 1,2,3 A,B,C 1,2.,4. 5.,NaN,10.0 In [78]: pd.read_csv(StringIO(data), comment="#", skiprows=4, header=1) Out[78]: A B C 0 1.0 2.0 4.0 1 5.0 NaN 10.0 ```#### 注释 有时文件中可能包含注释或元数据: ```py 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: ```py 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`: ```py 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
请注意
如果列或索引包含无法解析的日期,则整个列或索引将以对象数据类型不变返回。对于非标准日期时间解析,请在 pd.read_csv 后使用 to_datetime()。
请注意
read_csv 在解析 iso8601 格式的日期时间字符串(例如“2000-01-01T00:01:02+00:00”及类似变体)时具有快速路径。如果您可以安排数据以这种格式存储日期时间,加载时间将显著加快,观察到的速度提升约为 20 倍。
自版本 2.2.0 起已弃用:在 read_csv 中合并日期列已弃用。请改为在相关结果列上使用 pd.to_datetime。
日期解析函数
最后,解析器允许您指定自定义的 date_format。就性能而言,您应该按照以下顺序尝试这些日期解析方法:
- 如果您知道格式,请使用
date_format,例如:date_format="%d/%m/%Y"或date_format={column_name: "%d/%m/%Y"}。 - 如果不同列有不同格式,或者想要向
to_datetime传递任何额外选项(如utc),则应以object类型读取数据,然后使用to_datetime。
解析具有混合时区的 CSV
pandas 无法原生表示具有混合时区的列或索引。如果您的 CSV 文件包含具有混合时区的列,则默认结果将是一个对象类型的列,其中包含字符串,即使使用 parse_dates 也是如此。要将混合时区值解析为日期时间列,请以 object 类型读取,然后调用 to_datetime() 并设置 utc=True。
In [120]: content = """\ .....: a .....: 2000-01-01T00:00:00+05:00 .....: 2000-01-01T00:00:00+06:00""" .....: In [121]: df = pd.read_csv(StringIO(content)) In [122]: df["a"] = pd.to_datetime(df["a"], utc=True) In [123]: df["a"] Out[123]: 0 1999-12-31 19:00:00+00:00 1 1999-12-31 18:00:00+00:00 Name: a, dtype: datetime64[ns, UTC] ```#### 推断日期时间格式 以下是一些可以猜测的日期时间字符串示例(均表示 2011 年 12 月 30 日 00:00:00): + “20111230” + “2011/12/30” + “20111230 00:00:00” + “12/30/2011 00:00:00” + “30/Dec/2011 00:00:00” + “30/December/2011 00:00:00” 请注意,格式推断对 `dayfirst` 敏感。当 `dayfirst=True` 时,它会猜测“01/12/2011”是 12 月 1 日。当 `dayfirst=False`(默认)时,它会猜测“01/12/2011”是 1 月 12 日。 如果尝试解析日期字符串列,pandas 将尝试从第一个非 NaN 元素猜测格式,然后使用该格式解析列的其余部分。如果 pandas 无法猜测格式(例如,如果你的第一个字符串是 `'01 December US/Pacific 2000'`),那么将会发出警告,并且每一行将通过 `dateutil.parser.parse` 单独解析。解析日期的最安全方式是明确设置 `format=`。 ```py 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 引擎解析时使用特定的浮点数转换器。选项包括普通转换器、高���度转换器和往返转换器(在写入文件后保证往返值)。例如: ```py 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 的字符串,以便正确解析整数: 默认情况下,带有千位分隔符的数字将被解析为字符串: ```py 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') ```### 缺失值 要控制哪些值被解析为缺失值(用 `NaN` 表示),请在 `na_values` 中指定一个字符串。如果你指定一个字符串列表,那么其中的所有值都被视为缺失值。如果你指定一个数字(一个 `float`,比如 `5.0` 或一个 `integer`,比如 `5`),则相应的等效值也将被视为缺失值(在这种情况下,实际上 `[5.0, 5]` 被识别为 `NaN`)。 要完全覆盖默认识别为缺失的值,请指定 `keep_default_na=False`。 默认识别的 `NaN` 值为 `['-1.#IND', '1.#QNAN', '1.#IND', '-1.#QNAN', '#N/A N/A', '#N/A', 'N/A', 'n/a', 'NA', '<NA>', '#NA', 'NULL', 'null', 'NaN', '-NaN', 'nan', '-nan', 'None', '']`。 让我们考虑一些例子: ```py pd.read_csv("path_to_file.csv", na_values=[5])
在上面的示例中,5 和 5.0 将被识别为 NaN,除了默认值。一个字符串首先被解释为数值 5,然后作为 NaN。
pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=[""])
在上面的示例中,只有空字段将被识别为NaN。
pd.read_csv("path_to_file.csv", keep_default_na=False, na_values=["NA", "0"])
在上面的示例中,NA 和 0 作为字符串都被识别为 NaN。
pd.read_csv("path_to_file.csv", na_values=["Nope"])
默认值,除了字符串"Nope",也被识别为NaN。### 无穷大
inf 类似的值将被解析为np.inf(正无穷大),而 -inf 将被解析为-np.inf(负无穷大)。这些将忽略值的大小写,意思是Inf也将被解析为np.inf。### 布尔值
常见的值True、False、TRUE和FALSE都被识别为布尔值。偶尔你可能希望识别其他值为布尔值。要做到这一点,使用true_values和false_values选项如下:
In [156]: data = "a,b,c\n1,Yes,2\n3,No,4" In [157]: print(data) a,b,c 1,Yes,2 3,No,4 In [158]: pd.read_csv(StringIO(data)) Out[158]: a b c 0 1 Yes 2 1 3 No 4 In [159]: pd.read_csv(StringIO(data), true_values=["Yes"], false_values=["No"]) Out[159]: a b c 0 1 True 2 1 3 False 4 ```### 处理“坏”行 一些文件可能有格式错误的行,字段太少或太多。字段太少的行将在尾部字段中填充 NA 值。字段太多的行将默认引发错误: ```py 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 中新增。
或者通过传递一个可调用函数来处理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 mappingengine 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`](https://docs.python.org/3/library/csv.html#csv.Dialect "(在 Python v3.12)")实例。 假设你有一些未封闭引号的数据: ```py 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
解析器会尽力“做正确的事情”,并且不易受损。类型推断是一件很重要的事情。如果一个列可以被强制转换为整数类型而不改变内容,解析器将这样做。任何非数字列将与其他 pandas 对象一样以对象 dtype 传递。### 引用和转义字符
嵌套字段中的引号(和其他转义字符)可以以多种方式处理。一种方法是使用反斜杠;为了正确解析这些数据,你应该传递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()` 函数与具有已知和固定列宽的数据文件一起工作。`read_fwf` 的函数参数与 `read_csv` 大致相同,但有两个额外参数,并且 `delimiter` 参数的用法不同: + `colspecs`:一个给出每行固定宽度字段范围的对(元组)列表,作为半开区间(即,[from, to[)。字符串值 ‘infer’ 可以用于指示解析器尝试从数据的前 100 行检测列规格。如果未指定,默认行为是推断。 + `widths`:一个字段宽度列表,可以代替 ‘colspecs’ 使用,如果间隔是连续的。 + `delimiter`:固定宽度文件中要考虑为填充字符的字符。如果字段的填充字符不是空格(例如,‘~’),可以使用它来指定填充字符。 考虑一个典型的固定宽度数据文件: ```py 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
解析器将处理围绕列周围的额外空白,因此在文件中列之间有额外分隔是可以的。
默认情况下,read_fwf 将尝试通过使用文件的前 100 行推断文件的 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
Pandas 2.2 中文官方教程和指南(十·一)(2)https://developer.aliyun.com/article/1509772