林嶔 (Lin, Chin)
Lesson 7 資料清理及簡易驗證規則
– 一般來說,資料進來以後我們會設定驗證規則,如果不符合此一規則就刪除或重新檢視。
不正確的數據(Incorrect data):如年齡>130歲
不準確的數據(Inaccurate data):如年齡實際為50歲的人,被紀錄為60歲
重複的數據(Duplicate data):如重複key-in的問卷
不完整的數據(Incomplete data):不該遺漏而遺漏的數據
不一致的數據(Inconsistent data):如洗腎患者腎絲球過濾率卻有90 ml/min/1.73m2
違反規則(Rule violations):如收案日期為2010年以前,卻出現2013年的資料
– 這個資料是由門診護理師Key-in填入,按照計劃目標,所有病人每2個月追蹤一次(低於2個月不能給補助)。
– 現在你是腎臟醫學會核發衛教補助的承辦人員,你希望了解一下哪些紀錄是有問題的,而找到這些紀錄後你將要通知該醫院的門診護理師,請他再查閱紙本資料後重新Key-in
## 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
## [1] "factor"
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"
## 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
#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%」,還記得怎麼用嗎?
– 索引函數可以幫助我們設定規則,値得注意的是,未來如果我們想要將連續變項『血壓値』轉換為類別變項『高血壓』時,可以透過類似的方式。
## 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
## Patient Date Na K Wrong.K
## 32 1514 2013-04-23 5.2 139 TRUE
## 75 710 2010-07-22 5.3 139 TRUE
– Stage與GFR之關係:1 - 90以上、2 - 60至90、3 - 30至60、4 - 15至30、5 - 15以下
整理完後,請將整個檔案輸出,並試著在Excel中看看自己找到的異常値
注意,請整理自己的程式碼,增加他的可讀性,這樣未來才有多方協做的機會
#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)
– 先想步驟,好好思考該怎樣做?
– 讓我們先留下日期正確的資料
– 還記得怎樣了解個案數嗎?可以透過函數「levels()」以及函數「length()」的組合,但在最開始的時候我們要先確定變數『Patient』是否為因子,若不是則必須先轉換為因子
## [1] "integer"
## [1] 17
– 還記得上一節課如何教大家寫迴圈函數嗎?先令『i = 1』之後再開始
– 需要特別注意的是,『levels.Patient』是文字向量,而『dat$Patient』則是整數向量,雖然在這個案例中直接檢索是可以的,但最好還是先轉換成同樣的屬性比較不會出錯
– 由於整數轉文字比較不會出錯,所以最好是把『dat$Patient』轉成文字再比較
## 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
– 如果個案僅申報一筆資料就不用繼續了,所以必須加入條件判斷
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
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
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
## [1] "15" "240" "414" "566" "710"
– 在計算變化量的時候,同時要注意追蹤時間間格
請找出追蹤間格中有平均一個月變化超過2的人,把他找出來並增加一個新的變數『Wrong.GFR_interval』
另外,現在這種極長的迴圈最好使用進度條,上節課教的函數「txtProgressBar()」以及函數「setTxtProgressBar()」不要忘記喔:
#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,不管原始蒐集的資料為何,都能透過一系列的轉換&品質控制,獲得乾淨的資料
– 你應該已經學會下面的功能: