R 如何实现更快读取数据 - 使用readr包

readr package

Hadley Wickham 和 RStudio 团队开发了一个新的用于文本数据读取的包,那就是readr包。readr包提供了几个新的函数,能够更快的读取文本文件.


#1. 相比于R基础包

  • 比基础模块中函数速度更快(约快10倍);
  • 生成tibble,并且不会把字符向量转换成因子,不使用行名称,也不会随意改动列名称;
  • 更易于重复使用;

#2. readr介绍

##2.1 readr()也是tidyverse的核心包之一。

  • readr 的多数函数用于将平面文件转换为数据框。
read_csv()读取逗号分隔文件
read_csv2()读取分号分隔文件
read_tsv()读取制表符分隔文件
read_delim()读取使用任意分隔符的文件
read_fwf()读取固定宽度文件;既可以使用fwf_widths()函数按照宽度来设定域,也可以使用fwf_positions()函数按照位置来设定域。read_table()读取固定宽度文件的一种常用变体,其中使用空白字符来分隔各列
read_log()读取Apache风格的日志文件。
read_csv()函数使用数据的第一行作为列名称
read_table 可用于替代read.table();但是read.table()支持文件列之间存在不等长空格,read_table()要求列必需对齐;

有时候文件有几行元数据,可以使用skip=n来跳过前n行;或者使用comment="#"来丢弃所有以#开头的行;

##2.2 与R基础包进行比较

  • 比基础模块中函数速度更快(约快10倍)
  • 生成tibble,并且不会把字符向量转换成因子,不使用行名称,也不会随意改动列名称
  • 更易于重复使用

#3. 解析向量

parse_*()函数族接受一个字符向量,并返回一个特定向量,如逻辑、整数或日期向量

> str(parse_logical(c("TRUE","FALSE","NA")))
 logi [1:3] TRUE FALSE NA
> str(parse_integer(c("1","2","3")))
 int [1:3] 1 2 3
> str(parse_date(c("2010-01-01","1979-10-14")))
 Date[1:2], format: "2010-01-01" "1979-10-14"

##3.1 数值解析

  • parse_integer() 解析整数;
  • parse_double()和parse_number()都是数值型解析函数;

对于数值型解析主要会遇到3个问题:

1. 各地数值书写方式不同,小数分隔号有`.`和`,`
2. 数值赋予某种实际意义时,可能会加上某种符号,如$100或10%
3. 数值的分组处理,如1 000 000

对于第一个问题,可以通过创建一个新的地区对象设定参数decimal_mark解决;readr()默认的地区时美国。

parse_double("1.23")
parse_double("1,23",locale = locale(decimal_mark = ","))
  • parse_number()可以忽略数值前后的非数值型字符;
> parse_number("$100")
[1] 100
> parse_number("20%")
[1] 20
> parse_number("It cost $123.45")
[1] 123.45
  • parse_number()结合地区设置可以解决第三个问题;
> parse_number("$123,456,789")
[1] 123456789
#适用于欧洲多数国家
> parse_number("$123.456.789",locale = locale(grouping_mark = "."))
[1] 123456789
#适用于瑞士
> parse_number("$123'456,789",locale = locale(grouping_mark = "'"))
[1] 123456

##3.2 字符串解析

  • parse_character()函数用于字符串解析

    字符串解析时需要注意编码形式,默认编码方式时UTF-8;

> x2<-"\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"
> parse_character(x2,locale = locale(encoding = "shift-JIS"))
[1] "こんにちは"

​ 对于不知道编码方式的字符串,可以使用guess_encoding(charToRaw())找出编码方式,charToRaw()可以返回字符串的底层表示;

> guess_encoding(charToRaw(x2))
# A tibble: 1 x 2
  encoding confidence
           
1 KOI8-R         0.42

##3.3 因子解析

  • parse_factor()函数用于解析因子,其中国levels参数设置水平。
> fruit<-c("apple","banana")
> parse_factor(c("apple","banana","bananana"),levels = fruit)
Warning: 1 parsing failure.
row col           expected   actual
  3  -- value in level set bananana

