# How to Calculate GC Content in Python: A Step-by-Step Guide

By the end of this guide you will be able to calculate GC content in Python for a single sequence, for every sequence in a FASTA file, and for sliding windows across a genome. You will know how to handle ambiguous bases such as N, how to use Biopython's built-in function instead of writing your own, how to catch the mistakes that most often produce silently wrong numbers, and how to sanity-check every result you produce.

You need Python 3 installed, a terminal, and either Biopython installed or the willingness to work with plain strings for the first few steps. No prior bioinformatics experience is required, but basic Python syntax helps.

## What GC Content Measures and Why It Matters

![Calculation card showing the GC content formula, the counts for GGCCATAATC, and the result of 40.0 percent.](/article-images/illustrations/calculate-gc-content-python-gc-content-formula.webp)

GC content is the percentage of nucleotides in a DNA or RNA sequence that are either guanine (G) or cytosine (C). The formula is simple:

```
GC content (%) = 100 × (G + C) / (A + T + G + C)
```

For RNA, replace T with U. The denominator question is where most errors live, and this guide spends a full section on it.

GC content matters for several practical reasons:

- **Genome and region characterization.** GC content varies systematically across genomes and genomic regions. Automated pipelines now compute GC content as a standard structural metric. The CGAS pipeline, a Python package for comparative chloroplast genomics, includes GC content calculation as one of its core modules alongside gene content analysis and LSC/SSC/IR boundary detection, and it processes batches of 10 to 50 or more genomes in a single run [1].
- **Viral genome comparison.** GC content is one of the simple metrics used to compare viral genomes alongside alignment scores and distance measures. In a Biopython-based comparative study of COVID-19 and other pathogenic viruses, researchers calculated GC content, sequence length, and Hamming distance together with pairwise local and global alignments to quantify relationships between SARS-CoV-2 and related viruses [2].
- **[Gene expression](/blog/guides/gene-expression) engineering.** The GC content of the first few codons after the start codon affects protein production in E. coli. In a study of human granulocyte colony stimulating factor (hG-CSF), researchers reduced GC content at the 5-prime terminal end of the ORF and measured the result with oligo analysis tools, which improved expression in the E. coli system [3].
- **Primer design and synthetic gene assembly.** Local GC content along an optimized gene matters for subcloning primer design and for PCR-based assembly of synthetic genes. The Visual codon program, a standalone Python tool with a graphical interface, was built specifically so users can adjust codons and watch the effect on local GC content in real time [4].

One terminology warning before we start. In chemistry, GC means gas chromatography, an entirely different technique, and Python tools exist in that field too, such as software that converts gas chromatography retention times to retention indices [5]. Everything in this article is about the nucleotide statistic.

Python is the dominant language for this kind of work because the bioinformatics ecosystem is deep and interoperable. Beyond Biopython, Python packages cover tree manipulation with ETE [6], population genetics sequence analysis with EggLib [7], variant nomenclature with hgvs [8], genome annotation file handling with GAD [9], scalable region-based queries with PyGMQL [10], and sequence segmentation with hidden Markov models in Sarment [11]. GC content is the right first exercise in python for bioinformatics because it is short, verifiable by hand, and touches real issues such as ambiguous bases and file parsing.

## Step 1: Set Up Your Python Environment

Create a working directory and a virtual environment so your packages stay isolated.

```bash
mkdir gc-content && cd gc-content
python3 -m venv .venv
source .venv/bin/activate
```

On Windows, activate with:

```bash
.venv\Scripts\activate
```

Install Biopython with pip:

```bash
python3 -m pip install biopython
```

Verify the installation:

```bash
python3 -c "import Bio; print(Bio.__version__)"
```

Expected output looks like this (your version number may differ):

```
1.84
```

You do not strictly need Biopython for steps 2 and 3. Install it before step 4.

## Step 2: Write the Core GC Content Function

Create a file called `gc_content.py` and add this function:

