第 3 章 R Basics

參考資料

3.1 Packages

3.1.1 使用

library(googlesheets4)
若出現如下錯誤訊息,表示得先安裝相關套件:

Error in library(googlesheets4) : there is no package called ‘googlesheets4’

package若有成功library進來它會出現在:

3.1.2 視窗點選

以同時安裝dplyr及lubridate為例

3.1.3 指令

只安裝googlesheets4:

install.packages(c("googlesheets4"))

安裝googlesheets4及tidyverse:

install.packages(c("googlesheets4","tidyverse"))

3.2 Create an object with values

這章我們學習如何創造R裡帶有資訊的物件,在R裡創造方式為:

ObjectName <- object_value

object_value -> ObjectName
a <- 2
a = 2 # = 與 <-同義

3 -> b

#以後的文字均不會被當程式執行:

  • 適合用來進行程式註解。

  • 取消某行程行執行可將#放在一行開頭。

注意到右上角的Environment tab會顯示剛創的兩個objects及其values.

A valid variable name consists of:

  • letters, numbers and the dot (.) or underline (_) characters.

A variable name starts with:

  • a letter;
  • or the dot not followed by a number.

下面幾個可以被R接受:

my_108_total_credits <- 15
_108_total_credits <- 15
108_total_credits <- 15
_my_108_total_credits <- 15
my.108.total_credits <- 15
.108.total_credits <- 15 # 隱藏變數
.my.108.total_credits <- 15
我的108學年總學分數 <- 15
`我的108學年總學分數` <- 15 # 特殊名稱處理,`不是名稱的一部份
`.108.total_credits` <- 15

一個chunk內若想:

  • 只執行某一行, 游標放在該行任意處,按Ctrl+Enter (Mac: Command+Enter)。

  • 只執行某一塊, 滑鼠按住左鍵選擇該區塊放開後,按Ctrl+Enter (Mac: Command+Enter)。

常見命名方式:

  • snake: my_total_credits <- 15

  • camel: myTotalCredits <- 15

3.3 Calling an object

在變數設定以外的情境,以變數名稱設定時之寫法寫下變數名稱時,會是在呼叫變數

a <- 15 # 變數設定
b <- a # 呼叫a的值,用來設定變數b的值,相當於 b <- 15

建立一個變數名稱為我的年紀,其值為20。另外建立一個變數名稱為my_age,其值以呼叫我的年紀變數取得。

要呼叫某物件時,請先確認Environment tab裡有該物件存在,或執行ls()

3.4 Atomic Vector

c(...)將「相同類型」值以「逗點」分隔而形成的向量:

c(value1, value2, value3)
num1 <- 5
vNum1 <- c(2,5)
vNum2 <- c(2, num1)

只有一個值的向量可以不寫c()

以下兩變數值相同:

num1 <- 5
num2 <- c(5)

向量的堆疊依然是向量:

vNum1 <- c(-1,5)
vNum2 <- c(2,3)
vNum3 <- c(vNum1,vNum2)
vNum4 <- c(-1,5,2,3)
vNum5 <- c(c(-1,5),c(2,3))
vNum1 <- c(-1,5)
vNum2 <- c(2,3)
vNum4 <- c() # 空向量

請問以下操作後的變數(vNum3, vNum4)值為何?

vNum3 <- c(vNum2, vNum1)
vNum4 <- c(vNum4, vNum1) # 向量疊代 (recursive vector concatenate)

若反覆操作,它們會有什麼變化?

Atomic vector依其值的型態(type)而分成以下三類型:

  • Numeric
  • Character
  • Logical

3.4.1 Numeric

num1<-5 # 非整數
num2<-5L # 整數

num1Vector<-c(5,6,7)
num2Vector<-c(5L,6L,7L)
  • 5會被電腦記成5.0000

  • 5L才會被當成整數5

3.4.2 Character/String (vector)

在定義object value時,必需要用「成對」的雙引號"或單引號',把每個字串括起來。

char1<-"你好"
char2<-'你好'
char1Vector<-c("你好","How are you?")

使用class()查詢上述物件類別。

若要產生小明說:"明天不用上課。",值的內容有包含",這時可改用成對的'定義值,如:

dialogue <- c('小明說:"明天不用上課。"',
               '小花說:"真的嗎?"')

