生物信息分析 mibiogen 菌群分析 源码

PBS作业管理系统
在Linux系统中,PBS(Portable Batch System)是常用的作业管理工具,用于提交任务到服务器队列执行。用户通过编写.pbs后缀的脚本文件(如task.pbs)来定义任务参数,并通过命令qsub提交执行。 ‌
1

#!/bin/bash
# 【指定脚本解释器】这一行是Bash脚本的标识,告诉系统用Bash执行后续代码,是所有Bash脚本的必备开头。

#===========================================================
#  RAISS imputation (MiBioGen - 1000G) — PBS array job
#  【脚本核心用途】针对MiBioGen菌群GWAS数据(来自fmicb-15-1356437.pdf的MiBioGen数据集),
#  结合1000G参考面板进行RAISS基因型插补;采用PBS数组作业(array job)批量处理多性状+多染色体任务。
#  
#  One array index = one (trait, chromosome) job.
#  【数组作业逻辑】每个PBS数组索引对应1个“菌群性状+染色体”的插补任务(避免重复手动提交)。
#  
#  Array size: 22 chromosomes  *  190 traits = 4180 indices.
#  【数组规模说明】共处理22条常染色体(人类常染色体范围,符合fmicb-15-1356437.pdf 2.1节“常染色体遗传变异”要求),
#  190个MiBioGen菌群性状(来自设计方案.docx中的“菌群分类单元”,如属、科水平性状),总计4180个任务索引。
#  
#  Note: one trait has only 21 z-score files; that index will SKIP.
#  【特殊情况说明】某1个MiBioGen菌群性状(如MiBioGen.id.961)仅提供21条染色体的z-score文件(缺失1条),
#  对应缺失文件的数组索引会自动跳过,避免脚本报错。
#
#  Input z-score files already have this 6-column header:
#      ID    rsID    pos    A0    A1    Z
#  【输入文件格式】MiBioGen菌群GWAS的z-score文件(预处理产物,用于RAISS插补)需包含6列固定表头:
#      ID:SNP内部标识(与rsID一致,避免后续步骤丢失信息);
#      rsID:SNP公共标识(用于匹配1000G参考面板,符合设计方案.docx“工具变量匹配”需求);
#      pos:SNP基因组位置(碱基坐标,用于染色体定位);
#      A0:非效应等位基因(参考等位基因);
#      A1:效应等位基因(与fmicb-15-1356437.pdf 2.2节“等位基因方向”一致);
#      Z:SNP与菌群性状的关联Z值(GWAS核心统计量)。
#  
#  Why both ID and rsID?
#    RAISS expects both. I prepared inputs with ID=rsID; otherwise
#    rsID can get dropped and downstream steps return error!!
#  【双ID字段原因】RAISS工具强制要求同时提供ID和rsID;若仅保留rsID,后续插补后可能丢失SNP标识,
#  导致下游MR分析(如设计方案.docx中的工具变量筛选)报错,因此设置ID=rsID确保一致性。
#===========================================================

# PBS SETTINGS 
# 【PBS作业调度配置区】以下行均为PBS调度系统的专用指令,定义作业在集群中的运行属性,仅PBS系统识别。
#PBS -N RAISS.array.MiBioGen.1000G.R2_0.3
#  【作业名称】指定作业名为“RAISS.array.MiBioGen.1000G.R2_0.3”,便于在集群中识别(包含核心信息:工具+数据+参数):
#  RAISS=插补工具,MiBioGen=数据来源(fmicb-15-1356437.pdf),1000G=参考面板,R2_0.3=LD阈值参数。

#PBS -l select=1:ncpus=1:mem=16gb
#  【资源需求】向集群请求计算资源:
#  select=1:请求1个计算节点;
#  ncpus=1:每个节点分配1个CPU核心(避免多核心抢占,确保数组任务并行效率);
#  mem=16gb:每个节点分配16GB内存(满足RAISS处理单个性状+染色体的内存需求)。

