[R語言初學] R語言當中的資料型態
2Last Updated on 2023-10-22
什麼是資料型態?和資料結構有何差異?R語言當中有什麼常見的資料型態?本文告訴你
你可能聽過資料結構(data type)與資料型態(data type),但是不知道兩者有什麼差別。請別灰心,這很自然,我剛學的時候也會混淆。
本文會介紹R語言當中的資料型態,包含字元、數值、日期、布林邏輯等,並一併告訴大家如何判斷資料型態,還有轉換資料型態的具體方法。
資料結構指的是程式語言中,資料儲存的長相或者形式,例如我們平常時常見到的表格,就是一種以行與列儲存資料的長相;假設有張紙條上面記載著一連串的數字,這樣也是一種資料結構。
至於資料型態,則是指資料的類型,例如資料的原始型態為數值,也可能是字串,又或者是布林邏輯的值(TRUE
與 FALSE
)。
簡單來說,資料型態是資料的最基本單元,資料結構則是將資料組合在一起的方法。
如果以搭建房子比喻儲存資料,資料結構就是房屋的架構,有可能是用行列搭建,也可能採不規則的樑柱排序;資料型態則像是建材的材質,房子可能是木頭質地的,也可能用磚砌。無論是何者,主要看使用者的資料有何特點,像是國中某班某次段考30位同學的成績,就適合用資料表這種資料結構儲存,分數的資料型態則很明顯是數值。
我們會在這篇文章中介紹R語言當中的資料型態。
觀念釐清:資料型態、資料結構與資料格式的差異
我們來複習一下資料型態(Data Type)、資料結構(Data Structure)與資料格式(Data Format)的意義跟差別。
資料型態(Data Type)
- 意義:資料型態指資料儲存的原始型態,可以說是資料的最小單位。
- 範例:
int
eger,float
,string
,boolean
等。
資料結構(Data Structure)
- 意義:資料結構指將資料組合後儲存的長相與形式。
- 範例:
Array
,Linked List
,Stack
,Queue
,Tree
,Graph
等。在R語言當中以vector, list, dataframe為主。
資料格式(Data Format)
- 描述:資料格式指資料儲存的規範或標準,用來定義交換、展示與儲存資料在文件或其他媒介中的方法。
- 範例:
CSV
,JSON
,XML
,JPEG
,MP3
等。在R語言當中最有特色的是RDS,只能用R語言打開。
關係與差異
- 資料型態是最基本的單位,用於界定變數或對象可以存儲哪種類型的數據。
- 資料結構是由多個資料型態的資料組成,用來提升儲存、排序、搜尋資料的效率。
- 資料格式更往上一層,規定了(用特定資料結構儲存的)資料(用特定資料結構已經組織在某種
Data Structure
中)如何存在電腦裡、與他人交換。
總結來說,資料型態最基礎,資料結構由資料型態組合而成,而資料格式則是用於儲和存交換資料結構和資料型態的標準或規範。
以台灣籃球聯盟T1為例,想看某場比賽球員的表現,球員名字以character的資料型態儲存、得分以integer的資料型態儲存,將兩者以資料結構組合成dataframe,再輸出成csv, xlsx,讓其他人也能方便讀取。
R語言中的資料型態
R語言中有許多的資料型態,其中,最常用的是底下這4種:數值、字元、布林邏輯、日期。
數值
可以用平常見到的「數字」理解數值型態,例如3.14
、9.8
等等都屬於數值類型。不過,有些看起來屬於數值類型的資料,其實是以其他資料型態存在,像是整數型態(Integer)和複數型態(Complex)的數值,前者像是1
、2
、3
,後者則像1+2i
、3+4i
。
實務上最常混淆數值與整數,同樣都是1
這個數字,若是數值型態,會是1.0
,整數型態則是1
,兩者看起來沒有差別,但是在後面碰上資料結構時,就會顯現出差異。
例如向量(vector)的儲存規則為每個元素(element)的資料型態必須相同,若有人三次段考的分數分別為90
、90.1
、91.9
,以數值型態儲存則沒有問題,但如果是以整數型態儲存,則會四捨五入。
# numeric
c(90,90.1,91.9)
#> [1] 90.0 90.1 91.9
# integer
as.integer(c(90,90.1,91.9))
#> [1] 90 90 91
在上方的程式碼中,as.integer()
是一個函數(function),它可以把其他資料型態的變數轉換為整數。
函數是什麼呢?簡單來說,就是使用者輸入值後,按照特定邏輯輸出結果的方法,例如在學校學過的 y = ax + b
,若 a = 2, b = 1
,我們只要輸入 x = 1
,就會得到y = 3
,這就是一個函數。
字元
可以用書本上或是文章中的「文字」理解字元型態,無論是"你好嗎"
、"Halo"
,或者"3.14"
都屬於字元型態。不過,為什麼"3.14"
不是數值而是字元呢?因為我們在外面已經先用引號將其包覆,在R語言當中,無論是單引號或者雙引號,只要包覆了文字或者數字,都會變成字元型態,也因此無法再拿"3.14"
進行四則運算了。
3.14 + 1.86
#> [1] 5
"3.14" + 1.86
#> Error in "3.14" + 1.86: 二元運算子中有非數值引數
上面這串## Error in "3.14" + 1.86: non-numeric argument to binary operator
文字,就是執行程式碼出錯時會跳出的訊息。我自己剛學R的時候看到錯誤訊息,臉都會很臭,因為代表我的程式碼寫錯了,接下來會開始怨天尤人,不想看懂只想改對,有時候則會搖尾乞憐,請前輩幫忙指點。後來才知道,學會閱讀錯誤訊息是學程式的重要課題,因為只有看懂才能知道該如何。
細看上面的訊息,前半段"Error in "3.14" + 1.86 :"
是出錯的程式碼內容,後半段non-numeric argument to binary operator
又可以分為兩半,後面的”binary
operator”當中的binary就是雙元,因此”binary
operator”翻譯成二運算子,顧名思義就是指運作於兩個運算元的運算子。
什麼是運算元跟運算子?就算不懂也沒關係,單純看"3.14" + 1.86
這個算式,裡面的加號處理的兩個運算元為"3.14"
與1.86
,加號本身就是"binary operator"
。
看懂後面,前面就好懂了,non-numeric argument
直翻為非數值的參數,因為"3.14"
屬於字元型態,不是數值型態,但是加法這個二元運算子只能處理數值,我們丟了一個字元給它,它就出錯了,所以整體來看,non-numeric argument to binary operator
就是在說我們把一個非數值的參數(也就是"3.14"
)丟給一個二元運算子(也就是加號),所以報錯。
底下我們分別看把字元變成數值,以及數值變成字元的結果。
# 字元變數值
as.numeric("3.14")
#> [1] 3.14
# 數值變字元
as.character(3.14)
#> [1] "3.14"
布林邏輯
布林邏輯聽起來很神祕,但其實就是以前考試是非題當中的「是」與「非」。「是」對應到TRUE
,「非」對應到FALSE
,也可以用單一個大寫字母T
和F
代替。
我們可以用比較運算子(comparison operators)或者邏輯運算子(logical
operators)產出布林邏輯型態的資料:
2 > 1
#> [1] TRUE
(2 > 1) & (2 > 3)
#> [1] FALSE
第一行程式碼是運用比較運算子單純的比大小,第二行則是運用邏輯運算子中「且」的概念,因為沒有同時滿足,所以結果為FALSE
。
其他的比較運算子包含:
>
:大於<
:小於>=
:大於等於<=
:小於等於==
:等於!=
:不等於
其他的邏輯運算子包含:
- & 以及 &&:且:大於
- | 以及 ||:或
- !:否
要怎麼用這些運算子呢?後續會再介紹。另外需要注意的是,R語言的程式碼對於大小寫是敏感的(case sensitive),TRUE
不能寫成True
,所以底下程式碼的第二行會報錯。
TRUE
#> [1] TRUE
True
#> Error in eval(expr, envir, enclos): 找不到物件 'True'
日期
日期可以說是資料型態中,相對複雜難解的一種。它的加減運算不同於數值,想要提取年月日需要特定的函數,而且呈現方式也不只有數字,可能會用上英文的月份名稱,有時是全名有時是簡寫。
Sys.Date()
#> [1] "2023-02-28"
months(Sys.Date())
#> [1] "二月"
若單獨出現日期也就算了,再加上時間(time)會更需要花心思處理,有專門的套件(library)例如lubridate
可以對付日期與時間,之後在應對日期時間資料的章節會細談。
資料型態判斷與轉換
這個小節中,我們主要會介紹資料型態的判斷與轉換。
資料型態判斷
如同上面的介紹,R語言當中有好幾種資料型態,如果第一眼看到資料,不確定是什麼型態時,可以先用class()
函數查看資料型態。
class(1)
#> [1] "numeric"
class(1.0)
#> [1] "numeric"
欸,為什麼兩個都是數值變數,第一行不應該是整數嗎?因為如果想創建整數,需要明確地在數字後面加上”L”,若沒有加上,就會自動當成數值。
1L
#> [1] 1
class(1L)
#> [1] "integer"
1
#> [1] 1
class(1)
#> [1] "numeric"
想要判斷資料型態,除了使用 class()
以外,還能使用is._()
開頭的函數,確認某個變數是不是那個資料型態。
is.integer(1L)
#> [1] TRUE
is.character("你好嗎")
#> [1] TRUE
資料型態轉換
若是想要轉換資料型態,可以利用as._()
開頭的函數。這邊會介紹一些轉換時的獨特情形,還請細細參詳。
# character to integer
as.integer("10")
#> [1] 10
# character to integer
as.integer("十")
#> Warning: 強制變更過程中產生了 NA
#> [1] NA
在上面的案例中,"10"
本來是字元,但因為雙引號中的文字可以變成整數,因此可以成功轉換,但國字"十"
沒有辦法變成數字,所以就會變成代表遺失值(missing value)的NA
了。這個NA
看起來毫不起眼,日後卻會反覆出現。
# character to integer
as.integer("10.4")
#> [1] 10
# character to numeric
as.numeric("10.4")
#> [1] 10.4
雖然都是10.4
,但as.integer()
會四捨五入,as.numeric()
則不會。接下來則是常用的布林邏輯轉數字,TRUE
轉換為數值或者整數,都會變成1
,FALSE
則會變成0
。這有什麼要緊呢?在日後使用條件判斷式 if-else statement 的時候,我們有時候會拿條件判斷的結果來運算,這時就會將T
和F
從布林邏輯值轉換成數字了。
# boolean to integer
as.integer(T)
#> [1] 1
# boolean to integer
as.integer(F)
#> [1] 0
# +,-,*,/
is.numeric("10")+is.numeric(10)
#> [1] 1
我們同樣可以將數值或整數轉換成布林邏輯值,但需要注意的是,只有0
可以變成FALSE
,0
以外的數值都會轉變成TRUE
。
# numeric to boolean
as.logical(1)
#> [1] TRUE
as.logical(2)
#> [1] TRUE
as.logical(-1)
#> [1] TRUE
as.logical(-2)
#> [1] TRUE
# numeric to boolean
as.logical(0)
#> [1] FALSE
至於日期,可以從字串得來,還可以轉換成數字。為什麼可以轉換成數字呢?因為R語言中,將1970-01-01
設定為日期的開始,當天對應的數值為0
,每往後一天就會加上1,每往前一天則會減去1。
# date to numeric
as.numeric(as.Date("2023-02-01"))
#> [1] 19389
as.numeric(as.Date("1970-01-01"))
#> [1] 0
# character to date
as.Date("2023-02-01")
#> [1] "2023-02-01"
as.Date("2023/02/01")
#> [1] "2023-02-01"
另外,as.Date()
可以指定字元的格式,藉此判斷使用者提供的字串中,哪些可以對應到年月日。
# character to date
as.Date("2023-02-01", format = "%Y-%m-%d")
#> [1] "2023-02-01"
as.Date("20230201", format = "%Y%m%d")
#> [1] "2023-02-01"
as.Date("02/01/2023", format = "%m/%d/%Y")
#> [1] "2023-02-01"
as.Date("二月 01 2023", format = "%B %d %Y")
#> [1] "2023-02-01"
底下整理一個表格供參考,可以讓R語言知道你的原始日期格式到底是什麼。
符號 | 對應 | 例子 |
---|---|---|
%Y | 年,四字 | 2023 |
%y | 年,兩字 | 23 |
%m | 月,兩字 | 01 |
%B | 月,完整單字 | March/三月 |
%b | 月,簡寫單字 | Mar/三月 |
%d | 日,兩字 | 31 |
%A | 星期幾,完整 | Monday/週一 |
%a | 星期幾,簡寫 | Mon/週一 |
到這裡差不多介紹完了R語言當中的資料型態,以及不同資料型態的判斷與轉換方法,下邊則要討論怎麼在R語言裡面基本運算啦!
小結
我們在這篇文章中認識了R語言當中的資料型態,接下來則會找機會介紹R語言裡的基本運算。希望你喜歡這篇文章。
2023-06-03 at 07:42 //
I may need your help. I tried many ways but couldn’t solve it, but after reading your article, I think you have a way to help me. I’m looking forward for your reply. Thanks.
2023-06-13 at 01:14 //
Reading your article helped me a lot and I agree with you.