程序运行太久?不妨来试试多线程运行,%dopar%让你的程序快到飞起

在做数据分析时,我们经常遇到需要for循环的程序,如果for循环内的操作简单还好,但是我们经常遇到在for循环内做复杂操作的程序,这种程序相当耗时,必须等前一个任务完成了才进行下一个任务,如果每个任务都与之前的任务无关,那我们就可以使用多线程来运行这个for循环。

之前小花就遇到过计算mRNA与lncRNA相关性的分析,共有116个mRNA和4888个lncRNA,样本数是589,如果用普通的for循环的话那得运行到猴年马月,还好小花有服务器,可以在服务器上多线程运行。要是您有自己做不了的生信分析,可以联系我,如果您的数据量比较大也可以联系小花租赁高性价比的服务器哦。

首先我们来看一下数据:

原本小花也是用普通的for循环跑的,本来想着想让程序运行着,我去吃个饭再回来看,结果吃完饭又登陆几个小时程序还没有运行好:

up_glycolysis = read.table(“./input/up_glycolysis.txt”, sep = “\n”)$V1

down_glycolysis = read.table(“./input/down_glycolysis.txt”, sep = “\n”)$V1

diff_mRNA = c(up_glycolysis, down_glycolysis)

diffLab = read.csv(“./input/LUAD_lncRNA_diff.csv”)

#diffLab = diffLab[-which(is.na(diffLab$X)),]

diff_lncRNA = diffLab$X

mRNA_data_norm = read.csv(“./input/LUAD_mRNA_norm.csv”, row.names = 1)

lncRNA_data_norm = read.csv(“./input/LUAD_lncRNA_norm.csv”, row.names = 1)

m_lnc_cor = matrix(0, nrow = length(diff_mRNA), ncol = length(diff_lncRNA))

m_lnc_cor_p = matrix(0, nrow = length(diff_mRNA), ncol = length(diff_lncRNA))

rownames(m_lnc_cor) = diff_mRNA

colnames(m_lnc_cor) = diff_lncRNA

rownames(m_lnc_cor_p) = diff_mRNA

colnames(m_lnc_cor_p) = diff_lncRNA

for(i in 1:length(diff_mRNA)){

for(j in 1:length(diff_lncRNA)){

m_lnc_cor[i, j] = cor(as.numeric(mRNA_data_norm[diff_mRNA[i],]), as.numeric(lncRNA_data_norm[diff_lncRNA[j],]))

m_lnc_cor_p[i, j] = cor.test(as.numeric(mRNA_data_norm[diff_mRNA[i],]), as.numeric(lncRNA_data_norm[diff_lncRNA[j],]))[[3]]

}

}

这时候我想到,既然我的for循环内的语句前后间的计算结果并不会有影响,所以我想到了用多线程来运行:

## 多线程运行

library(parallel)

library(foreach)

library(doParallel)

# 自定义合并函数

combine_results <- function(…) {

# 将…中的所有列表转换为矩阵的列

lists <- list(…)

result <- do.call(rbind, lapply(lists, function(lst) unlist(lst)))

return(result)

}

# 检测可用的核心数

numCores <- detectCores()

# 创建并注册集群

clus <- makeCluster(numCores)

registerDoParallel(clus)

start_time <- Sys.time()

results <- foreach(i = 1:length(diff_mRNA), .combine=’combine_results’, .packages = c(“stats”)) %dopar% {

temp_cor <- numeric(length(diff_lncRNA))

temp_cor_p <- numeric(length(diff_lncRNA))

for(j in 1:length(diff_lncRNA)) {

temp_cor[j] <- cor(as.numeric(mRNA_data_norm[diff_mRNA[i],]), as.numeric(lncRNA_data_norm[diff_lncRNA[j],]))

temp_cor_p[j] <- cor.test(as.numeric(mRNA_data_norm[diff_mRNA[i],]), as.numeric(lncRNA_data_norm[diff_lncRNA[j],]))$p.value

}

list(cor = temp_cor, p_value = temp_cor_p)

}

end_time <- Sys.time()

# 关闭集群

stopCluster(clus)

time_taken_multi <- end_time – start_time

print(time_taken_multi)

m_lnc_cor = results[,1:length(diff_lncRNA)]

m_lnc_cor_p = results[,(length(diff_lncRNA)+1):(2*length(diff_lncRNA))]

rownames(m_lnc_cor) = diff_mRNA

colnames(m_lnc_cor) = diff_lncRNA

rownames(m_lnc_cor_p) = diff_mRNA

colnames(m_lnc_cor_p) = diff_lncRNA

saveRDS(m_lnc_cor, “./output/m_lnc_cor.rds”)

saveRDS(m_lnc_cor_p, “./output/m_lnc_cor_p.rds”)

这是用htop命令查看后台运行状况的结果,可以看到56个线程全部占满了:

最后只用了11分钟就结束了战斗:

这大大提高了我们的分析效率,当然不是所有程序都能使用多线程来运行,只有具备这些特点的程序才能改写成多线程运行的方式:

1、任务可分割性:程序中的任务可以被分割成多个小任务,这些小任务可以独立执行而不会相互干扰。

2、数据依赖性低:理想的多线程任务应该具有较低的数据依赖性,这意味着不同线程处理的数据可以相对独立,减少了线程间的通信和同步需求。

    聪明的你也许已经发现了,我们的程序中使用了foreach和%dopar%,因为在 R 语言中,foreach 循环通常与 %dopar% 结合使用,以实现并行计算。这样可以显著提高处理大数据集时的效率,因为它允许同时在多个核心上执行代码。这是在 R 中使用 foreach 而不是普通 for 循环的主要原因之一。%dopar% 是 R 语言中 foreach 包的一个操作符,用于在并行计算中执行循环。当你使用 %dopar% 时,它会将 foreach 循环中的每次迭代分配到不同的核心或处理器上并行执行,这样可以显著提高计算效率,特别是在处理大量数据时。

代码中的combine_results是我们自定义的合并函数,用于将多线程计算的结果合并,因为我们同时计算了相关性和相关性pvalue,所以不能用普通的rbind或cbind合并。

好了,今天的知识就分享到这里了,如果各位觉得自己运行代码太麻烦,欢迎用我们的云生信小工具(http://www.biocloudservice.com/home.html),只要输入合适的数据就可以直接出想要的图呢一寸光阴一寸金,寸金难买寸光阴,服务器可以大大提升我们的工作效率,欢迎联系小花租赁高性价比的服务器哦。