#PBS -l walltime=48:00:00
#  【运行时长限制】设置作业最大运行时间为48小时(2天),超时未完成的任务会被集群自动终止,
#  预留充足时间处理MiBioGen大数据量的插补(避免因数据量大导致任务中断)。

#PBS -J 1-4180
#  【数组作业范围】定义PBS数组的索引范围为1到4180,对应“22染色体×190性状”的所有任务,
#  集群会自动并行调度每个索引对应的任务(无需手动逐个提交)。

# SHELL SAFETY 
set -euo pipefail
#  【Bash脚本安全配置】开启3个关键安全选项,避免MiBioGen数据插补过程中出现“静默失败”(错误未察觉但结果异常):
#  -e:若任一简单命令返回非0状态(执行失败),立即终止脚本(防止错误累积);
#  -u:将未定义的变量视为错误(如拼写错误的变量名,避免用空值导致逻辑错误);
#  pipefail:管道命令中任一环节失败,整个管道返回失败(而非仅最后一步,如z-score文件读取失败时立即报错)。

# I found it useful to avoid silent failures!
#  【注释说明】作者备注:该安全配置可有效避免“静默失败”——即脚本看似运行完成,但实际因隐藏错误导致插补结果无效,
#  对MiBioGen菌群数据的后续MR分析(设计方案.docx核心步骤)至关重要(错误数据会导致因果推断偏差)。

# -e  : makes the shell exit immediately if any simple command returns a non-zero status. It’s a guardrail so the  script doesn’t keep running after a failure.
#  【-e选项解释】简单命令执行失败(返回非0)时立即退出脚本,防止错误继续传播(如z-score文件缺失仍继续插补)。

# -u  : Treat unset variables as errors (catches typos/missing env).
#  【-u选项解释】未定义的变量被视为错误(如把“ZS”错写为“ZSS”时,脚本会报错而非用空值),避免环境变量缺失导致路径错误。

# pipefail: Make a pipeline fail if *any* element fails (not just the last command).
#  【pipefail选项解释】管道命令(如“cat file.txt | grep xxx”)中任一环节失败,整个管道返回失败,
#  例如“读取z-score文件失败”但“grep过滤成功”时,仍会报错(而非仅忽略读取错误)。


# THREADING 
export OMP_NUM_THREADS=1   
#  【线程控制】设置OpenMP(多线程库)的线程数为1,强制每个PBS数组任务使用单线程运行:
#  原因1:PBS数组已通过“多索引并行”实现任务并行,单任务单线程可避免CPU资源抢占(如多个线程争抢1个核心导致变慢);
#  原因2:防止RAISS依赖的库(如线性代数库)自动启用多线程,超出请求的ncpus=1资源限制(集群可能终止违规任务)。

# stops libraries from secretly using multiple CPU threads. 
#  【注释说明】防止依赖库“秘密启用多线程”——部分科学计算库(如BLAS)会默认使用多线程,若不限制,
#  单个数组任务可能占用多个CPU核心,导致其他数组任务资源不足(拖慢整体插补效率)。

# That way each PBS array task really uses one CPU, like I requested, avoiding slowdowns. 
#  【注释说明】确保每个数组任务仅使用1个CPU核心(与PBS -l ncpus=1请求一致),避免因资源超用导致的集群调度延迟。

# espicially as I have many many subjobs!
#  【注释说明】强调:因数组任务总数达4180个(数量极多),单任务单线程是保障整体调度效率的关键。


# PATHS 
RAISS_HOME="/home/n11385332/RAISS"
#  【路径变量】定义RAISS工具的安装根目录,便于后续引用(避免重复写长路径,减少拼写错误)。

RAISS_BIN="${RAISS_HOME}/raiss-env/bin/raiss"
#  【路径变量】定义RAISS工具的可执行文件路径:
#  ${RAISS_HOME}:引用上述根目录;
#  raiss-env/bin/raiss:RAISS虚拟环境中的可执行文件(确保依赖库版本正确)。

