Rdata005 使用readr进行数据导入

1 readr与R基础包进行比较

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

2 R基础包

  • read.table()
    read.table(file, header = FALSE, sep = "", quote = "\"'", dec = ".", numerals = c("allow.loss", "warn.loss", "no.loss"), row.names, col.names, as.is = !stringsAsFactors, na.strings = "NA", colClasses = NA, nrows = -1, skip = 0, check.names = TRUE, fill = !blank.lines.skip, strip.white = FALSE, blank.lines.skip = TRUE, comment.char = "#", allowEscapes = FALSE, flush = FALSE, stringsAsFactors = default.stringsAsFactors(), fileEncoding = "", encoding = "unknown", text, skipNul = FALSE)

  • read.csv()读取逗号分隔文件
    read.csv(file, header = TRUE, sep = ",", quote = "\"", dec = ".", fill = TRUE, comment.char = "", ...)

  • read.csv2()读取分号分隔文件
    read.csv2(file, header = TRUE, sep = ";", quote = "\"", dec = ",", fill = TRUE, comment.char = "", ...)

  • read.delim()读取制表符分隔文件
    read.delim(file, header = TRUE, sep = "\t", quote = "\"", dec = ".", fill = TRUE, comment.char = "", ...)

  • read.delim2()读取制表符分隔文件
    read.delim2(file, header = TRUE, sep = "\t", quote = "\"", dec = ",", fill = TRUE, comment.char = "", ...)

