添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

对于第一种情况,代码编辑器中的中文跟 编辑器的编码 有关,因此可能会出现在编辑器中能够显示的中文,但是到了Qt的UI界面中就出现了乱码,例如

1
2
3
QString status = "echo 测试 && pause";
ui.textEdit->setText(status);

原因: Visual Studio中对中文的默认编码和QT对中文的默认编码不一样。也就是说,我们输入了X编码方案的中文,但是在编译阶段,Qt用了Y编码方案进行解码,从而导致源码阶段的乱码。

解决方案,就是设置好编码。

1
2
3
4
5
6
//声明来源是local 8bit
QString status = QString::fromLocal8Bit("echo 测试 && pause");
ui.textEdit->setText(status);
//用unicode字面量
QString status = fromUtf16(u"echo 测试 && pause");
ui.textEdit->setText(status);

对于第二种情况,QString获取到的中文就是它默认的编码方式,因此通过文件选择框获取的文件路径,如果包含中文,它也能够在Qt的UI界面正常展示。

但是如果直接转成string,然后调用system就会出现乱码,如

1
2
3
4
//乱码
QString status = QString::fromUtf16(u"echo 测试 && pause");
string test = status.toStdString();
system(test.c_str());

即,QString转string的时候存在编码上的问题。

解决方案, 将QString转成Loal8Bit才行,而不是直接使用toStdString,之后在构建成string,转成const char*才能避免乱码。

