Chapter 4 Dataframe

4.1 基本操作

4.1.1 產生新的Dataframe

4.1.1.1 建立資料並Assign給vector

用以下ChatGPT問句來產生測試資料「我現在正在準備R的教學範例, 請協助我產生台北市所有行政區的資料,包含行政區名、面積、人口數 分別指給town, area, population三個變數」。

town = c("松山區", "信義區", "大安區", "中山區", "中正區", "大同區", "萬華區", "文山區", "南港區", "內湖區", "士林區", "北投區")

area = c(9.2878, 11.2077, 11.3614, 13.6821, 7.6071, 5.6815, 8.8522, 31.5090, 21.8424, 31.5787, 62.3682, 56.8216) # 單位:平方公里

population = c(206375, 225561, 309835, 203276, 159608, 132397, 194160, 275207, 122103, 287726, 288324, 255688)  # 2023年的估計值

4.1.1.2 合併等長vector為dataframe

df <- data.frame(town, population, area)
df$density = df$population / df$area
str(df)
## 'data.frame':    6 obs. of  4 variables:
##  $ town      : chr  "中正" "大同" "中山" "松山" ...
##  $ population: num  158228 126687 228075 204903 308383 ...
##  $ area      : num  7.61 5.68 13.68 9.29 11.36 ...
##  $ density   : num  20800 22298 16670 22062 27143 ...
summary(df)
##      town             population          area           density     
##  Length:6           Min.   :126687   Min.   : 5.681   Min.   :16670  
##  Class :character   1st Qu.:165651   1st Qu.: 7.918   1st Qu.:20907  
##  Mode  :character   Median :196412   Median : 9.070   Median :21645  
##                     Mean   :202366   Mean   : 9.412   Mean   :21700  
##                     3rd Qu.:222282   3rd Qu.:10.843   3rd Qu.:22239  
##                     Max.   :308383   Max.   :13.682   Max.   :27143
# View(df)

4.1.1.3 存放台灣貿易各國進出口量

country <- c("CN", "US", "JP", "HK", "KR", "SG", "DE", "MY", "VN", "PH", "TH", "AU", "NL", "SA", "ID", "GB", "IN", "FR", "IT", "AE")

import <- c(26.142, 12.008, 7.032, 13.646, 4.589, 5.768, 2.131, 2.802, 3.428, 3.019, 1.976, 1.118, 1.624, 0.449, 0.983, 1.302, 1.027, 0.553, 0.670, 0.455)

export <- c(22.987, 12.204, 11.837, 7.739, 5.381, 4.610, 2.866, 2.784, 2.414, 2.092, 1.839, 1.788, 1.665, 1.409, 1.391, 1.075, 0.974, 0.899, 0.800, 0.728)

4.1.1.4 合併vector為data.frame

當我們讀取或創建資料框架時,過去R預設會將字符串類型的變數轉換為因子(Factors),這對於統計分析而言是有益的,因為統計分析經常將文字型態的數據視為類別變數來處理。然而,隨著資料科學領域的快速發展,需要處理大量文字數據的情況日益增多,這時將文字資料預設為因子型態可能不再適合所有情境。因此,現在R的預設的處理方式已經改變,預設將文字型態的變數保持為字符型態(Character),而不是自動將其轉換為因子。這意味著,當我們使用read.csv等函數讀取數據時,除非明確指定,否則讀入的字符串不會自動轉換為Factors型態。

如果你在進行統計分析時希望將文字型態的變數作為類別變數(即因子)處理,你需要手動設定stringsAsFactors參數為TRUE。這可以在讀取數據時(如使用read.csv函數)或在數據處理過程中明確進行轉換。例如,當使用read.csv讀取CSV文件時,若想將所有的字符串變數自動轉為因子型態,可以這樣做:df <- read.csv("your_file.csv", stringsAsFactors = TRUE)。若已經讀取數據且數據框架中的文字型態變數仍為Character型態,而你希望將其轉換為Factors,可以使用factor函數進行轉換:df$your_column <- factor(df$your_column)

df <- data.frame(country, import, export, stringsAsFactors = TRUE)
str(df)
## 'data.frame':    20 obs. of  3 variables:
##  $ country: Factor w/ 20 levels "AE","AU","CN",..: 3 19 11 7 12 17 4 13 20 15 ...
##  $ import : num  26.14 12.01 7.03 13.65 4.59 ...
##  $ export : num  22.99 12.2 11.84 7.74 5.38 ...
df <- data.frame(country, import, export)
str(df)
## 'data.frame':    20 obs. of  3 variables:
##  $ country: chr  "CN" "US" "JP" "HK" ...
##  $ import : num  26.14 12.01 7.03 13.65 4.59 ...
##  $ export : num  22.99 12.2 11.84 7.74 5.38 ...

其他功能:建立一個新且空的data.frame

df.test <- data.frame()

4.1.2 觀察dataframe