cat(dialogue, sep="\n")

有時一道指令太長為閱讀容易我們會想換行,但又不想讓R以為指令在第一行已經結束。R的換行原則是:

  • 只要指令定義不全,如括號不成對少右半邊(只有(沒看到))、結尾是, 等等

都表示指令不完整,R會再往下一行看,如下一行還是不完整就再往下一行。

以下只有三行指令:

library(httr)
library(dplyr)
GET("https://api.github.com/repos/tpemartin/108-2-56-inclass-practice/commits") %>%
  content() %>%
  View()
  • 小心字串一對引號(“…”)中間不可隨便斷行,它會改變字串的內容。

查詢函數用法:

  1. Script window: 游標放在函數名稱任何一個位置,按F1。
  2. Console window: ?函數名稱 按enter。
  3. 右下角Help tab的放大鏡打函數名稱。

以上對白請把它改成:

小明說:'明天不用上課。'
小花說:'真的嗎?'

以下程式碼中變數出席狀況表單網址缺乏變數值,請貼上本班出席表單網址使該變數值有定義。

library(googlesheets4)
`出席狀況表單網址` <-  # 缺乏定義值
ss <- as_sheets_id(`出席狀況表單網址`)
homework_sheet <- sheets_read(ss,sheet=1)

3.4.3 Logical

邏輯:

  • 為「真」: TTRUE (要全大寫)。

  • 為「否」: FFALSE (要全大寫)。

logi1 <- c(T,TRUE,F,FALSE,FALSE)

邏輯值遇到數學運算時T會被當成1,而F會被當成0。

# 加總向量內的所有元素值。
sum(logi1)

3.4.4 typeof()

顯示atomic vector元素的基本認定型態,它代表電腦記憶體在儲存時真正看待的型態。

num <- c(1.5, 2, 3)
int <- c(1L, 3L, -2L)
char <- c("1.5","2","3")
logi <- c(T,T,F)

typeof(num)
typeof(int)
typeof(char)
typeof(logi)

3.4.5 class()

依資料的螢幕顯示型態及能對它進行的操作所做的分類。

class(num)
class(int)
class(char)
class(logi)

3.5 Atomic Vector Extended

以Atomic types為基礎,因應資料需求而延伸的資料儲存類別。以下介紹兩個數字串向量而延伸的兩個類別:

  • factor: 類別資料
factor(
  字串向量
)
  • POSIXct/POSIXt: 時間資料。
ymd_hms(
  字串向量
)

3.5.1 Factor

當資料值只有固定幾類反覆出現時,此類資料稱之為類別資料(factor or categorical data):

  • 系級
  • 性別
# 10位學生的主系
majors10_char <- c('經濟學系','社會學系','社會學系','經濟學系','經濟學系','社會學系','經濟學系','經濟學系','經濟學系','社會學系')

typeof(majors10_char)
class(majors10_char)
majors10_factor <- factor(majors10_char)
# 或
majors10_factor <- factor(
  c('經濟學系','社會學系','社會學系','經濟學系','經濟學系','社會學系','經濟學系','經濟學系','經濟學系','社會學系')
)

typeof(majors10_factor)
class(majors10_factor)

majors10_char及majors10_factor的螢幕顯示型態是不同的:

majors10_char
majors10_factor

as.integer()將資料的class轉成integer。由於factor被轉成integer後,其螢幕顯示會顯示電腦是用什麼數字在存這些類別資料。

as.integer(majors10_factor) 

數字與類別文字的對照表mapping table:

levels(majors10_factor)

Atomic vector延伸的類別,其基本儲存型態還是會落在先前的基本型態裡,但多了資料顯示的mapping table。

參考資料:https://r4ds.had.co.nz/factors.html

3.5.2 Class conversion

R有一系列的as.{class名稱}()的函數用來轉換物件的class;as.{type名稱}()用來轉換物件的type。

as.integer(...)會將…物件(嘗試)轉成integer class/type的物件。例如:

stringInteger <- c("1","2","-11")
class(stringInteger) # 無法進行數值運算
stringInteger+2
trueInteger <- as.integer(stringInteger)
class(trueInteger) # 可以進行數值運算
typeof(trueInteger)
trueInteger+2
char3 <- c("小明","1","3")
as.integer(char3)

