8 精簡的變數型別

8.1 暸解不同的變數型別

變數型別 變數型別英文 範例
數值 numeric 2
整數 integer 2L
邏輯值 logical TRUE
文字 character “Learning R the easy way”
日期 Date Sys.Date()
時間 POSIXct POSIXt Sys.time()

我們不需要自己去猜測,可以使用一個函數 class() 讓 R 語言告訴我們輸入的變數是什麼型別,就像在 RStudio 的左下角命令列(Console)輸入這些程式,詢問 R 語言它們的型別為何?

class(2)
## [1] "numeric"
class(2L)
## [1] "integer"
class(TRUE)
## [1] "logical"
class("Learning R the easy way")
## [1] "character"
class(Sys.Date())
## [1] "Date"
class(Sys.time())
## [1] "POSIXct" "POSIXt"

如果您對 class() 這個函數感到好奇,可以在命令列輸入 ?class 或者 help(class),這兩個指令都會在右下角打開文件。即便您對 class() 不甚感興趣,知道如何使用 ?help() 進行查詢也相當有幫助。

8.2 數值

不論我們輸入的數字帶有小數位數或不帶有小數位數,R 語言預設儲存為數值(numeric)。

my_num <- 2.33
class(my_num)
## [1] "numeric"
my_num <- 2.0
class(my_num)
## [1] "numeric"
my_num <- 2
class(my_num)
## [1] "numeric"

8.3 整數

當我們輸入一個整數並加入 L,R 語言就會儲存為整數(integer)。如果整數帶有不必要的小數位數,R 語言會回傳警示訊息但依舊會儲存為整數;但若在帶有小數位數的數字後加上 L 則 R 語言回傳警示訊息,並且忽略 L 儲存為數值。

my_int <- 2L
class(my_int)
## [1] "integer"
my_int <- 2.0L
class(my_int)
## [1] "integer"
my_int <- 2.33L
class(my_int)
## [1] "numeric"

8.4 邏輯值

當我們進行判斷條件或者篩選的時候就會需要使用邏輯值(logical),邏輯值只有 TRUEFALSE 這兩個值,或者可以簡寫為 TF

class(TRUE)
## [1] "logical"
class(FALSE)
## [1] "logical"
class(T)
## [1] "logical"
class(F)
## [1] "logical"

這裡要提醒您一個觀念,R 語言對大小寫是敏感的(case-sensitive),像是 TRUE 會被 R 語言識別為邏輯值,但是 Truetrue 則不會喔!

class(TRUE)
## [1] "logical"
class(True)
## Error in eval(expr, envir, enclos): object 'True' not found
class(true)
## Error in eval(expr, envir, enclos): object 'true' not found

除了直接輸入邏輯值,我們也可以透過判斷條件得到邏輯值的輸出:

8 > 7 # 判斷 8 是否大於 7
## [1] TRUE
8 < 7 # 判斷 8 是否小於 7
## [1] FALSE
8 >= 7 # 判斷 8 是否大於等於 7
## [1] TRUE
8 <= 7 # 判斷 8 是否小於等於 7
## [1] FALSE
8 == 7 # 判斷 8 是否等於 7
## [1] FALSE
8 != 7 # 判斷 8 是否不等於 7
## [1] TRUE
7 %in% c(8, 7) # 判斷 7 是否包含於一個 c(8, 7) 的向量之中
## [1] TRUE

我們稍微停頓一下,因為這裡有兩個新觀念需要您花點時間吸收:

  1. c(8, 7) 是一種叫做向量的資料結構,我們會在之後的章節細談
  2. 常用的判斷運算子有:
判斷運算子 作用
== 等於
> 大於
< 小於
>= 大於等於
<= 小於等於
!= 不等於
%in% 包含於

8.5 文字

在 R 語言中我們可以使用單引號(’)或雙引號(“)來建立文字(character),我的習慣是使用雙引號(”),所以在本書中的範例都會使用 " 來建立文字。

first_name <- 'Tony'
first_name
## [1] "Tony"
class(first_name)
## [1] "character"

8.5.1 日期

在 R 語言中被定義為日期(Date)的變數外觀看起來跟文字沒有什麼差別,但是我們一但將它們放入 class() 函數中檢驗,就會發現它並不是文字,我們接下來要使用的 Sys.Date() 是一個不需要任何輸入就會輸出電腦系統日期的函數。