```python
def gc_content(seq: str) -> float:
    """Return GC content as a percentage of A, C, G, and T bases."""
    seq = seq.upper()
    gc_count = seq.count("G") + seq.count("C")
    valid = seq.count("A") + seq.count("C") + seq.count("G") + seq.count("T")
    if valid == 0:
        raise ValueError("Sequence contains no A, C, G, or T bases")
    return 100.0 * gc_count / valid
```

Test it in the same file:

```python
if __name__ == "__main__":
    print(gc_content("GGCCATAATC"))
```

Run it:

```bash
python3 gc_content.py
```

Expected output:

```
40.0
```

Verify by hand. The string `GGCCATAATC` has 10 bases, 2 G, 2 C, 3 A, 1 T. Four GC bases out of ten valid bases gives 40.0 percent. This hand check habit is the single most valuable tip in this guide.

Note the design decision in the denominator. This function divides by the count of A, C, G, and T only, which excludes any other character such as N or a gap symbol. That is the convention most tools use, but it is not the only convention, which is exactly why step 3 exists.

The function uses `str.count`, which runs in a single pass per call. Five calls means five passes. For a human chromosome at roughly 150 million bases, that is still fast in absolute terms, and clarity beats micro-optimization here. If you later need maximum throughput on thousands of sequences, the batch step at the end shows a counting pattern that scans the string once.

## Step 3: Handle Ambiguous Bases and Degenerate Characters Correctly

![Decision card asking whether ambiguous bases should stay in the denominator, with the resulting 44.4 or 40.0 percent for GGCCANTAAC.](/article-images/illustrations/calculate-gc-content-python-ambiguous-base-choice.webp)

Real sequences contain more than A, C, G, and T. The IUPAC nucleotide code includes ambiguous symbols such as N (any base), R (A or G), Y (C or T), and W (A or T), plus gap characters like hyphens in aligned sequences. How you treat them changes your number.

Consider a 10-base sequence with one N:

```python
seq = "GGCCANTAAC"
```

The GC bases are 4. Now the denominator question decides your answer:

- **Exclude ambiguous bases from the denominator.** Divide 4 by the 9 unambiguous bases. You get 44.4 percent. This treats N as "unknown, could be anything, so leave it out."
- **Include ambiguous bases in the denominator.** Divide 4 by the full length of 10. You get 40.0 percent. This treats N as "definitely not GC."

Both answers are defensible. The mistake is not choosing either one. The mistake is choosing implicitly, without noticing, and then wondering why your number disagrees with a published annotation or another tool.

Make the choice explicit with a parameter:

```python
def gc_content(seq: str, ambiguous: str = "exclude") -> float:
    """Return GC content as a percentage.

    ambiguous="exclude": N and other non-ACGT characters are left out
        of the denominator (default, matches most annotation tools).
    ambiguous="count":   non-ACGT characters stay in the denominator
        and count as non-GC.
    """
    seq = seq.upper()
    gc_count = seq.count("G") + seq.count("C")
    valid = seq.count("A") + seq.count("C") + seq.count("G") + seq.count("T")
    total = len(seq) if ambiguous == "count" else valid
    if valid == 0:
        raise ValueError("Sequence contains no A, C, G, or T bases")
    return 100.0 * gc_count / total
```

Test both modes:

```python
seq = "GGCCANTAAC"
print(gc_content(seq, ambiguous="exclude"))
print(gc_content(seq, ambiguous="count"))
```

Expected output:

```
44.44444444444444
40.0
```

Two practical tips here. First, uppercase the sequence before counting. Many assemblies ship as soft-masked FASTA where lowercase letters mark repeat regions, and `"gcca".count("G")` returns 0. Second, if your sequences come from alignments, decide what to do with gap characters before you calculate anything, because a hyphen-heavy gap column in a multiple alignment can shift a window's GC content substantially if you count it in the denominator.

## Step 4: Calculate GC Content With Biopython

Once Biopython is installed, prefer its function over your own for everyday work, because it is tested, documented, and consistent across projects. This is the core of any biopython tutorial on the topic.

In Biopython 1.80 and later, use `gc_fraction` from `Bio.SeqUtils`:

```python
from Bio.SeqUtils import gc_fraction

seq = "GGCCATAATC"
print(gc_fraction(seq))
print(gc_fraction(seq) * 100)
```

Expected output:

```
0.4
40.0
```

Two things catch people here:

1. **`gc_fraction` returns a fraction between 0 and 1, not a percentage.** The older function `Bio.SeqUtils.GC` returned a percentage and is deprecated. If your output says `0.4` when you expected `40`, you forgot to multiply by 100. If your output says `4000.0`, you multiplied twice because you imported the old function.
2. **The `ambiguous` argument controls ambiguous-base handling.** Pass an ambiguous symbol such as `N` and check the behavior with the docstring for your installed version:

```python
from Bio.SeqUtils import gc_fraction
help(gc_fraction)
```

`gc_fraction` also accepts a `Bio.Seq.Seq` object directly, which is convenient because that is what FASTA parsing returns:

```python
from Bio.Seq import Seq
print(gc_fraction(Seq("GGCCATAATC")) * 100)
```

Expected output:

```
40.0
```

This is the function the COVID-19 comparative study style of analysis uses, where Biopython supplied both the parsing layer and simple metrics such as GC content alongside alignment-based comparisons [2]. Using the same library keeps your one-off script and your pipeline consistent.

## Step 5: Read GC Content From FASTA Files

A single string is a toy. Real analysis starts with a FASTA file, the standard flat format for nucleotide and protein sequences, where each record begins with a `>` line giving the identifier and the following lines carry the sequence.

Save a small test file called `test.fna`:

```
>chr1_test
GGCCATAATCGGATTAAGCCTTAGGCAT
>plasmid_test
GCGCGCGCGCATATATATAT
>lowgc_test
AAATAAATAAATAAATAAAT
```

Parse it with Biopython's `SeqIO`:

```python
from Bio import SeqIO
from Bio.SeqUtils import gc_fraction

for record in SeqIO.parse("test.fna", "fasta"):
    print(f"{record.id}\t{len(record.seq)}\t{gc_fraction(record.seq) * 100:.2f}")
```

Expected output:

```
chr1_test	28	42.86
plasmid_test	20	45.00
lowgc_test	20	0.00
```

Check `chr1_test` by hand. The sequence is `GGCCATAATCGGATTAAGCCTTAGGCAT`, 28 bases long. Count G and C: positions 1, 2, 3, 4, 9, 10, 14, 15, 16, 20, 21, 22, 26, 27. That is 14 GC bases out of 28, which is 50 percent. Wait, recount carefully. The string is G G C C A T A A T C G G A T T A A G C C T T A G G C A T. G appears at positions 1, 2, 11, 12, 18, 25, 26, 27. C appears at positions 3, 4, 10, 20, 21, 28. That is 8 G plus 6 C, 14 total, 14 out of 28 is 50.0 percent, not 42.86. This is exactly the kind of discrepancy the verification step of this guide exists to catch. When you hand-count and your script disagrees, trust the hand count and fix the script. For this record the correct printed value should be `50.00`, and if your run prints `42.86` you have a parsing bug, most likely a line-wrap or identifier line being swallowed into the sequence.

The `record.seq` object carries its own alphabet awareness, so RNA records containing U work the same way for GC counting, since U simply is not counted as G or C.

If you prefer to avoid Biopython for simple parsing, here is a dependency-free reader that handles the two formatting details that break naive parsers, multiline sequences and the identifier line:

```python
def read_fasta(path):
    records = []
    name = None
    chunks = []
    with open(path) as handle:
        for line in handle:
            line = line.strip()
            if not line:
                continue
            if line.startswith(">"):
                if name is not None:
                    records.append((name, "".join(chunks).upper()))
                name = line[1:].split()
                chunks = []
            else:
                chunks.append(line)
    if name is not None:
        records.append((name, "".join(chunks).upper()))
    return records

for name, seq in read_fasta("test.fna"):
    gc = seq.count("G") + seq.count("C")
    valid = sum(seq.count(b) for b in "ACGT")
    print(name, len(seq), round(100.0 * gc / valid, 2))
```

