R正則表達式(grep,grepl,regexpr,sub,gsub)

一、Firstly? Dimension

正則表達式簡介:

傳統的統計學教育幾乎沒有告訴過我們,如何進行文本的統計建模分析。然而,我們日常生活中接觸到的大部分數據都是以文本的形式存在。文本分析與挖掘在業界中也有著非常廣泛的應用。

由于文本數據大多屬于非結構化的數據,要想對文本數據進行傳統的統計模型分析,必須要經過層層的數據清洗與整理。

今天我們要介紹的『正則表達式及R字符串處理』就是用來干這一種臟活累活的。

與建立酷炫的模型比起來,數據的清洗與整理似乎是一種低檔次的工作。如果把建立模型類比于高級廚師的工作,那么,數據清洗無疑是類似切菜洗碗打掃衛生的活兒。然而想要成為資深的『數據玩家』,這種看似低檔次的工作是必不可少的,并且,這種工作極有可能將占據你整個建模流程的80%的時間。

如果我們能夠掌握高效的數據清洗工具,那么我們將擁有更多的時間來進行模型選擇和參數調整,使得我們的模型更加合理有效。

我會從介紹兩種不同的正則表達式,但是他們解決的問題其實是一樣的。

下面將要將要舉例說明,一一介紹R中常用來處理此類工作的幾個函數。

一、首先學習grep函數,是R語言中最基本的正則表達式函數

賦值:

txt<-c("one more","Hello world","up up day","one the more","one two three")

num<-c("one","foru")


num在txt的位置


顯示出值


顯示沒有匹配上的值


返回布爾值


regexpr函數

# 第一行:返回布爾值(1-匹配上,0-沒匹配上)

# 第二行:返回匹配的長度,負值為沒有匹配上

# 第三行:類型

# 第四行:是否有匹配上的結果(顯示是否逐個字節進行匹配,默認為FALSE,即按字符而不是字節進行匹配)

attr(a,"index.type")# 類型

attr(a,"match.length") #返回匹配的長度,負值為沒有匹配上

attr(a,"useBytes") #查看是否有匹配上的結果

gregexpr函數

regexpr()函數只查詢匹配第一個特定字符,要想多次匹配需要使用gregexpr()函數


Sub函數, 返回替換字符串后的具體內容

sub(ignore.case,extended,perl,fixed,useBytes,pattern,replacement,x)

參數:

# ignore.case = FALSE,表示大小寫敏感

# extended = TRUE,表示使用egrep規則

# perl = FALSE,表示不使用Perl規則

# fixed = FALSE,表示不使用精確匹配

# useBytes = FALSE,表示按字符匹配

# pattern為字符串表示正則表達式

# replacement也是字符串表示替換的內容

# x為字符型向量表示被替換的字符向量

# 該函數會根據pattern的規則對x中各元素進行搜索,遇到符合條件的第一個子字符串的位置(gsub是替換所有符合條件的),用replacement替換該子字符串,返回替換后的結果,和x的結構相同


Sub函數

# 我們對replacement統一賦值為“”,在c("abcd","dcba")里面查找"a",替換為空

# 該例中的"a"只是一個字符,并不是正則表達式,真正的正則表達式依靠元字符進行靈活的匹配。


字符開始

# 表示將開頭為a的字符串中的a替換成空,在返回值中可以發現后面出現的a并沒有被替換。如果要將開頭的一個字符串替換,簡單地寫成“^ab”就行.


字符結尾

# 表示將以a結尾的字符串中的a替換成空。


除此以外


任意字符
邏輯或
補集

?由于sub只替換搜尋到的第一個,因此這個例子中用gsub效果更好.


a/b
范圍
范圍(數字)

以上是最基礎的正則表達式元字符,在一些正則表達式的書籍和資料中有非常詳細的介紹。最后需要提一下的是“貪婪”和“懶惰”的匹配規則。默認情況下是匹配盡可能多的字符,是為貪婪匹配,默認匹配最長的a開頭b結尾的字串,也就是整個字符串。

貪婪匹配

如果要進行懶惰匹配,也就是匹配最短的字串,只需要在后面加個“?”#就會匹配最開始找到的最短的a開頭b結尾的字串。

懶惰匹配



二、Secondly?? Dimension

正則表達式不是R的專屬內容,這里只做簡單介紹,更詳細的內容請查閱其他文章。

正則表達式是用于描述/匹配一個文本集合的表達式:

所有英文字母、數字和很多可顯示的字符本身就是正則表達式,用于匹配它們自己。比如 “a” 就是匹配字母 “a” 的正則表達式

