R数据科学chapter8

library(tidyverse)

#使用readr进行数据导入

#• read_csv() 读取逗号分隔文件、

#read_csv2() 读取分号分隔文件(这在用 , 表示小数位的国家非常普遍)、

#read_tsv() 读取制表符分隔文件、

#read_delim() 可以读取使用任意分隔符的文件。

#• read_fwf() 读取固定宽度的文件。

#既可以使用 fwf_widths() 函数按照宽度来设定域,也可以使用fwf_positions() 函数按照位置来设定域。

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

#• read_log() 读取 Apache 风格的日志文件。(但需要检查是否安装了 webreadr 包,https:// github.com/Ironholds/webreadr,因为该包位于 read_log() 函数的开头,还可以提供很多 有用的工具。)

#read_csv() 函数的第一个参数是最重要的,该参数是要读取的文件的路径

#当运行read_csv() 时,它会打印一份数据列说明,给出每个列的名称和类型。

read_csv("a,b,c

1,2,3

4,5,6")

#read_csv() 函数都使用数据的第一行作为列名称,这是一种常见做法。

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

read_csv("The first line of metadata

      The second line of metadata

      x,y,z

      1,2,3", skip = 2)

read_csv("# A comment I want to skip

      x,y,z

      1,2,3", comment = "#")

#• 数据没有列名称。可以使用col_names = FALSE来通知read_csv()不要将第一行作为列标题,而是将各列依次标注为 X1 至 Xn:

read_csv("1,2,3\n4,5,6", col_names = FALSE)

#"\n" 是非常便捷的快捷方式,用于添加新行

#你也可以向 col_names 传递一个字符向量,以用作列名称

read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))

#另一个通常需要修改的选项是 na。它设定使用哪个值(或哪些值)来表示文件中的缺失值:

read_csv("a,b,c\n1,2,.", na = ".")

#你肯定很想知道我们为什么不使用 read.csv() 函数

#• 一般来说,它们比基础模块中的函数速度更快(约快 10 倍)。

#• 它们可以生成 tibble,并且不会将字符向量转换为因子,不使用行名称,也不会随意改动列名称。

#• 它们更易于重复使用。

#解析向量

#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"

#第一个参数是需要解析的字符向量,

#na 参数设定了哪些字符串应该当作缺失值来处理

parse_integer(c("1", "231", ".", "456"), na = ".")

#> [1] 1 231 NA 456

#如果解析失败,你会收到一条警告:

  x <- parse_integer(c("123", "345", "abc", "123.45"))

#> Warning: 2 parsing failures.

#> row col expected actual

#解析失败的值在输出中是以缺失值的形式存在的

  x

#如果解析失败的值很多,那么就应该使用 problems() 函数来获取完整的失败信息集合。

#这个函数会返回一个 tibble,你可以使用 dplyr 包来进行处理

  problems(x)

#在解析函数的使用方面,最重要的是要知道有哪些解析函数,

#以及每种解析函数用来处理 哪种类型的输入。

#具体来说,重要的解析函数有 8 种。

#• parse_logical() 和 parse_integer() 函数分别解析逻辑值和整数。因为这两个解析函数基本不会出现问题,所以我们不再进行更多介绍。

#• parse_double() 是严格的数值型解析函数,parse_number() 则是灵活的数值型解析函数。 这两个函数要比你预想的更复杂,因为世界各地书写数值的方式不尽相同。

#• parse_character() 函数似乎太过简单,甚至没必要存在。但一个棘手的问题使得这个函数变得非常重要:字符编码。

#• parse_factor() 函数可以创建因子,R 使用这种数据结构来表示分类变量,该变量具有固定数目的已知值。

#• parse_datetime()、parse_date() 和 parse_time() 函数可以解析不同类型的日期和时间。 它们是最复杂的,因为有太多不同的日期书写形式。

#数值

#• 世界各地的人们书写数值的方式不尽相同。例如,有些国家使用 . 来分隔实数中的整数 和小数部分,而有些国家则使用 ,。

#• 数值周围经常有表示某种意义的其他字符,如 $1000 或 10%。

#• 数值经常包含“分组”,以便更易读,如 1 000 000,而且世界各地用来分组的字符也不尽相同。

#readr 使用了“地区”这一概念,这是可以按照不同地区设置解析选项的一个对象。

#在解析数值时,最重要的选项就是用来表示小数点的字符。

#通过创建一个新的地区对象并设定 decimal_mark 参数,可以覆盖 . 的默认值

parse_double("1.23")

parse_double("1,23", locale = locale(decimal_mark = ","))

#readr 的默认地区是 US-centric,因为 R 是以美国为中心的(也就是说,R 基础包的文档是用美式英语写成的)。

#parse_number() 解决了第二个问题:它可以忽略数值前后的非数值型字符。

#这个函数特别适合处理货币和百分比,也可以提取嵌在文本中的数值:

parse_number("$100")

#> [1] 100

parse_number("20%")

#> [1] 20

parse_number("It cost $123.45")

#> [1] 123

#组合使用 parse_number() 和地区设置可以解决最后一个问题,因为 parse_number() 可以忽略“分组符号”:

# 适用于美国

parse_number("$123,456,789")

#> [1] 1.23e+08

# 适用于多数欧洲国家

parse_number(

"123.456.789",

locale = locale(grouping_mark = ".")

)

#> [1] 1.23e+08

# 适用于瑞士

parse_number(

"123'456'789",

locale = locale(grouping_mark = "'")

)

#> [1] 1.23e+08

#字符串

#parse_character() 函数似乎真的很简单,只要返回输入值就可以了。

#问题是生活没这么简单,因为同一个字符串有多种表示方式。

#在 R 中,我们可以使用 charToRaw() 函数获得一个字符串的底层表示

charToRaw("Hadley")

#> [1] 48 61 64 6c 65 79

#每个十六进制数表示信息的一个字节:48 是 H、61 是 a 等。

#从十六进制数到字符的这种映射称为编码,这个示例中的编码方式称为 ASCII。

#UTF-8 可以为现在人类使用的所有字符进行编码,同时还支持很多特殊字符(如 表情符号!)

#readr 全面支持 UTF-8:当读取数据时,它假设数据是 UTF-8 编码的,并总是使用 UTF-8 编码写入数据。

#但对于从不支持 UTF-8 的那些旧系统中产生的数 据则无能为力。遇到这种情况时,你的字符串打印出来就是一堆乱码。

x1 <- "El Ni\xf1o was particularly bad this year"

x2 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"

#要想解决这个问题,需要在 parse_character() 函数中设定编码方式:

  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] "こんにちは"