1
2
3
QString status = QString::fromUtf16(u"echo 测试 && pause");
string test(status.toLocal8Bit());
system(test.c_str());
  • https://cloud.tencent.com/developer/article/1464366
  • 彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)
  • 借Qt中文乱码谈谈Coding中的编码问题
  • 1
    2
    int a = 1;
    int *ptr = &a;

    常量指针(const pointer), 指针本质上是个变量,因此可以用 const 进行修饰,表示该指针记录的值是一个常量。常量指针在声明时需要初始化值。

    1
    2
    int a=1;
    int *const ptr = &a;

    指向常量的指针(pointer refer to const)

    1
    2
    3

    int const a = 1;
    int cont *ptr = &a;

    指向常量的常量指针

    1
    2
    3

    int const *const ptr

    这是我第一次在号称宇宙最强的IDE Visual Studio上调用非标准库编译 C++ 程序,中间遇到了很多的报错,让我怀疑人生。

    首先,我们需要从 Sourceforge 下载OpenCV, 目前最新版是4.5.2

    下载的exe文件双击之后会出现解压界面

    解压缩后得到如下文件

    我们需要依次系统属性->高级->环境变量,找到并设置设置环境变量Path

    如果不在环境变量中设置opencv中的bin路径,会出现如下报错 【由于找不到opencv_world452d.dll, 无法继续执行代码】

    在Visual Studio 2019中使用OpenCV构建项目的流程如下

    第一步:新建一个C++的控制台项目。

    第二步:配置项目的项目名称,例如opencv-test

    创建项目之后,先将Debug 改为x64,因为后续构建的是x64的项目。

    第三步:为了能让代码正常运行,我们需要配置这个OpenCV项目的所需的include和library路径,以及依赖的lib文件。

    在菜单栏的项目(P)中选择属性(P)

    选择VC++目录(VC++ Directories)的包含目录(Include Directories),点击编辑

    添加opencv的include的路径(头文件)

    同样的操作,也用于库目录(Library Directories)的设置

    添加library路径(路径比较深)

    检查下,刚刚修改的include和library

    此外还需要增加一个包括所有模块的lib文件。选择链接器(linker)中的输入(input), 接着编辑附加依赖项

    ![linker( https://halo-1252249331.cos.ap-shanghai.myqcloud.com/upload/2021/05/image-75a058ac43214ee9bfb9fb18f884df75.png )

    添加 opencv_world452d.lib ,该文件包括opencv里的所有模块(lib文件和opencv版本有关,可从lib目录中确认)。

    如果不设置该变量,会出现【无法解析外部符号】的报错

    如果写错成 opencv_world452d.dll,就会出现【 LNK1104 无法打开文件”opencv_world452d.dll”】报错(之所以写成dll,是因为之前出现的dll找不到报错,导致我以为增加的是这个文件)。

    如下是测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    int main()
    {
    Mat image = Mat::zeros(600, 600, CV_8UC3);
    putText(image, "xuzhougeng", Point(100,300), FONT_HERSHEY_COMPLEX, 2.0, Scalar(100,200,200 ), 5 );
    imshow("Display Window", image);
    waitKey(0);
    return 0;
    }

    能正常出图表示能正确在项目中调用OpenCV。

    参考资料:

  • https://docs.opencv.org/master/d3/d52/tutorial_windows_install.html
  • https://medium.com/@subwaymatch/opencv-410-with-vs-2019-3d0bc0c81d96
  • Slurm的安装依赖于root权限

    munge配置

    1
    2
    3
    4
    5
    wget https://github.com/dun/munge/releases/download/munge-0.5.14/munge-0.5.14.tar.xz
    rpmbuild -tb --without verify munge-0.5.14.tar.xz
    cd /root/rpmbuild/RPMS/x86_64
    rpm -ivh munge-0.5.14-1.el7.x86_64.rpm \
    munge-devel-0.5.14-1.el7.x86_64.rpm munge-libs-0.5.14-1.el7.x86_64.rpm
    1
    2
    sudo -u munge /usr/sbin/mungekey -v
    # mungekey: Info: Created "/etc/munge/munge.key" with 1024-bit key

    生成的 munge.key 文件需要分发到所有的计算节点。

    启动守护进程(daemon)

    1
    2
    3
    4
    systemctl enable munge
    systemctl start munge
    # 检查状态
    systemctl status munge

    方法1: RPM安装

    下载页面, https://www.schedmd.com/downloads.php

    因为是CentOS7, 因此我下载的是19.05版本。 而20.11可能不再支持Python2。

    1
    2
    3
    4
    5
    6
    wget https://download.schedmd.com/slurm/slurm-19.05.8.tar.bz2
    yum install pam-devel perl-Switch -y
    rpmbuild -ta slurm-19.05.8.tar.bz2
    cd /root/rpmbuild/RPMS/x86_64
    rpm --install slurm-*.rpm

    创建用户 slurm

    1
    2
    adduser slurm

    创建配置文件(非常关键)

    1
    2
    mkdir -p /etc/slurm
    touch /etc/slurm/slurm.conf

    etc中slurm.conf文件里面的配置信息来自于 https://slurm.schedmd.com/configurator.html 生成,需要配置如下选项

  • SlurmctldHost: 信息来自于 hostname -f

  • NodeName: 信息来自于 hostname -f , 只不过是子节点的服务器信息,如果只有单个主机,那么同上

  • ComputeNodeAddress: 计算节点的IP地址,仅有单个节点时,信息为空

  • PartitionName: 任务分配名,改成batch

  • CPUs: 设置为空

  • CoresPerSocket: 实际的物理CPU数,例如96

  • ThreadsPerCore: 如果超线程,设置为2

  • RealMemory: 服务器内存大小,单位为Mb

  • SlurmUser: slurm要求有一个专门的用户,

  • StateSaveLocation: 一定要改成 /var/spool/slurmd, 否则会出现权限问题

    最后还需要增加一行 CgroupMountpoint=/sys/fs/cgroup

    启动 slurmctld, slurmd 的守护进程(deamon)

    1
    2
    3
    4
    5
    6
    7
    8
    # 控制节点
    systemctl enable slurmctld
    systemctl start slurmctld
    systemctl status slurmctld
    # 计算节点
    systemctl enable slurmd
    systemctl start slurmd
    systemctl status slurmd

    方法2: 通过OpenHPC仓库

    测试安装

    安装结果后,我们创建一个 test.sbatch, 信息如下,用于测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/bin/bash
    #SBATCH -J test # Job name
    #SBATCH -o job.%j.out # Name of stdout output file (%j expands to %jobId)
    #SBATCH -N 1 # Total number of nodes requested
    #SBATCH -n 2 # Total number of mpi tasks #requested
    #SBATCH -t 01:30:00 # Run time (hh:mm:ss) - 1.5 hours
    # Launch MPI-based executable
    echo "Test output from Slurm Testjob"
    NODEFILE=`generate_pbs_nodefile`
    cat $NODEFILE
    sleep 20
    1
    2
    3
    sbatch ./test.sbatch 
    # Submitted batch job 2

    1
    squeue

    如果能输出一个job.X.out 文件,说明我们的SLURM已经配置成功。

    可能报错和解决方案

    使用 rpm --install 的时候可能会遇到如下的报错。这表示你需要安装perl的Switch模块

    1
    2
    3
    4
    error: Failed dependencies:
    perl(Switch) is needed by slurm-openlava-19.05.8-1.el7.x86_64
    perl(Switch) is needed by slurm-torque-19.05.8-1.el7.x86_64

    启动 slurmd的deamon失败

    1
    2
    3
    # systemctl start slurmd
    Job for slurmd.service failed because the control process exited with error code.
    See "systemctl status slurmd.service" and "journalctl -xe" for details.

    按照提示运行 systemctl status slurmd.service 发现error信息如下

    1
    2
    3
    error: Node configuration differs from hardware: Procs=1:192(hw) Boards=1:1(hw) SocketsPerBoard=1:4(hw) ...e=1:2(hw)
    error: cgroup namespace 'freezer' not mounted. aborting

    第一个error原因是在 https://slurm.schedmd.com/configurator.html 填写 “Compute Machines” 的硬件信息出现错误

    第二个error原因是配置文件的默认配置表现不佳,需要做如下替换

    1
    echo CgroupMountpoint=/sys/fs/cgroup >> /etc/slurm/cgroup.conf

    参考: https://stackoverflow.com/questions/62641323/error-cgroup-namespace-freezer-not-mounted-aborting

    参考资料

    配置slurm: https://slurm.schedmd.com/configurator.html

    单节点slurm: http://docs.nanomatch.de/technical/SimStackRequirements/SingleNodeSlurm.html

    munge配置: https://github.com/dun/munge/wiki/Installation-Guide

    Slurm安装与使用: http://wiki.casjc.com/?p=378

    最近有一个需求,计算250条序列两两之间的相似度,之后根据相似度继续进行序列过滤,即A和B如果相似度高于一个阈值,那么就把B去掉。

    我最初想到的就是在Python里面调用EMBOSS的needle,准备使用Python的os或者subprocess模块,但是突然想到之前看WGDI源代码时,发现作者时通过biopython的API进行MAFFT的调用,于是就决定改用Biopython.

    通过查找文档,我找到了Biopython对这部分内容的介绍,见 http://biopython.org/DIST/docs/tutorial/Tutorial.html#sec97 ,示例代码如下

    1
    2
    3
    4
    5
    6
    7
    from Bio.Emboss.Applications import NeedleCommandline
    needle_cline = NeedleCommandline(asequence="alpha.faa",
    bsequence="beta.faa",
    gapopen=10, gapextend=0.5,
    outfile="needle.txt")
    stdout, stderr = needle_cline()
    align = AlignIO.read("needle.txt", "emboss")

    但是这个代码距离我最终代码还有很长的路要走,因为我需要先将序列写出到文件,然后调用命令行,然后再读取输出文件。我的问题是,能不能省掉这几次I/O操作。在Emboss文档的下面,我发现Biopython提供了两个模块,分别是Bio.pairwise2和Bio.Align.PairwiseAligner, 可以直接使用Biopython的.SeqRecord对象。按照文档描述,pairwise2和PairwiseAligner能够达到和调用EMBOSS相同的速度,经过我测试,发现两者速度的确差别不大,这说明直接在Python里面进行配对序列联配并不比先输出运行所需文件,然后读取运行生成文件这个流程快,所以经过一番折腾之后,我还是决定使用NeedleCommandline。

    为了方便后续调用,我写了一个函数,函数的前3个参数是用来从SeqRecord列表中提取序列用于写出,第4个参数用来提供needle程序的路径。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    def run_needle(aseq_pos, bseq_pos, seq, needle_path, verbose=False):
    """run EMBOSS needle retrun align
    """

    # get the Bio object
    aseq = seq[aseq_pos]
    bseq = seq[bseq_pos]

    # create tempfile for write fasta and needle result
    aseq_file = mkstemp(suffix=".fas")[1]
    bseq_file = mkstemp(suffix=".fas")[1]

    needle_file = mkstemp(suffix=".needle")[1]

    # write the sequence
    SeqIO.write(aseq, aseq_file, "fasta")
    SeqIO.write(bseq, bseq_file, "fasta")

    # build needle commnad
    if needle_path is None:
    needle_cmd = NeedleCommandline( asequence=aseq_file, bsequence=bseq_file,
    gapopen=10, gapextend=0.5, outfile=needle_file)
    else:
    assert os.path.isfile(needle_path)
    needle_cmd = NeedleCommandline( needle_path, asequence=aseq_file, bsequence=bseq_file,
    gapopen=10, gapextend=0.5, outfile=needle_file)

    # running needle_cmd
    stdout, stderr = needle_cmd()
    if verbose:
    print(stdout + stderr, file = sys.stderr)

    align = AlignIO.read(needle_file, "emboss")
    # deleting the tempfile
    os.unlink(aseq_file)
    os.unlink(bseq_file)
    os.unlink(needle_file)
    return align

    在写这个函数时,我在输出文件的文件名上折腾了很长时间,最初想使用原序列的ID,但ID包括一些奇奇怪怪的字符,例如 (|: , 而NeedleCommandline不会自动在构建的命令中增加引号,使得运行时会出错。(你可以试试 echo a(b 会出现什么情况 )

    后来,我使用了随机数作为文件名。这个方法在单个进程时问题不大,当时当我为了加速使用多进程时,却发现发现随机数重复了。进程A输出的文件A由于和进程B的输入文件名一样,导致被跑完的进程B删了。当然,解决方法也很简单,加上一些判断文件是否存在的ifelse语句即可。

    不过,我还是选择调用tempfile标准库,使用mkstemp获取临时文件名,才解决了输出文件名的危机。

    最终我就可以在循环中调用这个函数,然后将结果保存在列表中,用于后续处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    fasta = "/path/to/your/sequence.fasta"
    seqs = [ fas for fas in SeqIO.parse(fasta, "fasta") ]
    needle_path = "/opt/biosoft/EMBOSS-6.6.0/bin/needle"

    align_res = []

    for i in range(len(seqs)):
    for j in range(i+1, len(seqs)):
    align = run_needle(i, j, seqs , needle_path)
    align_res.append(align )
    # 以pickle保存python对象
    outfile = open("pairwise_stats.txt", "w")
    for align in align_res:
    line = get_psa_stat(align)
    line = "\t".join(line) + "\n"
    outfile.write(line)

    outfile.close()

    根据我的计算,循环要运行3万多次,按照平均1s一个循环,只要500多分钟,小于9个小时,我只要在回去前运行这个程序(写完第一版大概是晚上10点),然后第二天早上过来收结果就行了。

    但是,正如前文提及到的,「该程序会在多个进程运行时报错」,你也就知道我没有跑循环,而是选择了通过并行对循环进行加速。在一篇中,我将会讲一个,如何把一个简单的问题复杂化,最终又回到最初代码的故事

    最近总觉得Github的访问速度变慢了,导致我的工作效率也肉眼可见的降低了,主要体现在代码数量和质量的双重降低。

    为了解决这一问题,我通过网络检索找到了一个非常好的工具,叫做dev-sidecar ( https://github.com/docmirror/dev-sidecar ), 这个工具的好处在于,图形化界面,完美解决了windows和MacOS上的问题,但是问题也出在图形化界面上,这意味着没有配置图形界面的Linux就用不了。

    但是这难不倒我们,因为我看到了这个工具的参考部分。

    从中, 我们可以看到一个非常重要的地址,fastgit, 这是一个对于 GitHub.com 的镜像加速器。它的使用方法及其简单,也就是将原来的github.com替换成 hub.fastgit.org即可。

    以 bioconda/bioconda-recipes为例,在使用原版的GitHub时,我们的下载速度基本上维持在2MiB/s,某些时候可能不到100Kb.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ time git clone https://github.com/bioconda/bioconda-recipes
    Cloning into 'bioconda-recipes'...
    remote: Enumerating objects: 64, done.
    remote: Counting objects: 100% (64/64), done.
    remote: Compressing objects: 100% (50/50), done.
    remote: Total 276716 (delta 30), reused 31 (delta 11), pack-reused 276652
    Receiving objects: 100% (276716/276716), 303.49 MiB | 3.72 MiB/s, done .
    Resolving deltas: 100% (152345/152345), done.
    Checking out files: 100% (17906/17906), done.
    git clone https://github.com/bioconda/bioconda-recipes 46.51s user 7.04s system 53% cpu 1:39.24 total

    但是使用fastgit加速之后,我的下载速度直接飙升到10Mib/s以上,峰值可以达到30Mib/s.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ time git clone https://hub.fastgit.org//bioconda/bioconda-recipes
    Cloning into 'bioconda-recipes'...
    remote: Enumerating objects: 64, done.
    remote: Counting objects: 100% (64/64), done.
    remote: Compressing objects: 100% (50/50), done.
    remote: Total 276716 (delta 30), reused 31 (delta 11), pack-reused 276652
    Receiving objects: 100% (276716/276716), 303.23 MiB | 21.16 MiB/s, done.
    Resolving deltas: 100% (152348/152348), done.
    Checking out files: 100% (17906/17906), done.
    git clone https://hub.fastgit.org//bioconda/bioconda-recipes 44.88s user 6.09s system 101% cpu 50.367 total

    速度的提升可能是其次的,最重要的是原本因为网络问题的fatal error导致根本下载不了的repos,现在起码能保证能克隆到本地了,实现了从0到1的进步。

    当然,如果觉得每次都需要替换URL太过麻烦,fastgit还支持直接修改Git的配置,即

    1
    2
    git config --global url."https://hub.fastgit.org/".insteadOf "https://github.com/"
    git config --global protocol.http.allow always

    之后,原本需要从Github上克隆的资源都会被定向到fastgit上,不再需要手动进行修改了。

    不过这一缺陷在于,部分时候镜像站点可能会出现不可用的情况,此时你从Github克隆时依旧会被改向到镜像站点,你会误以为原站点出现了问题。不过只要我们人人都去支持下这个项目,随着可用节点的增多,这一缺陷也不是问题了。

    在上一篇教程的最后,我写道「我们能够更加直观的观察到2个peak,基本确定2个多倍化事件」。这里就引出了一个问题,为什么我们可以根据peak来推断多倍化时间?在Lynch和Conery在2000年发表在Science的论文中,他们证明了小规模基因复制的Ks分布是L型,而在L型分布背景上叠加的峰则是来自于演化历史中某个突然的大规模复制事件。例如下图a中,实线是小规模复制的L型分布(呈指数分布, exponential distribution), 最初的峰可能是近期的串联复制引起,随着时间推移基因丢失,形成一个向下的坡。另一条虚线中的峰(呈正态分布, normal distribution)则是由全基因组复制引起。而b-h是一些物种的ks分布。

    注,Lynch和Conery这两人是最早一批研究真核基因组的复制基因的总体保留和丢失情况的研究者。

    也就是说,我们可以通过对Ks频率分布图的观察来判断物种在历史上发生的基因组复制事件。同时又因为WGD在Ks频率分布图中表现为正态分布,那么我们可以通过对峰进行拟合来得到WGD事件所对应的Ks值。

    使用WGDI对峰进行拟合需要注意的是, 它一次只能拟合一个峰 ,因此我们需要先通过之前Ks可视化中所用到kspeaks模块(-kp)来过滤,接着用 PeaksFit (-pf)模块对峰进行拟合得到模型参数,最后用KsFigures(-kf)将拟合结果绘制到一张图上。

    过滤的目的是筛选出同一个WGD事件所形成的共线性区块,这可以通过设置kspeaks模块中和过滤有关的参数来实现

  • pvalue: 共线性区块的显著性
  • tandem: 是否过滤串联重复基因
  • block_length: 共线性区块的基因对的数目
  • ks_area: 根据 ks_median 筛选给定区间的共线性区块
  • multiple: 选择homo的哪列作为筛选标准
  • homo: 根据homo,筛选给定区间内的共线性区块对
  • 其中比较关键的是ks_area 和homo,mutiple, 前者确定ks的大致区间,后者则是更精细地筛选共线性区块。block_length可以过滤掉一些比较小的共线性区块,毕竟基因数越多,就越不可能是随机事件所产生的共线性。

    通过观察之前Ks频率分布图,我们大致确定两个峰所对应的区间分别是0 1.25,1.25 2.5。

    我们分别用 wgdi -kp \? > peak1.conf wgdi -kp \? > peak2.conf 创建两个文件,主要修改其中的ks_area, homo都设置为-1,1 表示不筛选

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # peak1.conf
    [kspeaks]
    blockinfo = ath_block_information.csv
    pvalue = 0.05
    tandem = true
    block_length = 5
    ks_area = 0,1.25
    multiple = 1
    homo = -1,1
    fontsize = 9
    area = 0,2
    figsize = 10,6.18
    savefig = ath.ks_peak1.distri.pdf
    savefile = ath.ks_peak1.distri.csv

    # peak2
    [kspeaks]
    blockinfo = ath_block_information.csv
    pvalue = 0.05
    tandem = true
    block_length = 5
    ks_area = 1.25,2.5
    multiple = 1
    homo = -1,1
    fontsize = 9
    area = 0,2
    figsize = 10,6.18
    savefig = ath.ks_peak2.distri.pdf
    savefile = ath.ks_peak2.distri.csv

    运行 wgdi -kp peak1.conf wgdi -kp peak.conf 输出结果。下图中左是peak1, 右是peak2.

    得到这个图之后,我们还可以进一步绘制Ks点阵图, 使用 wgdi -bk >>peak1.conf wgdi -bk >> peak2.conf 增加配置信息,然后对其进行修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # peak1
    [blockks]
    lens1 = ath.len
    lens2 = ath.len
    genome1_name = Athaliana
    genome2_name = Athaliana
    blockinfo = ath.ks_peak1.distri.csv
    pvalue = 0.05
    tandem = true
    tandem_length = 200
    markersize = 1
    area = 0,2
    block_length = 5
    figsize = 8,8
    savefig = ath.ks_peak1_dotplot.pdf
    # peak2
    [blockks]
    lens1 = ath.len
    lens2 = ath.len
    genome1_name = Athaliana
    genome2_name = Athaliana
    blockinfo = ath.ks_peak2.distri.csv
    pvalue = 0.05
    tandem = true
    tandem_length = 200
    markersize = 1
    area = 0,2
    block_length = 5
    figsize = 8,8
    savefig = ath.ks_peak2_dotplot.pdf

    运行 wgdi -bk peak1.conf ,和 wgdi -bk peak2.conf 输出结果。下图中,左边是peak1,右边是peak2.

    由于我们设置的是 homo=-1,1 因此只是根据共线性块的Ks中位数筛选不同的peak,如果需要更加精细的挑选给定Ks中位数范围内的共线性区块,可以尝试修改homo值再观察结果。

    homo值表示的共线性区块中基因对的匹配情况,越接近于1,越有可能是近期WGD事件得到的基因对,越接近-1,越有可能是古老WGD事件得到的基因对。

    接着,我们将用blockks步骤输出的ath.ks_peak1.distri.csv和tah.ks_peak2.distri.csv 作为peakfit模块的输入,用于拟合。

    分别用 wgdi -pf \? >> peak1.conf wgdi -pf \? >> peak2.conf 创建配置文件, 主要修改blockinfo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # peak1
    [peaksfit]
    blockinfo = ath.ks_peak1.distri.csv
    mode = median
    bins_number = 200
    ks_area = 0,10
    fontsize = 9
    area = 0,3
    figsize = 10,6.18
    savefig = ath.ks_peak1_peaksfit.pdf

    # peak2
    [peaksfit]
    blockinfo = ath.ks_peak2.distri.csv
    mode = median
    bins_number = 200
    ks_area = 0,10
    fontsize = 9
    area = 0,3
    figsize = 10,6.18
    savefig = ath.ks_peak2_peaksfit.pdf

    分别运行 wgdi -pf peak1.conf wgdi -pf peak2.conf , 会得到拟合的图,以及拟合得到参数

    peak1拟合参数

    1
    2
    3
    R-square: 0.9804748699350867
    The gaussian fitting curve parameters are :
    5.02360835744403 | 0.8319599832352784 | 0.10382203381206191

    peak2拟合参数

    1
    2
    3
    R-square: 0.9188261142099129
    The gaussian fitting curve parameters are :
    2.084853812322066 | 1.8332872127128195 | 0.2506813629824027

    拟合参数将会用于后续的 ksfigure 模块。

    我们新建一个 all_ks.csv文件, 该文件的第一行为标题行,第二行以后为数据行。一共有4+3n列,其中第一列是样本信息,第2-3列对应线条的属性,后面都是拟合参数

    1
    2
    ,color,linewidth,linestyle,,,,,,
    ath_ath,green,1,--,5.02360835744403,0.8319599832352784,0.10382203381206191,2.084853812322066,1.8332872127128195,0.2506813629824027

    注意,在终端里面编辑时,需要记住linestyle后面的逗号数量是 3n个, 其中n是最大peak数,例如拟南芥有2个peak,那么就得有6个逗号。

    假如,我们之前用wgdi拟合过其他物种的peak,也得到了一些拟合参数,那么添加到这个文件中,例如

    1
    
    
    
    
        
    
    2
    3
    4
    5
     ,color,linewidth,linestyle,,,,,,
    ath_ath,red,1,--,5.02360835744403,0.8319599832352784,0.10382203381206191,2.084853812322066,1.8332872127128195,0.2506813629824027
    vvi_vvi,yellow,1,-,3.00367275,1.288717936,0.177816426
    vvi_oin_gamma,orange,2,-,1.910418336,1.328469514,0.262257112
    vvi_oin,green,1,-,4.948194212,0.882608858,0.10426

    最后我们用 wgdi -kf >> ath.conf , 创建配置文件并进行修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [ksfigure]
    ksfit = all_ks.csv
    labelfontsize = 15
    legendfontsize = 15
    xlabel = none
    ylabel = none
    title = none
    area = 0,4
    figsize = 10,6.18
    savefig = all_ks.pdf

    运行 wgdi -kf ath.conf 得到下图

    最后,对这篇教程进行总结。

  • 我们依次使用 wgdi -kp 过滤共线性区块, wgdi -pf 对峰进行拟合, wgdi -kf 展示最后的拟合结果
  • 共线性区块的过滤是一个精细的工作,可以尝试设置不同的homo范围观察峰的变化来确定参数是否合理
  • chr1 , start1 , end1 即参考基因组(点图的左边)的共线性范围(对应GFF1的位置)

  • chr2 , start2 , end2 即参考基因组(点图的上边)的共线性范围(对应GFF2的位置)

  • pvalue 即共线性结果评估,常常认为小于0.01的更合理些

  • length 即共线性片段中基因对数目

  • ks_median 即共线性片段上所有基因对 ks 的中位数(主要用来评判ks分布的)

  • ks_average 即共线性片段上所有基因对 ks 的平均值

  • block1 , block2 分别为共线性片段上基因 order 的位置。

  • ks 共线性片段上所有基因对的 ks

  • density1 , density2 共线性片段的基因分布密集程度。值越小表示稀疏。

    最后两列, class1 class2 会在 alignment 模块中用到,对应的是两个block分组,默认值是0表示两个block是同一组。这两列后期需要自己根据覆盖率,染色体核型等多个方面进行确定。举个例子,我们可以根据 homo1 的取值范围对class1进行赋值,例如-1 -0.5 是 1,-0.5 ~ 0.5 是2,0.5 1是3,最后在alignment中会就会用三种颜色来展示,例如下图的1,2,3分别对应red,blue,green.

    中间的 homo1 , homo2 , homo3 , homo4 , homo5 并非那么直观,先说结论:

  • 这里的homoN(N=1,2,3,4,5) 表示一个基因有N个最佳匹配时的取值

  • N由mutiple参数确定,对应点阵图(dotplot)中的红点

  • multiple的取值一般取1即可,表示最近一次的WGD可能是一次二倍化事件,因此每个基因只会有一个最佳匹配。如果设置为2,可能是一次3倍化,每个基因由两个最佳匹配。当然实际情况可能会更加复杂,比如说异源四倍体,或者异源六倍体,或者没有多倍化只是小规模的基因复制(small-scale gene duplication) 等情况,也会影响multiple的设置。

  • homoN会在后面过滤共线性区块时用到,一般最近的WGD事件所产生的共线性区块会比较接近1,而古老的WGD产生的共线性区块则接近-1.

    接着,我们将根据源代码 blast_homo blast_position 来说明结算过程。

    首先需要用到blast_homo函数,用来输出每个基因对在不同最佳匹配情况下的取值(-1,0,1)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def blast_homo(self, blast, gff1, gff2, repeat_number):
    index = [group.sort_values(by=11, ascending=False)[:repeat_number].index.tolist()
    for name, group in blast.groupby([0])]
    blast = blast.loc[np.concatenate(
    np.array([k[:repeat_number] for k in index], dtype=object)), [0, 1]]
    blast = blast.assign(homo1=np.nan, homo2=np.nan,
    homo3=np.nan, homo4=np.nan, homo5=np.nan)
    for i in range(1, 6):
    bluenum = i+5
    redindex = np.concatenate(
    np.array([k[:i] for k in index], dtype=object))
    blueindex = np.concatenate(
    np.array([k[i:bluenum] for k in index], dtype=object))
    grayindex = np.concatenate(
    np.array([k[bluenum:repeat_number] for k in index], dtype=object))
    blast.loc[redindex, 'homo'+str(i)] = 1
    blast.loc[blueindex, 'homo'+str(i)] = 0
    blast.loc[grayindex, 'homo'+str(i)] = -1
    return blast

    for循环前的代码作用是提取每个基因BLAST后的前N个最佳结果。循环的作用基因对进行赋值,主要规则是基因对如果在点图中为 红色 ,赋值为1, 蓝色 赋值为0, 灰色 赋值为-1。

  • homo1 对应 redindex = 0:1, bluenum = 1:6, grayindex = 6:repeat_number

  • homo2 对应redindex = 0:2, bluenum = 2:7, grayindex = 7:repeat_number

  • homo5对应redindex=0:5, bluenum=5:10, grayindex = 10:repeat_number

    最终函数返回的就是每个基因对,在不同最佳匹配数下的赋值结果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
                    0          1  homo1  homo2  homo3  homo4  homo5
    185893 AT1G01010 AT4G01550 1.0 1.0 1.0 1.0 1.0
    185894 AT1G01010 AT1G02230 0.0 1.0 1.0 1.0 1.0
    185899 AT1G01010 AT4G35580 -1.0 0.0 0.0 0.0 0.0
    185900 AT1G01010 AT1G33060 -1.0 -1.0 0.0 0.0 0.0
    185901 AT1G01010 AT3G49530 -1.0 -1.0 -1.0 0.0 0.0
    185902 AT1G01010 AT5G24590 -1.0 -1.0 -1.0 -1.0 0.0
    250822 AT1G01030 AT1G13260 0.0 0.0 0.0 1.0 1.0
    250823 AT1G01030 AT1G68840 0.0 0.0 0.0 0.0 1.0
    250825 AT1G01030 AT1G25560 0.0 0.0 0.0 0.0 0.0
    250826 AT1G01030 AT3G25730 -1.0 0.0 0.0 0.0 0.0
    250824 AT1G01030 AT5G06250 -1.0 -1.0 0.0 0.0 0.0

    然后block_position函数, 会用 for k in block[1] 的循环提取每个共线性区块中每个基因对的homo值,然后用 df = pd.DataFrame(blk_homo) homo = df.mean().values 求均值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    def block_position(self, collinearity, blast, gff1, gff2, ks):
    data = []
    for block in collinearity:
    blk_homo, blk_ks = [], []
    if block[1][0][0] not in gff1.index or block[1][0][2] not in gff2.index:
    continue
    chr1, chr2 = gff1.loc[block[1][0][0],
    'chr'], gff2.loc[block[1][0][2], 'chr']
    array1, array2 = [float(i[1]) for i in block[1]], [
    float(i[3]) for i in block[1]]
    start1, end1 = array1[0], array1[-1]
    start2, end2 = array2[0], array2[-1]
    block1, block2 = [], []
    ## 提取block中对应基因对的homo值
    for k in block[1]:
    block1.append(int(float(k[1])))
    block2.append(int(float(k[3])))
    if k[0]+","+k[2] in ks.index:
    pair_ks = ks[str(k[0])+","+str(k[2])]
    blk_ks.append(pair_ks)
    elif k[2]+","+k[0] in ks.index:
    pair_ks = ks[str(k[2])+","+str(k[0])]
    blk_ks.append(pair_ks)
    else:
    blk_ks.append(-1)
    if k[0]+","+k[2] not in blast.index:
    continue
    blk_homo.append(
    blast.loc[k[0]+","+k[2], ['homo'+str(i) for i in range(1, 6)]].values.tolist())
    ks_arr = [k for k in blk_ks if k >= 0]
    if len(ks_arr) == 0:
    ks_median = -1
    ks_average = -1
    else:
    arr_ks = [k for k in blk_ks if k >= 0]
    ks_median = base.get_median(arr_ks)
    ks_average = sum(arr_ks)/len(arr_ks)
    # 对5列homo值求均值
    df = pd.DataFrame(blk_homo)
    homo = df.mean().values
    if len(homo) == 0:
    homo = [-1, -1, -1, -1, -1]
    blkks = '_'.join([str(k) for k in blk_ks])
    block1 = '_'.join([str(k) for k in block1])
    block2 = '_'.join([str(k) for k in block2])
    data.append([block[0], chr1, chr2, start1, end1, start2, end2, block[2], len(
    block[1]), ks_median, ks_average, homo[0], homo[1], homo[2], homo[3], homo[4], block1, block2, blkks])
    data = pd.DataFrame(data, columns=['id', 'chr1', 'chr2', 'start1', 'end1', 'start2', 'end2',
    'pvalue', 'length', 'ks_median', 'ks_average', 'homo1', 'homo2', 'homo3',
    'homo4', 'homo5', 'block1', 'block2', 'ks'])
    data['density1'] = data['length'] / \
    ((data['end1']-data['start1']).abs()+1)
    data['density2'] = data['length'] / \
    ((data['end2']-data['start2']).abs()+1)
    return data

    最终得到的homo1的homo5,是不同最佳匹配基因数下计算的值。如果共线性的点大部分 为红色 ,那么该值接近于1;如果共线性的点大部分 为蓝色 ,那么该值接近于0;如果共线性的点大部分 为灰色 ,那么该值接近于-1。也就是我们可以根据最初的点图中的颜色来确定将来筛选不同WGD事件所产生共线性区块。

    这也就是为什么homoN可以作为共线性片段的筛选标准。

    Ks可视化

    我们在上一篇 如何用WGDI进行共线性分析(一) 得到共线性分析结果和Ks值输出结果的整合表格文件后,我们就可以绘制Ks点阵图和Ks频率分布图对共线性区的Ks进行探索性分析,从而确定可能的Ks峰值,用于后续分析。

    Ks点阵图

    最初绘制的点图信息量很大,基本上涵盖了历史上发生的加倍事件所产生的共线性区。我们能大致的判断片段是否存在复制以及复制了多少次,至于这些片段是否来自于同一次加倍事件则不太好确定。借助于Ks信息 (Ks值可以反应一定尺度内的演化时间),我们就可以较为容易地根据点阵图上共线性区域的颜色来区分多倍化事件。

    首先,创建配置文件(这次是 -bk 模块,BlockKs)

    1
    wgdi -bk \? >> ath.conf

    然后,修改配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [blockks]
    lens1 = ath.len
    lens2 = ath.len
    genome1_name = A. thaliana
    genome2_name = A. thaliana
    blockinfo = ath_block_information.csv
    pvalue = 0.05
    tandem = true
    tandem_length = 200
    markersize = 1
    area = 0,3
    block_length = 5
    figsize = 8,8
    savefig = ath.ks.dotplot.pdf

    blockinfo 是前面共线性分析和Ks分析的整合结果,是绘图的基础。根据下面几个标准进行过滤

  • pvalue: 共线性区的显著性, 对应blockinfo中的pvalue列
  • tandem: 是否过滤由串联基因所形成的共线性区,即点阵图中对角线部分
  • tandem_length: 如果过滤,那么评估tandem的标准就是两个区块的物理距离
  • block_length: 一个共线区的最小基因对的数量,对应blockinfo中的length列
  • 最后运行wgdi,输出图片。

    1
    wgdi -bk ath.conf

    输出图片中的Ks的取值范围由参数area控制。图中的每个点都是各个共线性区内的基因对的Ks值。不难发现每个共线区内的Ks的颜色都差不多,意味着Ks值波动不大,基于这个现象,我们在判定Ks峰值的时候,采用 共线性区的中位数 就比其他方式要准确的多。在图中,我们大致能观察到2种颜色,绿色和蓝色,对应着两次比较近的加倍事件。

    Ks频率分布图

    除了用点阵图展示Ks外,我们还可以绘制Ks频率的分布情况。假如一个物种在历史上发生过多倍化,那么从那个时间点增加的基因经过相同时间的演化后,基因对之间的Ks值应该相差不多,即,归属于某个Ks区间的频率会明显高于其他区间。

    首先,创建配置文件(-kp, ksPeak)

    1
    2
    wgdi -kp ? >> ath.conf

    然后,修改配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [kspeaks]
    blockinfo = ath_block_information.csv
    pvalue = 0.05
    tandem = true
    block_length = 5
    ks_area = 0,10
    multiple = 1
    homo = -1,1
    fontsize = 9
    area = 0,3
    figsize = 10,6.18
    savefig = ath.ks_median.distri.pdf
    savefile = ath.ks_median.distri.csv

    这一步除了输出Ks峰图(savefig)外,还会输出一个根据输入文件( blockinfo )进行过滤后的输出文件(savefile)。 过滤标准 除了之前Ks点阵图提及的 tandem, pvalue, block_length 外,还多了三个参数, ks_area, multiple, homo.

  • pvalue: 共线性区的显著性, 对应blockinfo中的pvalue列,pvalue设置为0.05时会保留看着很不错的共线性片段,但是会导致古老片段的减少。

  • ks_area对应blockinfo中的ks列,该列记录了共线区所有基因对的ks值。ks_area=0,10 表示只保留ks值在0到10之间的基因对。

  • multiple和blockinfo中的homo1,homo2,homo3,home4,homo5…列有关。一般默认为1, 表示选择homo1列用于后续的过滤。如果改成multiple=2, 表示选择homo2

  • homo用于根据共线性中基因对的总体得分(取值范围为-1到1,值越高表明最佳匹配的基因对越多)对共线性区域进行过滤。当multiple=1, homo=-1,1时,表示根据homo1进行过滤,只保留取值范围在-1到1之间的共线性区。

    最后运行程序

    1
    wgdi -kp ath.conf

    从图中,我们能够更加直观的观察到2个peak,基本确定2个多倍化事件。

    既然得到了一个Ks的peak图,我们可以和另一款工具wgd的拟南芥分析结果进行对比。wgd的Ks值计算流程为,先进行所有蛋白之间的相互比对,根据基因之间的同源性进行聚类,然后构建系统发育树确定同源基因,最后调用PAML的codeml计算Ks,对应下图A的A.thaliana。如果存在参考基因组,那么根据共线性锚点(对应下图D)对Ks分布进行更新, 对应下图A的A.thaliana anchors。

    同样是拟南芥的分析,wgd的点图(上图D)信息比较杂乱,存在较多的噪声点,而WGDI的Ks点阵图能有效的反应出不同共线性区域的Ks特点。wgd的Ks分布图中的只能看到一个比较明显的峰,而WGDI的分析结果能明显的观察到两个。wgd在Ks上存在的问题很大一部分原因是它们是直接采用旁系同源基因计算ks,容易受到串联重复基因积累的影响。而wgd则是基于共线性区计算Ks,结果更加可靠,尤其是后续还可以通过不断的调整参数,来确保Ks的峰值正确判断,这也是为什么在绘制Ks频率分布图的同时还要生成一个过滤后的文件。

    题外话: wgd是我在2019年学习WGD相关分析找到的一个流程工具,那个时候虽然也看了文章里面关于软件的细节介绍,但是由于对比较基因组学这一领域并不熟悉,所以也不好评判结果的可靠性。最近在学习wgdi时,一直和开发者反复讨论软件的一些参数细节,这才知道这里面的很多细节。这也警醒我,不能追求软件数量上的多,只求用软件跑出自己想要的结果发表文章,失去了科研的严谨性。

    我们会在下一节介绍如何利用Ks点阵图和Ks频率分布图更可靠的拟合Ks峰值。

    ATAC-seq | 单细胞 ATAC-seq | 差异分析 ATAC-seq | 表观组 | ChIP-seq ATAC-seq | 表观组 | 单细胞 BigWig BioNano C/C++ C/C++ | 算法 ChIP-seq GitHub Hi-C JCVI MacOS NGS NGS | 遗传定位 PBMC Perl | 软件安装 QT | MSVC Seurat Seurat | 单细胞 | 数据挖掘 Seurat | 转录组 | 单细胞 Web开发 Zotero | 文献 | 坚果云 biocondutor conda samtools typora 个人博客 单细胞 可视化 可视化 | CIRCOS 可视化 | JCVI 可视化 | 单细胞 可视化 | 比较基因组学 基因家族 基因组 小技巧 小技巧 | SSH 序列比对 数据挖掘 数据结构 数据结构 | C/C++ 服务器 正则表达式 正则表达式 | 字符串处理 比较基因组学 水稻 | 转录组 水稻 | 转录组 | 遗传定位 注释 注释 | MAKER 注释 | 序列比对 注释 | 流程工具 注释 | 流程工具 | MAKER 注释 | 重复序列 流程工具 流程工具 | 基因家族 流程工具 | 服务器 深度学习 源码解读 源码解读 | hash 爬虫 环境变量 环境配置 | WSL 算法 系统发育 组装 组装 | Hi-C 组装 | 转录组 | 注释 编程语言 自然语言 | 深度学习 表观组 读书 转录组 转录组 | 单细胞 转录组 | 差异分析 | TCGA 转录组 | 序列比对 软件安装 遗传定位 重复序列 随笔
  •