當我們處理數據框架(dataframe)時,有幾種常用的方法可以幫助我們更好地了解和觀察數據的結構和內容。

  1. View(df): 使用RStudio提供的圖形使用者介面直接觀看dataframe。這個功能允許你直觀地瀏覽整個數據集,方便地查看不同行(變數)和列(觀測值)。這對於初步瞭解數據的分佈和檢查數據的格式特別有用。

  2. head(df): 這個函數用於取出數據框架的前六筆資料(也就是前六列)。這可以讓我們快速概覽數據集的開頭部分,了解數據的基本結構和內容。如果需要查看更多或更少的列,可以向head函數傳遞一個額外的參數,如head(df, n = 10)來查看前十列。

  3. class(df): 此函數返回該變數的類型。對於dataframe,它將返回”DataFrame”,表明該對象是一個dataframe。了解對象的類型是重要的基礎步驟,尤其是在R中,不同類型的變項能夠做的操作和應用的函數也不同。

  4. str(df): str是結構(structure)的縮寫,這個函數提供了dataframe的詳細結構信息,包括變項的數量、變項名稱、變項數據類型以及每個變項前幾個值。這是一個非常強大的函數,用於深入了解數據集的內部結構,特別是當處理大型數據集時。

  5. summary(df): 此函數提供了數據框架的摘要統計信息,包括數值變數的最小值、最大值、中位數、平均值、第一四分位數和第三四分位數,以及因子變數的水平計數。這對於快速獲取數據集的統計概述非常有用。

# View(df)
head(df)    # get first part of the data.frame
##   country import export
## 1      CN 26.142 22.987
## 2      US 12.008 12.204
## 3      JP  7.032 11.837
## 4      HK 13.646  7.739
## 5      KR  4.589  5.381
## 6      SG  5.768  4.610
class(df)
## [1] "data.frame"
str(df)
## 'data.frame':    20 obs. of  3 variables:
##  $ country: chr  "CN" "US" "JP" "HK" ...
##  $ import : num  26.14 12.01 7.03 13.65 4.59 ...
##  $ export : num  22.99 12.2 11.84 7.74 5.38 ...
summary(df)
##    country              import           export      
##  Length:20          Min.   : 0.449   Min.   : 0.728  
##  Class :character   1st Qu.: 1.016   1st Qu.: 1.312  
##  Mode  :character   Median : 2.054   Median : 1.966  
##                     Mean   : 4.536   Mean   : 4.374  
##                     3rd Qu.: 4.884   3rd Qu.: 4.803  
##                     Max.   :26.142   Max.   :22.987
# look up help
help(summary)
?summary

4.1.2.1 觀察資料維度

dim(df)
## [1] 20  3
ncol(df)
## [1] 3
nrow(df)
## [1] 20
length(df)
## [1] 3

4.1.3 操作dataframe

4.1.3.1 取出一個變項

  • names(df) 列出變數名稱
  • df$發生.現.地點 顯示該變數內容
  • df$發生時段 顯示該變數內容
  • length(df$發生時段) 顯示該變數的長度(相當於有幾個)
names(df)
## [1] "country" "import"  "export"
head(df$export)
## [1] 22.987 12.204 11.837  7.739  5.381  4.610
length(df$import)
## [1] 20
summary(df)
##    country              import           export      
##  Length:20          Min.   : 0.449   Min.   : 0.728  
##  Class :character   1st Qu.: 1.016   1st Qu.: 1.312  
##  Mode  :character   Median : 2.054   Median : 1.966  
##                     Mean   : 4.536   Mean   : 4.374  
##                     3rd Qu.: 4.884   3rd Qu.: 4.803  
##                     Max.   :26.142   Max.   :22.987

4.1.3.2 (mutate)透過運算產生新變數

  • 這裡容易犯錯的是,要記得跟程式講說你要加總或四則運算的是哪個df的variable。
  • 從下面的這個操作中,該data.frame會產生一個新的變數sub,這就相當於Excel中的某一行減去某一行,然後把資料放在新的一行。
df$sub <- df$import - df$export

4.1.3.3 (filter)篩選資料、選取變數

  • 注意,要告訴程式importexport是哪個data.frame的。

  • df[,]為存取df中某個區段的數值或某個數值的方法。因此df[1, 1]會取出第一行第一列,也就是第一筆資料的第一個vector。df[2, 3]則會取出第二筆資料的第三個variable。

  • 下面的例子nrow(df)為1894,有1894筆資料,所以自然df\(import與df\)export的長度都是1894。因此,比較這兩個變數的大小會得到一個長度為1894的boolean (logical) variable。因此把這個長度為1894、充滿TRUE和FALSE的logical vector丟進df的row之處,因為取自df,大小判斷式結果的長度自然和原本的df的列數相同。因此當這個TRUE/FALSE被丟在df的列之處,便會篩選出import大於p.xport的數值。

  • 原本的df有五個variable,而上述的操作是篩選資料,所以被篩選的是列,因此行的數量、名稱都不會變。因此,我篩選完後,直接存取這個被篩選過的data.frame的country variable,自然是可以的。

