|
a |
|
b/R/runKappa.R |
|
|
1 |
#' @name runKappa |
|
|
2 |
#' @title Run consistency evaluation using Kappa statistic |
|
|
3 |
#' @description Calculate Kappa statistic to measure the consistency between two appraisements |
|
|
4 |
#' @param subt1 A numeric vector to indicate the first appraisement. Order should be exactly the same with subt2 for each sample. |
|
|
5 |
#' @param subt2 A numeric vector to indicate the second appraisement. Order should be exactly the same with subt1 for each sample. |
|
|
6 |
#' @param subt1.lab A string value to indicate the label of the first subtype. |
|
|
7 |
#' @param subt2.lab A string value to indicate the label of the second subtype. |
|
|
8 |
#' @param fig.path A string value to indicate the output path for storing the consistency heatmap. |
|
|
9 |
#' @param fig.name A string value to indicate the name of the consistency heatmap. |
|
|
10 |
#' @param width A numeric value to indicate the width of output figure. |
|
|
11 |
#' @param height A numeric value to indicate the height of output figure. |
|
|
12 |
#' @details This function evaluates the consistency between two appraisements that targets to the same cohort. |
|
|
13 |
#' For example, the NTP-predicted subtype amd PAM-predicted subtype of external cohort, or the current subtype and predicted subtype of discovery cohort. |
|
|
14 |
#' Therefore, the arguments `subt1` and `subt2` can be the `clust` column of `clust.res` derived from `getMOIC()` with one specified algorithm |
|
|
15 |
#' or `get\%algorithm_name\%` or `getConsensusMOIC()` with a list of multiple algorithms or `runNTP()` or `runPAM()`. |
|
|
16 |
#' However, subtypes identified from different algorithm (i.e., `get\%algorithm_name1\%` and `get\%algorithm_name2\%`) can not be evaluated |
|
|
17 |
#' because the subtype 1 identified from the first algorithm may not be the same subtype 1 from the second algorithm. |
|
|
18 |
#' @return A figure of consistency heatmap (.pdf). |
|
|
19 |
#' @export |
|
|
20 |
#' @importFrom grDevices dev.copy2pdf |
|
|
21 |
#' @examples # There is no example and please refer to vignette. |
|
|
22 |
runKappa <- function(subt1 = NULL, |
|
|
23 |
subt2 = NULL, |
|
|
24 |
subt1.lab = NULL, |
|
|
25 |
subt2.lab = NULL, |
|
|
26 |
fig.path = getwd(), |
|
|
27 |
fig.name = "constheatmap", |
|
|
28 |
width = 5, |
|
|
29 |
height = 5) { |
|
|
30 |
|
|
|
31 |
subt1 <- as.vector(as.character(subt1)) |
|
|
32 |
subt2 <- as.vector(as.character(subt2)) |
|
|
33 |
|
|
|
34 |
# check sample |
|
|
35 |
if(length(subt1) != length(subt2)) { |
|
|
36 |
stop("subtypes identified from different cohorts.") |
|
|
37 |
} |
|
|
38 |
|
|
|
39 |
# check subtype |
|
|
40 |
if(!identical(sort(unique(subt1)),sort(unique(subt2)))) { |
|
|
41 |
stop("subtypes fail to matched from two appraisements.") |
|
|
42 |
} |
|
|
43 |
|
|
|
44 |
# check argument |
|
|
45 |
if(is.null(subt1.lab) | is.null(subt2.lab)) { |
|
|
46 |
stop("label for subtype1 and subtype2 must be both indicated.") |
|
|
47 |
} |
|
|
48 |
|
|
|
49 |
# calculate consistency |
|
|
50 |
comb.subt <- data.frame(subt1 = paste0("CS", subt1), |
|
|
51 |
subt2 = paste0("CS", subt2), |
|
|
52 |
stringsAsFactors = F) |
|
|
53 |
|
|
|
54 |
tab_classify <- as.data.frame.array(table(comb.subt$subt1,comb.subt$subt2)) |
|
|
55 |
|
|
|
56 |
# n.moic <- length(unique(moic.res$clust.res$clust)) |
|
|
57 |
# if(n.moic < 5) { |
|
|
58 |
# p.fisher <- fisher.test(tab_classify, workspace = 1e9)$p.value |
|
|
59 |
# } else { |
|
|
60 |
# message("--using simulated p value in fisher's exact test.") |
|
|
61 |
# p.fisher <- fisher.test(tab_classify, simulate.p.value = T)$p.value |
|
|
62 |
# } |
|
|
63 |
|
|
|
64 |
# calculate kappa statistic |
|
|
65 |
x <- table(comb.subt$subt1,comb.subt$subt2) |
|
|
66 |
nr <- nrow(x); nc <- ncol(x); N <- sum(x) |
|
|
67 |
Po <- sum(diag(x))/N; Pe <- sum(rowSums(x) * colSums(x)/N)/N |
|
|
68 |
kappa <- (Po - Pe)/(1 - Pe) |
|
|
69 |
seK0 <- sqrt(Pe/(N * (1 - Pe))) |
|
|
70 |
p.v <- 1 - pnorm(kappa/seK0) |
|
|
71 |
p.lab <- ifelse(p.v < 0.001, "P < 0.001", paste0("P = ", format(round(p.v,3), nsmall = 3))) |
|
|
72 |
|
|
|
73 |
# generate consistency table |
|
|
74 |
blue <- "#204F8D" |
|
|
75 |
lblue <- "#498EB9" |
|
|
76 |
dwhite <- "#B6D1E8" |
|
|
77 |
white <- "#E6EAF7" |
|
|
78 |
|
|
|
79 |
par(bty="n", mgp = c(2,0.5,0), mar = c(4.1,4.1,4.1,2.1),tcl=-.25, font.main=3) |
|
|
80 |
par(xpd=NA) |
|
|
81 |
plot(c(0,ncol(tab_classify)),c(0,nrow(tab_classify)), |
|
|
82 |
col = "white", |
|
|
83 |
xlab = "",xaxt = "n", |
|
|
84 |
ylab = "",yaxt = "n") |
|
|
85 |
title(paste0("Consistency between ",subt1.lab," and ",subt2.lab,"\nKappa = ", format(round(kappa,3), nsmall = 3), |
|
|
86 |
"\n", p.lab), |
|
|
87 |
adj = 0, line = 0) |
|
|
88 |
|
|
|
89 |
# add y-axis |
|
|
90 |
axis(2, at = 0.5:(nrow(tab_classify)-0.5), labels = FALSE) |
|
|
91 |
text(y = 0.5:(nrow(tab_classify)-0.5), |
|
|
92 |
par("usr")[1], |
|
|
93 |
labels = rownames(tab_classify)[nrow(tab_classify):1], |
|
|
94 |
srt = 0, pos = 2, xpd = TRUE) |
|
|
95 |
mtext(paste0("Subtypes derived from ", subt1.lab), side = 2, line = 3) |
|
|
96 |
|
|
|
97 |
# add x-axis |
|
|
98 |
axis(1, at = 0.5:(ncol(tab_classify)-0.5), labels = FALSE) |
|
|
99 |
text(x = 0.5:(ncol(tab_classify)-0.5), |
|
|
100 |
par("usr")[1] - 0.2, |
|
|
101 |
labels = colnames(tab_classify), |
|
|
102 |
srt = 45, pos = 1, xpd = TRUE) |
|
|
103 |
mtext(paste0("Subtypes derived from ", subt2.lab), side = 1, line = 3) |
|
|
104 |
|
|
|
105 |
# generate colors |
|
|
106 |
input_matrix <- as.matrix(tab_classify) |
|
|
107 |
mat.max = max(input_matrix) |
|
|
108 |
unq.value <- unique(sort(as.vector(input_matrix))) |
|
|
109 |
rbPal <- colorRampPalette(c(white,dwhite,lblue,blue)) |
|
|
110 |
col.vec <- rbPal(max(unq.value) + 1) |
|
|
111 |
col.mat <- matrix(NA,byrow = T,ncol = ncol(input_matrix),nrow = nrow(input_matrix)) |
|
|
112 |
|
|
|
113 |
# fill matrix |
|
|
114 |
for (i in 1:nrow(col.mat)) { |
|
|
115 |
for (j in 1:ncol(col.mat)) { |
|
|
116 |
col.mat[i,j] <- col.vec[input_matrix[i,j] + 1] |
|
|
117 |
} |
|
|
118 |
} |
|
|
119 |
|
|
|
120 |
# generate heatmap |
|
|
121 |
x_size <- ncol(input_matrix) |
|
|
122 |
y_size <- nrow(input_matrix) |
|
|
123 |
|
|
|
124 |
my_xleft = rep(c(0:(x_size-1)),each = x_size) |
|
|
125 |
my_xright = my_xleft + 1 |
|
|
126 |
my_ybottom = rep(c((y_size-1):0),y_size) |
|
|
127 |
my_ytop = my_ybottom + 1 |
|
|
128 |
rect(xleft = my_xleft, |
|
|
129 |
ybottom = my_ybottom, |
|
|
130 |
xright = my_xright, |
|
|
131 |
ytop = my_ytop, |
|
|
132 |
col=col.mat, |
|
|
133 |
border = F) |
|
|
134 |
|
|
|
135 |
# fill count |
|
|
136 |
text(my_xleft + 0.5,my_ybottom + 0.5,input_matrix, cex = 1.3) |
|
|
137 |
|
|
|
138 |
# output to pdf |
|
|
139 |
outFig <- paste0(fig.name,".pdf") |
|
|
140 |
invisible(dev.copy2pdf(file = file.path(fig.path, outFig), width = width, height = height)) |
|
|
141 |
} |