# How to Convert FASTA to FASTQ: A Step-by-Step Guide

By the end of this guide you will be able to take a nucleotide FASTA file and produce a FASTQ file that every standard parser accepts, with no truncation, no length mismatches, and no silent corruption. You will also know which placeholder quality value to assign, how to prove the output is structurally correct, and when the whole exercise is a bad idea.

You need a terminal, a text editor, and one of two things: `awk` (present on every Linux and macOS system) or `seqtk`. For verification you will want `seqkit`, which ships as a dependency-free binary for Linux, macOS, and Windows and is built specifically for FASTA and FASTQ manipulation [1]. Nothing else is required. No Python environment, no compiler, no reference genome.

The conversion itself is not the hard part. The hard part is that FASTA contains no quality information, so any FASTQ you produce contains invented data. This guide is mostly about managing that fact responsibly.

## What FASTA and FASTQ Actually Store

### FASTA: sequence and nothing else

FASTA is a plain-text format built from two element types. A header line starts with a greater-than sign (`>`) and carries an identifier plus optional free-text description. Sequence lines follow, and they may wrap across as many lines as the writer chose. There is no quality data, no fixed line count, and no required line width.

That simplicity is why FASTA is everywhere. Reference genomes, coding sequence sets, protein databases, and multiple sequence alignments are all distributed as FASTA. In proteomics, a protein FASTA file is a required input for essentially every search engine, and its absence is one of the recurring reasons published datasets cannot be reanalyzed [2]. The format also absorbs extensions. DRAMP 5.0 introduced a variant called FASTS that appends SMILES structure strings to FASTA annotation lines so that non-natural residues and chemical modifications survive in a single text file [3]. FASTA survives because it is boring.

### FASTQ: sequence plus a per-base confidence score

FASTQ keeps the sequence and adds one thing: a quality string with one character per base. Each record uses four lines.

```
@read1 sample=A
ACGTACGTACGTACGT
+
IIIIIIIIIIIIIIII
```

Line 1 starts with `@` and holds the identifier and description. Line 2 is the sequence. Line 3 starts with `+` and may repeat the header. Line 4 is the quality string, and its length must equal the sequence length exactly.

FASTQ is the native output of basecallers and sequencers. On Oxford Nanopore instruments, raw electrical signal is stored separately in POD5 files, with the open-source SLOW5 and BLOW5 formats positioned as alternatives to the older FAST5 container [4]. The basecaller reads that signal and emits reads, and those reads are written as FASTQ. A published MinION analysis of a multidrug-resistant *Escherichia coli* strain shows the chain explicitly: raw FAST5 reads were basecalled with Albacore and then converted to both FASTA and FASTQ with Poretools before assembly [5]. Basecalled reads exist as FASTQ first and as FASTA only after someone deliberately strips the quality.

### The conversion is one-way in information terms

FASTQ to FASTA is lossy in a controlled way. You delete line 4 and change `@` to `>`, and the sequence is unchanged. FASTA to FASTQ is the reverse direction but not the inverse operation. You must manufacture line 4 from nothing. The sequence comes back byte-identical, but the quality data does not exist and never did.

| Feature | FASTA | FASTQ |
|--|--|--|
| Header character | `>` | `@` |
| Lines per record | 2 or more, sequence may wrap | 4, fixed |
| Sequence line width | arbitrary | conventionally one line |
| Per-base quality | absent | required, length must match sequence |
| Typical contents | genomes, proteins, alignments, assembled contigs | raw or trimmed sequencing reads |
| Effect of converting to the other format | quality permanently discarded | quality fabricated, not recoverable |

## Decide What You Have Before You Convert

Not every FASTA should become a FASTQ. Run through this list first, because the most common conversion mistake happens before any command is typed.

| What your FASTA contains | Should you convert? | Why |
|--|--|--|
| Basecalled reads exported as FASTA | Yes, with a conservative placeholder | These were FASTQ once, and a downstream tool refuses FASTA input |
| Assembled contigs or a draft genome | No | There are no reads and no per-base quality to represent |
| Reference genome or transcriptome | No | Tools that need it expect FASTA, and fake reads mislead variant callers |
| Protein sequences | No | FASTQ is defined for nucleotide alphabets, and protein quality has no meaning |
| A [multiple sequence alignment](/blog/guides/multiple-sequence-alignment) with gap characters | No, unless you degap it first | `-` and `.` are not valid FASTQ sequence characters |
| Coding sequences with stop codons written as `*` | No, unless you remove the `*` | `*` is not a nucleotide and will break parsers |