df
##    country import export    sub
## 1       CN 26.142 22.987  3.155
## 2       US 12.008 12.204 -0.196
## 3       JP  7.032 11.837 -4.805
## 4       HK 13.646  7.739  5.907
## 5       KR  4.589  5.381 -0.792
## 6       SG  5.768  4.610  1.158
## 7       DE  2.131  2.866 -0.735
## 8       MY  2.802  2.784  0.018
## 9       VN  3.428  2.414  1.014
## 10      PH  3.019  2.092  0.927
## 11      TH  1.976  1.839  0.137
## 12      AU  1.118  1.788 -0.670
## 13      NL  1.624  1.665 -0.041
## 14      SA  0.449  1.409 -0.960
## 15      ID  0.983  1.391 -0.408
## 16      GB  1.302  1.075  0.227
## 17      IN  1.027  0.974  0.053
## 18      FR  0.553  0.899 -0.346
## 19      IT  0.670  0.800 -0.130
## 20      AE  0.455  0.728 -0.273
names(df)
## [1] "country" "import"  "export"  "sub"
nrow(df)
## [1] 20
# filter row data by column value
df[df$import > df$export,]
##    country import export   sub
## 1       CN 26.142 22.987 3.155
## 4       HK 13.646  7.739 5.907
## 6       SG  5.768  4.610 1.158
## 8       MY  2.802  2.784 0.018
## 9       VN  3.428  2.414 1.014
## 10      PH  3.019  2.092 0.927
## 11      TH  1.976  1.839 0.137
## 16      GB  1.302  1.075 0.227
## 17      IN  1.027  0.974 0.053
df[df$import > df$export,]$country
## [1] "CN" "HK" "SG" "MY" "VN" "PH" "TH" "GB" "IN"
df[df$import > df$export,1]
## [1] "CN" "HK" "SG" "MY" "VN" "PH" "TH" "GB" "IN"
# 1 row == a data.frame with only one data entry
class(df[df$import > df$export,1])
## [1] "character"
class(df[,1]) # character vector
## [1] "character"
class(df[1,]) # data.frame
## [1] "data.frame"
class(unlist(df[1, -1])) # filter the 1st row and select all columns except 1
## [1] "numeric"

4.1.3.4 (arrange) 按某個變數排序

  • df.sorted <- df[order(df$import),]會使得整個df照import的大小排序重新做排列。因為order(df$import)會把資料照指定順序排列後的位置傳回來,所以把他丟給df的列的位置,便會使得df的資料照指定的順序排列。 預設是由小到大,加上decreasing = T這個參數後變成由大而小。
# sort rows by df$import column
df.sorted <- df[order(df$import),]
# View(df.sorted)

# sort rows in decreasing order
df.sorted <- df[order(df$import, decreasing = T),]

# add - to column in order() can sort in decreasing order
df.sorted <- df[order(-df$import),]

head(df.sorted)
##   country import export    sub
## 1      CN 26.142 22.987  3.155
## 4      HK 13.646  7.739  5.907
## 2      US 12.008 12.204 -0.196
## 3      JP  7.032 11.837 -4.805
## 6      SG  5.768  4.610  1.158
## 5      KR  4.589  5.381 -0.792

4.2 簡易繪圖

  • graphics::plot()為會預載入R的繪圖套件,如果希望繪圖的同時加上回歸線和資料點標籤的話,必須要三行一起執行。
# plot(df) # raise error, 1st column is a character vector
plot(df[, 2:3])

plot(df[1:10, 2:3])
text(import, export, labels=country, cex= 0.5, pos=3)
lines(1:25, 1:25, col='red')

?plot
## Help on topic 'plot' was found in the following packages:
## 
##   Package               Library
##   graphics              /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library
##   base                  /Library/Frameworks/R.framework/Resources/library
## 
## 
## Using the first match ...

4.3 延伸學習

4.3.1 使用dplyr

library(dplyr)
df <- data.frame(country, import, export, stringsAsFactors = F)
df <- mutate(df, sub = import - export)
filter(df, import > export)
##   country import export   sub
## 1      CN 26.142 22.987 3.155
## 2      HK 13.646  7.739 5.907
## 3      SG  5.768  4.610 1.158
## 4      MY  2.802  2.784 0.018
## 5      VN  3.428  2.414 1.014
## 6      PH  3.019  2.092 0.927
## 7      TH  1.976  1.839 0.137
## 8      GB  1.302  1.075 0.227
## 9      IN  1.027  0.974 0.053
select(df, c(1, 3))
##    country export
## 1       CN 22.987
## 2       US 12.204
## 3       JP 11.837
## 4       HK  7.739
## 5       KR  5.381
## 6       SG  4.610
## 7       DE  2.866
## 8       MY  2.784
## 9       VN  2.414
## 10      PH  2.092
## 11      TH  1.839
## 12      AU  1.788
## 13      NL  1.665
## 14      SA  1.409
## 15      ID  1.391
## 16      GB  1.075
## 17      IN  0.974
## 18      FR  0.899
## 19      IT  0.800
## 20      AE  0.728
message(df$country)
print(df$country)
##  [1] "CN" "US" "JP" "HK" "KR" "SG" "DE" "MY" "VN" "PH" "TH" "AU" "NL" "SA" "ID"
## [16] "GB" "IN" "FR" "IT" "AE"

4.3.2 比較tibble, data_frame, data.frame

警告: "data_frame()" was deprecated in tibble 1.1.0. Please use "tibble()" instead.

df <- data.frame(a=1:2, b=3:4, c=5:6)
class(df)
## [1] "data.frame"
df <- data_frame(a=1:2, b=3:4, c=5:6)
class(df)
## [1] "tbl_df"     "tbl"        "data.frame"
df <- tibble(a=1:2, b=3:4, c=5:6)
class(df)
## [1] "tbl_df"     "tbl"        "data.frame"

4.4 Paid Maternity Leave

