R 程式設計/文字處理
此頁面包含處理 R 中字串所需的所有資料。即使您只需要執行一些簡單的任務,有關正則表示式的部分可能對理解頁面其餘內容也很有用。
此頁面可能對您有所幫助,可以:
- 進行統計文字分析。
- 從無格式的文字檔案中收集資料。
- 處理字元變數。
在本頁面中,我們將學習如何讀取文字檔案以及如何使用 R 函式處理字元。處理字元的函式有兩種,分別是簡單函式和正則表示式。許多函式是標準 R **base** 包的一部分。
help.search(keyword = "character", package = "base")
但是,對於所有使用者來說,它們的名稱和語法並不直觀。Hadley Wickham 開發了 **stringr** 包,該包定義了具有類似行為的函式,但它們的名稱更容易記住,語法也更系統化[1]。
- 關鍵字:文字挖掘、自然語言處理
- 請參閱 CRAN 上有關自然語言處理的任務檢視[2]
- 另請參見以下包 **tm**、**tau**、**languageR**、**scrapeR**。
讀取和寫入文字檔案
[edit | edit source]**R** 可以使用 readLines() 或 scan() 讀取任何文字檔案。可以使用 readLines() 指定匯入文字檔案的編碼。文字檔案的整個內容可以讀取到 R 物件中(例如,字元向量)。scan() 更加靈活。可以在第二個引數中指定預期的資料型別(例如,字元(0) 用於字串)。
text <- readLines("file.txt",encoding="UTF-8")
scan("file.txt", character(0)) # separate each word
scan("file.txt", character(0), quote = NULL) # get rid of quotes
scan("file.txt", character(0), sep = ".") # separate each sentence
scan("file.txt", character(0), sep = "\n") # separate each line
我們可以使用 cat() 或 writeLines() 將 R 物件的內容寫入文字檔案。預設情況下,cat() 在寫入文字檔案時會連線向量。您可以透過新增選項 sep="\n" 或 fill=TRUE 來更改它。預設編碼取決於您的計算機。
cat(text,file="file.txt",sep="\n")
writeLines(text, con = "file.txt", sep = "\n", useBytes = FALSE)
在讀取文字檔案之前,您可以檢視其屬性。nlines()(**parser** 包)和 countLines()(**R.utils** 包)計算檔案中的行數。count.chars()(**parser** 包)計算檔案中每行的位元組數和字元數。您也可以使用 file.show() 顯示文字檔案。
字元編碼
[edit | edit source]R 提供了處理各種編碼方案集的函式。如果您處理使用其他作業系統建立的文字檔案,尤其是在語言不是英語並且包含許多重音和特定字元的情況下,這很有用。例如,Linux 中的標準編碼方案是“UTF-8”,而 Windows 中的標準編碼方案是“Latin1”。Encoding() 函式返回字串的編碼。iconv() 與 unix 命令 iconv 相似,並轉換編碼。
iconvlist()提供計算機上可用的編碼方案列表。readLines()、scan()和file.show()也具有編碼選項。is.utf8()(**tau**)測試編碼是否為“utf8”。is.locale()(**tau**)測試編碼是否與計算機上的預設編碼相同。translate()(**tau**)將編碼轉換為當前區域設定。fromUTF8()(**descr**)的通用性低於iconv()。utf8ToInt()(**base**)
示例
[edit | edit source]以下示例在 Windows 下執行。因此,預設編碼為“latin1”。
> texte <- "Hé hé"
> Encoding(texte)
[1] "latin1"
> texte2 <- iconv(texte,"latin1","UTF-8")
> Encoding(texte2)
[1] "UTF-8"
正則表示式
[edit | edit source]正則表示式是字串集中的一種特定模式。例如,可以有以下模式:2 位數字、2 個字母和 4 位數字。**R** 提供了處理正則表示式的強大函式。**R** 中使用兩種型別的正則表示式[3]
- 擴充套件正則表示式,由
‘perl = FALSE’(預設值)使用, - Perl 風格的正則表示式,由
‘perl = TRUE’使用。
還有一個名為 ‘fixed = TRUE’ 的選項,可以將其視為字面正則表示式。fixed()(**stringr**)等效於標準正則表示式函式中的 fixed=TRUE。預設情況下,這些函式區分大小寫。可以透過指定選項 ignore.case = TRUE 來更改此設定。
如果您不是正則表示式專家,那麼您可能會發現 glob2rx() 很有用。此函式會為特定(“glob”或“萬用字元”)模式提供一些正則表示式建議
> glob2rx("abc.*")
[1] "^abc\\."
R 中使用正則表示式的函式
[edit | edit source]sub()、gsub()、str_replace()(**stringr**)對字串進行一些替換。grep()、str_extract()(**stringr**)提取一些值grepl()、str_detect()(**stringr**)檢測模式是否存在。- 另請參見
splitByPattern()(**R.utils**) - 另請參見 **gsubfn** 包中的
gsubfn()。
擴充套件正則表示式(預設值)
[edit | edit source]"."代表任何字元。"[ABC]"表示 A、B 或 C。"[A-Z]"表示 A 到 Z 之間的任何大寫字母。"[0-9]"表示 0 到 9 之間的任何數字。
以下是元字元 ‘$ * + . ? [ ] ^ { } | ( ) \’ 的列表。如果您需要使用其中一個字元,請在其前面加上雙反斜槓。
以下是正則表示式的一些類別:用於數字
‘[:digit:]’數字:‘0 1 2 3 4 5 6 7 8 9’。
用於字母
‘[:alpha:]’字母字元:‘[:lower:]’和‘[:upper:]’。‘[:upper:]’大寫字母。‘[:lower:]’小寫字母。
請注意,字母字元集包含諸如 é è ê 之類的重音,這些重音在法語等一些語言中非常常見。因此,它比 "[A-Za-z]" 更通用,後者不包含帶重音的字母。
用於其他字元
‘[:punct:]’標點符號:‘! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~’。‘[:space:]’空格字元:製表符、換行符、垂直製表符、換頁符、回車符和空格。‘[:blank:]’空白字元:空格和製表符。‘[:cntrl:]’控制字元。
其他類別的組合
[:alnum:]字母數字字元:‘[:alpha:]’和‘[:digit:]’。‘[:graph:]’圖形字元:‘[:alnum:]’和‘[:punct:]’。‘[:print:]’可列印字元:‘[:alnum:]’、‘[:punct:]’和空格。‘[:xdigit:]’十六進位制數字:‘0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f’。
你可以在正則表示式後新增以下字元來量化重複次數
‘?’前一項是可選的,最多匹配一次。‘*’前一項將被匹配零次或多次。‘+’前一項將被匹配一次或多次。‘{n}’前一項被精確匹配 'n' 次。‘{n,}’前一項被匹配 'n' 次或更多次。‘{n,m}’前一項至少被匹配 'n' 次,但不超過 'm' 次。
^強制正則表示式位於字串的開頭$強制正則表示式位於字串的結尾
如果你想了解更多,請檢視以下兩個幫助檔案
>?regexp # gives some general explanations
>?grep # help file for grep(),regexpr(),sub(),gsub(),etc
| 本節為殘缺內容。 你可以透過 擴充套件 來幫助 Wikibooks。 |
也可以使用“Perl 風格”的正則表示式。你只需要使用 perl=TRUE 選項。
如果你想刪除字串中的空格字元,可以使用 \\s Perl 宏。
sub('\\s', '',x, perl = TRUE)
paste()連線字串。str_c()(stringr) 執行類似的任務。cat()列印和連線字串。
> paste("toto","tata",sep=' ')
[1] "toto tata"
> paste("toto","tata",sep=",")
[1] "toto,tata"
> str_c("toto","tata",sep=",")
[1] "toto,tata"
> x <- c("a","b","c")
> paste(x,collapse=" ")
[1] "a b c"
> str_c(x, collapse = " ")
[1] "a b c"
> cat(c("a","b","c"), sep = "+")
a+b+c
strsplit(): 根據字元向量 'x' 中與子字串 'split' 的匹配結果,將 'x' 的元素拆分為子字串。- 另見
str_split()(stringr)。
> unlist(strsplit("a.b.c", "\\."))
[1] "a" "b" "c"
tokenize()(tau) 將字串拆分為標記。
> tokenize("abc defghk")
[1] "abc" " " "defghk"
nchar()給出字串的長度。注意,對於非 ASCII 編碼,存在多種測量長度的方法。- 另見
str_length()(stringr)
> nchar("abcdef")
[1] 6
> nchar(NA)
[1] NA
> nchar("René")
[1] 4
> nchar("René", type = "bytes")
[1] 5
grepl()返回一個邏輯表示式 (TRUE 或 FALSE)。str_detect()(stringr) 執行類似的任務。
> string <- "23 mai 2000"
> string2 <- "1 mai 2000"
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> grepl(pattern = regexp, x = string)
[1] TRUE
> str_detect(string, regexp)
[1] TRUE
> grepl(pattern = regexp, x = string2)
[1] FALSE
第一個為真,第二個為假,因為第一個數字中只有一個數字。
textcnt()(tau) 統計文字中每個模式或每個詞出現的次數。
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> textcnt(string,n=1L,method="string")
blabla mai
2 2
attr(,"class")
[1] "textcnt"
cpos()(cwhmisc) 返回子字串在字串中的位置。substring.location()(cwhmisc) 執行相同的任務,但返回第一個和最後一個位置。
> cpos("abcdefghijklmnopqrstuvwxyz","p",start=1)
[1] 16
> substring.location("abcdefghijklmnopqrstuvwxyz","def")
$first
[1] 4
$last
[1] 6
regexpr()返回正則表示式的起始位置。str_locate()(stringr) 執行相同的任務。gregexpr()類似於regexpr(),但返回每個匹配項的起始位置。str_locate_all()(stringr) 執行相同的任務。
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> regexpr(pattern = regexp, text = string)
[1] 8
attr(,"match.length")
[1] 11
> gregexpr(pattern = regexp, text = string)
[[1]]
[1] 8 27
attr(,"match.length")
[1] 11 11
> str_locate(string,regexp)
start end
[1,] 8 18
> str_locate_all(string,regexp)
[[1]]
start end
[1,] 8 18
[2,] 27 37
substr()獲取子字串。str_sub()(stringr) 類似。
> substr("simple text",1,3)
[1] "sim"
> str_sub("simple text",1,3)
[1] "sim"
first.word()Hmisc 包中字串或表示式的第一個詞
> first.word("abc def ghk")
[1] "abc"
- 如果
value=T,則grep()返回正則表示式的值,如果value=F,則返回其位置。
> grep(pattern = regexp, x = string , value = T)
[1] "23 mai 2000"
> grep(pattern = regexp, x = string2 , value = T)
character(0)
> grep(pattern = regexp, x = string , value = F)
[1] 1
> grep(pattern = regexp, x = string2 , value = F)
integer(0)
str_extract()、str_extract_all()、str_match()、str_match_all()(stringr) 和m()(caroline 包) 類似於grep()。str_extract()和str_extract_all()返回一個向量。str_match()和str_match_all()返回一個矩陣,而m()返回一個數據框。
> library("stringr")
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> str_extract(string,regexp)
[1] "23 mai 2000"
> str_extract_all(string,regexp)
[[1]]
[1] "23 mai 2000" "18 mai 2004"
> str_match(string,regexp)
[,1] [,2] [,3] [,4]
[1,] "23 mai 2000" "23" "mai" "2000"
> str_match_all(string,regexp)
[[1]]
[,1] [,2] [,3] [,4]
[1,] "23 mai 2000" "23" "mai" "2000"
[2,] "18 mai 2004" "18" "mai" "2004"
> library("caroline")
> m(pattern = regexp, vect = string, names = c("day","month","year"), types = rep("character",3))
day month year
1 18 mai 2004
- 命名捕獲正則表示式可用於在正則表示式中定義列名(這也用於記錄正則表示式)。透過
devtools::install_github("tdhock/namedCapture")安裝 namedCapture 包以使用str_match_all_named()。它使用基本函式gregexpr(perl=TRUE)來解析 Perl 相容正則表示式,並返回一個包含帶列名的匹配矩陣的列表
> named.regexp <- paste0(
+ "(?<day>[[:digit:]]{2})",
+ " ",
+ "(?<month>[[:alpha:]]+)",
+ " ",
+ "(?<year>[[:digit:]]{4})")
> namedCapture::str_match_all_named(string, named.regexp)
[[1]]
day month year
[1,] "23" "mai" "2000"
[2,] "18" "mai" "2004"
sub()進行替換。gsub()類似於sub(),但它替換模式的所有出現,而sub()只替換第一次出現。str_replace()(stringr) 類似於 sub,str_replace_all()(stringr) 類似於 gsub。
在下面的例子中,我們有一個法語日期。正則表示式模式如下:2 位數字,一個空格,一些字母,一個空格,4 位數字。我們使用[[:digit:]]{2}表示式捕獲 2 位數字,使用[[:alpha:]]+捕獲字母,使用[[:digit:]]{4}捕獲 4 位數字。這三個子字串中的每一個都被括號包圍。第一個子字串儲存在"\\1"中,第二個儲存在"\\2"中,第三個儲存在"\\3"中。
string <- "23 mai 2000"
regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
sub(pattern = regexp, replacement = "\\1", x = string) # returns the first part of the regular expression
sub(pattern = regexp, replacement = "\\2", x = string) # returns the second part
sub(pattern = regexp, replacement = "\\3", x = string) # returns the third part
在下面的例子中,我們比較了sub()和gsub()的結果。第一個刪除了第一個空格,而第二個刪除了文字中的所有空格。
> text <- "abc def ghk"
> sub(pattern = " ", replacement = "", x = text)
[1] "abcdef ghk"
> gsub(pattern = " ", replacement = "", x = text)
[1] "abcdefghk"
chartr()替換表示式中的字元。它代表“字元轉換”。replacechar()(cwhmisc) 執行相同的工作...- 以及
str_replace_all()(stringr)。
> chartr(old="a",new="o",x="baba")
[1] "bobo"
> chartr(old="ab",new="ot",x="baba")
[1] "toto"
> replacechar("abc.def.ghi.jkl",".","_")
[1] "abc_def_ghi_jkl"
> str_replace_all("abc.def.ghi.jkl","\\.","_")
[1] "abc_def_ghi_jkl"
tolower()將大寫字母轉換為小寫。toupper()將小寫字母轉換為大寫。capitalize()(Hmisc) 將字串的第一個字母大寫- 另請參閱 cwhmisc 包中的
cap()、capitalize()、lower()、lowerize()和CapLeading()。
> tolower("ABCdef")
[1] "abcdef"
> toupper("ABCdef")
[1] "ABCDEF"
> capitalize("abcdef")
[1] "Abcdef"
padding()(cwhmisc) 用某些字元填充字串以適應給定的長度。另請參閱str_pad()(stringr)。
> library("cwhmisc")
> padding("abc",10," ","center") # adds blanks such that the length of the string is 10.
[1] " abc "
> str_pad("abc",width=10,side="center", pad = "+")
[1] "+++abc++++"
> str_pad(c("1","11","111","1111"),3,side="left",pad="0")
[1] "001" "011" "111" "1111"
請注意,str_pad()非常慢。例如,對於長度為 10,000 的向量,計算時間非常長。padding()似乎無法處理字元向量,但最好的解決方案可能是將sapply()和padding()函式一起使用。
>library("stringr")
>library("cwhmisc")
>a <- rep(1,10^4)
> system.time(b <- str_pad(a,3,side="left",pad="0"))
utilisateur système écoulé
50.968 0.208 73.322
> system.time(c <- sapply(a, padding, space = 3, with = "0", to = "left"))
utilisateur système écoulé
7.700 0.020 12.206
trimws()(memisc 包) 修剪前導空格和尾隨空格。trim()(gdata 包) 執行相同的工作。- 另請參閱
str_trim()(stringr)
> library("memisc")
> trimws(" abc def ")
[1] "abc def"
> library("gdata")
> trim(" abc def ")
[1] "abc def"
> str_trim(" abd def ")
[1] "abd def"
==如果兩個字串相同則返回 TRUE,否則返回 false。
> "abc"=="abc"
[1] TRUE
> "abc"=="abd"
[1] FALSE
很少有包實現了Levenshtein 距離,即兩個字串之間的距離。
- 基本包 utils 中的
adist() - MiscPsycho 中的
stringMatch() - stringdist 中的
stringdist() - RecordLinkage 中的
levenshteinDist()
此處提供了比較levenshteinDist()和stringdist()速度的基準測試:[1]。
> adist("test","tester")
[1] 2
stringMatch() (MiscPsycho) 計算如果normalize="YES",則 Levenshtein 距離將除以每個字串的最大長度。
> library("MiscPsycho")
> stringMatch("test","tester",normalize="NO",penalty=1,case.sensitive = TRUE)
[1] 2
agrep()使用Levenshtein 距離搜尋近似匹配。
- 如果 'value = TRUE',則返回字串的值
- 如果 'value = FALSE',則返回字串的位置
- max返回最大 Levenshtein 距離。
> agrep(pattern = "laysy", x = c("1 lazy", "1", "1 LAZY"), max = 2, value = TRUE)
[1] "1 lazy"
> agrep("laysy", c("1 lazy", "1", "1 LAZY"), max = 3, value = TRUE)
[1] "1 lazy"
deparse():將未評估的表示式轉換為字元字串。char.expand()(base) 根據目標擴充套件字串。pmatch()(base) 和charmatch()(base) 在第二個引數中查詢第一個引數元素的匹配項。
> pmatch(c("a","b","c","d"),table = c("b","c"), nomatch = 0)
[1] 0 1 2 0
make.unique()使字元字串唯一。如果要使用字串作為資料中的識別符號,這將很有用。
> make.unique(c("a", "a", "a"))
[1] "a" "a.1" "a.2"
- ↑ Hadley Wickham "stringr: modern, consistent string processing" The R Journal, December 2010, Vol 2/2, http://journal.r-project.org/archive/2010-2/RJournal_2010-2_Wickham.pdf
- ↑ http://cran.r-project.org/web/views/NaturalLanguageProcessing.html
- ↑ 在以前版本(< 2.10)中,我們還在 R 中擁有基本正則表示式:
- 擴充套件正則表示式,由
extended = TRUE(預設值)使用, - 基本正則表示式,由
extended = FALSE(在 R 2.10 中已棄用)使用。
‘extended = FALSE’) 現在已棄用,因此extended選項在 2.11 版中已棄用。 - 擴充套件正則表示式,由
