• 详解pandas的read_csv方法


    楔子

    基于 Python 和 NumPy 开发的 Pandas,在数据分析领域,应用非常广泛。而使用 Pandas 处理数据的第一步往往就是读入数据,比如读写 CSV 文件,而Pandas也提供了强劲的读取支持,参数有 38 个之多。这些参数中,有的容易被忽略,但却在实际工作中用处很大。比如:

    • 文件读取时设置某些列为时间类型
    • 导入文件,含有重复列
    • 过滤某些列
    • 每次迭代读取 10 行

    而pandas读取csv文件时通过read_csv函数读取的,下面我们就来好好看看这个函数生得一副什么模样,都有哪些参数。

    read_csv中的参数

    以下都是read_csv中的参数,但是根据功能我们划分为不同的类别。

    基本参数

    filepath_or_buffer

    数据输入路径,可以是文件路径,也可以是 URL,或者实现 read 方法的任意对象。就是我们输入的第一个参数。

    In [2]: pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data')
    Out[2]:
         5.1  3.5  1.4  0.2     Iris-setosa
    0    4.9  3.0  1.4  0.2     Iris-setosa
    1    4.7  3.2  1.3  0.2     Iris-setosa
    2    4.6  3.1  1.5  0.2     Iris-setosa
    3    5.0  3.6  1.4  0.2     Iris-setosa
    4    5.4  3.9  1.7  0.4     Iris-setosa
    ..   ...  ...  ...  ...             ...
    144  6.7  3.0  5.2  2.3  Iris-virginica
    145  6.3  2.5  5.0  1.9  Iris-virginica
    146  6.5  3.0  5.2  2.0  Iris-virginica
    147  6.2  3.4  5.4  2.3  Iris-virginica
    148  5.9  3.0  5.1  1.8  Iris-virginica
    
    [149 rows x 5 columns]
    

    sep

    数据文件的分隔符,默认为逗号。

    举例:下面的girl.csv 文件的分割符为 ,如果使用 sep 默认的逗号分隔符,读入后的数据混为一体。

    In [4]: df = pd.read_csv('girl.csv')
    In [5]: df
    Out[5]:
     	  name	age	gender
    0     椎名真白	18	女
    1     古明地觉	17	女
    2     古明地恋	16	女
    # 分隔符指定错误,所以多个列的数据连在一块了
    

    sep 必须设置为 ' ',数据分割才会正常。

    In [1]: df = pd.read_csv('girl.csv', sep='	')
    In [2]: df
    Out[2]:
       name  	age 	gender
    0  椎名真白   18      女
    1  古明地觉   17      女
    2  古明地恋   16      女
    

    delimiter

    分隔符的另一个名字,与 sep 功能相似。

    delimiter_whitespace

    0.18 版本后新加参数,默认为 False,设置为 True 时,表示分割符为空白字符,可以是空格、 等。

    如下 girl.csv 文件分隔符为 ,设置 delim_whitespace 为 True:

    In [4]: df = pd.read_csv('girl.csv',delim_whitespace=True)
    
    In [5]: df
    Out[5]:
       name  	age 	gender
    0  椎名真白   18      女
    1  古明地觉   17      女
    2  古明地恋   16      女
    # 不管分隔符是什么,只要是空白字符,那么可以通过delim_whitespace=True进行读取
    

    header

    设置导入 DataFrame 的列名称,默认为 'infer',注意它与下面介绍的 names 参数的微妙关系。

    names

    当names没被赋值时,header会变成0,即选取数据文件的第一行作为列名。

    当 names 被赋值,header 没被赋值时,那么header会变成None。如果都赋值,就会实现两个参数的组合功能。

    我们举例说明

    • names 没有被赋值,header 也没赋值:

      In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True)
      
      In [2]: df
      Out[2]:
         name  	age 	gender
      0  椎名真白   18      女
      1  古明地觉   17      女
      2  古明地恋   16      女
      # 我们说这种情况下,header为变成0,即选取文件的第一行作为表头
      
    • names 没有赋值,header 被赋值

      In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, header=1)
      
      In [2]: df
      Out[2]:
         椎名真白  18  女
      0  古明地觉  17  女
      1  古明地恋  16  女
      # 不指定names,指定header为1,则选取第二行当做表头,第二行下面的是数据
      
    • names 被赋值,header 没有被赋值

      In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, names=["姓名", "年龄", "性别"])
      
      In [2]: df
      Out[2]:
           姓名    年龄      性别
      0  name  	 age  	gender
      1  椎名真白   18       女
      2  古明地觉   17       女
      3  古明地恋   16       女    
      # 我们看到names适用于没有表头的情况
      # 指定names没有指定header,那么header相当于None
      # 一般来说,读取文件会有一个表头的,一般是第一行,但是有的文件只是数据而没有表头
      # 那么这个时候我们就可以通过names手动指定、或者生成表头,而文件里面的数据则全部是内容
      # 所以这里那么name、age、gender也当成是一条记录了,本来它是表头的,但是我们指定了names,所以它就变成数据了,表头是我们在names里面指定的
      
    • names和header都被赋值

      In [1]: df = pd.read_csv('girl.csv', 
                               delim_whitespace=True, 
                               names=["姓名", "年龄", "性别"],
                               header=0)
      
      In [2]: df
      Out[2]:
           姓名  年龄 性别
      0  椎名真白  18  女
      1  古明地觉  17  女
      2  古明地恋  16  女
      # 这个相当于先不看names,只看header,我们说header等于0代表什么呢?显然是把第一行当做表头,下面的当成数据
      # 好了,然后再把表头用names给替换掉
      
      # 再来看个栗子
      In [1]: df = pd.read_csv('girl.csv', 
                               delim_whitespace=True, 
                               names=["姓名", "年龄", "性别"],
                               header=1)
      
      In [2]: df
      Out[2]:
           姓名  年龄 性别
      0  古明地觉  17  女
      1  古明地恋  16  女
      # header=1,表示第二行当做表头,第二行下面当成数据
      # 然后再把表头用names给替换掉,就是上面的结果
      # 所以一般情况下,有表头并且是第一行,那么names和header都无需指定
      # 但是有表头、而表头不是第一行,可能从下面几行开始才是真正的表头和数据,这个是指定header即可
      # 如果没有表头,全部是纯数据,那么我们可以通过names手动生成表头
      # 而names和header都指定的情况下,一般是有表头但是这个表头你不想用,所以用names来把原来的表头替换掉,其实就等价于读取之后再对列名进行rename
      

    index_col

    我们在读取文件之后,生成的索引默认是0 1 2 3...,我们当然可以set_index,但是也可以在读取的时候就指定某个列为索引

    In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, index_col="name")
    
    In [2]: df
    Out[2]:
          age gender
    name            
    椎名真白   18      女
    古明地觉   17      女
    古明地恋   16      女
    # 这里指定"name"作为索引,另外除了指定单个列,还可以指定多个列,比如["name", "age"]
    # 并且我们可以输入列的名字,也可以输入对应的索引。比如:name, age, gender,它们对应的索引就是0, 1, 2
    

    usecols

    如果列有很多,而我们不想要全部的列、而是只要指定的列就可以使用这个参数。

    In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, usecols=["name", "age"])
    
    In [2]: df
    Out[2]:
       name  age
    0  椎名真白   18
    1  古明地觉   17
    2  古明地恋   16
    # 这里只要name和age两列
    

    mangle_dupe_cols

    实际生产用的数据会很复杂,有时导入的数据会含有重名的列。参数 mangle_dupe_cols 默认为 True,重名的列导入后面多一个 .1。如果设置为 False,会抛出不支持的异常:

    # ValueError: Setting mangle_dupe_cols=False is not supported yet
    

    prefix

    prefix 参数,当导入的数据没有 header 时,设置此参数会自动加一个前缀。比如:

    In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, header=None)
    
    In [2]: df
    Out[2]:
          0    	1      	2
    0  name  	age  	gender
    1  椎名真白   18       女
    2  古明地觉   17       女
    3  古明地恋   16       女
    # 我们看到在不指定names的时候,header默认为0,表示以第一行为表头
    # 但如果不指定names、还显式地将header指定为None,那么会自动生成表头0 1 2 3...
    # 因为DataFrame肯定是要有列名(表头)的
    
    In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, header=None, prefix="xx")
    
    In [2]: df
    Out[2]:
          xx0  xx1     xx2
    0  name  	age  	gender
    1  椎名真白   18       女
    2  古明地觉   17       女
    3  古明地恋   16       女
    # 而prefix就是给这样的列名增加前缀的
    # 但感觉不是很常用
    

    通用解析参数

    dtype

    笔者就曾遇到一件比较尴尬的事情,就是处理地铁人员数据的。工作人员的id都是以0开头的,比如0100012521,这是一个字符串。但是在读取的时候解析成整型了,结果把开头的0给丢了。这个时候我们就可以通过dtype来指定某个列的类型,就是告诉pandas你在解析的时候按照我指定的类型进行解析。

    In [1]: df = pd.read_csv('girl.csv', delim_whitespace=True, dtype={"age": str})
    
    In [2]: df
    Out[2]:
       name age gender
    0  椎名真白  18      女
    1  古明地觉  17      女
    2  古明地恋  16      女
    # 这里就表示要把age解析成字符串
    

    engine

    pandas解析数据时用的引擎,Pandas 目前的解析引擎提供两种:C、Python,默认为 C,因为 C 引擎解析速度更快,但是特性没有 Python 引擎全。如果使用 C 引擎没有的特性时,会自动退化为 Python 引擎。

    比如使用分隔符进行解析,如果指定分隔符不是单个字符、或者"s+",那么C引擎就无法解析了。我们知道如果分隔符为空白字符的话,那么可以指定delim_whitespace=True,但是也可以指定sep=r"s+"。

    In [1]: df = pd.read_csv('girl.csv', sep=r"s+")
    
    In [2]: df
    Out[2]:
       name age gender
    0  椎名真白  18      女
    1  古明地觉  17      女
    2  古明地恋  16      女
    # 如果sep是单个字符,或者字符串s+,那么C是可以解决的。
    # 但如果我们指定的sep比较复杂,这时候引擎就会退化。
    
    # 我们指定的s{0}相当于没指定,s+s{0}在结果上等同于s+。
    # 但是它不是单个字符,也不是s+,因此此时的C引擎就无法解决了,而是会退化为python引擎
    In [1]: df = pd.read_csv('girl.csv', sep=r"s+s{0}", encoding="utf-8")
    
    In [2]: df
    Out[2]:
    ParserWarning: Falling back to the 'python' engine 
        because the 'c' engine does not support regex separators (separators > 1 char and different from 's+' are interpreted as regex); 
    	you can avoid this warning by specifying engine='python'.
        
       name  age gender
    0  椎名真白   18      女
    1  古明地觉   17      女
    2  古明地恋   16      女
    # 我们看到虽然自动退化,但是弹出了警告,这个时候需要手动的指定engine="python"来避免警告
    # 这里面还用到了encoding参数,这个后面会说,因为引擎一旦退化,在Windows上不指定会读出乱码
    # 这里我们看到sep是可以支持正则的,但是说实话sep这个参数都会设置成单个字符
    # 基本上在生成文件的时候,分隔符不会复杂到读取的时候需要使用正则来分隔的。
    

    converters

    可以在读取的时候对列数据进行变换

    In [1]: df = pd.read_csv('girl.csv', sep="	", converters={"age": lambda x: int(x) + 10})
    
    In [2]: df
    Out[2]:
       name  age gender
    0  椎名真白   28      女
    1  古明地觉   27      女
    2  古明地恋   26      女    
    

    看到少女们都长了10岁,完成对 age 列的数据加 10,注意 int(x),此处解析器默认所有列的类型为 str,所以需要显式类型转换。

    true_values和false_value

    指定哪些值应该被清洗为True,哪些值被清洗为False。这两位老铁需要成对出现,只出现一个没有效果。但是说实话,这个不常用

    In [1]: df = pd.read_csv('girl.csv', sep="	")
    
    In [2]: df
    Out[2]:
       name  	age   gender 对错
    0  椎名真白   18      女  对
    1  古明地觉   17      女  错
    2  古明地恋   16      女  对    
    # 加了一个字段
    
    In [1]: df = pd.read_csv('girl.csv', sep="	", true_values=["对"], false_values=["错"])
    
    In [2]: df
    Out[2]:
       name  	age   gender  对错
    0  椎名真白   18      女   True
    1  古明地觉   17      女  False
    2  古明地恋   16      女   True    
    

    skiprows

    skiprows 过滤行,想过滤掉哪些行,就写在一个列表里面传递给skiprows即可。注意的是:这里是先过滤,然后再确定表头,比如:

    In [1]: df = pd.read_csv('girl.csv', sep="	", skiprows=[0])
    
    In [2]: df
    Out[2]:
       椎名真白  18  女  对
    0  古明地觉  17  女  错
    1  古明地恋  16  女  对
    # 我们把第一行过滤掉了,但是第一行是表头
    # 所以过滤掉之后,第二行就变成表头了
    
    # 这里过滤第二行
    In [1]: df = pd.read_csv('girl.csv', sep="	", skiprows=[1])
    
    In [2]: df
    Out[2]:
       name  	age   gender 对错
    0  古明地觉   17      女  错
    1  古明地恋   16      女  对    
    

    里面除了传入具体的数值,来表明要过滤掉哪一行,还可以传入一个函数

    In [1]: df = pd.read_csv('girl.csv', sep="	", skiprows=lambda x: x > 0 and x % 2 == 0)
    
    In [2]: df
    Out[2]:
       name  	age   gender 对错
    0  椎名真白   18      女  对
    1  古明地恋   16      女  对  
    # 由于索引从0开始,凡是索引大于0、并且%2等于0的记录都过滤掉
    # 索引大于0,是为了保证表头不被过滤掉 
    

    skipfooter

    从文件末尾过滤行,解析引擎退化为 Python。这是因为 C 解析引擎没有这个特性。

    In [1]: df = pd.read_csv('girl.csv', sep="	", skipfooter=1, encoding="utf-8", engine="python")
    
    In [2]: df
    Out[2]:
       name  	age   gender 对错
    0  椎名真白   18      女  对
    1  古明地觉   17      女  错   
    # skipfooter接收整型,表示从结尾往上过滤掉指定数量的行
    # 因为引擎退化为python,那么要手动指定engine="python",不然会警告
    # 另外需要指定encoding="utf-8",因为csv存在编码问题,当引擎退化为python的时候,在Windows上读取会乱码
    

    nrows

    nrows 参数设置一次性读入的文件行数,它在读入大文件时很有用,比如 16G 内存的PC无法容纳几百 G 的大文件。

    low_memory

    这个看起来是和内存有关的,但其实它是和数据类型相关的。在解释这个原因之前,我们还要先从DataFrame的数据类型说起。

    我们知道得到DataFrame的每一列都是有类型的,那么在读取csv的时候,pandas也是要根据数据来判断每一列的类型的。但pandas主要是靠"猜"的方法,因为在读取csv的时候是分块读取的,每读取一块的时候,会根据数据来判断每一列是什么类型;然后再读取下一块,会再对类型进行一个判断,得到每一列的类型,如果得到的结果和上一个块得到结果不一样,那么就会发出警告,提示有以下的列存在多种数据类型:

    DtypeWarning: Columns (1,5,8,......) have mixed types. Specify dtype option on import or set low_memory=False.
    

    而为了保证正常读取,那么会把类型像大的方向兼容,比如第一个块的user_id解释成整型,但是第二个块发现user_id有的值无法解析成整型的,那么类型整体就会变成字符串,于是pandas提示该列存在混合类型。而一旦设置low_memory=False,那么pandas在读取csv的时候就不分块读了,而是直接将文件全部读取到内存里面,这样只需要对整体进行一次判断,就能得到每一列的类型。但是这种方式也有缺陷,一旦csv过大,就会内存溢出。

    但是从数据库读取就不用担心了,因为数据库是规定了每一列的类型的。如果是从数据库读取得到的DataFrame,那么每一列的数据类型和数据库表中的类型是一致的。

    还有,我们在上面介绍了dtype,这个是我们手动规定类型。那么pandas就会按照我们规定的类型去解析指定的列,但是一旦无法解析就会报错。

    memory_map

    如果你知道python的一个模块mmap,那么你肯定很好理解。如果使用的数据在内存里,那么直接进行映射即可,不会再次进行IO操作。默认为False

    空值处理相关参数

    na_values

    na_values 参数可以配置哪些值需要处理成 NaN,这个是非常常用的,但是用的人不多。

    df = pd.read_csv('girl.csv')
    print(df)
    """
          name  age gender
    0  mashiro   18      女
    1        #   17      女
    2   koishi   16      #
    """
    
    # 将#全部换成NaN
    df = pd.read_csv('girl.csv', na_values=["#"])
    print(df)
    """
          name  age gender
    0  mashiro   18      女
    1      NaN   17      女
    2   koishi   16    NaN
    """
    
    # 也可以指定多个值
    df = pd.read_csv('girl.csv', na_values=["#", "mashiro"])
    print(df)
    """
         name  age gender
    0     NaN   18      女
    1     NaN   17      女
    2  koishi   16    NaN
    """
    
    # 也可以只对指定的列进行替换
    df = pd.read_csv('girl.csv', na_values={"name": ["#"], "gender": ["女"]})
    print(df)
    """
          name  age gender
    0  mashiro   18    NaN
    1      NaN   17    NaN
    2   koishi   16      #
    """
    

    keep_default_na 是和 na_values 搭配的,如果前者为 True,则 na_values 被解析为 Na/NaN 的字符除了用户设置外,还包括默认值。默认为True

    skip_blank_lines

    skip_blank_lines 默认为 True,则过滤掉空行,如为 False 则解析为 NaN

    verbose

    打印一些重要信息

    时间处理相关参数

    parse_dates

    指定某些列为时间类型。

    df = pd.read_csv("xx.csv", parse_dates=["column"])
    

    date_parser

    date_parser 参数定制某种时间类型,详细使用过程总结如下。因为有些格式虽然是日期,但不是那种可以直接转换的样子:比如'2018-01-01'。可能是这种类型:'2018年1月1日',这个时候我们就需要手动来定制解析的规则

    df = pd.read_csv("xx.csv", parse_dates=["column"], date_parser=lambda x: pd.datetime.strptime(x, "%Y年%m月%d日"))
    

    infer_datetime_format

    infer_datetime_format 参数默认为 False。如果设定为 True 并且 parse_dates 可用,那么 Pandas 将尝试转换为日期类型,如果可以转换,转换方法并解析,在某些情况下会快 5~10 倍。

    分块读入相关参数

    分块读入内存,尤其单机处理大文件时会很有用。

    iterator

    iterator 取值 boolean,默认为False。如果为True,那么返回一个 TextFileReader 对象,以便逐块处理文件。这个在文件很大时,内存无法容纳所有数据文件,此时分批读入,依次处理。

    # 此时返回一个类似于迭代器的对象
    chunk = pd.read_csv('girl.csv', iterator=True)
    # 调用get_chunk,里面传入整型,来读取指定的行数
    print(chunk.get_chunk(1))
    """
          name  age gender
    0  mashiro   18      女
    """
    print(chunk.get_chunk(2))
    """
         name  age gender
    1       #   17      女
    2  koishi   16      #
    """
    try:
        print(chunk.get_chunk(2))
    except StopIteration:
        print("文件读取完毕")  # 文件读取完毕
    
    # 文件总共三行,所以读完之后,再读就溢出了
    # 如果还剩下1行,但是我们指定读取100,那么也不会报错
    # 不够指定的行数,那么有多少返回多少
    

    chunksize

    chunksize 整型,默认为 None,设置文件块的大小。

    chunk = pd.read_csv('girl.csv', chunksize=2)
    # 还是返回一个类似于迭代器的对象
    print(chunk)  # <pandas.io.parsers.TextFileReader object at 0x00000258C41B2CD0>
    
    # 调用get_chunk,如果不指定行数,那么就是默认的chunksize
    print(chunk.get_chunk())
    """
          name  age gender
    0  mashiro   18      女
    1        #   17      女
    """
    # 但也可以指定
    print(chunk.get_chunk(1))
    """
         name  age gender
    2  koishi   16      #
    """
    try:
        print(chunk.get_chunk(2))
    except StopIteration:
        print("文件读取完毕")  # 文件读取完毕
    

    格式和压缩相关参数

    compression

    compression 参数取值为 {‘infer’, ‘gzip’, ‘bz2’, ‘zip’, ‘xz’, None},默认 ‘infer’,直接使用磁盘上的压缩文件。

    如果使用 infer 参数,则使用 gzip、bz2、zip 或者解压文件名中以 ‘.gz’、‘.bz2’、‘.zip’ 或 ‘xz’ 这些为后缀的文件,否则不解压。

    如果使用 zip,那么 ZIP 包中必须只包含一个文件。设置为 None 则不解压。

    # 直接将上面的girl.csv添加到压缩文件,打包成girl.zip
    df = pd.read_csv('girl.zip', compression="zip")
    print(df)
    """
          name  age gender
    0  mashiro   18      女
    1        #   17      女
    2   koishi   16      #
    """
    # 会自动解包,读取文件,但是如果没有compression参数,那么就会报错了
    # UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 0: invalid continuation byte
    

    thousands

    千分位分割符,如 , 或者 .,默认为None

    encoding

    encoding 指定字符集类型,通常指定为 'utf-8'。根据情况也可能是ISO-8859-1

    error_bad_lines和warn_bad_lines

    如果一行包含过多的列,假设csv的数据有3列,但是某一行却有4个数据,显然数据有问题。那么默认情况下不会返回DataFrame,而是会报错。

    # pandas.errors.ParserError: Error tokenizing data. C error: Expected 3 fields in line 3, saw 4
    

    我们在某一行中多加了一个数据,结果显示错误。因为girl.csv里面有三列,但是有一行却有四个数据,所以报错。

    在小样本读取时,这个错误很快就能发现。但是如果样本比较大、并且由于数据集不可能那么干净、很容易出现这种情况,那么该怎么办呢?而且这种情况下,Excel基本上是打不开这么大的文件的。这个时候我们就可以将error_bad_lines设置为False(默认为True),意思是遇到这种情况,直接把这一行给我扔掉。同时会设置 warn_bad_lines 设置为True,打印剔除的这行。

    df = pd.read_csv('girl.csv', error_bad_lines=False, warn_bad_lines=True)
    print(df)
    """
          name  age gender
    0  mashiro   18      女
    1   koishi   16      #
    
    b'Skipping line 3: expected 3 fields, saw 4
    '
    """
    

    以上两参数只能在C解析引擎下使用。

    总结

    pandas在读取csv的时候支持的参数是很多的,其中部分参数也适用于读取其它类型的文件。这些参数虽然很多平常都不会用,但是还是要了解,因为read_csv的其它参数是可以很方便地解决某些问题的。

  • 相关阅读:
    创建被访问的swf文件
    BFS寻路算法的实现
    Flex里的命名空间,fx、mx、s【转】
    Flex的基础用法【转】
    Pow(x, n)
    Roman to Integer
    Integer to Roman
    Divide Two Integers
    Single Number II
    Single Number I
  • 原文地址:https://www.cnblogs.com/traditional/p/12514914.html
Copyright © 2020-2023  润新知