R的函數均不會主動更改輸入物件,只會把結果值另行輸出,所以若要保留轉換結果則必需指定到一個物件名稱上。

as.integer(stringInteger)
class(stringInteger)
stringInteger <- as.integer(stringInteger)
class(stringInteger)

以下為學生學號

studentId <- c(410773002, 410773015)
  • 它目前是什麼class
  • 學號用什麼class比較合理

3.5.3 Date/Time

若收到兩筆訂單分別來自:

  • 台北 2020-03-18 13:52:40
  • 葡萄牙 Mar.18, 2020, 05:52:40
    上兩個時間/地點。哪一筆訂單最先進來?

答案是:同時間。

如何比較兩時間的先後必需要將這兩個時間轉換成同時區。

處理時間所需資訊:

  • 時間輸入方式
  • 時間來自時區
  • 時間儲存以哪個時區為準(進行比較用)

其中最後一點通訊協定是採UTC時區為公定標準,故不用煩惱。

library(lubridate)

3.5.3.1 台北

2020-03-18 13:52:40

  • 時間輸入方式: ymd_hms
  • 時間來自時區: Asia/Taipei
tpeTime <- ymd_hms("2020-03-18 13:52:40",
        tz="Asia/Taipei")

我們使用lubridate中的ymd_hms函數有以下兩種做法:

作法一:使用library()

library(lubridate)
tpeTime <- ymd_hms("2020-03-18 13:52:40",
        tz="Asia/Taipei")

作法二:不使用library()

tpeTime <- lubridate::ymd_hms("2020-03-18 13:52:40",
        tz="Asia/Taipei")

3.5.3.2 葡萄牙

Mar.18, 2020, 05:52:40

  • 時間輸入方式: mdy_hms
  • 時間來自時區: Europe/Lisbon
pgTime <- mdy_hms("Mar.18, 2020, 05:52:40",
                  tz="Europe/Lisbon")

來自相同時區相同輸入方式的時間文字字串,可以直接套入相同函數轉成Date/Time類別。

tpeTime <- c("2020-03-18 13:52:40",
             "2020-03-11 03:12:40")
tpeTime <- 
  ymd_hms(
    tpeTime, # 呼叫tpeTime向量值
    tz="Asia/Taipei"
    )

相當於

tpeTime <- 
  ymd_hms(
    c("2020-03-18 13:52:40","2020-03-11 03:12:40"),
    tz="Asia/Taipei"
    )
  • 若時間字串來自UTC,那可以不設定tz參數,即
ymd_hms(
c("2020-03-18 13:52:40","2020-03-11 03:12:40")
)
  • 若時間字串長得像“2020-03-11T06:56:17Z”,它來自UTC時區:
ymd_hms("2020-03-11T06:56:17Z")

執行以下程式,其中firstCommit及lastCommit均來自台灣時區:

browseURL("https://docs.google.com/spreadsheets/d/1EAG49qDKPWWi0ebkVr29TLrvVnoBPfkvxYY-J3xLAHY/edit#gid=458686645")

選一位學生將他的created_at, firstCommit, lastCommit存成Date/Time(即POSIXct) class的向量。

3.5.3.3 以某個時區顯示

List of Time Zones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

以UTC表示

with_tz(tpeTime, tzone="UTC")
with_tz(pgTime, tzone="UTC")

以Europe/Paris表示

with_tz(tpeTime, tzone="Europe/Paris")
with_tz(pgTime, tzone="Europe/Paris")

小心以下兩函數的input中一個是tz,一個是tzone

ymd_hms(..., tz="Asia/Taipei")
with_tz(..., tzone="Asia/Taipei")

將上一個練習的時區都以Europe/Paris顯示。

3.5.3.4 type

class(tpeTime)
typeof(tpeTime)

as.numeric(tpeTime)
as.numeric(pgTime)

Date/Time的儲存其實是數值,它的原點是UTC時區的1970-01-01 00:00:00。 as.numeric(tpeTime)的值顯示tpeTime離那個原點已過了多少秒。

數於Date/Time class的向量內容是數值,所以它是可以做數值相減。請問台北“2020-03-11 12:55:30”與巴黎“2020-02-13 15:33:10”相減後會出現什麼?

