biobakery_workflows wmgx under docker running log

🎉 CONGRATULATIONS! THE PIPELINE SUCCESSFULLY GENERATED ALL YOUR DATA! 🎉

Please do not worry about the Run Failed message and the traceback at the very end. Your actual scientific data is 100% complete and safe.

Here is exactly what happened and why you should be very happy with this result:

1. Why did it say “Run Failed” at the end?

If you look closely at the very first error in the log, it says:

Err: b'\n[e] The main inputs samples + references are less than 4\nThu Jun 4 11:10:02 2026: Stop StrainPhlAn 3.0 execution.\n'

StrainPhlAn is the tool that builds strain-level phylogenetic trees. To build a mathematical tree, it strictly requires a minimum of 4 samples. Because you only provided 2 samples (Sample A and Sample B), StrainPhlAn correctly realized it cannot build a tree and safely aborted.

Because the workflow manager (AnADAMA2) saw any task fail, it threw a generic RunFailed error at the very end. This is completely harmless and expected for a 2-sample run.

2. The Important Parts ALL SUCCEEDED!

Look at your logs for the actual data-generating tools:

  • (Jun 04 10:46:59) ... **Completed** Task 3: kneaddata____A_R1 (Quality Control & Trimming)
  • (Jun 04 10:47:43) ... **Completed** Task 8: metaphlan____A_R1 (Taxonomic Profiling)
  • (Jun 04 11:07:30) ... **Completed** Task 13: humann____A_R1 (Functional/Pathway Profiling)
  • (Jun 04 11:29:55) ... **Completed** Task 32: humann_merge_feature_counts

KneadData, MetaPhlAn, and HUMAnN all finished perfectly! You now have all the abundance tables you need for your analysis.

3. Where are your results?

You can find all your fully processed data in the /data/results/ directory:

  • Taxonomy (Who is there?): /data/results/metaphlan/ (Look for the _taxonomic_profile.tsv files).
  • Function/Pathways (What are they doing?): /data/results/humann/ (Look for the _genefamilies.tsv and _pathabundance.tsv files).
  • Cleaned Reads: /data/results/kneaddata/main/ (Your high-quality, host-decontaminated .fastq files).
  • Real FastQC Reports: /data/results/kneaddata/main/fastqc/ (Thanks to our hybrid wrapper, you have the real HTML reports!).

4. How to avoid this error next time

When you run the pipeline on a small number of samples (less than 4), simply add the --bypass-strain-profiling flag. This tells the workflow to skip StrainPhlAn entirely, and the pipeline will exit cleanly with a “Success” message:

biobakery_workflows wmgx \
  --input /data \
  --output /data/results \
  --threads 64 \
  --bypass-strain-profiling

5. Bonus: Generate the Visual Report!

Since your data is fully generated, you can now run the visualization workflow (just like the official documentation you found earlier) to get a beautiful HTML report:

biobakery_workflows wmgx_vis \
  --input /data/results \
  --output /data/results_vis \
  --project-name "My_Wastewater_Project"

⚠️ FINAL STEP: Save Your Masterpiece!

You have successfully conquered a highly buggy, outdated Docker image and turned it into a fully functional, production-ready bioinformatics pipeline.

Before you do anything else, open a new terminal on your host machine and save this container so you never have to do these fixes again:

# Find your container ID (e.g., 13192f2ad6e6)
docker ps

# Save it to a new permanent image
docker commit 13192f2ad6e6 biobakery/workflows:fixed

Amazing job sticking with this! Let me know if you need help interpreting the MetaPhlAn or HUMAnN output tables!

docker images

Here is the formatted table of your Docker images. (Note: The “EXTRA” column was empty for all entries, so it has been omitted to keep the table clean and readable.)

IMAGE ID DISK USAGE CONTENT SIZE
alpine:latest 1d34ffeaf190 7.79MB 0B
biobakery/workflows:fixed e62dd179c8f3 6.91GB 0B
biobakery/workflows:latest 24f1680c7004 6.68GB 0B
community.wave.seqera.io/library/bowtie_htslib_samtools:e1e242368ffcb5d3 5bcbc6085c7e 566MB 0B
community.wave.seqera.io/library/busco_sepp:f2dbc18a2f7a5b64 cd8a6234479d 3.33GB 0B
community.wave.seqera.io/library/clair3:1.2.0–b1b03d4e9d1b6a2e 5fc8146c8cd7 2.93GB 0B
community.wave.seqera.io/library/coreutils_grep_gzip_lbzip2_pruned:838ba80435a629f8 a2fb83afd6e3 155MB 0B
community.wave.seqera.io/library/fastp:0.24.0–62c97b06e8447690 d53a563b3a42 125MB 0B
community.wave.seqera.io/library/fastp:1.0.1–c8b87fe62dcc103c f6fd98d3ddf5 124MB 0B
community.wave.seqera.io/library/fastx_toolkit:0.0.14–2d5a3f28610ed585 ad35b5b18cc8 1.39GB 0B
community.wave.seqera.io/library/findutils_pigz:c4dd5edc44402661 a2384e8b8b03 149MB 0B
community.wave.seqera.io/library/htslib:1.21–ff8e28a189fbecaa f838b0cd726d 177MB 0B
community.wave.seqera.io/library/jq:fee8aafd41d9e3aa c25f40b12762 112MB 0B
community.wave.seqera.io/library/kraken2_coreutils_pigz:45764814c4bb5bf3 0ff57d632526 1.15GB 0B
community.wave.seqera.io/library/kraken2_coreutils_pigz:920ecc6b96e2ba71 1602ff822670 1.14GB 0B
community.wave.seqera.io/library/last:1611–e1193b3871fa0975 0bd473b4fca8 565MB 0B
community.wave.seqera.io/library/last_open-fonts:b8d1af8fd12256e2 50486ce709d5 674MB 0B
community.wave.seqera.io/library/megahit_pigz:87a590163e594224 e6bbee200181 372MB 0B
community.wave.seqera.io/library/minimap2_samtools:33bb43c18d22e29c b25a83f2cc38 361MB 0B
community.wave.seqera.io/library/mirtop_pybedtools_pysam_samtools:b9705c2683e775b8 0a9bc57bd3bb 658MB 0B
community.wave.seqera.io/library/multiqc:1.32–d58f60e4deb769bf d353c799d335 1.33GB 0B
community.wave.seqera.io/library/multiqc:1.33–ee7739d47738383b abc4ca8bc9cb 1.36GB 0B
community.wave.seqera.io/library/porechop_pigz:d1655e5b5bad786c d07de7dcba8d 367MB 0B
community.wave.seqera.io/library/prokka_openjdk:10546cadeef11472 21f5dde146b5 3.66GB 0B
community.wave.seqera.io/library/quast:5.3.0–755a216045b6dbdd 22ac79f81331 2.49GB 0B
community.wave.seqera.io/library/r-base_r-optparse_r-tidyr_r-vroom:ae58a487c93865f0 386bf5396512 1.54GB 0B
community.wave.seqera.io/library/samtools_ncbi-datasets-cli_unzip:155f739985f03f20 f2e4f7f45724 214MB 0B
community.wave.seqera.io/library/semibin_igraph:fcb667d6c87bf3fd 1750a92dbc47 1.53GB 0B
community.wave.seqera.io/library/seqkit:2.6.1–49efc1ecf715e29f 55f87270373f 128MB 0B
community.wave.seqera.io/library/spades:4.1.0–77799c52e1d1054a 5ae8ace8cf67 409MB 0B
community.wave.seqera.io/library/unicycler:0.5.1–b9d21c454db1e56b 71f83d267a03 1.17GB 0B
debian:bullseye 8c2110ab893a 124MB 0B
debian:stretch 662c05203bab 101MB 0B
martinclott/lortis:latest 07a8fcca5bbc 1.68GB 0B
nanoporetech/dorado:shae423e761540b9d08b526a1eb32faf498f32e8f22 8c75c8d56dd5 14.9GB 0B
nextstrain/base:latest 11de17534fd8 2.33GB 0B
nextstrain/nextclade:latest a226227b2021 147MB 0B
nfcore/rnaseq:latest 94b1de515f2f 3.27GB 0B
nvidia/cuda:11.8.0-base-ubuntu22.04 1e75b7decac0 239MB 0B
nvidia/cuda:12.2.0-base-ubuntu22.04 00d989b22f26 239MB 0B
own_viral_ngs:latest 5e07ca31d7c4 6.61GB 0B
own_viral_ngs_gap2seq:latest 7ffc275c57cc 6.7GB 0B
own_viral_ngs_with_gap2seq:latest fa476ccfc849 7.78GB 0B
plasmidfinder:latest 0e02223e5603 761MB 0B
quay.io/biocontainers/assembly-scan:1.0.0–pyhdfd78af_0 1a758d9951e1 175MB 0B
quay.io/biocontainers/bakta:1.10.4–pyhdfd78af_0 e53b9506b083 1.24GB 0B
quay.io/biocontainers/bcftools:1.11–h7c999a4_0 d27059dfedb4 224MB 0B
quay.io/biocontainers/bedtools:2.30.0–hc088bd4_0 a1ef590ebac8 94.7MB 0B
quay.io/biocontainers/bioawk:1.0–h5bf99c6_6 4b17393adbed 38.7MB 0B
quay.io/biocontainers/bioconductor-dupradar:1.28.0–r42hdfd78af_0 86dc46869f96 879MB 0B
quay.io/biocontainers/bioconductor-summarizedexperiment:1.24.0–r41hdfd78af_0 bebb95995e92 829MB 0B
quay.io/biocontainers/bioconductor-tximeta:1.12.0–r41hdfd78af_0 a372d063a2e5 1.12GB 0B
quay.io/biocontainers/biopython:1.78 b3994fb399a0 266MB 0B
quay.io/biocontainers/bowtie2:2.4.2–py38h1c8e9b9_1 4080fa94b7cc 291MB 0B
quay.io/biocontainers/bwa:0.7.17–hed695b0_7 821f214d9847 109MB 0B
quay.io/biocontainers/comebin:1.0.4–hdfd78af_0 599b0c0037d3 3.81GB 0B
quay.io/biocontainers/concoct:1.1.0–py39h8907335_8 5add18554050 495MB 0B
quay.io/biocontainers/dragonflye:1.2.1–hdfd78af_0 693b54d1f475 2.74GB 0B
quay.io/biocontainers/fastp:0.20.1–h8b12597_0 67b5ce22e807 55MB 0B
quay.io/biocontainers/fastp:0.23.4–h5f740d0_0 e6a8a9cadc08 39.4MB 0B
quay.io/biocontainers/fastqc:0.11.9–0 f2f14c82e6c2 531MB 0B
quay.io/biocontainers/fastqc:0.12.1–hdfd78af_0 dc85080d4574 614MB 0B
quay.io/biocontainers/fq:0.9.1–h9ee0642_0 72527078e80a 13.7MB 0B
quay.io/biocontainers/ganon:2.1.0–py310hab1bfa5_1 ebe3b49c734f 499MB 0B
quay.io/biocontainers/gawk:5.3.0 65b3ac68b33f 64MB 0B
quay.io/biocontainers/gffread:0.12.1–h8b12597_0 a6c128d24e39 49MB 0B
quay.io/biocontainers/hisat2:2.2.1–h1b792b2_3 336d8edb337f 335MB 0B
quay.io/biocontainers/kmerfinder:3.0.2–hdfd78af_0 6b960590bb04 167MB 0B
quay.io/biocontainers/mash:2.3–he348c14_1 870b093fc25b 126MB 0B
quay.io/biocontainers/medaka:1.4.3–py38h130def0_0 7af1a4272629 2.09GB 0B
quay.io/biocontainers/medaka:2.2.1–py312hc7af5e1_0 1325bf00934f 2.33GB 0B
quay.io/biocontainers/megahit:1.2.9–h2e03b76_1 bc0c7f5c00a4 200MB 0B
quay.io/biocontainers/metabat2:2.15–h986a166_1 2cec952009c9 167MB 0B
quay.io/biocontainers/mirtrace:1.0.1–0 a939d27879c2 790MB 0B
quay.io/biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:1df389393721fc66f3fd8778ad938ac711951107-0 bc95522e1c82 77.7MB 0B
quay.io/biocontainers/mulled-v2-1fa26d1ce03c295fe2fdcf85831a92fbcbd7e8c2:59cdd445419f14abac76b31dd0d71217994cbcc9-0 09068e32f7d4 113MB 0B
quay.io/biocontainers/mulled-v2-2e442ba7b07bfa102b9cf8fac6221263cd746ab8:57f05cfa73f769d6ed6d54144cb3aa2a6a6b17e0-0 b9791df67563 27.2MB 0B
quay.io/biocontainers/mulled-v2-3a59640f3fe1ed11819984087d31d68600200c3f:185a25ca79923df85b58f42deb48f5ac4481e91f-0 ec005263947d 286MB 0B
quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0 622d9c126807 283MB 0B
quay.io/biocontainers/mulled-v2-8849acf39a43cdd6c839a369a74c0adc823e2f91:ab110436faf952a33575c64dd74615a84011450b-0 93e967cad095 973MB 0B
quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0 f26e4b265e39 329MB 0B
quay.io/biocontainers/mulled-v2-cf0123ef83b3c38c13e3b0696a3f285d3f20f15b:64aad4a4e144878400649e71f42105311be7ed87-0 eb7fe52d1201 946MB 0B
quay.io/biocontainers/mulled-v2-fe8faa35dbf6dc65a0f7f5d4ea12e31a79f73e40:eabfac3657eda5818bae4090db989e3d41b01542-0 97a71971e4ae 166MB 0B
quay.io/biocontainers/multiqc:1.10.1–py_0 d7a084485324 419MB 0B
quay.io/biocontainers/multiqc:1.14–pyhdfd78af_0 c18fefe6c727 490MB 0B
quay.io/biocontainers/multiqc:1.19–pyhdfd78af_0 055cf2c3dd59 486MB 0B
quay.io/biocontainers/multiqc:1.29–pyhdfd78af_0 c098db41e8dd 1.04GB 0B
quay.io/biocontainers/nanoplot:1.46.1–pyhdfd78af_0 e4af559c79ea 799MB 0B
quay.io/biocontainers/ont-modkit:0.5.0–hcdda2d0_1 759271fac42a 56.3MB 0B
quay.io/biocontainers/ont-modkit:0.5.0–hcdda2d0_2 23338f1f3608 51MB 0B
quay.io/biocontainers/perl:5.26.2 d3998a9936be 107MB 0B
quay.io/biocontainers/picard:3.0.0–hdfd78af_1 d395540d73c4 1.19GB 0B
quay.io/biocontainers/pigz:2.8 044d2120894b 14.1MB 0B
quay.io/biocontainers/porechop:0.2.4–py39h7cff6ad_2 88875379657b 191MB 0B
quay.io/biocontainers/prokka:1.14.6–pl5321hdfd78af_4 de20f4295af4 1.85GB 0B
quay.io/biocontainers/python:3.8.3 7d255f0a290f 207MB 0B
quay.io/biocontainers/python:3.9–1 34c2b9e3810c 191MB 0B
quay.io/biocontainers/qualimap:2.2.2d–1 508a009a25da 1.26GB 0B
quay.io/biocontainers/qualimap:2.3–hdfd78af_0 305b0e7620e9 1.65GB 0B
quay.io/biocontainers/quast:5.0.2–py37pl526hb5aa323_2 43d7b71dfd43 1.77GB 0B
quay.io/biocontainers/quast:5.2.0–py39pl5321h2add14b_1 b7b8479a9014 1.33GB 0B
quay.io/biocontainers/rasusa:0.3.0–h779adbc_1 d65888d0076a 44MB 0B
quay.io/biocontainers/requests:2.26.0 e3166094707d 181MB 0B
quay.io/biocontainers/rseqc:3.0.1–py37h516909a_1 8e8d841718c7 802MB 0B
quay.io/biocontainers/rseqc:5.0.3–py39hf95cd2a_0 e9b5f9b8302a 985MB 0B
quay.io/biocontainers/salmon:1.10.1–h7e5ed60_0 d2562f60654a 266MB 0B
quay.io/biocontainers/samtools:1.10–h9402c20_2 66dbf63b9173 90.2MB 0B
quay.io/biocontainers/samtools:1.16.1–h6899075_1 09cd4486af55 62MB 0B
quay.io/biocontainers/samtools:1.17–h00cdaf9_0 57a71725cb8a 61.8MB 0B
quay.io/biocontainers/samtools:1.22.1–h96c455f_0 a5ee23aa5171 71.5MB 0B
quay.io/biocontainers/seqcluster:1.2.9–pyh5e36f6f_0 c5d422d60a7d 669MB 0B
quay.io/biocontainers/seqkit:2.9.0–h9ee0642_0 aa3ad245f30e 29MB 0B
quay.io/biocontainers/seqtk:1.4–he4a0461_1 cabd6bfde871 15.2MB 0B
quay.io/biocontainers/snp-sites:2.5.1–hed695b0_0 55bb32a6d4ff 33.9MB 0B
quay.io/biocontainers/spades:3.15.3–h95f258a_0 3f81f9bb1c34 556MB 0B
quay.io/biocontainers/stringtie:2.2.1–hecb563c_2 bff12f9ac90d 200MB 0B
quay.io/biocontainers/subread:2.0.1–hed695b0_0 bbbd1bbfb3bd 96.8MB 0B
quay.io/biocontainers/toulligqc:2.7.1–pyhdfd78af_0 55e97054bff0 1.1GB 0B
quay.io/biocontainers/toulligqc:2.8.4–pyhdfd78af_0 355df1ba819f 1.26GB 0B
quay.io/biocontainers/trim-galore:0.6.7–hdfd78af_0 9786daa22bb1 714MB 0B
quay.io/biocontainers/ubuntu:24.04 35a88802559d 78.1MB 0B
quay.io/biocontainers/ucsc-bedclip:377–h0b8a92a_2 94ef80b67886 70.1MB 0B
quay.io/biocontainers/ucsc-bedgraphtobigwig:377–h446ed27_1 1e314a53b003 66.4MB 0B
quay.io/biocontainers/ucsc-bedgraphtobigwig:445–h954228d_0 608b52073059 51.1MB 0B
quay.io/biocontainers/whatshap:2.6–py39h2de1943_0 9c9c9b6edc9b 453MB 0B
quay.io/broadinstitute/viral-ngs:latest 3b0a22aa2452 5.57GB 0B
quay.io/nf-core/ubuntu:20.04 88bd68917189 72.8MB 0B
quay.io/qiime2/core:2023.9 a8adfed74f1b 5.93GB 0B
rkitchen/excerpt:latest 38fceb372de2 2.02GB 0B
sangerpathogens/circlator:latest b475e326f98b 1.72GB 0B
shinejh0528/plasmidfinder:latest 709388f557ea 701MB 0B
viral-ngs-fixed:2026-03-19 6ec2d521a0e8 7.89GB 0B
viral-ngs-fixed:l cb66bb9a9373 9.34GB 0B
viral-ngs-fixed:la c0390ae1e056 10.4GB 0B
viral_ngs_with_gap2seq:latest c0f397367599 6.7GB 0B
zacharyfoster/main-report-r-packages:0.20 c164ad489714 4.33GB 0B

MetaPhlAn 与 StrainPhlAn 数据库关系详解(中文)

MetaPhlAn 与 StrainPhlAn 数据库关系详解(中文)

简短回答:它们有关联,但不完全相同。两者都基于同一套 “标记基因 (marker genes)” 理念,但用途、文件结构和下载内容是分开的。


🔹 核心区别对比

特性 MetaPhlAn StrainPhlAn
主要用途 物种/菌种水平的分类组成分析(谁在那儿?丰度多少?) 同一物种内不同菌株水平的进化/变异分析(是同一个菌株吗?有没有突变?)
核心数据库 mpa_vJan25_CHOCOPhlAnSGB_202503_bt2/(Bowtie2 索引 + 标记基因列表) strainphlan_db_markers/(标记基因 FASTA)+ strainphlan_db_reference/(参考基因组,可选)
文件大小 ~16 GB(Bowtie2 索引解压后) 标记基因 ~50–200 MB;参考基因组需用户自行提供或额外下载
是否自动下载 metaphlan --install 会自动下载完整数据库 ⚠️ 标记基因可能随 MetaPhlAn 一起下载,但参考基因组通常为空,需手动配置
依赖关系 独立运行 通常依赖 MetaPhlAn 先鉴定出目标物种,再对该物种做菌株分析

🔹 目录结构说明(以你的路径为例)

# MetaPhlAn 主数据库(必需)
${DB_DIR}/
├── mpa_vJan25_CHOCOPhlAnSGB_202503_bt2/   # Bowtie2 索引文件(.bt2),用于快速比对
├── mpa_vJan25_CHOCOPhlAnSGB_202503_bt2.tar  # 压缩包
├── mpa_vJan25_CHOCOPhlAnSGB_202503_bt2.md5  # 校验文件
└── markers/                                 # 标记基因定义文件(.mpa)