sys_date <- Sys.Date() # 系統日期
sys_date # 看起來跟文字相同
## [1] "2018-05-12"
class(sys_date)
## [1] "Date"

而這兩個變數類型最大的分野,就在於日期是可以被轉換為整數,而文字是不行的:

sys_date <- Sys.Date()
sys_date_char <- as.character(sys_date) # 創造一個文字類型
 
as.integer(sys_date)
## [1] 17663
as.integer(sys_date_char)
## Warning: NAs introduced by coercion
## [1] NA

這裡我們使用的 as.character()as.integer() 函數是 R 語言中用來進行變數類型轉換的函數,我們可以清楚文字類型的系統日期在轉換整數時失敗,以致於產生一個遺失值(Not Available,NA)。

那麼日期類型的系統日期轉換的整數是有什麼根據嗎?答案是有的,R 語言預設以西元 1970 年 1 月 1 日作為 0,在這一天以後的每天都 +1 來記錄,而這一天以前的每天都 -1 來記錄。

date_of_origin <- as.Date("1970-01-01")
as.integer(date_of_origin)
## [1] 0
as.integer(date_of_origin + 1)
## [1] 1
as.integer(date_of_origin - 1)
## [1] -1
date_of_origin
## [1] "1970-01-01"
date_of_origin + 1
## [1] "1970-01-02"
date_of_origin - 1
## [1] "1969-12-31"

也因為這樣的特性,日期類型可以進行四則運算,而文字類型不行:

sys_date <- Sys.Date()
sys_date_char <- as.character(sys_date) # 創造一個文字類型
sys_date - 1 # 昨天的日期
## [1] "2018-05-11"
sys_date_char - 1
## Error in sys_date_char - 1: non-numeric argument to binary operator

8.6 時間

在 R 語言中被定義為時間(POSIXct POSIXt)的變數外觀看起來跟文字同樣也沒有什麼差別,但是我們一但將它們放入 class() 函數中檢驗,就會發現它並不是文字,我們接下來要使用的 Sys.time() 是一個不需要任何輸入就會輸出電腦系統時間的函數。

sys_time <- Sys.time() # 系統時間
sys_time # 看起來跟文字相同
## [1] "2018-05-12 01:37:53 UTC"
class(sys_time)
## [1] "POSIXct" "POSIXt"

與日期相似,時間亦是可以被轉換為整數的:

sys_time <- Sys.time()
as.integer(sys_time)
## [1] 1526089073

與日期相似,R 語言預設以西元 1970 年 1 月 1 日格林威治標準時間(Greenwich Mean Time,GMT)00 時 00 分 00 秒作為 0,在這個時間點以後的每秒都 +1 來記錄,這個時間點以前的每秒都 -1 來記錄。

time_of_origin <- as.POSIXct("1970-01-01 00:00:00", tz = "GMT")
as.integer(time_of_origin)
## [1] 0
as.integer(time_of_origin + 1)
## [1] 1
as.integer(time_of_origin - 1)
## [1] -1
time_of_origin
## [1] "1970-01-01 GMT"
time_of_origin + 1
## [1] "1970-01-01 00:00:01 GMT"
time_of_origin - 1
## [1] "1969-12-31 23:59:59 GMT"

我們這裡所使用的參數 tz = "GMT" 是指定時區,假如您的電腦和我一樣時區是設在中原標準時間(Chungyuan Standard Time,CST),早格林威治標準時間八個小時(GMT + 8),則基準時間會是西元 1970 年 1 月 1 日 08 時 00 分 00 秒。

time_of_origin_cst <- as.POSIXct("1970-01-01 08:00:00")
as.integer(time_of_origin_cst)
## [1] 28800

8.7 數學運算

對數值,整數與邏輯值進行數學運算,常用的數學運算子有七種:

數學運算子 作用
+
-
*
/
^** 次方
%/% 回傳商數
%% 回傳餘數

我們先進行僅有數值的運算:

first_num <- 8
second_num <- 7

first_num + second_num
## [1] 15
first_num - second_num
## [1] 1
first_num * second_num
## [1] 56
first_num / second_num
## [1] 1.142857
first_num^second_num
## [1] 2097152
first_num**second_num
## [1] 2097152
first_num %% second_num
## [1] 1
first_num %/% second_num
## [1] 1

接著我們進行僅有整數的運算:

first_int <- 8L
second_int <- 7L
 
ans <- first_int * second_int
ans
## [1] 56
class(ans)
## [1] "integer"
ans <- first_int / second_int
ans
## [1] 1.142857
class(ans)
## [1] "numeric"

