Python 数据处理(十四)—— IO 工具 CSV 示例
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
参数,但是由于末尾的分隔符,导致数据的第一列作为索引而无法使数据正确对齐