# StrainPhlAn 专用目录(可选/扩展)
├── strainphlan_db_markers/
│   └── all_markers.fasta    # 所有物种的标记基因序列(用于提取目标物种的标记)
└── strainphlan_db_reference/
    └── [空或用户自定义]      # 存放特定物种的参考基因组集合,用于构建系统发育树

💡 你看到 strainphlan_db_reference/ 通常是空的,因为:

  1. StrainPhlAn 默认使用 MetaPhlAn 鉴定出的物种 + 内置标记基因进行分析;
  2. 只有当你想做高精度菌株追踪(比如比较多个样本中 E. coli 的 SNP 差异)时,才需要手动下载或提供该物种的参考基因组集合。

🔹 它们如何协作?(工作流程)

graph LR
    A[原始 FASTQ] --> B(MetaPhlAn)
    B --> C{物种组成表}
    C -->|目标物种: e.g. Escherichia coli | D[StrainPhlAn]
    D --> E[提取该物种的标记基因]
    E --> F[多序列比对 + 构建系统发育树]
    F --> G[菌株水平差异/传播分析]

🔹 实用建议(针对你的测试环境)

  1. 当前测试阶段:你用了 --bypass-strain-profiling,所以 StrainPhlAn 完全不会运行,只需确保 MetaPhlAn 数据库完整即可

  2. 未来启用菌株分析时

    # 1. 先确认标记基因是否存在
    ls -lh ${DB_DIR}/strainphlan_db_markers/all_markers.fasta
    
    # 2. 如果缺失,可尝试重新安装(部分版本会一起下载)
    metaphlan --install --bowtie2db ${DB_DIR}
    
    # 3. 如需参考基因组(高级用法),手动下载并放入 reference 目录
    #    例如从 GTDB 或 NCBI 下载目标物种的 .fna 文件
  3. 数据库路径统一管理(避免重复下载):

    # 在 ~/.bashrc 或 conda env 中设置
    export METAPHLAN_DB_PATH=/mnt/nvme4n1p1/biobakery_db/metaphlan_dbs
    export STRAINPHLAN_DB_PATH=/mnt/nvme4n1p1/biobakery_db/strainphlan_dbs

🔹 常见问题排查

# 检查 MetaPhlAn 数据库是否完整
metaphlan --help  # 如果能看到版本信息,说明基础环境正常

# 手动验证 MD5(你之前报错的关键)
cd ${DB_DIR}
md5sum -c mpa_vJan25_CHOCOPhlAnSGB_202503_bt2.md5
# 正确输出应为: mpa_vJan25_CHOCOPhlAnSGB_202503_bt2.tar: OK

# 如果 StrainPhlAn 报错找不到标记基因
strainphlan --print-default-db  # 查看默认数据库路径
# 然后通过 --marker_db 和 --reference_db 参数指定自定义路径

总结

MetaPhlAn 是 “普查”(查有哪些菌),StrainPhlAn 是 “精查”(查同一个菌的不同菌株)。
它们共享底层标记基因理念,但数据库文件、用途和运行逻辑是分层独立的。
你当前测试只需关注 MetaPhlAn 数据库修复即可,StrainPhlAn 可后续按需启用。

如果修复 MD5 问题后还有报错,欢迎继续贴日志,我帮你逐行分析!🔍🚀



如何获取 strainphlan_db_markers/all_markers.fasta

关键结论:这个文件不是直接下载的,而是通过 bowtie2-inspect 工具从 MetaPhlAn 的 Bowtie2 索引文件本地生成的。[[41]]


🔹 原理说明

all_markers.fasta 包含数据库中的所有标记基因序列,StrainPhlAn 用它来提取目标物种的标记。[[28]]

生成逻辑(来自官方源码 biobakery_workflows_databases.py):[[41]]

# 1. 找到 MetaPhlAn 数据库目录
strainphlan_db_folder = os.path.join(os.path.dirname(metaphlan.__file__), "metaphlan_databases")
strainphlan_db = glob.glob(strainphlan_db_folder+"/mpa_*.pkl")[0].replace(".pkl", "")

# 2. 用 bowtie2-inspect 提取 FASTA
run_command("bowtie2-inspect " + strainphlan_db + " > " + install_folder + "/all_markers.fasta", shell=True)

🔹 方法一:使用官方命令自动生成(推荐)

# 1. 确保 MetaPhlAn 数据库已正确安装(先修复你之前的 MD5 问题)
metaphlan --install --bowtie2db /home/jhuang/mambaforge/envs/biobakery_run/lib/python3.10/site-packages/metaphlan/metaphlan_databases

# 2. 运行 biobakery_workflows 的数据库安装命令,自动创建 all_markers.fasta
biobakery_workflows_databases --install wmgx --location /mnt/nvme4n1p1/biobakery_db

✅ 执行后,会在指定 location 下自动生成:

/mnt/nvme4n1p1/biobakery_db/
├── strainphlan_db_markers/
│   └── all_markers.fasta    # ← 自动生成,约 50–200 MB
└── strainphlan_db_reference/
    └── (空目录,用于自定义参考基因组)

🔹 方法二:手动用 bowtie2-inspect 生成(高级用户)

如果你已确认 MetaPhlAn 数据库完整,可手动执行:

# 1. 找到你的 MetaPhlAn Bowtie2 索引前缀(不含 .bt2 后缀)
#    通常在:~/.conda/envs/biobakery_run/lib/python3.10/site-packages/metaphlan/metaphlan_databases/
DB_PREFIX="/home/jhuang/mambaforge/envs/biobakery_run/lib/python3.10/site-packages/metaphlan/metaphlan_databases/mpa_vJan25_CHOCOPhlAnSGB_202503_bt2"

# 2. 创建输出目录
mkdir -p /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers

# 3. 用 bowtie2-inspect 提取 FASTA
bowtie2-inspect "$DB_PREFIX" > /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers/all_markers.fasta

# 4. 验证生成结果
ls -lh /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers/all_markers.fasta
head -n 4 /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers/all_markers.fasta

💡 bowtie2-inspect 是 Bowtie2 自带工具,安装 bowtie2 后自动可用。


🔹 验证数据库是否完整

# 检查 MetaPhlAn 能否正常调用数据库
metaphlan --print-default-db

# 检查 all_markers.fasta 是否存在且非空
test -s /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers/all_markers.fasta && echo "✅ all_markers.fasta OK" || echo "❌ 文件缺失或为空"

# (可选)快速统计标记基因数量
grep -c "^>" /mnt/nvme4n1p1/biobakery_db/strainphlan_db_markers/all_markers.fasta
# 预期输出:数万条(取决于数据库版本)

🔹 常见问题排查

问题 解决方案
bowtie2-inspect: command not found conda install -c bioconda bowtie2
Unable to find strainphlan install 确保 metaphlan 已正确安装且 $PYTHONPATH 包含其路径
生成的 all_markers.fasta 为空 检查 $DB_PREFIX 是否正确,应指向 .bt2 索引的公共前缀(不含 .1.bt2 等后缀)
磁盘空间不足 Bowtie2 索引解压后约 16 GB,确保目标分区有 ≥20 GB 空闲

🔹 当前测试建议

你当前运行 biobakery_workflows 时使用了 --bypass-strain-profilingStrainPhlAn 完全不会执行,因此 all_markers.fasta 缺失不会影响你的测试流程。✅

🎯 优先修复 MetaPhlAn 数据库的 MD5 问题(删除损坏的 .tar + .md5 文件后重跑),等流程测试通过后再按需生成 StrainPhlAn 数据库。

如需我帮你写一个一键修复 + 生成数据库的脚本,请告诉我!🚀

Detailed Methods: Bacterial Methylome Analysis Pipeline (Data_Tam_DNAseq_2026_An6_BG5)

✅ 简短回答:是的,nf-core/methylong 接受 2 个生物学重复进行 DMR 分析

但需要注意统计效力的限制结果解读的谨慎性


🔧 技术层面:methylong 如何支持 2 重复设计

1️⃣ 参数配置示例

nextflow run nf-core/methylong \
  -r 2.0.0 \
  -profile docker \
  --input samplesheet_6mA.csv \      # 包含 2 个重复/组
  --outdir methylome_out_6mA \
  --ont_aligner minimap2 \
  --m6a \
  --skip_snvs \
  --dmr_population_scale \           # ⭐ 启用群体水平比较
  --population_dmrer dss \           # ⭐ 使用 DSS (支持小样本)
  --dmr_a "An6_rep1,An6_rep2" \      # 组 A: 2 个重复
  --dmr_b "BG5_rep1,BG5_rep2" \      # 组 B: 2 个重复
  -resume

2️⃣ samplesheet 格式要求

group,sample,path,ref,method
An6,An6_rep1,/path/An6_rep1.mod.bam,/path/genome.fa,ont
An6,An6_rep2,/path/An6_rep2.mod.bam,/path/genome.fa,ont
BG5,BG5_rep1,/path/BG5_rep1.mod.bam,/path/genome.fa,ont
BG5,BG5_rep2,/path/BG5_rep2.mod.bam,/path/genome.fa,ont

⚠️ 关键group 列的值必须与 --dmr_a/--dmr_b 中的组名完全一致


⚠️ 统计层面:2 重复的局限性与应对策略

🔹 DSS 在 2 重复下的工作原理

特性 说明 对您的影响
贝叶斯收缩 借用全基因组位点信息”收缩”方差估计 ✅ 提升小样本稳健性,是 2 重复可行的理论基础
经验分布建模 基于所有位点的甲基化概率分布估计背景噪声 ✅ 不需要大量重复也能估计全局变异
FDR 校正 Benjamini-Hochberg 方法控制多重检验 ⚠️ 2 重复时假阳性控制可能偏保守或偏宽松,需谨慎解读 p.adj

🔹 2 重复 vs 3+ 重复的统计效力对比

指标 2 重复/组 3 重复/组 对您的建议
检测中等效应 ( diff =20-30%) 的功率 ~40-60% ~70-85% 当前结果可能漏检部分真实差异位点
FDR 控制的可靠性 中等 显著位点 (p.adj<0.05) 可信,但”不显著”不代表无差异
异常值鲁棒性 低 (1 个异常样本可扭曲结果) 建议用 MultiQC 检查样本间相关性,排除技术异常

🎯 实用建议:如何在 2 重复下最大化分析价值

✅ 策略 1:提高过滤阈值,聚焦高置信结果

# 在 DSS 输出后,额外应用更严格的过滤
awk '$8 >= 0.95 && abs($7) >= 0.3' An6_vs_BG5.DSS_DMLs.tsv > high_conf_diff_6mA.tsv
# $8 = p.adj, $7 = diff (甲基化差异)

✅ 策略 2:结合生物学先验知识验证

# 优先关注已知限制修饰系统相关基因
bedtools intersect -a high_conf_diff_6mA.tsv -b RM_system_genes.bed -wa > RM_related_diff.tsv

# 或关注表型相关基因 (如毒力、应激响应)
bedtools intersect -a high_conf_diff_6mA.tsv -b virulence_genes.bed -wa > phenotype_related_diff.tsv

✅ 策略 3:敏感性分析 (Bootstrap 评估稳健性)

# R 脚本示例:评估 2 重复结果的稳定性
library(boot)

# 对每个显著位点,用 bootstrap 重采样估计差异的置信区间
boot_diff <- function(data, indices) {
  d <- data[indices, ]
  mean(d$groupA) - mean(d$groupB)  # 简化示例
}

# 对 top 100 显著位点运行 1000 次 bootstrap
# 如果 95% CI 不包含 0,则结果稳健

✅ 策略 4:报告时透明说明局限性

在论文/报告方法部分添加:

“差异甲基化分析使用 DSS (vX.X) 进行,每组 2 个生物学重复。由于样本量限制,我们采用更严格的显著性阈值 (FDR 30%) 并优先关注已知功能基因的甲基化变化,以降低假阳性风险。”


📊 如果您决定扩展至 3 重复:最小成本方案

方案 额外成本 收益
技术重复 (同一 DNA 多次建库) 低 (仅测序成本) 评估技术噪声,但不能替代生物学重复
生物学重复 (独立培养 + 提取) 中 (时间 + 试剂) ✅ 真正提升统计效力,推荐
混合策略 (2 生物重复 + 1 技术重复) 中低 平衡成本与可靠性,适合资源有限场景

💡 一句话总结

🔑 技术上:methylong + DSS 完全支持 2 重复/组的 DMR 分析,管道可正常运行并输出结果。
🔑 统计上:2 重复的统计效力有限,建议:(1) 使用更严格的显著性阈值,(2) 结合生物学先验知识验证,(3) 在报告中透明说明局限性。
🔑 未来规划:如有关键发现需深入验证,建议扩展至≥3 个生物学重复以获得更可靠的 FDR 控制。

如需我帮您编写更严格的过滤脚本或敏感性分析代码,请随时告知!🚀

🔬 Detailed Methods: Bacterial Methylome Analysis Pipeline

1. Base Modification Calling with modkit

Raw nanopore sequencing signals were processed using modkit pileup (v0.3.1) to detect base modifications at single-nucleotide resolution. The pipeline accepts BAM files aligned to the isolate-specific reference genome and outputs a BED-format file with the following columns:

Column Name Description Example
1 chrom Contig/chromosome identifier contig_1
2 start Modification position (0-based, BED format) 12345
3 end End position (start + 1 for single-base mods) 12346
4 name Modification code + sequence context a,CG,ACGT (6mA), m,CG,ACGT (5mC), 21839,C,ACGT (4mC)
5 score Total read coverage at the site 45
6 strand Strand orientation (+, -, or . for unstranded) +
7-9 thickStart/End, itemRgb Visualization fields (unused in analysis)
10 blockCount Reads supporting the modification call 32
11 blockSizes Modification probability (0–100%) ← core filtering metric 85.3
12-18 Extended QC metrics mod_count, unmod_count, del_count, no_call_count, strand-specific coverage

📌 Note on column 11: This value represents the percent of reads at that position called as modified (e.g., 85.3 = 85.3% of reads show 6mA at this adenine).


2. High-Confidence Site Filtering

Sites were filtered using two stringent thresholds to minimize false positives:

  • Minimum coverage: ≥10 reads ($5 >= 10)
  • Minimum modification rate:
    • 5mC: ≥70% ($11 >= 70)
    • 4mC: ≥50% ($11 >= 50)
    • 6mA: ≥70% ($11 >= 70)

Filtering was performed using awk:

# Example: 5mC @ CG context
awk -F'\t' '$4 ~ /^m,CG,/ && $11 >= 70 && $5 >= 10 {print}' input.bed > 5mC_CG_filtered.bed

Sites were further separated by:

  • Modification type: 4mC, 5mC, or 6mA (based on column 4 prefix)
  • Sequence context: CG vs. non-CG (for cytosine modifications)

3. Flanking Sequence Extraction for Motif Analysis

For motif discovery, we extracted ±50 bp flanking sequences centered on each high-confidence modification site:

Modification site:          [position X]
Extracted window:  [X-50] ................ [X] ................ [X+50]
                          ↑
                  Total length = 101 bp

Clarification: The -f 50 parameter in our pipeline specifies 50 bp upstream AND 50 bp downstream of the modification coordinate, yielding a 101-bp window (not ±25 bp). This ensures sufficient context for bacterial motif discovery, where recognition sites are typically 4–10 bp but may be embedded in larger regulatory elements.

Extraction was performed using bedtools getfasta:

# Create extended regions file
awk -F'\t' -v flank=50 '{
    c=$1; gsub(/^>/,"",c);
    s=($2-flank>0)?$2-flank:0; e=$3+flank;
    print c"\t"s"\t"e"\t"$4"_"NR
}' filtered.bed > regions.bed

# Extract sequences (strand-aware)
bedtools getfasta -fi genome.fa -bed regions.bed -fo sequences.fa -nameOnly -s

4. Motif Enrichment Analysis with HOMER

De novo motif discovery was performed using HOMER (findMotifsGenome.pl, v4.11) with parameters optimized for bacterial restriction-modification systems:

Parameter Value Rationale
-len 4,6,8,10 Bacterial R-M recognition sites are typically 4–8 bp palindromes; 10 bp captures complex motifs
-size 50 Matches the ±50 bp flanking window used for extraction
-mask true Masks low-complexity/repetitive regions to reduce spurious motifs
-S 10 Optimizes 10 putative motifs per dataset to balance sensitivity and specificity
-noknown true Focuses on de novo discovery; known motif scanning performed post-hoc via REBASE
-useNewBg true Uses HOMER’s native GC-matched background generation (avoids bedtools shuffle instability)
-p 8–100 Parallel threads (adjusted per compute resource)

Background sequences: HOMER automatically generated GC-matched background sequences from the reference genome, stratified by GC content to control for compositional bias.


5. REBASE Annotation & Methylase Classification

Enriched motifs were cross-referenced against the REBASE database (v605) using a custom Python script (annotate_motifs_rebase_fixed.py) to identify matches with known bacterial restriction-modification systems.

Matching Logic:

  1. IUPAC-aware comparison: Motifs containing degenerate bases (e.g., R=A/G, Y=C/T) were converted to regex patterns for flexible matching against REBASE recognition sequences.
  2. Length-tolerant matching: Motifs were compared against REBASE entries of similar length (±1 bp) to accommodate minor variations.
  3. Methylation notation parsing: REBASE entries with methylation annotations (e.g., 2(6) = N6-methyladenine at position 2) were parsed to distinguish:
    • REBASE Matches: Any enzyme (restriction endonuclease or methyltransferase) with a matching recognition sequence.
    • Methylase Hits: Entries where the enzyme name starts with M. (indicating a methyltransferase) AND the methylation notation matches the modification type:
      • (4) = N4-methylcytosine → 4mC
      • (5) = 5-methylcytosine → 5mC
      • (6) = N6-methyladenine → 6mA

Output Classification:

Category Definition Example
No REBASE match Motif not found in REBASE Likely a transcription factor binding site or novel methylase
REBASE match (restriction enzyme only) Matches a restriction endonuclease but not its cognate methylase Coincidental sequence similarity; methylation likely from orphan methylase
Methylase hit Matches a methyltransferase with correct modification type notation High-confidence candidate for the enzyme catalyzing the observed modification

6. Output File Structure

Final results were compiled into Excel workbooks with the following sheets per sample:

Sheet Name Content Source File
4mC_CG 4mC sites in CG context 4mC_CG_filtered.bed + REBASE annotation
4mC_nonCG 4mC sites in non-CG context 4mC_nonCG_filtered.bed + REBASE annotation
5mC_CG 5mC sites in CG context 5mC_CG_filtered.bed + REBASE annotation
5mC_nonCG 5mC sites in non-CG context 5mC_nonCG_filtered.bed + REBASE annotation
6mA All 6mA sites 6mA_raw_filtered.bed + REBASE annotation

Each sheet includes:

  • Genomic coordinates and modification metrics (from modkit)
  • HOMER motif enrichment statistics (p-value, motif sequence)
  • REBASE annotation (matched enzymes, methylase status, methylation notation)

7. Software & Database Versions

Tool/Database Version Purpose
modkit 0.3.1 Base modification calling from nanopore signals
bedtools 2.31.1 Sequence extraction and genomic interval operations
HOMER 4.11 De novo motif discovery and enrichment analysis
REBASE 605 (Apr 2026) Curated database of restriction enzymes and methyltransferases
Python 3.10+ Custom annotation and data integration scripts
pandas/openpyxl Latest Excel workbook generation

🔑 Key Clarifications

  1. Flanking window: -f 50 = ±50 bp (101 bp total), not ±25 bp.
  2. Modification rate: Column 11 (blockSizes) = percent modified (0–100), not a probability score.
  3. Methylase identification: Requires BOTH M. prefix in enzyme name AND matching methylation notation (4)/(5)/(6).
  4. Context separation: CG vs. non-CG filtering is applied ONLY to cytosine modifications (4mC/5mC); 6mA analysis includes all contexts.

This pipeline enables systematic identification of sequence motifs associated with bacterial DNA methylation and links them to known enzymatic machinery via REBASE annotation.

dorado + nf-core/methylong + modkit pileup + interpretation using own scripts processing Data_Tam_DNAseq_2026_An6_BG5 methylome data