相乘之後仍然是整數的變數型別,注意相除之後 R 語言自動幫我們把答案轉換成了適當型別,我們時不時都可以觀察到 R 語言的彈性:

my_num <- 8
my_int <- 7L

ans <- my_num + my_int
class(ans)
## [1] "numeric"

最後我們把邏輯值也放進來做四則運算,看看會發生什麼事情:

my_num <- 8
my_int <- 7L
my_logi <- TRUE
 
ans <- my_num + my_int + my_logi
ans
## [1] 16
class(ans)
## [1] "numeric"
my_logi <- FALSE
ans <- my_num + my_int + my_logi
ans
## [1] 15
class(ans)
## [1] "numeric"

為什麼 TRUEFALSE 納入四則運算沒有任何問題呢?原來在 R 語言中,TRUE1 或者 1L 是相等的;FALSE0 或者 0L 是相等的。

my_num <- 0
my_int <- 0L
my_logi <- FALSE
 
my_num == my_logi
## [1] TRUE
my_int == my_logi
## [1] TRUE
my_num <- 1
my_int <- 1L
my_logi <- TRUE
 
my_num == my_logi
## [1] TRUE
my_int == my_logi
## [1] TRUE

8.8 變數型別的判斷與轉換

R 語言針對變數型別的判斷除了使用 class() 函數直截了當告訴我們答案以外,也能夠使用一系列 is.型別名稱() 的函數回傳邏輯值,用 TRUE 或者 FALSE 回傳判斷的結果;而變數型別的轉換則是透過一系列 as.型別名稱() 的函數進行轉換。

8.8.1 變數型別的判斷

除了判斷日期與時間變數型別要使用的是 inherits() 函數,其他都可以使用 is.型別名稱()這樣形式的函數判斷。

函數 範例
is.numeric() is.numeric(7.7)
is.integer() is.integer(7L)
is.logical() is.logical(FALSE)
is.character() is.character("Learn R the easy way")
inherits(x, what = "Date") inherits(Sys.Date(), what = "Date")
inherits(x, what = "POSIXct") inherits(Sys.time(), what = "POSIXct")

8.8.1.1 判斷是否為數值

使用 is.numeric() 函數請 R 語言判斷這個變數是不是數值。

is.numeric(8.7)
## [1] TRUE
is.numeric("8.7")
## [1] FALSE

8.8.1.2 判斷是否為整數

使用 is.integer() 函數請 R 語言判斷這個變數是不是整數。

is.integer(7L)
## [1] TRUE
is.integer(7)
## [1] FALSE

8.8.1.3 判斷是否為邏輯值

使用 is.logical() 函數請 R 語言判斷這個變數是不是邏輯值。

is.logical(FALSE)
## [1] TRUE
is.logical("FALSE")
## [1] FALSE

8.8.1.4 判斷是否為文字

使用 is.character() 函數請 R 語言判斷這個變數是不是文字。

is.character("TRUE")
## [1] TRUE
is.character(TRUE)
## [1] FALSE

8.8.1.5 判斷是否為日期

使用 inherit(x, what = "Date") 函數請 R 語言判斷這個變數 x 是不是日期,Sys.Date() 是一個 Date 的型別,因此 inherit(Sys.Date(), what = "Date") 函數會回傳 TRUE;而 "1970-01-01" 是一個 Character 的型別,inherit("1970-01-01", what = "Date") 函數會回傳 FALSE

inherits(Sys.Date(), what = "Date") # Sys.Date() 是日期型別
## [1] TRUE
inherits("1970-01-01", what = "Date") # "1970-01-01" 是文字型別
## [1] FALSE

8.8.1.6 判斷是否為時間

使用 inherit(x, what = "POSIXct") 函數請 R 語言判斷這個變數 x 是不是時間,Sys.time() 是一個 POSIXct 的型別,因此 inherit(Sys.time(), what = "POSIXct") 函數會回傳 TRUE;而 "1970-01-01 00:00:00" 是一個 Character 的型別,inherit("1970-01-01 00:00:00", what = "POSIXct") 函數會回傳 FALSE

inherits(Sys.time(), what = "POSIXct") # Sys.time() 是時間型別
## [1] TRUE
inherits("1970-01-01 00:00:00", what = "POSIXct") # "1970-01-01 00:00:00" 是文字型別
## [1] FALSE

8.8.2 變數型別的轉換