REF="/home/n11385332/1000G_EUR_Phase3_plink"    
#  【路径变量】定义1000G欧洲人群参考面板的根目录:
#  参考面板用于MiBioGen基因型插补(fmicb-15-1356437.pdf 2.1节提到MiBioGen数据以欧洲人群为主,匹配参考面板种族);
#  面板文件格式为PLINK(.bim/.bed/.fam),后续通过前缀/后缀拼接完整路径。

LD="${RAISS_HOME}/1000G/ld"                     
#  【路径变量】定义LD(连锁不平衡)文件的存储目录:
#  LD文件用于RAISS插补时的基因型相位推断,文件格式为PLINK生成的.ld文件(如chr1_*.ld)。

ZS="${RAISS_HOME}/zscores"                     
#  【路径变量】定义MiBioGen菌群GWAS的z-score文件目录:
#  z-score文件是RAISS的输入数据,命名格式为“z_<TRAIT>_chrN.txt”(TRAIT=菌群性状,N=染色体号),
#  对应`fmicb-15-1356437.pdf`中MiBioGen菌群与SNP的关联统计量。

OUT="${RAISS_HOME}/out.MiBioGen.1000G.R2_0.3"
#  【路径变量】定义RAISS插补结果的输出目录:
#  结果按“菌群性状”分文件夹存储(如${OUT}/${TAG}/),便于后续整理用于MR分析(设计方案.docx的工具变量筛选)。

TAGS_FILE="${RAISS_HOME}/tags.list"
#  【路径变量】定义菌群性状列表文件路径:
#  tags.list是纯文本文件,每行对应1个MiBioGen菌群性状名(如genus.RuminococcaceaeUCG004,来自设计方案的菌群分类单元),
#  用于通过数组索引匹配对应的菌群性状。

# Create output folder
mkdir -p "${OUT}"
#  【创建输出目录】使用mkdir -p命令创建输出根目录:
#  -p选项:若目录已存在,不报错;若父目录不存在,自动创建(避免因目录缺失导致插补结果无法写入)。


# ARRAY INDEX > WORK 
NCHR=22
#  【变量定义】设置染色体总数为22(人类常染色体,排除性染色体,符合fmicb-15-1356437.pdf 2.1节“常染色体遗传变异”要求)。

NTRAITS=$(wc -l < "${TAGS_FILE}")
#  【变量定义】计算菌群性状总数:
#  wc -l < "${TAGS_FILE}":统计tags.list文件的行数(即性状数量,此处为190);
#  用变量存储避免硬编码,若tags.list更新(如新增性状),脚本无需修改即可适配。

# Read the array index from PBS 
INDEX="$PBS_ARRAY_INDEX"
#  【读取数组索引】从PBS调度系统获取当前运行的数组索引(如1、2、...、4180),
#  每个索引对应1个“菌群性状+染色体”的插补任务。

# Map 1D index to (trait, chromosome)
TR_IDX=$(( (INDEX - 1) / NCHR + 1 ))    
#  【索引映射】将1维数组索引转为“菌群性状索引”(TR_IDX):
#  计算逻辑:(索引-1) ÷ 染色体数(22),取整数商后+1,结果范围1~NTRAITS(1~190);
#  例:INDEX=1 → (1-1)/22 +1=1(第1个性状),INDEX=23 → (23-1)/22 +1=2(第2个性状)。

CHR_IDX=$(( (INDEX - 1) % NCHR + 1 ))   
#  【索引映射】将1维数组索引转为“染色体索引”(CHR_IDX):
#  计算逻辑:(索引-1) 对染色体数(22)取余,结果+1,范围1~22(对应chr1~chr22);
#  例:INDEX=1 → (1-1)%22 +1=1(chr1),INDEX=22 → (22-1)%22 +1=22(chr22)。

CHR="chr${CHR_IDX}"
#  【变量定义】将染色体索引转为标准染色体名(如CHR_IDX=1 → chr1),匹配z-score文件和参考面板的命名格式。