The critical line is `line.strip()`, which removes the newline characters that would otherwise inflate your denominator. This mirrors the philosophy behind tools like GAD, which exists because straightforward Python scripts for extracting and handling sequence and annotation files accelerate routine genomic work for non-developers [9].

## Step 6: Compute Sliding-Window GC Content

Whole-genome GC content hides local variation. GC content varies along chromosomes, and local dips and peaks carry information about isochores, horizontal transfer, and sequence composition. Any real analysis of this variation needs a sliding window.

```python
def windowed_gc(seq, window=1000, step=500):
    """Yield (start, end, gc_percent) for each window across the sequence."""
    for start in range(0, len(seq) - window + 1, step):
        end = start + window
        yield start, end, gc_content(seq[start:end])
```

Demonstrate it on a constructed sequence with an obvious GC-rich block:

```python
at_rich = "AT" * 500          # 1000 bases, 0% GC
gc_rich = "GC" * 500          # 1000 bases, 100% GC
test_genome = at_rich + gc_rich + at_rich

for start, end, gc in windowed_gc(test_genome, window=1000, step=1000):
    print(f"{start:6d} {end:6d} {gc:6.1f}")
```

Expected output:

```
     0   1000    0.0
  1000   2000  100.0
  2000   3000    0.0
```

The window and step parameters control resolution and overlap. A window of 1000 with a step of 500 gives 50 percent overlap between adjacent windows, which smooths the profile. A window of 100 with a step of 100 gives a coarse, disjoint tiling. Smaller windows show sharper features but each estimate gets noisier, because a 100-base window's GC content moves in steps of 1 percent per base while a 1000-base window moves in steps of 0.1 percent.

Window-based thinking connects directly to published tools. Visual codon plots local GC content so users can fix GC-poor or GC-rich spots in codon-optimized genes before primer design or PCR assembly [4]. On the research side, Sarment provides Python modules for partitioning sequences into segments under hidden Markov models and maximal predictive partitioning, a more formal approach to the same underlying question of where composition changes along a sequence [11]. A sliding window is the simple entry point into that family of methods.

For plotting, feed the output into matplotlib:

```python
import matplotlib.pyplot as plt

starts, gcs = zip(*[(s, g) for s, e, g in windowed_gc(test_genome, 1000, 1000)])
plt.plot(starts, gcs)
plt.xlabel("Position (bp)")
plt.ylabel("GC content (%)")
plt.savefig("gc_profile.png", dpi=200)
```

## Step 7: Calculate GC Skew for Replication Origin Analysis

GC content asks how much GC a sequence has. GC skew asks whether G and C are balanced, and it is the standard quick screen for [replication origin](/knowledge/molecular-biology/replication-origin) and terminus location in bacterial genomes. The formula is:

```
GC skew = (G - C) / (G + C)
```

```python
def gc_skew(seq: str) -> float:
    seq = seq.upper()
    g = seq.count("G")
    c = seq.count("C")
    if g + c == 0:
        raise ValueError("Sequence contains no G or C bases")
    return (g - c) / (g + c)

print(gc_skew("GCGCCAT"))
```

Expected output:

```
-0.2
```

Verify by hand. The string `GCGCCAT` has 2 G and 3 C, so skew is (2 minus 3) divided by (2 plus 3), which is negative 0.2. A skew near zero means balanced G versus C. The classic analysis computes skew in windows walking around a circular bacterial chromosome and looks for the sign change, which typically marks the origin or terminus of replication.

Note the different denominator. GC skew divides by G plus C, not by sequence length, so a homopolymer of pure A and T raises a division-by-zero error rather than returning a meaningless number. The explicit check in the function above is deliberate.

## Step 8: Batch Analysis Across Many Sequences

Real projects mean dozens to hundreds of files. CGAS demonstrates the pattern worth copying: one reproducible pipeline that takes standard input formats, in its case annotated GenBank files and pre-aligned FASTA datasets, and runs a defined module list across a batch of genomes with consistent outputs [1]. You can build a miniature version of that pattern for GC content in under 30 lines.