本案例將使用R重新製作華盛頓郵報2016年8月13日的一篇報導,該報導探討了美國婦女產假支薪情況。案例中將應用data.frame和基本的繪圖與資料摘要方法。

原始新聞來源:The world is getting better at paid maternity leave. The U.S. is not. - The Washington Post。該篇報導提及,美國因為目前的政策不保障帶薪產假,許多女性感到必須在工作和照顧家庭之間做出選擇,這種性別不平等破壞了她們在工作機會上的平等機會。同時,世界各地的婦女待遇正在逐漸改善。至少190個國家對嬰兒的母親規定了某種形式的帶薪假期,產假待遇在56個國家有所提高。專家表示,現在美國城市和州正通過不同形式的帶薪家庭假法案,這顯示美國雇主正在展示有競爭力的福利不會影響員工表現。特別是科技公司,如Twitter、Facebook和Google等,處於提供員工帶薪產假福利的前沿,美國可能有望追趕其他國家。

本案例主要呈現核心的視覺化概念,可以在Review Paid Maternity by dplyr找到更詳盡的案例說明與解析。

4.4.1 Reading .xlsx by readxl package

在進行產假支薪調查數據的分析與視覺化時,我們從該調查網站上所下載的資料是一個Excel文件。由於R語言本身不直接支援讀取Excel格式的文件,我們必須依靠外部的套件來實現這一功能,如readxl套件。它是專門設計來讀取.xls.xlsx格式文件的強大工具。readxl套件是tidyverse套件集的一部分。tidyverse是一組旨在數據科學和數據處理領域提供便利的R套件集合,包括了ggplot2dplyrtidyr等多個流行的套件。如果你之前已經安裝了tidyverse,那麼readxl套件應該也已經安裝在你的系統上,無需進行重複安裝。

然而,即便readxl已經安裝,它並不會隨著tidyverse套件集的其他部分自動加載到R的執行環境中。這意味著,在你打算使用readxl套件來讀取Excel文件之前,需要先手動執行library(readxl)命令來加載它。

# Import readxl package
# install.packages("tidyverse")
library(readxl)

這段程式碼使用read_excel()函式從data資料夾中的WORLD-MACHE_Gender_6.8.15.xls檔案中的Sheet1工作表讀取資料。其中col_names=T為該函式的參數,表示第一列為欄位名稱。讀取後的資料會被Assign給變數df

# Use read_excel() to convert excel sheet to data.frame
df <- read_excel("data/WORLD-MACHE_Gender_6.8.15.xls", "Sheet1", col_names=T)

4.4.2 Previewing data by View(), class(), dim(), str(), summary() and names()

