統計R語言實作筆記系列 – R的字串處理:grep套件包(grep、grepl、regexpr、gregexpr、regexec)

差不多從 7、8 年前首次聽到 R 這個名字的時候開始,就對於 R 有一個刻板印象:「很好的數值資料處理工具,但並不擅長用來處理文字資料」。

最近因為有個小任務需要把非結構文字資料轉為結構化的矩陣,趁著這個機會也更新一下自己對 R 的認識,在找資料的過程才發現即使是「中文字串」這個 Text-Mining 當中的煩人問題,現在也可以透過 R 當中的套件做快速的處理,分析者甚至不需要了解何謂程式語言中的 regular expression,簡直神奇!

這篇文章主要分享的部分是 R 2.1 版本(目前最新應該是 3.1.2 版)以後收錄的基本套件 grep 當中的幾個 function,以及搭配 substring 等其他 function 的組合用法。

文字處理與 Text-Mining

在筆者經驗當中, Text-Mining 的前置資料處理大概可以分為兩種:第一,將原始文章資料透過「辭庫」進行斷字斷詞,每一個被認定為單獨的詞將對應到一個欄位,將多篇文章斷詞完畢之後,將得到一個擁有巨量欄位的計數資料矩陣,接下來再應用 Clustering 或者 Classification 的演算法進行關聯性分析或預測。

這種方式構成 Text-Mining 給人的主要印象,但僅限使用於文章性質的資料分析,實務上會遇到的非結構化文字資料未必都是文章、留言,例如網路購物的訂單 email ,假如訂單資料庫設計時並未儲存所有訂購相關資訊,而必須收集訂單 email 中的資訊反過來重組資料庫時,前述斷字斷詞的方法就派不上用場,因為每封 email 中的系統詞彙(例如金額、姓名欄位)都是固定的,重點是如何擷取位於這些系統詞彙前後左右的個人化資料(例如實際金額的數字),這就是另一種文字資料前置處理時要做的工作(沒有 R 的話,多半得精通 regular expression 才做得出來)。

其他範例還包括時下流行的網路爬文程式,在過濾網路原始檔時也會遇到許多重複但固定的格式,這些時候必須先擷取指定的資料,組成結構化的資料矩陣後,才能進行後續的統計或斷字斷詞分析。

範例:維基百科-中華民國頁面的一部分網頁原始檔

<html lang="zh-TW" dir="ltr" class="client-nojs">
<head>
<meta charset="UTF-8″ />
<title>中華民國 – 維基百科,自由的百科全書</title>
<meta name="generator" content="MediaWiki 1.25wmf14″ />
<link rel="alternate" href="android-app://org.wikipedia/http/zh.m.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B" />

R 的字串處理好幫手:grep

如果你在 R 當中輸入help(grep),就可以看到在這個包底下共有 6 種函數:

  • 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)
  • sub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE,fixed = FALSE, useBytes = FALSE)
  • gsub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE,fixed = FALSE, useBytes = FALSE)
  • regexpr(pattern, text, ignore.case = FALSE, perl = FALSE,fixed = FALSE, useBytes = FALSE)
  • gregexpr(pattern, text, ignore.case = FALSE, perl = FALSE,fixed = FALSE, useBytes = FALSE)
  • regexec(pattern, text, ignore.case = FALSE,fixed = FALSE, useBytes = FALSE)

由於這些函數格式都很類似,只要會用一種,其他也就八九不離十了,實際上要注意的是這些函數返回的「值」要如何運用以及在參數中使用「TRUE/FALSE」的時機。

為了更快速了解它們的用法,我們一邊以維基百科頁面的例子試驗,一邊說明每個函數的差異。

首先先設定範例字串「ROC」:
> ROC<-“<title>中華民國 – 維基百科,自由的百科全書</title>"
> ROC
[1] “<title>中華民國 – 維基百科,自由的百科全書</title>"