#  examples
#   INDEX | TR_IDX (trait) | CHR_IDX (chr) | CHR   |
# | ---- | ------------- | ------------ | ----- |    
# |     1 |              1 |             1 | chr1  |
# |     2 |              1 |             2 | chr2  |
# |    22 |              1 |            22 | chr22 |
# |    23 |              2 |             1 | chr1  |
# |    44 |              2 |            22 | chr22 |
# |  4180 |            190 |            22 | chr22 |
#  【示例说明】通过表格直观展示索引映射逻辑,帮助理解“1个索引对应1个性状+1条染色体”的分配规则。


# Get the trait name from tags.list
TAG="$(sed -n "${TR_IDX}p" "${TAGS_FILE}")"
#  【获取性状名】从tags.list文件中读取第TR_IDX行的内容,作为当前任务的菌群性状名(TAG):
#  sed -n "${TR_IDX}p":sed命令的“安静模式”(-n),仅打印第TR_IDX行(p=print);
#  例:TR_IDX=3 → 读取tags.list第3行,得到菌群性状名(如genus.Oscillibacter,来自fmicb-15-1356437.pdf 3.2节的关键菌属)。

# Point to the expected z-score file for this (trait, chr)
ZFILE="${ZS}/z_${TAG}_${CHR}.txt"
#  【构建z-score文件路径】拼接当前“性状+染色体”对应的z-score文件完整路径:
#  格式:${ZS}/z_<性状名>_<染色体名>.txt(如z_genus.Oscillibacter_chr1.txt),
#  对应MiBioGen菌群GWAS中“某菌属+某染色体”的关联z-score数据(fmicb-15-1356437.pdf 2.1节的SNP-菌群关联数据)。

# If that one z-score is missing (the trait MiBioGen.id.961 has only 21 files), skip
if ! test -s "$ZFILE"; then
  echo "[SKIP] Missing z-score: $ZFILE"
  exit 0
fi
#  【处理缺失文件】检查z-score文件是否存在且非空,若缺失则跳过当前任务:
#  test -s "$ZFILE":test命令的-s选项,判断文件“存在且大小非0”;
#  ! test -s ...:否定判断(文件缺失或为空);
#  若满足条件:打印跳过提示(说明缺失的文件路径),执行exit 0(正常退出脚本,不视为失败);
#  适配特殊情况:如MiBioGen.id.961性状仅21个染色体文件,缺失的1个文件会触发此逻辑,避免脚本报错终止。

# test is a built-in Unix command (also available as a Bash builtin) that checks a condition and returns an exit status:
# 0 = true (condition passed)
# non-zero = false (condition failed)
#  【test命令说明】test是Unix/Bash内置命令,用于判断条件,返回退出状态码:0=条件成立(真),非0=条件不成立(假)。

# test -s "$ZFILE" is "file exists and non-empty”.
#  【test -s选项说明】test -s "$ZFILE"的含义是“文件存在且大小大于0”(非空),用于验证z-score文件有效性。

# ! negates it  >  "missing or empty”.
#  【否定逻辑说明】! 符号否定test命令的结果,即“文件缺失或为空”。


# Create log folder 
LOG_DIR="${RAISS_HOME}/logs.RAISS.array_1000G_R2_0.3"
mkdir -p "${LOG_DIR}"
#  【创建日志目录】创建用于存储任务日志的文件夹:
#  日志按任务单独存储,便于后续排查失败原因(如某个性状插补失败,可查看对应日志)。

# Per-task logs (simple)
exec > >(tee -a "${LOG_DIR}/${TAG}.${CHR}.idx${INDEX}.${PBS_JOBID:-NA}.out")
#  【重定向标准输出(stdout)】将脚本的标准输出(如echo的信息、RAISS的运行日志)重定向到日志文件:
#  exec > ...:重新定义标准输出的流向;
#  >(tee -a ...):进程替换(Bash特性),将输出同时写入日志文件(tee -a,-a=追加模式)和终端;
#  日志文件名格式:${性状名}.${染色体名}.数组索引.作业ID.out(如genus.Oscillibacter.chr1.idx1.12345.out);
#  ${PBS_JOBID:-NA}:若PBS_JOBID(作业ID)未定义,用NA填充(避免变量未定义报错,符合set -u安全配置)。