If you are working from public short-read data, check whether the FASTQ already exists before converting anything. RNA-seq protocols built on the SRA Toolkit download reads and pass them through quality checking with FastQC and adapter trimming with Trimmomatic before alignment, which means the FASTQ is normally the starting point rather than a derived product [6].

## How to Convert FASTA to FASTQ Step by Step

The workflow below is the one to use when you want to understand and control every byte. It uses only `awk` for the conversion and `seqkit` for verification.

### Step 1. Inspect the input file

Look at the first few lines and count records.

```bash
head -4 reads.fa
grep -c '^>' reads.fa
awk '/^>/{n++} !/^>/{if(length($0)>0) c++} END{print n" records, "c" non-empty sequence lines"}' reads.fa
```

Expected output for a small two-record file:

```
>read1 sample=A
ACGTACGTAC
GTACGT
>read2
TTTTAAAA
2
2 records, 3 non-empty sequence lines
```

The mismatch between record count and sequence line count tells you the file is wrapped. That is legal FASTA and the reason a naive line-by-line converter fails.

Also check for Windows line endings, which leave a carriage return at the end of every line and produce quality strings with invisible trailing bytes.

```bash
file reads.fa
awk '/\r$/{n++} END{print n+0" lines end with CR"}' reads.fa
```

If the second command prints anything above zero, strip the carriage returns before proceeding.

```bash
sed -i 's/\r$//' reads.fa
```

### Step 2. Flatten and clean the FASTA

Convert to one sequence line per record, remove gaps, and force uppercase. SeqKit does all three in one pass.

```bash
seqkit seq -w 0 -g -u reads.fa > reads.clean.fa
```

- `-w 0` sets line width to zero, meaning no wrapping. Every sequence becomes a single line.
- `-g` removes gap characters (`-` and `.`), which are illegal in a read.
- `-u` uppercases the sequence. Soft-masked lowercase is valid FASTA but some downstream tools treat it inconsistently.

Confirm the flattening worked.

```bash
awk '/^>/{n++} !/^>/{if(length($0)>0) c++} END{print n" records, "c" sequence lines"}' reads.clean.fa
```

For a well-formed file the two numbers are now equal.

### Step 3. Pick a placeholder quality value

This is the only decision in the process that involves judgment. The quality string is ASCII-encoded Phred, so the character you choose sets the claimed error probability for every base. The table in the Phred section below gives the arithmetic. The practical rule:

- Use `I` (Phred 40) when the sequences genuinely came from high-accuracy short-read data and you simply lost the quality file.
- Use `5` (Phred 20) when you want a mid-range value that will survive most quality filters without being absurd.
- Do not use `!` (Phred 0) unless you intend for every read to be discarded.
- Do not use `I` for nanopore or other long-read data, where per-base accuracy is lower and Q40 is not a credible claim.

### Step 4. Convert with awk

This script handles wrapped FASTA, strips stray whitespace, and writes a valid four-line record for every entry.

```bash
awk -v q='I' '
function rep(c, n,   s, i) { s = ""; for (i = 0; i < n; i++) s = s c; return s }
/^>/ {
  if (seq != "") printf "@%s\n%s\n+\n%s\n", header, seq, rep(q, length(seq))
  header = substr($0, 2)
  seq = ""
  next
}
{
  gsub(/[ \t\r]/, "", $0)
  seq = seq $0
}
END {
  if (seq != "") printf "@%s\n%s\n+\n%s\n", header, seq, rep(q, length(seq))
}
' reads.clean.fa > reads.fastq
```

Save it as a file if you will reuse it.