# View(df)
class(df)       # [1] "tbl_df"     "tbl"        "data.frame"
## [1] "tbl_df"     "tbl"        "data.frame"
dim(df)
## [1] 197 156
# Show names of variables (vectors, columns) by names()
names(df)
##   [1] "country"           "iso2"              "iso3"             
##   [4] "region"            "wb_econ"           "matleave_95"      
##   [7] "matleave_96"       "matleave_97"       "matleave_98"      
##  [10] "matleave_99"       "matleave_00"       "matleave_01"      
##  [13] "matleave_02"       "matleave_03"       "matleave_04"      
##  [16] "matleave_05"       "matleave_06"       "matleave_07"      
##  [19] "matleave_08"       "matleave_09"       "matleave_10"      
##  [22] "matleave_11"       "matleave_12"       "matleave_13"      
##  [25] "matleave_wrr_95"   "matleave_wrr_96"   "matleave_wrr_97"  
##  [28] "matleave_wrr_98"   "matleave_wrr_99"   "matleave_wrr_00"  
##  [31] "matleave_wrr_01"   "matleave_wrr_02"   "matleave_wrr_03"  
##  [34] "matleave_wrr_04"   "matleave_wrr_05"   "matleave_wrr_06"  
##  [37] "matleave_wrr_07"   "matleave_wrr_08"   "matleave_wrr_09"  
##  [40] "matleave_wrr_10"   "matleave_wrr_11"   "matleave_wrr_12"  
##  [43] "matleave_wrr_13"   "bf_dur_95"         "bf_dur_96"        
##  [46] "bf_dur_97"         "bf_dur_98"         "bf_dur_99"        
##  [49] "bf_dur_00"         "bf_dur_01"         "bf_dur_02"        
##  [52] "bf_dur_03"         "bf_dur_04"         "bf_dur_05"        
##  [55] "bf_dur_06"         "bf_dur_07"         "bf_dur_08"        
##  [58] "bf_dur_09"         "bf_dur_10"         "bf_dur_11"        
##  [61] "bf_dur_12"         "bf_dur_13"         "mat_bfeed_6mon_95"
##  [64] "mat_bfeed_6mon_96" "mat_bfeed_6mon_97" "mat_bfeed_6mon_98"
##  [67] "mat_bfeed_6mon_99" "mat_bfeed_6mon_00" "mat_bfeed_6mon_01"
##  [70] "mat_bfeed_6mon_02" "mat_bfeed_6mon_03" "mat_bfeed_6mon_04"
##  [73] "mat_bfeed_6mon_05" "mat_bfeed_6mon_06" "mat_bfeed_6mon_07"
##  [76] "mat_bfeed_6mon_08" "mat_bfeed_6mon_09" "mat_bfeed_6mon_10"
##  [79] "mat_bfeed_6mon_11" "mat_bfeed_6mon_12" "mat_bfeed_6mon_13"
##  [82] "minage_fem_leg_95" "minage_fem_leg_96" "minage_fem_leg_97"
##  [85] "minage_fem_leg_98" "minage_fem_leg_99" "minage_fem_leg_00"
##  [88] "minage_fem_leg_01" "minage_fem_leg_02" "minage_fem_leg_03"
##  [91] "minage_fem_leg_04" "minage_fem_leg_05" "minage_fem_leg_06"
##  [94] "minage_fem_leg_07" "minage_fem_leg_08" "minage_fem_leg_09"
##  [97] "minage_fem_leg_10" "minage_fem_leg_11" "minage_fem_leg_12"
## [100] "legal_diff_leg_95" "legal_diff_leg_96" "legal_diff_leg_97"
## [103] "legal_diff_leg_98" "legal_diff_leg_99" "legal_diff_leg_00"
## [106] "legal_diff_leg_01" "legal_diff_leg_02" "legal_diff_leg_03"
## [109] "legal_diff_leg_04" "legal_diff_leg_05" "legal_diff_leg_06"
## [112] "legal_diff_leg_07" "legal_diff_leg_08" "legal_diff_leg_09"
## [115] "legal_diff_leg_10" "legal_diff_leg_11" "legal_diff_leg_12"
## [118] "minage_fem_pc_95"  "minage_fem_pc_96"  "minage_fem_pc_97" 
## [121] "minage_fem_pc_98"  "minage_fem_pc_99"  "minage_fem_pc_00" 
## [124] "minage_fem_pc_01"  "minage_fem_pc_02"  "minage_fem_pc_03" 
## [127] "minage_fem_pc_04"  "minage_fem_pc_05"  "minage_fem_pc_06" 
## [130] "minage_fem_pc_07"  "minage_fem_pc_08"  "minage_fem_pc_09" 
## [133] "minage_fem_pc_10"  "minage_fem_pc_11"  "minage_fem_pc_12" 
## [136] "legal_diff_pc_95"  "legal_diff_pc_96"  "legal_diff_pc_97" 
## [139] "legal_diff_pc_98"  "legal_diff_pc_99"  "legal_diff_pc_00" 
## [142] "legal_diff_pc_01"  "legal_diff_pc_02"  "legal_diff_pc_03" 
## [145] "legal_diff_pc_04"  "legal_diff_pc_05"  "legal_diff_pc_06" 
## [148] "legal_diff_pc_07"  "legal_diff_pc_08"  "legal_diff_pc_09" 
## [151] "legal_diff_pc_10"  "legal_diff_pc_11"  "legal_diff_pc_12" 
## [154] "minwage_ppp_2013"  "mw_overtime"       "oecd"

4.4.3 Select variables

由於所需要的資料為第三欄的變數iso3(為國家代碼)和第六至24欄的matleave95~matleave13共29年的資料,所以需要在df[ , ]中選出這幾欄。只要把所要取的欄以vector的型態放在df[row,col]col的位置,便可以選出所要的欄。

# Select the 3rd and 6th to 24th columns
matleave <- df[ , c(3, 6:24)]

# Use class(), dim(), and str() to inspect the data
class(matleave)
## [1] "tbl_df"     "tbl"        "data.frame"
dim(matleave)
## [1] 197  20
str(matleave)
## tibble [197 × 20] (S3: tbl_df/tbl/data.frame)
##  $ iso3       : chr [1:197] "AFG" "ALB" "DZA" "AND" ...
##  $ matleave_95: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_96: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_97: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_98: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_99: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_00: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_01: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_02: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_03: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_04: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_05: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_06: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_07: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_08: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_09: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_10: num [1:197] 2 5 3 3 2 2 2 5 NA 5 ...
##  $ matleave_11: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...
##  $ matleave_12: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...
##  $ matleave_13: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...

4.4.4 Check & Replace NAs

  • 處理開放資料常常會遇到紀錄遺漏的情形,這些遺漏的值在R語言中通常以NA(Not Available)來表示。這種情況很常見,特別是當數據來自於廣泛的來源,如網絡調查或公開資料庫時。適當處理這些NA值對於維持分析的準確性和可靠性至關重要。
  • 為了識別和處理這些NA值,R提供了一些有用的函數和技巧。例如,is.na(v)函數可以用來檢測向量v中的NA值。如果你想選擇所有的NA紀錄,可以使用v[is.na(v)]這樣的語法。這個表達式會傳回所有在向量v中為NA的元素,這對於進一步的分析和資料清洗非常有幫助。
  • 在某些情況下,你可能會想要以某個特定值來取代NA值,以避免在繪圖或進行其他數據分析時產生錯誤。例如,你可以選擇以0來取代所有的NA值,這可以通過v[is.na(v)] <- 0來實現。這樣,所有原本為NA的資料格都會被賦予0值。
  • 此外,sum(is.na(v))這個表達式可以用來檢測向量v中還有多少NA值。這個函數的運作機制是計算所有is.na(v)TRUE的情況,即所有NA值的總數。如果這個結果不是0,那麼就表示在向量或dataframe中還存在NA值。這對於確保數據清理工作已經完成,並且數據集準備好進行分析是非常有用的。