#如何才能找到正确的编码方式呢?

#因此 readr 提供了 guess_encoding() 函数来帮助你找出编码方式。

#希望试验几次后,你就能够找到正确的编码方式:

guess_encoding(charToRaw(x1))

#> encoding confidence

#> 1 ISO-8859-1 0.46

#> 2 ISO-8859-9 0.23

guess_encoding(charToRaw(x2))

#> encoding confidence

#> 1  KOI8-R      0.42

#guess_encoding() 的第一个参数可以是一个文件路径,也可以是一个原始向量(适用于字 符串已经在 R 中的情况),就像本示例一样。

#因子

#R 使用因子表示取值范围是已知集合的分类变量。

#如果 parse_factor() 函数的 levels 参数被赋予一个已知向量,

#那么只要存在向量中没有的值,就会生成一条警告:

fruit <- c("apple", "banana")

parse_factor(c("apple", "banana", "bananana"), levels = fruit)

#日期、日期时间与时间

#根据需要的是日期型数据(从 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"

challenge <- read_csv(

  readr_example("challenge.csv"),

  col_types = cols(

    x = col_double(),

    y = col_date()

  )

)

tail(challenge)

#解析文件

#• readr 如何自动猜出文件每列的数据类型。

#• 如何修改默认设置。

#强烈建议你总是提供 col_types 参数,从 readr 打印出的输出中可以知道它的值。

#这可以确保数据导入脚本的一致性,并可以重复使用。

#这种方式结合 type_convert() 函数使用时特别有效,后者可以在数据框的字符列上应用启发式解析过程

df <- tribble(

  ~x,  ~y,

  "1", "1.21",

  "2", "2.32",

  "3", "4.56"

)

df

type_convert(df)

read_csv("x,y

        1, 1.21,

        2, 2.32,

        3, 4.56",col_types = cols(

          x = col_integer(),

          y = col_double()))

#write_csv() 和 write_tsv()

#• 它们总是使用 UTF-8 对字符串进行编码;

#• 它们使用 ISO 8601 格式来保存日期和日期时间数据,以便这些数据不论在何种环境下都更容易解析。

#如果想要将 CSV 文件导为 Excel 文件,可以使用 write_excel_csv() 函数,

#该函数会在文件开头写入一个特殊字符(字节顺序标记),告诉 Excel 这个文件使用的是 UTF-8 编码。

#这几个函数中最重要的参数是 x(要保存的数据框)和 path(保存文件的位置)。

#na 参数设定如何写入缺失值,如果想要追加到现有的文件,需要设置 append 参数

write_csv(challenge, "challenge.csv")

#注意,当保存为 CSV 文件时,类型信息就丢失了

#这使得 CSV 文件在暂存临时结果时有些不可靠——每次加载时都要重建列类型。

#• write_rds() 和 read_rds() 函数是对基础函数 readRDS() 和 saveRDS() 的统一包装。

#前者可以将数据保存为 R 自定义的二进制格式,称为 RDS 格式

write_rds(challenge, "challenge.rds")

read_rds("challenge.rds")

#feather 包实现了一种快速二进制格式,可以在多个编程语言间共享:

library(feather)

write_feather(challenge, "challenge.feather")

read_feather("challenge.feather")

#feather 要比 RDS 速度更快,而且可以在 R 之外使用。

#RDS 支持列表列(我们将在第 19 章 中介绍),feather 目前还不行。

#其他类型的数据

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

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

#• 配合专用的数据库后端程序(如 RMySQL、RSQLite、RPostgreSQL 等),DBI 可以对相

#应数据库进行 SQL 查询,并返回一个数据框。

#对于层次数据,可以使用 jsonlite(由 JeroenOoms 开发)读取 JSON 串,使用 xml2 读取XML 文件。

你可能感兴趣的:(R数据科学chapter8)