在處理資料時,除了數值資料之外,文字資料也是很常見的資料類型,尤其是在整理第一手的原始資料時,通常都會有非常大量的文字資料需要處理,而因子則是用於儲存類別型式的資料(categorical data),它的性質介於整數與字元變數之間,以下我們將介紹 R 的字串與因子使用方式。
字串(String)
在 R 中文字的資料都是儲存在字元向量中,而字元向量中的每一個元素都是一個完整的字串(string)。
這裡我們使用字串(string)這個名稱來稱呼字元向量的元素。
建立與輸出字串
字元向量跟一般向量一樣可以使用 c 函數來建立,我們可以使用雙引號或單引號包住字串:
c("Hello", 'World')
[1] "Hello" "World"
若遇到字串中包含雙引號或單引號的情況,可以用反斜線(\)來跳脫處理:
c("Hello, \"World\"")
[1] "Hello, \"World\""
或是使用單引號包住含有雙引號的字串(反之亦可):
c('Hello, "World"')
[1] "Hello, \"World\""
如果要將多個字串連接起來,可以使用 paste 函數:
paste("Hello", "World")
[1] "Hello World"
paste 預設會使用一個空白字元當作分隔符號,將所有的字串連接起來,我們可以使用 sep 參數自行指定分隔字元:
paste("Hello", "World", sep = "-")
[1] "Hello-World"
如果不想要有任何分隔符號,可以使用 paste0 這個函數:
paste0("Hello", "World")
[1] "HelloWorld"
如果遇到不同長度的字元向量時,較短的字元向量就會被重複使用:
paste(c("red", "green"), "apple")
[1] "red apple" "green apple"
若指定 collapse 參數,paste 就會使用這個參數所指定的內容當作分隔符號,將字元向量中所有的字串全部串接成一個字串:
paste(c("red", "green"), "apple", collapse = ", ")
[1] "red apple, green apple"
toString 是一個類似 paste 的函數,他可以將各種向量轉為字串:
x <- (1:10)^2
toString(x)
[1] "1, 4, 9, 16, 25, 36, 49, 64, 81, 100"
toString 加上 width 可以限制輸出字串的長度上限:
toString(x, width = 20)
[1] "1, 4, 9, 16, 25,...."
cat 函數是一個類似 paste 的低階函數,一般使用者不太會有機會需要使用到它,不過由於大部分 print 函數內部都會使用 cat 來輸出,所以多少了解一下會比較好。cat 會直接將所有的元素直接以字串輸出(不管向量長度):
cat(c("red", "green"), "apple", 1:3)
red green apple 1 2 3
在 R 中一般的字串輸出時都會以雙引號包住,如果不想要讓字串出現雙引號,可以使用 noquote 函數來處理:
x <- c("If", "people", "do", "not", "believe",
"that", "mathematics", "is", "simple,",
"it", "is", "only", "because", "they",
"do", "not", "realize", "how",
"complicated", "life", "is");
x
[1] "If" "people" "do" "not" [5] "believe" "that" "mathematics" "is" [9] "simple," "it" "is" "only" [13] "because" "they" "do" "not" [17] "realize" "how" "complicated" "life"
noquote(x)
[1] If people do not believe [6] that mathematics is simple, it [11] is only because they do [16] not realize how complicated life [21] is
格式化數值
若要將數值的資料依據指定的格式轉換為字串,可以使用 formatC 這個函數,這個函數可以讓我們使用類似 C 語言的方式來指定輸出格式。
x <- exp(1:3)
x
[1] 2.718282 7.389056 20.085537
formatC 函數的 digits 參數可以指定輸出數值的位數:
formatC(x, digits = 5)
[1] "2.7183" "7.3891" "20.086"
format 參數可以指定輸出數值格式:
formatC(x, digits = 5, format = "e")
[1] "2.71828e+00" "7.38906e+00" "2.00855e+01"
width 可指定輸出字串長度,不足的部分則會以空白補齊:
formatC(x, digits = 5, width = 8)
[1] " 2.7183" " 7.3891" " 20.086"
在 R 中也有一個 sprintf 函數,它跟一般程式語言中的 sprintf 函數用法一樣,其第一個參數是輸出的樣板,第二個以後的參數則是要輸出的各種資料變數。
item <- "the apple"
weight <- 3.2
sprintf("The weight of %s is %f kg.", item, weight)
[1] "The weight of the apple is 3.200000 kg."
sprintf 也可以直接處理向量的輸出:
items <- c("the apple", "the banana")
weights <- c(3.2, 2.5)
sprintf("The weight of %s is %f kg.", items, weights)
[1] "The weight of the apple is 3.200000 kg." [2] "The weight of the banana is 2.500000 kg."
%.1f 可以指定浮點數輸出至小數點以下第一位:
sprintf("The weight of %s is %.1f kg.", items, weights)
[1] "The weight of the apple is 3.2 kg." [2] "The weight of the banana is 2.5 kg."
以科學記號輸出,精確度到小數點下第二位:
sprintf("The weight of %s is %.2e kg.", items, weights)
[1] "The weight of the apple is 3.20e+00 kg." [2] "The weight of the banana is 2.50e+00 kg."
另外還有兩個用於格式化數值輸出的 format 與 prettyNum 函數,format 跟 formatC 類似,只是參數的用法有些小差異而已。
format(x, digits = 5)
[1] " 2.7183" " 7.3891" "20.0855"
format(x, digits = 5, trim = TRUE)
[1] "2.7183" "7.3891" "20.0855"
format(x, digits = 3, scientific = TRUE)
[1] "2.72e+00" "7.39e+00" "2.01e+01"
prettyNum 函數則適用於輸出比較大或比較小的數值:
prettyNum(2^30, big.mark = ",")
[1] "1,073,741,824"
prettyNum(1/2^20, small.mark = " ", scientific = FALSE)
"0.00000 09536 743"
特殊字元
在字串中若要加入一些特殊字元(例如 tab 字元),可以運用跳脫字元的方式來處理:
cat("foo\tbar")
foo bar
換行字元可使用 \n:
cat("foo\nbar")
foo bar
如果要輸入反斜線,則使用 \\:
cat("foo\\bar")
foo\bar
單引號與雙引號也可以利用跳脫字元輸入:
cat("foo\"bar")
foo"bar
cat('foo\'bar')
foo'bar
如果是以雙引號包住單引號,就可以不需要跳脫字元,反之亦然:
cat('foo"bar')
foo"bar
cat("foo'bar")
foo'bar
另一個比較特別的字元是 alarm 字元(\a),輸出這個字元時會讓喇叭產生聲響:
cat("\a")
輸出 alarm 字元的效果等同時呼叫 alarm 函數:
alarm()
改變大小寫
如果要改變字串中英文字母的大小寫,可以運用 toupper 與 tolower 函數:
toupper("Foo Bar")
[1] "FOO BAR"
tolower("Foo Bar")
[1] "foo bar"
子字串
若要從字串中取出一部分的子字串,可以使用 substr 或 substring 函數,這兩個函數的用法幾乎都相同,第一個參數是輸入的字串,第二與第三個參數則是子字串的開始與結束位置:
substr("abcdef", 2, 4)
[1] "bcd"
substring("abcdef", 2, 4)
[1] "bcd"
substr 與 substring 只有在處理向量時會有一些小差異:
x.strings <- c(
"abcdefghij",
"ABCDEFGHIJ",
"1234567890"
)
substr(x.strings, 1:4, 8)
[1] "abcdefgh" "BCDEFGH" "345678"
substring(x.strings, 1:4, 8)
[1] "abcdefgh" "BCDEFGH" "345678" "defgh"
分割字串
若要將一個字串分割成多個字串,可以使用 strsplit 函數,第一個參數是輸入的字串,而第二個參數則是分隔字串:
strsplit("foo,bar,Foo,BAR", ",")
[[1]] [1] "foo" "bar" "Foo" "BAR"
strsplit 函數預設會使用正規表示法(regular expression)來匹配分隔字串,如果要將指定的分隔字串視為一般的文字,可以加上 fixed = TRUE 參數。
strsplit("foo+bar+Foo+BAR", "+", fixed = TRUE)
[[1]] [1] "foo" "bar" "Foo" "BAR"
strsplit 函數的輸出是一個列表(list),這樣設計的原因在於處理向量時,每一個結果的長度可能會不同:
x.str <- c("foo bar Foo BAR", "abc def")
strsplit(x.str, " ", fixed = TRUE)
[[1]] [1] "foo" "bar" "Foo" "BAR" [[2]] [1] "abc" "def"
這是一個使用正規表示法的例子,使用逗號、句點或空白作為分隔字元:
strsplit("foo,bar.Foo BAR", "[,. ]")
[[1]] [1] "foo" "bar" "Foo" "BAR"
檔案路徑
在 R 中我們可以藉由 getwd 函數取得目前的工作目錄,在存取檔案時若沒有特別指定路徑的話,就會以這個路徑為準。
getwd()
[1] "d:/workspace"
我們可以使用 setwd 函數來更改工作目錄:
setwd("c:/mypath")
getwd()
[1] "c:/mypath"
這裡我們使用正斜線(/)來區隔路徑,而如果要使用 Windows 慣用的反斜線也可以,只是在輸入反斜線時,要使用跳脫字元:
setwd("c:\\mypath")
另外我們也可以使用 file.path 函數來從各個目錄名稱來建立完整的路徑:
file.path("c:", "Program Files", "R")
[1] "c:/Program Files/R"
R.home 可以顯示 R 的安裝路徑:
R.home()
[1] "C:/PROGRA~1/R/R-32~1.4"
basename 可以從一串完整路徑中取出不包含路徑的檔案名稱:
basename("C:/Users/GTWang/Documents")
[1] "Documents"
因子(Factor)
R 的因子(factor)變數是專門用來儲存類別資料的變數,它同時具有字串與整數的特性。
建立因子變數
若要建立一個因子變數,可以使用 factor 函數:
colors <- c("red", "yellow", "green", "red", "green")
colors.factor <- factor(colors)
colors.factor
[1] red yellow green red green Levels: green red yellow
因子變數在輸出時,看起來跟一般的字元向量類似,而最後一行的 levels 會列出這個因子變數所有的類別。我們可以使用 levels 這個函數取得因子的 levels:
levels(colors.factor)
[1] "green" "red" "yellow"
nlevels 則可以取得因子 levels 的數量:
nlevels(colors.factor)
[1] 3
在建立因子變數時,可以使用 levels 參數指定 levels 的排列順序:
colors.factor2 <- factor(colors,
levels = c("red", "yellow", "green"))
colors.factor2
[1] red yellow green red green Levels: red yellow green
labels 參數則可以指定個類別的名稱:
colors.factor3 <- factor(colors,
levels = c("red", "yellow", "green"),
labels = c("R", "Y", "G"))
colors.factor3
[1] R Y G R G Levels: R Y G
在建立 data frame 時,R 預設會將文字的資料轉為因子:
values <- c(12, 54, 23, 83, 2)
colors <- c("red", "yellow", "green", "red", "green")
my.table <- data.frame(values, colors)
my.table
values colors 1 12 red 2 54 yellow 3 23 green 4 83 red 5 2 green
class(my.table$colors)
[1] "factor"
改變因子的 Levels
因子變數中的元素值只能是 levels 中的其中一種或是缺失值(NA),如果將因子的元素指定為其他的字串,該元素就會被設定為缺失值,並產生警告訊息:
colors.factor[1] <- "blue"
Warning message: In `[<-.factor`(`*tmp*`, 1, value = "blue") : invalid factor level, NA generated
colors.factor
[1] <NA> yellow green red green Levels: green red yellow
如果要新增一個 levels 中沒有的類別資料,可以先使用 levels 函數先新增一個 level,再指定新的值:
colors.levels <- levels(colors.factor)
levels(colors.factor) <- c(colors.levels, "blue")
colors.factor[1] <- "blue"
colors.factor
[1] blue yellow green red green Levels: green red yellow blue
在更動因子變數的 levels 時,要注意其排列的順序,新增的 level 要放在最後面,如果順序搞錯會造成資料錯誤,若要避免順序錯亂的問題,可以使用 list 的方式指定:
levels(colors.factor) <- list(
blue = "blue", green = "green",
red = "red", yellow = "yellow")
colors.factor[1] <- "blue"
colors.factor
[1] blue yellow green red green Levels: blue green red yellow
relevel 可以重新排序因子變數的 levels,將指定的 level 放在第一個位置:
relevel(colors.factor, "red")
[1] blue yellow green red green Levels: red blue green yellow
移除因子的 Levels
有時候在處理一些原始的文字資料時,會出現類似這樣英文大小寫不統一的狀況:
colors.raw <- c("Red", "yellow", "green", "red", "green")
colors.factor <- factor(colors.raw)
colors.factor
[1] Red yellow green red green Levels: green red Red yellow
而經過修正之後,就會出現一些沒有用的 levels。
colors.factor[1] <- "red"
unique(colors.factor)
[1] red yellow green Levels: green red Red yellow
若要移除因子變數中沒有用到的 levels,可以使用 droplevels 函數:
colors.factor <- droplevels(colors.factor)
colors.factor
[1] red yellow green red green Levels: green red yellow
droplevels 也可以直接處理 data frame 之內的因子欄位:
colors.raw <- c("Red", "yellow", "green", "red", "green")
values <- c(12, 54, 23, 83, 2)
my.table2 <- data.frame(values, colors.raw)
my.table2$colors.raw
[1] Red yellow green red green Levels: green red Red yellow
my.table2$colors.raw[1] <- "red"
unique(my.table2$colors.raw)
[1] red yellow green Levels: green red Red yellow
my.table2 <- droplevels(my.table2)
my.table2$colors.raw
[1] red yellow green red green Levels: green red yellow
有序因子
有一些資料雖然是類別型的資料,但是不同類別之間是可以比較的,這種有順序性的類別資料可以使用有序因子來儲存,最常見的例子就是問卷調查的資料,例如滿意度的評價可分為非常差(worst)、差(bad)、普通(so-so)、好(good)與非常好(perfect):
choices <- c("worst", "bad", "so-so", "good", "perfect")
samples <- sample(choices, 10, replace = TRUE)
samples.factor <- factor(samples, levels = choices)
samples.factor
[1] perfect so-so perfect good perfect [6] worst worst so-so good so-so Levels: worst bad so-so good perfect
若遇到這樣的資料,可以改用 ordered 函數來建立有序的因子變數(或是在呼叫 factor 函數時加入 ordered = TRUE 也可以):
samples.ordered <- ordered(samples, levels = choices)
samples.ordered
[1] perfect so-so perfect good perfect worst [7] worst so-so good so-so Levels: worst < bad < so-so < good < perfect
有序因子也是屬於因子變數:
is.factor(samples.ordered)
[1] TRUE
有序的因子跟一般的因子變數的使用方式都相同,唯一的差異就只是它的 levels 有順序性而已。
將連續型變數轉換為離散型變數
如果要看一群連續型資料大致的分佈,可以將其轉換為離散的群組,這樣會比較容易一眼看出整個分布狀況:
grouped <- cut(iris$Sepal.Length, seq(4.3, 7.9, 4))
head(grouped)
[1] (4.3,5.2] (4.3,5.2] (4.3,5.2] (4.3,5.2] (4.3,5.2] (5.2,6.1] Levels: (4.3,5.2] (5.2,6.1] (6.1,7] (7,7.9]
table(grouped)
grouped
(4.3,5.2] (5.2,6.1] (6.1,7] (7,7.9]
45 50 43 12這個作用跟使用 hist 函數畫直方圖相同。
將離散型變數轉換為連續型變數
有些時候我們拿到的原始資料有一些錯誤(例如打字上的錯誤),造成 R 在讀入資料時,將數值當成字串讀入,然後自動轉換為因子,而這種變數若直接使用 as.numeric 轉換為數值的話,會出現錯誤:
raw <- data.frame(
x = c("1.23", "4..56", "7.89")
)
as.numeric(raw$x)
[1] 1 2 3
這裡的 as.numeric 是將因子變數內部代表各類別的數值資料直接取出,但這並不是我們想要的結果。我們可以將因子先轉換為字串,再轉為數值:
as.numeric(as.character(raw$x))
[1] 1.23 NA 7.89 Warning message: 強制變更過程中產生了 NA
而根據 R 的 FAQ 說明文件,比較好的做法是像下面這種,這個寫法的執行效率會比較高:
as.numeric(levels(raw$x))[as.integer(raw$x)]
[1] 1.23 NA 7.89 Warning message: 強制變更過程中產生了 NA
產生因子的 Levels
gl 函數可以依據指定的樣式來產生因子變數,第一個參數 n 是指定因子的 level 數目,而第二個參數 k 則是指定每個 level 出現的次數:
gl(n = 2, k = 8)
[1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
labels 參數可以用來指定各個 levels 的名稱:
gl(n = 2, k = 8, labels = c("Control", "Treat"))
[1] Control Control Control Control Control Control Control Control Treat [10] Treat Treat Treat Treat Treat Treat Treat Levels: Control Treat
length 參數可以指定產生的因子變數長度:
gl(n = 2, k = 2, length = 8,
labels = c("Control", "Treat"))
[1] Control Control Treat Treat Control Control Treat Treat Levels: Control Treat
結合因子變數
如果要將兩個(或多個)因子變數結合,可以使用 interaction 函數:
a <- gl(2, 4, 8)
a
[1] 1 1 1 1 2 2 2 2 Levels: 1 2
b <- gl(2, 2, 8, labels = c("ctrl", "treat"))
b
[1] ctrl ctrl treat treat ctrl ctrl treat treat Levels: ctrl treat
interaction(a, b)
[1] 1.ctrl 1.ctrl 1.treat 1.treat 2.ctrl 2.ctrl 2.treat 2.treat Levels: 1.ctrl 2.ctrl 1.treat 2.treat
超過兩個因子變數的情況也可以使用 interaction 處理,而 sep 參數可以指定結合 level 時的分隔符號。
s <- gl(2, 1, 8, labels = c("M", "F"))
s
[1] M F M F M F M F Levels: M F
interaction(a, b, s, sep = ":")
[1] 1:ctrl:M 1:ctrl:F 1:treat:M 1:treat:F 2:ctrl:M 2:ctrl:F 2:treat:M [8] 2:treat:F 8 Levels: 1:ctrl:M 2:ctrl:M 1:treat:M 2:treat:M 1:ctrl:F ... 2:treat:F