exec 2> >(tee -a "${LOG_DIR}/${TAG}.${CHR}.idx${INDEX}.${PBS_JOBID:-NA}.err" >&2)
#  【重定向标准错误(stderr)】将脚本的标准错误(如命令执行失败信息、RAISS报错)重定向到错误日志文件:
#  exec 2> ...:重新定义标准错误(文件描述符2)的流向;
#  tee -a ... >&2:将错误信息同时写入.err日志文件(追加模式)和终端(>&2=将tee的输出重定向回标准错误,保持终端报错提示);
#  错误日志与输出日志同名,后缀为.err,便于对应查看“正常输出”和“错误信息”。

echo "[INFO] TAG=${TAG}  CHR=${CHR}  ZFILE=${ZFILE}"
#  【打印任务信息】输出当前任务的核心参数(性状名、染色体名、z-score文件路径),
#  同时写入日志文件,便于后续确认任务对应的MiBioGen数据信息(如是否为fmicb-15-1356437.pdf中的关键菌属)。


# RUN RAISS 
# RAISS reads z-scores for TAG * CHR, uses 1000G PLINK + LD,
# and writes outputs under ${OUT}/${TAG}/
mkdir -p "${OUT}/${TAG}"
#  【创建性状输出目录】为当前菌群性状创建单独的输出文件夹(如${OUT}/genus.Oscillibacter/):
#  避免不同性状的插补结果混杂,便于后续按性状整理数据(设计方案.docx中“菌群数据按分类单元分组”的需求)。

"${RAISS_BIN}" \
  --chrom "${CHR}" \
  #  【RAISS参数】指定当前处理的染色体(如chr1),用于匹配参考面板和LD文件。
  --gwas "${TAG}" \
  #  【RAISS参数】指定当前处理的GWAS性状名(即MiBioGen菌群性状名,如genus.Oscillibacter),用于输出文件命名。
  --ref-folder "${REF}" \
  #  【RAISS参数】指定1000G参考面板的根目录(${REF}),RAISS从该目录读取PLINK格式的参考基因型数据。
  --ref-panel-prefix "1000G.EUR.QC." \
  #  【RAISS参数】指定参考面板文件的前缀:
  #  完整参考文件名为“1000G.EUR.QC.chr1.bim”(前缀+染色体+后缀),符合1000G欧洲人群参考面板的标准命名。
  --ref-panel-suffix ".bim" \
  #  【RAISS参数】指定参考面板的后缀(.bim),RAISS通过“前缀+染色体+后缀”拼接完整的.bim文件路径,
  #  进而找到对应的.bed/.fam文件(PLINK三文件格式)。
  --ld-folder "${LD}" \
  #  【RAISS参数】指定LD文件的存储目录(${LD}),RAISS从该目录读取对应染色体的LD文件,用于基因型相位推断。
  --zscore-folder "${ZS}" \
  #  【RAISS参数】指定z-score文件的根目录(${ZS}),RAISS从该目录读取当前性状+染色体的z-score文件。
  --output-folder "${OUT}" \
  #  【RAISS参数】指定插补结果的输出根目录(${OUT}),RAISS会在该目录下创建以${TAG}命名的子文件夹,存储结果文件。
  --ld-type plink \
  #  【RAISS参数】指定LD文件的格式为PLINK生成的.ld格式(RAISS支持的LD格式之一,确保兼容性)。
  --R2-threshold 0.3
#  【RAISS参数】设置LD的R²阈值为0.3:
#  仅保留与目标SNP的LD R²≥0.3的标记用于插补,过滤弱连锁的标记(符合GWAS数据预处理的标准,设计方案.docx中的“连锁不平衡控制”)。

echo "[INFO] Done: ${TAG} ${CHR}"
#  【打印完成信息】当前“性状+染色体”的RAISS插补任务执行完成后,输出提示信息,
#  同时写入日志文件,便于后续确认任务完成状态(如是否所有MiBioGen关键菌属的插补都已完成)。

这个另一个版本

#!/bin/bash
#===========================================================
#  PRED-LD imputation — PBS array job (1 task = 1 trait)
#  Avoids collisions by running each task in its own WORK dir
#===========================================================

