R語言程式設計導論

林嶔 (Lin, Chin)

Lesson 7 資料清理及簡易驗證規則

第一節:資料清理(1)

– 一般來說,資料進來以後我們會設定驗證規則,如果不符合此一規則就刪除或重新檢視。

  1. 不正確的數據(Incorrect data):如年齡>130歲

  2. 不準確的數據(Inaccurate data):如年齡實際為50歲的人,被紀錄為60歲

  3. 重複的數據(Duplicate data):如重複key-in的問卷

  4. 不完整的數據(Incomplete data):不該遺漏而遺漏的數據

  5. 不一致的數據(Inconsistent data):如洗腎患者腎絲球過濾率卻有90 ml/min/1.73m2

  6. 違反規則(Rule violations):如收案日期為2010年以前,卻出現2013年的資料

第一節:資料清理(2)

– 這個資料是由門診護理師Key-in填入,按照計劃目標,所有病人每2個月追蹤一次(低於2個月不能給補助)。

– 現在你是腎臟醫學會核發衛教補助的承辦人員,你希望了解一下哪些紀錄是有問題的,而找到這些紀錄後你將要通知該醫院的門診護理師,請他再查閱紙本資料後重新Key-in

dat = read.csv("validated_example.csv", header = TRUE, fileEncoding = 'CP950') 
head(dat)
##   Patient       Date MDRD.GFR Stage   WBC  RBC  HB  Hct  MCV Urea.Nitrogen
## 1    1472  2011/5/31     15.9     4 10.10 2.76 8.7 27.0 98.0            75
## 2    1472   2011/8/8     24.0     4  5.79 1.80 5.7 17.9 99.4            41
## 3    1472 2011/10/17     22.7     4  5.20 2.44 7.8 23.0 94.3            49
## 4    1472  2012/1/10     18.7     4 12.99 2.95 9.4 28.2 95.6            46
## 5    1472  2013/5/14     30.4     3  6.78 3.02 9.7 30.0 99.3            48
## 6    1472  2013/8/22     26.8     4  6.88 3.73 8.6 26.0 95.2            82
##   Creatinine Uric.Acid  Na   K Albumin
## 1        3.0       3.9 139 3.3     4.2
## 2        2.1       4.8 143 4.6     4.2
## 3        2.2       4.7 146 4.8     4.5
## 4        2.6       4.0 141 4.2     4.6
## 5        1.7       3.9 141 4.4     4.1
## 6        1.9       3.0 138 4.1     4.3

第一節:資料清理(3)

class(dat[,"Date"])
## [1] "factor"
dat$Date = as.Date(dat[,"Date"])
test.date = c("2011/01/05", "2011/09/31", "2011/02/29", "2016/02/29")
test.date = as.Date(test.date)
test.date
## [1] "2011-01-05" NA           NA           "2016-02-29"
dat$Wrong.Date = is.na(dat$Date)
dat[dat$Wrong.Date == TRUE,]
##    Patient Date MDRD.GFR Stage WBC  RBC   HB  Hct  MCV Urea.Nitrogen Creatinine
## 45     566 <NA>      2.5     5 6.9 2.95  8.9 25.6 86.8           203       21.4
## 68     710 <NA>     22.3     4 9.2 4.13 12.9 38.2 92.6            60        2.3
##    Uric.Acid  Na   K Albumin Wrong.Date
## 45        NA 120 4.2     3.6       TRUE
## 68      11.2 141 4.2     3.9       TRUE

第一節:資料清理(4)

#Read data
dat = read.csv("validated_example.csv", header = TRUE, fileEncoding = 'CP950') 

#Rule 1: check date-format
dat$Date = as.Date(dat[,"Date"])
dat$Wrong.Date = is.na(dat$Date)
#dat[dat$Wrong.Date == TRUE,]

#Rule 2: ...

– 可以利用之前學過的函數「%in%」,還記得怎麼用嗎?

dat$Wrong.Stage = !dat$Stage %in% 1:5

第一節:資料清理(5)

– 索引函數可以幫助我們設定規則,値得注意的是,未來如果我們想要將連續變項『血壓値』轉換為類別變項『高血壓』時,可以透過類似的方式。