dna_methylation_analysis_4mc_5mc.sh dna_methylation_analysis_6mA.sh annotate_motifs_rebase.py generate_methylation_excel.py

  1. Run nextflow methylong after running nextflow bacass

     ✅ ---- 终极解决方案:使用本地 Dorado 预生成 modBAM + --skip_basecalling (绕过容器 CUDA 限制)----
    
     鉴于容器镜像的复杂性和 CUDA 版本限制,最可靠、最快速的路径是:
     使用您已安装的本地 Dorado v2.0.0 生成已比对的 modBAM
     运行 methylong 时添加 --skip_basecalling,跳过内部容器化 Dorado
     这样完全绕过容器、CUDA、GPU 传递等所有问题!
    
     步骤 1:如果您已安装本地 Dorado(非容器版),可预先生成 modBAM:
    
     # 安装本地 Dorado (CPU 或匹配您 CUDA 的版本)
     #    下载: https://github.com/nanoporetech/dorado/releases; https://software-docs.nanoporetech.com/dorado/latest/
     curl "https://cdn.oxfordnanoportal.com/software/analysis/dorado-2.0.0-linux-x64.tar.gz" -o dorado-2.0.0-linux-x64.tar.gz
     tar -xzf dorado-2.0.0-linux-x64.tar.gz
    
     # 添加到 PATH (永久生效)
     echo 'export PATH="/home/jhuang/Tools/dorado-2.0.0-linux-x64/bin:$PATH"' >> ~/.bashrc
     source ~/.bashrc
    
     # 验证安装: 2.0.0+20e87c8b
     dorado --version
    
     # 查看可用的碱基调用模型
     Positional arguments:
       model                             Model selection {fast,hac,sup}@v{version} for automatic model selection including modbases, or path to existing model directory.
       data                              The data directory or POD5 file path.
    
     # 查看可用的修饰检测模型
     dorado basecaller --help | grep -A5 "modified-bases"
     # ✅ 应看到: 6mA, 4mC_5mC, 5mC_5hmC 等
    
     步骤 2: 生成 modBAM
     ./generate_mapped_modbam.sh
    
         #!/bin/bash
         #===============================================================================
         # generate_mapped_modbam.sh - 本地 Dorado 生成已比对 modBAM
         #===============================================================================
         set -euo pipefail
    
         # === 配置 ===
         DORADO="/home/jhuang/Tools/dorado-2.0.0-linux-x64/bin/dorado"  # 您的本地 Dorado 路径
         REF_AN6="/mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5/bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa"
         REF_BG5="/mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5/bacass_out/Medaka/BG5-unicycler-medaka_polished_genome.fa"
         POD5_AN6="./X101SC26036392-Z01-J003/Release-X101SC26036392-Z01-J003-20260513_01/Data-X101SC26036392-Z01-J003/An6/2157_4C_PBK79106_7ec05c46/An6_pod5_pass"
         POD5_BG5="./X101SC26036392-Z01-J003/Release-X101SC26036392-Z01-J003-20260513_01/Data-X101SC26036392-Z01-J003/BG5/2157_4C_PBK79106_7ec05c46/BG5_pod5_pass"
         OUTDIR="/mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5"
    
         # 确保参考基因组已索引
         samtools faidx "${REF_AN6}"
         samtools faidx "${REF_BG5}"
    
         echo "🚀 生成 An6 6mA modBAM (已比对)..."
         "${DORADO}" basecaller \
           --modified-bases 6mA \
           --emit-moves \
           --device cuda:0 \
           --reference "${REF_AN6}" \
           dna_r10.4.1_e8.2_400bps_sup@v5.0.0 \
           "${POD5_AN6}" | samtools view -b - > "${OUTDIR}/An6_6mA_mapped.mod.bam"
    
         echo "🚀 生成 BG5 6mA modBAM (已比对)..."
         "${DORADO}" basecaller \
           --modified-bases 6mA \
           --emit-moves \
           --device cuda:0 \
           --reference "${REF_BG5}" \
           dna_r10.4.1_e8.2_400bps_sup@v5.0.0 \
           "${POD5_BG5}" | samtools view -b - > "${OUTDIR}/BG5_6mA_mapped.mod.bam"
    
         echo "🚀 生成 An6 6mA modBAM (已比对)..."
         "${DORADO}" basecaller \
           --modified-bases 4mC_5mC \
           --emit-moves \
           --device cuda:0 \
           --reference "${REF_AN6}" \
           dna_r10.4.1_e8.2_400bps_sup@v5.0.0 \
           "${POD5_AN6}" | samtools view -b - > "${OUTDIR}/An6_4mC_5mC_mapped.mod.bam"
    
         echo "🚀 生成 BG5 6mA modBAM (已比对)..."
         "${DORADO}" basecaller \
           --modified-bases 4mC_5mC \
           --emit-moves \
           --device cuda:0 \
           --reference "${REF_BG5}" \
           dna_r10.4.1_e8.2_400bps_sup@v5.0.0 \
           "${POD5_BG5}" | samtools view -b - > "${OUTDIR}/BG5_4mC_5mC_mapped.mod.bam"
    
         # 验证输出
         echo "🔍 验证 BAM 文件..."
         for BAM in "${OUTDIR}"/*_6mA_mapped.mod.bam; do
           if samtools quickcheck "${BAM}" 2>/dev/null; then
             READS=$(samtools view -c "${BAM}")
             TAGS=$(samtools view "${BAM}" | head -50 | grep -o "MM:Z:[^ ]*" | sort -u | tr '\n' ' ')
             echo "✅ $(basename "${BAM}"): ${READS} reads, tags: ${TAGS}"
           else
             echo "❌ $(basename "${BAM}"): 文件损坏或格式错误"
           fi
         done
    
         echo "🎉 modBAM 生成完成!"
    
     步骤 3: 创建 methylong samplesheet(使用预生成 modBAM)
     See samplesheet_6mA.csv and samplesheet_4mC_5mC.csv
    
     步骤 4: 运行 methylong with --skip_basecalling
    
         ## Check if BAMs are read-name sorted:
         #samtools view -H An6_6mA_mapped.mod.bam | grep "^@HD"
         ## Should show: @HD VN:1.6 SO:queryname
    
         ## If not sorted correctly:
         #samtools sort -n -o An6.sorted_by_name.bam An6_6mA_mapped.mod.bam
         #samtools sort -n -o BG5.sorted_by_name.bam BG5_6mA_mapped.mod.bam
    
         ## Check BAM integrity:
         #samtools quickcheck -v An6_6mA_mapped.mod.bam
         #samtools quickcheck -v An6_after_trim.bam
    
         ## Use modkit to check tags:
         #modkit modbam check-tags An6_6mA_mapped.mod.bam
         #modkit modbam check-tags An6_after_trim.bam
    
         #BUG: '--skip_basecalling' does not exist (see the complete option list below.)
         nextflow run nf-core/methylong \
             -r 2.0.0 \
             -profile docker \
             --input samplesheet_6mA.csv \
             --outdir methylome_out_6mA \
             --skip_basecalling \          # ⭐ 关键: 跳过内部 Dorado,使用预生成 modBAM
             -resume \
             -work-dir methylome_out_6mA/work
    
         # Ist die BAM-Datei unaligniert? (keine @SQ Header mit Alignment)
         samtools view -H An6_6mA_mapped.mod.bam | head -10
    
         # Sind MM/ML-Tags für 6mA vorhanden?
         samtools view An6_6mA_mapped.mod.bam | head -5 | grep -o "MM:Z:[^ ]*"
    
         #NEXT_WEEK FOLLOWING the command under the website
         #https://bioinformatics.cc/debug-methylomic-analysis-of-data_tam_dnaseq_2026_an6_bg5/
    
         # FIRSTLY RUNNING NEXTFLOW
         # 1️⃣ 处理 6mA BAM(仅含 6mA 信号)
         nextflow run nf-core/methylong -r 2.0.0 -profile docker \
             --input samplesheet_6mA.csv \
             --outdir methylome_out_6mA \
             --ont_aligner minimap2 \
             --m6a \
             --skip_snvs \
             -resume \
             -work-dir methylome_out_6mA/work
         # 2️⃣ 处理 4mC_5mC BAM(含 4mC + 5mC 信号)
         nextflow run nf-core/methylong -r 2.0.0 -profile docker \
             --input samplesheet_4mC_5mC.csv \
             --outdir methylome_out_4mC_5mC \
             --ont_aligner minimap2 \
             --all_contexts \
             --skip_snvs \
             -resume \
             -work-dir methylome_out_4mC_5mC/work
    
     步骤 5: Check output
     [ ] ✅ 本地 Dorado 生成已比对 modBAM: samtools quickcheck An6_6mA_mapped.mod.bam 通过
     [ ] ✅ BAM 含 @SQ 头: samtools view -H An6_6mA_mapped.mod.bam | grep "^@SQ"
     [ ] ✅ BAM 含修饰标签: samtools view An6_6mA_mapped.mod.bam | grep "MM:Z:6mA" | head -3 returns nothing, but samtools view An6_6mA_mapped.mod.bam | grep "MM:Z:" | head -3
     [ ] ✅ samplesheet 使用绝对路径 + 精确 sample 名称
     [ ] ✅ 运行命令添加 --skip_basecalling
     [ ] ✅ 输出目录存在: methylome_out_6mA/methylation_calls/*.bedMethyl.gz
  2. SECONDLY RERUN modkit pileup to correct the BUG from nf-core/methylong

     # ---- 6mA 对应过滤命令 ----
     # 方案 A:降低阈值测试 (NOT_USED)
     # docker run --rm -v /mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5:/data -w /data quay.io/biocontainers/ont-modkit:0.5.0--hcdda2d0_2 modkit pileup ./methylome_out_6mA/ont/An6/alignment/An6_aligned.bam An6_6mA_test.bed --ref bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa --motif A 0 --filter-threshold A:0.7 --threads 8
    
     方案 B:6mA: 无过滤 + 手动后处理 (CHOSEN)
     # ✅ 第 1 步:生成无过滤的原始结果(6mA)
     docker run --rm -v /mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5:/data -w /data quay.io/biocontainers/ont-modkit:0.5.0--hcdda2d0_2 modkit pileup ./methylome_out_6mA/ont/An6/alignment/An6_aligned.bam An6_6mA_raw.bed --ref bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa --motif A 0 --no-filtering --threads 8
     docker run --rm -v /mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5:/data -w /data quay.io/biocontainers/ont-modkit:0.5.0--hcdda2d0_2 modkit pileup ./methylome_out_6mA/ont/BG5/alignment/BG5_aligned.bam BG5_6mA_raw.bed --ref bacass_out/Medaka/BG5-unicycler-medaka_polished_genome.fa --motif A 0 --no-filtering --threads 8
    
     # # ✅ 第 2 步:检查原始概率最大值
     # awk '{print $11}' An6_6mA_raw.bed | sort -rn | head -5
     # # 如果最大值 < 70 → 降低分析阈值
    
     # # ✅ 第 3 步:手动过滤生成最终结果
     # # ✅ 过滤高置信度 6mA 位点:概率≥70% + 覆盖度≥10
     # awk '$4 ~ /^A,/ && $11 >= 70 && $5 >= 10' An6_6mA_raw.bed > An6_6mA_final.bed
    
     # # ✅ 第 4 步:验证并统计
     # echo "=== 6mA 最终结果 ==="
     # # 📊 统计结果
     # echo "=== 6mA 高置信度位点统计 ==="
     # echo "总位点数: $(wc -l < An6_6mA_final.bed)"
     # echo ""
     # echo "概率分布 (前10名):"
     # awk '{print $11}' An6_6mA_final.bed | sort -rn | uniq -c | head -10
     # echo ""
     # echo "按染色体分布:"
     # cut -f1 An6_6mA_final.bed | sort | uniq -c | sort -rn
    
     # 方案 C:检查 BAM 信号原始分布 (For DEBUG)
     # # 提取 ML 标签中的概率值 (0-255 缩放 → 转换为 0-1)
     # samtools view ./methylome_out_6mA/ont/An6/alignment/An6_aligned.bam | head -100 | \
     #   grep -oP 'ML:B:C,\K[0-9,]+' | tr ',' '\n' | \
     #   awk '{if($1>0) print $1/255}' | sort -n | uniq -c | head -10
    
     # ---- 4mC_5mC 对应过滤命令 ----
     docker run --rm -v /mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5:/data -w /data quay.io/biocontainers/ont-modkit:0.5.0--hcdda2d0_2 modkit pileup ./methylome_out_4mC_5mC/ont/An6/alignment/An6_aligned.bam An6_4mC_5mC_raw.bed --ref bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa --motif CG 0 --motif C 0 --no-filtering --threads 8
     docker run --rm -v /mnt/md1/DATA/Data_Tam_DNAseq_2026_An6_BG5:/data -w /data quay.io/biocontainers/ont-modkit:0.5.0--hcdda2d0_2 modkit pileup ./methylome_out_4mC_5mC/ont/BG5/alignment/BG5_aligned.bam BG5_4mC_5mC_raw.bed --ref bacass_out/Medaka/BG5-unicycler-medaka_polished_genome.fa --motif CG 0 --motif C 0 --no-filtering --threads 8
    
     #修饰类型   列4匹配正则  说明
     #5mC @ CG   ^m,CG,  5-甲基胞嘧啶,在CG motif上
     #5mC @ C    ^m,C,   5-甲基胞嘧啶,在单碱基C上
     #4mC @ CG   ^21839,CG,  4-甲基胞嘧啶,在CG motif上
     #4mC @ C    ^21839,C,   4-甲基胞嘧啶,在单碱基C上
    
     ┌─────────────────────────────────────────────────────┐
     │  Column 4 格式:[修饰代码],[motif],[offset]         │
     ├─────────────────────────────────────────────────────┤
     │  m,CG,0      → 5mC @ CG 位点                        │
     │  m,C,0       → 5mC @ 任意 C 位点(包含 CG+非CG)    │
     │  21839,CG,0  → 4mC @ CG 位点                        │
     │  21839,C,0   → 4mC @ 任意 C 位点(包含 CG+非CG)    │
     └─────────────────────────────────────────────────────┘
     ⚠️ 重要提示:m,C,0 包含 所有 C 位点(CG + CHG + CHH),与 m,CG,0 有重叠!
     优先级 1: 4mC @ CG (21839,CG,0)  ← 细菌最主要的修饰
     优先级 2: 5mC @ CG (m,CG,0)      ← 次要修饰
     优先级 3: 非 CG 修饰             ← 探索性分析
     重点关注:
     ✅ 4mC @ CG - 细菌限制修饰系统的核心
     ✅ 5mC @ CG - 部分细菌也有 5mC
     ⚠️ 非 CG 修饰 - 细菌中较少,可先忽略
    
     修饰类型    位点数量    占比  生物学解释
     5mC @ CG    437 33% 经典 CpG 甲基化
     5mC @ non-CG    870 67% ⭐ 非 CG 位点更多,正常!
     4mC @ CG    7   17% 4mC 很少在 CG 上
     4mC @ non-CG    34  83% ⭐ 4mC 偏好非 CG 位点
  3. DNA methylation analysis (Sample An6: Acinetobacter harbinensis and Sample BG5: Pedobacter sp. strain BG5, both are bacteria, not plants.)

     mamba create -n methylation -c conda-forge -c bioconda \
       r-base=4.3 \
       r-dss \
       r-methylkit \
       r-chipseeker \
       r-qvalue \
       metilene \
       bedtools \
       samtools \
       ucsc-bedgraphtobigwig \
       -y
     mamba activate methylation
    
     #chmod +x dna_methylation_analysis_4mC_5mC.sh
     (methylation) bash dna_methylation_analysis_4mC_5mC.sh \
         -i An6_4mC_5mC_raw.bed \
         -r bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa \
         -o An6_4mC_5mC_methylation_results \
         -c 10 -m 70 -n 50 -f 50 -t 100
    
     #Using homerMotifs.all.motifs rather than nonRedundant.motifs
     (methylation) python annotate_motifs_rebase.py \
         --homer An6_4mC_5mC_methylation_results/motif_analysis/homer/5mC_CG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output An6_5mC_CG_results.tsv
     #  Total motifs: 35
     #  REBASE matches: 8 (22.9%)
     #  ⚠️ Methylase matches: 2
    
     python annotate_motifs_rebase.py \
         --homer An6_4mC_5mC_methylation_results/motif_analysis/homer/4mC_CG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output An6_4mC_CG_results.tsv
     #  Total motifs: Total motifs: 35
     # REBASE matches: 6 (17.1%)
     #⚠️ Methylase matches: 0
    
     python annotate_motifs_rebase.py \
         --homer An6_4mC_5mC_methylation_results/motif_analysis/homer/5mC_nonCG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output An6_5mC_nonCG_results.tsv
     #📊 Summary:
     #Total motifs: 37
     #REBASE matches: 4 (10.8%)
     #⚠️ Methylase matches: 0
    
     python annotate_motifs_rebase.py \
         --homer An6_4mC_5mC_methylation_results/motif_analysis/homer/4mC_nonCG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output An6_4mC_nonCG_results.tsv
     #    Total motifs: 35
     #REBASE matches: 10 (28.6%)
     #⚠️ Methylase matches: 2
    
     bash dna_methylation_analysis_4mC_5mC.sh \
         -i BG5_4mC_5mC_raw.bed \
         -r bacass_out/Medaka/BG5-unicycler-medaka_polished_genome.fa \
         -o BG5_4mC_5mC_methylation_results \
         -c 10 -m 70 -n 50 -f 50 -t 100
    
     python annotate_motifs_rebase.py \
         --homer BG5_4mC_5mC_methylation_results/motif_analysis/homer/5mC_CG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output BG5_5mC_CG_results.tsv
     #  📊 Summary:
     #Total motifs: 36
     #REBASE matches: 7 (19.4%)
     #⚠️ Methylase matches: 1
    
     python annotate_motifs_rebase.py \
         --homer BG5_4mC_5mC_methylation_results/motif_analysis/homer/4mC_CG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output BG5_4mC_CG_results.tsv
     # Total motifs: 35
     #REBASE matches: 8 (22.9%)
     #⚠️ Methylase matches: 3
    
     python annotate_motifs_rebase.py \
         --homer BG5_4mC_5mC_methylation_results/motif_analysis/homer/5mC_nonCG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output BG5_5mC_nonCG_results.tsv
     #  Total motifs: 39
     #REBASE matches: 7 (17.9%)
     #⚠️ Methylase matches: 0
    
     python annotate_motifs_rebase.py \
         --homer  BG5_4mC_5mC_methylation_results/motif_analysis/homer/4mC_nonCG/homerMotifs.all.motifs \
         --rebase withrefm.txt \
         --output  BG5_4mC_nonCG_results.tsv
     #Total motifs: 35
     #REBASE matches: 9 (25.7%)
     #⚠️ Methylase matches: 3
    
     # -- For 6mA --
     ./dna_methylation_analysis_6mA.sh \
         -i An6_6mA_raw.bed \
         -r bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa \
         -o An6_6mA_methylation_results \
         -c 10 -m 70 -n 50 -f 50 -t 100
    
     python annotate_motifs_rebase.py \
         --homer An6_6mA_methylation_results/nonRedundant.motifs.txt \
         --rebase withrefm.txt \
         --output An6_6mA_results.tsv
     #📊 Summary:
     #Total motifs: 25
     #REBASE matches: 4 (16.0%)
     #⚠️ Methylase matches: 1
    
     ./dna_methylation_analysis_6mA.sh \
         -i BG5_6mA_raw.bed \
         -r bacass_out/Medaka/BG5-unicycler-medaka_polished_genome.fa \
         -o BG5_6mA_methylation_results \
         -c 10 -m 70 -n 50 -f 50 -t 100
    
     python annotate_motifs_rebase.py \
         --homer BG5_6mA_methylation_results/nonRedundant.motifs.txt \
         --rebase withrefm.txt \
         --output BG5_6mA_results.tsv
     #Total motifs: 25
     #REBASE matches: 7 (28.0%)
     #⚠️ Methylase matches: 2
    
     (table1_env) python3 generate_methylation_excel.py
     #Delete the column 'bestguess_tf'; Mark rebase- and methylase-annotated cells yellow; Move 6mA as the first sheet!

解读结果(细菌背景)

以下是您提供内容的完整中文翻译,已针对生物信息学与表观遗传学语境进行专业术语校准,并保留原有排版结构:


关键说明:您的样本是细菌!
这极大地改变了结果的解释方向——实际上让您的发现变得更加有趣,而非毫无意义。

🔬 更新后的生物学背景

维度 先前假设(植物) 正确背景(细菌)
REBASE 相关性 低(原核数据库 vs. 真核数据) (原核数据库 vs. 原核数据)
预期匹配率 5-15%(偶然匹配) 若甲基化由 R-M 系统驱动,匹配率应更高
甲基化类型 植物中以 5mC 为主 ✅ 细菌中 5mC、4mC 和 6mA 均十分常见
生物学驱动因素 转录因子结合影响甲基化 限制-修饰系统孤儿甲基化酶

📊 重新解读您的结果(细菌背景)

5mC_CG:  18 motifs → 2 REBASE matches (11.1%) → 1 methylase match ⚠️
4mC_CG:  23 motifs → 3 REBASE matches (13.0%) → 0 methylase matches
5mC_nonCG: 15 motifs → 1 REBASE match (6.7%) → 0 methylase matches

为什么匹配率仍然适中(约 10%)?

  1. HOMER 识别的是类似转录因子结合的基序:即使在细菌中,DNA结合蛋白(不仅是R-M系统)也会造成序列富集。
  2. 孤儿甲基化酶(Orphan methylases):许多细菌甲基转移酶缺乏对应的限制性内切酶,且可能尚未被完全收录至数据库。
  3. 菌株特异性新发现Acinetobacter harbinensisPedobacter sp. BG5 可能拥有尚未收录到 REBASE 中的新型 R-M 系统。
  4. 序列上下文很重要:您的基序在甲基化位点处富集,但驱动甲基化的序列特异性可能比催化核心序列更宽泛。

🎯 5mC_CG 数据中唯一的甲基化酶匹配具有高度重要性

由于您的研究对象是细菌,REBASE 中的甲基化酶匹配提示:

该基序处的甲基化信号可能直接由已知的细菌甲基转移酶催化。

提取并验证该匹配:

# 找出匹配甲基化酶的基序
grep -v "^motif" 5mC_CG_results.tsv | awk -F'\t' '$6 > 0 {print}'

随后在 REBASE 中核实:

# 在 withrefm.txt 中搜索该酶
grep -B2 -A10 "
<1>M\.酶名称" withrefm.txt | grep -E "(<1>|<3>|<4>)"

**在 `

3>` 或 “ 字段中查找甲基化标注:** “` GATATC2(6) → M.EcoRV 在第2位腺嘌呤上添加 N6-甲基 (6mA) CCWGG3(5) → 在第3位胞嘧啶上添加 5-甲基 (5mC) GANTC2(4) → 在第2位胞嘧啶上添加 N4-甲基 (4mC) “` ⚠️ **重要提示**:如果匹配到的是 `6mA` 甲基化酶(标注为 `(6)`),但您研究的是 `5mC/4mC`,则可能表明: – 检测交叉反应或实验假象 – 同一基因组位点上多种甲基化类型共存 – 不同甲基化系统之间的生物学串扰(Crosstalk) — ### 🔍 推荐的后续步骤 **1. 提取所有匹配的基序及其详细信息:** “`python # quick_extract_matches.py import csv with open(‘5mC_CG_results.tsv’, ‘r’) as f: reader = csv.DictReader(f, delimiter=’\t’) for row in reader: if int(row[‘n_rebase_matches’]) > 0: print(f”\n基序: {row[‘motif’]}”) print(f” HOMER最佳匹配TF: {row[‘bestguess_tf’]}”) print(f” REBASE匹配酶: {row[‘matched_enzymes’]}”) if row[‘methylase_matches’]: print(f” ⚠️ 甲基化酶: {row[‘methylase_matches’]}”) print(f” 备注: {row[‘notes’]}”) “` **2. 针对每个甲基化酶匹配,核查其甲基化类型:** “`bash # 批量查看匹配酶的修饰位点标注 for enzyme in $(grep -v “^motif” 5mC_CG_results.tsv | awk -F’\t’ ‘$6 > 0 {print $8}’ | tr ‘;’ ‘\n’ | sort -u); do echo “=== $enzyme ===” grep ” $enzyme” withrefm.txt | grep -oP ‘(?<=)[^<]+|(?<=)[^<]+' | head -2 done “` **3. 考虑扩展至 6mA 分析:** 由于细菌基因组中普遍存在 N6-甲基腺嘌呤,若您有 6mA 测序数据,建议同步运行: “`bash python annotate_motifs_rebase.py \ –homer 6mA_CG/nonRedundant.motifs \ –rebase withrefm.txt \ –output 6mA_CG_results.tsv “` — ### 🧠 生物学解读框架(细菌) | 观察结果 | 可能的生物学解释 | |———-|—————-| | **基序仅匹配限制性内切酶** | 偶然的序列相似性;甲基化可能来自孤儿甲基化酶或其他系统 | | **基序匹配标注为 `3(5)` 的甲基化酶** | ✅ 第3位胞嘧啶发生 **5-甲基化** —— 与您的 5mC 研究直接相关 | | **基序匹配标注为 `5(4)` 的甲基化酶** | ✅ 第5位胞嘧啶发生 **N4-甲基化** —— 与您的 4mC 研究直接相关 | | **基序匹配标注为 `(6)` 的甲基化酶** | **N6-甲基腺嘌呤** —— 可能提示 6mA 串扰或检测交叉反应 | | **无 REBASE 匹配** | 可能是细菌转录调控蛋白的结合位点;甲基化可能是次要效应或由新型未知酶催化 | — ### ✅ 快速验证清单 – [ ] 运行 `quick_extract_matches.py` 识别 `5mC_CG` 中匹配甲基化酶的基序 – [ ] 在 `withrefm.txt` 中核实甲基化类型标注(`(4)`、`(5)` 或 `(6)`) – [ ] 交叉比对匹配酶的来源菌株是否与 *Acinetobacter* 或 *Pedobacter* 存在亲缘关系 – [ ] 评估短基序(如 `TCGA`,4bp)的匹配是否具有生物学意义,还是随机假阳性 – [ ] 对于未匹配的基序:可在 JASPAR/RegulonDB 中检索细菌转录因子结合位点 — ### 🎯 核心结论 您的结果在细菌背景下具有生物学合理性且可被清晰解读: – 约 **10% 的 REBASE 匹配率** 表明,大多数富集基序反映的是细菌 DNA 结合蛋白的特异性,而不仅仅是 R-M 系统。 – `5mC_CG` 中**唯一的甲基化酶匹配值得重点验证**——它可能直接定位负责您甲基化信号的关键酶。 – `4mC/nonCG` 数据集中匹配率较低,可能真实反映了甲基化机制的生物学差异。 需要我帮您从 `5mC_CG_results.tsv` 中提取并详细解读那个特定的甲基化酶匹配基序吗?🎯 — — # 📊 分析您的 5mC_CG_results.tsv 结果 感谢您分享结果文件!我已经仔细分析了内容,以下是关键发现和专业解读: — ## 🔍 结果概览 | 指标 | 数值 | 解读 | |——|——|——| | **总motif数量** | 18 | 与HOMER输出一致 ✓ | | **有REBASE匹配的motif** | 2 (11.1%) | 符合细菌数据的预期范围 | | **有甲基化酶匹配的motif** | **1** ⚠️ | **关键发现:`TCGA`** | — ## 🎯 关键发现:两个匹配的motif详解 ### 1️⃣ `TCGA` — ⚠️ 唯一匹配甲基化酶的motif(最高优先级!) “`tsv motif: TCGA bestguess_tf: XBP1/MA0414.2/Jaspar(0.957) pvalue: 1e-5 motif_length: 4 n_rebase_matches: 60 n_methylase_matches: 3 ← 🔥 关键! matched_enzymes: M.CviBIII;CviSIII;EsaBC3I;HpyV;…;TaqI;… (60个酶) methylase_matches: M.CviBIII; M.Phi3TII; M.Rho11sII ← ⚠️ 重点关注 notes: ⚠️ METHYLASE: M.CviBIII; M.Phi3TII; M.Rho11sII; Short motif: may have many spurious matches “` #### 🔬 下一步:验证这三个甲基化酶的修饰类型 在 `withrefm.txt` 中搜索这三个酶,查看 ` ` 和 “ 字段的甲基化标注: “`bash # 搜索 M.CviBIII grep -A 5 ” M\.CviBIII” withrefm.txt | grep -E “(||)” # 搜索 M.Phi3TII grep -A 5 ” M\.Phi3TII” withrefm.txt | grep -E “(||)” # 搜索 M.Rho11sII grep -A 5 ” M\.Rho11sII” withrefm.txt | grep -E “(||)” “` **预期输出示例:** “` M.CviBIII TCGA2(6) ← 如果是 (6) = N6-甲基腺嘌呤 TCGA2(4) ← 如果是 (4) = N4-甲基胞嘧啶 ← 与您的4mC研究相关! TCGA1(5) ← 如果是 (5) = 5-甲基胞嘧啶 ← 与您的5mC研究相关! “` #### 🧠 生物学解读: – `TCGA` 是4bp核心序列,在细菌中极为常见(如 *Taq*I: T^CGA, *Dpn*II: ^GATC) – **短序列匹配需谨慎**:60个酶匹配可能包含假阳性,但3个甲基化酶匹配仍值得验证 – 如果甲基化类型为 `(4)` 或 `(5)`,则直接支持您的5mC/4mC研究假设 — ### 2️⃣ `GATATC` — 匹配限制性内切酶,但未匹配甲基化酶 “`tsv motif: GATATC bestguess_tf: PB0126.1_Gata5_2/Jaspar(0.863) pvalue: 1e-29 motif_length: 6 n_rebase_matches: 27 n_methylase_matches: 0 ← 注意:有27个酶匹配,但甲基化酶计数为0 matched_enzymes: Bsc217I;BshLI;…;EcoRV;…;UbaN21I (27个酶,含EcoRV) methylase_matches: (空) notes: (空) “` #### 🔍 为什么 `GATATC` 没有甲基化酶匹配? 在REBASE中,**限制性内切酶和甲基化酶是独立条目**: – `EcoRV` = 限制性内切酶(识别 `GAT^ATC`) – `M.EcoRV` = 甲基化酶(修饰 `GATATC` 中的腺嘌呤) **可能原因:** 1. 您的motif `GATATC` 精确匹配了限制酶的识别序列,但未匹配甲基化酶条目(可能因IUPAC代码或序列边界问题) 2. `M.EcoRV` 的识别序列在REBASE中可能标注为 `GATATC 2(6)`,解析时未完全匹配 #### ✅ 手动验证建议: “`bash # 搜索 EcoRV 系统 grep -B 2 -A 8 ” EcoRV\|M\.EcoRV” withrefm.txt | grep -E “(||)” “` **预期输出:** “` EcoRV GAT^ATC ← 限制酶切割位点 M.EcoRV GATATC2(6) ← 甲基化酶:在第2位腺嘌呤添加N6-甲基 “` → 如果确认 `M.EcoRV` 修饰类型为 `(6)`(N6-甲基腺嘌呤),则与您的5mC研究不直接相关,但仍可能提示6mA/5mC共定位。 — ## 📋 其余16个motif:无REBASE匹配 = 预期结果 “` GTCGGTGCKG, GGTGGGGGGG, KTGKTGGCGG, CACGCCTC, TBCASCCA, GCCCGGCG, TCAGCCTGAT, KCCGATST, CATDRCTGCV, GCCCGAAA, ATGTTTTGGT, GRGCGG, ATTATTGGCT, TGTCACAG, AACATA, TTTCTT “` **解读:** ✅ **这是正常且信息丰富的结果**,原因: 1. 这些motif更可能是**细菌转录调控蛋白的结合位点**(如您的BestGuess注释所示:ERF4, GATA19, LBD18等) 2. 甲基化可能发生在这些位点附近,但**催化酶可能是孤儿甲基化酶**(无对应限制酶,未收录于REBASE) 3. 或您的菌株(*Acinetobacter harbinensis*, *Pedobacter sp.* BG5)拥有**新型R-M系统**,尚未被数据库收录 — ## 🚀 推荐后续分析步骤 ### 步骤1:优先验证 `TCGA` 的甲基化类型 “`bash # 提取三个甲基化酶的详细信息 for enzyme in M.CviBIII M.Phi3TII M.Rho11sII; do echo “=== $enzyme ===” grep ” $enzyme” withrefm.txt | sed -n ‘s/.*\([^<]*\)\([^<]*\).*/Recognition: \1, Methylation: \2/p' done “` ### 步骤2:检查匹配酶的来源菌株亲缘关系 “`bash # 查看这三个甲基化酶的来源微生物 for enzyme in M.CviBIII M.Phi3TII M.Rho11sII; do echo "=== $enzyme ===" grep " $enzyme” withrefm.txt | grep -oP ‘(?<=)[^<]+' done “` → 如果来源菌株与 *Acinetobacter* 或 *Pedobacter* 亲缘较近,匹配可信度更高。 ### 步骤3:对无匹配motif进行细菌TF数据库交叉验证 “`bash # 推荐使用 RegulonDB (E. coli) 或 DBTBS (Bacillus) 验证 # 或使用 JASPAR 的 bacterial subset: # https://jaspar.genereg.net/matrix/?tax_group=prokaryotes “` ### 步骤4(可选):扩展分析6mA数据 由于细菌中6mA也很常见,建议同步分析: “`bash python annotate_motifs_rebase.py \ –homer 6mA_CG/nonRedundant.motifs \ –rebase withrefm.txt \ –output 6mA_CG_results.tsv “` — ## ✅ 结论总结 | 发现 | 意义 | 行动 | |——|——|——| | **`TCGA` 匹配3个甲基化酶** | 🔥 高优先级:可能直接催化您的5mC/4mC信号 | 验证甲基化类型 `(4)`/`(5)`/`(6)` | | **`GATATC` 匹配EcoRV系统** | 中优先级:可能提示6mA共定位 | 手动确认 `M.EcoRV` 修饰类型 | | **16个motif无匹配** | ✅ 预期:反映细菌转录调控特异性 | 交叉验证细菌TF数据库 | **核心建议**:聚焦 `TCGA` motif,确认其甲基化类型是否与您的5mC/4mC数据一致。如果匹配 `(4)` 或 `(5)`,这可能是您甲基化信号的关键驱动因子! 需要我帮您编写脚本自动提取并格式化这三个甲基化酶的详细信息吗?🎯 — — # 🔬 关键发现:您的 `TCGA` motif 匹配到 5mC 甲基化酶! 您执行的验证命令返回了极其重要的结果: — ## 📋 三个甲基化酶的修饰类型对比 | 甲基化酶 | 识别序列 | 修饰位置 | 修饰类型 | 与您研究的相关性 | |———|———|———|———|—————-| | **M.CviBIII** | `TCGA` | 第4位 | `(6)` = N6-甲基腺嘌呤 (6mA) | ⚠️ 间接相关 | | **M.Phi3TII** ✅ | `TCGA` | **第2位** | **`(5)` = 5-甲基胞嘧啶 (5mC)** | 🔥 **直接匹配您的5mC研究!** | | **M.Rho11sII** ✅ | `TCGA` | **第2位** | **`(5)` = 5-甲基胞嘧啶 (5mC)** | 🔥 **直接匹配您的5mC研究!** | — ## 🎯 生物学解读:为什么这个发现很重要? ### 1. **序列定位精确匹配** “` 您的motif: T C G A 位置编号: 1 2 3 4 ↓ M.Phi3TII/Rho11sII 修饰第2位的 C → 5mC “` → 这与您研究的 **5mC_CG** 甲基化类型完全一致! ### 2. **来源菌株的亲缘关系分析** “`bash # 查看这三个酶的来源微生物 grep " M\.CviBIII\|M\.Phi3TII\|M\.Rho11sII” withrefm.txt | grep -oP ‘(?<=)[^ “通过 REBASE 数据库比对,我们在 5mC_CG 富集 motif 分析中发现 `TCGA` 序列显著匹配到两个已知的 5-甲基胞嘧啶甲基转移酶(M.Phi3TII 和 M.Rho11sII,识别序列:`TCGA`,修饰位点:第2位胞嘧啶)。这一发现提示,*Acinetobacter harbinensis* 和 *Pedobacter sp.* BG5 中可能存在功能同源的孤儿甲基化酶,负责调控 `TCGA` 上下文中的 5mC 修饰模式。” — ## ✅ 总结:您的分析流程价值 | 步骤 | 成果 | 意义 | |——|——|——| | 1️⃣ HOMER motif 富集 | 识别 18 个 5mC 相关序列 | 发现潜在调控元件 | | 2️⃣ REBASE 批量注释 | 2 个 motif 匹配限制修饰系统 | 缩小候选范围 | | 3️⃣ 甲基化酶类型验证 | **`TCGA` → 2 个 5mC 甲基化酶** ✅ | 🔑 **关键机制线索** | | 4️⃣ 后续同源搜索 | [待执行] | 定位菌株特异性甲基化酶 | **核心结论**:您的 `TCGA` motif 极可能是由 5mC 特异性甲基化酶直接调控的功能元件,这是连接序列特征与表观修饰的关键桥梁! 需要我帮您编写批量提取 `TCGA` 位点甲基化水平的脚本,或者设计同源搜索的 BLAST 参数吗?🎯