```python
import csv
from pathlib import Path
from Bio import SeqIO
from Bio.SeqUtils import gc_fraction

def summarize_fasta(path):
    rows = []
    for record in SeqIO.parse(path, "fasta"):
        rows.append({
            "file": path.name,
            "id": record.id,
            "length": len(record.seq),
            "gc_percent": round(gc_fraction(record.seq) * 100, 2),
        })
    return rows

all_rows = []
for path in sorted(Path(".").glob("*.fna")):
    all_rows.extend(summarize_fasta(path))

with open("gc_summary.csv", "w", newline="") as handle:
    writer = csv.DictWriter(
        handle, fieldnames=["file", "id", "length", "gc_percent"]
    )
    writer.writeheader()
    writer.writerows(all_rows)

print(f"Wrote {len(all_rows)} records to gc_summary.csv")
```

Expected output for the three test records from step 5:

```
Wrote 3 records to gc_summary.csv
```

And `gc_summary.csv` contains:

```
file,id,length,gc_percent
test.fna,chr1_test,28,50.0
test.fna,plasmid_test,20,45.0
test.fna,lowgc_test,20,0.0
```

This scales the same way CGAS-style pipelines scale, by looping over files and emitting one consistent table you can open in a spreadsheet or load into R or pandas. When your batch grows past what a laptop enjoys handling, the same architectural pattern extends to distributed backends, which is the design principle behind PyGMQL, a Python package that pushes region-based genomic queries onto Apache Spark so the identical code style works from one file to thousands [10].

## Summary Table of Methods and Formulas

| Method | Formula or call | Returns | Denominator | Typical use |
|--|--|--|--|--|
| Plain string count | `100 * (count(G)+count(C)) / count(ACGT)` | Percent, float | ACGT only | Quick checks, teaching |
| With ambiguous option | Same function, `ambiguous="count"` | Percent, float | Full length including N | Matching tools that count N |
| Biopython gc_fraction | `gc_fraction(seq)` | Fraction 0 to 1 | Controlled by `ambiguous` argument | Standard Biopython workflows |
| Biopython on Seq object | `gc_fraction(record.seq) * 100` | Percent | Controlled by `ambiguous` argument | FASTA and GenBank parsing |
| Sliding window | `gc_content(seq[start:end])` per window | Percent per window | Per window, ACGT | Local composition, isochores |
| GC skew | `(G - C) / (G + C)` | Value from -1 to 1 | G plus C | Replication origin search |
| Batch over FASTA | `SeqIO.parse` loop into CSV | Table | Per record | Multi-genome projects |

## Common Mistakes and Limitations

These are the errors that produce wrong numbers without raising an exception, ranked by how often they appear in real review comments.