一些特殊的字符在正則表達式中不在用來描述它自身,它們在正則表達式中已經被“轉義”,這些字符稱為“元字符”。perl類型的正則表達式中被轉義的字符有:. \ | ( ) [ ] { } ^ $ * + ?。被轉義的字符已經有特殊的意義,如點號 . 表示任意字符;方括號表示選擇方括號中的任意一個(如[a-z] 表示任意一個小寫字符);^ 放在表達式開始出表示匹配文本開始位置,放在方括號內開始處表示非方括號內的任一字符;大括號表示前面的字符或表達式的重復次數;| 表示可選項,即 | 前后的表達式任選一個。

如果要在正則表達式中表示元字符本身,比如我就要在文本中查找問號“?”, 那么就要使用引用符號(或稱換碼符號),一般是反斜杠 “\”。需要注意的是,在R語言中得用兩個反斜杠即 “\”,如要匹配括號就要寫成 “”

不同語言或應用程序(事實上很多規則都通用)定義了一些特殊的元字符用于表示某類字符,如 \d 表示數字0-9, \D 表示非數字,\s 表示空白字符(包括空格、制表符、換行符等),\S 表示非空白字符,\w 表示字(字母和數字),\W 表示非字,< 和 > 分別表示以空白字符開始和結束的文本。

正則表達式符號運算順序:圓括號括起來的表達式最優先,然后是表示重復次數的操作(即:* + {} ),接下來是連接運算(其實就是幾個字符放在一起,如abc),最后是表示可選項的運算(|)。所以 “foot|bar” 可以匹配“foot”或者“bar”,但是“foot|ba{2}r”匹配的是“foot”或者“baar”。

2 字符數統計和字符翻譯

2.1 nchar和length

nchar這個函數簡單,統計向量中每個元素的字符個數,注意這個函數和length函數的差別:nchar是向量元素的字符個數,而length是向量長度(向量元素的個數)。其他沒什么需要說的。

2.2 tolower,toupper和chartr

這三個函數用法也很簡單: tolower 大寫轉小寫 toupper 小寫轉大寫 chartr

chartr(“Tt”, “Uu”, DNA)

[1] “AuGCuuuACC”

chartr(“Tt”, “UU”, DNA)

[1] “AUGCUUUACC”

3 字符串連接

3.1 paste函數

paste應該是R中最常用字符串函數了,也是R字符串處理函數里面非常純的不使用正則表達式的函數(因為用不著)。它相當于其他語言的strjoin,但是功能更強大。它把向量連成字串向量,其他類型的數據會轉成向量,但不一定是你要的結果:

paste(“CK”, 1:6, sep = “”)

[1] “CK1” “CK2” “CK3” “CK4” “CK5” “CK6”

x <- list(a = “aaa”, b = “bbb”, c = “ccc”)

y <- list(d = 1, e = 2)

paste(x, y, sep = “-”) #較短的向量被循環使用

[1] “aaa-1” “bbb-2” “ccc-1”

z <- list(x, y)

paste(“T”, z, sep = “:”)

[1] “T:list(a = “aaa”, b = “bbb”, c = “ccc”)”

[2] “T:list(d = 1, e = 2)”

paste函數還有一個用法,設置collapse參數,連成一個字符串:

paste(x, y, sep = “-”, collapse = "; ")

[1] “aaa-1; bbb-2; ccc-1”

paste(x, collapse = "; ")

[1] “aaa; bbb; ccc”

4 字符串拆分

4.1 strsplit函數

strsplit函數使用正則表達式,使用格式為:

strsplit(x, split, fixed = FALSE, perl = FALSE, useBytes = FALSE)

參數x為字串向量,每個元素都將單獨進行拆分。

參數split為拆分位置的字串向量,默認為正則表達式匹配(fixed=FALSE)。如果你沒接觸過正則表達式,設置fixed=TRUE,表示使用普通文本匹配或正則表達式的精確匹配。普通文本的運算速度快。

perl=TRUE/FALSE的設置和perl語言版本有關,如果正則表達式很長,正確設置表達式并且使用perl=TRUE可以提高運算速度。

參數useBytes設置是否逐個字節進行匹配,默認為FALSE,即按字符而不是字節進行匹配。

text <- “Hello Adam!\nHello Ava!”

strsplit(text, " ")

[[1]]

[1] “Hello” “Adam!\nHello” “Ava!”

R語言的字符串事實上也是正則表達式,上面文本中的\n在圖形輸出中是被解釋為換行符的

strsplit(text, “\s”)

[[1]]

[1] “Hello” “Adam!” “Hello” “Ava!”

strsplit得到的結果是列表,后面要怎么處理就得看情況而定了:

class(strsplit(text, “\s”))

[1] “list”

有一種情況很特殊:如果split參數的字符長度為0,得到的結果就是一個個的字符:

strsplit(text, “”)

[[1]]

[1] “H” “e” “l” “l” “o” " " “A” “d” “a” “m” “!” “\n” “H” “e”

[15] “l” “l” “o” " " “A” “v” “a” “!”