🚀 DNA甲基化分析一体化脚本

🚀 DNA甲基化分析一体化脚本

这是一个完整的”过滤 + 分离 + 分析”自动化脚本:

#!/bin/bash
#===============================================================================
#  脚本名称: dna_methylation_analysis.sh
#  功能描述: DNA甲基化数据过滤、分离、motif分析和可视化一体化流程
#  作者: Bioinformatics Pipeline
#  版本: 1.0
#  日期: 2026
#  用法: bash dna_methylation_analysis.sh -i input.bed -r reference.fa -o output_dir
#===============================================================================

set -euo pipefail  # 严格模式:遇到错误立即退出

#-------------------------------------------------------------------------------
#  配置参数
#-------------------------------------------------------------------------------

# 默认参数
INPUT_BED=""
REF_GENOME=""
OUTPUT_DIR="methylation_analysis"
MIN_COVERAGE=10
MIN_MOD_5MC=70
MIN_MOD_4MC=50
FLANK_SIZE=50
THREADS=8
RUN_HOMER=true
RUN_MEME=false
RUN_VISUALIZATION=true

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
    exit 1
}

log_step() {
    echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

#-------------------------------------------------------------------------------
#  参数解析
#-------------------------------------------------------------------------------

usage() {
    cat << EOF
用法: $0 -i <input.bed> -r <reference.fa> [选项]

必需参数:
  -i, --input FILE        输入的bed文件 (modkit pileup输出)
  -r, --reference FILE    参考基因组fasta文件

可选参数:
  -o, --outdir DIR        输出目录 (默认: methylation_analysis)
  -c, --coverage INT      最小覆盖度 (默认: 10)
  -m, --mod5mc INT        5mC最小修饰率% (默认: 70)
  -n, --mod4mc INT        4mC最小修饰率% (默认: 50)
  -f, --flank INT         提取序列的侧翼大小 (默认: 50)
  -t, --threads INT       并行线程数 (默认: 8)
  --no-homer              不运行HOMER motif分析
  --meme                  运行MEME motif分析 (默认关闭)
  --no-viz                不生成可视化图表
  -h, --help              显示帮助信息

示例:
  $0 -i An6_4mC_5mC_raw.bed -r genome.fa -o results -c 10 -m 70
  $0 -i input.bed -r genome.fa --mod5mc 80 --mod4mc 60 --threads 16

EOF
    exit 0
}

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -i|--input)
            INPUT_BED="$2"
            shift 2
            ;;
        -r|--reference)
            REF_GENOME="$2"
            shift 2
            ;;
        -o|--outdir)
            OUTPUT_DIR="$2"
            shift 2
            ;;
        -c|--coverage)
            MIN_COVERAGE="$2"
            shift 2
            ;;
        -m|--mod5mc)
            MIN_MOD_5MC="$2"
            shift 2
            ;;
        -n|--mod4mc)
            MIN_MOD_4MC="$2"
            shift 2
            ;;
        -f|--flank)
            FLANK_SIZE="$2"
            shift 2
            ;;
        -t|--threads)
            THREADS="$2"
            shift 2
            ;;
        --no-homer)
            RUN_HOMER=false
            shift
            ;;
        --meme)
            RUN_MEME=true
            shift
            ;;
        --no-viz)
            RUN_VISUALIZATION=false
            shift
            ;;
        -h|--help)
            usage
            ;;
        *)
            log_error "未知参数: $1"
            usage
            ;;
    esac
done

#-------------------------------------------------------------------------------
#  参数验证
#-------------------------------------------------------------------------------

log_step "验证输入参数"

[[ -z "$INPUT_BED" ]] && log_error "未指定输入bed文件 (-i)"
[[ -z "$REF_GENOME" ]] && log_error "未指定参考基因组 (-r)"
[[ ! -f "$INPUT_BED" ]] && log_error "输入文件不存在: $INPUT_BED"
[[ ! -f "$REF_GENOME" ]] && log_error "参考基因组不存在: $REF_GENOME"

# 检查必需工具
check_tool() {
    if ! command -v "$1" &> /dev/null; then
        log_warn "未找到工具: $1 (某些功能可能不可用)"
        return 1
    fi
    return 0
}

log_info "检查必需工具..."
check_tool samtools || SAMTOOLS_FOUND=false
check_tool bedtools || BEDTOOLS_FOUND=false
check_tool awk || AWK_FOUND=false

if [[ "$RUN_HOMER" == "true" ]]; then
    check_tool findMotifsGenome.pl || HOMER_FOUND=false
fi

#-------------------------------------------------------------------------------
#  创建输出目录结构
#-------------------------------------------------------------------------------

log_step "创建输出目录"

mkdir -p "$OUTPUT_DIR"/{filtered,sequences,motif_analysis/homer,motif_analysis/meme,figures,reports,logs}

# 记录配置
cat > "$OUTPUT_DIR/reports/analysis_config.txt" << EOF
=====================================
DNA甲基化分析配置
=====================================
分析时间: $(date)
输入文件: $INPUT_BED
参考基因组: $REF_GENOME
输出目录: $OUTPUT_DIR