```bash
cat > fa2fq.awk <<'EOF'
function rep(c, n,   s, i) { s = ""; for (i = 0; i < n; i++) s = s c; return s }
/^>/ {
  if (seq != "") printf "@%s\n%s\n+\n%s\n", header, seq, rep(q, length(seq))
  header = substr($0, 2)
  seq = ""
  next
}
{
  gsub(/[ \t\r]/, "", $0)
  seq = seq $0
}
END {
  if (seq != "") printf "@%s\n%s\n+\n%s\n", header, seq, rep(q, length(seq))
}
EOF

awk -v q='I' -f fa2fq.awk reads.clean.fa > reads.fastq
```

Expected output for the example input, shown with `head -8`:

```
@read1 sample=A
ACGTACGTACGTACGT
+
IIIIIIIIIIIIIIII
@read2
TTTTAAAA
+
IIIIIIII
```

The header text after the identifier is preserved verbatim. Note that many downstream tools truncate the read name at the first whitespace, so `read1 sample=A` becomes `read1` internally. That is normal and consistent, but it means the description field is not a reliable place to store sample identity.

### Step 5. Convert with seqtk

`seqtk` is the conventional one-liner. Its `seq` subcommand has an option to write a constant fake quality character.

```bash
seqtk seq -F 'I' reads.clean.fa > reads.fastq
```

If your input is gzip-compressed, seqtk reads it directly.

```bash
seqtk seq -F 'I' reads.clean.fa.gz > reads.fastq
```

Check `seqtk seq` usage on your installed version if the `-F` flag is rejected, since option sets differ between releases. The `awk` version above has no such dependency and behaves identically across platforms.

### Step 6. Check the four-line structure

Every record must have a `@` on line 1 of the block, a `+` on line 3, and a sequence and quality of equal length.

```bash
awk 'NR%4==1 && substr($0,1,1)!="@" {print "bad header at line " NR}' reads.fastq
awk 'NR%4==3 && substr($0,1,1)!="+" {print "bad plus line at line " NR}' reads.fastq
awk 'NR%4==0 {print "total records: " NR/4; exit}' reads.fastq
```

Clean files print only the record count. Any other line means the file is malformed and every downstream tool will fail in a different, confusing way.

### Step 7. Check sequence and quality lengths

This is the check that catches the two most common corruptions: a wrapped sequence that was not flattened, and a quality string built from the wrong length variable.

```bash
awk '
NR%4==1 {id=$0}
NR%4==2 {slen=length($0)}
NR%4==0 {
  if (length($0) != slen)
    printf "length mismatch at %s: seq=%d qual=%d\n", id, slen, length($0)
}
' reads.fastq
```

No output means every record is internally consistent. Add a summary line to confirm the check actually ran over all records.

```bash
awk 'END{print NR/4" records checked"}' reads.fastq
```

### Step 8. Summarize with SeqKit

`seqkit stats` gives a compact audit of the file and, for FASTQ input, reports quality-derived columns.

```bash
seqkit stats reads.fastq
```

The output is a table with one row per file.

```
file         format  type  num_seqs  sum_len  min_len  avg_len  max_len  Q20(%)  Q30(%)  AvgQual  GC(%)
reads.fastq  FASTQ   DNA          2       24        8     12.0       16   100.0   100.0     40.0   33.3
```

The `Q20(%)` and `Q30(%)` columns will read 100.0 whenever the placeholder is at or above Phred 20, because every base carries the same constant score. That is expected, and it is also the reason those columns stop being informative after a conversion.

Use `-a` for the fuller statistic set, including quartiles and N50.

```bash
seqkit stats -a reads.fastq
```

### Step 9. Compress and record provenance

FASTQ is roughly twice the size of the equivalent FASTA sequence content, since it carries an extra quality line plus two structural lines per record. Compress with a parallel gzip if available.

```bash
pigz -p 8 reads.fastq
```

Write a small note next to the file stating that the quality values are synthetic, which character was used, and the command that produced it. Institutional data layers that deliver sequencing files to downstream users rely on exactly this kind of metadata to keep file identity and provenance intact across projects [7]. A one-line README costs nothing and prevents a future analyst from treating `AvgQual 40.0` as a measurement.

## Phred Scores: What the Quality String Really Encodes

A Phred quality score `Q` maps to an estimated per-base error probability `P` through a base-10 logarithm.

```
Q = -10 * log10(P)
P = 10^(-Q/10)
```