3.6 List

listObject <- list(value1, value2, value3)

特色:

  1. 每個元素值可以是不同type的物件值。
小明於「2020-03-31T13:40:55Z」在「一芳」買了「2杯」「水果茶」。
library(lubridate)
`小明交易1` <- list(
  ymd_hms("2020-03-31T13:40:55Z"), # Date/Time class
  "一芳", # character
  2, # numeric
  "水果茶" # character
)
print(`小明交易1`)
  1. 物件值可以是vector形式,也可以list。
小明於「108學年第1學期」修了「高級會計學」、「高等統計學」;於「108學年第2學期」修了「食在拉丁美洲」。
# list含兩個vectors
`小明108學年課表A` <- list(
  c("108-1","高級會計學","高等統計學"),
  c("108-2","食在拉丁美洲")
)
print(`小明108學年課表A`)
# list含兩個lists
`小明108學年課表B` <- list(
  list("108-1","高級會計學","高等統計學"),
  list("108-2","食在拉丁美洲")
)
print(`小明108學年課表B`)
# list含兩個lists, 子層list又各含兩個vectors
`小明108學年課表C` <- list(
  list(
    c("108-1"), # 可只寫 "108-1"
    c("高級會計學","高等統計學")
       ),
  list(
    c("108-2"),
    c("食在拉丁美洲")
  )
)
print(`小明108學年課表C`)
  1. 物件值可以取名。
小明的課表我們可以將108學年第一學期的lists取名108學年第1學期, 將108學年第二學期的lists取名108學年第2學期
# list含兩個lists, 子層list又各含兩個vectors
`小明108學年課表C` <- list(
  `108學年第1學期`=list(
    c("108-1"), # 可只寫 "108-1"
    c("高級會計學","高等統計學")
       ),
  `108學年第2學期`=list(
    c("108-2"),
    c("食在拉丁美洲")
  )
)
print(`小明108學年課表C`)

取名稱時=把它想成<-,名稱在<-左邊,所以物件名稱規定也同樣適用在這裡取名上。

