library(tidyverse)
#使用readr進(jìn)行數(shù)據(jù)導(dǎo)入
#? read_csv() 讀取逗號(hào)分隔文件狡孔、
#read_csv2() 讀取分號(hào)分隔文件(這在用 , 表示小數(shù)位的國(guó)家非常普遍)讯泣、
#read_tsv() 讀取制表符分隔文件、
#read_delim() 可以讀取使用任意分隔符的文件矿辽。
#? read_fwf() 讀取固定寬度的文件竞膳。
#既可以使用 fwf_widths() 函數(shù)按照寬度來設(shè)定域渗钉,也可以使用fwf_positions() 函數(shù)按照位置來設(shè)定域彤恶。
#read_table() 讀取固定寬度文件的一種常用變體钞钙,其中使用空白字符來分隔各列。
#? read_log() 讀取 Apache 風(fēng)格的日志文件声离。(但需要檢查是否安裝了 webreadr 包芒炼,https:// github.com/Ironholds/webreadr,因?yàn)樵摪挥?read_log() 函數(shù)的開頭术徊,還可以提供很多 有用的工具本刽。)
#read_csv() 函數(shù)的第一個(gè)參數(shù)是最重要的,該參數(shù)是要讀取的文件的路徑
#當(dāng)運(yùn)行read_csv() 時(shí)赠涮,它會(huì)打印一份數(shù)據(jù)列說明子寓,給出每個(gè)列的名稱和類型。
read_csv("a,b,c
1,2,3
4,5,6")
#read_csv() 函數(shù)都使用數(shù)據(jù)的第一行作為列名稱笋除,這是一種常見做法斜友。
#? 有時(shí)文件開頭會(huì)有好幾行元數(shù)據(jù)。你可以使用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 = "#")
#? 數(shù)據(jù)沒有列名稱垃它∠势粒可以使用col_names = FALSE來通知read_csv()不要將第一行作為列標(biāo)題,而是將各列依次標(biāo)注為 X1 至 Xn:
read_csv("1,2,3\n4,5,6", col_names = FALSE)
#"\n" 是非常便捷的快捷方式国拇,用于添加新行
#你也可以向 col_names 傳遞一個(gè)字符向量墙歪,以用作列名稱
read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))
#另一個(gè)通常需要修改的選項(xiàng)是 na。它設(shè)定使用哪個(gè)值(或哪些值)來表示文件中的缺失值:
read_csv("a,b,c\n1,2,.", na = ".")
#你肯定很想知道我們?yōu)槭裁床皇褂?read.csv() 函數(shù)
#? 一般來說贝奇,它們比基礎(chǔ)模塊中的函數(shù)速度更快(約快 10 倍)。
#? 它們可以生成 tibble靠胜,并且不會(huì)將字符向量轉(zhuǎn)換為因子掉瞳,不使用行名稱,也不會(huì)隨意改動(dòng)列名稱浪漠。
#? 它們更易于重復(fù)使用陕习。
#解析向量
#parse_*() 函數(shù)族。
#這些函數(shù)接受一個(gè)字符向量址愿,并返回一個(gè)特定向量该镣,如邏輯、整數(shù)或日期向量:
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"
#第一個(gè)參數(shù)是需要解析的字符向量响谓,
#na 參數(shù)設(shè)定了哪些字符串應(yīng)該當(dāng)作缺失值來處理
parse_integer(c("1", "231", ".", "456"), na = ".")
#> [1] 1 231 NA 456
#如果解析失敗损合,你會(huì)收到一條警告:
? x <- parse_integer(c("123", "345", "abc", "123.45"))
#> Warning: 2 parsing failures.
#> row col expected actual
#解析失敗的值在輸出中是以缺失值的形式存在的
? x
#如果解析失敗的值很多,那么就應(yīng)該使用 problems() 函數(shù)來獲取完整的失敗信息集合娘纷。
#這個(gè)函數(shù)會(huì)返回一個(gè) tibble嫁审,你可以使用 dplyr 包來進(jìn)行處理
? problems(x)
#在解析函數(shù)的使用方面,最重要的是要知道有哪些解析函數(shù)赖晶,
#以及每種解析函數(shù)用來處理 哪種類型的輸入律适。
#具體來說,重要的解析函數(shù)有 8 種。
#? parse_logical() 和 parse_integer() 函數(shù)分別解析邏輯值和整數(shù)捂贿。因?yàn)檫@兩個(gè)解析函數(shù)基本不會(huì)出現(xiàn)問題纠修,所以我們不再進(jìn)行更多介紹。
#? parse_double() 是嚴(yán)格的數(shù)值型解析函數(shù)厂僧,parse_number() 則是靈活的數(shù)值型解析函數(shù)扣草。 這兩個(gè)函數(shù)要比你預(yù)想的更復(fù)雜,因?yàn)槭澜绺鞯貢鴮憯?shù)值的方式不盡相同吁系。
#? parse_character() 函數(shù)似乎太過簡(jiǎn)單德召,甚至沒必要存在。但一個(gè)棘手的問題使得這個(gè)函數(shù)變得非常重要:字符編碼汽纤。
#? parse_factor() 函數(shù)可以創(chuàng)建因子上岗,R 使用這種數(shù)據(jù)結(jié)構(gòu)來表示分類變量,該變量具有固定數(shù)目的已知值蕴坪。
#? parse_datetime()肴掷、parse_date() 和 parse_time() 函數(shù)可以解析不同類型的日期和時(shí)間。 它們是最復(fù)雜的背传,因?yàn)橛刑嗖煌娜掌跁鴮懶问健?/p>
#數(shù)值
#? 世界各地的人們書寫數(shù)值的方式不盡相同呆瞻。例如,有些國(guó)家使用 . 來分隔實(shí)數(shù)中的整數(shù) 和小數(shù)部分径玖,而有些國(guó)家則使用 ,痴脾。
#? 數(shù)值周圍經(jīng)常有表示某種意義的其他字符,如 $1000 或 10%梳星。
#? 數(shù)值經(jīng)常包含“分組”赞赖,以便更易讀,如 1 000 000冤灾,而且世界各地用來分組的字符也不盡相同前域。
#readr 使用了“地區(qū)”這一概念,這是可以按照不同地區(qū)設(shè)置解析選項(xiàng)的一個(gè)對(duì)象韵吨。
#在解析數(shù)值時(shí)匿垄,最重要的選項(xiàng)就是用來表示小數(shù)點(diǎn)的字符。
#通過創(chuàng)建一個(gè)新的地區(qū)對(duì)象并設(shè)定 decimal_mark 參數(shù)归粉,可以覆蓋 . 的默認(rèn)值
parse_double("1.23")
parse_double("1,23", locale = locale(decimal_mark = ","))
#readr 的默認(rèn)地區(qū)是 US-centric椿疗,因?yàn)?R 是以美國(guó)為中心的(也就是說,R 基礎(chǔ)包的文檔是用美式英語寫成的)盏浇。
#parse_number() 解決了第二個(gè)問題:它可以忽略數(shù)值前后的非數(shù)值型字符变丧。
#這個(gè)函數(shù)特別適合處理貨幣和百分比,也可以提取嵌在文本中的數(shù)值:
parse_number("$100")
#> [1] 100
parse_number("20%")
#> [1] 20
parse_number("It cost $123.45")
#> [1] 123
#組合使用 parse_number() 和地區(qū)設(shè)置可以解決最后一個(gè)問題绢掰,因?yàn)?parse_number() 可以忽略“分組符號(hào)”:
# 適用于美國(guó)
parse_number("$123,456,789")
#> [1] 1.23e+08
# 適用于多數(shù)歐洲國(guó)家
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() 函數(shù)似乎真的很簡(jiǎn)單痒蓬,只要返回輸入值就可以了童擎。
#問題是生活沒這么簡(jiǎn)單,因?yàn)橥粋€(gè)字符串有多種表示方式攻晒。
#在 R 中顾复,我們可以使用 charToRaw() 函數(shù)獲得一個(gè)字符串的底層表示
charToRaw("Hadley")
#> [1] 48 61 64 6c 65 79
#每個(gè)十六進(jìn)制數(shù)表示信息的一個(gè)字節(jié):48 是 H、61 是 a 等鲁捏。
#從十六進(jìn)制數(shù)到字符的這種映射稱為編碼芯砸,這個(gè)示例中的編碼方式稱為 ASCII。
#UTF-8 可以為現(xiàn)在人類使用的所有字符進(jìn)行編碼给梅,同時(shí)還支持很多特殊字符(如 表情符號(hào)!)
#readr 全面支持 UTF-8:當(dāng)讀取數(shù)據(jù)時(shí)假丧,它假設(shè)數(shù)據(jù)是 UTF-8 編碼的,并總是使用 UTF-8 編碼寫入數(shù)據(jù)动羽。
#但對(duì)于從不支持 UTF-8 的那些舊系統(tǒng)中產(chǎn)生的數(shù) 據(jù)則無能為力包帚。遇到這種情況時(shí),你的字符串打印出來就是一堆亂碼运吓。
x1 <- "El Ni\xf1o was particularly bad this year"
x2 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"
#要想解決這個(gè)問題渴邦,需要在 parse_character() 函數(shù)中設(shè)定編碼方式:
? 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() 函數(shù)來幫助你找出編碼方式。
#希望試驗(yàn)幾次后拘哨,你就能夠找到正確的編碼方式:
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() 的第一個(gè)參數(shù)可以是一個(gè)文件路徑谋梭,也可以是一個(gè)原始向量(適用于字 符串已經(jīng)在 R 中的情況),就像本示例一樣倦青。
#因子
#R 使用因子表示取值范圍是已知集合的分類變量瓮床。
#如果 parse_factor() 函數(shù)的 levels 參數(shù)被賦予一個(gè)已知向量,
#那么只要存在向量中沒有的值产镐,就會(huì)生成一條警告:
fruit <- c("apple", "banana")
parse_factor(c("apple", "banana", "bananana"), levels = fruit)
#日期纤垂、日期時(shí)間與時(shí)間
#根據(jù)需要的是日期型數(shù)據(jù)(從 1970-01-01 開始的天數(shù))、
#日期時(shí)間型數(shù)據(jù)(從 1970-01-01 午夜開始的秒數(shù))磷账,
#或者是時(shí)間型數(shù)據(jù)(從午夜開始的秒數(shù))
#? parse_datetime() 期待的是符合ISO 8601 標(biāo)準(zhǔn)的日期時(shí)間。
#ISO 8601 是一種國(guó)際標(biāo)準(zhǔn)贾虽, 其中日期的各個(gè)部分按從大到小的順序排列逃糟,即年、月蓬豁、日绰咽、小時(shí)、分鐘地粪、秒:
parse_datetime("2010-10-01T2010")
#> [1] "2010-10-01 20:10:00 UTC"
# 如果時(shí)間被省略了取募,那么它就會(huì)被設(shè)置為午夜
parse_datetime("20101010")
#> [1] "2010-10-10 UTC"
#這是最重要的日期 / 時(shí)間標(biāo)準(zhǔn)
#? parse_date() 期待的是四位數(shù)的年份、一個(gè) - 或 /蟆技、月玩敏、一個(gè) - 或 /斗忌,然后是日:
parse_date("2010-10-01")
#> [1] "2010-10-01"
#? parse_time() 期待的是小時(shí)、:旺聚、分鐘织阳、可選的 : 和秒,以及一個(gè)可選的 a.m./p.m. 標(biāo)識(shí)符:
library(hms)
parse_time("01:10 am")
#> 01:10:00
parse_time("20:10:01")
#> 20:10:01
#找出正確格式的最好方法是創(chuàng)建幾個(gè)解析字符向量的示例砰粹,并使用某種解析函數(shù)進(jìn)行測(cè)
試唧躲。例如:
? 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 如何自動(dòng)猜出文件每列的數(shù)據(jù)類型。
#? 如何修改默認(rèn)設(shè)置碱璃。
#強(qiáng)烈建議你總是提供 col_types 參數(shù)弄痹,從 readr 打印出的輸出中可以知道它的值。
#這可以確保數(shù)據(jù)導(dǎo)入腳本的一致性嵌器,并可以重復(fù)使用肛真。
#這種方式結(jié)合 type_convert() 函數(shù)使用時(shí)特別有效,后者可以在數(shù)據(jù)框的字符列上應(yīng)用啟發(fā)式解析過程
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 對(duì)字符串進(jìn)行編碼;
#? 它們使用 ISO 8601 格式來保存日期和日期時(shí)間數(shù)據(jù)嘴秸,以便這些數(shù)據(jù)不論在何種環(huán)境下都更容易解析毁欣。
#如果想要將 CSV 文件導(dǎo)為 Excel 文件,可以使用 write_excel_csv() 函數(shù)岳掐,
#該函數(shù)會(huì)在文件開頭寫入一個(gè)特殊字符(字節(jié)順序標(biāo)記)凭疮,告訴 Excel 這個(gè)文件使用的是 UTF-8 編碼。
#這幾個(gè)函數(shù)中最重要的參數(shù)是 x(要保存的數(shù)據(jù)框)和 path(保存文件的位置)串述。
#na 參數(shù)設(shè)定如何寫入缺失值执解,如果想要追加到現(xiàn)有的文件,需要設(shè)置 append 參數(shù)
write_csv(challenge, "challenge.csv")
#注意纲酗,當(dāng)保存為 CSV 文件時(shí)衰腌,類型信息就丟失了
#這使得 CSV 文件在暫存臨時(shí)結(jié)果時(shí)有些不可靠——每次加載時(shí)都要重建列類型。
#? write_rds() 和 read_rds() 函數(shù)是對(duì)基礎(chǔ)函數(shù) readRDS() 和 saveRDS() 的統(tǒng)一包裝觅赊。
#前者可以將數(shù)據(jù)保存為 R 自定義的二進(jìn)制格式右蕊,稱為 RDS 格式
write_rds(challenge, "challenge.rds")
read_rds("challenge.rds")
#feather 包實(shí)現(xiàn)了一種快速二進(jìn)制格式,可以在多個(gè)編程語言間共享:
library(feather)
write_feather(challenge, "challenge.feather")
read_feather("challenge.feather")
#feather 要比 RDS 速度更快吮螺,而且可以在 R 之外使用饶囚。
#RDS 支持列表列(我們將在第 19 章 中介紹),feather 目前還不行鸠补。
#其他類型的數(shù)據(jù)
#? haven 可以讀取 SPSS萝风、Stata 和 SAS 文件;
#? readxl 可以讀取 Excel 文件(.xls 和 .xlsx 均可);
#? 配合專用的數(shù)據(jù)庫后端程序(如 RMySQL、RSQLite紫岩、RPostgreSQL 等)规惰,DBI 可以對(duì)相
#應(yīng)數(shù)據(jù)庫進(jìn)行 SQL 查詢,并返回一個(gè)數(shù)據(jù)框泉蝌。
#對(duì)于層次數(shù)據(jù)歇万,可以使用 jsonlite(由 JeroenOoms 開發(fā))讀取 JSON 串揩晴,使用 xml2 讀取XML 文件。