# is.na() to indicate each element is NA or NOT(TRUE/FALSE)
head(is.na(matleave), n=20)
##        iso3 matleave_95 matleave_96 matleave_97 matleave_98 matleave_99
##  [1,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [2,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [3,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [4,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [5,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [6,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [7,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [8,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [9,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [10,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [11,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [12,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [13,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [14,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [15,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [16,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [17,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [18,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [19,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [20,] FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##       matleave_00 matleave_01 matleave_02 matleave_03 matleave_04 matleave_05
##  [1,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [2,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [3,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [4,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [5,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [6,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [7,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [8,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [9,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [10,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [11,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [12,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [13,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [14,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [15,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [16,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [17,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [18,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [19,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [20,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##       matleave_06 matleave_07 matleave_08 matleave_09 matleave_10 matleave_11
##  [1,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [2,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [3,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [4,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [5,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [6,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [7,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [8,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
##  [9,]       FALSE       FALSE       FALSE       FALSE        TRUE       FALSE
## [10,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [11,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [12,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [13,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [14,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [15,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [16,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [17,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [18,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [19,]       FALSE       FALSE       FALSE       FALSE       FALSE       FALSE
## [20,]       FALSE        TRUE        TRUE       FALSE       FALSE       FALSE
##       matleave_12 matleave_13
##  [1,]       FALSE       FALSE
##  [2,]       FALSE       FALSE
##  [3,]       FALSE       FALSE
##  [4,]       FALSE       FALSE
##  [5,]       FALSE       FALSE
##  [6,]       FALSE       FALSE
##  [7,]       FALSE       FALSE
##  [8,]       FALSE       FALSE
##  [9,]       FALSE       FALSE
## [10,]       FALSE       FALSE
## [11,]       FALSE       FALSE
## [12,]       FALSE       FALSE
## [13,]       FALSE       FALSE
## [14,]       FALSE       FALSE
## [15,]       FALSE       FALSE
## [16,]       FALSE       FALSE
## [17,]       FALSE       FALSE
## [18,]       FALSE       FALSE
## [19,]       FALSE       FALSE
## [20,]       FALSE       FALSE
# Assign 0 to those NA data
matleave[is.na(matleave)] <- 0

# anyNA() to check if there are still NA cells.
anyNA(matleave)
## [1] FALSE
# sum(is.na()) to count the number of NA
sum(is.na(matleave))
## [1] 0

4.4.5 Filtering data

4.4.5.1 Filtered by the last year value

matleave[matleave$'matleave_13'==5, ]中的第一個matleave表示要篩選的資料集,中括號中的matleave$'matleave_13'==5是篩選條件,表示將篩選matleave資料集中的matleave_13變數值等於5的列;中括號中的逗號後方未有欄位名稱表示將保留所有欄位(變項),僅篩選出符合條件的列,並將篩選後所產生的dataframe指給變數m5

# Use logical comparison to see if the last year equals to 5
# Assign matching data to var m5
m5 <- matleave[matleave$'matleave_13'==5, ]

# nrow() to count matching data
nrow(m5)
## [1] 34
# Is it possible to use length() to check the data length?
# matleave$'matleave_13'
# matleave$'matleave_13'==5
# length(matleave$'matleave_13'==5)

4.4.5.2 Filtered data by the first year value

接下來我們再做一次篩選,從m5中篩選出matleave_95這個欄位為5的資料,並指給m55;同時也從m5中篩選出matleave_95這個欄位不為5的資料,並指給m05m5m55m05無特殊含義,只是變數名稱而已。

# filter rows whose 'matleave_95' is 5, and assign to var m55
m55<- m5[m5$'matleave_95'==5,]

# filter rows whose 'matleave_95' is not 5, and assign to var m05
m05<- m5[m5$'matleave_95'!=5,]

4.4.6 Plotting

  • 當我們在R中進行資料視覺化時,理解資料結構對於正確使用圖形化函數是非常重要的。以matleave資料集為例,如果我們想要繪製其第二列所有行(除了第一行)的條形圖,這裡有一段示範程式碼及相關的概念解釋。
  • 首先,為何要除去第一行?因為第一行為國家名稱。所以我們利用class(matleave[2, -1])來查看matleave資料集第二行和除了第一列外所有列的資料類型。這個操作返回的是一個data.frame的資料類型,因為即使是單一行的選取,R仍然保持了資料的data.frame結構。
  • 然而,當我們嘗試使用barplot()函數繪製長條圖時,就不能直接把data.framebarplot()進行繪製。。這是因為barplot()函數期望的輸入是一個vector。因此,我們使用unlist(matleave[2, -1])將單行的data.frame轉換成vectorunlist()函數的作用是將一個列表(或在這個案例中是data.frame)中的所有元素合併成一個vector,這樣就可以用於barplot()
  • 為了進一步理解這種差異,我們可以使用class()str()函數來觀察未經unlist()處理的資料。這將顯示出資料仍然保留在data.frame結構中,與unlist()後轉換為vector的結構有顯著的不同。這種轉換對於使用某些特定的繪圖函數,如barplot(),是必要的,因為它們需要一個vector作為輸入來正確地繪製圖形。

4.4.6.1 Plotting one row (one country)

# barplot() the second row of m55
# barplot(m55[2, ])       # raise error

