前言
前面我们介绍了 pandas
的基础语法操作,下面我们开始介绍 pandas
的数据读写操作。
pandas
的 IO
API
是一组顶层的 reader
函数,比如 pandas.read_csv()
,会返回一个 pandas
对象。
而相应的 writer
函数是对象方法,如 DataFrame.to_csv()
。
下面列出了所有的 reader
和 writer
函数
注意:后面会用到 StringIO
,请确保导入
# python3
from io import StringIO
# python2
from StringIO import StringIO
CSV 和文本文件
pandas
用于读取文本文件的主要函数是 read_csv()
,可以读取固定分隔符的文件,如 csv
、txt
、tsv
以及 xls
等
1 参数解析
read_csv()
接受以下常用参数:
1.1 基础
filepath_or_buffer
: 变量
- 可以是文件路径、文件
URL
或任何带有read()
函数的对象
sep
: str
,默认 ,
,对于 read_table
是 \t
- 文件分隔符,如果设置为
None
,则C
引擎无法自动检测分隔符,而Python
引擎可以通过内置的嗅探器工具自动检测分隔符。 - 此外,如果设置的字符长度大于
1
,且不是'\s+'
,那么该字符串会被解析为正则表达式,且强制使用Python
解析引擎。 - 例如
'\\r\\t'
,但是正则表达式容易忽略文本中的引用数据。
delimiter
: str
, 默认为 None
sep
的替代参数,功能一致
1.2 列、索引、名称
header
: int
或 list
, 默认为 'infer'
- 用作列名的行号,默认行为是对列名进行推断:
- 如果未指定
names
参数其行为类似于header=0
,即从读取的第一行开始推断。 - 如果设置了
names
,则行为与header=None
相同。
- 如果未指定
- 也可以为
header
设置列表,表示多级列名。如[0,1,3]
,未指定的行(这里是2
)将会被跳过,如果skip_blank_lines=True
,则会跳过空行和注释的行。因此header=0
并不是代表文件的第一行
names
: array-like
, 默认为 None
- 需要设置的列名列表,如果文件中不包含标题行,则应显式传递
header=None
,且此列表中不允许有重复值。
index_col
: int
, str
, sequence of int/str
, False
, 默认为 None
-
用作
DataFrame
的索引的列,可以字符串名称或列索引的形式给出。如果指定了列表,则使用MultiIndex
-
注意:
index_col=False
可用于强制pandas
不要将第一列用作索引。例如,当您的文件是每行末尾都带有一个分隔符的错误文件时。
usecols
: 列表或函数, 默认为 None
-
只读取指定的列。如果是列表,则所有元素都必须是位置(即文件列中的整数索引)或字符串,这些字符串必须与
names
参数提供的或从文档标题行推断出的列名相对应。 -
列表中的顺序会被忽略,即
usecols=[0, 1]
等价于[1, 0]
-
如果是可调用函数,将会根据列名计算,返回可调用函数计算为
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
使用此参数可以大大加快解析时间并降低内存使用
squeeze
: boolean
, 默认为 False
- 如果解析的数据只包含一列,那么返回一个
Series
prefix
: str
, 默认为 None
- 当没有标题时,添加到自动生成的列号的前缀,例如
'X'
表示X0
,X1
…
mangle_dupe_cols
: boolean
, 默认为 True
- 重复的列将被指定为
'X'
,'X.1'
…'X.N'
,而不是'X'
… 。如果在列中有重复的名称,传递False
将导致数据被覆盖
1.3 常规解析配置
dtype
: 类型名或类型字典(column -> type
), 默认为 None
- 数据或列的数据类型。例如。
{'a':np.float64,'b':np.int32}
engine
: {'c', 'python'}
- 要使用的解析器引擎。
C
引擎更快,而Python
引擎目前功能更完整
converters
: dict
, 默认为 None
- 用于在某些列中对值进行转换的函数字典。键可以是整数,也可以是列名
true_values
: list
, 默认为 None
- 数据值解析为
True
false_values
: list
, 默认为 None
- 数据值解析为
False
skipinitialspace
: boolean
, 默认为 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
skipfooter
: int
, 默认为 0
- 需要跳过文件末尾的行数(不支持
C
引擎)
nrows
: int
, 默认为 None
- 要读取的文件行数,对于读取大文件很有用
memory_map
: boolean
, 默认为 False
- 如果为
filepath_or_buffer
参数指定了文件路径,则将文件对象直接映射到内存中,然后直接从那里访问数据。使用此选项可以提高性能,因为不再有任何I/O
开销
1.4 NA 和缺失数据处理
na_values
: scalar
, str
, list-like
, dict
, 默认为 None
- 需要转换为
NA
值的字符串
keep_default_na
: boolean
, 默认为 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
, 只解析na_values
指定的NaN
-
keep_default_na=False
, 且未指定na_values
, 字符串不会被解析为NaN
-
-
注意:如果
na_filter=False
,那么keep_default_na
和na_values
参数将被忽略
na_filter
: boolean
, 默认为 True
- 检测缺失值标记(空字符串和
na_values
的值)。在没有任何NA
的数据中,设置na_filter=False
可以提高读取大文件的性能
skip_blank_lines
: boolean
, 默认为 True
- 如果为
True
,则跳过空行,而不是解释为NaN
值
1.5 日期时间处理
parse_dates
: 布尔值、列表或嵌套列表、字典, 默认为 False
.
-
如果为
True
-> 尝试解析索引 -
如果为
[1, 2, 3]
-> 尝试将1, 2, 3
列解析为分隔的日期 -
如果为
[[1, 3]]
-> 将1, 3
列解析为单个日期列 -
如果为
{'foo': [1, 3]}
-> 将1, 3
列作为日期并设置列名为foo
infer_datetime_format
: 布尔值, 默认为 False
- 如果设置为
True
且设置了parse_dates
,则尝试推断datetime
格式以加快处理速度
date_parser
: 函数, 默认为 None
- 用于将字符串序列转换为日期时间实例数组的函数。默认使用
dateutil.parser.parser
进行转换,pandas
将尝试以三种不同的方式调用date_parser
- 传递一个或多个数组(
parse_dates
定义的列)作为参数; - 将
parse_dates
定义的列中的字符串值连接到单个数组中,并将其传递; - 使用一个或多个字符串(对应于
parse_dates
定义的列)作为参数,对每一行调用date_parser
一次。
- 传递一个或多个数组(
dayfirst
: 布尔值, 默认为 False
DD/MM
格式的日期
cache_dates
: 布尔值, 默认为 True
- 如果为
True
,则使用唯一的、经过转换的日期缓存来应用datetime
转换。 - 在解析重复的日期字符串,特别是带有时区偏移量的日期字符串时,可能会显著提高速度。
1.6 迭代
iterator
: boolean
, 默认为 False
- 返回
TextFileReader
对象以进行迭代或使用get_chunk()
来获取块
1.7 引用、压缩和文件格式
compression
: {'infer', 'gzip', 'bz2', 'zip', 'xz', None, dict}
, 默认为 'infer'
-
用于对磁盘数据进行即时解压缩。如果为
"infer"
,则如果filepath_or_buffer
是文件路径且以".gz"
,".bz2"
,".zip"
或".xz"
结尾,则分别使用gzip
,bz2
,zip
或xz
解压,否则不进行解压缩。 -
如果使用
"zip"
,则ZIP
文件必须仅包含一个要读取的数据文件。设置为None
表示不解压 -
也可以使用字典的方式,键为
method
的值从{'zip', 'gzip', 'bz2'}
中选择。例如
compression={'method': 'gzip', 'compresslevel': 1, 'mtime': 1}
thousandsstr
, 默认为 None
- 数值在千位的分隔符
decimal
: str
, 默认为 '.'
- 小数点
float_precision
: string
, 默认为 None
- 指定
C
引擎应该使用哪个转换器来处理浮点值。普通转换器的选项为None
,高精度转换器的选项为high
,双向转换器的选项为round_trip
。
quotechar
: str
(长度为 1
)
- 用于表示被引用数据的开始和结束的字符。带引号的数据里的分隔符将被忽略
comment
: str
, 默认为 None
- 用于跳过该字符开头的行,例如,如果
comment='#'
,将会跳过#
开头的行
encoding
: str
, 默认为 None
- 设置编码格式
1.8 错误处理
error_bad_linesboolean
, 默认为 True
- 默认情况下,字段太多的行(例如,带有太多逗号的
csv
文件)会引发异常,并且不会返回任何DataFrame
。 - 如果设置为
False
,则这些坏行将会被删除
warn_bad_linesboolean
, 默认为 True
- 如果
error_bad_lines=False
且warn_bad_lines=True
,每个坏行都会输出一个警告
2. 指定数据列的类型
您可以指示整个 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
你可以使用 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]:
<class 'str'> 4
Name: col_1, 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]:
<class 'float'> 4
Name: col_1, dtype: int64
它将所有有效的数值转换为浮点数,而将无效的解析为 NaN
最后,如何处理包含混合类型的列取决于你的具体需要。在上面的例子中,如果您只想要将异常的数据转换为 NaN
,那么 to_numeric()
可能是您的最佳选择。
然而,如果您想要强制转换所有数据,而无论类型如何,那么使用 read_csv()
的 converters
参数会更好
注意
在某些情况下,读取包含混合类型列的异常数据将导致数据集不一致。
如果您依赖
pandas
来推断列的类型,解析引擎将继续推断数据块的类型,而不是一次推断整个数据集。In [25]: col_1 = list(range(500000)) + ["a", "b"] + list(range(500000)) In [26]: df = pd.DataFrame({"col_1": col_1}) In [27]: df.to_csv("foo.csv") In [28]: mixed_df = pd.read_csv("foo.csv") In [29]: mixed_df["col_1"].apply(type).value_counts() Out[29]: <class 'int'> 737858 <class 'str'> 262144 Name: col_1, dtype: int64 In [30]: mixed_df["col_1"].dtype Out[30]: dtype('O')
这就导致
mixed_df
对于列的某些块包含int
类型,而对于其他块则包含str
,这是由于读取的数据是混合类型。
3 分类类型
分类类型可以通过指定 dtype='category'
或 dtype=CategoricalDtype(categories, ordered)
直接解析
In [31]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3"
In [32]: pd.read_csv(StringIO(data))
Out[32]:
col1 col2 col3
0 a b 1
1 a b 2
2 c d 3
In [33]: pd.read_csv(StringIO(data)).dtypes
Out[33]:
col1 object
col2 object
col3 int64
dtype: object
In [34]: pd.read_csv(StringIO(data), dtype="category").dtypes
Out[34]:
col1 category
col2 category
col3 category
dtype: object
也可以使用字典对指定列设置类型
In [35]: pd.read_csv(StringIO(data), dtype={"col1": "category"}).dtypes
Out[35]:
col1 category
col2 object
col3 int64
dtype: object
指定 dtype ='category'
将导致无序分类,其类别是数据中所有观察值的集合。
如果要更好地控制类别和顺序,请提前创建 CategoricalDtype
,然后将其传递给该列的 dtype
In [36]: from pandas.api.types import CategoricalDtype
In [37]: dtype = CategoricalDtype(["d", "c", "b", "a"], ordered=True)
In [38]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).dtypes
Out[38]:
col1 category
col2 object
col3 int64
dtype: object
使用 dtype=CategoricalDtype
时,超出的数据类型将被视为缺失值
In [39]: dtype = CategoricalDtype(["a", "b", "d"]) # No 'c'
In [40]: pd.read_csv(StringIO(data), dtype={"col1": dtype}).col1
Out[40]:
0 a
1 a
2 NaN
Name: col1, dtype: category
Categories (3, object): ['a', 'b', 'd']
这与 Categorical.set_categories()
的行为相匹配
注意
当
dtype='category'
时,生成的类别将始终解析为字符串(object
类型)。如果类别是数字型,则可以使用
to_numeric()
函数或其他转换器进行转换,如to_datetime()
当dtype
是一个同构(所有数字、所有日期时间等)的CategoricalDtype
时,转换将自动完成In [41]: df = pd.read_csv(StringIO(data), dtype="category") In [42]: df.dtypes Out[42]: col1 category col2 category col3 category dtype: object In [43]: df["col3"] Out[43]: 0 1 1 2 2 3 Name: col3, dtype: category Categories (3, object): ['1', '2', '3'] In [44]: df["col3"].cat.categories = pd.to_numeric(df["col3"].cat.categories) In [45]: df["col3"] Out[45]: 0 1 1 2 2 3 Name: col3, dtype: category Categories (3, int64): [1, 2, 3]
4 列的命名和使用
4.1 处理列名
一个文件可能有也可能没有标题行,pandas
默认将第一行用作列名
In [46]: data = "a,b,c\n1,2,3\n4,5,6\n7,8,9"
In [47]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9
In [48]: pd.read_csv(StringIO(data))
Out[48]:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
通过将 names
参数与 header
一起使用
In [49]: print(data)
a,b,c
1,2,3
4,5,6
7,8,9
In [50]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=0)
Out[50]:
foo bar baz
0 1 2 3
1 4 5 6
2 7 8 9
In [51]: pd.read_csv(StringIO(data), names=["foo", "bar", "baz"], header=None)
Out[51]:
foo bar baz
0 a b c
1 1 2 3
2 4 5 6
3 7 8 9
如果标题不在第一行中,可以将行号传递给 header
,将会跳过前面的行
In [52]: data = "skip this skip it\na,b,c\n1,2,3\n4,5,6\n7,8,9"
In [53]: pd.read_csv(StringIO(data), header=1)
Out[53]:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
5. 重复列名处理
如果文件或表头包含重复的名称,默认情况下 pandas
会将它们区分开,以防止数据覆盖
In [54]: data = "a,b,a\n0,1,2\n3,4,5"
In [55]: pd.read_csv(StringIO(data))
Out[55]:
a b a.1
0 0 1 2
1 3 4 5
默认情况下,mangle_dupe_cols=True
会使用 .N
的方式标记重名的列,如果设置 mangle_dupe_cols=False
将会出现重复的列
In [2]: data = 'a,b,a\n0,1,2\n3,4,5'
In [3]: pd.read_csv(StringIO(data), mangle_dupe_cols=False)
Out[3]:
a b a
0 2 1 2
1 5 4 5
为了防止用户遇到重复数据的问题,现在,如果 mangle_dupe_cols != True
,则会引发 ValueError
异常:
In [2]: data = 'a,b,a\n0,1,2\n3,4,5'
In [3]: pd.read_csv(StringIO(data), mangle_dupe_cols=False)
...
ValueError: Setting mangle_dupe_cols=False is not supported yet
5.1 筛选列
usecols
参数允许你选择文件中指定的列,可以使用列名、位置或一个可调用的函数
In [56]: data = "a,b,c,d\n1,2,3,foo\n4,5,6,bar\n7,8,9,baz"
In [57]: pd.read_csv(StringIO(data))
Out[57]:
a b c d
0 1 2 3 foo
1 4 5 6 bar
2 7 8 9 baz
In [58]: pd.read_csv(StringIO(data), usecols=["b", "d"])
Out[58]:
b d
0 2 foo
1 5 bar
2 8 baz
In [59]: pd.read_csv(StringIO(data), usecols=[0, 2, 3])
Out[59]:
a c d
0 1 3 foo
1 4 6 bar
2 7 9 baz
In [60]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["A", "C"])
Out[60]:
a c
0 1 3
1 4 6
2 7 9
usecols
参数还可以用于指定最终结果中不使用哪些列
In [61]: pd.read_csv(StringIO(data), usecols=lambda x: x not in ["a", "c"])
Out[61]:
b d
0 2 foo
1 5 bar
2 8 baz
在这个例子中,我们排除了 a
和 c
列
6 注释和空行
6.1 忽略注释行和空行
如果指定了 comment
参数,则注释的行将被忽略。默认情况下,空白的行也会被忽略
In [62]: data = "\na,b,c\n \n# commented line\n1,2,3\n\n4,5,6"
In [63]: print(data)
a,b,c
# commented line
1,2,3
4,5,6
In [64]: pd.read_csv(StringIO(data), comment="#")
Out[64]:
a b c
0 1 2 3
1 4 5 6
如果设置 skip_blank_lines=False
,则不会忽略空行和注释行
In [65]: data = "a,b,c\n\n1,2,3\n\n\n4,5,6"
In [66]: pd.read_csv(StringIO(data), skip_blank_lines=False)
Out[66]:
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 [67]: data = "#comment\na,b,c\nA,B,C\n1,2,3"
In [68]: pd.read_csv(StringIO(data), comment="#", header=1)
Out[68]:
A B C
0 1 2 3
In [69]: data = "A,B,C\n#comment\na,b,c\n1,2,3"
In [70]: pd.read_csv(StringIO(data), comment="#", skiprows=2)
Out[70]:
a b c
0 1 2 3
如果 header
和 skiprows
都指定了,则 header
将相对于 skiprows
的末尾。例如
In [71]: 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 [72]: 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 [73]: pd.read_csv(StringIO(data), comment="#", skiprows=4, header=1)
Out[73]:
A B C
0 1.0 2.0 4.0
1 5.0 NaN 10.0
6.2 注释
有时文件中可能包含注释或元数据:
In [74]: 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 [75]: df = pd.read_csv("tmp.csv")
In [76]: df
Out[76]:
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 [77]: df = pd.read_csv("tmp.csv", comment="#")
In [78]: df
Out[78]:
ID level category
0 Patient1 123000 x
1 Patient2 23000 y
2 Patient3 1234018 z
7 处理 Unicode 数据
encoding
参数应用于编码 unicode
数据,它将导致字节字符串在结果中需要 unicode
解码
In [79]: from io import BytesIO
In [80]: data = b"word,length\n" b"Tr\xc3\xa4umen,7\n" b"Gr\xc3\xbc\xc3\x9fe,5"
In [81]: data = data.decode("utf8").encode("latin-1")
In [82]: df = pd.read_csv(BytesIO(data), encoding="latin-1")
In [83]: df
Out[83]:
word length
0 Träumen 7
1 Grüße 5
In [84]: df["word"][1]
Out[84]: 'Grüße'
一些情况下必须指定正确的解码格式才能正确解析数据
8 索引列和末尾分隔符
如果一个文件的数据列比列名多一列,第一列将被用作 DataFrame
的行名
In [85]: data = "a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"
In [86]: pd.read_csv(StringIO(data))
Out[86]:
a b c
4 apple bat 5.7
8 orange cow 10.0
In [87]: data = "index,a,b,c\n4,apple,bat,5.7\n8,orange,cow,10"
In [88]: pd.read_csv(StringIO(data), index_col=0)
Out[88]:
a b c
index
4 apple bat 5.7
8 orange cow 10.0
通常,您可以使用 index_col
参数来实现此行为
当在每个数据行的末尾带有一个分隔符的文件时,会出现一些异常情况,让解析器感到头大。要显式禁用索引列推断并放弃最后一列,可以设置 index_col=False
In [89]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"
In [90]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,
In [91]: pd.read_csv(StringIO(data))
Out[91]:
a b c
4 apple bat NaN
8 orange cow NaN
In [92]: pd.read_csv(StringIO(data), index_col=False)
Out[92]:
a b c
0 4 apple bat
1 8 orange cow
如果使用 usecols
参数提取数据的子集,index_col
的作用将基于该子集,而不是原始数据
In [93]: data = "a,b,c\n4,apple,bat,\n8,orange,cow,"
In [94]: print(data)
a,b,c
4,apple,bat,
8,orange,cow,
In [95]: pd.read_csv(StringIO(data), usecols=["b", "c"])
Out[95]:
b c
4 bat NaN
8 cow NaN
In [96]: pd.read_csv(StringIO(data), usecols=["b", "c"], index_col=0)
Out[96]:
b c
4 bat NaN
8 cow NaN
注意:虽然使用了 usecols
参数,但是由于末尾的分隔符,导致数据的第一列作为索引而无法使数据正确对齐
9 日期处理
9.1 指定日期列
为了更好地处理 datetime
数据,read_csv()
提供了 parse_dates
和 date_parser
关键字参数来指定日期/时间格式,以便将输入文本数据转换为 datetime
对象
最简单的一种情况是只传递 parse_dates=True
# Use a column as an index, and parse it as dates.
In [97]: df = pd.read_csv("foo.csv", index_col=0, parse_dates=True)
In [98]: df
Out[98]:
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 [99]: df.index
Out[99]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', name='date', freq=None)
通常情况下,我们可能希望单独存储日期和时间数据,或者单独存储各种日期字段。
parse_dates
参数可用于指定要从中解析日期和/或时间的列组合
您可以为 parse_dates
指定一组列,组合后的日期列将被添加到输出结果的前面(为了不影响现有的列顺序),新的列名将是以 _
连接的列名
In [100]: print(open("tmp.csv").read())
KORD,19990127, 19:00:00, 18:56:00, 0.8100
KORD,19990127, 20:00:00, 19:56:00, 0.0100
KORD,19990127, 21:00:00, 20:56:00, -0.5900
KORD,19990127, 21:00:00, 21:18:00, -0.9900
KORD,19990127, 22:00:00, 21:56:00, -0.5900
KORD,19990127, 23:00:00, 22:56:00, -0.5900
In [101]: df = pd.read_csv("tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]])
In [102]: df
Out[102]:
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 [103]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=[[1, 2], [1, 3]], keep_date_col=True
.....: )
.....:
In [104]: df
Out[104]:
1_2 1_3 0 1 2 3 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 19990127 19:00:00 18:56:00 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 19990127 20:00:00 19:56:00 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD 19990127 21:00:00 20:56:00 -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD 19990127 21:00:00 21:18:00 -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD 19990127 22:00:00 21:56:00 -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD 19990127 23:00:00 22:56:00 -0.59
注意,如果您希望将多个列合并为一个日期列,则必须使用嵌套列表。
也就是说,parse_dates=[1,2]
表示第二和第三列应该分别解析为单独的日期列,而 parse_dates=[[1,2]]
表示这两列应该解析为一个单独的列
你也可以使用字典来自定义组合的名称列
In [105]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}
In [106]: df = pd.read_csv("tmp.csv", header=None, parse_dates=date_spec)
In [107]: df
Out[107]:
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 [108]: date_spec = {"nominal": [1, 2], "actual": [1, 3]}
In [109]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=date_spec, index_col=0
.....: ) # index is the nominal column
.....:
In [110]: df
Out[110]:
actual 0 4
nominal
1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
注意:
如果列或索引包含无法解析的日期,则整个列或索引将不变地作为 object
数据类型返回。
对于非标准日期时间解析,请在 pd.read_csv
之后使用 to_datetime()
解析
9.2 日期解析函数
解析器允许您指定一个自定义的 date_parser
函数,以充分利用日期解析 API
的灵活性
In [111]: df = pd.read_csv(
.....: "tmp.csv", header=None, parse_dates=date_spec, date_parser=pd.to_datetime
.....: )
.....:
In [112]: df
Out[112]:
nominal actual 0 4
0 1999-01-27 19:00:00 1999-01-27 18:56:00 KORD 0.81
1 1999-01-27 20:00:00 1999-01-27 19:56:00 KORD 0.01
2 1999-01-27 21:00:00 1999-01-27 20:56:00 KORD -0.59
3 1999-01-27 21:00:00 1999-01-27 21:18:00 KORD -0.99
4 1999-01-27 22:00:00 1999-01-27 21:56:00 KORD -0.59
5 1999-01-27 23:00:00 1999-01-27 22:56:00 KORD -0.59
pandas
将尝试以三种不同的方式调用 date_parser
函数。如果抛出异常,则尝试下一种方式
-
date_parser
首先使用一个或多个数组作为参数被调用,这些数组是使用parse_dates
定义的(例如,date_parser(['2013', '2013'], ['1', '2'])
) -
如果
1
失败了,则会调用date_parser
,并将所有列按行连接到单个数组中(例如,date_parser(['2013 1','2013 2'])
)
注意,在性能方面,你应该尝试按以下顺序来解析日期
- 尝试使用
infer_datetime_format=True
来推断格式 - 如果你知道格式,可以使用
pd.to_datetime()
:date_parser=lambda x: pd.to_datetime(x, format=...)
- 如果是非标准时间日期格式,请使用自定义
date_parser
函数
9.3 解析带有混合时区的 CSV
pandas
本身不能表示带有混合时区的列或索引。如果 CSV
文件包含混合时区的列,则默认结果将是一个字符串类型的列,即使使用了 parse_dates
也一样
In [113]: content = """\
.....: a
.....: 2000-01-01T00:00:00+05:00
.....: 2000-01-01T00:00:00+06:00"""
.....:
In [114]: df = pd.read_csv(StringIO(content), parse_dates=["a"])
In [115]: df["a"]
Out[115]:
0 2000-01-01 00:00:00+05:00
1 2000-01-01 00:00:00+06:00
Name: a, dtype: object
要将混合时区值解析为 datetime
类型,需要传递一个部分应用的 to_datetime()
,且设置参数 utc=True
作为 date_parser
In [116]: df = pd.read_csv(
.....: StringIO(content),
.....: parse_dates=["a"],
.....: date_parser=lambda col: pd.to_datetime(col, utc=True),
.....: )
.....:
In [117]: df["a"]
Out[117]:
0 1999-12-31 19:00:00+00:00
1 1999-12-31 18:00:00+00:00
Name: a, dtype: datetime64[ns, UTC]
9.4 推断 datetime 格式
如果您为部分或所有列启用了 parse_dates
,并且 datetime
字符串都是相同的格式,那么通过设置 infer_datetime_format=True
,可以大大提升解析速度。
如果无法猜测格式,或者猜测的格式不能正确解析整个字符串列,将会使用通用的解析方式
下面是一些可以猜测的 datetime
字符串的例子(都表示 2011
年 12
月 30
日 00:00:00
)
20111230
2011/12/30
20111230 00:00:00
12/30/2011 00:00:00
30/Dec/2011 00:00:00
30/December/2011 00:00:00
注意:infer_datetime_format
对 dayfirst
敏感。如果 dayfirst=True
,它就会把 01/12/2011
认为是 12
月 1
日。dayfirst=False
(默认值)将把 01/12/2011
猜测为 1
月 12
日
# Try to infer the format for the index column
In [118]: df = pd.read_csv(
.....: "foo.csv",
.....: index_col=0,
.....: parse_dates=True,
.....: infer_datetime_format=True,
.....: )
.....:
In [119]: df
Out[119]:
A B C
date
2009-01-01 a 1 2
2009-01-02 b 3 4
2009-01-03 c 4 5
9.5 国际日期格式
虽然美国的日期格式往往是 MM/DD/YYYY
,但许多国际格式使用 DD/MM/YYYY
代替。为方便起见,提供了 dayfirst
关键字
In [120]: print(open("tmp.csv").read())
date,value,cat
1/6/2000,5,a
2/6/2000,10,b
3/6/2000,15,c
In [121]: pd.read_csv("tmp.csv", parse_dates=[0])
Out[121]:
date value cat
0 2000-01-06 5 a
1 2000-02-06 10 b
2 2000-03-06 15 c
In [122]: pd.read_csv("tmp.csv", dayfirst=True, parse_dates=[0])
Out[122]:
date value cat
0 2000-06-01 5 a
1 2000-06-02 10 b
2 2000-06-03 15 c
9.6 将 CSV 写入二进制文件对象
df.to_csv(..., mode="wb")
允许将 CSV
写入打开的二进制模式的文件对象。
在大多数情况下,无需指定模式,因为 Pandas
会自动检测文件对象是以文本模式还是二进制模式打开的
In [123]: import io
In [124]: data = pd.DataFrame([0, 1, 2])
In [125]: buffer = io.BytesIO()
In [126]: data.to_csv(buffer, encoding="utf-8", compression="gzip")
10 指定浮点数转换方法
可以在 C
引擎解析期间使用 float_precision
参数来指定浮点数转换器
该参数有三个可选的值:
None
: 普通转换器high
: 高精度转换器round_trip
: 保证文件读写之后小数点精度不变
In [127]: val = "0.3066101993807095471566981359501369297504425048828125"
In [128]: data = "a,b,c\n1,2,{0}".format(val)
In [129]: abs(
.....: pd.read_csv(
.....: StringIO(data),
.....: engine="c",
.....: float_precision=None,
.....: )["c"][0] - float(val)
.....: )
.....:
Out[129]: 5.551115123125783e-17
In [130]: abs(
.....: pd.read_csv(
.....: StringIO(data),
.....: engine="c",
.....: float_precision="high",
.....: )["c"][0] - float(val)
.....: )
.....:
Out[130]: 5.551115123125783e-17
In [131]: abs(
.....: pd.read_csv(StringIO(data), engine="c", float_precision="round_trip")["c"][0]
.....: - float(val)
.....: )
.....:
Out[131]: 0.0
11 千位分隔符
对于使用千位分隔符编写的大数,可以将千位关键字设置为长度为 1
的字符串,以便正确解析整数
而在默认情况下,带有千位分隔符的数字将被解析为字符串
In [132]: print(open("tmp.csv").read())
ID|level|category
Patient1|123,000|x
Patient2|23,000|y
Patient3|1,234,018|z
In [133]: df = pd.read_csv("tmp.csv", sep="|")
In [134]: df
Out[134]:
ID level category
0 Patient1 123,000 x
1 Patient2 23,000 y
2 Patient3 1,234,018 z
In [135]: df.level.dtype
Out[135]: dtype('O')
可以设置 thousands
参数来解析
In [136]: print(open("tmp.csv").read())
ID|level|category
Patient1|123,000|x
Patient2|23,000|y
Patient3|1,234,018|z
In [137]: df = pd.read_csv("tmp.csv", sep="|", thousands=",")
In [138]: df
Out[138]:
ID level category
0 Patient1 123000 x
1 Patient2 23000 y
2 Patient3 1234018 z
In [139]: df.level.dtype
Out[139]: dtype('int64')
12 NA 值
要控制哪些值被解析为缺失值(用 NaN
表示),请在 na_values
中指定一个字符串。
如果指定了一个字符串列表,那么其中的所有值都被认为是缺失值
如果您指定一个数字(如浮点数 5.0
或整数 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', '']
考虑下面额例子
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
13 无限值
inf
类似的值将被解析为 np.inf
(正无穷)和 -inf
为 -np.inf
(负无穷)。
解析会忽略值的大小写,即 Inf
,也将被解析为 np.inf
14 返回 Series
使用 squeeze
关键字参数,将返回单个列的 Series
形式输出:
In [140]: print(open("tmp.csv").read())
level
Patient1,123000
Patient2,23000
Patient3,1234018
In [141]: output = pd.read_csv("tmp.csv", squeeze=True)
In [142]: output
Out[142]:
Patient1 123000
Patient2 23000
Patient3 1234018
Name: level, dtype: int64
In [143]: type(output)
Out[143]: pandas.core.series.Series
15 布尔值
常见的值 True
和 False
都被认为是布尔值。有时您可能想要识别其他值为布尔值。
为此,可以使用 true_values
和 false_values
参数,如下所示:
In [144]: data = "a,b,c\n1,Yes,2\n3,No,4"
In [145]: print(data)
a,b,c
1,Yes,2
3,No,4
In [146]: pd.read_csv(StringIO(data))
Out[146]:
a b c
0 1 Yes 2
1 3 No 4
In [147]: pd.read_csv(StringIO(data), true_values=["Yes"], false_values=["No"])
Out[147]:
a b c
0 1 True 2
1 3 False 4
16 处理错误的行
某些文件的行格式存在错误,字段太少或太多。字段太少的行将在尾随字段中填充 NA
值。默认情况下,包含太多字段的行将引发错误
In [148]: data = "a,b,c\n1,2,3\n4,5,6,7\n8,9,10"
In [149]: pd.read_csv(StringIO(data))
---------------------------------------------------------------------------
ParserError Traceback (most recent call last)
<ipython-input-5-6388c394e6b8> in <module>
----> 1 pd.read_csv(StringIO(data))
~/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, dialect, error_bad_lines, warn_bad_lines, delim_whitespace, low_memory, memory_map, float_precision)
684 )
685
--> 686 return _read(filepath_or_buffer, kwds)
687
688
~/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py in _read(filepath_or_buffer, kwds)
456
457 try:
--> 458 data = parser.read(nrows)
459 finally:
460 parser.close()
~/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py in read(self, nrows)
1194 def read(self, nrows=None):
1195 nrows = _validate_integer("nrows", nrows)
-> 1196 ret = self._engine.read(nrows)
1197
1198 # May alter columns / col_dict
~/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py in read(self, nrows)
2153 def read(self, nrows=None):
2154 try:
-> 2155 data = self._reader.read(nrows)
2156 except StopIteration:
2157 if self._first_chunk:
pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader.read()
pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._read_low_memory()
pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._read_rows()
pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._tokenize_rows()
pandas/_libs/parsers.pyx in pandas._libs.parsers.raise_parser_error()
ParserError: Error tokenizing data. C error: Expected 3 fields in line 3, saw 4
你可以选择跳过错误的行
In [29]: pd.read_csv(StringIO(data), error_bad_lines=False)
b'Skipping line 3: expected 3 fields, saw 4\n'
Out[29]:
a b c
0 1 2 3
1 8 9 10
你也可以使用 usecols
参数来消除某些行中出现的多余的列数据
In [30]: pd.read_csv(StringIO(data), usecols=[0, 1, 2])
Out[30]:
a b c
0 1 2 3
1 4 5 6
2 8 9 10
17 dialect
dialect
参数在读取特定格式的文件时提供了更大的灵活性,默认情况下,它使用 Excel
方言,但您可以指定方言名称或 csv.Dialect
实例
假设您的数据带有未封闭的引号
In [150]: print(data)
label1,label2,label3
index1,"a,c,e
index2,b,d,f
默认情况下,read_csv
使用 Excel
方言,并将双引号视为引号字符,这会导致在换行符之前无法找到配对的双引号。
我们可以用 dialect
来解决这个问题:
In [151]: import csv
In [152]: dia = csv.excel()
In [153]: dia.quoting = csv.QUOTE_NONE
In [154]: pd.read_csv(StringIO(data), dialect=dia)
Out[154]:
label1 label2 label3
index1 "a c e
index2 b d f
可以通过关键字参数分别指定所有方言选项
In [155]: data = "a,b,c~1,2,3~4,5,6"
In [156]: pd.read_csv(StringIO(data), lineterminator="~")
Out[156]:
a b c
0 1 2 3
1 4 5 6
另一个常见的方言选项是 skipinitialspace
,跳过分隔符后的任何空白字符
In [157]: data = "a, b, c\n1, 2, 3\n4, 5, 6"
In [158]: print(data)
a, b, c
1, 2, 3
4, 5, 6
In [159]: pd.read_csv(StringIO(data), skipinitialspace=True)
Out[159]:
a b c
0 1 2 3
1 4 5 6
解析器会尽一切努力做正确的事情,类型推断非常重要
18 引号和转义符
嵌入字段中的引号(和其他转义字符)可以通过多种方式处理。
一种方法是使用反斜杠。要正确解析此数据,您应该传递 escapechar
选项:
In [160]: data = 'a,b\n"hello, \\"Bob\\", nice to see you",5'
In [161]: print(data)
a,b
"hello, \"Bob\", nice to see you",5
In [162]: pd.read_csv(StringIO(data), escapechar="\\")
Out[162]:
a b
0 hello, "Bob", nice to see you 5
19 固定列宽文件
read_fwf()
函数可用于读取具有固定列宽的数据文件
read_fwf
的函数参数与 read_csv
的函数参数基本相同,只是有两个额外的参数,而且 delimiter
参数的用法也不同:
colspecs
:一个元组列表,给出每行固定宽度字段的范围,半开区间。默认设置为infer
可以让解析器自动从数据的前100
行中推断出格式。widths
:一个字段宽度列表,如果间隔是连续的,可以用它来代替colspecs
delimiter
:在固定宽度的文件中作为填充字符。如~
考虑如下固定宽度文件
In [163]: print(open("bar.csv").read())
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
为了将此文件解析为 DataFrame
,我们只需要将列与文件名一起提供给 read_fwf
函数
# Column specifications are a list of half-intervals
In [164]: colspecs = [(0, 6), (8, 20), (21, 33), (34, 43)]
In [165]: df = pd.read_fwf("bar.csv", colspecs=colspecs, header=None, index_col=0)
In [166]: df
Out[166]:
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
另外,您可以仅提供连续列的列宽:
# Widths are a list of integers
In [167]: widths = [6, 14, 13, 10]
In [168]: df = pd.read_fwf("bar.csv", widths=widths, header=None)
In [169]: df
Out[169]:
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
行来推断文件的 colspec
。
仅当文件的列能够通过提供的定界符对齐并正确分隔时,才可以这样做
In [170]: df = pd.read_fwf("bar.csv", header=None, index_col=0)
In [171]: df
Out[171]:
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 [172]: pd.read_fwf("bar.csv", header=None, index_col=0).dtypes
Out[172]:
1 float64
2 float64
3 float64
dtype: object
In [173]: pd.read_fwf("bar.csv", header=None, dtype={2: "object"}).dtypes
Out[173]:
0 object
1 float64
2 object
3 float64
dtype: object
20 索引
20.1 文件隐式索引列
考虑下面这个文件,表头的数量比数据列的数量少一个
In [174]: print(open("foo.csv").read())
A,B,C
20090101,a,1,2
20090102,b,3,4
20090103,c,4,5
在这种情况下,read_csv
假定第一列数据将作为 DataFrame
的索引
In [175]: pd.read_csv("foo.csv")
Out[175]:
A B C
20090101 a 1 2
20090102 b 3 4
20090103 c 4 5
注意: 在这种情况下,日期不会自动解析,你需要像以前一样指定参数
In [176]: df = pd.read_csv("foo.csv", parse_dates=True)
In [177]: df.index
Out[177]: DatetimeIndex(['2009-01-01', '2009-01-02', '2009-01-03'], dtype='datetime64[ns]', freq=None)
20.2 多级索引
假设您有两列索引的数据
In [178]: print(open("data/mindex_ex.csv").read())
year,indiv,zit,xit
1977,"A",1.2,.6
1977,"B",1.5,.5
1977,"C",1.7,.8
1978,"A",.2,.06
1978,"B",.7,.2
1978,"C",.8,.3
1978,"D",.9,.5
1978,"E",1.4,.9
1979,"C",.2,.15
1979,"D",.14,.05
1979,"E",.5,.15
1979,"F",1.2,.5
1979,"G",3.4,1.9
1979,"H",5.4,2.7
1979,"I",6.4,1.2
可以使用 index_col
参数传递一个列索引列表,以将多个列组合为多重索引
In [179]: df = pd.read_csv("data/mindex_ex.csv", index_col=[0, 1])
In [180]: df
Out[180]:
zit xit
year indiv
1977 A 1.20 0.60
B 1.50 0.50
C 1.70 0.80
1978 A 0.20 0.06
B 0.70 0.20
C 0.80 0.30
D 0.90 0.50
E 1.40 0.90
1979 C 0.20 0.15
D 0.14 0.05
E 0.50 0.15
F 1.20 0.50
G 3.40 1.90
H 5.40 2.70
I 6.40 1.20
In [181]: df.loc[1978]
Out[181]:
zit xit
indiv
A 0.2 0.06
B 0.7 0.20
C 0.8 0.30
D 0.9 0.50
E 1.4 0.90
20.3 列多级索引
通过为 header
参数指定行号列表,您可以将列表中读取的行作为列的多级索引。
如果指定了非连续的行,将会跳过中间的行
In [182]: from pandas._testing import makeCustomDataframe as mkdf
In [183]: df = mkdf(5, 3, r_idx_nlevels=2, c_idx_nlevels=4)
In [184]: df.to_csv("mi.csv")
In [185]: print(open("mi.csv").read())
C0,,C_l0_g0,C_l0_g1,C_l0_g2
C1,,C_l1_g0,C_l1_g1,C_l1_g2
C2,,C_l2_g0,C_l2_g1,C_l2_g2
C3,,C_l3_g0,C_l3_g1,C_l3_g2
R0,R1,,,
R_l0_g0,R_l1_g0,R0C0,R0C1,R0C2
R_l0_g1,R_l1_g1,R1C0,R1C1,R1C2
R_l0_g2,R_l1_g2,R2C0,R2C1,R2C2
R_l0_g3,R_l1_g3,R3C0,R3C1,R3C2
R_l0_g4,R_l1_g4,R4C0,R4C1,R4C2
In [186]: pd.read_csv("mi.csv", header=[0, 1, 2, 3], index_col=[0, 1])
Out[186]:
C0 C_l0_g0 C_l0_g1 C_l0_g2
C1 C_l1_g0 C_l1_g1 C_l1_g2
C2 C_l2_g0 C_l2_g1 C_l2_g2
C3 C_l3_g0 C_l3_g1 C_l3_g2
R0 R1
R_l0_g0 R_l1_g0 R0C0 R0C1 R0C2
R_l0_g1 R_l1_g1 R1C0 R1C1 R1C2
R_l0_g2 R_l1_g2 R2C0 R2C1 R2C2
R_l0_g3 R_l1_g3 R3C0 R3C1 R3C2
R_l0_g4 R_l1_g4 R4C0 R4C1 R4C2
read_csv
还能够解析一种更常见的多列索引格式
,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 [188]: pd.read_csv("mi2.csv", header=[0, 1], index_col=0)
Out[188]:
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
(例如,数据没有索引),那么列索引上的列名都会丢失
21 自动嗅探分隔符
read_csv
能够推断出文件的分隔符,因为 pandas 使用了 csv
模块的 csv.Sniffer
类。为此,你必须指定 sep=None
。
In [189]: print(open("tmp2.sv").read())
:0:1:2:3
0:0.4691122999071863:-0.2828633443286633:-1.5090585031735124:-1.1356323710171934
1:1.2121120250208506:-0.17321464905330858:0.11920871129693428:-1.0442359662799567
2:-0.8618489633477999:-2.1045692188948086:-0.4949292740687813:1.071803807037338
3:0.7215551622443669:-0.7067711336300845:-1.0395749851146963:0.27185988554282986
4:-0.42497232978883753:0.567020349793672:0.27623201927771873:-1.0874006912859915
5:-0.6736897080883706:0.1136484096888855:-1.4784265524372235:0.5249876671147047
6:0.4047052186802365:0.5770459859204836:-1.7150020161146375:-1.0392684835147725
7:-0.3706468582364464:-1.1578922506419993:-1.344311812731667:0.8448851414248841
8:1.0757697837155533:-0.10904997528022223:1.6435630703622064:-1.4693879595399115
9:0.35702056413309086:-0.6746001037299882:-1.776903716971867:-0.9689138124473498
In [190]: pd.read_csv("tmp2.sv", sep=None, engine="python")
Out[190]:
Unnamed: 0 0 1 2 3
0 0 0.469112 -0.282863 -1.509059 -1.135632
1 1 1.212112 -0.173215 0.119209 -1.044236
2 2 -0.861849 -2.104569 -0.494929 1.071804
3 3 0.721555 -0.706771 -1.039575 0.271860
4 4 -0.424972 0.567020 0.276232 -1.087401
5 5 -0.673690 0.113648 -1.478427 0.524988
6 6 0.404705 0.577046 -1.715002 -1.039268
7 7 -0.370647 -1.157892 -1.344312 0.844885
8 8 1.075770 -0.109050 1.643563 -1.469388
9 9 0.357021 -0.674600 -1.776904 -0.968914
22 逐块地遍历文件
假设你希望惰性地遍历一个(可能非常大的)文件,而不是一次性将整个文件读入内存
例如,有如下文件
In [191]: print(open("tmp.sv").read())
|0|1|2|3
0|0.4691122999071863|-0.2828633443286633|-1.5090585031735124|-1.1356323710171934
1|1.2121120250208506|-0.17321464905330858|0.11920871129693428|-1.0442359662799567
2|-0.8618489633477999|-2.1045692188948086|-0.4949292740687813|1.071803807037338
3|0.7215551622443669|-0.7067711336300845|-1.0395749851146963|0.27185988554282986
4|-0.42497232978883753|0.567020349793672|0.27623201927771873|-1.0874006912859915
5|-0.6736897080883706|0.1136484096888855|-1.4784265524372235|0.5249876671147047
6|0.4047052186802365|0.5770459859204836|-1.7150020161146375|-1.0392684835147725
7|-0.3706468582364464|-1.1578922506419993|-1.344311812731667|0.8448851414248841
8|1.0757697837155533|-0.10904997528022223|1.6435630703622064|-1.4693879595399115
9|0.35702056413309086|-0.6746001037299882|-1.776903716971867|-0.9689138124473498
In [192]: table = pd.read_csv("tmp.sv", sep="|")
In [193]: table
Out[193]:
Unnamed: 0 0 1 2 3
0 0 0.469112 -0.282863 -1.509059 -1.135632
1 1 1.212112 -0.173215 0.119209 -1.044236
2 2 -0.861849 -2.104569 -0.494929 1.071804
3 3 0.721555 -0.706771 -1.039575 0.271860
4 4 -0.424972 0.567020 0.276232 -1.087401
5 5 -0.673690 0.113648 -1.478427 0.524988
6 6 0.404705 0.577046 -1.715002 -1.039268
7 7 -0.370647 -1.157892 -1.344312 0.844885
8 8 1.075770 -0.109050 1.643563 -1.469388
9 9 0.357021 -0.674600 -1.776904 -0.968914
通过为 read_csv
指定 chunksize
,将会返回 TextFileReader
类型的可迭代对象:
In [194]: with pd.read_csv("tmp.sv", sep="|", chunksize=4) as reader:
.....: reader
.....: for chunk in reader:
.....: print(chunk)
.....:
Unnamed: 0 0 1 2 3
0 0 0.469112 -0.282863 -1.509059 -1.135632
1 1 1.212112 -0.173215 0.119209 -1.044236
2 2 -0.861849 -2.104569 -0.494929 1.071804
3 3 0.721555 -0.706771 -1.039575 0.271860
Unnamed: 0 0 1 2 3
4 4 -0.424972 0.567020 0.276232 -1.087401
5 5 -0.673690 0.113648 -1.478427 0.524988
6 6 0.404705 0.577046 -1.715002 -1.039268
7 7 -0.370647 -1.157892 -1.344312 0.844885
Unnamed: 0 0 1 2 3
8 8 1.075770 -0.10905 1.643563 -1.469388
9 9 0.357021 -0.67460 -1.776904 -0.968914
指定 iterator=True
可以返回 TextFileReader
对象
In [195]: with pd.read_csv("tmp.sv", sep="|", iterator=True) as reader:
.....: reader.get_chunk(5)
23 指定解析引擎
pandas
有两个解析器:
- 用
C
语言实现的快速高效的解析器 - 用
Python
实现的功能更加完善的解析器
pandas
尽可能使用 C
解析器,但是如果指定了 C
不支持的选项,将会使用 Python
解析器。
C
不支持的选项包括:
sep
:除了单字符之外,如,正则表达式skipfooter
:sep=Non
e 且delim_whitespace=False
除非使用 engine ='python'
明确选择 python
引擎,否则指定以上任何选项都将产生 ParserWarning
24 远程文件读写
您可以传入一个 URL
来读取或写入远程文件,以下示例显示读取 CSV
文件:
df = pd.read_csv("https://download.bls.gov/pub/time.series/cu/cu.item", sep="\t")
25 数据写出
25.1 写出 CSV 格式
Series
和 DataFrame
对象都有 to_csv
方法,该方法允许将对象的内容存储为逗号分隔文件。
该函数带有多个参数。只有第一个是必须的
path_or_buf
: 要写入文件的路径或文件对象,如果是文件对象,必须使用newline=''
sep
: 输出文件的字段分隔符 (默认为,
)na_rep
: 缺失值的字符串表示形式(默认为''
)float_format
: 浮点数的格式化字符串columns
: 要写入的列 (默认为None
)header
: 是否写出列名 (默认True
)index
: 是否写出索引名 (默认True
)index_label
: 索引列的列名,默认为None
,并且header
和index
为True
,则使用索引名称。如果DataFrame
使用MultiIndex
,则应给出一个序列mode
:Python
写入模式,默认'w'
encoding
: 字符串编码格式line_terminator
: 表示行尾的字符序列(默认os.linesep
)quoting
: 在csv
模块中设置引用规则(默认为csv.QUOTE_MINIMAL
)。注意:如果您设置了float_format
,那么浮点数将被转换为字符串,csv.QUOTE_NONNUMERIC
将它们视为非数字quotechar
: 用于引用字段的字符(默认"
)doublequote
: 控制字段中quotechar
的引用 (默认True
)escapechar
: 在适当时用于转义sep
和quotechar
的字符(默认None
)chunksize
: 一次写入的行数date_format
:datetime
对象的格式化字符串
25.2 写出格式化字符串
DataFrame
对象具有 to_string
实例方法,该方法允许控制对象的字符串表示形式。所有参数都是可选的
buf
: 默认为None
,例如一个StringIO
对象columns
: 默认为None
, 要写入的列col_space
: 默认为None
,每列的最小宽度。na_rep
: 默认为NaN
,NA
值的表示formatters
: 默认为None
, 一个函数的字典(按列),每个函数接受一个参数并返回一个格式化的字符串。float_format
: 默认为None
, 一个带有单个(浮点)参数并返回格式化字符串的函数;应用于DataFrame
中的浮点数sparsify
: 默认为True
, 对于具有层次结构索引的DataFrame
,将其设置为False
可在每一行打印每个MultiIndex
键index_names
: 默认为True
, 将打印索引名index
: 默认为True
, 打印索引header
: 默认为True
, 打印列名justify
: 默认left
, 列左对齐或右对齐
Series
对象也有一个 to_string
方法,但是只有 buf
、na_rep
、float_format
参数。
还有一个 length
参数,如果设置为 True
,将额外输出序列的长度