The character stored in the FASTQ file is that score plus an ASCII offset of 33 for modern data.

```
character_code = Q + 33
```

| Character | ASCII | Phred Q | Error probability | Reads as |
|--|--|--|--|--|
| `!` | 33 | 0 | 1.0 (1 in 1) | every base wrong, tools discard |
| `#` | 35 | 2 | 0.63 | very poor |
| `+` | 43 | 10 | 0.1 (1 in 10) | poor |
| `5` | 53 | 20 | 0.01 (1 in 100) | common filter cutoff |
| `?` | 63 | 30 | 0.001 (1 in 1,000) | common filter cutoff |
| `I` | 73 | 40 | 0.0001 (1 in 10,000) | high-quality placeholder |
| `J` | 74 | 41 | 0.00008 | above typical short-read range |

A worked calculation makes the consequence concrete. For a 150 bp read, the expected number of erroneous bases is `150 * P`. At a constant Q40 that is `150 * 0.0001 = 0.015` errors per read, so roughly one read in 67 carries an error. At Q20 it is `150 * 0.01 = 1.5` errors per read. Same sequence, wildly different implied data quality, purely from the character you chose.

Two encoding pitfalls deserve names. Modern FASTQ uses Phred+33, meaning ASCII 33 corresponds to Q0. Legacy Solexa and early Illumina pipelines used Phred+64, where ASCII 64 corresponds to Q0. If a Phred+64 file is read as Phred+33, every score appears 31 points higher than it is. This matters when you inherit old files, not when you generate new ones, since anything you write with the scripts above is Phred+33 by construction.

## Options and Parameters That Matter

| Tool and flag | What it does | When to change it |
|--|--|--|
| `awk -v q='I'` | Sets the placeholder quality character | Lower it for long-read or low-accuracy data |
| `seqtk seq -F 'I'` | Writes a constant fake quality character | Use `5` for a mid-range placeholder |
| `seqkit seq -w 0` | Disables sequence line wrapping | Keep as is, FASTQ needs unwrapped sequence |
| `seqkit seq -g` | Removes gap characters | Required if the source is an alignment |
| `seqkit seq -u` | Uppercases the sequence | Optional, improves consistency |
| `seqkit stats -a` | Full statistics including quartiles and N50 | Use when you need length distributions |
| `seqkit fq2fa` | Converts FASTQ back to FASTA | Use for the reverse direction, drops quality |
| `pigz -p 8` | Parallel gzip compression | Match thread count to available cores |

## Common Mistakes and Limitations

### Mistakes that produce structurally invalid files

**Leaving the FASTA wrapped.** A script that assumes two lines per record will emit a sequence line longer than its quality line whenever the source wraps. The downstream error is usually a length mismatch reported at some arbitrary record number. Fix it with `seqkit seq -w 0` or the flattening `awk` logic above.

**Forgetting the plus line.** The `+` is not optional. Omitting it shifts every subsequent record by one line and produces a file that fails on record two rather than record one.

**Leaving carriage returns in place.** A FASTA edited on Windows carries `\r` at every line end. The quality string then ends with an invisible character, and length checks fail by one. Strip with `sed -i 's/\r$//'` before converting.

**Passing an alignment straight through.** Gap characters `-` and `.` are not nucleotides. A converted alignment produces FASTQ records that some parsers accept and others reject, which is worse than a clean failure. Degap first with `seqkit seq -g`.

**Converting protein FASTA.** The alphabet is wrong, the quality concept does not apply, and tools expecting nucleotide FASTQ will either error or silently misinterpret the data.

### Mistakes that produce valid but misleading files

**Assigning Phred 0.** Every read fails every quality filter. If your pipeline silently drops all reads and reports zero, this is the first thing to check.

**Assigning Phred 40 to long reads.** Nanopore and other long-read data do not carry Q40 per-base accuracy. Inflating the scores causes variant callers and consensus tools to treat every base as equally trustworthy.

**Trusting the Q20 and Q30 columns afterward.** They will read 100 percent because the scores are constant. Any quality-based statistic computed after conversion describes your placeholder, not the sequencing run.

