(1)准備數據過程中,遇到了缺失值的問題。以往都是自己手動寫代碼,用缺失值樣本所在類別的均值或者眾數替換掉,結果今天發現,DMwR2包就有處理缺失值的函數,而且思想一致【大哭】
先奉上代碼:
- install.packages("DMwR2");
- library(DMwR2) ;
- knnImputation(YourDataFrame)
(2)准備用SMOTE函數時,發現DMwR包在老早之前,就從R語言 CRAN中移除了。多方搜索,找到了DMwR包的網頁下載路勁:https://cran.r-project.org/src/contrib/Archive/DMwR/?C=D;O=A。直接下載最新版,下載后將Zip文件存放至R語言目標文件夾的library中(主要是方便以后別人需要,我可以很快找到這個東西在哪里)。
比如,我的存放路徑是:D:\Software\R-4.1.1\library
那么,現在回到R語言編程界面,輸入代碼:install.packages("D:/Software/R-4.1.1/library/DMwR_0.4.1.tar.gz", repos=NULL, type="source"),執行。【在這里我犯了一個小錯,輸入路徑時候,把壓縮包的后綴'.gz'落下了,由此走了好多彎路,解決不了,還下載R語言的2個老版本企圖解決問題,不過還好問題沒解決,我又發現了這個小錯誤。】
結果報錯:“ERROR: dependency 'abind' is not available for package 'DMwR'”。於是,再手動install.packages("abind"),安裝成功后,再次執行install.packages("D:/Software/R-4.1.1/library/DMwR_0.4.1.tar.gz", repos=NULL, type="source")。到這里,我的DMwR就成功了。
如果上面這個ERROR后還跟了其他的包也缺失的話,就多單獨安裝一下。
(3)SMOTE函數的運用
完成上述的操作,library(DMwR)即可調用SMOTE函數。
SMOTE函數如下:SMOTE(form, data, perc.over = 200, k = 5, perc.under = 200, learner = NULL, …)。
1) form是公式,直接Y~.即可(這個Y就是你因變量的名稱)。值得注意的是,這里的Y,如果是數據集中是數值型的話,一定要轉換成factor類型(DataFrame[,"Y"] <- factor(DataFrame[,"Y"]) 即可);
2)data,我們的數據集,包含了因變量和很多自變量的表;
3)perc.over:指定從少數樣本中采樣的比例;
4)perc.under:對多數樣本下采樣,有多少比例的樣本被選入新的數據集中;
5)k:跟K近鄰的思想一致,理解為通過周圍都少個鄰居決定生成數據吧;
6)重點!!!!怎么確定perc.over和perc.under的輸入數值呢?
這里,針對我們的數據集做幾個假設:
- 數據集中少數樣本個數:N=30
- 數據集中少數樣本個數:M=100
- 想要生成的少數樣本數量TarN;多數樣本TarM;| TarN=60;TarM=0
- perc.over:X; perc.under:Y
- 現在,我想生成90個少數類樣本,多數類不變。怎么確定我的X和Y呢?
且看公式:
X =(TarN)*100/N = 200
Y =(M+TarM)*100*100/(X*N) = 166.6667(保留3-4位小數就可以比較精准了)
這時候,再看我們的數據集,就有N+TarN+M+TarM條數據了。這篇博客的計算公式主要參考如下鏈接: https://blog.csdn.net/qq_34139222/article/details/57461398,但是在實際驗證的過程中,發現該博客在perc.over確定的時候,跟實際情況對不上。所以做了修改。經驗證,上述X,Y的最終計算結果應該是木有問題的了。
最后,用SMOTE處理了數據的時候,記得一定要在原始數據集中分出一批數據,不參與Smote的擴增處理,保證一批干凈的數據用於后續模型的驗證。但是多次碰到一個問題,就是用SMOTE處理了數據后,在內部驗證的結果都非常好,但一用前面說的干凈數據集驗證時,結果特別差。以前沒有仔細找原因,今天特地看了一下自己的生成數據,發現一個問題:我的數據集中有很多類別型變量,但是用smote算法后,它由於是靠距離決定的,所以模擬數據的結果,所有的變量都是數值型變量。因此,生成了模擬數據后,還需要再做一步的工作是,對類別型變量做一個round(X,0)的處理,不要小數點,不要變成數值型數據。
做了這一步改進之后,結果依然很差。再后,找到原因了。我在生成虛擬數據集的時候,少數類在原本數據集的基礎上,增加了我想增加的數量,原本的少數類數據不變。但是,多數類樣本的數值,幾乎全都變了。但是這個問題,我不知道Smote的哪個參數能解決。所以,就寫了段代碼,自動用原來的多數類樣本,替換掉虛擬集的多數類樣本信息。代碼如下,希望你們能用到。
setwd("E:\\Radiomics Program\\1-ICCProgram")
# 讀入數據
Clincial <- read.csv("ClinicalForR.csv",encoding = "UTF-8", stringsAsFactors = FALSE,na.strings="")
# 加載需要包
library(magrittr)
library(DMwR)
# 查看兩類數據的樣本分布。我的第一列就是因變量
PositiveRows <- which(Clinical[,1]==1)
NegativeRows <- which(Clinical[,1]==0)
# 在兩組中,分別隨機選擇部分數據出來,不參與Smote過程,做外部驗證
set.seed(1000)
IndepTestRowsPosi <- sample(PositiveRows,18,replace=F)
IndepTestRowsNega <- sample(NegativeRows ,15,replace=F)
# 自己寫了一個SMOTEData的函數,除了滿足smote之外,還做一些其他的處理。
SMOTEData <- function(Data,PositiveRows,NegativeRows ,SmoteRowsMinus,SmoteRowsMijnos,Name,OverTimes){
# 只選擇外部驗證數據集之外的數據,參與SMOTE過程
TmpData <- Data[c(setdiff(PositiveRows,SmoteRowsMinus),setdiff(NegativeRows ,SmoteRowsMijnos )),]
# 因變量一定要轉換成因子型
TmpData[,1] <- factor(TmpData[,1])
str(TmpData)
# 對一列做統計,后面需要用到這個統計信息
Table <- TmpData[,1] %>% table() %>% as.data.frame();Table
# SMOTE算法 擴充OverTimes倍陽性患者。計算好我們的過采樣和降采樣所需要的數據,具體計算過程看博客正文。
MinusN <- Table[which.min(Table[,2]),2]# %>% as.character() %>% as.numeric()
MijorN <- Table[which.max(Table[,2]),2]
X <- MinusN*OverTimes*100/MinusN
Y <- MijorN*100*100/(X*MinusN )
newData <- SMOTE(Label~ ., TmpData, perc.over = X,perc.under=Y)
table(newData[,1])#;str(newData)
# 生成數據集中,類別型數據也被生成了數值型數據,所以做一個簡單的處理。就是把 不同數值低於5個列找出來,認為他是類別型變量,然后取整數即可。
TrainData <- newData
# 確定分類數據,分類數據,不能是小數表示
TmpData2 <- TrainData[1:30,]
for(i in 2:ncol(TrainData)){
UniqueLen <- TmpData2[,i] %>% unique() %>% length()
if(UniqueLen <= 5){
TrainData[,i] <- round(TrainData[,i],0)
}
else{
next()
}
}
# 多數類並沒有要繼續生成虛擬數據,但是在smote函數運行過程總,多數類樣本的指標信息被修改了。替換
Rep <- Table[which.max(Table[,2]),1] %>% as.character() %>% as.numeric()
TrainData[which(TrainData[,1]==Rep),] <- TmpData[which(TmpData[,1]==Rep),]
TestData <- Data[-c(setdiff(PositiveRows,SmoteRowsMinus),setdiff(NegativeRows ,SmoteRowsMijnos )),]
# 將我們生成好的訓練集數據和獨立測試數據保存包設定工作目錄下面
write.csv(TrainData,paste0(getwd(),"/Smote_train_",Name,".csv"),row.names=F)
write.csv(TestData ,paste0(getwd(),"/Smote_independent_test_",Name,".csv"),row.names=F)
}
SMOTEData(Clinical,PositiveRows ,NegativeRows ,IndepTestRowsPosi ,IndepTestRowsNega,"Clinical",2)
最后,哪兩批數據來測試,親測有效。沒有訓練和內部驗證結果特別好,外部驗證結果AUC不到0.55的這種極端情況了~