dat$Wrong.K[dat$K>10 | dat$K<1.5] = TRUE
dat$Wrong.K[dat$K<=10 & dat$K>=1.5] = FALSE
dat[dat$Wrong.K == TRUE, c("Patient", "Date", "Na", "K", "Wrong.K")]
##       Patient       Date  Na   K Wrong.K
## NA         NA       <NA>  NA  NA      NA
## NA.1       NA       <NA>  NA  NA      NA
## NA.2       NA       <NA>  NA  NA      NA
## NA.3       NA       <NA>  NA  NA      NA
## 32       1514 2013-04-23 5.2 139    TRUE
## NA.4       NA       <NA>  NA  NA      NA
## NA.5       NA       <NA>  NA  NA      NA
## NA.6       NA       <NA>  NA  NA      NA
## NA.7       NA       <NA>  NA  NA      NA
## NA.8       NA       <NA>  NA  NA      NA
## NA.9       NA       <NA>  NA  NA      NA
## NA.10      NA       <NA>  NA  NA      NA
## NA.11      NA       <NA>  NA  NA      NA
## NA.12      NA       <NA>  NA  NA      NA
## 75        710 2010-07-22 5.3 139    TRUE
dat[dat$Wrong.K %in% TRUE, c("Patient", "Date", "Na", "K", "Wrong.K")]
##    Patient       Date  Na   K Wrong.K
## 32    1514 2013-04-23 5.2 139    TRUE
## 75     710 2010-07-22 5.3 139    TRUE

練習1:設置其他生化質的驗證規則

– Stage與GFR之關係:1 - 90以上、2 - 60至90、3 - 30至60、4 - 15至30、5 - 15以下

練習1答案

#Read data
dat = read.csv("validated_example.csv", header = TRUE, fileEncoding = 'CP950') 

#Rule 1: check date-format
dat$Date = as.Date(dat[,"Date"])
dat$Wrong.Date = is.na(dat$Date)

#Rule 2: check Stage & MDRD.GFR
dat$my.Stage[dat$MDRD.GFR > 90] = 1
dat$my.Stage[dat$MDRD.GFR > 60 & dat$MDRD.GFR <= 90] = 2
dat$my.Stage[dat$MDRD.GFR > 30 & dat$MDRD.GFR <= 60] = 3
dat$my.Stage[dat$MDRD.GFR > 15 & dat$MDRD.GFR <= 30] = 4
dat$my.Stage[dat$MDRD.GFR <= 15] = 5
dat$Wrong.Stage = (dat$my.Stage != dat$Stage)

第二節:簡易驗證規則(1)

– 先想步驟,好好思考該怎樣做?

第二節:簡易驗證規則(2)

– 讓我們先留下日期正確的資料

dat = dat[!dat$Date %in% NA,]

– 還記得怎樣了解個案數嗎?可以透過函數「levels()」以及函數「length()」的組合,但在最開始的時候我們要先確定變數『Patient』是否為因子,若不是則必須先轉換為因子

class(dat$Patient)
## [1] "integer"
levels.Patient = levels(as.factor(dat$Patient))
n.Patient = length(levels.Patient)
n.Patient
## [1] 17
x = rep(NA, n.Patient)

– 還記得上一節課如何教大家寫迴圈函數嗎?先令『i = 1』之後再開始

– 需要特別注意的是,『levels.Patient』是文字向量,而『dat$Patient』則是整數向量,雖然在這個案例中直接檢索是可以的,但最好還是先轉換成同樣的屬性比較不會出錯

– 由於整數轉文字比較不會出錯,所以最好是把『dat$Patient』轉成文字再比較

i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
subdat
##   Patient       Date MDRD.GFR Stage WBC RBC  HB  Hct MCV Urea.Nitrogen
## 8      15 2006-06-14     25.3     4  NA  NA 9.2 29.3  NA            45
## 9      15 2006-08-01     26.4     4  NA  NA  NA   NA  NA            40
##   Creatinine Uric.Acid Na  K Albumin Wrong.Date my.Stage Wrong.Stage
## 8        2.7      14.4 NA NA      NA      FALSE        4       FALSE
## 9        2.6       8.0 NA NA      NA      FALSE        4       FALSE

第二節:簡易驗證規則(3)

– 如果個案僅申報一筆資料就不用繼續了,所以必須加入條件判斷

i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
n.date = length(subdat$Date)
if (n.date>1) {
  dif = rep(NA, n.date-1)
  for (k in 1:(n.date-1)) {
    dif[k] = subdat$Date[k+1] - subdat$Date[k]
  }
}
dif
## [1] 48
i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
n.date = length(subdat$Date)
if (n.date>1) {
  dif = diff(subdat$Date)
}
dif
## Time difference of 48 days

第二節:簡易驗證規則(4)

i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
n.date = length(subdat$Date)
if (n.date>1) {
  dif = diff(subdat$Date)
  check = dif < 60
}
check
## [1] TRUE
i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
n.date = length(subdat$Date)
if (n.date>1) {
  dif = diff(subdat$Date)
  check = dif < 60
}
TRUE %in% check
## [1] TRUE
i = 1
subdat = dat[dat$Patient==levels.Patient[i],]
n.date = length(subdat$Date)
if (n.date>1) {
  dif = diff(subdat$Date)
  check = dif < 60
  x[i] = TRUE %in% check
} else {
  x[i] = FALSE
}

第二節:簡易驗證規則(5)

levels.Patient = levels(as.factor(dat$Patient))
n.Patient = length(levels.Patient)
x = rep(NA, n.Patient)

for (i in 1:n.Patient) {
  subdat = dat[dat$Patient==levels.Patient[i],]
  n.date = length(subdat$Date)
  if (n.date>1) {
    dif = diff(subdat$Date)
    check = dif < 60
    x[i] = TRUE %in% check
  } else {
    x[i] = FALSE
  }
}

x
##  [1]  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE
levels.Patient[x]
## [1] "15"  "240" "414" "566" "710"
dat$Wrong.Date_interval = dat$Patient %in% levels.Patient[x]

練習2:檢查GFR的變化量

– 在計算變化量的時候,同時要注意追蹤時間間格

n = 100
pb = txtProgressBar(max = n, style=3)
for(i in 1:n) {
  Sys.sleep(0.1)
  setTxtProgressBar(pb, i)
}
close(pb)

練習2答案

#Read data
dat = read.csv("validated_example.csv", header = TRUE, fileEncoding = 'CP950') 

#Rule 1: check date-format
dat$Date = as.Date(dat[,"Date"])
dat$Wrong.Date = is.na(dat$Date)
dat = dat[dat$Wrong.Date == FALSE,]

#Rule 2: check eGFR change
levels.Patient = levels(as.factor(dat$Patient))
n.Patient = length(levels.Patient)
x = rep(NA, n.Patient)

pb = txtProgressBar(max = n, style = n.Patient)
for (i in 1:n.Patient) {
  subdat = dat[dat$Patient==levels.Patient[i],]
  n.date = length(subdat$Date)
  if (n.date>1) {
    diff_eGFR = diff(subdat$MDRD.GFR)
    diff_date = as.numeric(diff(subdat$Date), units = 'days') # 注意要轉成數字,否則無法相除
    slope = diff_eGFR / diff_date * 30
    check = (slope < -2 | slope > 2) # 也可以用「check = abs(slope) > 2」
    x[i] = TRUE %in% check
  } else {
    x[i] = FALSE
  }
  setTxtProgressBar(pb, i)
}
close(pb)

dat$Wrong.eGFR_change = dat$Patient %in% levels.Patient[x]

小結

– 目前為止有跟上進度的同學,應該可以建立一個資料處理pipline,不管原始蒐集的資料為何,都能透過一系列的轉換&品質控制,獲得乾淨的資料

– 你應該已經學會下面的功能:

  1. 資料讀入及寫出
  2. 資料清理
  3. 簡單資料合併
  4. 熟練的利用迴圈做大量重複的事
  5. 對於資料處理時的事前規劃
  6. 驗證規則的設定