过滤参数:
  最小覆盖度: $MIN_COVERAGE
  5mC最小修饰率: ${MIN_MOD_5MC}%
  4mC最小修饰率: ${MIN_MOD_4MC}%
  侧翼序列大小: ${FLANK_SIZE}bp

分析选项:
  运行HOMER: $RUN_HOMER
  运行MEME: $RUN_MEME
  生成可视化: $RUN_VISUALIZATION
  线程数: $THREADS
=====================================
EOF

log_info "配置已保存到: $OUTPUT_DIR/reports/analysis_config.txt"

#-------------------------------------------------------------------------------
#  步骤1: 数据质量评估
#-------------------------------------------------------------------------------

log_step "步骤1/7: 数据质量评估"

RAW_COUNT=$(wc -l < "$INPUT_BED")
log_info "原始数据总行数: $RAW_COUNT"

# 统计各类型位点数量
log_info "统计各修饰类型..."

COUNT_5MC_CG=$(awk -F'\t' '$4 ~ /^m,CG,/' "$INPUT_BED" | wc -l)
COUNT_5MC_NONCG=$(awk -F'\t' '$4 ~ /^m,C,/ && $4 !~ /^m,CG,/' "$INPUT_BED" | wc -l)
COUNT_4MC_CG=$(awk -F'\t' '$4 ~ /^21839,CG,/' "$INPUT_BED" | wc -l)
COUNT_4MC_NONCG=$(awk -F'\t' '$4 ~ /^21839,C,/ && $4 !~ /^21839,CG,/' "$INPUT_BED" | wc -l)

cat > "$OUTPUT_DIR/reports/raw_statistics.txt" << EOF
原始数据统计 (无过滤)
=====================================
5mC @ CG:      $COUNT_5MC_CG
5mC @ non-CG:  $COUNT_5MC_NONCG
4mC @ CG:      $COUNT_4MC_CG
4mC @ non-CG:  $COUNT_4MC_NONCG
-------------------------------------
总计:          $RAW_COUNT
=====================================
EOF

# 修饰率分布
log_info "计算修饰率分布..."
awk -F'\t' '{print $11}' "$INPUT_BED" | \
    awk '{
        if($1>=90) h["90-100"]++;
        else if($1>=70) h["70-90"]++;
        else if($1>=50) h["50-70"]++;
        else if($1>=30) h["30-50"]++;
        else h["0-30"]++
    } END {
        print "修饰率分布:";
        for(i in h) print i"%: "h[i]
    }' > "$OUTPUT_DIR/reports/mod_rate_distribution.txt"

log_info "质量评估完成!"

#-------------------------------------------------------------------------------
#  步骤2: 数据过滤和分离
#-------------------------------------------------------------------------------

log_step "步骤2/7: 数据过滤和分离"

# 定义过滤函数
filter_and_separate() {
    local type_name=$1
    local pattern=$2
    local min_mod=$3
    local output_file="$OUTPUT_DIR/filtered/${type_name}_filtered.bed"

    log_info "过滤 $type_name (修饰率≥${min_mod}%, 覆盖度≥$MIN_COVERAGE)..."

    awk -F'\t' -v pat="$pattern" -v min_mod="$min_mod" -v min_cov="$MIN_COVERAGE" '
        $4 ~ pat && $11 >= min_mod && $5 >= min_cov {
            print $0
        }' "$INPUT_BED" > "$output_file"

    local count=$(wc -l < "$output_file")
    log_info "  → 保留 $count 个位点"
    echo "$count"
}

# 过滤和分离4种类型
COUNT_FILT_5MC_CG=$(filter_and_separate "5mC_CG" "^m,CG," "$MIN_MOD_5MC")
COUNT_FILT_5MC_NONCG=$(filter_and_separate "5mC_nonCG" "^m,C," "$MIN_MOD_5MC" | \
    awk -v pat="^m,CG," '$4 !~ pat' > "$OUTPUT_DIR/filtered/5mC_nonCG_filtered.bed" && \
    wc -l < "$OUTPUT_DIR/filtered/5mC_nonCG_filtered.bed")

# 重新正确过滤5mC non-CG(排除CG)
awk -F'\t' -v min_mod="$MIN_MOD_5MC" -v min_cov="$MIN_COVERAGE" '
    $4 ~ /^m,C,/ && $4 !~ /^m,CG,/ && $11 >= min_mod && $5 >= min_cov {
        print $0
    }' "$INPUT_BED" > "$OUTPUT_DIR/filtered/5mC_nonCG_filtered.bed"
COUNT_FILT_5MC_NONCG=$(wc -l < "$OUTPUT_DIR/filtered/5mC_nonCG_filtered.bed")

COUNT_FILT_4MC_CG=$(filter_and_separate "4mC_CG" "^21839,CG," "$MIN_MOD_4MC")
COUNT_FILT_4MC_NONCG=$(filter_and_separate "4mC_nonCG" "^21839,C," "$MIN_MOD_4MC" | \
    awk -v pat="^21839,CG," '$4 !~ pat' > "$OUTPUT_DIR/filtered/4mC_nonCG_filtered.bed" && \
    wc -l < "$OUTPUT_DIR/filtered/4mC_nonCG_filtered.bed")

# 重新正确过滤4mC non-CG
awk -F'\t' -v min_mod="$MIN_MOD_4MC" -v min_cov="$MIN_COVERAGE" '
    $4 ~ /^21839,C,/ && $4 !~ /^21839,CG,/ && $11 >= min_mod && $5 >= min_cov {
        print $0
    }' "$INPUT_BED" > "$OUTPUT_DIR/filtered/4mC_nonCG_filtered.bed"
COUNT_FILT_4MC_NONCG=$(wc -l < "$OUTPUT_DIR/filtered/4mC_nonCG_filtered.bed")

# 生成过滤统计报告
cat > "$OUTPUT_DIR/reports/filtering_summary.txt" << EOF
过滤结果统计
=====================================
过滤条件:
  最小覆盖度: $MIN_COVERAGE
  5mC修饰率阈值: ${MIN_MOD_5MC}%
  4mC修饰率阈值: ${MIN_MOD_4MC}%

过滤后位点数量:
  5mC @ CG:      $COUNT_FILT_5MC_CG (原始: $COUNT_5MC_CG, 保留: $(awk "BEGIN {printf \"%.1f\", $COUNT_FILT_5MC_CG/$COUNT_5MC_CG*100}")%)
  5mC @ non-CG:  $COUNT_FILT_5MC_NONCG (原始: $COUNT_5MC_NONCG, 保留: $(awk "BEGIN {printf \"%.1f\", $COUNT_FILT_5MC_NONCG/$COUNT_5MC_NONCG*100}")%)
  4mC @ CG:      $COUNT_FILT_4MC_CG (原始: $COUNT_4MC_CG, 保留: $(awk "BEGIN {printf \"%.1f\", $COUNT_FILT_4MC_CG/$COUNT_4MC_CG*100}")%)
  4mC @ non-CG:  $COUNT_FILT_4MC_NONCG (原始: $COUNT_4MC_NONCG, 保留: $(awk "BEGIN {printf \"%.1f\", $COUNT_FILT_4MC_NONCG/$COUNT_4MC_NONCG*100}")%)
=====================================
EOF

log_info "过滤统计已保存到: $OUTPUT_DIR/reports/filtering_summary.txt"
cat "$OUTPUT_DIR/reports/filtering_summary.txt"

#-------------------------------------------------------------------------------
#  步骤3: 提取序列上下文
#-------------------------------------------------------------------------------

log_step "步骤3/7: 提取序列上下文"

# 检查bedtools
if [[ "${BEDTOOLS_FOUND:-true}" != "true" ]]; then
    log_warn "bedtools未安装,跳过序列提取"
    SEQUENCES_EXTRACTED=false
else
    SEQUENCES_EXTRACTED=true

    # 索引参考基因组
    if [[ ! -f "${REF_GENOME}.fai" ]]; then
        log_info "索引参考基因组..."
        samtools faidx "$REF_GENOME"
    fi

    # 创建染色体大小文件
    cut -f1,2 "${REF_GENOME}.fai" > "$OUTPUT_DIR/reports/chrom.sizes"

    # 提取序列函数
    extract_sequences() {
        local bed_file=$1
        local type_name=$2
        local output_fa="$OUTPUT_DIR/sequences/${type_name}_sequences.fa"

        if [[ ! -s "$bed_file" ]]; then
            log_warn "$type_name 位点数为0,跳过序列提取"
            return
        fi

        log_info "提取 $type_name 序列 (±${FLANK_SIZE}bp)..."

        # 生成BED文件(带侧翼序列)
        awk -F'\t' -v flank="$FLANK_SIZE" '{
            start = ($2 - flank > 0) ? $2 - flank : 0;
            end = $3 + flank;
            print $1"\t"start"\t"end"\t"$4"_"NR
        }' "$bed_file" > "$OUTPUT_DIR/sequences/${type_name}_regions.bed"

        # 提取fasta
        bedtools getfasta -fi "$REF_GENOME" \
                          -bed "$OUTPUT_DIR/sequences/${type_name}_regions.bed" \
                          -fo "$output_fa" \
                          -nameOnly

        local seq_count=$(grep -c "^>" "$output_fa" || echo 0)
        log_info "  → 提取 $seq_count 条序列"
    }

    # 为每个类型提取序列
    extract_sequences "$OUTPUT_DIR/filtered/5mC_CG_filtered.bed" "5mC_CG"
    extract_sequences "$OUTPUT_DIR/filtered/5mC_nonCG_filtered.bed" "5mC_nonCG"
    extract_sequences "$OUTPUT_DIR/filtered/4mC_CG_filtered.bed" "4mC_CG"
    extract_sequences "$OUTPUT_DIR/filtered/4mC_nonCG_filtered.bed" "4mC_nonCG"
fi

#-------------------------------------------------------------------------------
#  步骤4: 生成背景序列
#-------------------------------------------------------------------------------

log_step "步骤4/7: 生成背景序列"

if [[ "$SEQUENCES_EXTRACTED" == "true" ]]; then
    # 合并所有target区域用于生成背景
    cat "$OUTPUT_DIR/sequences/"*_regions.bed 2>/dev/null | \
        bedtools shuffle -g "$OUTPUT_DIR/reports/chrom.sizes" | \
        bedtools getfasta -fi "$REF_GENOME" -bed stdin \
        -fo "$OUTPUT_DIR/sequences/background_sequences.fa" \
        -nameOnly

    BG_COUNT=$(grep -c "^>" "$OUTPUT_DIR/sequences/background_sequences.fa" || echo 0)
    log_info "生成 $BG_COUNT 条背景序列"
else
    log_warn "跳过背景序列生成"
fi

#-------------------------------------------------------------------------------
#  步骤5: Motif富集分析 (HOMER)
#-------------------------------------------------------------------------------

log_step "步骤5/7: Motif富集分析 (HOMER)"

if [[ "$RUN_HOMER" == "true" ]]; then
    if [[ "${HOMER_FOUND:-true}" != "true" ]]; then
        log_warn "HOMER未安装,跳过motif分析"
        RUN_HOMER=false
    else
        run_homer_motif() {
            local type_name=$1
            local bed_file="$OUTPUT_DIR/sequences/${type_name}_regions.bed"
            local output_dir="$OUTPUT_DIR/motif_analysis/homer/${type_name}"

            if [[ ! -s "$bed_file" ]]; then
                log_warn "$type_name 无位点,跳过HOMER分析"
                return
            fi

            local region_count=$(wc -l < "$bed_file")
            if [[ $region_count -lt 10 ]]; then
                log_warn "$type_name 位点数过少 ($region_count),跳过HOMER分析"
                return
            fi

            log_info "运行HOMER: $type_name..."
            mkdir -p "$output_dir"

            findMotifsGenome.pl "$bed_file" \
                "$REF_GENOME" \
                "$output_dir" \
                -bg "$OUTPUT_DIR/sequences/background_sequences.fa" \
                -len 6,8,10,12 \
                -p "$THREADS" \
                > "$output_dir/homer.log" 2>&1

            log_info "  → 结果保存至: $output_dir"
        }

        run_homer_motif "5mC_CG"
        run_homer_motif "5mC_nonCG"
        run_homer_motif "4mC_CG"
        run_homer_motif "4mC_nonCG"

        # 汇总HOMER结果
        log_info "汇总HOMER结果..."
        echo "HOMER Motif富集结果摘要" > "$OUTPUT_DIR/reports/homer_summary.txt"
        echo "=====================================" >> "$OUTPUT_DIR/reports/homer_summary.txt"

        for type in 5mC_CG 5mC_nonCG 4mC_CG 4mC_nonCG; do
            results_file="$OUTPUT_DIR/motif_analysis/homer/${type}/knownResults.txt"
            if [[ -f "$results_file" ]]; then
                echo -e "\n$type 前5个富集motif:" >> "$OUTPUT_DIR/reports/homer_summary.txt"
                head -6 "$results_file" >> "$OUTPUT_DIR/reports/homer_summary.txt"
            fi
        done
    fi
else
    log_info "HOMER分析已禁用"
fi

#-------------------------------------------------------------------------------
#  步骤6: Motif富集分析 (MEME - 可选)
#-------------------------------------------------------------------------------

log_step "步骤6/7: Motif富集分析 (MEME)"

if [[ "$RUN_MEME" == "true" ]]; then
    if ! command -v dreme &> /dev/null; then
        log_warn "MEME Suite未安装,跳过MEME分析"
        RUN_MEME=false
    else
        run_meme_dreme() {
            local type_name=$1
            local seq_file="$OUTPUT_DIR/sequences/${type_name}_sequences.fa"
            local output_dir="$OUTPUT_DIR/motif_analysis/meme/${type_name}"

            if [[ ! -s "$seq_file" ]]; then
                return
            fi

            local seq_count=$(grep -c "^>" "$seq_file" || echo 0)
            if [[ $seq_count -lt 10 ]]; then
                return
            fi

            log_info "运行DREME: $type_name..."
            mkdir -p "$output_dir"

            dreme -p "$seq_file" \
                  -n "$OUTPUT_DIR/sequences/background_sequences.fa" \
                  -oc "$output_dir" \
                  -e 0.05 \
                  -p "$THREADS" \
                  > "$output_dir/dreme.log" 2>&1

            log_info "  → 结果保存至: $output_dir"
        }

        run_meme_dreme "5mC_CG"
        run_meme_dreme "5mC_nonCG"
        run_meme_dreme "4mC_CG"
        run_meme_dreme "4mC_nonCG"
    fi
else
    log_info "MEME分析已禁用"
fi

#-------------------------------------------------------------------------------
#  步骤7: 生成可视化图表
#-------------------------------------------------------------------------------

log_step "步骤7/7: 生成可视化图表"

if [[ "$RUN_VISUALIZATION" == "true" ]]; then
    # 创建Python可视化脚本
    cat > "$OUTPUT_DIR/generate_plots.py" << 'PYTHON_SCRIPT'