**Collapsing duplicate identifiers.** FASTA permits duplicate headers and FASTQ tools frequently do not. Duplicate read names cause silent record collisions during sorting, pairing, or conversion to BAM, where sequencing data are normally stored after alignment [8]. Check for duplicates before converting.

**Assuming the description field survives.** Many tools keep only the first whitespace-delimited token of the header. Put sample and barcode information somewhere structured, not in the FASTA description.

**Writing an unstable output the second time.** If you convert the same FASTA twice with different placeholder characters, you get two different files with the same name. Record which command produced which file.

### Hard limitations

The fabricated quality cannot be recovered, corrected, or validated. Any analysis that depends on per-base confidence, including quality-score recalibration, error-rate modeling, and likelihood-based variant calling, is invalid on a converted file. The sequence content is exact. Everything about confidence is invented.

FASTA to FASTQ is not reversible in the sense that matters. Converting back with `seqkit fq2fa` gives you the original sequence, but the quality line is gone forever unless you kept the source file.

The format itself is not the bottleneck. Metagenomics workflows illustrate the broader problem: the number of file formats a pipeline must handle keeps growing, and each conversion point is a place where data can be dropped or mislabeled [9]. Keeping a small number of canonical formats and converting only at defined boundaries is easier to maintain than converting in every direction.

Library behavior reinforces this. Biopython's `SeqIO.write` refuses to write a FASTQ record that has no quality annotation, raising an error of the form `ValueError: No suitable quality scores found in letter_annotations of SeqRecord (id=read1)`. That refusal is a feature. It forces you to make the placeholder decision explicitly rather than inheriting a default.

## When FASTA to FASTQ Is the Wrong Move

If you have raw signal data and need real quality scores, do not convert a FASTA. Basecall the raw data directly. The raw files, whether POD5 on an Oxford Nanopore instrument or an alternative container, hold the signal that a basecaller converts into reads with genuine per-base quality, which is exactly the path taken in published nanopore workflows where basecalled FAST5 files were converted to FASTA and FASTQ with Poretools [4][5].

If you have a reference genome, the answer is also no. Reference FASTA is an input format for aligners, not a read format. Turning a chromosome into a fake read with uniform Q40 will confuse every tool that expects reads to be shorter than the reference.

If you have a protein FASTA, the answer is no for a different reason. Protein FASTA is a database format. Its role in a pipeline is to be searched against, and its absence from a deposited dataset is enough to block reanalysis entirely [2].

If you have assembled contigs and a tool insists on FASTQ, the honest options are to find a tool that accepts FASTA or to state clearly in your methods that qualities were synthetic. Converting silently and reporting Q30 statistics afterward is the one approach that produces a published result nobody can reproduce.

## Frequently Asked Questions

### Can I convert FASTA to FASTQ without losing information?

No. The sequence survives exactly, but FASTA has no quality data to carry across, so every quality score in the output is invented. The sequence is preserved and the confidence information is fabricated.

### What quality score should I assign when converting FASTA to FASTQ?

Use `I` (Phred 40) for high-accuracy short-read data and `5` (Phred 20) when you want converted reads to pass typical filters without claiming unrealistic accuracy. Never use `!`, which encodes Phred 0 and causes every read to be discarded.

### Why does my converted FASTQ fail quality filtering?

The placeholder character is almost always the cause. A Phred 0 placeholder fails every filter, and a very low placeholder fails most. Check the character used in the conversion command before investigating the sequences.

### Does the conversion preserve FASTA header lines?

The full header text after the `>` is preserved and the leading character becomes `@`. Many downstream tools keep only the first whitespace-delimited token as the read identifier, so put important metadata in a structured file rather than the header description.

### Can I convert a multi-line FASTA file to FASTQ?

Yes, but you must flatten it first. Either run `seqkit seq -w 0` or use a converter that accumulates sequence lines until it sees the next header. Line-by-line converters produce length mismatches on wrapped input.

### Is FASTA to FASTQ conversion reversible?

Only in one direction. Converting FASTQ to FASTA discards quality permanently, and converting FASTA to FASTQ invents quality that was never there. A round trip returns the original sequence and a fabricated quality string, not the original data.

### Which file is smaller, FASTA or FASTQ?