# barplot() the second row when neglecting the first column
# barplot(m55[2, -1])     # raise error

# Take a look at the data type of matleave[2, ]
class(matleave[2, -1])
## [1] "tbl_df"     "tbl"        "data.frame"
class(unlist(matleave[2, -1]))
## [1] "numeric"
# unlist() to convert a single row data.frame to a vector for barplot()
barplot(unlist(m55[2, -1]))

Testing

# View(matleave[1]) # select the 1st variable
# View(matleave[ ,1]) # select the 1st column
# View(matleave[1, ]) # select the 1st row

class(m55[1])       # "tbl_df"     "tbl"        "data.frame"
## [1] "tbl_df"     "tbl"        "data.frame"
class(m55[ ,1]) # "tbl_df"     "tbl"        "data.frame"
## [1] "tbl_df"     "tbl"        "data.frame"
class(m55[1, ]) # "tbl_df"     "tbl"        "data.frame"
## [1] "tbl_df"     "tbl"        "data.frame"
class(m55$iso3) # character (vector)
## [1] "character"

4.4.6.2 More arguments (args)

接下來我們要微調一下視覺化的結果。這行程式碼使用R中的barplot函數繪製一個長條圖,其中的參數說明如下:

  1. unlist(m55[2, -1]): 將m55資料集的第2行(不包括第1欄)轉換為一個向量,並作為長條圖的高度(即每個長條的高度)。
  2. ylim=c(0, 5): 設置y軸的範圍為0到5,即長條圖的最大高度為5。
  3. space=0: 設置相鄰兩個長條之間的距離為0,即長條緊密相連。
  4. border=NA: 設置長條的邊框為透明,即不顯示邊框。
  5. xaxt="n": 不顯示x軸的標籤。
  6. yaxt="n": 不顯示y軸的標籤。
# barplot() the unlisted second row (neglecting the first col)
barplot(unlist(m55[2, -1]))

# use ?barplot to know more argument of the function.
?barplot

# Add arguments ylim, space, border, and axat/yaxt one by one to barplot()
barplot(unlist(m55[2, -1]), ylim=c(0, 5))

barplot(unlist(m55[2, -1]), ylim=c(0, 5), space=0)

barplot(unlist(m55[2, -1]), ylim=c(0, 5), space=0, border=NA)

