查看原文
其他

竟然被awk生成的随机数给整蒙了,也谈随机数生成种子

生信宝典 生信宝典 2022-03-29

我们现在主要的一个业务是给科研单位等提供数据库构建服务,目前承接的数据库已经发表了3NAR文章,具体见你的数据也可以-三篇NAR的数据库

这次一位老师需要做数据库,但数据还没做好,时间要求却比较急,我们只能随机生成一些数据来作为测试先把数据库搭起来,等完成后再替换为真实数据,稍加测试,也就完成了。

最开始是这么生成随机数字的,看上去没问题,每运行一次都会生成一个随机数,符合预期。

awk 'BEGIN{OFS=FS="\t";}{ $2=100 * rand(); print $0;}' 00232503-7e34-479a-b6fb-0b52e78b554e.txt | cut -f 1-3 | head
Rnu7-186P 99.4034 ACC-3
Rnu2-41P 24.6362 ACC-3
awk 'BEGIN{OFS=FS="\t";}{ $2=100 * rand(); print $0;}' 00232503-7e34-479a-b6fb-0b52e78b554e.txt | cut -f 1-3 | head
Rnu7-186P 24.3382 ACC-3
Rnu2-41P 87.6752 ACC-3

但当放到一个for循环中时 ,问题就出来了,每次循环的随机数都一样:

for i in *.txt; do awk 'BEGIN{OFS=FS="\t";}{ $2=100 * rand(); print $0;}' $i | cut -f 1-3 | head -n 2; echo "------------"; done
Rnu7-186P 99.0514 ACC-3
Rnu2-41P 82.4637 ACC-3
------------
Rnu7-186P 99.0514 ACC-3
Rnu2-41P 82.4637 ACC-3
------------
Rnu7-186P 99.0514 ACC-3
Rnu2-41P 82.4637 ACC-3
------------

这猜测是每次循环时随机数发生器给的种子都是一致的,导致随机数在每个循环都一致了,修改如下:每次循环单独给一个随机数的种子就好了。

for i in `seq 1 3`; do awk -v seed=$RANDOM 'BEGIN{OFS=FS="\t";srand(seed);}{ $2=100 * rand(); print $0;}' 00232503-7e34-479a-b6fb-0b52e78b554e.txt | cut -f 1-3 | head -n 2; echo "------------"; done
Rnu7-186P 38.0502 ACC-3
Rnu2-41P 76.7106 ACC-3
------------
Rnu7-186P 99.1498 ACC-3
Rnu2-41P 65.7196 ACC-3
------------
Rnu7-186P 92.9258 ACC-3
Rnu2-41P 24.0214 ACC-3
------------

这就是awk自己的坑了。

In most awk implementations, including gawk, rand() starts generating numbers from the same starting number, or seed, each time you run awk. Thus, a program generates the same results each time you run it. The numbers are random within one awk run but predictable from run to run. This is convenient for debugging, but if you want a program to do different things each time it is used, you must change the seed to a value that is different in each run. To do this, use srand().

随机数生成器的种子

除了在显示生成随机数做测试时会用到随机数生成器,很多其它时候比如做Kmeans聚类时WGCNA分析时, 随机森林分析时也都会有随机过程,每次运行结果都有可能不同,为了保证结果的可重复性,这时就可以设置一个随机数种子。其原则是:种子定了,每次运行结果也就不会变了

通常这个种子是一个整数,任意整数都可以。讲课时,我一般说大家可以选择自己的幸运数字来设置,在R中通常通过函数set.seed来设置:

通常,如果我们没有自己设置种子,大部分程序语言中会调用当前的时间戳作为随机数的种子,每次操作时间都不同,时间戳也就不同,获得的随机数序列也就不同。

下面是一个R中的示例,可以看到前面两次运行rnorm(5)获得的返回值都不同。而在设置set.seed(10)后,两次运行rnorm(5)获得的返回值完全一致。当然这个设置只是对最近的命令有效,下面再运行一次rnorm(5),又是基于时间戳生成的完全不同的数据。

# 不设置
rnorm(5)
# [1] 1.1017795 0.7557815 -0.2382336 0.9874447 0.7413901
rnorm(5)
# [1] 0.08934727 -0.95494386 -0.19515038 0.92552126 0.48297852


set.seed(10)
rnorm(5)
# [1] 0.01874617 -0.18425254 -1.37133055 -0.59916772 0.29454513

set.seed(10)
rnorm(5)
# [1] 0.01874617 -0.18425254 -1.37133055 -0.59916772 0.29454513

rnorm(5)
[1] 0.3897943 -1.2080762 -0.3636760 -1.6266727 -0.2564784

关于随机数种子,虽然看上去比较简单,但每次课程,总会有多位老师问起,问起最多的就是为什么你选择10作为随机数种子?依据是什么?我怎么选?实际就记住两点:

  1. 同一个随机数种子获得的随机数序列是一致的,不管这个种子是10, 20还是 30

  2. 随机数种子可以是任意值,看心情选择就好,课程中选哪个也都是随机的。

这个为生信学习和生信作图打造的开源R教程真香!!!

这个为生信学习打造的开源Linux教程真香!!!

往期精品(点击图片直达文字对应教程)


后台回复“生信宝典福利第一波”或点击阅读原文获取教程合集

 

(请备注姓名-学校/企业-职务等)


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存