[toc] 写在前面数据导入是使用R语言分析数据的第一步,但是这部分的细节确实非常多,尤其是对于咱们微生物组数据来说,经常会遇到制表符和逗号作为分隔符的文件,其次,物种注释文件的七级注释往往使用分号和“|”作为分隔符,如何在不做修改的情况下进行文件导入,值得我们学习。其次我们经常通过biom转化的文件开头带有“#”,如何忽略或者修正这个符号也十分重要。下面我将带领大家学习本小结内容,如何调整并导入文件。 文件的导入,R会定义一个字符类型,这些类型主要包括:字符,数值,因子等我们常用的类型,如何解析是一个问题,尤其是在R语言解析错误的时候,例如常见的因子解析成了字符串,还有一些奇怪的问题,例如:美元符号,数字的科学计数法,数字之间的逗号分隔,日期等这些数据如何解析,日常遇到的比较少,但是一旦遇到这个问题几乎是致命的,那就来学习吧。 如何将纯文本格式的矩形文件读入 R? readr 也是 tidyverse 的核心R包之一。通过
library(tidyverse)导入 数据导入1. 读取文件-从文件读入为数据框(1) 相关函数 (2/1)用法1:读取现有文件,以read_csv()函数为例。第一个参数文件路径。函数运行时会打印一份数据列说明,给出每个列的名称和类型。 (2/2)用法2:创建行内 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 (3)列名称 默认: 用数据的第一行作为列名称,如上面用法2中的例子; 用 skip = n 来跳过前 n行
read_csv("The first line of metadata The second line of metadata x,y,z 1,2,3", skip = 2)
# A tibble: 1 x 3 x y z <dbl> <dbl> <dbl> 1 1 2 3 read_csv("# A comment I want to skip x,y,z 1,2,3", comment = "#")
# A tibble: 1 x 3 x y z <dbl> <dbl> <dbl> 1 1 2 3 read_csv("1,2,3\n4,5,6", col_names = FALSE) #"\n"是换行符
# A tibble: 2 x 3 X1 X2 X3 <dbl> <dbl> <dbl> 1 1 2 3 2 4 5 6 read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))
# A tibble: 2 x 3 x y z <dbl> <dbl> <dbl> 1 1 2 3 2 4 5 6 read_csv("a,b,c\n1,2,.", na = ".")
# A tibble: 1 x 3 a b c <dbl> <dbl> <lgl> 1 1 2 NA 2. 解析向量-解析单个向量(1)重要函数-parse_*()函数族:接受一个字符向量,返回一个特定向量(如逻辑、整数或日期向量) ① 解析逻辑值:parse_logical() > parse_logical(c("TRUE", "FALSE", "NA")) [1] TRUE FALSE NA ② 解析整数:parse_integer() > parse_integer(c("1", "2", "3")) [1] 1 2 3 ③ 解析数值 # 问题:世界各地书写数值的方式不同,最重要的是用来表示小数点的字符不同(有的用“.”,有的用“,”) # 解决方法:readr使用“地区”这一概念可以按照不同地区设置解析选项,如创建一个新的地区对象并设定decimal_mark参数为“,”来覆盖默认值(“.”) parse_double("1.23") #> [1] 1.23 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 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] 123456789 ④ 解析字符串:parse_character() # 问题:字符串打印出来是乱码 # 解决办法:在parse_number()函数中设定编码方式。> 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] "こんにちは" # 问题:怎么知道字符串的正确编码方式?# 解决办法:用 charToRaw() 函数获得一个字符串的底层表示;然后用guess_encoding()函数找出编码方式 # guess_encoding() 的第一个参数可以是一个文件路径,也可以是一个原始向量
> guess_encoding(charToRaw(x1)) # A tibble: 2 x 2 encoding confidence <chr> <dbl> 1 ISO-8859-1 0.46 #Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。2 ISO-8859-9 0.23
> guess_encoding(charToRaw(x2)) # A tibble: 1 x 2 encoding confidence <chr> <dbl> 1 KOI8-R 0.42 ⑤ 解析因子:parse_factor() # R 使用因子表示取值范围是已知集合的分类变量
> fruit <- c("apple", "banana") > parse_factor(c("apple", "banana", "bananana")) [1] apple banana bananana Levels: apple banana bananana ⑥ 解析时间 ISO 8601:是一种国际标准,
其中日期的各个部分按从大到小的顺序排列,即年(4位数)、月(2位数)、日(2位数)、小时、分钟、秒:日期时间组合表示时在时间前加T > parse_datetime("20101010") [1] "2010-10-10 UTC"
> parse_date("2010-10-01") [1] "2010-10-01"
> parse_time("20:10:01") 20:10:01 如果默认设置不适合,可以提供自己的日期时间格式,格式由以下部分组成:- 年:%Y(4 位数);%y(2 位数) - 月:%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(时区);%z(与国际标准时间的时差,如+0800)。- 非数值字符:%.(跳过一个非数值字符);%*(跳过所有非数值字符)。> parse_date("01/02/15", "%m/%d/%y") [1] "2015-01-02"
# 如果对非英语月份名称使用 %b 或 %B,那么你就需要在 locale() 函数中设置 lang 参数。> parse_date("1 janvier 2015", "%d %B %Y", locale = locale("fr")) [1] "2015-01-01" 3. 解析文件-readr如何解析文件(综合使用解析函数来解析文件)(1)readr 如何自动猜出文件每列的数据类型? 根据前面的行猜测:先读取文件的前 1000 行,然后使用(相对保
守的)某种启发式算法确定每列的类型。 方法:# 先用 guess_parser() 函数返回 readr 最可信的猜测 > guess_parser("2010-10-01") [1] "date"
# 接着 parse_guess() 函数使用这个猜测来解析列 > 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 标准的任何日期。- 如果以上类型均不匹配,那么这一列就还是一个字符串向量 (2)默认设置无效时如何修改默认设置? 问题1:前1000行不足以代表整个文件,如一列双精度数值的前 1000 行可能都是整数,按前1000行解析的话是整数,但实际是双精度数值。 challenge <- read_csv(readr_example("challenge.csv"))
# 用默认设置解析出来的列类型为x-integer,y-character #> Parsed with column specification: #> cols( #> x = col_integer(), #> y = col_character() #> )
# 收到一条警告(warning),说明默认设置无效 #> Warning: 1000 parsing failures. #> row col expected actual #> 1001 x no trailing characters .23837975086644292 #> 1002 x no trailing characters .41167997173033655 #> 1003 x no trailing characters .7460716762579978 #> 1004 x no trailing characters .723450553836301 #> 1005 x no trailing characters .614524137461558 #> .... ... ...................... .................. #> See problems(...) for more details. 用 problems()函数明确地列出失败记录,一列列地处理,直至解决所有问题 # 从上述警告信息中看出, x列存在大量解析问题——整数后面有拖尾字符。这说明我们应该使用双精度解析函数
# 首先,复制列类型(x-integer,y-character)并将其粘贴到初始调用中 challenge <- read_csv( readr_example("challenge.csv"), col_types = cols( x = col_integer(), y = col_character() ) )
# 接着将x列类型由integer修改为double challenge <- read_csv( readr_example("challenge.csv"), col_types = cols( x = col_double(), y = col_character() ) )
> challenge # A tibble: 2,000 x 2 x y <int> <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 问题2:列中可能含有大量缺失值(如上面的例子)。如果前 1000 行中都是 NA,那么 readr 会猜测这是一个字符
向量,但其实想将这一列解析为更具体的值(查看后几行发现是日期数据)。 # 如果查看最后几行的话,会发现保存在字符向量中的其实是日期数据:tail(challenge) #> # A tibble: 6 × 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类型为date challenge <- read_csv( readr_example("challenge.csv"), col_types = cols( x = col_double(), y = col_date() ) ) tail(challenge) #> # A tibble: 6 × 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 (3)其他解析策略 # 比默认方式多检查几行 challenge2 <- read_csv( readr_example("challenge.csv"), guess_max = 1001 )
# 将所有列都作为字符向量读入,然后结合 type_convert() 函数使用(可以在数据框的字符列上应用启发式解析过程) df <- tribble( ~x, ~y, "1", "1.21", "2", "2.32", "3", "4.56" ) df #> # A tibble: 3 × 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 × 2 #> x y #> <int> <dbl> #> 1 1 1.21 #> 2 2 2.32 #> 3 3 4.56
# 读取一个非常大的文件时,将 n_max 设置为一个较小的数
# 如果遇到严重的解析问题,有时使用 read_lines() 函数按行读入字符向量会更容易,甚至可以使用 read_file() 函数读入一个长度为 1 的字符向量 4. 写入文件-将数据写回到磁盘(导出文件)(1)重要函数 write_csv() 函数: write_tsv() 函数: write_excel_csv() 函数: 将 CSV 文件导为 Excel 文件
(2)重要参数 x: 要保存的数据框 path: 保存文件的位置 na: 设定如何写入缺失值 append: 追加到现有的文件
(3)问题:当保存为 CSV 文件时,类型信息会丢失 # 替代方式1:用write_rds() 和 read_rds() 函数将数据保存为 R 自定义的二进制格式,称为 RDS 格式 > 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
# 替代方式2:用feather包中的write_feather() 和 read_feather() 函数实现一种快速二进制格式,可以在多个编程语言间共享 > library(feather) > write_feather(challenge, "challenge.feather") > read_feather("challenge.feather") 5. 其他类型的数据(1)对矩形数据: haven: 可以读取 SPSS、 Stata 和 SAS 文件; readxl: 可以读取 Excel 文件(.xls 和 .xlsx 均可) 配合专用的数据库后端程序, DBI 可以对相应数据库进行 SQL 查询,并返回一个数据框。
(2)对层次数据: (3)对其他类型数据:
|