使用 as.型別名稱() 這樣形式的函數進行轉換。

函數 範例
as.numeric() as.numeric(7L)
as.integer() as.integer(7)
as.logical() as.logical("TRUE")
as.character() as.character(Sys.Date())
as.Date() as.Date("1970-01-01")
as.POSIXct() as.POSIXct("1970-01-01 00:00:00", tz = "GMT")

8.8.2.1 轉換成數值

使用 as.numeric() 函數轉換變數為數值型別,我們可以輸入整數、邏輯值、日期或時間讓 R 語言轉換成數值。

as.numeric(7L)
## [1] 7
as.numeric(TRUE)
## [1] 1
as.numeric(FALSE)
## [1] 0
as.numeric(Sys.Date())
## [1] 17663
as.numeric(Sys.time())
## [1] 1526089073

8.8.2.2 轉換成整數

使用 as.integer() 函數轉換變數為數值型別,我們可以輸入沒有小數位數的數值、邏輯值、日期或時間讓 R 語言轉換成整數。

as.integer(7)
## [1] 7
as.integer(TRUE)
## [1] 1
as.integer(FALSE)
## [1] 0
as.integer(Sys.Date())
## [1] 17663
as.integer(Sys.time())
## [1] 1526089073

8.8.2.3 轉換成邏輯值

使用 as.logical() 函數轉換變數為邏輯值型別,輸入數值或整數型別的 0 會轉換成為 FALSE,其他的數字則一律轉換為 TRUE

as.logical(0)
## [1] FALSE
as.logical(0L)
## [1] FALSE
as.logical(1L)
## [1] TRUE
as.logical(-1.3)
## [1] TRUE
as.logical(87)
## [1] TRUE

輸入文字型別的 "TRUE""True""true" 則會轉換成為 TRUE,反之亦同。

as.logical("TRUE")
## [1] TRUE
as.logical("True")
## [1] TRUE
as.logical("true")
## [1] TRUE
as.logical("FALSE")
## [1] FALSE
as.logical("False")
## [1] FALSE
as.logical("false")
## [1] FALSE

8.8.2.4 轉換成文字

使用 as.character() 函數轉換變數為文字型別,我們可以輸入任意的變數型別讓 R 語言轉換成文字。

as.character(8.7)
## [1] "8.7"
as.character(87L)
## [1] "87"
as.character(TRUE)
## [1] "TRUE"
as.character(Sys.Date())
## [1] "2018-05-12"
as.character(Sys.time())
## [1] "2018-05-12 01:37:53"

8.8.2.5 轉換成日期

使用 as.Date() 函數轉換變數為日期型別,我們可以輸入文字讓 R 語言轉換成日期。as.Date() 函數預設可以識別 %Y-%m-%d%Y/%m/%d 這兩種格式。

as.Date("1970-01-01")
## [1] "1970-01-01"
as.Date("1970/01/01")
## [1] "1970-01-01"

如果是其他的格式,必須要加入 format 參數告知日期被記錄的文字格式為何,函數才能順利轉換,例如文字不是預設的格式,而是將月與日的資訊寫在年份的前面,沒有以 format 參數指定就會轉換成錯誤的日期:

as.Date("01-01-1970") # 轉換錯誤
## [1] "1-01-19"
as.Date("01-01-1970", format = "%m-%d-%Y") # 轉換正確
## [1] "1970-01-01"
as.Date("01/01/70") # 不是預設格式,轉換失敗
## Error in charToDate(x): character string is not in a standard unambiguous format
as.Date("01/01/70", format = "%m/%d/%y") # 轉換正確
## [1] "1970-01-01"

不同符號所代表的格式整理在下表:

符號 格式 範例
%d 01
%a 禮拜幾的縮寫 Mon
%A 禮拜幾 Monday
%m 01
%b 月名稱的縮寫 Jan
%B 月名稱 January
%y 兩位數的年 70
%Y 四位數的年 1970

想查詢更多的格式,可以在 R Console 輸入 ?strptime

8.8.2.6 轉換成時間

使用 as.POSIXct() 函數轉換變數為時間型別,我們可以輸入文字讓 R 語言轉換成時間。如果沒有指定參數 tz =會預設使用電腦的時區。

as.POSIXct("1970-01-01 00:00:00")
## [1] "1970-01-01 UTC"
as.POSIXct("1970-01-01 00:00:00", tz = "GMT")
## [1] "1970-01-01 GMT"