[1] apple  banana   
attr(,"problems")
# A tibble: 1 x 4
    row   col expected           actual  
                     
1     3    NA value in level set bananana
Levels: apple banana

##3.4 日期、日期时间和时间解析

  • parse_datetime() 接受符合ISO 8601标准的日期时间,其中日期的各个部分按从大到小的顺序排列,即年、月、日,小时,分钟、秒;
> parse_datetime("2010-10-01T2010")
[1] "2010-10-01 20:10:00 UTC"
  • parse_date() 接受年月日;
> parse_date("2010-10-01")
[1] "2010-10-01"
  • parse_time() 接受小时;但是还可以加上分钟,秒,以及a.m./p.m.标识符
> parse_time("01:01 am")
01:01:00
  • R 基础包中没有能够很好表示时间数据的内置类,但是可以使用hms包提供的时间类。
> library(hms)
> parse_date("01/02/15","%m/%d/%y")
[1] "2015-01-02"

#4. 解析文件

##4.1 每列数据类型确定

​ 解析文件,首要任务就是对文件每列数据类型的确定;大多数工具会根据文件header或随机抽取一定数量行数确定数据类型;readr 通过读取文件前1000行来确定每列的类型,使用guess_parser()函数返回readr解析的数据类型,parse_guess()利用这个类型去解析文件的数据。

> guess_parser("2010-10-01")
[1] "date"
> guess_parser("15:01")
[1] "time"

​ 遇到特殊情况,读取1000行的方法是行不通的;这时可以调控一些参数来解决遇到的问题。
col_types: 在文件读取时,通过参数col_types指定每列的类型;

> challenge<-read_csv(readr_example("challenge.csv"))
Parsed with column specification:
cols(
  x = col_double(),
  y = col_logical()
)
Warning: 1000 parsing failures.
 row col           expected     actual                                                                         file
1001   y 1/0/T/F/TRUE/FALSE 2015-01-16 'C:/Users/labworker/Documents/R/win-library/3.5/readr/extdata/challenge.csv'
1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 'C:/Users/labworker/Documents/R/win-library/3.5/readr/extdata/challenge.csv'
1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 'C:/Users/labworker/Documents/R/win-library/3.5/readr/extdata/challenge.csv'
1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 'C:/Users/labworker/Documents/R/win-library/3.5/readr/extdata/challenge.csv'
1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 'C:/Users/labworker/Documents/R/win-library/3.5/readr/extdata/challenge.csv'
.... ... .................. .......... ............................................................................
See problems(...) for more details.

> head(challenge)
# A tibble: 6 x 2
      x y    
   
1   404 NA   
2  4172 NA   
3  3004 NA   
4   787 NA   
5    37 NA   
6  2332 NA   
> challenge<-read_csv(readr_example("challenge.csv"),
                    col_types = cols(
                      x = col_double(),
                      y = col_character()
                    )
                    )

guess_max: 指定用于解析列变量类型的行数;
n_max():指定文件读入行数;在处理大内存文件时相当有用;
read_lines(): 按行读入文件

#5. 写入文件

  • write.csv(), write.tsv() 常用于将数据输出到磁盘,默认使用UTF-8编码;
write_csv(challenge,"challenge.csv")
read_csv("challenge.csv")

​ 但是这种输出方式的缺点就是无法保留列类型信息,当再次读入文件时需要重新判定类的类型;这对于数据处理过程中输出读取临时文件会产生没必要的麻烦或错误;如果要避免这样的现象,可以使用其它输出方法:write_rds()和write_feather(), 后者需要调用feather包。

  • write_rds()联合read_rds()使用,write_rds()将数据保存为自定义的二进制形式(RDS格式)
write_rds(challenge,"challenge.csv")
read_rds("challenge.csv")
  • feather包也是实现一种二进制形式,可以在多个编程语言之间共享;相比于RDS,速度更快。
library(feather)
write_feather(challenge,"challenge.csv")
read_feather("challenge.csv")

参考

New packages for reading data into R — fast
R 数据科学

你可能感兴趣的:(R 如何实现更快读取数据 - 使用readr包)