接著以「中華民國」為 pattern 參數,投入各函數當中:
> grep(“中華民國",ROC)
[1] 1
# grep返回數值的意義不難猜,即是告訴我們在「 ROC 」當中,符合”中華民國”條件的值有幾個,當然,答案是 1 。

> grepl(“中華民國",ROC)
[1] TRUE
# grepl 名字和 grep 相似,做的事情也差不多,唯一的差別在於 grepl 使用二分法 TRUE 或者 FALSE 表達在指定條件”中華民國”下,指定範圍 ROC 裡是否有符合的字元。

> regexpr(“中華民國",ROC)
[1] 8
attr(,"match.length")
[1] 4
#包括 regexpr 、 gregexpr 及 regexec 都帶有兩個值,第一個值表示符合條件的字串”中華民國”出現在指定範圍” ROC ”當中的位置是從第幾個字元開始,稍微數一下你就會有點感覺了,答案是第 8 個字元開始。第二個值「 match.length 」表示的是符合條件”中華民國”的結果有幾個字元,當然就是 4 個了。

> gregexpr(“中華民國",ROC)
[[1]]
[1] 8
attr(,"match.length")
[1] 4
#從返回值來看, regexpr、 gregexpr 及 regexec 似乎都一樣,假如有配對成功的字串的確看不出差異,沒有配對成功時則有些許不同,使用細節可參考help()當中的說明。

> regexec(“中華民國",ROC)
[[1]]
[1] 8
attr(,"match.length")
[1] 4
# 同上。> sub(“中華民國","台灣",ROC)
[1] “<title>台灣 – 維基百科,自由的百科全書</title>"
# sub 及 gsub 作用相同,跟前面幾個函數不同的是,除了指定 pattern 和 text 之外,還要多指定 replacement ,因為它就是用來取代字元的函數。在這個範例中,原先的中華民國被取代成了台灣。

> gsub(“中華民國","台灣",ROC)
[1] “<title>台灣 – 維基百科,自由的百科全書</title>"
# 同上。

grep 包的應用實例

看過這些文字處理函數之後,或許你也跟我剛開始使用的時候一樣,還是看不出來要如何應用這些函數進行結構化的處理。的確,以筆者常用的 regexpr 為例,它只是告訴我們關鍵的字元起始位置,並未幫我們把這些資訊給擷取出來,因此還要搭配字串切割的函數才能發揮威力。

這些互相搭配的函數中,必須要學會的是 substring 。這個函數的用法是:

substr(x, start, stop)

其中

x, text => a character vector.

start, first => integer. The first element to be replaced.

stop, last => integer. The last element to be replaced.

只要指定了起始以及結束的位置, substr 就能在指定的範圍 x 當中找到並切割出我們所要的資料。

但我們要如何知道關鍵資料的起始和結束位置呢?它正好就是 grep 系列函數的輸出值啊!

沿用維基百科的例子,假如現在有個大資料集包含了一萬筆資料的原始檔叫做 CountryName,每筆資料都代表一個頁面並包含唯一的 title 值(例如:<title>中華民國 – 維基百科,自由的百科全書</title>),而我們想要把這組 title 找出來並存成一個欄位,該如何做呢?

由於我們已經知道關鍵字串「中華民國」的位置,它不一定是四個字,也可能是兩個字的美國或五個字的哥斯大黎加,但它必定會出現在字串「 <title> 」起算的第 8 個字元開始到「 維基百科 」之前的 4 個字元為止,於是我們可以利用 regexpr 找出這一萬筆資料當中所有的「國名」位置,並存為 StartName 及 EndName 兩個變數,例如:

CountryName <- 原始資料

StartName<-regexpr(“<title>", CountryName)

EndName<- regexpr(“維基百科", CountryName)

接著利用 subtr ,輸入 StartName 及 EndName 兩個變數所包含的位置資料,切割出想要的國名資料,存為 ABC 變數:

ABC<-subtr(CountryName, StartName+8, EndName-4)

結果應該會長得如下:
ABC
1 中華民國
2 美國
3 哥斯大黎加
….

R 中常用的字串處理 Function 清單

除了 substr 之外,《Handling and Processing Strings in R》的作者 Gaston Sanchez 幫大家整理了一些常用的字串處理函數,蓋列如下以供參考:

  • Basic functions

character() creating a character vector

is.character() test character mode

as.character() convert as character

paste() pasting

Printing related functions

print() generic printing

noquote() unquoted characters

cat() concatenate and print

format() special formats

sprintf() C-style string formatting

toString() convert to string objects

  • Basic String Manipulations

char() count number of characters

tolower() convert to lower case

toupper() convert to upper case

casefold() case folding

chartr() character translation

abbreviate() abbreviation

substring() substrings of a character vector

substr() substrings of a character vector

  • String Manipulations with stringr

str_c() concatenation

str_length() number of characters

str_sub() substring

str_dup() string duplication

str_pad() string padding

str_wrap() string wrapping

str_trim() trimming whitespaces

word() extract words from a sentence

  • Functions for Regular Expressions

grep()

grepl()

regexpr()

gregexpr()

regexec()

sub()

gsub()

strsplit()

str_detect()

str_extract()

str_extract_all()

str_match()

str_match_all()

str_locate()

str_locate_all()

str_replace()

str_replace_all()

str_split()

str_split_fixed()

延伸閱讀不嫌多!

(Visited 5,294 times, 51 visits today)

Wendell.Huang

科技公司嫌棄太活潑,消費品牌挑剔太沉悶..., 經常必須解釋自己在學什麼, 不小心就摔破對方眼鏡的跨領域玩家。