blob: efca8b9e8f732d266e6d183fe97f41bf336f4509 [file] [log] [blame]
Marc Kupietzc3bf3502021-02-19 17:18:57 +01001library(caret)
2library(tidyverse)
3library(DMwR)
4library(randomForest)
5library(FSelector)
6# library(randomForestExplainer)
7# may need to: options(expressions = 5e5) to avoid stackoverflow for installing package
8
Marc Kupietz358a2962021-02-22 07:55:49 +01009set.seed(42)
10
PeterFankhauserIDSed93d2e2021-02-20 14:51:13 +010011# Test
12
PeterFankhauserIDS3f97f362021-02-23 10:28:07 +010013ngramfile<-"gold03_anno_ml_synfeat_nstopw" # 2nd dataset
14# ngramfile <-"goldstandard01_anno_ml_synfeat_nstop1" # 1st dataset
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010015
Marc Kupietzc3bf3502021-02-19 17:18:57 +010016setwd(dirname(rstudioapi::getSourceEditorContext()$path))
17stopwords <- readLines(con = "../data/stopwords.txt",encoding="UTF-8")
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010018oringramme <- read.csv(paste("../data/",ngramfile,".csv",sep=""), header = TRUE, sep = "\t", dec=".", quote="", encoding="UTF-8",stringsAsFactors=FALSE)
Marc Kupietzc3bf3502021-02-19 17:18:57 +010019syfeaturenames <- read.csv("../data/syfeatures.tsv", header = TRUE, sep = "\t", dec=".", quote="", encoding="UTF-8",stringsAsFactors=FALSE)
20# syfeaturenames$navalue<-sapply(syfeaturenames$navalue,as.numeric)
21
22deleteStopwords = function(wl, stopwords = NULL) {
23 wl[!(wl %in% stopwords)]
24}
25
Marc Kupietz631800f2021-02-19 17:27:26 +010026oringramme <- oringramme %>%
PeterFankhauserIDS3f97f362021-02-23 10:28:07 +010027 mutate(CO_IDIOM = ifelse(is.na(CO_IDIOM),0,CO_IDIOM)) %>% # treat NAs as 0
Marc Kupietz631800f2021-02-19 17:27:26 +010028 filter(CO_IDIOM < 2) # just two classes: 0 no idiom, 1 idiom
29
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010030# Reduce number of classes, treat null values, add NSTOPW, change names for SY features
31
Marc Kupietzc3bf3502021-02-19 17:18:57 +010032ngramme <- oringramme %>%
33 add_column(NSTOPW = sapply(oringramme$tokens,function(x) length(deleteStopwords(tolower(unlist(strsplit(x," "))),stopwords)))) %>%
34 # select(-matches("CO_TOKEN.*"), -tokens) %>%
35 select(-matches("CO_TOKEN.*")) %>% # keep tokens for interpretability
36 mutate(across(matches(".rank.*"), ~ replace_na(.x, 1000))) %>%
37 mutate(across(c("dice", "lfmd", "llr", "ld", "pmi"), ~ replace_na(.x, min(.x) - 1))) %>%
Marc Kupietzaced2702021-02-19 19:09:29 +010038 rename_at(syfeaturenames$innames, ~ syfeaturenames[syfeaturenames$innames==.x,]$synames ) %>%
Marc Kupietzc3bf3502021-02-19 17:18:57 +010039 mutate(across(everything(), ~ replace_na(.x, 0))) %>%
Marc Kupietz201e6f32021-02-22 12:34:13 +010040 mutate(CO_IDIOM = as.factor(if_else(CO_IDIOM == 1, "idiom", "no_idiom"))) # just two classes: 0 no idiom, 1 idiom
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010041
42# Optional
43write.table(ngramme,file=paste("../data/",ngramfile,"_cosy.csv",sep=""), sep = "\t", quote=F)
44
45# featuresets
Marc Kupietzc3bf3502021-02-19 17:18:57 +010046
47covars <- c("CO_LL", "CO_Z", "CO_G", "CO_T", "CO_LOGDICE", "CO_PMI", "CO_MI3", "CO_DEREKO", "CO_SGT", "CO_WIN5_VEC","CO_WIN5_VEC_AUTOSEM")
48syvars <- c(syfeaturenames$synames,"NSTOPW")
49vars <- c(covars,syvars)
50
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010051# formulae for training and testing rf
52
Marc Kupietzc3bf3502021-02-19 17:18:57 +010053fmla <- as.formula(paste("CO_IDIOM ~ ", paste(vars, collapse= "+")))
54fmlaco <- as.formula(paste("CO_IDIOM ~ ", paste(covars, collapse= "+")))
55fmlasy <- as.formula(paste("CO_IDIOM ~ ", paste(syvars, collapse= "+")))
56
57# Simple train/test split
58
59trainRows <- sample(nrow(ngramme), nrow(ngramme)*0.8, replace = FALSE)
60train <- ngramme[trainRows,]
61test <- ngramme[setdiff(1:nrow(ngramme),trainRows),]
62
Marc Kupietz355d5482021-02-22 17:13:56 +010063cat("Random Forest\n")
Marc Kupietz65733b22021-02-22 08:09:08 +010064
Marc Kupietz13f67ed2021-02-22 07:55:03 +010065rf_classifier = randomForest(fmla, train, importance=TRUE)
Marc Kupietzc3bf3502021-02-19 17:18:57 +010066
67# only SY features
PeterFankhauserIDSa5b2acf2021-02-22 18:14:18 +010068# rf_classifier = randomForest(fmlasy, train, importance=TRUE)
Marc Kupietzc3bf3502021-02-19 17:18:57 +010069
70prediction_for_table <- predict(rf_classifier, test %>% select(-CO_IDIOM))
71
Marc Kupietz201e6f32021-02-22 12:34:13 +010072res <- confusionMatrix(prediction_for_table, test$CO_IDIOM, positive= "idiom")
Marc Kupietz13f67ed2021-02-22 07:55:03 +010073print(res)
Marc Kupietz355d5482021-02-22 17:13:56 +010074collected_results <- bind_cols("rf" = res$byClass)
PeterFankhauserIDSc2622782021-02-21 18:10:01 +010075
76# Sensitivity is recall of class 1
77# Pos Pred Value is precision
Marc Kupietzc3bf3502021-02-19 17:18:57 +010078varImpPlot(rf_classifier)
79
Marc Kupietz355d5482021-02-22 17:13:56 +010080cat("Random Forest with cutoff\n")
81prediction_for_table <- predict(rf_classifier,test %>% select(-CO_IDIOM), cutoff = c(0.2, 0.8))
82res <- confusionMatrix(prediction_for_table,test$CO_IDIOM, positive = "idiom")
83collected_results <- bind_cols(collected_results, "rf with cutoff" = res$byClass)
84print(res)
Marc Kupietzc3bf3502021-02-19 17:18:57 +010085
Marc Kupietz355d5482021-02-22 17:13:56 +010086cat("With SMOTE resampled training data\n")
Marc Kupietzc3bf3502021-02-19 17:18:57 +010087smoted.data <- SMOTE(fmla, subset(train, select = c("CO_IDIOM", vars)), perc.over = 1200, perc.under = 100)
Marc Kupietz13f67ed2021-02-22 07:55:03 +010088rf_classifier = randomForest(fmla, smoted.data, importance=TRUE)
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010089prediction_for_table <- predict(rf_classifier,test %>% select(-CO_IDIOM))
Marc Kupietz201e6f32021-02-22 12:34:13 +010090res <- confusionMatrix(prediction_for_table,test$CO_IDIOM, positive = "idiom")
Marc Kupietz355d5482021-02-22 17:13:56 +010091collected_results <- bind_cols(collected_results, "rf with SMOTE" = res$byClass)
Marc Kupietz13f67ed2021-02-22 07:55:03 +010092print(res)
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +010093
Marc Kupietz355d5482021-02-22 17:13:56 +010094cat("With SMOTE and cutoff\n")
Marc Kupietz201e6f32021-02-22 12:34:13 +010095prediction_for_table <- predict(rf_classifier,test %>% select(-CO_IDIOM), cutoff = c(0.2, 0.8))
96res <- confusionMatrix(prediction_for_table,test$CO_IDIOM, positive = "idiom")
Marc Kupietz355d5482021-02-22 17:13:56 +010097collected_results <- bind_cols(collected_results, "rf with SMOTE and cutoff" = res$byClass)
Marc Kupietz1be40eb2021-02-22 08:10:29 +010098print(res)
99
Marc Kupietz355d5482021-02-22 17:13:56 +0100100collected_results <- collected_results %>%
101 round(3) %>%
102 add_column(measure = names(res$byClass)) %>%
103 column_to_rownames("measure")
104
105View(collected_results)
Marc Kupietz1be40eb2021-02-22 08:10:29 +0100106
PeterFankhauserIDS03d4ece2021-02-22 20:58:28 +0100107# Analysing tradeoff between Fscore, Recall, Precision for various cutoffs
PeterFankhauserIDSd2c893a2021-02-22 21:16:06 +0100108# full range from precision almost 100% to recall almost 100%
PeterFankhauserIDS03d4ece2021-02-22 20:58:28 +0100109rf_classifier = randomForest(fmla, train, importance=TRUE)
Marc Kupietzb1b03362021-02-23 09:32:16 +0100110cvalues<-tibble()
Marc Kupietz347a0392021-02-23 09:56:05 +0100111for (c in c(seq(from=0.4,to=0.99,by=0.025),0.999)) {
112 prediction_for_table <- predict(rf_classifier, test %>% select(-CO_IDIOM), cutoff = c(1-c, c))
PeterFankhauserIDS03d4ece2021-02-22 20:58:28 +0100113 conf<-confusionMatrix(prediction_for_table, test$CO_IDIOM, positive = "idiom")
Marc Kupietzb1b03362021-02-23 09:32:16 +0100114 cvalues <-bind_rows(cvalues, c(cutoff=c, conf$byClass))
PeterFankhauserIDS03d4ece2021-02-22 20:58:28 +0100115}
Marc Kupietzb1b03362021-02-23 09:32:16 +0100116cvalues %>%
117 select(c("cutoff", "Recall", "Precision", "F1", "Specificity", "Balanced Accuracy")) %>%
118 pivot_longer(!cutoff, names_to=c("measure")) %>%
PeterFankhauserIDS3f97f362021-02-23 10:28:07 +0100119 ggplot(aes(cutoff, value, colour=measure)) + geom_line() +
120 scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
121 scale_y_continuous(breaks = scales::pretty_breaks(n = 10))
PeterFankhauserIDS03d4ece2021-02-22 20:58:28 +0100122
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100123# Using estimates by random forest on entire dataset
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100124
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100125library(randomForest)
Marc Kupietz13f67ed2021-02-22 07:55:03 +0100126rf_classifier_full = randomForest(fmla, data=ngramme, importance=TRUE)
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100127rf_classifier_full
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100128# class.error is 1 - recall
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100129varImpPlot(rf_classifier_full)
130
131# Feature ranking
132
133# rf features as table
134
135# correlated features seem to split their rankings
136
137rfranks<-importance(rf_classifier_full)[,3:4]
138
139# ttest
140
Marc Kupietz201e6f32021-02-22 12:34:13 +0100141idioms<-ngramme %>% filter(CO_IDIOM == "idiom")
142nonidioms<-ngramme %>% filter(CO_IDIOM != "idiom")
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100143
144ttestPvalues<-sapply(vars,
145 function(sel) t.test(idioms[sel],nonidioms[sel])$p.value)
146
147# information gain
148# multiply by 1000 to avoid undersized bins
PeterFankhauserIDSa5b2acf2021-02-22 18:14:18 +0100149# features are ranked individually no matter their correlation
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100150igain<-information.gain(fmla, data=ngramme%>%mutate_at(vars, ~ . * 1000),unit="log2")
151
152featureRanks<-cbind(rfranks,igain,ttestPvalues)
153
154#randomForestExplainer::explain_forest(rf_classifier )
155
156# averate estimates and feature ranks over 10 runs
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100157
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100158errrate<-0
159conf<-matrix(0,2,3)
160featureRanks<-matrix(0,4,length(vars))
161for (i in 1:10) {
Marc Kupietz13f67ed2021-02-22 07:55:03 +0100162 rfc =randomForest(fmla, data=ngramme, importance=TRUE)
PeterFankhauserIDSa5b2acf2021-02-22 18:14:18 +0100163 #rfc =randomForest(fmla, data=ngramme, importance=TRUE, cutoff=c(0.2, 0.8))
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100164 errrate<-errrate+rfc$err.rate[100,1]
165 conf<-conf+rfc$confusion
166 featureRanks<-featureRanks+
167 cbind(importance(rfc)[,3:4],
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100168 information.gain(fmla, data=ngramme%>%mutate_at(vars, ~ . * 1000),unit="log2"),
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100169 sapply(vars,
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100170 function(sel) t.test(idioms[sel],nonidioms[sel])$p.value))
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100171 print(errrate/i)
172 conf1<-round(
173 rbind(
174 cbind(conf[,1:2]/i,(1-conf[,3]/i)*100),
175 c(100*diag(conf[,1:2])/colSums(conf[,1:2]),NA),
176 c(rowSums(conf[,1:2]/i),NA)),digits=2)
Marc Kupietz201e6f32021-02-22 12:34:13 +0100177 colnames(conf1)<-c("1","0","rec")
178 rownames(conf1)<-c("1","0","prec","sum")
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100179 print(conf1)
180}
181featureRanks<-featureRanks/10
PeterFankhauserIDSd1f3df82021-02-20 14:44:01 +0100182colnames(featureRanks)<-c("MeanDecreaseAccuracy","MeanDecreaseGini","InformationGain","Ttest")
Marc Kupietzc3bf3502021-02-19 17:18:57 +0100183
184
185
186