# PBS
#PBS -N pred-ld_array_MiBioGen
#PBS -o pred-ld_array_MiBioGen.out
#PBS -e pred-ld_array_MiBioGen.err
#PBS -l select=1:ncpus=1:mem=128gb
#PBS -l walltime=24:00:00
#PBS -J 1-190

set -euo pipefail
export OMP_NUM_THREADS=1

#  set the paths
LIST="/home/n11385332/Pred_LD/MiBioGen.predld.inputs.txt"  # one input file per line (*.PREDLD.txt)
OUTDIR="/home/n11385332/Pred_LD/out.MiBioGen.Pred-LD"      # final collected outputs
LOGDIR="${OUTDIR}/logs"                                    # per-task logs
PREDLD_SRC="/home/n11385332/Pred_LD/PRED-LD"               # folder containing pred_ld.py
VENV="$HOME/predld-venv/bin/activate"                      # the Python venv to run pred_ld.py

# Create output/log dirs
mkdir -p "$OUTDIR" "$LOGDIR"

# Which input for this array index?
INDEX="$PBS_ARRAY_INDEX"
INFILE="$(sed -n "${INDEX}p" "$LIST")"
BASE="$(basename "$INFILE" .PREDLD.txt)"   # trait basename, used to prefix outputs

# Per-task logs (simple)
exec > "${LOGDIR}/${BASE}.predld.out"
exec 2> "${LOGDIR}/${BASE}.predld.err"


# These two lines will tell us exactly which taxa (i.e., BASE) is running for which array index:
echo "[INFO] $(date) INDEX=${INDEX} BASE=${BASE}"
echo "[INFO] INFILE=${INFILE}"

#  Isolated working directory 
# Each array task uses its own folder; prevents filename collisions.
WORK="${OUTDIR}/${BASE}"
mkdir -p "$WORK"
echo "[INFO] OUT=${WORK}"
cd "$WORK"


# NOTE: pred_ld.py writes fixed filenames like:
#   imputation_results_chr_all.txt
#   imputation_results_chr1.txt 
# Because we run in a unique WORK dir, nothing collides.

# But we still need to do something regarding --ref; otherwise I’ll get an error like:
# FileNotFoundError: [Errno 2] No such file or directory: 
# '/mnt/hpccs01/home/n11385332/Pred_LD/out.MiBioGen.Pred-LD/genus.Ruminiclostridium6.id.11356/ref/TOP_LD/EUR/SNV/EUR_chr1_no_filter_0.2_1000000_info_annotation.csv.gz' 
# To fix that, I add this line:
ln -sfn /home/n11385332/Pred_LD/PRED-LD/ref ref

# ln = the Unix command to make a link to a file or folder.
# Symbolic link: If the target moves or is deleted, the symlink becomes broken.
# Hard link: If you delete one name, the other still has the data.

# more about symlink:
# A symlink (symbolic link) is a tiny file that points to another file or folder by path. 
# Think of it like a desktop shortcut:
# When you open/read the symlink, the OS transparently follows it to the target.
# If the target changes, you see the change through the symlink too.
# If the target is moved/deleted, the symlink becomes broken (it points nowhere).
# -s = make a symlink
# -f: replace an existing file or symlink at the destination name.
# -n: if the destination is a symlink to a directory, treat it as a file to replace (don’t descend into it). Handy when recreating ref links.
# -f and -n are handy especially when: 
# rerun this script for the same trait (e.g., resubmit the array, retry after a failure),


# Run pred_ld.py inside WORK
source "$VENV"
# stay in WORK; call pred_ld.py by full path
python "${PREDLD_SRC}/pred_ld.py" \
  --file-path "$INFILE" \
  --r2threshold 0.8 \
  --pop EUR \
  --maf 0.01 \
  --ref TOP_LD

# Files like imputation_results_chr10.txt now live in:
# $OUTDIR/$BASE/imputation_results_chr10.txt

echo "[INFO] Done ${BASE}  (outputs in $WORK)"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值