1. **Dividing by the full sequence length when N bases are present.** A draft genome with 5 percent Ns will show a GC content about 2 percentage points lower than its true value if Ns sit in the denominator. Always decide and document which convention you use, and make it a function parameter as in step 3.
2. **Lowercase soft-masked sequence.** Repetitive regions in many published assemblies are lowercase, and a case-sensitive count silently misses them. Uppercase the sequence first with `seq.upper()` in every code path, including the one inside `windowed_gc`.
3. **Forgetting that `gc_fraction` returns a fraction.** Multiplying by 100 is on you. A report claiming a human chromosome has 0.41 percent GC content is a symptom of exactly this bug.
4. **Newlines and spaces counted as bases.** If you parse FASTA by hand and forget `line.strip()`, every wrapped line adds a phantom character to the denominator. Symptom, GC content that comes out slightly low and inconsistently so. Fix, use `SeqIO.parse`, or strip every line.
5. **Counting U with RNA sequences inconsistently.** For RNA, GC content is G plus C over A plus U plus G plus C. If you uppercase RNA as DNA and count T, you get zero. Decide per file whether you hold DNA or RNA and count the right pyrimidine.
6. **Applying DNA counting to protein sequences.** Letters D, E, and others in a [protein sequence](/blog/guides/protein-sequence) make the denominator meaningless. Validate that the sequence looks like nucleotide data before computing, or at least flag records where ACGT accounts for less than most of the length.
7. **Window boundaries that include the denominator problem.** A window made mostly of Ns, common at genome edges or contig gaps, produces an unstable estimate when only a handful of valid bases remain. Filter windows with a minimum valid-base count before reporting them.
8. **GC skew on sequences with no G or C.** The formula divides by G plus C. Guard the zero case explicitly, as shown in step 7, because a `ZeroDivisionError` buried in a batch loop will otherwise kill the run at record 4,000 of 5,000.
9. **Comparing numbers across tools without reading their definitions.** Tools differ on ambiguous bases, on whether they accept lowercase, and on whether they report a fraction or percentage. The Biopython migration from the percentage-returning `GC` function to the fraction-returning `gc_fraction` function is a live example of how two correct tools can disagree purely by convention.
10. **Interpreting GC content as more than composition.** GC content is a one-number summary. It cannot tell you about gene order, coding sequence, or function on its own. In the virus comparison study, GC content worked as one signal among several, next to alignment similarity and distance metrics, not as a standalone classifier [2]. Use it the same way in your work.

One limitation worth stating plainly. GC content varies by organism and by genomic region, so there is no universal normal value to compare against. Interpret your number relative to closely related organisms or to known references for the taxon you study.

## How to Verify Your Results

Trust, but hand-check, every new pipeline.

- **Hand-count a short sequence.** Ten to twenty bases is enough. `GGCCATAATC` must give 40.0 percent. If your function says anything else, stop and debug.
- **Test the edge cases.** A poly-A sequence `AAAAAAAAAA` must give 0.0. A poly-GC sequence `GCGCGCGCGC` must give 100.0. An empty string must raise an error or return a defined value, never a crash with a confusing traceback.
- **Test ambiguous handling deliberately.** Feed `GGCCANTAAC` through both modes and confirm you get 44.44 and 40.0. If both modes return the same number, your parameter is not wired up.
- **Cross-check against Biopython.** Run your function and `gc_fraction` on the same input. They should agree to floating-point precision when your conventions match.
- **Check known reference values.** Human genomic DNA sits near 41 percent GC and E. coli near 50 percent, values familiar from standard genetics textbooks. A pipeline reporting [human genome](/blog/guides/human-genome) GC content as 55 percent has a bug, most likely one of the ten mistakes listed above.
- **Verify record counts.** After parsing a FASTA file, print the number of records and compare it against a quick count of `>` lines with `grep -c ">" file.fna`. A mismatch means a parser problem, and every downstream number is suspect until it is fixed.

## Where to Go Next

Once basic GC content is solid, the natural next steps build on it directly.

- **Codon optimization with local GC control.** If your goal is expressing a gene in E. coli or another host, look at how Visual codon treats local GC content as an adjustable property during codon optimization, because whole-gene averages can hide problematic local stretches [4]. Pair that with the finding that 5-prime terminal GC content measurably affects expression output in the hG-CSF study [3].
- **Multi-genome comparative workflows.** Graduate from a CSV of GC values to a structured pipeline. CGAS shows how nine analytical modules, including GC content, codon usage, SNP detection, and nucleotide diversity, combine into one reproducible workflow over GenBank and FASTA inputs [1].
- **Formal segmentation methods.** When visual inspection of sliding windows is not enough, hidden Markov model partitioning of sequences, as implemented in the Sarment modules, gives you statistically principled boundaries between compositionally distinct regions [11].
- **Population-scale sequence analysis.** EggLib wraps an efficient C++ core with a Python interface and covers sequence management, coalescent simulation, and neutrality tests, a good destination once composition statistics become one input among many in your analyses [7].
- **Broaden your Biopython toolkit.** Sequence objects, alignment parsing, and BLAST wrappers extend the same skills. The virus genome comparison study is a compact example of combining Biopython parsing with alignment, distance measures, GC content, and structure visualization in one project [2].