参数
参数 含义
file 文件名(包在“”内,或者使用一个字符型变量),可能需要全程路径(注意即使是在windows下,符号\也不允许包含在内,必须使用/替换),或者一个URL链接(http://...)(用URL对文件远程访问)
header 一个逻辑值(FALSE or TRUE)用来反映这个文件的第一行是否包含变量名
sep 文件中的字段分隔符,例如对用制表符分隔的文件使用sep=“\t”
quote 指定用于包围字符型数据的字符
dec 用来表示小数点的字符
row.names 保存行名的向量,或文件中一个变量的序号或名字,缺省时行号取为1,2,3...
col.names 指定列名的字符向量(缺省值是:V1,V2,V3...)
as.is 控制是否将字符型变量转化为因子型变量(如果值为FALSE),或者仍将其保留为字符型(TRUE)。as.is可以是逻辑型,数值型或字符型向,用来判断变量是否被保留为字符
na.strings 代表缺失数据的值(转化NA)
colClasses 指定各列的数据类型的一个字符型变量
nrows 可以读取的最大行数(忽略负值)
skip 在读取数据前跳过的行数
check.names 如果为TRUE,则检查变量名是否存在R中有效
fill 如果为TRUE且非所有的行中变量数目相同,则用空白填补
strip.white 在sep已指定的情况下,如果为TRUE,则删除字符型变量前后多余的空格
blank.lines.skip 如果为TRUE,忽略空白行
comment.char 一个字符用来在数据文件中写注释,以这个字符开头的行将被忽略(要禁用这个参数,可使用comment.char="")

3 readr包

readr是tidyverse的核心包之一,readr 的多数函数用于将平面文件转换为数据框

  • read_csv()读取逗号分割文件
    read_csv(file, col_names = TRUE, col_types = NULL, locale = default_locale(), na = c("", "NA"), quoted_na = TRUE, quote = "\"", comment = "", trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = min(1000, n_max), progress = show_progress(), skip_empty_rows = TRUE)

  • read_csv2()读取分号分割文件
    read_csv2(file, col_names = TRUE, col_types = NULL, locale = default_locale(), na = c("", "NA"), quoted_na = TRUE, quote = "\"", comment = "", trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = min(1000, n_max), progress = show_progress(), skip_empty_rows = TRUE)

  • read_tsv()读取制表符分割文件
    read_tsv(file, col_names = TRUE, col_types = NULL, locale = default_locale(), na = c("", "NA"), quoted_na = TRUE, quote = "\"", comment = "", trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = min(1000, n_max), progress = show_progress(), skip_empty_rows = TRUE)

  • read_delim()读取任意分隔符分割的文件
    read_delim(file, delim, quote = "\"", escape_backslash = FALSE, escape_double = TRUE, col_names = TRUE, col_types = NULL, locale = default_locale(), na = c("", "NA"), quoted_na = TRUE, comment = "", trim_ws = FALSE, skip = 0, n_max = Inf, guess_max = min(1000, n_max), progress = show_progress(), skip_empty_rows = TRUE)

  • read_fwf()读取固定宽度文件;既可以使用fwf_widths()函数按照宽度来设定域,也可以使用fwf_positions()函数按照位置来设定域。

  • read_table()读取固定宽度文件的一种常用变体,其中使用空白字符来分隔各列

  • read_log()读取Apache风格的日志文件

3.1 参数:
参数 含义
file 文件名(包在“”内,或者使用一个字符型变量),可能需要全程路径(注意即使是在windows下,符号\也不允许包含在内,必须使用/替换),或者一个URL链接(http://...)(用URL对文件远程访问)
header 一个逻辑值(FALSE or TRUE)用来反映这个文件的第一行是否包含变量名
sep 文件中的字段分隔符,例如对用制表符分隔的文件使用sep=“\t”
quote 指定用于包围字符型数据的字符
dec 用来表示小数点的字符
fill 如果为TRUE且非所有的行中变量数目相同,则用空白填补
comment.char 一个字符用来在数据文件中写注释,以这个字符开头的行将被忽略(要禁用这个参数,可使用comment.char="")
3.2 read_csv()函数
# 加载readr
library(tidyverse)

# read_csv()使用数据的第一行作为列名称
read_csv("a,b,c
         1,2,3
         4,5,6")
# # A tibble: 2 x 3
# a     b     c
# <dbl> <dbl> <dbl>
# 1     1     2     3
# 2     4     5     6

# skip=n用来跳过数据的前n行,因为第一列是默认的列名称,所以skip=1,实际上会跳过2列
read_csv("a,b,c
         1,2,3
         4,5,6",skip=1)
# # A tibble: 1 x 3
# `1`   `2`   `3`
# <dbl> <dbl> <dbl>
#  1     4     5     6

# 使用comment=“#”,可以丢弃所有以“#”开头的行
read_csv("# it is a test
a,b,c
1,2,3
4,5,6",comment="#")
# # A tibble: 2 x 3
# a     b     c
# <dbl> <dbl> <dbl>
# 1     1     2     3
# 2     4     5     6

# 第一行仅有一条数据,所以仅得到一列数据
read_csv("# it is a test
a,b,c
1,2,3
4,5,6")
# # A tibble: 3 x 1
# `# it is a test`
# <chr>           
# 1 a               
# 2 1               
# 3 4 

# 用逗号进行分割,读取得到完整的数据
read_csv("# it is a test,,
a,b,c
1,2,3
4,5,6")
# # A tibble: 3 x 3
# `# it is a test` X2    X3   
# <chr>            <chr> <chr>
# 1 a                b     c    
# 2 1                2     3    
# 3 4                5     6 

# col_names=FALSE,用于通知read_csv()取消第一行作为列名称
read_csv("a,b,c
         1,2,3
         4,5,6",col_names=F)
# # A tibble: 3 x 3
#   X1    X2    X3   
# <chr> <chr> <chr>
# 1 a     b     c    
# 2 1     2     3    
# 3 4     5     6 

# 使用换行符“\n”添加新行
read_csv("a,b,c\n1,2,3\n4,5,6",col_names=F)
# # A tibble: 3 x 3
#   X1    X2    X3   
# <chr> <chr> <chr>
# 1 a     b     c    
# 2 1     2     3    
# 3 4     5     6 

# 通过向量赋值指定col_names
read_csv("a,b,c
         1,2,3
         4,5,6",col_names=c("A","B","C"))
# # A tibble: 3 x 3
#   A     B     C    
# <chr> <chr> <chr>
# 1 a     b     c    
# 2 1     2     3    
# 3 4     5     6  

# 设定na=“ ”,用于设定使用哪个用来表示文件的缺失值
read_csv("a,b,c
         1,.,3
         4,5,.",na=".")
# # A tibble: 2 x 3
#       a     b     c
# <dbl> <dbl> <dbl>
# 1     1    NA     3
# 2     4     5    NA

# 通过向量设定na=“ ”,用于设定使用哪些值来表示文件的缺失值
read_csv("a,b,c
         1,.,3
         4,5,/",na=c(".","/"))
# # A tibble: 2 x 3
#       a     b     c
# <dbl> <dbl> <dbl>
# 1     1    NA     3
# 2     4     5    NA

4. 向量解析 parse_*()

4.1 重要的解析函数有8 种
  • parse_logical()parse_integer() 函数分别解析逻辑值和整数。因为这两个解析函数
    基本不会出现问题,所以我们不再进行更多介绍。
  • parse_double() 是严格的数值型解析函数,parse_number() 则是灵活的数值型解析函数。
    这两个函数要比你预想的更复杂,因为世界各地书写数值的方式不尽相同。
  • parse_character() 函数似乎太过简单,甚至没必要存在。但一个棘手的问题使得这个
    函数变得非常重要:字符编码。
  • parse_factor()函数可以创建因子,R 使用这种数据结构来表示分类变量,该变量具有
    固定数目的已知值。
  • parse_datetime()parse_date()parse_time()函数可以解析不同类型的日期和时间。
    它们是最复杂的,因为有太多不同的日期书写形式。
4.2 举例
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"

# 如解析失败,会收到Warning信息
parse_integer(c("1", "2", "abc","3"))
# Warning: 1 parsing failure.
# row col   expected actual
# 3  -- an integer    abc

# na参数可以设定哪些字符串当做缺失值进行处理
parse_integer(c("1", "2", "abc","3"),na="abc")
# [1]  1  2 NA  3

# 解析失败的值在输出中是以缺失值的形式存在
x <- parse_integer(c("123", "345", "abc", "123.45"))
# Warning: 2 parsing failures.
# row col               expected actual
# 3  -- an integer                abc
# 4  -- no trailing characters    .45

x
# [1] 123 345  NA  NA
# attr(,"problems")
# # A tibble: 2 x 4
#      row   col expected               actual
#     <int> <int> <chr>                  <chr> 
# 1     3    NA an integer             abc   
# 2     4    NA no trailing characters .45   
# Warning message:
#   `...` is not empty.

# 如果解析失败的值很多,那么就应该使用problems() 函数来获取完整的失败信息集合。这
个函数会返回一个tibble,你可以使用dplyr 包来进行处理
problems(x)
# # A tibble: 2 x 4
#      row   col expected               actual
#     <int> <int> <chr>                  <chr> 
# 1     3    NA an integer             abc   
# 2     4    NA no trailing characters .45 

4.3 解析数值 parse_datetime()parse_date()parse_time()

解析数值似乎是非常直截了当的,但以下3 个问题增加了数值解析的复杂性。
• 世界各地的人们书写数值的方式不尽相同。例如,有些国家使用. 来分隔实数中的整数
和小数部分,而有些国家则使用,。
• 数值周围经常有表示某种意义的其他字符,如$1000 或10%。
• 数值经常包含“分组”,以便更易读,如1 000 000,而且世界各地用来分组的字符也不
尽相同。

# 在解析数值时,最重要的选项就是用来表示小数点的字符。通过创建一个新的地区对象并设定decimal_mark 参数,可以覆盖"." 的默认值
parse_double("1.23")
# [1] 1.23
# decimal_mark,十进制标记
parse_double("1,23", locale = locale(decimal_mark = ","))
# [1] 1.23
# decimal_mark只能在点"."和逗号","里面选择
parse_double("1;23", locale = locale(decimal_mark = ";"))
# Error in locale(decimal_mark = ";") : 
#   decimal_mark %in% c(".", ",") is not TRUE


# 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忽略分组符号
parse_number("$123,456,789")
# [1] 123456789
# 适用于多数欧洲国家
# locale()指定分组符号
parse_number(
  "123.456.789",
  locale = locale(grouping_mark = ".")
)
# [1] 123456789

locale()函数
locale(date_names = "en", date_format = "%AD", time_format = "%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8", asciify = FALSE)

4.4 解析字符串 parse_character()
# charToRaw(x),将字符串转换为十六进制数,从十六进制数到字符的这种映射称为编码,这个示例中的编码方式称为ASCII(American Standard Code for Information Interchange)
charToRaw("Hadley")
# [1] 48 61 64 6c 65 79

# 对于英语之外的其他语言,有多种编码方式。
# Latin1(即ISO-8859-1,用于西欧语言)
# Latin2(即ISO-8859-2,用于东欧语言)是两种常用的编码方式
# UTF-8 可以为现在人类使用的所有字符进行编码,同时还支持很多特殊字符
x1 <- "El Ni\xf1o was particularly bad this year"
x2 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"
parse_character(x1,locale = locale(encoding = "Latin1"))
# [1] "El Niño was particularly bad this year"
parse_character(x2,locale = locale(encoding = "Shift-JIS"))
# [1] "こんにちは"

# guess_encoding() 函数可以帮助找到编码方式,但也并不是绝对准确
guess_encoding(charToRaw(x1))
# # A tibble: 2 x 2
#    encoding   confidence
#     <chr>           <dbl>
# 1 ISO-8859-1       0.46
# 2 ISO-8859-9       0.23

guess_encoding(charToRaw(x2))
# # A tibble: 1 x 2
#    encoding confidence
#     <chr>         <dbl>
#   1 KOI8-R         0.42
# Warning message:
#   `...` is not empty.

4.5 因子 parse_factor()

R 使用因子表示取值范围是已知集合的分类变量。如果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 <NA>  
#   attr(,"problems")
# # A tibble: 1 x 4
# row   col expected           actual  
# <int> <int> <chr>              <chr>   
#   1     3    NA value in level set bananana
# Levels: apple banana
# Warning message:
#   `...` is not empty.

4.6 日期、日期时间与时间 parse_datetime()parse_date()parse_time()

分别根据需要,在下述3 种解析函数之间进行选择

  • 日期型数据(从1970-01-01 开始的天数)
  • 日期时间型数据(从1970-01-01午夜开始的秒数)
  • 时间型数据(从午夜开始的秒数)
# parse_datetime() 期待的是符合ISO 8601 标准的日期时间。ISO 8601 是一种国际标准,其中日期的各个部分按从大到小的顺序排列,即年、月、日、小时、分钟、秒:
parse_datetime("2010-10-01T2010")
# [1] "2010-10-01 20:10:00 UTC"

# 如果时间被省略了,那么它就会被设置为午夜
parse_datetime("20101010")
# [1] "2010-10-10 UTC"

# parse_date() 期待的是四位数的年份、一个- 或/、月、一个- 或/,然后是日:
parse_date("2010-10-01")
# [1] "2010-10-01"

# parse_time() 期待的是小时、:、分钟、可选的: 和秒,以及一个可选的a.m./p.m. 标识符:
library(hms)
parse_time("01:10 am")
# 01:10:00
parse_time("20:10:01")
# 20:10:01


# 如果这些默认设置不适合你的数据,那么你可以提供自己的日期时间格式
parse_date("01/02/15", "%m/%d/%y")
# [1] "2015-01-02"
parse_date("01/02/15", "%d/%m/%y")
# [1] "2015-02-01"
parse_date("01/02/15", "%y/%m/%d")
# [1] "2001-02-15"

# 年
### %Y(4 位数)
### %y(2 位数;00-69 → 2000-2069、70-99 → 1970-1999)
# 月
### %m(2 位数)
### %b(简写名称,如Jan)
### %B(完整名称,如January)
# 日
### %d(1 位或2 位数)
### %e(2 位数)
# 时间
### %H(0-23 小时)
### %I(0-12 小时,必须和%p 一起使用)
### %p(表示a.m./p.m.)
### %M(分钟)
### %S(整数秒)
### %OS(实数秒)
### %Z(时区,America/Chicage 这样的名称)
### %z(与国际标准时间的时差,如+0800)
# 非数值字符
### %.(跳过一个非数值字符)
### %*(跳过所有非数值字符)

5 解析文件

5.1 策略

readr 使用一种启发式过程来确定每列的类型:先读取文件的前 1000 行,然后使用(相对保守的)某种启发式算法确定每列的类型。可以使用字符向量模拟这个过程,先使用 guess_ parser()函数返回 readr 最可信的猜测,接着 parse_guess() 函数使用这个猜测来解析列

guess_parser("2010-10-01") 
#> [1] "date" 
guess_parser("15:01") 
#> [1] "time" 
guess_parser(c("TRUE", "FALSE")) #> [1] "logical" 
guess_parser(c("1", "5", "9")) 
#> [1] "integer" 
guess_parser(c("12,352,561")) 
#> [1] "number" 
str(parse_guess("2010-10-10")) 
#>  Date[1:1], format: "2010-10-10"

这个启发式过程会尝试以下每种数据类型,直至找到匹配的类型。

  • 逻辑值
    只包括 F、T、FALSE 和 TRUE。
  • 整数
    只包括数值型字符(以及 -)。
  • 双精度浮点数
    只包括有效的双精度浮点数(也包括 4.5e-5 这样的数值)。
  • 数值
    只包括带有分组符号的有效双精度浮点数。
  • 时间
    与默认的 time_format 匹配的值。
  • 日期
    与默认的 date_format 匹配的值。
  • 日期时间
    符合 ISO 8601 标准的任何日期。
    如果以上类型均不匹配,那么这一列就还是一个字符串向量
5.2 问题

这些默认设置对更大的文件并不是一直有效的。以下是两个主要问题。

  • 前 1000 行可能是一种特殊情况,readr 猜测出的类型不足以代表整个文件。例如,一列双精度数值的前 1000 行可能都是整数。
  • 列中可能含有大量缺失值。如果前 1000 行中都是 NA,那么 readr 会猜测这是一个字符向量,但你其实想将这一列解析为更具体的值。
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/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# .... ... .................. .......... ...............................................................................
# See problems(...) for more details.

# 以上的输出可以分为两部分:从前 1000 行中猜测出的列类型与前 5 条解析失败记录。我们总是应该使用 "problems()" 函数明确地列出这些失败记录,以便更加深入地探究其中的问题:
problems(challenge)
# # A tibble: 1,000 x 5
#    row col   expected           actual     file                                                                           
#    <int> <chr> <chr>              <chr>      <chr>                                                                          
# 1  1001 y     1/0/T/F/TRUE/FALSE 2015-01-16 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 2  1002 y     1/0/T/F/TRUE/FALSE 2018-05-18 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 3  1003 y     1/0/T/F/TRUE/FALSE 2015-09-05 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 4  1004 y     1/0/T/F/TRUE/FALSE 2012-11-28 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 5  1005 y     1/0/T/F/TRUE/FALSE 2020-01-13 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 6  1006 y     1/0/T/F/TRUE/FALSE 2016-04-17 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 7  1007 y     1/0/T/F/TRUE/FALSE 2011-05-14 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 8  1008 y     1/0/T/F/TRUE/FALSE 2020-07-18 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 9  1009 y     1/0/T/F/TRUE/FALSE 2011-04-30 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# 10  1010 y     1/0/T/F/TRUE/FALSE 2010-05-11 'C:/Users/caoqiansheng/Documents/R/win-library/3.6/readr/extdata/challenge.csv'
# # ... with 990 more rows

# 一列列地处理,直至解决所有问题,是一种良好策略。根据报错信息修改如下

(
  challenge <- read_csv(
  readr_example("challenge.csv"),
  col_types = cols(
    x=col_double(),
    y=col_character()
  )
)
)

# # A tibble: 2,000 x 2
#     x    y    
#   <dbl> <chr>
# 1   404 NA   
# 2  4172 NA   
# 3  3004 NA   
# 4   787 NA   
# 5    37 NA   
# 6  2332 NA   
# 7  2489 NA   
# 8  1449 NA   
# 9  3665 NA   
# 10  3863 NA   
# # ... with 1,990 more rows

# 这样就解决了第一个问题,但如果查看最后几行的话,你会发现保存在字符向量中的其实是日期数据:
tail(challenge)

# # A tibble: 6 x 2
#     x       y         
#   <dbl>   <chr>     
# 1 0.805 2019-11-21
# 2 0.164 2018-03-29
# 3 0.472 2014-08-04
# 4 0.718 2015-08-16
# 5 0.270 2020-02-04
# 6 0.608 2019-01-06

# 设定 y 为日期列可以解决这个问题
  challenge <- read_csv(
    readr_example("challenge.csv"),
    col_types = cols(
      x=col_double(),
      y=col_date()
    )
  )


tail(challenge)
# # A tibble: 6 x 2
#     x       y         
#   <dbl>   <date>    
# 1 0.805 2019-11-21
# 2 0.164 2018-03-29
# 3 0.472 2014-08-04
# 4 0.718 2015-08-16
# 5 0.270 2020-02-04
# 6 0.608 2019-01-06

每个 parse_xyz() 函数都有一个对应的 col_xyz()函数。如果数据已经保存在 R 的字符向量中,那么你可以使用 arse_xyz();如果想要告诉readr 如何加载数据,则应该使用 col_xyz()
我们强烈建议你总是提供 col_types参数,从 readr打印出的输出中可以知道它的值。这可以确保数据导入脚本的一致性,并可以重复使用。如果不提供这个参数,而是依赖猜测的类型,那么当数据发生变化时,readr会继续读入数据。如果想要严格解析,可以使用stop_for_problems()函数:当出现任何解析问题时,它会抛出一个错误,并终止脚本。

5.3 其他策略

我们再介绍其他几种有助于解析文件的通用策略。
在前面的示例中,我们的运气太差了:如果比默认方式再多检查 1 行,我们就能一蹴而就,解析成功。


challenge2 <- read_csv(
  readr_example("challenge.csv"),
  guess_max = 1001
  )
)
# Parsed with column specification:
#   cols(
#     x = col_double(),
#     y = col_date(format = "")
#   )
challenge2
# # A tibble: 2,000 x 2
#     x    y         
#   <dbl> <date>    
# 1   404 NA        
# 2  4172 NA        
# 3  3004 NA        
# 4   787 NA        
# 5    37 NA        
# 6  2332 NA        
# 7  2489 NA        
# 8  1449 NA        
# 9  3665 NA        
# 10  3863 NA        
# # ... with 1,990 more rows

# 有时如果将所有列都作为字符向量读入的话,会更容易诊断出问题

challenge2 <- read_csv(
  readr_example("challenge.csv"),
  col_types = cols(.default = col_character())
)
)

# 这种方式结合 type_convert() 函数使用时特别有效,后者可以在数据框的字符列上应用启发式解析过程:
df <- tribble(
  ~x, ~y,
  "1","1.21",
  "2","2.32",
  "3","4.56"
)
df
# # A tibble: 3 x 2
#   x     y    
#  <chr> <chr>
# 1 1     1.21 
# 2 2     2.32 
# 3 3     4.56

type_convert(df) 
# Parsed with column specification:
#   cols(
#     x = col_double(),
#     y = col_double()
#   )
# # A tibble: 3 x 2
#      x     y
#    <dbl> <dbl>
# 1     1  1.21
# 2     2  2.32
# 3     3  4.56

6 写入文件

readr 还提供了两个非常有用的函数,用于将数据写回到磁盘:write_csv()write_ tsv()。这两个函数输出的文件能够顺利读取的概率更高,因为:

  • 它们总是使用 UTF-8 对字符串进行编码;
  • 它们使用 ISO 8601 格式来保存日期和日期时间数据,以便这些数据不论在何种环境下都更容易解析。
6.1 用法:

write_delim(x, path, delim = " ", na = "NA", append = FALSE, col_names = !append, quote_escape = "double")

write_csv(x, path, na = "NA", append = FALSE, col_names = !append, quote_escape = "double")

write_csv2(x, path, na = "NA", append = FALSE, col_names = !append, quote_escape = "double")

write_excel_csv(x, path, na = "NA", append = FALSE, col_names = !append, delim = ",", quote_escape = "double")

write_excel_csv2(x, path, na = "NA", append = FALSE, col_names = !append, delim = ";", quote_escape = "double")

write_tsv(x, path, na = "NA", append = FALSE, col_names = !append, quote_escape = "double")

6.2 导为 Excel 文件

如果想要将 CSV 文件导为 Excel 文件,可以使用 write_excel_csv()函数,该函数会在文件开头写入一个特殊字符(字节顺序标记),告诉 Excel 这个文件使用的是 UTF-8 编码。
这几个函数中最重要的参数是 x(要保存的数据框)和 path(保存文件的位置)。还可以使用 na 参数设定如何写入缺失值,如果想要追加到现有的文件,需要设置 append 参数:
write_csv(challenge, "challenge.csv")

  • 当保存为 CSV 文件时,类型信息就丢失了
challenge
# # A tibble: 2,000 x 2
#       x y         
#   <dbl> <date>    
# 1   404 NA        
# 2  4172 NA        
# 3  3004 NA        
# 4   787 NA        
# 5    37 NA        
# 6  2332 NA        
# 7  2489 NA        
# 8  1449 NA        
# 9  3665 NA        
# 10  3863 NA        
# # ... with 1,990 more rows
write_csv(challenge, "challenge-2.csv")
read_csv("challenge-2.csv") 

# # A tibble: 2,000 x 2
#     x    y    
#   <dbl> <lgl>
# 1   404 NA   
# 2  4172 NA   
# 3  3004 NA   
# 4   787 NA   
# 5    37 NA   
# 6  2332 NA   
# 7  2489 NA   
# 8  1449 NA   
# 9  3665 NA   
# 10  3863 NA   
# # ... with 1,990 more rows

7 其他类型的数据读取

要想将其他类型的数据导入 R 中,我们建议首先从下列的 tidyverse 包开始。它们当然远非完美,但确实是一个很好的起点。对矩形数据来说

haven 可以读取 SPSS、Stata 和 SAS 文件

readxl 可以读取 Excel 文件(.xls 和 .xlsx 均可);

  • readxl-package {readxl}
  • readxl: Read Excel Files


    image.png

配合专用的数据库后端程序(如 RMySQL、RSQLite、RPostgreSQL 等),DBI 可以对相应数据库进行 SQL 查询,并返回一个数据框

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348