PythonPython数据分析

Python 数据处理(十四)—— IO 工具 CSV 示例

2021-02-15  本文已影响0人  名本无名

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

在这个例子中,我们排除了 ac

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

如果 headerskiprows 都指定了,则 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 参数,但是由于末尾的分隔符,导致数据的第一列作为索引而无法使数据正确对齐

上一篇下一篇

猜你喜欢

热点阅读