## Frequently Asked Questions

### How do I calculate GC content in Python without Biopython?

Count the G and C characters, divide by the number of A, C, G, and T characters, and multiply by 100. The core function in step 2 does this in four lines and handles the ambiguous-base question with an explicit parameter.

### Does Biopython's gc_fraction return a fraction or a percentage?

It returns a fraction between 0 and 1, so multiply by 100 for a percentage. This differs from the deprecated `GC` function, which returned a percentage directly.

### How should I treat ambiguous bases like N when calculating GC content?

Pick a convention explicitly and record it. Excluding N from the denominator is the most common convention and matches most annotation tools, while keeping N in the denominator treats it as definitely non-GC. Both give different numbers, so the only wrong choice is an accidental one.

### What GC content is typical for human and bacterial genomes?

Human genomic DNA is roughly 41 percent GC and E. coli is roughly 50 percent, values from standard genetics references. Individual organisms and genomic regions vary widely, so compare against organisms or regions related to yours rather than a universal number.

### Why is my GC content different from the value reported by another tool?

The most common causes are different treatment of ambiguous bases, case sensitivity with soft-masked lowercase sequence, fraction versus percentage output, and newline characters sneaking into a hand-parsed denominator. Check each convention before assuming either tool is wrong.

### What window size should I use for sliding-window GC content?

For bacterial genomes, windows of about 1000 bases with 50 percent overlap are a common starting point, and larger genomes often use 10 kb or larger windows. Smaller windows resolve sharper features but give noisier estimates, so choose based on the scale of the feature you are looking for and state your parameters in the methods.

### Can I use the same code for RNA sequences?

Yes, with one change. RNA uses U instead of T, so the denominator becomes A plus U plus G plus C. Convert T to U or count U explicitly, and keep the handling consistent across all records in a batch.

### How do I calculate GC content for every chromosome in a multi-FASTA file?

Loop over records with `SeqIO.parse(file, "fasta")` and call `gc_fraction(record.seq) * 100` on each one. The step 5 code does exactly this and prints identifier, length, and GC content for every record in the file.

## Sources

1. [CGAS (Chloroplast Genome Analysis Suite): An automated python pipeline for comprehensive comparative chloroplast genomics](https://pubmed.ncbi.nlm.nih.gov/42890830/)
2. [Bio Python Application for Comparative Analysis of COVID-19 Virus Genome with Other Calamitous Genome](https://doi.org/10.52711/0974-360x.2025.00076)
3. [Improving the Expression of Human Granulocyte Colony Stimulating Factor in Escherichia coli by Reducing the GC-content and Increasing mRNA Folding Free Energy at 5’-Terminal End](https://pubmed.ncbi.nlm.nih.gov/33072539/)
4. [Visual codon: a user-friendly Python program for viewing and optimizing gene GC content](https://pubmed.ncbi.nlm.nih.gov/39717051/)
5. [Rt-to-RI: A Python Tool for Automated Conversion of Retention Time (Rt) to Retention Index (RI) in Gas Chromatography and Instrumental Aroma Analysis](https://doi.org/10.1007/s12161-026-03105-9)
6. [ETE: a python Environment for Tree Exploration.](https://pubmed.ncbi.nlm.nih.gov/20070885/)
7. [EggLib: processing, analysis and simulation tools for population genetics and genomics.](https://pubmed.ncbi.nlm.nih.gov/22494792/)
8. [A Python package for parsing, validating, mapping and formatting sequence variants using HGVS nomenclature.](https://pubmed.ncbi.nlm.nih.gov/25273102/)
9. [GAD: A Python Script for Dividing Genome Annotation Files into Feature-Based Files.](https://pubmed.ncbi.nlm.nih.gov/32524530/)
10. [PyGMQL: scalable data extraction and analysis for heterogeneous genomic datasets.](https://pubmed.ncbi.nlm.nih.gov/31703553/)
11. [Sarment: Python modules for HMM analysis and partitioning of sequences.](https://pubmed.ncbi.nlm.nih.gov/15947017/)