FASTA is smaller. For identical sequence content, FASTQ adds a quality line of the same length as the sequence plus a plus line per record, which roughly doubles the file before compression.

### Can I convert a protein FASTA file to FASTQ?

No. FASTQ is defined for nucleotide sequences with per-base quality scores, and protein data has neither the right alphabet nor a meaningful quality value. Protein FASTA is a search database format, not a read format.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Can I convert FASTA to FASTQ without losing information?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. The sequence survives exactly, but FASTA has no quality data to carry across, so every quality score in the output is invented. The sequence is preserved and the confidence information is fabricated."
      }
    },
    {
      "@type": "Question",
      "name": "What quality score should I assign when converting FASTA to FASTQ?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use I (Phred 40) for high-accuracy short-read data and 5 (Phred 20) when you want converted reads to pass typical filters without claiming unrealistic accuracy. Never use !, which encodes Phred 0 and causes every read to be discarded."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my converted FASTQ fail quality filtering?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The placeholder character is almost always the cause. A Phred 0 placeholder fails every filter, and a very low placeholder fails most. Check the character used in the conversion command before investigating the sequences."
      }
    },
    {
      "@type": "Question",
      "name": "Does the conversion preserve FASTA header lines?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The full header text after the greater-than sign is preserved and the leading character becomes an at sign. Many downstream tools keep only the first whitespace-delimited token as the read identifier, so put important metadata in a structured file rather than the header description."
      }
    },
    {
      "@type": "Question",
      "name": "Can I convert a multi-line FASTA file to FASTQ?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, but you must flatten it first. Either run seqkit seq -w 0 or use a converter that accumulates sequence lines until it sees the next header. Line-by-line converters produce length mismatches on wrapped input."
      }
    },
    {
      "@type": "Question",
      "name": "Is FASTA to FASTQ conversion reversible?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Only in one direction. Converting FASTQ to FASTA discards quality permanently, and converting FASTA to FASTQ invents quality that was never there. A round trip returns the original sequence and a fabricated quality string, not the original data."
      }
    },
    {
      "@type": "Question",
      "name": "Which file is smaller, FASTA or FASTQ?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "FASTA is smaller. For identical sequence content, FASTQ adds a quality line of the same length as the sequence plus a plus line per record, which roughly doubles the file before compression."
      }
    },
    {
      "@type": "Question",
      "name": "Can I convert a protein FASTA file to FASTQ?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. FASTQ is defined for nucleotide sequences with per-base quality scores, and protein data has neither the right alphabet nor a meaningful quality value. Protein FASTA is a search database format, not a read format."
      }
    }
  ]
}
</script>

## Sources

1. [SeqKit: A Cross-Platform and Ultrafast Toolkit for FASTA/Q File Manipulation.](https://pubmed.ncbi.nlm.nih.gov/27706213/)
2. [Preventing Proteomics Data Tombs Through Collective Responsibility and Community Engagement.](https://pubmed.ncbi.nlm.nih.gov/41571719/)
3. [DRAMP 5.0: an update integrating sequence and structure in a novel FASTS format for AI-driven design of antimicrobial peptides.](https://pubmed.ncbi.nlm.nih.gov/42876369/)
4. [The enduring advantages of the SLOW5 file format for raw nanopore sequencing data.](https://pubmed.ncbi.nlm.nih.gov/41100284/)
5. [MinION nanopore sequencing identifies the position and structure of bacterial antibiotic resistance determinants in a multidrug-resistant strain of enteroaggregative Escherichia coli.](https://pubmed.ncbi.nlm.nih.gov/30235111/)
6. [Streamline Protocol for Bulk-RNA Sequencing: From Data Extraction to Expression Analysis.](https://pubmed.ncbi.nlm.nih.gov/41637157/)
7. [MetaServe: a lightweight, metadata-aware governance and delivery layer for pre-publication research omics data.](https://pubmed.ncbi.nlm.nih.gov/42323537/)
8. [Atlantool: a command line tool to retrieve DNA and RNA sequencing reads from BAM files by the read identifier.](https://pubmed.ncbi.nlm.nih.gov/41230488/)
9. [CAMP: a modular metagenomics analysis system for integrated multistep data exploration.](https://pubmed.ncbi.nlm.nih.gov/41551931/)