注意:若名稱不符規定要用「反頓點`」一對括起來,千萬不要用一對單引號',或一對雙引號"…那就遜了。


另一個複雜的例子:

GitHub使用者Martin老師,用戶email為mtlin@gm.ntpu.edu.tw,於2020-03-25T07:17:40Z在他的repo進行了更動,助教使用者emilyluckey,用戶email為emily007@gmail.com,於2020-03-26T08:18:40Z幫老師commit了這個變化,她在commit時於summary處寫下了update

* author: Martin老師, mtlin@gm.ntpu.edu.tw, 2020-03-25T07:17:40Z

  • committer: emilyluckey, emily007@gmail.com, 2020-03-26T08:18:40Z

  • message: update

使用Atomic vector

author <- c("Martin老師", "mtlin@gm.ntpu.edu.tw", "2020-03-25T07:17:40Z")

committer <- c("emilyluckey", "emily007@gmail.com", "2020-03-26T08:18:40Z")

message <- c("update")
  • 物件值可以是vector形式,也可以list。
commit <- list(
  author, committer, message
)
print(commit)
  • 物件值可以取名。
commit <- list(
  author = c("Martin老師", "mtlin@gm.ntpu.edu.tw", "2020-03-25T07:17:40Z"),
  committer = c("emilyluckey", "emily007@gmail.com", "2020-03-26T08:18:40Z"),
  message = c("update")
)
print(commit)

author與committer裡的三個資訊如果有名稱會更清楚:

  • list物件值可以取名。(author及committer的值也改用list存起來。)
authorValues <- 
  list( # 由c() 改list(), 元素命名
    name="Martin老師", 
    email="mtlin@gm.ntpu.edu.tw", time="2020-03-25T07:17:40Z"
  )
committerValues <- 
  list( # 由c() 改list(), 元素命名
    name="emilyluckey", 
    email="emily007@gmail.com", time="2020-03-26T08:18:40Z"
  )

commit <- list(
  author=authorValues,
  commmitter=committerValues,
  message="update"
)

print(commit)

記得「list可將好幾個物件,不管它是什麼type都可放在一起」,可將date用Date/Time class存起來:

library(lubridate)
authorValues <- 
  list(
    name="Martin老師",  
    email="mtlin@gm.ntpu.edu.tw",
    time=ymd_hms("2020-03-25T07:17:40Z")
  )
committerValues <- 
  list(
    name="emilyluckey", 
    email="emily007@gmail.com",
    time=ymd_hms("2020-03-26T08:18:40Z")
  )

commit <- list(
  author=authorValues,
  commmitter=committerValues,
  message="update"
)

print(commit)

網路上的資料回傳大部份時候長得像list的格式,如之前查詢commit記錄:

browseURL("https://api.github.com/repos/tpemartin/108-2-56-inclass-practice/commits") 

這裡截取一部份代表某次commit的資訊:

(省略前面)
"commit": {  
      "author": {  
        "name": "Martin老師",  
        "email": "mtlin@gm.ntpu.edu.tw",  
        "date": "2020-03-25T07:17:40Z"  
      },  
      "committer": {  
        "name": "emilyluckey",  
        "email": "emily007@gmail.com",  
        "date": "2020-03-26T08:18:40Z"  
      },  
      "message": "update"  
      (後面省略)  
    }  
(後面省略)  

這種回傳資訊格式叫做JSON(JavaScript Object Notation), 每個程式語法都會準備一個物件形式用來儲存接收到的JSON資料;R會用list type,Python會用dictionary type。

也因為每個程式都可接收JSON形式資料,不同程式開發者若有必要進行網路資訊交換時會將複雜資料結構轉成JSON再交換出去。

我們的程式要在網路送出commit資訊時,可以透過以下程序將list轉成JSON:

library(jsonlite) # 不少同學這行會有Error,還記得如何排除嗎?
toJSON(commit)

學生小明,在108學年第1學期修了Alice老師所授的個體經濟學(得分85分)、Mark老師所授的總體經濟學(得分73分)。在108學年第2學期修了Jason老師所授的作業研究(得分90分)。

使用list你會怎麼記錄上面的資訊。

# 108-1 
course1_1081 <- 
  list(
    name="個體經濟學",
    teacher="Alice",
    grade=85
  )
course2_1081 <-
  list(
    name="總體經濟學",
    teacher="Mark",
    grade=78
  )
`108-1修課記錄` <- 
  list(
    course1_1081,
    course2_1081
  )

# 108-2
course1_1082 <- 
  list(
    name="作業研究",
    teacher="Jason",
    grade=90
  )
`108-2修課記錄` <- 
  list(
    course1_1082
  )

# 整合兩學期
`每學期修課記錄` <- list(
  `108-1`=`108-1修課記錄`,
  `108-2`=`108-2修課記錄`
)

# 完成記錄
`小明的修課記錄` <- 
  list(
    name="小明",
    semesters=`每學期修課記錄`
  )
`小明的修課記錄` <- 
  list(
    name="小明",
    semesters=
      list(
        `108-1`=
          list(
            year=108,
            semester=1,
            courses=
              list(
                list(
                  name="個體經濟學",
                  teacher="Alice",
                  grade=85
                ),
                list(
                  name="總體經濟學",
                  teacher="Mark",
                  grade=78
                )
              )
            ),
        `108-2`=
          list(
            year=108,
            semester=2,
            courses=
              list(
                list(
                  name="作業研究",
                  teacher="Jason",
                  grade=90
                )
              )
            )
      ) 
    )

今(“2020-03-31”)明(“2020-04-01”)「台北市」氣溫最高25度,最低18度;「新北市」氣溫最高24度,最低15度。

一般天氣預報-今明36小時天氣預報:

library(jsonlite)
fromJSON("https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=rdec-key-123-45678-011121314&format=JSON") ->
  weather_next36hours

3.7 物件儲存

存下Global Environment中有的物件:

save(object1, object2, ..., file="myData.Rda")

下次取回來Global Environment用:

load("myData.Rda")
object1 <- c(2,5)
object2 <- ymd_hms(
  c("2015-03-22 12:28:42","2017-12-22 15:33:48"),
  tz="Asia/Taipei"
)
object3 <- list(2, FALSE, c("a","b","c"))
save(object1, object2, object3, file="threeObjects.Rda")
load("threeObjects.Rda")