搜索
查看: 581|回复: 0

[R] <R for Data Science> 读书笔记7 | 第十一章: Data import

[复制链接]

12

主题

18

帖子

155

积分

注册会员

Rank: 2

积分
155
发表于 2018-11-12 12:16:18 | 显示全部楼层 |阅读模式
本帖最后由 冒刷 于 2018-11-12 12:18 编辑

11  Data import | readr package
11.1 简介
本章节中,学习如何将纯文本格式的矩形文件读入R,这当中的很多原则适用于其他类型的数据。

11.1.1 准备工作
使用tidyverse的核心包之一,readr包将平面文件导入R。
[AppleScript] 纯文本查看 复制代码
library(tidyverse)
11.2 入门
readr的多数函数用于将平面文件转换为数据框。
read_csv()读取逗号分隔的文件,read_csv2()读取分号分隔的文件,read_tsv()读取制表符分隔的文件,而read_delim()可以读取使用任意分隔符的文件。

read_fwf()读取宽度固定的文件。既可以使用fwf_widths()()按宽度来设定域,也可以使用fwf_positions()()按位置来设定域。read_table()读取固定宽度文件的一种常用变体,使用空白符来分隔各列。

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

本章节重点讲read_csv(),其他函数举一反三。

read_csv()函数的第一个重要参数:读取文件的路径。
[AppleScript] 纯文本查看 复制代码
data <- read_csv(“/Users/xx/Downloads/test.csv")[/color][/size][/font][/align][font=Arial][size=2]# Parsed with column specification:
# cols(
# .default = col_character(),
# Start = col_integer(),
# End = col_integer()
# )
#See spec(...) for full column specifications.
# | =====================================================| 100% 59 MB
# test.csv 是个人电脑的大数据文件,加载速度不错,3s完成59MBcsv文件加载
# 书本介绍说,当运行read_csv()时,它会打印数据列说明,给出每个列的名称和类型。不知是否由于这里的test.csv文件列过多,190+列,并未详尽列出每一列的名称和类型。
  
你还可以提供一个行内csv文件。
[AppleScript] 纯文本查看 复制代码
read_csv("a, b, c[/color][/size][/font][/align][font=Arial][size=2] 1, 2, 3
4, 5, 6”)
# A tibble: 2 x 3
# a b c
# <int> <int> <int>
# 1 1 2 3
# 2 4 5 6
  
以上两种情况,read_csv()函数都使用数据的第一行做为列名称。
但…可能在以下两种情况下需要改变这种做法:
  • 1. 有时文件的开头会有好几行元数据(又称中介数据,为描述数据的信息,如一本书的元数据可以是题名Jobs,著者Jacson,出版者 中信出版社,ISBN_xxxx,可使用skip = n来跳过前n行;或使用comment = “#”来丢弃所有以比如#开头的行。
    [AppleScript] 纯文本查看 复制代码
    read_csv("The 1st line of metadata[/size][/font]
    [font=Arial][size=2]The 2nd line of metadata[/size][/font]
    [font=Arial][size=2]x, y, z[/size][/font]
    [font=Arial][size=2]1, 2, 3", skip = 2)[/size][/font]
    [font=Arial][size=2]# A tibble: 1 x 3[/size][/font]
    [font=Arial][size=2]# x y z[/size][/font]
    [font=Arial][size=2]# <int> <int> <int>[/size][/font]
    [font=Arial][size=2]# 1 1 2 3[/size][/font]
    
    [font=Arial][size=2]read_csv("# A comment I want to skip[/size][/font]
    [font=Arial][size=2]# x, y, z[/size][/font]
    [font=Arial][size=2]# 1, 2, 3", comment = "#")[/size][/font]
    [font=Arial][size=2]# A tibble: 1 x 3[/size][/font]
    [font=Arial][size=2]# x y z[/size][/font]
    [font=Arial][size=2]# <int> <int> <int>[/size][/font]
    [font=Arial][size=2]# 1 1 2 3
    2. 而有时,数据没有列名称。可使用col_names = FALSE来告诉read_csv()函数不要将第一行做为列标题,而是将各列依次标注为X1至Xn
    [AppleScript] 纯文本查看 复制代码
    read_csv("1, 2, 3\n4, 5, 6", col_names = FALSE)[/size][/font]
    [font=Arial][size=2]# A tibble: 2 x 3[/size][/font]
    [font=Arial][size=2]# X1 X2 X3[/size][/font]
    [font=Arial][size=2]# <int> <int> <int>[/size][/font]
    [font=Arial][size=2]# 1 1 2 3[/size][/font]
    [font=Arial][size=2]# 2 4 5 6[/size][/font]
    [font=Arial][size=2]# 或者向col_names传递一个字符向量,做列名称。[/size][/font]
    [font=Arial][size=2]read_csv("1, 2, 3\n4, 5, 6", col_names = c("x", "y", "z"))[/size][/font]
    [font=Arial][size=2]# A tibble: 2 x 3[/size][/font]
    [font=Arial][size=2]# x y z[/size][/font]
    [font=Arial][size=2]# <int> <int> <int>[/size][/font]
    [font=Arial][size=2]# 1 1 2 3[/size][/font]
    [font=Arial][size=2]# 2 4 5 6
    3. 另一个通常需要修改的选项是na。它指明了文件中哪些值代表缺失值。
    [AppleScript] 纯文本查看 复制代码
    read_csv("a, b, c\n1, 2, .", na = ".")[/size][/font][/align][font=Arial][size=2]# A tibble: 1 x 3
    # a b c 
    # <int> <int> <chr>
    # 1 1 2 <NA> 
       
    11.2.1 与R基础包进行比较
    喜欢用readr中的函数的理由:

    • 比基础模块的函数速度快约10倍。
    • 可生成tibble,且不会将字符向量转换为因子,不使用行名称,不随意改动列名称。
    • 更易于重复使用。R基础包中的函数会继承操作系统的功能,并依赖环境变量,在本地计算机上正常运行的代码在导入不同计算机时不一定能正常运行。


11.3 解析向量 Parsing a vector
函数parse_*()接受一个字符向量,并返回一个特定向量,如逻辑、整数或日期。
[AppleScript] 纯文本查看 复制代码
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", "1997-10-31")))
# Date[1:2], format: "2010-01-01" “1997-10-31"
parse_*()函数的用法:第一个参数是需要解析的字符向量,na参数指明了需要当作缺失值的字符串。
[AppleScript] 纯文本查看 复制代码
parse_integer(c("1", "231", ".", "456"), na = “.")[/color][/size][/font][/align][font=Arial][size=2]# [1] 1 231 NA 456
解析失败时会给出警告信息,且解析失败的值在输出中以缺失值形式存在。若解析失败的值很多,使用problems()函数来获取完整的失败信息集合。这会返回一个tibble,可以用dplyr包来进行处理。
[AppleScript] 纯文本查看 复制代码
x <- parse_integer(c("12", "23", "abc", “12.23"))[/size][/font]
[font=Arial][size=2]x
# [1] 12 23 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 .23
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 .23
  
重要的8种解析函数:
  • parse_logical()和parse_integer(): 分别解析逻辑值和整数;
  • parse_double()和parse_number(): 严格的/灵活的数值型解析函数;
  • parse_character(): 字符编码方面;
  • parse_factor(): 可用于创建因子,R使用这种数据结构表示分类变量,该变量具有固定数目的已知值;
  • parse_datetime(), parse_date()和parse_time(): 解析不同类型的日期和时间。


11.3.1 数值解析
由于世界各地书写数字习惯的不同,比如有的国家小数点分隔用的是逗号,这使得数值解析问题变得困难。
[AppleScript] 纯文本查看 复制代码
# 使用locale指明如小数点的字符是逗号而非习惯的.号[/size][/font][/align][align=left][font=Arial][size=2]parse_double("1,23", locale = locale(decimal_mark = ","))
# [1] 1.23

# 使用parse_number处理货币和百分比,或提取文本中的数值
parse_number("$100")
# [1] 100
parse_number("20%")
# [1] 20
parse_number("It costs $123.45")
# [1] 123.45

# 组合使用parse_number()和locale可以忽略分组符号
parse_number("123.456.789", locale = locale(grouping_mark = “."))
11.3.2 字符串解析
通过charToRaw()获得一个字符串的底层编码。
[AppleScript] 纯文本查看 复制代码
charToRaw("Genetics")[/color][/size][/font][/align][font=Arial][size=2]# [1] 47 65 6e 65 74 69 63 73
# i.e. using ASCII to represent English characters
readr全面支持UTF-8,默认数据按UTF-8编码,若不支持UTF-8的旧系统产生的数据时,打印出来会是一堆乱码,需在parse_character()函数中指明编码方式。
[AppleScript] 纯文本查看 复制代码
x1 <- "El Ni \xf1o was particularly good"[/size][/font]
[font=Arial][size=2]x2 <- "\x82\xb1\x82\xf1"
parse_character(x1, locale = locale(encoding = "Latin1"))
# [1] "El Ni ño was particularly good”
parse_character(x2, locale = locale(encoding = "Shift-JIS"))
# [1] “こん"
  
readr包的guess_encoding()函数可用于寻找数据文档的编码方式。
[AppleScript] 纯文本查看 复制代码
guess_encoding(charToRaw(x1))[/color][/size][/font][/align][font=Arial][size=2]# A tibble: 1 x 2
# encoding confidence
# <chr> <dbl>
# 1 ISO-8859-1 0.48
  
但guess_encoding()函数并非万物一失,文本少时不容易提供线索,如
[AppleScript] 纯文本查看 复制代码
guess_encoding(charToRaw(x2))[/color][/size][/font][/align][font=Arial][size=2]# A tibble: 0 x 2
# ... with 2 variables: encoding <chr>, confidence <dbl>

x3 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"
parse_character(x3, locale = locale(encoding = "Shift-JIS"))
# [1] "こんにちは"
guess_encoding(charToRaw(x3))
# A tibble: 1 x 2
# encoding confidence
# <chr> <dbl>
# 1 KOI8-R 0.42
#可以看到,x3变量较x2有更多的文本信息时,guess_encoding()函数则提供了关于x3的可能的编码方式的线索。
# guess_encoding()函数的第一个参数可以是本例的原始向量,也可以是一个文本路径。
  

11.3.3 因子
  
R使用因子来表示取值范围已知的分类变量。若parse_factor()函数的level参数被赋予一个已知向量,那么只要遇到向量中没有的值,就会输出警告信息。
[AppleScript] 纯文本查看 复制代码
fruit <- c("apple", “banana")[/color][/size][/font][/align][font=Arial][size=2]parse_factor(c("apple", "banana", "bananas"), levels = fruit)
# Warning: 1 parsing failure.
# row # A tibble: 1 x 4 col row col expected actual expected <int> <int> <chr> <chr> actual 1 3 NA value in level set bananas

# [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 bananas
# Levels: apple banana
  
11.3.4 日期
[AppleScript] 纯文本查看 复制代码
parse_datetime("2010-10-01T2010")[/color][/size][/font][/align][font=Arial][size=2]# [1] "2010-10-01 20:10:00 UTC"
parse_datetime("2010-10-01")
# [1] "2010-10-01 UTC”
parse_date("2010-10-01")
# [1] “2010-10-01"
library(hms)
parse_time("01:01 am")
# 01:01:00
parse_time("20:10:01")
# 20:10:01
parse_date("01/10/01", "%d/%m/%y")
# [1] “2001-10-01"
parse_date("01/10/01", “%m/%d/%y")
# [1] “2001-01-10”
  
11.4 解析文件
11.4.1 策略
readr使用一种启发式(heuristic)算法来确定列的类型:先读取文件的前1000行,根据相对保守的启发式算法确定每列的类型。这个过程可通过调用guess_parser()函数使用字符向量模拟实现,函数将返回readr最可信的猜测;接着调用parse_guess()函数使用这个猜测来解析列。
[AppleScript] 纯文本查看 复制代码
guess_parser("2010-10-01")[/size][/font][/color][/align]# [1] "date"
guess_parser("15:01")
# [1] "time"
guess_parser(c("TRUE"))
# [1] "logical"
guess_parser(c("1", "5"))
# [1] "integer"
guess_parser(c("12, 352, 561"))
# [1] "character"
guess_parser(c("12,352,561"))
# [1] “number"
str(parse_guess("2010-10-01"))
# Date[1:1], format: “2010-10-01"
11.4.2 存在的问题
默认对列类型的解析不一定总适用于大文件。
  •   前1000行的猜测也许不够,你的文件可能有一列小数列在前1000行恰巧都是整数。
  •   列中可能含有大量缺失值,若前1000行都是NA,那么readr会猜测这是个字符向量。
  • [AppleScript] 纯文本查看 复制代码
    challenge <- read_csv(readr_example(“challenge.csv”))
    # cols(
    # x = col_integer(),
    # y = col_character()
    # )
    # Warning: 1000 parsing failures.
    # row # A tibble: 5 x 5 col ……
    problems(challenge)
    # A tibble: 1,000 x 5
    # row col expected actual file 
    # <int> <chr> <chr> <chr> <chr> 
    # 1 1001 x no trailing characters .23837975086644292 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 2 1002 x no trailing characters .41167997173033655 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 3 1003 x no trailing characters .7460716762579978 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 4 1004 x no trailing characters .723450553836301 ‘/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 5 1005 x no trailing characters .614524137461558 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 6 1006 x no trailing characters .473980569280684 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 7 1007 x no trailing characters .5784610391128808 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 8 1008 x no trailing characters .2415937229525298 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 9 1009 x no trailing characters .11437866208143532 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # 10 1010 x no trailing characters .2983446326106787 '/Users/xx/Library/R/3.4/library/readr/extdata/challenge.csv'
    # ... with 990 more rows
    解决办法:
    一列一列处理,直到解决所有问题。上述例子中的x列中有大量解析问题-整数后面有拖尾字符。说明我们应使用双精度解析函数。
    [AppleScript] 纯文本查看 复制代码
    # troubleshooting: 复制原始解析的列类型结果并将x列的类型修改为双精度[/font][/align]challenge <- read_csv(
    readr_example("challenge.csv"),
    col_types = cols(
    x = col_double(),
    y = col_character()
    )
    )
    # 至此,解决了x列类型解析错误的问题。
    
    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
    # 查看challenge的最后几行发现,保存在字符向量中的是日期数据
    # 同样按上述方法解决y列类型解析错误的问题,修改y列类型为date
    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_xx()函数都有对应的col_xx()函数。若数据已保存在R的字符向量中,那么你可以使用parse_xx(); 若想要告诉readr如何加载数据,则使用col_xx()。保持使用col_types()参数的习惯,从readr打印出的输出中可以知道它的值,以确保数据导入脚本的一致性。使用stop_for_problems()函数可以保证当出现解析错误时,它会报错并终止脚本。

11.4.3 其他策略
其他几种有助于解析文件的策略:
    比默认的检查前1000行多一行。
  •   
    [AppleScript] 纯文本查看 复制代码
    challenge2 <- read_csv(readr_example("challenge.csv"), guess_max = 10001)
    # 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
  •     将所有列视作字符向量。结合type_convert()函数尤其有效,后者可在字符列应用启发式解析过程。
    [AppleScript] 纯文本查看 复制代码
    challenge2 <- read_csv(readr_example("challenge.csv"), col_types = cols(.default = col_character()))
    tail(challenge2)
    # A tibble: 6 x 2
    # x y 
    # <chr> <chr> 
    # 1 0.805274312151596 2019-11-21
    # 2 0.1635163405444473 2018-03-29
    # 3 0.47193897631950676 2014-08-04
    # 4 0.7183186465408653 2015-08-16
    # 5 0.26987858884967864 2020-02-04
    # 6 0.608237189007923 2019-01-06
    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_integer(),
    # y = col_double()
    # )
    # A tibble: 3 x 2
    # x y
    # <int> <dbl>
    # 1 1 1.21
    # 2 2 2.32
    # 3 3 4.56
  • 若正在读取一个非常大的文件,设置n_max为一个较小的数值,如10,000或100,000,这可以加快重复测试的过程。遇到严重的解析问题时,使用read_lines()按行读入字符向量更容易,或使用read_file()读入长度为1的字符向量。再使用后面学到的“字符串解析”来解析各式的数据形式。



11.5 写入文件
readr提供write_csv()和write_tsv()函数用于向磁盘写入数据。如果导出csv文件至Excel,使用write_excel_csv()函数。
[AppleScript] 纯文本查看 复制代码
write_csv(challenge, "challenge.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

# 上述例子说明,当数据保存为csv文件时,类型信息丢失了,以下有两种解决办法。

# troubleshooting 1: write_rds()和read_rds函数是对基础函数readRDS()和saveRDS()的统一包装,前者将数据存为R自定义的二进制格式,即RDS格式。RDS支持列表列,feather目前不行。
write_rds(challenge, "challenge.rds")
read_rds("challenge.rds")
# 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

# troubleshooting 2: feather包实现了一种快速二进制文件格式,可在多个编程语言间共享。feather比RDS速度更快,且可在R之外使用。
library(feather)
write_feather(challenge, "challenge.feather")
read_feather("challenge.feather")
# 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
11.6 其他类型的数据
1. 矩形数据 rectangular data
    haven读取SPSS, Stata, SAS
    readxi读取Excel
2. 层次数据 hierarchical data
    jsonlite读取 json串
    xml2读取XML文件

可学习的相关资源:






上一篇:Markdown + pandoc
下一篇:qqplot
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|生信技能树 ( 粤ICP备15016384号  

GMT+8, 2019-10-21 09:23 , Processed in 0.047827 second(s), 28 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.