從這里也可以看到R把 \n 是當成一個字符來處理的。

5 字符串查詢:

5.1 grep和grepl函數:

這兩個函數返回向量水平的匹配結果,不涉及匹配字符串的詳細位置信息。

grep(pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE, fixed = FALSE,

useBytes = FALSE, invert = FALSE)

grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)

雖然參數看起差不多,但是返回的結果不一樣。下來例子列出C:\windows目錄下的所有文件,然后用grep和grepl查找exe文件:

files <- list.files(“c:/windows”)

grep("\.exe$", files)

[1] 8 28 30 35 36 58 69 99 100 102 111 112 115 117

grepl("\.exe$", files)

[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE

[12] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[23] FALSE FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE

[34] FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[45] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[56] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[67] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[78] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[89] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE

[100] TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

[111] TRUE TRUE FALSE FALSE TRUE FALSE TRUE FALSE

grep僅返回匹配項的下標,而grepl返回所有的查詢結果,并用邏輯向量表示有沒有找到匹配。兩者的結果用于提取數據子集的結果都一樣:

files[grep("\.exe$", files)]

[1] “bfsvc.exe” “explorer.exe” “fveupdate.exe” “HelpPane.exe”

[5] “hh.exe” “notepad.exe” “regedit.exe” “twunk_16.exe”

[9] “twunk_32.exe” “uninst.exe” “winhelp.exe” “winhlp32.exe”

[13] “write.exe” “xinstaller.exe”

files[grepl("\.exe$", files)]

[1] “bfsvc.exe” “explorer.exe” “fveupdate.exe” “HelpPane.exe”

[5] “hh.exe” “notepad.exe” “regedit.exe” “twunk_16.exe”

[9] “twunk_32.exe” “uninst.exe” “winhelp.exe” “winhlp32.exe”

[13] “write.exe” “xinstaller.exe”

5.2 regexpr、gregexpr和regexec

這三個函數返回的結果包含了匹配的具體位置和字符串長度信息,可以用于字符串的提取操作。

text <- c(“Hellow, Adam!”, “Hi, Adam!”, “How are you, Adam.”)

regexpr(“Adam”, text)

[1] 9 5 14

attr(,“match.length”)

[1] 4 4 4

attr(,“useBytes”)

[1] TRUE

gregexpr(“Adam”, text)

[[1]]

[1] 9

attr(,“match.length”)

[1] 4

attr(,“useBytes”)

[1] TRUE

[[2]]

[1] 5

attr(,“match.length”)

[1] 4

attr(,“useBytes”)

[1] TRUE

[[3]]

[1] 14

attr(,“match.length”)

[1] 4

attr(,“useBytes”)

[1] TRUE

regexec(“Adam”, text)

[[1]]

[1] 9

attr(,“match.length”)

[1] 4

[[2]]

[1] 5

attr(,“match.length”)

[1] 4

[[3]]

[1] 14

attr(,“match.length”)

[1] 4

6 字符串替換

6.1 sub和gsub函數

雖然sub和gsub是用于字符串替換的函數,但嚴格地說R語言沒有字符串替換的函數,因為R語言不管什么操作對參數都是傳值不傳址

text

[1] “Hellow, Adam!” “Hi, Adam!” “How are you, Adam.”

sub(pattern = “Adam”, replacement = “world”, text)

[1] “Hellow, world!” “Hi, world!” “How are you, world.”

text

[1] “Hellow, Adam!” “Hi, Adam!” “How are you, Adam.”

可以看到:雖然說是“替換”,但原字符串并沒有改變,要改變原變量我們只能通過再賦值的方式。 sub和gsub的區別是前者只做一次替換(不管有幾次匹配),而gsub把滿足條件的匹配都做替換:

sub(pattern = “Adam|Ava”, replacement = “world”, text)

[1] “Hellow, world!” “Hi, world!” “How are you, world.”

gsub(pattern = “Adam|Ava”, replacement = “world”, text)

[1] “Hellow, world!” “Hi, world!” “How are you, world.”

sub和gsub函數可以使用提取表達式(轉義字符+數字)讓部分變成全部:

sub(pattern = “.(Adam).”, replacement = “\1”, text)

[1] “Adam” “Adam” “Adam”

7 字符串提取

7.1 substr和substring函數

substr和substring函數通過位置進行字符串拆分或提取,它們本身并不使用正則表達式,但是結合正則表達式函數regexpr、gregexpr或regexec使用可以非常方便地從大量文本中提取所需信息。兩者的參數設置基本相同:

substr(x, start, stop)

substring(text, first, last = 1000000L)

x均為要拆分的字串向量

start/first 為截取的起始位置向量

stop/last 為截取字串的終止位置向量

但它們的返回值的長度(個數)有差 別:

substr返回的字串個數等于第一個參數的長度

而substring返回字串個數等于三個參數中最長向量長度,短向量循環使用。

先看第1參數(要 拆分的字符向量)長度為1例子:

x <- “123456789”

substr(x, c(2, 4), c(4, 5, 8))

[1] “234”

substring(x, c(2, 4), c(4, 5, 8))

[1] “234” “45” “2345678”

因為x的向量長度為1,所以substr獲得的結果只有1個字串,即第2和第3個參數向量只用了第一個組合:起始位置2,終止位置4。 而substring的語句三個參數中最長的向量為c(4,5,8),執行時按短向量循環使用的規則第一個參數事實上就是c(x,x,x),第二個參數就成了c(2,4,2),最終截取的字串起始位置組合為:2-4, 4-5和2-8。

請按照這樣的處理規則解釋下面語句運行的結果:

x <- c(“123456789”, “abcdefghijklmnopq”)

substr(x, c(2, 4), c(4, 5, 8))

[1] “234” “de”

substring(x, c(2, 4), c(4, 5, 8))

[1] “234” “de” “2345678”

用substring函數可以很方便地把DNA/RNA序列進行三聯拆分(用于蛋白質翻譯):

bases <- c(“A”, “T”, “G”, “C”)

DNA <- paste(sample(bases, 12, replace = T), collapse = “”)

DNA

[1] “GCAGCGCATATG”

substring(DNA, seq(1, 10, by = 3), seq(3, 12, by = 3))

[1] “GCA” “GCG” “CAT” “ATG”

用regexpr、gregexpr或regexec函數獲得位置信息后再進行字符串提取的操作可以自己試試看。

8 其他:

8.1 strtrim函數

用于將字符串修剪到特定的顯示寬度,其用法為strtrim(x, width),返回字符串向量的長度等于x的長度。因為是“修剪”,所以只能去掉多余的字符不能增加其他額外的字符:如果字符串本身的長度小于width,得到的是原字符串,別指望它會用空格或其他什么字符補齊:

strtrim(c(“abcdef”, “abcdef”, “abcdef”), c(1, 5, 10))

[1] “a” “abcde” “abcdef”

strtrim(c(1, 123, 1234567), 4)

[1] “1” “123” “1234”

8.2 strwrap函數

該函數把一個字符串當成一個段落的文字(不管字符串中是否有換行符),按照段落的格式(縮進和長度)和斷字方式進行分行,每一行是結果中的一個字符串。例如:

str1 <- “Each character string in the input is first split into paragraphs\n(or lines containing whitespace only).

The paragraphs are then\nformatted by breaking lines at word boundaries.

The target\ncolumns for wrapping lines and the indentation of the first and\nall subsequent lines of a paragraph can be controlled\nindependently.”

str2 <- rep(str1, 2)

strwrap(str2, width = 80, indent = 2)

[1] " Each character string in the input is first split into paragraphs (or lines"

[2] “containing whitespace only). The paragraphs are then formatted by breaking”

