看到群里有老师在问“如何从p-value计算获得q-value”。今天简单进行下讨论,并附送一份可在命令行下独立运行的代码。
首先,关于“P值”我们之前也进行过讨论(计算个“P值”,咱也不太懂,咱也不敢信)。大家或许还对下面的这张PPT还有印象:对于来自相同分布的两组样本,如果很多次采集数据并进行假设检验,“本不应显著的”两组样本也会出现P值很小的“显著结果”(“P-hacking”)。
上图中,我们对来自相同正态分布n(5,5)的x和y进行仿真,每次模拟三个重复,并进行t检验;假设我们有这么一个“劳模”进行了1000次(x轴)独立实验,并各自计算出p值(y轴);其中,红线为p=0.05的阈值线。从图上看出,1000次实验中,有很多次已经出现p<0.05的情况了。实际上,这种情况下“P-value”服从[0,1]区间的“均匀分布”;在随机情况下,1000次实验出现也能出现大约50次“P<0.05的情况”。
所以,在进行了很多次假设检验的时候,为了控制FDR(错误发现率),就要使用统计学方法进行“多检验矫正”(multiple-testing correction),并计算得到q-value。
关于p-value、q-value和FDR的关系,大家可以参考本文链接的网页上的解释(英文的哦~~)。本文不重复讨论。
但简单地,我们对q-value和p-value的特点进行以下总结:
P-value和Q-value都是分布在[0,1]范围内的实数。
从P-value列表计算得到Q-value列表的统计模型有很多(参考R语言中p.adjust函数)。
P-value 列表计算得到Q-value后,各个元素的大小排序不发生改变(不考虑相等的情况)。
相对于P-value列表中的对应元素的p值,其q值只会变大(或不变),不会变小(但不会超过1)。
P值经放大到对应的Q值的过程中,和列表中的元素的个数也有关系:即,不同的总体元素个数下,同一个P值经放大往往会得到不同的Q值。
如果同时进行的假设检验次数很多时,只使用“P值”进行讨论的稿件,会被审稿人质疑作者的统计学基础。
以下是小编附送的一份R语言的代码,另存为可执行的文件后,可通过一行shell命令将P值转换为Q值。供各位同行参考。
在linux命令行下,将下的代码另存为 Pv2Qv.R文件,并添加可执行权限。
#!/usr/bin/env Rscript
# by 麦陇 @ 小麦研究联盟
suppressPackageStartupMessages(library("optparse"))
option_list <- list(
make_option(c("-i", "--infile"), dest = "infile", default = "",
help="[opt] input file, use STDIN if omitted"),
make_option(c("-p", "--pv"), dest = "pv_col", default = 1,
help="The column number for p value [default: %default]"),
make_option(c("-o", "--outfile"), dest = "outfile", default = "",
help = "[opt] output file, use STDOUT if omitted")
)
#
parser <- OptionParser(usage = "%prog [options] file",
option_list=option_list, description = "Description: \
Calculate the q-values from a list of p-values.\
New columns will be added to in right most column of output.xls.\
Example: \
Pv2Qv.R -i input.xls -o output.xls \
"
)
#
arguments <- parse_args(parser)
opt <- arguments$options
# infile
infile <- arguments$infile
if(infile == "") {
infile = file("stdin")
}
# outfile
outfile = arguments$outfile
if( outfile == "") {
outfile = stdout
}
# Read the input file
T = read.table( infile, header=FALSE, sep = "\t", check.names = FALSE)
PV = T[,arguments$pv_col]
QV = format(p.adjust(PV, method = "fdr"), digits = 4, scientific = TRUE)
write.table( cbind(T, QV), file = outfile, quote = FALSE, sep = "\t",
row.names = FALSE, col.names = FALSE)
之后,就可以参考如下命令行从P值直接生成Q值。
说明:输入文件需要是包含所有元素的、同时计算了P值的列表;可以有多列,在命令行中指定P值所在的列号即可(如第三列:“-p 3”)。
./Pv2Qv.R -i input_file -p 3 -o output_file
# 假设输入文件中第三列是P值
# 生成文件output_file会在input_file的基础上增加一列对应的q值。