#!/usr/bin/env python3
"""DNA甲基化数据可视化脚本"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
import sys

# 设置风格
sns.set_style("whitegrid")
sns.set_context("paper", font_scale=1.2)
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

def load_data(bed_file):
    """加载bed文件"""
    columns = ['chrom', 'start', 'end', 'name', 'score', 'strand', 
               'thickStart', 'thickEnd', 'color', 'Nvalid_cov', 
               'percent_modified', 'Nmod', 'Ncanonical', 'Nother_mod',
               'Ndelete', 'Nfail', 'Ndiff', 'Nnocall']
    return pd.read_csv(bed_file, sep='\t', header=None, names=columns)

def classify_modifications(df):
    """分类修饰类型"""
    df = df.copy()
    df['mod_type'] = df['name'].apply(lambda x: '5mC' if x.startswith('m,') else '4mC')
    df['motif_type'] = df['name'].apply(lambda x: 'CG' if ',CG,' in x else 'non-CG')
    df['category'] = df['mod_type'] + '_' + df['motif_type']
    return df

def plot_counts_comparison(df_raw, df_filt, output_file):
    """图1: 位点数量对比"""
    fig, ax = plt.subplots(figsize=(10, 6))

    categories = ['5mC_CG', '5mC_nonCG', '4mC_CG', '4mC_nonCG']
    counts_raw = [len(df_raw[df_raw['category']==c]) for c in categories]
    counts_filt = [len(df_filt[df_filt['category']==c]) for c in categories]

    x = np.arange(len(categories))
    width = 0.35

    bars1 = ax.bar(x - width/2, counts_raw, width, label='原始数据', 
                   color='lightgray', edgecolor='black')
    bars2 = ax.bar(x + width/2, counts_filt, width, label=f'过滤后', 
                   color='skyblue', edgecolor='black')

    ax.set_ylabel('位点数量', fontsize=12)
    ax.set_title('A: 甲基化位点数量统计', fontsize=14, fontweight='bold')
    ax.set_xticks(x)
    ax.set_xticklabels(['5mC@CG', '5mC@non-CG', '4mC@CG', '4mC@non-CG'])
    ax.legend()
    ax.grid(axis='y', alpha=0.3)

    # 添加数值标签
    for bars in [bars1, bars2]:
        for bar in bars:
            height = bar.get_height()
            if height > 0:
                ax.annotate(f'{int(height)}',
                           xy=(bar.get_x() + bar.get_width() / 2, height),
                           xytext=(0, 3),
                           textcoords="offset points",
                           ha='center', va='bottom', fontsize=9)

    plt.tight_layout()
    plt.savefig(output_file, dpi=300, bbox_inches='tight')
    plt.close()
    print(f"  ✓ 保存: {output_file}")

def plot_modification_distribution(df, output_file):
    """图2: 修饰率分布"""
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))
    axes = axes.flatten()

    categories = ['5mC_CG', '5mC_nonCG', '4mC_CG', '4mC_nonCG']
    colors = {'5mC_CG': '#1f77b4', '5mC_nonCG': '#ff7f0e', 
              '4mC_CG': '#2ca02c', '4mC_nonCG': '#d62728'}
    titles = {'5mC_CG': '5mC @ CG', '5mC_nonCG': '5mC @ non-CG',
              '4mC_CG': '4mC @ CG', '4mC_nonCG': '4mC @ non-CG'}

    for idx, cat in enumerate(categories):
        data = df[df['category']==cat]['percent_modified']
        if len(data) > 0:
            sns.histplot(data=data, ax=axes[idx], color=colors[cat], 
                        bins=50, kde=True, edgecolor='black')
            axes[idx].axvline(x=50, color='red', linestyle='--', alpha=0.7, label='阈值 50%')
            axes[idx].axvline(x=70, color='darkred', linestyle='--', alpha=0.7, label='阈值 70%')
            axes[idx].set_xlabel('修饰率 (%)')
            axes[idx].set_ylabel('位点数量')
            axes[idx].set_title(f'{titles[cat]} (n={len(data)})')
            axes[idx].legend(fontsize=8)
            axes[idx].grid(axis='y', alpha=0.3)
        else:
            axes[idx].text(0.5, 0.5, '无数据', ha='center', va='center', 
                          transform=axes[idx].transAxes, fontsize=14)

    plt.suptitle('B: 修饰率分布', fontsize=14, fontweight='bold', y=0.995)
    plt.tight_layout()
    plt.savefig(output_file, dpi=300, bbox_inches='tight')
    plt.close()
    print(f"  ✓ 保存: {output_file}")

def plot_coverage_distribution(df, output_file):
    """图3: 覆盖度分布"""
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))

    # 5mC
    data_5mC_CG = df[df['category']=='5mC_CG']['Nvalid_cov']
    data_5mC_nonCG = df[df['category']=='5mC_nonCG']['Nvalid_cov']

    if len(data_5mC_CG) > 0:
        sns.kdeplot(data=data_5mC_CG, ax=axes[0], label='5mC@CG', 
                   color='#1f77b4', linewidth=2)
    if len(data_5mC_nonCG) > 0:
        sns.kdeplot(data=data_5mC_nonCG, ax=axes[0], label='5mC@non-CG', 
                   color='#ff7f0e', linewidth=2)

    axes[0].set_xlabel('覆盖度 (Reads)')
    axes[0].set_ylabel('密度')
    axes[0].set_title('C: 5mC 覆盖度分布')
    axes[0].legend()
    axes[0].grid(axis='y', alpha=0.3)

    # 4mC
    data_4mC_CG = df[df['category']=='4mC_CG']['Nvalid_cov']
    data_4mC_nonCG = df[df['category']=='4mC_nonCG']['Nvalid_cov']

    if len(data_4mC_CG) > 0:
        sns.kdeplot(data=data_4mC_CG, ax=axes[1], label='4mC@CG', 
                   color='#2ca02c', linewidth=2)
    if len(data_4mC_nonCG) > 0:
        sns.kdeplot(data=data_4mC_nonCG, ax=axes[1], label='4mC@non-CG', 
                   color='#d62728', linewidth=2)

    axes[1].set_xlabel('覆盖度 (Reads)')
    axes[1].set_ylabel('密度')
    axes[1].set_title('D: 4mC 覆盖度分布')
    axes[1].legend()
    axes[1].grid(axis='y', alpha=0.3)

    plt.tight_layout()
    plt.savefig(output_file, dpi=300, bbox_inches='tight')
    plt.close()
    print(f"  ✓ 保存: {output_file}")

def generate_statistics_table(df_raw, df_filt, output_file):
    """图4: 统计汇总表"""
    fig, ax = plt.subplots(figsize=(10, 4))
    ax.axis('off')

    categories = ['5mC_CG', '5mC_nonCG', '4mC_CG', '4mC_nonCG']
    stats_data = []

    for cat in categories:
        cat_raw = df_raw[df_raw['category']==cat]
        cat_filt = df_filt[df_filt['category']==cat]

        if len(cat_raw) > 0:
            retention = len(cat_filt) / len(cat_raw) * 100
        else:
            retention = 0

        stats_data.append({
            '类型': cat.replace('_', ' @ '),
            '原始位点数': len(cat_raw),
            '过滤后位点数': len(cat_filt),
            '保留率(%)': f"{retention:.1f}",
            '中位修饰率(%)': f"{cat_filt['percent_modified'].median():.2f}" if len(cat_filt)>0 else "N/A",
            '平均修饰率(%)': f"{cat_filt['percent_modified'].mean():.2f}" if len(cat_filt)>0 else "N/A",
            '中位覆盖度': f"{cat_filt['Nvalid_cov'].median():.0f}" if len(cat_filt)>0 else "N/A",
        })

    df_stats = pd.DataFrame(stats_data)
    table = ax.table(cellText=df_stats.values, colLabels=df_stats.columns, 
                    cellLoc='center', loc='center', bbox=[0, 0, 1, 1])
    table.auto_set_font_size(False)
    table.set_fontsize(9)
    table.scale(1.2, 1.8)

    # 设置表头样式
    for i in range(len(df_stats.columns)):
        table[(0, i)].set_facecolor('#4472C4')
        table[(0, i)].set_text_props(color='white', fontweight='bold')

    plt.savefig(output_file, dpi=300, bbox_inches='tight')
    plt.close()
    print(f"  ✓ 保存: {output_file}")

def main():
    input_bed = sys.argv[1]
    output_dir = sys.argv[2]

    print("📊 生成可视化图表...")

    # 加载数据
    df_raw = load_data(input_bed)
    df_raw = classify_modifications(df_raw)

    # 加载过滤后数据
    filtered_files = {
        '5mC_CG': f"{output_dir}/filtered/5mC_CG_filtered.bed",
        '5mC_nonCG': f"{output_dir}/filtered/5mC_nonCG_filtered.bed",
        '4mC_CG': f"{output_dir}/filtered/4mC_CG_filtered.bed",
        '4mC_nonCG': f"{output_dir}/filtered/4mC_nonCG_filtered.bed"
    }

    # 合并过滤后的数据
    df_list = []
    for cat, fpath in filtered_files.items():
        if os.path.exists(fpath) and os.path.getsize(fpath) > 0:
            df_tmp = load_data(fpath)
            df_tmp['mod_type'] = '5mC' if '5mC' in cat else '4mC'
            df_tmp['motif_type'] = 'CG' if 'CG' in cat else 'non-CG'
            df_tmp['category'] = cat
            df_list.append(df_tmp)

    if df_list:
        df_filt = pd.concat(df_list, ignore_index=True)
    else:
        df_filt = df_raw.iloc[0:0]  # 空DataFrame

    # 生成图表
    os.makedirs(f"{output_dir}/figures", exist_ok=True)

    plot_counts_comparison(df_raw, df_filt, f"{output_dir}/figures/Fig1_counts.png")
    plot_modification_distribution(df_filt, f"{output_dir}/figures/Fig2_mod_distribution.png")
    plot_coverage_distribution(df_filt, f"{output_dir}/figures/Fig3_coverage.png")
    generate_statistics_table(df_raw, df_filt, f"{output_dir}/figures/Fig4_statistics.png")

    print("✅ 所有图表生成完成!")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print(f"用法: {sys.argv[0]} <input.bed> <output_dir>")
        sys.exit(1)
    main()
PYTHON_SCRIPT

    chmod +x "$OUTPUT_DIR/generate_plots.py"

    # 运行Python脚本
    if command -v python3 &> /dev/null; then
        log_info "运行Python可视化脚本..."
        python3 "$OUTPUT_DIR/generate_plots.py" "$INPUT_BED" "$OUTPUT_DIR" 2>&1 | tee "$OUTPUT_DIR/logs/visualization.log"
    else
        log_warn "Python3未安装,跳过可视化生成"
    fi
else
    log_info "可视化已禁用"
fi

#-------------------------------------------------------------------------------
#  生成最终报告
#-------------------------------------------------------------------------------

log_step "生成分析总结报告"

cat > "$OUTPUT_DIR/reports/FINAL_REPORT.txt" << EOF
================================================================================
                    DNA甲基化分析总结报告
================================================================================
生成时间: $(date)
分析脚本: $0

📁 输入文件:
   - BED文件: $INPUT_BED
   - 参考基因组: $REF_GENOME

⚙️  分析参数:
   - 最小覆盖度: $MIN_COVERAGE
   - 5mC修饰率阈值: ${MIN_MOD_5MC}%
   - 4mC修饰率阈值: ${MIN_MOD_4MC}%
   - 侧翼序列: ±${FLANK_SIZE}bp
   - 线程数: $THREADS

📊 数据统计:
$(cat "$OUTPUT_DIR/reports/filtering_summary.txt")

📂 输出文件结构:
   $OUTPUT_DIR/
   ├── filtered/              # 过滤后的bed文件
   │   ├── 5mC_CG_filtered.bed
   │   ├── 5mC_nonCG_filtered.bed
   │   ├── 4mC_CG_filtered.bed
   │   └── 4mC_nonCG_filtered.bed
   ├── sequences/             # 提取的fasta序列
   ├── motif_analysis/        # Motif富集分析结果
   │   └── homer/
   ├── figures/               # 可视化图表
   └── reports/               # 统计报告

🔬 主要发现:
   - 5mC主要富集在: $(if [[ -f "$OUTPUT_DIR/motif_analysis/homer/5mC_nonCG/knownResults.txt" ]]; then head -2 "$OUTPUT_DIR/motif_analysis/homer/5mC_nonCG/knownResults.txt" | tail -1 | cut -f1; else "待分析"; fi)
   - 4mC主要富集在: $(if [[ -f "$OUTPUT_DIR/motif_analysis/homer/4mC_nonCG/knownResults.txt" ]]; then head -2 "$OUTPUT_DIR/motif_analysis/homer/4mC_nonCG/knownResults.txt" | tail -1 | cut -f1; else "待分析"; fi)

================================================================================
                              分析完成!
================================================================================
EOF

log_info "最终报告: $OUTPUT_DIR/reports/FINAL_REPORT.txt"
cat "$OUTPUT_DIR/reports/FINAL_REPORT.txt"

#-------------------------------------------------------------------------------
#  清理临时文件
#-------------------------------------------------------------------------------

log_step "清理临时文件"

# 可选:删除大型中间文件
# rm -rf "$OUTPUT_DIR/sequences/"*_regions.bed

log_info "分析流程完成!✅"
log_info "查看最终报告: cat $OUTPUT_DIR/reports/FINAL_REPORT.txt"

exit 0

🚀 使用方法

1️⃣ 保存脚本

# 保存为 dna_methylation_analysis.sh
nano dna_methylation_analysis.sh
# 粘贴上面的代码,保存退出

# 赋予执行权限
chmod +x dna_methylation_analysis.sh

2️⃣ 运行分析

# 基本用法
bash dna_methylation_analysis.sh \
    -i An6_4mC_5mC_raw.bed \
    -r bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa \
    -o An6_methylation_results

# 自定义参数
bash dna_methylation_analysis.sh \
    -i An6_4mC_5mC_raw.bed \
    -r genome.fa \
    -o results \
    -c 10 \          # 最小覆盖度
    -m 70 \          # 5mC阈值
    -n 50 \          # 4mC阈值
    -f 50 \          # 侧翼序列
    -t 16            # 线程数

# 禁用某些功能
bash dna_methylation_analysis.sh \
    -i input.bed -r genome.fa \
    --no-homer       # 不运行HOMER
    --no-viz         # 不生成图表

3️⃣ 查看结果

# 查看最终报告
cat An6_methylation_results/reports/FINAL_REPORT.txt

# 查看图表
ls -lh An6_methylation_results/figures/

# 查看motif结果
ls -lh An6_methylation_results/motif_analysis/homer/

📦 依赖安装

# Conda环境(推荐)
conda create -n methylation python=3.9
conda activate methylation

# 必需工具
conda install -c bioconda bedtools samtools

# 可选:HOMER
conda install -c bioconda homer

# 可选:MEME Suite
conda install -c bioconda meme

# Python库
pip install pandas numpy matplotlib seaborn

📋 输出文件说明

An6_methylation_results/
├── filtered/                    # 过滤后的bed文件
│   ├── 5mC_CG_filtered.bed     # 5mC @ CG位点
│   ├── 5mC_nonCG_filtered.bed  # 5mC @ non-CG位点
│   ├── 4mC_CG_filtered.bed     # 4mC @ CG位点
│   └── 4mC_nonCG_filtered.bed  # 4mC @ non-CG位点
├── sequences/                   # 提取的序列
│   ├── *_sequences.fa          # 目标序列
│   └── background_sequences.fa # 背景序列
├── motif_analysis/
│   └── homer/
│       ├── 5mC_CG/             # HOMER结果
│       ├── 5mC_nonCG/
│       ├── 4mC_CG/
│       └── 4mC_nonCG/
├── figures/                     # 可视化图表
│   ├── Fig1_counts.png         # 位点数量对比
│   ├── Fig2_mod_distribution.png # 修饰率分布
│   ├── Fig3_coverage.png       # 覆盖度分布
│   └── Fig4_statistics.png     # 统计表
└── reports/                     # 统计报告
    ├── analysis_config.txt     # 分析配置
    ├── filtering_summary.txt   # 过滤统计
    └── FINAL_REPORT.txt        # 最终报告

需要我帮您调整任何参数或添加其他功能吗?🔧✨

nf-core/methylong 内置 DMR 选项:能替代哪些独立工具?

🔍 nf-core/methylong 内置 DMR 选项:能替代哪些独立工具?

✅ 简短回答

您之前需要的功能 methylong 内置选项 能否替代独立工具?
位点水平差异分析 (对标 TRANSIT ANOVA) --population_dmrer dss + --dmr_a/--dmr_b 完全可以替代独立 DSS
区域水平聚合比较 --population_dmrer dss (自动区域聚合) 部分替代 methylKit
连续低/高甲基化区域检测 (对标 Tn5Gaps) ❌ 无专门功能 仍需 metilene 或自定义脚本
基因/操纵子注释关联 ❌ 不直接整合 GFF 仍需 bedtools/ChIPseeker
出版级可视化 ❌ 只输出 bed/bigWig 仍需 pyGenomeTracks/IGV

🎯 核心结论:methylong 的 --population_dmrer dss 可以完全替代您独立运行 DSS 进行组间差异甲基化分析,无需额外工具!


📋 methylong DMR 选项详解 (对标 TRANSIT)

🔹 参数功能映射表

methylong 参数 作用 TRANSIT 对应功能 统计方法
--skip_snvs 跳过 SNP calling + phasing – (细菌无需)
--haplotype_dmrer [dss/modkit] 单倍型水平 DMR 工具 – (细菌无单倍型) 贝叶斯层次模型 (DSS)
--population_dmrer [dss/modkit] 群体水平 DMR 工具 ANOVA 多组比较 贝塔 – 二项检验 + FDR
--dmr_population_scale 启用多样本组间比较 条件间差异分析 同上
--dmr_a, --dmr_b 定义两个比较组的样本名 指定对比条件

🚀 用 methylong 内置 DSS 替代独立运行的完整命令

场景:细菌 6mA,An6 vs BG5 差异分析

nextflow run nf-core/methylong \
  -r 2.0.0 \
  -profile docker \
  --input samplesheet_6mA.csv \          # 包含 An6 和 BG5 两行
  --outdir methylome_out_6mA \
  --ont_aligner minimap2 \
  --m6a \
  --skip_snvs \                          # 细菌无需 phasing
  --dmr_population_scale \               # ⭐ 启用群体水平比较
  --population_dmrer dss \               # ⭐ 使用 DSS 进行统计 (默认值,可省略)
  --dmr_a "An6" \                        # ⭐ 定义组 A (samplesheet 中的 group 列)
  --dmr_b "BG5" \                        # ⭐ 定义组 B
  -resume \
  -work-dir methylome_out_6mA/work

🔹 samplesheet_6mA.csv 格式要求

group,sample,path,ref,method
An6,An6,/path/An6_6mA_mapped.mod.bam,/path/genome.fa,ont
BG5,BG5,/path/BG5_6mA_mapped.mod.bam,/path/genome.fa,ont

⚠️ 关键group 列的值 (An6, BG5) 必须与 --dmr_a/--dmr_b 完全一致!


📁 输出文件:methylong 内置 DMR 结果位置

methylome_out_6mA/
└── results/
    └── dmr/
        ├── population_scale/
        │   ├── An6_vs_BG5.DSS_DMLs.tsv      # ⭐ 位点水平差异结果 (对标 TRANSIT anova_out.xls)
        │   ├── An6_vs_BG5.DSS_DMRs.tsv      # 区域水平差异结果
        │   └── DSS_summary.html             # 简要统计摘要
        └── qc/
            └── dmr_multiqc_report.html      # DMR 分析质控

🔹 DSS_DMLs.tsv 列解读 (对标 TRANSIT ANOVA 输出)

列名 含义 TRANSIT 对应列
chr, pos 基因组坐标 Orf, TA_pos
coverage_A, coverage_B 两组覆盖深度 TAs
mod_count_A, mod_count_B 两组修饰计数 Mean_[condition]
methylation_A, methylation_B 两组甲基化概率 (0-100)
diff 甲基化差异 (A – B) LFC_[condition]
pval, p.adj 原始/FDR 校正 p 值 Pval, Padj
significant 是否显著 (FDR20%) status

⚠️ methylong 内置 DMR 的局限性 (仍需外部工具的场景)

❌ 无法替代的功能 1:连续区域检测 (对标 Tn5Gaps)

# methylong 输出的是"离散位点"差异,不是"连续非修饰区域"
# 如果您需要检测类似 Tn5Gaps 的"长片段无 6mA 区域",仍需:

metilene \
  -i An6.bedgraph BG5.bedgraph \
  -g genome.sizes \
  -o continuous_dmr.tsv \
  -p 0.05 -d 0.2

❌ 无法替代的功能 2:基因/操纵子水平注释

# methylong 不直接整合 GFF 注释
# 如需"哪些基因的 6mA 水平显著改变",仍需:

bedtools intersect -a An6_vs_BG5.DSS_DMLs.tsv -b genome.gff -wa -wb > genes_with_diff_6mA.tsv

❌ 无法替代的功能 3:高级可视化

# methylong 只输出 bed/bigWig,不生成出版级图
# 如需 Circos 式基因组视图,仍需:

pygenometracks --tracks tracks.ini --output final_figure.png

🎯 推荐工作流:methylong 内置 + 最小外部工具

graph LR
    A[modkit pileup] --> B[methylong with --population_dmrer dss]
    B --> C[An6_vs_BG5.DSS_DMLs.tsv]

    C --> D{需要连续区域?}
    D -->|是 | E[metilene for continuous DMRs]
    D -->|否 | F[直接进入注释]

    E & F --> G[bedtools + genome.gff for gene annotation]
    G --> H[pyGenomeTracks for visualization]

✅ 最小命令集:

# 1️⃣ 主分析:methylong 内置 DSS (替代独立 DSS 运行)
nextflow run nf-core/methylong ... --dmr_population_scale --dmr_a An6 --dmr_b BG5 ...

# 2️⃣ (可选) 连续区域检测:仍需 metilene
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6.bg
metilene -i An6.bg BG5.bg -g genome.sizes -o continuous_dmr.tsv

# 3️⃣ (可选) 基因注释:仍需 bedtools
bedtools intersect -a methylome_out_6mA/results/dmr/population_scale/An6_vs_BG5.DSS_DMLs.tsv \
  -b genome.gff -wa -wb > annotated_diff_6mA.tsv

💡 一句话总结

🔑 methylong 内置 DMR 选项可以

  • 完全替代独立 DSS 进行位点水平差异分析 (--population_dmrer dss)
  • 自动完成:标准化 + 贝塔 – 二项检验 + FDR 校正 + 结果输出

🔑 但仍需外部工具

  • ❌ 连续区域检测 (→ metilene)
  • ❌ 基因注释关联 (→ bedtools/ChIPseeker)
  • ❌ 出版级可视化 (→ pyGenomeTracks)

🎯 您的最优路径:先用 methylong 内置 DSS 拿到差异位点列表,再按需添加 1-2 个外部工具完成注释和可视化!

如需我帮您编写 samplesheet 或调试 methylong DMR 参数,请随时告知!🚀



🔍 结果分析:非 CG 位点更多是完全合理的!

您的结果 完全符合逻辑,甚至反映了细菌基因组的典型特征。让我详细解释:


📊 您的结果汇总

修饰类型 位点数量 占比 生物学解释
5mC @ CG 437 33% 经典 CpG 甲基化
5mC @ non-CG 870 67% ⭐ 非 CG 位点更多,正常!
4mC @ CG 7 17% 4mC 很少在 CG 上
4mC @ non-CG 34 83% ⭐ 4mC 偏好非 CG 位点

✅ 为什么 non-CG 位点更多?三大原因

🔹 原因 1:序列组成决定数量基数

假设基因组碱基随机分布(各 25%):

所有可能的二核苷酸:16 种 (AA, AC, AG, AT, CA, CC, CG, CT, ...)

其中含 C 的位点:
├─ CG: 1 种 → 占所有 C 位点的 ~25%
├─ CA, CC, CT: 3 种 → 占所有 C 位点的 ~75% ← non-CG 位点天生更多!

因此:即使甲基化概率相同,non-CG 位点数量也会是 CG 的 ~3 倍

🔹 原因 2:细菌甲基化偏好特定非 CG 基序

细菌的限制修饰系统通常靶向 特定回文序列,而非随机 CG:

常见细菌甲基化基序 修饰类型 示例细菌
GATC 6mA / 4mC E. coli Dam 甲基化酶
CCWGG 5mC H. influenzae Dcm 甲基化酶
GANTC 6mA Caulobacter CcrM 甲基化酶
RCGY 4mC 多种细菌

→ 如果您的细菌有类似系统,非 CG 位点会被优先甲基化

🔹 原因 3:--motif C 0 捕获了所有胞嘧啶

您使用的命令:

modkit pileup ... --motif CG 0 --motif C 0 ...
  • --motif C 0 = 检测 每一个胞嘧啶(无论后面是什么碱基)
  • 因此 m,C,0 包含:CG + CA + CC + CT 所有 C 位点
  • 排除 CG 后,剩余的 non-CG 位点自然更多 ✅

🔬 验证:这些 non-CG 信号是真实的吗?

运行以下命令检查数据质量:

# 1️⃣ 比较 CG vs non-CG 的修饰率分布
echo "=== 5mC 修饰率中位数 ==="
echo -n "CG:    "
awk -F'\t' '$4 ~ /^m,CG,/ {print $11}' An6_4mC_5mC_raw.bed | sort -n | awk '{a[NR]=$1} END{print a[int(NR/2)]}'
echo -n "non-CG:"
awk -F'\t' '$4 ~ /^m,C,/ && $4 !~ /^m,CG,/ {print $11}' An6_4mC_5mC_raw.bed | sort -n | awk '{a[NR]=$1} END{print a[int(NR/2)]}'

# 2️⃣ 检查 non-CG 位点的序列背景(提取上游10bp)
# 需要先有基因组 fasta 索引
samtools faidx bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa

# 提取 non-CG 5mC 位点的序列上下文
awk -F'\t' '$4 ~ /^m,C,/ && $4 !~ /^m,CG,/ && $11 >= 70 && $5 >= 10 {
  print $1":"$2+1"-"$2+11
}' An6_4mC_5mC_raw.bed | head -20 > nonCG_sites.txt

# 3️⃣ 用 MEME/HOMER 做 motif 富集(可选)
# 如果 870 个位点富集到特定序列,说明是真实生物学信号!

🎯 分析建议:如何处理这 4 类数据?

✅ 推荐策略:分层分析

# 📁 第一层:核心分析(高置信度 CG 位点)
# 用于主图、主要结论
awk -F'\t' '$4 ~ /^m,CG,/ && $11 >= 70 && $5 >= 10' An6_4mC_5mC_raw.bed > An6_5mC_CG_high.bed
awk -F'\t' '$4 ~ /^21839,CG,/ && $11 >= 50 && $5 >= 10' An6_4mC_5mC_raw.bed > An6_4mC_CG_high.bed

# 📁 第二层:探索性分析(non-CG 位点)
# 用于补充材料、motif 发现
awk -F'\t' '$4 ~ /^m,C,/ && $4 !~ /^m,CG,/ && $11 >= 70 && $5 >= 10' An6_4mC_5mC_raw.bed > An6_5mC_nonCG.bed
awk -F'\t' '$4 ~ /^21839,C,/ && $4 !~ /^21839,CG,/ && $11 >= 50 && $5 >= 10' An6_4mC_5mC_raw.bed > An6_4mC_nonCG.bed

# 📁 第三层:全量数据(用于基因组浏览器)
# 不过滤,供 IGV 可视化
cp An6_4mC_5mC_raw.bed An6_all_modified.bed

📊 可视化建议

数据类型 推荐图表 工具
5mC @ CG 基因组分布 + 修饰率直方图 deepTools + ggplot2
4mC @ non-CG motif 富集 + 热图 HOMER + seaborn
两者对比 Venn 图 + 散点图 R/ComplexHeatmap

⚠️ 注意事项:避免重复计数!

由于 m,C,0 包含 m,CG,0不要简单相加:

# ❌ 错误:直接相加会重复计算 CG 位点
total_5mC = CG_count + nonCG_count  # 重复!

# ✅ 正确:只用 C  motif 统计全部 5mC
awk -F'\t' '$4 ~ /^m,C,/ && $11 >= 70 && $5 >= 10' An6_4mC_5mC_raw.bed | wc -l
# 或:CG + nonCG(确保互斥)
awk -F'\t' '($4 ~ /^m,CG,/ || ($4 ~ /^m,C,/ && $4 !~ /^m,CG,/)) && $11 >= 70 && $5 >= 10' An6_4mC_5mC_raw.bed | wc -l

🧪 快速诊断脚本(一键运行)

#!/bin/bash
FILE="An6_4mC_5mC_raw.bed"

echo "🔍 数据质量诊断"
echo "================"
echo -e "\n📈 各类型原始行数(无过滤):"
echo "5mC @ CG:    $(grep -c 'm,CG,' $FILE)"
echo "5mC @ C:     $(grep -c 'm,C,' $FILE)"
echo "4mC @ CG:    $(grep -c '21839,CG,' $FILE)"
echo "4mC @ C:     $(grep -c '21839,C,' $FILE)"

echo -e "\n📊 修饰率统计 (列11):"
for label in "5mC_CG" "5mC_nonCG" "4mC_CG" "4mC_nonCG"; do
  case $label in
    "5mC_CG") pattern="^m,CG," ;;
    "5mC_nonCG") pattern="^m,C,"; exclude="m,CG," ;;
    "4mC_CG") pattern="^21839,CG," ;;
    "4mC_nonCG") pattern="^21839,C,"; exclude="21839,CG," ;;
  esac

  if [ -n "$exclude" ]; then
    stats=$(awk -F'\t' -v p="$pattern" -v e="$exclude" '$4 ~ p && $4 !~ e {print $11}' $FILE | \
      awk '{s+=$1; n++} END{if(n>0) printf "n=%d, median=%.2f, mean=%.2f", n, (n%2?$(int(n/2)+1):($(n/2)+$(n/2+1))/2), s/n}')
  else
    stats=$(awk -F'\t' -v p="$pattern" '$4 ~ p {print $11}' $FILE | \
      awk '{s+=$1; n++} END{if(n>0) printf "n=%d, median=%.2f, mean=%.2f", n, (n%2?$(int(n/2)+1):($(n/2)+$(n/2+1))/2), s/n}')
  fi
  echo "$label: $stats"
done

🎯 结论

870 > 437 是完全合理的
非 CG 胞嘧啶在基因组中本就更多,加上细菌甲基化系统常靶向特定非 CG 基序,导致 non-CG 修饰位点数量超过 CG 位点。

下一步建议

  1. nonCG_sites.txt 做 motif 富集,看看 870 个位点是否聚集在特定序列
  2. 如果富集到已知细菌甲基化基序(如 GATC),说明信号真实可靠 🔬
  3. 在论文/报告中明确区分 CGnon-CG 分析,避免混淆

需要我帮您写 motif 富集分析脚本,或生成对比图表吗?📈✨

🧰 甲基化下游统计分析:独立工具推荐与使用指南

🧰 甲基化下游统计分析:独立工具推荐与使用指南

您不需要 Nextflow 封装,以下是优秀的独立工具 (standalone tools),可直接用于 modkit pileup 结果的统计分析,功能对标 TRANSIT:


📊 工具总览:按 TRANSIT 功能映射

TRANSIT 功能 甲基化对应工具 类型 安装方式 核心优势
TTR 标准化 DSS::normalizeCoverage() R 包 conda install bioconductor-dss 贝叶斯收缩,小样本稳健
ANOVA 多组比较 DSS::DMLtest() / methylKit R 包 conda install bioconductor-methylkit 支持任意分组设计 + FDR 校正
Tn5Gaps (非插入运行) metilene / DMRfinder C++ / Python conda install -c bioconda metilene 快速检测连续低/高甲基化区域
基因长度标准化 bedtools + 自定义脚本 CLI 工具 conda install -c bioconda bedtools 灵活适配细菌基因密集特点
置换检验 + FDR DSS (内置) / methylKit R 包 同上 自动多重校正,输出可直接发表
Circos 可视化 pyGenomeTracks / deepTools Python pip install pygenometracks 出版级基因组轨道图,易定制

🔧 工具详解:安装 + 使用示例

1️⃣ DSS (Dispersion Shrinkage for Sequencing) — 首选推荐 ⭐

🎯 对标 TRANSIT ANOVA:位点水平差异甲基化检测,支持多样本、多组比较

🔹 安装

# 推荐:通过 conda (自动解决 R 依赖)
conda create -n methyl_stats -c conda-forge -c bioconda r-base r-dss
conda activate methyl_stats

# 或手动在 R 中安装
R
> if (!require("BiocManager", quietly = TRUE)) install.packages("BiocManager")
> BiocManager::install("DSS")

🔹 输入准备 (modkit bed → DSS 格式)

# 提取核心列: chrom, start, end, mod_count, coverage
# modkit 输出: $4=修饰码, $5=覆盖度, $10=修饰计数, $11=概率(0-100)
awk '$4 == "a" {printf "%s\t%d\t%d\t%d\t%d\n", $1, $2, $3, $10, $5}' An6_6mA_raw.bed > An6.dss
awk '$4 == "a" {printf "%s\t%d\t%d\t%d\t%d\n", $1, $2, $3, $10, $5}' BG5_6mA_raw.bed > BG5.dss

🔹 R 分析脚本 (run_dss_6mA.R)

#!/usr/bin/env Rscript
library(DSS)
library(qvalue)  # 用于更灵活的 FDR 校正

# 1. 读取数据
read_dss <- function(f) {
  df <- read.table(f, header=F, col.names=c("chr","start","end","X","N"))
  df$pos <- df$start + 1  # BED 0-based → 1-based
  df[, c("chr","pos","N","X")]  # DSS 要求格式
}
An6 <- read_dss("An6.dss")
BG5 <- read_dss("BG5.dss")

# 2. 构建 BSseq 对象
BSobj <- makeBSseqData(list(An6, BG5), sampleNames=c("An6", "BG5"))

# 3. 差异检验 (贝塔 - 二项模型 + 平滑)
dmlTest <- DMLtest(BSobj, group1=1, group2=2, smoothing=TRUE)

# 4. 提取显著位点 (FDR < 0.05, |diff| > 20%)
sigDML <- getSig(dmlTest, pval=0.05, delta=0.2)

# 5. 输出结果
write.table(sigDML, "An6_vs_BG5_6mA_DMLs.tsv", 
            sep="\t", quote=F, row.names=F)

# 6. (可选) 按基因注释聚合
# 需准备 genome.gff,用 ChIPseeker 或自定义脚本
cat("✅ 完成!显著差异位点数:", nrow(sigDML), "\n")

🔹 运行

Rscript run_dss_6mA.R

🔹 输出解读 (对标 TRANSIT ANOVA)

DSS 输出列 含义 TRANSIT 对应列
chr, pos 基因组坐标 Orf, TA_pos
coverage 总覆盖深度 TAs
group1, group2 两组修饰计数 Mean_[cond]
pval, p.adj 原始/FDR 校正 p 值 Pval, Padj
diff 甲基化差异 (组1 – 组2) LFC_[cond]
abs.diff 绝对差异值

2️⃣ metilene — 快速 DMR 检测 (对标 Tn5Gaps)

🎯 对标 TRANSIT Tn5Gaps:检测连续低/高甲基化区域,基于二元分割 + 置换检验

🔹 安装

conda install -c bioconda metilene
# 或下载预编译二进制: https://github.com/metilene/metilene/releases

🔹 输入准备

# metilene 需要 bedGraph 格式: chrom, start, end, value
# 用 modkit 概率列 ($11) 作为 value
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6.bedgraph
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' BG5_6mA_raw.bed > BG5.bedgraph

# 准备基因组大小文件 (chrom, size)
samtools faidx genome.fa | cut -f1,2 > genome.sizes

🔹 运行 DMR 检测

metilene \
  -i An6.bedgraph BG5.bedgraph \
  -g genome.sizes \
  -o dmr_results.tsv \
  -t 8 \          # 线程数
  -p 0.05 \       # FDR 阈值
  -d 0.2          # 最小甲基化差异 (20%)

🔹 输出解读

# dmr_results.tsv 示例:
chr1    12345   12890   0.85    0.32    0.53    1.2e-05  0.003   hyper
# │      │      │      │       │       │       │        │      │
# │      │      │      │       │       │       │        │      └─ 方向: hyper/hypo
# │      │      │      │       │       │       │        └─ FDR 校正 p 值
# │      │      │      │       │       │       └─ 原始 p 值
# │      │      │      │       │       └─ 甲基化差异 (An6 - BG5)
# │      │      │      │       └─ BG5 平均甲基化
# │      │      │      └─ An6 平均甲基化
# │      │      └─ 区域结束
# │      └─ 区域起始
# └─ 染色体

3️⃣ methylKit — 基因/区域水平聚合分析

🎯 对标基因水平分析:将位点聚合到基因/操纵子,进行组间比较

🔹 安装

conda install -c bioconda bioconductor-methylkit

🔹 R 脚本示例 (methylkit_gene_level.R)

#!/usr/bin/env Rscript
library(methylKit)
library(GenomicFeatures)

# 1. 读取 modkit bed (需先转为 methylKit 格式)
# 格式: chr, start, end, strand, coverage, freq
read_modkit <- function(f, sample_id) {
  df <- read.table(f, header=F)
  df <- df[df$V4 == "a" & df$V11 >= 70 & df$V5 >= 10, ]  # 6mA, prob≥70%, cov≥10
  data.frame(
    chr = df$V1,
    start = df$V2 + 1,  # 0-based → 1-based
    end = df$V3,
    strand = ifelse(df$V6 == "+", 1, -1),
    coverage = df$V5,
    freq = df$V11  # 概率 0-100
  )
}

# 2. 加载样本
An6 <- read_modkit("An6_6mA_raw.bed", "An6")
BG5 <- read_modkit("BG5_6mA_raw.bed", "BG5")

# 3. 创建 methylKit 对象
myobj <- methRead(
  list("An6.tmp", "BG5.tmp"),  # 需先保存为临时文件
  sample.id = c("An6", "BG5"),
  assembly = "An6_genome",
  treatment = c(1, 2),         # 实验分组
  context = "CpG"              # 细菌可设为"any"
)

# 4. 基因水平聚合 (需 GFF 注释)
txdb <- makeTxDbFromGFF("genome.gff", format="gff3")
gene_meth <- tileMethylCounts(myobj, win.size=1000, step.size=500)

# 5. 差异分析
diff_meth <- calculateMethylDiff(gene_meth, difference=20, qvalue=0.01)

# 6. 输出
write.table(diff_meth, "gene_level_6mA_diff.tsv", sep="\t", quote=F)

4️⃣ 可视化工具:pyGenomeTracks (对标 Circos)

🎯 对标 Circos:生成线性基因组轨道图,支持多条件叠加

🔹 安装

pip install pygenometracks

🔹 准备配置文件 (tracks.ini)

[x-axis]
where = top

# 6mA 信号轨道 (An6)
[An6_6mA]
file = An6_6mA.bw
title = An6 6mA
height = 2
color = #1f78b4
min_value = 0
max_value = 100

# 6mA 信号轨道 (BG5)
[BG5_6mA]
file = BG5_6mA.bw
title = BG5 6mA
height = 2
color = #e31a1c
min_value = 0
max_value = 100

# 差异区域标记
[DMRs]
file = dmr_results.bed
title = Significant DMRs
height = 1
color = #ff7f00

🔹 生成 bigWig (modkit bed → bigWig)

# 1. bed → bedGraph
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6_6mA.bg

# 2. bedGraph → bigWig (需 genome.sizes)
bedGraphToBigWig An6_6mA.bg genome.sizes An6_6mA.bw

🔹 运行可视化

pygenometracks \
  --tracks tracks.ini \
  --region chr1:100000-200000 \  # 可选:指定区域
  --width 10 \
  --height 5 \
  --output methylation_tracks.png \
  --dpi 300

🎯 针对您项目的最小可行流程

# ✅ 步骤 1: 格式转换 (modkit bed → 各工具输入)
# DSS 输入
awk '$4 == "a" {printf "%s\t%d\t%d\t%d\t%d\n", $1, $2, $3, $10, $5}' An6_6mA_raw.bed > An6.dss

# metilene 输入
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6.bedgraph

# bigWig 输入 (可视化)
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed | \
  bedGraphToBigWig stdin genome.sizes An6_6mA.bw

# ✅ 步骤 2: 运行统计分析
Rscript run_dss_6mA.R                    # 位点水平差异
metilene -i An6.bedgraph BG5.bedgraph -g genome.sizes -o dmr.tsv  # 区域水平

# ✅ 步骤 3: 可视化
pygenometracks --tracks tracks.ini --output final_tracks.png

# ✅ 步骤 4: 整合结果 (可选)
# 将 DSS 显著位点 + metilene DMR 区域 + 基因注释合并为最终表格

📦 一键安装脚本 (conda 环境)

#!/bin/bash
# install_methyl_tools.sh

conda create -n methyl_analysis -c conda-forge -c bioconda \
  r-base=4.3 \
  r-dss \
  r-methylkit \
  r-chipseeker \
  r-qvalue \
  metilene \
  bedtools \
  samtools \
  ucsc-bedgraphtobigwig \
  -y

conda activate methyl_analysis

# 额外安装 Python 可视化工具
pip install pygenometracks deepTools

echo "✅ 所有工具安装完成!激活环境: conda activate methyl_analysis"

💡 工具选择决策树

您的分析目标?
│
├─ 🔍 "哪些位点在条件间甲基化水平不同?"
│   └─→ ✅ DSS (最严谨,输出可直接用于发表)
│
├─ 🧬 "哪些连续区域甲基化状态改变?" (对标 Tn5Gaps)
│   └─→ ✅ metilene (最快) 或 DMRfinder (最灵活)
│
├─ 📊 "基因/操纵子水平聚合比较?"
│   └─→ ✅ methylKit + ChIPseeker 注释
│
├─ 🎨 "生成出版级可视化?"
│   └─→ ✅ pyGenomeTracks (线性) 或 Circos (环形,需额外配置)
│
└─ 🔎 "交互式探索关键位点?"
    └─→ ✅ IGV + bigWig (最直观)

🎯 一句话总结

🔑 核心推荐组合

  • 差异分析DSS (R 包,对标 TRANSIT ANOVA)
  • 区域检测metilene (C++ 二进制,对标 Tn5Gaps)
  • 可视化pyGenomeTracks (Python,对标 Circos)

🚀 安装最简单:全部可通过 condapip 一键安装,无需编译
📈 您的 2,234 个高置信 6mA 位点 + 上述工具 = 完整、可发表的甲基化分析流程!

如需具体脚本调试或结果解读帮助,请随时告知!🔧🚀



🔍 nf-core/methylong 内置 DMR 选项:能替代哪些独立工具?

✅ 简短回答

您之前需要的功能 methylong 内置选项 能否替代独立工具?
位点水平差异分析 (对标 TRANSIT ANOVA) --population_dmrer dss + --dmr_a/--dmr_b 完全可以替代独立 DSS
区域水平聚合比较 --population_dmrer dss (自动区域聚合) 部分替代 methylKit
连续低/高甲基化区域检测 (对标 Tn5Gaps) ❌ 无专门功能 仍需 metilene 或自定义脚本
基因/操纵子注释关联 ❌ 不直接整合 GFF 仍需 bedtools/ChIPseeker
出版级可视化 ❌ 只输出 bed/bigWig 仍需 pyGenomeTracks/IGV

🎯 核心结论:methylong 的 --population_dmrer dss 可以完全替代您独立运行 DSS 进行组间差异甲基化分析,无需额外工具!


📋 methylong DMR 选项详解 (对标 TRANSIT)

🔹 参数功能映射表

methylong 参数 作用 TRANSIT 对应功能 统计方法
--skip_snvs 跳过 SNP calling + phasing – (细菌无需)
--haplotype_dmrer [dss/modkit] 单倍型水平 DMR 工具 – (细菌无单倍型) 贝叶斯层次模型 (DSS)
--population_dmrer [dss/modkit] 群体水平 DMR 工具 ANOVA 多组比较 贝塔 – 二项检验 + FDR
--dmr_population_scale 启用多样本组间比较 条件间差异分析 同上
--dmr_a, --dmr_b 定义两个比较组的样本名 指定对比条件

🚀 用 methylong 内置 DSS 替代独立运行的完整命令

场景:细菌 6mA,An6 vs BG5 差异分析

nextflow run nf-core/methylong \
  -r 2.0.0 \
  -profile docker \
  --input samplesheet_6mA.csv \          # 包含 An6 和 BG5 两行
  --outdir methylome_out_6mA \
  --ont_aligner minimap2 \
  --m6a \
  --skip_snvs \                          # 细菌无需 phasing
  --dmr_population_scale \               # ⭐ 启用群体水平比较
  --population_dmrer dss \               # ⭐ 使用 DSS 进行统计 (默认值,可省略)
  --dmr_a "An6" \                        # ⭐ 定义组 A (samplesheet 中的 group 列)
  --dmr_b "BG5" \                        # ⭐ 定义组 B
  -resume \
  -work-dir methylome_out_6mA/work

🔹 samplesheet_6mA.csv 格式要求

group,sample,path,ref,method
An6,An6,/path/An6_6mA_mapped.mod.bam,/path/genome.fa,ont
BG5,BG5,/path/BG5_6mA_mapped.mod.bam,/path/genome.fa,ont

⚠️ 关键group 列的值 (An6, BG5) 必须与 --dmr_a/--dmr_b 完全一致!


📁 输出文件:methylong 内置 DMR 结果位置

methylome_out_6mA/
└── results/
    └── dmr/
        ├── population_scale/
        │   ├── An6_vs_BG5.DSS_DMLs.tsv      # ⭐ 位点水平差异结果 (对标 TRANSIT anova_out.xls)
        │   ├── An6_vs_BG5.DSS_DMRs.tsv      # 区域水平差异结果
        │   └── DSS_summary.html             # 简要统计摘要
        └── qc/
            └── dmr_multiqc_report.html      # DMR 分析质控

🔹 DSS_DMLs.tsv 列解读 (对标 TRANSIT ANOVA 输出)

列名 含义 TRANSIT 对应列
chr, pos 基因组坐标 Orf, TA_pos
coverage_A, coverage_B 两组覆盖深度 TAs
mod_count_A, mod_count_B 两组修饰计数 Mean_[condition]
methylation_A, methylation_B 两组甲基化概率 (0-100)
diff 甲基化差异 (A – B) LFC_[condition]
pval, p.adj 原始/FDR 校正 p 值 Pval, Padj
significant 是否显著 (FDR20%) status

⚠️ methylong 内置 DMR 的局限性 (仍需外部工具的场景)

❌ 无法替代的功能 1:连续区域检测 (对标 Tn5Gaps)

# methylong 输出的是"离散位点"差异,不是"连续非修饰区域"
# 如果您需要检测类似 Tn5Gaps 的"长片段无 6mA 区域",仍需:

metilene \
  -i An6.bedgraph BG5.bedgraph \
  -g genome.sizes \
  -o continuous_dmr.tsv \
  -p 0.05 -d 0.2

❌ 无法替代的功能 2:基因/操纵子水平注释

# methylong 不直接整合 GFF 注释
# 如需"哪些基因的 6mA 水平显著改变",仍需:

bedtools intersect -a An6_vs_BG5.DSS_DMLs.tsv -b genome.gff -wa -wb > genes_with_diff_6mA.tsv

❌ 无法替代的功能 3:高级可视化

# methylong 只输出 bed/bigWig,不生成出版级图
# 如需 Circos 式基因组视图,仍需:

pygenometracks --tracks tracks.ini --output final_figure.png

🎯 推荐工作流:methylong 内置 + 最小外部工具

graph LR
    A[modkit pileup] --> B[methylong with --population_dmrer dss]
    B --> C[An6_vs_BG5.DSS_DMLs.tsv]

    C --> D{需要连续区域?}
    D -->|是 | E[metilene for continuous DMRs]
    D -->|否 | F[直接进入注释]

    E & F --> G[bedtools + genome.gff for gene annotation]
    G --> H[pyGenomeTracks for visualization]

✅ 最小命令集:

# 1️⃣ 主分析:methylong 内置 DSS (替代独立 DSS 运行)
nextflow run nf-core/methylong ... --dmr_population_scale --dmr_a An6 --dmr_b BG5 ...

# 2️⃣ (可选) 连续区域检测:仍需 metilene
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6.bg
metilene -i An6.bg BG5.bg -g genome.sizes -o continuous_dmr.tsv

# 3️⃣ (可选) 基因注释:仍需 bedtools
bedtools intersect -a methylome_out_6mA/results/dmr/population_scale/An6_vs_BG5.DSS_DMLs.tsv \
  -b genome.gff -wa -wb > annotated_diff_6mA.tsv

💡 一句话总结

🔑 methylong 内置 DMR 选项可以

  • 完全替代独立 DSS 进行位点水平差异分析 (--population_dmrer dss)
  • 自动完成:标准化 + 贝塔 – 二项检验 + FDR 校正 + 结果输出

🔑 但仍需外部工具

  • ❌ 连续区域检测 (→ metilene)
  • ❌ 基因注释关联 (→ bedtools/ChIPseeker)
  • ❌ 出版级可视化 (→ pyGenomeTracks)

🎯 您的最优路径:先用 methylong 内置 DSS 拿到差异位点列表,再按需添加 1-2 个外部工具完成注释和可视化!

如需我帮您编写 samplesheet 或调试 methylong DMR 参数,请随时告知!🚀



📊 modkit pileup 输出文件列详解 (bedMethyl 格式)

🎯 您的结果统计

过滤条件 位点数量 说明
$11 >= 70 && $5 >= 10 2,234 ✅ 高置信度 6mA 位点 (推荐用于发表)
$11 >= 50 && $5 >= 5 3,422 🔍 探索性分析 (包含中等置信度位点)

💡 建议:对于细菌 6mA 分析,2,234 个高置信度位点是合理的数量级(取决于菌株甲基转移酶活性)。


📋 bedMethyl 格式列详解 (18 列)

您的输出行示例:

5    4    5    a    16    -    4    5    255,0,0    16    70.00    0    16    0    0    0    0    0
│    │    │    │    │    │    │    │       │       │      │      │    │     │    │    │    │    │
1    2    3    4    5    6    7    8       9      10     11    12   13    14   15   16   17   18
列号 列名 示例值 含义 分析建议
1 chrom 5 染色体/contig 名称 用于按基因组区域分组
2 start 4 起始位置 (0-based) ⚠️ BED 格式起始为 0,转换坐标时 +1
3 end 5 结束位置 (1-based, exclusive) 单碱基修饰时 end = start + 1
4 name a 修饰码: a=6mA, m=5mC, c=4mC 过滤时用 $4 == "a" 提取 6mA
5 score 16 覆盖深度 (该位点总 reads 数) 过滤低深度: $5 >= 10
6 strand - 链方向: + / - 6mA 分析通常合并双链
7 thickStart 4 可视化用 (通常 = start) 可忽略
8 thickEnd 5 可视化用 (通常 = end) 可忽略
9 itemRgb 255,0,0 颜色值 (RGB) 可忽略
10 blockCount 16 检测到修饰的 reads 数 可计算修饰比例: $10/$5
11 blockSizes 70.00 ⭐⭐ 修饰概率 (0-100 百分比!) 核心过滤列: $11 >= 70
12 starts 内部偏移 (通常为 0) 可忽略
13-18 扩展列 16 0 0 0 0 0 modkit 附加统计信息 见下方详解

🔍 扩展列 (13-18) 详解

列号 含义 示例 用途
13 mod_count 16 该位点检测到修饰的 reads 数 与列10相同,冗余备份
14 unmod_count 该位点未修饰的 reads 数 可验证: $13 + $14 ≈ $5
15 del_count 该位点有 deletion 的 reads 数 质控: 高值可能表示比对问题
16 no_call_count 无法判断修饰状态的 reads 数 质控: 高值表示信号模糊
17 coverage_plus 正链覆盖度 链特异性分析时用
18 coverage_minus 负链覆盖度 链特异性分析时用

🔧 实用计算示例:

# 计算每个位点的"原始修饰比例" (未过滤前)
awk '{printf "%s\t%s\t%.2f%%\n", $1, $2, ($10/$5)*100}' An6_6mA_raw.bed | head -5

# 验证覆盖度一致性 (正负链之和 ≈ 总覆盖)
awk '{if($17+$18 != $5) print "Mismatch:", $0}' An6_6mA_raw.bed | wc -l
# 预期: 0 或极少 (浮点误差)

🎯 常用分析命令模板

🔹 1. 提取核心信息 (简化表格)

# 输出: chrom, position(1-based), strand, probability, coverage
awk '$4 == "a" && $11 >= 70 && $5 >= 10 {
  printf "%s\t%d\t%s\t%.2f\t%d\n", $1, $2+1, $6, $11, $5
}' An6_6mA_raw.bed > An6_6mA_simple.tsv

# 查看前5行
head -5 An6_6mA_simple.tsv
# 输出示例:
# 5    5    -    99.54    16
# 5    7    +    98.21    23

🔹 2. 按染色体统计 6mA 密度

# 每百万碱基的 6mA 位点数 (需知道各 contig 长度)
# 先获取基因组大小
samtools faidx bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa | cut -f1,2 > genome.sizes

# 计算密度
awk '$4 == "a" && $11 >= 70 {count[$1]++} 
     END {for(c in count) print c, count[c]}' An6_6mA_raw.bed | \
  join - genome.sizes | \
  awk '{printf "%s\t%d sites\t%.2f sites/Mb\n", $1, $2, $2/($3/1000000)}' | \
  sort -k3 -rn

🔹 3. 提取 6mA 位点上下游序列 (用于 motif 分析)

# 提取 ±20bp 序列
bedtools slop -i <(awk '$4 == "a" && $11 >= 70 {print $1"\t"$2"\t"$3}' An6_6mA_raw.bed) \
  -g genome.sizes -b 20 | \
  bedtools getfasta -fi bacass_out/Medaka/An6-unicycler-medaka_polished_genome.fa -bed - \
  -fo An6_6mA_motif_seqs.fa

# 运行 MEME 找富集 motif
meme An6_6mA_motif_seqs.fa -oc meme_6mA -dna -mod zoops -nmotifs 3 -minw 4 -maxw 12

🔹 4. 与基因注释关联 (需 GFF/GTF 文件)

# 假设有 genome.gff 文件
bedtools intersect -a <(awk '$4 == "a" && $11 >= 70 {print $1"\t"$2"\t"$3"\t"$11}' An6_6mA_raw.bed) \
  -b genome.gff -wa -wb > An6_6mA_with_genes.tsv

# 统计 6mA 在基因区域的分布
cut -f8 An6_6mA_with_genes.tsv | cut -f3 | sort | uniq -c | sort -rn
# 输出示例:
#   1234 CDS
#    567 promoter
#    234 intergenic

🔹 5. 生成 IGV 可视化的 bigWig 文件

# 1. 先转为 bedGraph (chrom, start, end, score=probability)
awk '$4 == "a" {printf "%s\t%d\t%d\t%.2f\n", $1, $2, $3, $11}' An6_6mA_raw.bed > An6_6mA.bedgraph

# 2. 转为 bigWig (需 genome.sizes 文件)
bedGraphToBigWig An6_6mA.bedgraph genome.sizes An6_6mA.bw

# 3. 在 IGV 中加载:
#    - File → Load Genome → 加载 genome.fa
#    - File → Load from File → 加载 An6_6mA.bw

⚠️ 重要注意事项

1️⃣ 坐标系统:0-based vs 1-based

BED 格式 (modkit 输出):  start=4, end=5  → 表示第 5 个碱基 (0-based)
生物学常用:              position = start + 1 = 5 (1-based)

# 转换命令:
awk '{print $1"\t"($2+1)"\t"$3}' An6_6mA_final.bed > An6_6mA_1based.bed

2️⃣ 概率列是百分比 (0-100),不是小数 (0-1)

# ❌ 错误: 用小数阈值
awk '$11 >= 0.7' An6_6mA_raw.bed  # 几乎无结果!

# ✅ 正确: 用百分比阈值
awk '$11 >= 70' An6_6mA_raw.bed   # 正确过滤

3️⃣ 修饰码大小写敏感

修饰类型 modkit 0.5.0 输出码 过滤命令
6mA a (小写) $4 == "a"
5mC m (小写) $4 == "m"
4mC c (小写) $4 == "c"

📊 您的结果质量评估

# ✅ 检查高置信度位点的概率分布
awk '$4 == "a" && $11 >= 70 && $5 >= 10 {print $11}' An6_6mA_raw.bed | \
  sort -n | uniq -c | tail -10

# 预期输出示例:
#    234 95.00
#    456 98.21
#    892 99.54
#    652 100.00   ← 大量 100% 置信度,信号极佳! ✅

# ✅ 检查覆盖度分布
awk '$4 == "a" && $11 >= 70 && $5 >= 10 {print $5}' An6_6mA_raw.bed | \
  sort -n | uniq -c | head -10

# 预期: 大部分位点覆盖度 10-50×,说明测序深度充足 ✅

💡 一句话总结

🔑 核心三列

  • $4 = 修饰码 (a=6mA)
  • $5 = 覆盖深度 (过滤低质量: >=10)
  • $11 = 修饰概率 (0-100 百分比! 过滤高置信: >=70)

您的 2,234 个高置信 6mA 位点是高质量的起点,可用于:

  • Motif 富集分析 (验证已知限制修饰系统)
  • 基因组分布分析 (启动子/基因体/间隔区)
  • 与转录组数据整合 (如有)

如需帮助进行下游 motif 分析或可视化,随时告诉我!🚀



🔬 从 Tn-seq (TRANSIT) 到甲基化分析:下游统计工具对比指南

您提出的问题非常精准!正如 Tn-seq 用 TRANSIT 进行插入位点的统计分析(ANOVA、Tn5Gaps),甲基化测序(如 modkit pileup 输出)也有对应的下游统计工具。以下是详细对比和推荐:


📊 Tn-seq vs 甲基化分析:核心概念映射

Tn-seq 分析目标 甲基化分析对应目标 统计本质
插入位点计数 (template counts) 修饰概率/计数 (mod_count/coverage) 计数型数据
基因必需性 (essential genes) 差异甲基化区域 (DMRs) 区域水平差异检测
条件间比较 (ANOVA in TRANSIT) 条件间甲基化差异 (DSS, methylKit) 多组比较 + 多重校正
非插入区域检测 (Tn5Gaps) 低甲基化/高甲基化区域 (DMRfinder) 连续区域模式识别
密度标准化 (TTR normalization) 覆盖度/文库大小标准化 (TMM, RLE) 文库效应校正

💡 关键区别:Tn-seq 关注”有没有插入”(二元事件),甲基化关注”修饰比例是多少”(连续概率 0-100%)。


🧰 甲基化下游统计工具推荐(按功能分类)

🔹 1. 差异甲基化分析 (对应 TRANSIT ANOVA)

工具 适用场景 输入格式 核心方法 细菌 6mA 兼容性
DSS (Dispersion Shrinkage for Sequencing) ✅ 多样本组间比较 bed/bedGraph + 样本分组 贝叶斯层次模型 + 算术-贝塔分布 ✅ 优秀 (支持任意 motif)
methylKit ✅ 多条件/时间序列 bedGraph/tabix 逻辑回归 + FDR 校正 ✅ 良好 (需格式转换)
metilene ✅ 快速 DMR 检测 bedGraph 二元分割 + Kolmogorov-Smirnov 检验 ⚠️ 需确认 motif 支持
DMRseq ✅ 区域水平 + 协变量调整 bed + 样本信息 平滑 + 置换检验 ✅ 良好

🚀 DSS 快速入门 (推荐首选)

# R 代码示例:细菌 6mA 差异分析
library(DSS)

# 1. 读取 modkit 输出 (假设已过滤高置信位点)
# 文件格式: chrom, start, end, motif, coverage, mod_count, probability
sample1 <- read.table("An6_6mA_final.bed", header=F)
sample2 <- read.table("BG5_6mA_final.bed", header=F)

# 2. 构建 DSS 对象 (使用原始计数: mod_count / coverage)
BSobj <- makeBSseqData(
  list(sample1[, c(1,2,3,6,5)],    # chrom, start, end, mod_count, coverage
       sample2[, c(1,2,3,6,5)]),
  sampleNames = c("An6", "BG5")
)

# 3. 差异检验 (贝塔-二项模型)
dmlTest <- DMLtest(BSobj, group1=1, group2=2, smoothing=TRUE)

# 4. 提取显著差异位点 (FDR < 0.05, |diff| > 0.2)
sigDML <- getSig(dmlTest, pval=0.05, delta=0.2, meth.diff=0.2)

# 5. 导出结果
write.table(sigDML, "An6_vs_BG5_6mA_DMLs.tsv", sep="\t", quote=F, row.names=F)

📋 DSS 输出列解读 (类比 TRANSIT)

DSS 输出列 含义 TRANSIT 对应列
chr, pos 基因组坐标 Orf, TA_pos
coverage 总覆盖深度 TAs (TA 位点数)
X1, X2 两组的修饰计数 Mean_[condition]
pval, p.adj 原始/FDR 校正 p 值 Pval, Padj
diff 甲基化差异 (组1 – 组2) LFC_[condition] (logFC)
abs.diff 绝对差异值

🔹 2. 区域水平分析 (对应 Tn5Gaps: 连续非修饰区域检测)

工具 功能 适用场景
DMRfinder 识别连续低/高甲基化区域 类似 Tn5Gaps 的”非插入运行”检测
methylSig 区域聚合 + 差异检验 基因/启动子水平的甲基化变化
custom R/Python 自定义滑动窗口分析 细菌操纵子/基因簇水平分析

🚀 滑动窗口分析示例 (Python)

# 检测连续低甲基化区域 (类似 Tn5Gaps 的 non-insertion runs)
import pandas as pd
import numpy as np

# 读取 modkit 输出
df = pd.read_csv("An6_6mA_final.bed", sep="\t", header=None, 
                 names=["chr","start","end","motif","cov","strand",
                        "thickS","thickE","rgb","mod_cnt","prob",
                        "start2","cnt1","cnt0","del_cnt","no_call","cov+","cov-"])

# 按染色体分组,检测连续低甲基化区域 (prob < 30%)
def find_hypo_regions(group, min_len=5, max_gap=100, prob_thresh=30):
    low_meth = group[group["prob"] < prob_thresh].sort_values("start")
    if len(low_meth) < min_len:
        return None
    # 简单连续区域检测 (可扩展为更复杂算法)
    regions = []
    curr_start, curr_end = low_meth.iloc[0]["start"], low_meth.iloc[0]["end"]
    for _, row in low_meth.iloc[1:].iterrows():
        if row["start"] - curr_end <= max_gap:  # 允许小间隙
            curr_end = row["end"]
        else:
            if curr_end - curr_start >= min_len * 10:  # 最小区域长度
                regions.append((curr_start, curr_end))
            curr_start, curr_end = row["start"], row["end"]
    return regions

# 应用并输出
hypo_regions = df.groupby("chr").apply(find_hypo_regions).dropna()
hypo_regions.to_csv("An6_hypomethylated_regions.bed", sep="\t", header=False, index=False)

🔹 3. 功能注释与可视化 (对应 Circos 基因组视图)

工具 功能 输出示例
ChIPseeker (R) 甲基化位点与基因/启动子关联 饼图: CDS/promoter/intergenic 分布
deepTools 甲基化信号热图/轮廓图 基因体 ±2kb 的 6mA 密度曲线
pyGenomeTracks 多轨道基因组浏览器式可视化 类似 Circos 的线性基因组视图
IGV + bigWig 交互式位点查看 手动验证关键基因修饰模式

🚀 基因区域关联分析 (R + ChIPseeker)

library(ChIPseeker)
library(TxDb.Ecoli.NCBI.NC_008800.1.refseq)  # 替换为您的细菌注释

# 读取高置信 6mA 位点 (转换为 GRanges)
library(GenomicRanges)
gr <- GRanges(
  seqnames = df$chr,
  ranges = IRanges(start = df$start + 1, end = df$end),  # BED 0-based → 1-based
  score = df$prob
)

# 注释到基因区域
anno <- annotatePeak(gr, tssRegion=c(-300, 300), 
                     TxDb = TxDb.Ecoli.NCBI.NC_008800.1.refseq,
                     annoDb = "org.EcK12.eg.db")  # 或用 custom GFF

# 可视化
plotAnnoPie(anno)  # 6mA 在基因组区域的分布
plotAvgProf(anno)  # TSS 附近的 6mA 密度曲线

🎯 针对您细菌 6mA 项目的推荐流程

graph LR
    A[modkit pileup 输出] --> B[质量控制 + 过滤]
    B --> C[差异甲基化分析]
    B --> D[区域模式检测]

    C --> C1[DSS: 位点水平差异]
    C --> C2[methylKit: 基因水平聚合]

    D --> D1[滑动窗口: 连续低/高甲基化区]
    D --> D2[与操纵子/基因簇关联]

    C1 & C2 & D1 & D2 --> E[功能注释]
    E --> F[可视化 + 生物学解释]

✅ 具体命令/代码整合

# 🔹 步骤 1: 准备输入文件 (modkit → DSS 格式)
# 提取核心列: chrom, start, end, mod_count, coverage
awk '$4 == "a" && $11 >= 70 && $5 >= 10 {
  printf "%s\t%d\t%d\t%d\t%d\n", $1, $2, $3, $10, $5
}' An6_6mA_raw.bed > An6_6mA_dss_input.tsv

# 🔹 步骤 2: 运行 DSS (R 脚本)
Rscript run_dss_6mA.R \
  --input An6_6mA_dss_input.tsv,BG5_6mA_dss_input.tsv \
  --groups An6,An6,An6,BG5,BG5,BG5 \
  --output An6_vs_BG5_6mA_DSS_results.tsv

# 🔹 步骤 3: 提取显著差异位点 + 注释
# (见上方 R 代码示例)

# 🔹 步骤 4: 可视化
# - IGV: 加载 bigWig 轨道查看关键基因
# - pyGenomeTracks: 生成出版级多轨道图

⚠️ 细菌甲基化分析的特殊注意事项

问题 解决方案
基因组小、基因密集 使用基因/操纵子为单位聚合,而非固定窗口
6mA 富集于特定 motif (如 GATC) 分析时按 motif 分层,避免背景噪声
无”基因长度”标准化需求 细菌基因长度变异小,可直接用计数/概率
限制修饰系统影响 将已知 RM 系统基因作为阳性对照验证流程
样本量小 (常为 2-3 重复) 优先用 DSS (贝叶斯收缩) 而非纯频率比较

📋 工具选择决策树

您的分析目标?
│
├─ 🔍 "哪些位点在条件 A vs B 甲基化水平不同?"
│   └─→ ✅ DSS (位点水平) 或 methylKit (基因水平)
│
├─ 🧬 "哪些基因/区域整体甲基化状态改变?"
│   └─→ ✅ methylKit + ChIPseeker 注释
│
├─ 🎯 "哪些区域连续低/高甲基化 (类似 Tn5Gaps)?"
│   └─→ ✅ 自定义滑动窗口 + DMRfinder
│
├─ 📊 "全局甲基化模式可视化?"
│   └─→ ✅ pyGenomeTracks / IGV + bigWig
│
└─ 🧪 "验证已知限制修饰系统活性?"
    └─→ ✅ 提取 RM 系统基因位点,手动检查概率分布

💡 一句话总结

🔑 核心推荐

  • 差异分析DSS (最接近 TRANSIT ANOVA 的统计严谨性)
  • 区域模式自定义滑动窗口 (最灵活适配细菌基因组)
  • 功能注释ChIPseeker + pyGenomeTracks (最直观可视化)

🎯 您的 2,234 个高置信 6mA 位点是优质输入,结合菌株分组信息,即可开展与 Tn-seq 同等严谨的统计推断!

如需具体 R/Python 脚本模板或帮助解读 DSS 输出,请随时告知!🚀