[3] “lines at word boundaries. The target columns for wrapping lines and the”

[4] “indentation of the first and all subsequent lines of a paragraph can be”

[5] “controlled independently.”

[6] " Each character string in the input is first split into paragraphs (or lines"

[7] “containing whitespace only). The paragraphs are then formatted by breaking”

[8] “lines at word boundaries. The target columns for wrapping lines and the”

[9] “indentation of the first and all subsequent lines of a paragraph can be”

[10] “controlled independently.”

simplify參數用于指定結果的返回樣式,默認為TRUE,即結果中所有的字符串都按順序放在一個字符串向量中(如上);如果為FALSE,那么結果將是列表。另外一個參數exdent用于指定除第一行以外的行縮進:

strwrap(str1, width = 80, indent = 0, exdent = 2)

[1] “Each character string in the input is first split into paragraphs (or lines”

[2] " containing whitespace only). The paragraphs are then formatted by breaking"

[3] " lines at word boundaries. The target columns for wrapping lines and the"

[4] " indentation of the first and all subsequent lines of a paragraph can be"

[5] " controlled independently."

8.3 match和charmatch

match(“xx”, c(“abc”, “xx”, “xxx”, “xx”))

[1] 2

match(2, c(3, 1, 2, 4))

[1] 3

charmatch(“xx”, “xx”)

[1] 1

charmatch(“xx”, “xxa”)

[1] 1

charmatch(“xx”, “axx”)

[1] NA

match按向量進行運算,返回第一次匹配的元素的位置(如果有),非字符向量也可用。charmatch函數真坑爹。其他不看了,其實有正則表達式就足夠。

END

有問題,歡迎留言。

每日更新(sql,R,python,databricks,sqlserver),感謝關注!!!

百家號更加精彩---ID:SL1談數據分析

---------------------------------------------------------------------------------------------

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容