關卡 1

這門課程要跟各位同學介紹JSON(JavaScript Object Notation)資料的處理。

關卡 2

JSON在網際網路上已經是最廣泛使用的資料格式之一,尤其常見於各種傳遞資料的API之中。 舉例來說,台北的YouBike、或甚至是Facebook上的API資料,都是以JSON格式傳輸。

關卡 3

JSON格式也非常簡單,一般人可以輕鬆理解。 舉例來說,請同學輸入:cat(facebook_error, sep = "\n")來看看Facebook的錯誤訊息。

cat(facebook_error, sep = "\n")

關卡 4

從畫面上,我們可以看到整個訊息的前後分別有{},代表整個訊息就是一個JSON物件。 每個JSON物件中則包含一對對「名稱」與「值」的組合。 上述例子中,Facebook錯誤訊息的這個JSON物件只包含一對名稱與值的組合。名稱是:“error”,值則是另一個JSON物件。

關卡 5

請問同學,在“error”的值中,包含的一對一對名稱與值的組合,而對應到值為“OAuthException”的為下列哪一個名稱呢?

type

關卡 6

在R 中,我們可以利用jsonlite套件,將一個JSON文件轉換成R 的list。 請同學先安裝jsonlite套件。

check_then_install("jsonlite", "0.9.19")

關卡 7

接著,請載入jsonlite套件。

library(jsonlite)

關卡 8

接著,請同學檢查看看jsonlite套件的作者是否提供vignette,供R 的使用者閱讀。 請同學輸入:vignette(package = "jsonlite")

vignette(package = "jsonlite")

關卡 9

jsonlite的作者很熱情,一口氣寫了四、五篇vignettes。 由於這堂課的目的是要上手jsonlite物件,所以就請同學輸入:vignette("json-aaquickstart", "jsonlite")

vignette("json-aaquickstart", "jsonlite")

關卡 10

這份文件是以網頁,而非pdf的形式呈現給使用者。 這種方式雖然比較不正式,但是和pdf相比之下較為彈性,近年來受到許多R 套件開發者的青睞。 例如我本人也寫了一篇這種類型的vignettes。

關卡 11

在Vignettes的第一段,很清楚的說明了jsonlite的功能:讓R 和JSON物件之間能在不損失資訊的狀況下轉換。 作者給了一個簡單的範例:使用jsonlite提供的toJSONfromJSON,可以完整的還原一個R 的data.frame。 這種方式,是驗證轉換過程中是否有遺失資訊的一種方式。

關卡 12

範例中所使用的all.equal是一種用來驗證程式碼正確性時,經常使用到的工具。 它會一字不漏的比對兩個R 物件,必需要一模一樣才會回傳TRUE,否則會回報不一致的理由。

關卡 13

文件的第二段描述jsonlite在把JSON物件轉換成R 物件時,會自動嘗試把JSON物件轉換成更適合的物件。 舉例來說,如果直接轉換["Amsterdam", "Rotterdam", "Utrecht", "Den Haag"],結果可能會變成一個長度為4 的list,且每個元素都是長度為1 的字串向量。

關卡 14

我們已經把["Amsterdam", "Rotterdam", "Utrecht", "Den Haag"]這段文字儲存於變數x1。 請同學試試看:fromJSON(x1, simplifyVector = FALSE)

fromJSON(x1, simplifyVector = FALSE)

關卡 15

如上方所述,這是一個長度為4 的list,而且每個元素都是長度為1 的字串向量。

關卡 16

但是如果我們把simplifyVector設定成TRUE,那結果就會不同。 本案例中,因為simplifyVector參數的預設值是TRUE,所以不需要額外的設定。 如果想知道函數的預設值,說明文件中都有清楚的記載。 請同學輸入:fromJSON(x1)

fromJSON(x1)

關卡 17

jsonlite的vignettes後面的段落有處理JSON細節的仔細介紹,未來同學如果有需要使用,建議先將作者提供的vignettes讀過,能省掉很多找bug的時間。

關卡 18

jsonlite是很方便的套件,請同學利用它來萃取youbike API所抓下來的資料。 請同學在完成之後存檔,並輸入submit()來檢查結果是否符合預期。 如果同學在檔案中看到亂碼,請使用Rstudio 左上角的File ->Reopen With Encoding… -> 選取:UTF-8

youbike1 <- fromJSON(youbike_path)

sna1 <- youbike1$result$results$sna
lat1 <- youbike1$result$results$lat
lng1 <- youbike1$result$results$lng
tot1 <- youbike1$result$results$tot
sbi1 <- youbike1$result$results$sbi

rm(youbike1) # 刪除youbike
youbike2 <- fromJSON(youbike_path, simplifyDataFrame = FALSE)
results <- youbike2$result$results
#' `sapply(results, "[[", "sna")`,在R中等價於以下的函數:
#' sapply(results, function(element) {
#'   element[["sna"]]
#' })
sna2 <- sapply(results, "[[", "sna")
lat2 <- sapply(results, "[[", "lat")
lng2 <- sapply(results, "[[", "lng")
tot2 <- sapply(results, "[[", "tot")
sbi2 <- sapply(results, "[[", "sbi")

answer <- data.frame(stringsAsFactors = FALSE,
  sna = sna2,
  lat = as.numeric(lat2),
  lng = as.numeric(lng2),
  tot = as.integer(tot2),
  sbi = as.integer(sbi2)
)

stopifnot(nrow(answer) == 10)
stopifnot(isTRUE(all.equal(sum(answer$lat), 250.35549511)))
stopifnot(isTRUE(all.equal(sum(answer$lng), 1215.65644412)))
stopifnot(sum(answer$tot) == 702)
stopifnot(sum(answer$sbi) == 0)