barplot(unlist(m55[2, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")

4.4.6.3 Plotting multiple lines

我們已經成功繪製了一個國家的資料,接下來我們要繪出所有國家的資料。以m55這個篩選後的資料為例,我分別要繪製出第1列至第6列的國家。底下可以看見每一行非常相似且一致的特徵,僅有matleave內的索引由1被列出至6。對於這種重複的程式碼,最好的方法是用迴圈(for-loop)的方式將相同的程式碼,從1~6之間做六次。

# plot the first row
barplot(unlist(m55[1, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")

# plot the second to 6th rows
barplot(unlist(m55[2, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")

barplot(unlist(m55[3, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
barplot(unlist(m55[4, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
barplot(unlist(m55[5, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
barplot(unlist(m55[6, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")

4.4.6.4 for-loop to plot multiple lines

這段R語言程式碼使用for-loop來重複執行一個指定的程式區塊,將m55資料集的前六行資料分別繪製成長條圖。在這段程式碼中,變數i控制了for-loop的迭代次數,它從1到6依次取值,然後依次執行所指定的程式區塊。

一般的for-loop的結構如下:for (variable in sequence) {# code block to be executed}。其中,變數variable是用來控制for-loop的迭代次數的,它會從序列sequence中逐一取出元素,並將其賦值給變數variable,然後執行大括號{...}中所指定的程式區塊。

# use for loop and use i as index to barplot multiple subgraphs
for(i in 1:6){
  barplot(unlist(m55[i, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
}

4.4.6.5 Subplots

但這樣一個國家就要畫成一個Plot,如果要將多個國家、也就是多個Plots繪製在同一張圖上的話,R也有支援Subplot的函式與設定。在R語言中,par(parameter的縮寫)是一個用於設置繪圖參數的函數,通過它可以控制繪圖的外觀、尺寸、排列等各方面,以便更好地展示數據和分析結果。par函數可以用來設置以下參數:

  • mfrow:設置畫布的分割,即將畫布分為多少行和多少列,例如mfrow=c(3,2)代表三列二行。
  • mai:設置畫布的邊緣大小,包括上下左右四個邊緣的大小。
  • cex:設置字體大小的縮放比例。
  • col:設置線條、點和字體的顏色。
  • pch:設置散點圖中點的形狀。
  • lty:設置線條的類型。

在這段程式碼中,par函數被用來設置畫布的分割和邊緣大小,具體來說,par(mfrow=c(3,2), mai= c(0.2, 0.2, 0.2, 0.2))表示將畫布分為3行2列的子圖,並設置邊緣大小為0.2,包括上下左右四個邊緣。這樣可以方便地在同一張畫布上顯示多個圖形,並控制它們之間的排列和間距。

# use ?par to get more plotting parameters
?par

# use par() to set-up the layout of subgraphs
# use the parameter main=c(0.2, 0.2, 0.2, 0.2) to thrink the padding of figures.
par(mfrow=c(3,2), mai= c(0.2, 0.2, 0.2, 0.2))
for(i in 1:6){
  barplot(unlist(m55[i, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
}

接下來我們用相同的for-loop來繪製10張子圖(十個國家)看看。會發現mfrow=c(3,2)可以容納六張子圖,多餘六張子圖時,會繪製至下一張。

# plot more rows to see what happens
par(mfrow=c(3,2), mai= c(0.2, 0.2, 0.2, 0.2))
for(i in 1:10){
    barplot(unlist(m55[i, -1]), ylim=c(0, 5), space=0, border=NA, xaxt="n", yaxt="n")
}

# plot all subplots in a figure

最後,我用nrow(m55)來取得m55這個data.frame共有多少個國家,然後,我讓for-loop1:nrow(m55)相當於繪製完所有m55中的子圖。注意我已經修改了mfrowmfrow=c(4, 6)

# nrow() to check number of row of m55.
nrow(m55)
## [1] 18
# use par() to set-up plotting parameters.
par(mfrow=c(4, 6), mai= c(0.2, 0.2, 0.2, 0.2))

# use for-loop to plot all graph as subgraph
for (i in 1:nrow(m55)){
  barplot(unlist(m55[i, -1]), border=NA, space=0, xaxt="n", yaxt="n", ylim = c(0,5))
}

在每個子圖上,我要加上每個國家的國別代碼iso3,也就是m55的第一行,我用同樣的i來掃過每一列,繪製完barplot()後,便用title()函式來繪製文字。結果如下。注意我的設定title(m55[i,1], line = -4, cex.main=3)line為繪製文字的基線,而cex.main是字型大小。

par(mfrow=c(4,6), mai= c(0.2, 0.2, 0.2, 0.2))
for (i in 1:nrow(m55)){
  barplot(unlist(m55[i, -1]), border=NA, space=0,xaxt="n", yaxt="n", ylim = c(0,5))
    title(m55[i,1], line = -4, cex.main=3)
}

4.4.7 Practice. Plotting more

  • 請繪製m05的資料,也就是matleave_95!=5matleave_13==5的資料。
  • 請繪製m04的資料,也就是matleave_95!=4matleave_13==4的資料。
  • 請繪製m44的資料,也就是matleave_95==4matleave_13==4的資料。
# plotting matleave_95 != 5 but matleave_13 == 5

# plotting for matleave_13 == 4

4.4.8 Practice. Selecting and filtering by dplyr I

請嘗試問問ChatGPT,如果將以下程式碼改為dplyr的寫法,要怎麼寫。

df <- read_excel("data/WORLD-MACHE_Gender_6.8.15.xls", "Sheet1", col_names=T)

# select columns by index
# matleave <- df[ , c(3, 6:24)]

# select all NA cells and assign 0 to them
# matleave[is.na(matleave)] <- 0

# filter rows by condition
# m5 <- matleave[matleave$'matleave_13' == 5, ]

# filter rows by condition
# m55<- m5[m5$'matleave_95' == 5,]

# plot
par(mfrow=c(4,6), mai= c(0.2, 0.2, 0.2, 0.2))
for (i in c(1:nrow(m55))){
    barplot(unlist(m55[i,-1]),
            border=NA, space=0,xaxt="n", yaxt="n", ylim = c(0,5))
    title(m55[i,1], line = -4, cex.main=3)
}

4.4.9 (More) Clean version

# readxl::read_excel() to import the xls file
df <- read_excel("data/WORLD-MACHE_Gender_6.8.15.xls", "Sheet1", col_names=T)

# select iso3, and matleave columns by index
matleave <- df[ , c(3, 6:24)]

# str() to inspect the data structure of 
str(matleave)
## tibble [197 × 20] (S3: tbl_df/tbl/data.frame)
##  $ iso3       : chr [1:197] "AFG" "ALB" "DZA" "AND" ...
##  $ matleave_95: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_96: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_97: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_98: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_99: num [1:197] 2 5 3 2 2 2 2 3 1 5 ...
##  $ matleave_00: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_01: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_02: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_03: num [1:197] 2 5 3 3 2 2 2 3 1 5 ...
##  $ matleave_04: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_05: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_06: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_07: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_08: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_09: num [1:197] 2 5 3 3 2 2 2 5 1 5 ...
##  $ matleave_10: num [1:197] 2 5 3 3 2 2 2 5 NA 5 ...
##  $ matleave_11: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...
##  $ matleave_12: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...
##  $ matleave_13: num [1:197] 2 5 3 3 2 2 2 5 3 5 ...
# select all NA cells and assign 0 to them
matleave[is.na(matleave)] <- 0

# filter rows by condition
m5 <- matleave[matleave$'matleave_13' == 5, ]

# filter rows by condition
m55<- m5[m5$'matleave_95' == 5,]

# plot
par(mfrow=c(4,6), mai= c(0.2, 0.2, 0.2, 0.2))
for (i in c(1:nrow(m55))){
    barplot(unlist(m55[i,-1]),
            border=NA, space=0,xaxt="n", yaxt="n", ylim = c(0,5))
    title(m55[i,1], line = -4, cex.main=3)
}

4.4.10 (More) The fittest version to compute staySame

# staySame version
# staySame <- apply(m5[,2:20], 1, function(x) length(unique(x[!is.na(x)]))) 
# m55 <- m5[staySame, ]
# m50 <- m5[!staySame, ]