8: Data Analysis and Working with Data in Python
Chapter 8: Data Analysis and Working with Data in Python
Next-Generation Sequencing (NGS) produces massive volumes of structured tables: sample manifests, gene quantification matrices, variant call sets, and clinical outcome registries. Mastering data cleaning, regex pattern extraction, and robust tabular file manipulation is fundamental to bioinformatics.
1. Working with Complex Biological Datasets
Biological datasets frequently feature multi-delimiter structures, headers with metadata (such as VCF header lines starting with ##), and missing values. Processing them requires clean data pipelines.
2. Importing and Exporting Data
Streaming large genomic tables line-by-line prevents out-of-memory errors on large sequencing datasets:
# Memory-efficient streaming of large gene expression tables
def process_expression_data(input_file, output_file, min_cpm=1.0):
with open(input_file, "r") as infile, open(output_file, "w") as outfile:
header = infile.readline()
outfile.write(header)
passed_genes = 0
for line in infile:
parts = line.strip().split("\t")
gene_id = parts[0]
expression_values = [float(x) for x in parts[1:]]
# Keep genes expressed above threshold in at least 50% of samples
if sum(x >= min_cpm for x in expression_values) >= len(expression_values) / 2:
outfile.write(line)
passed_genes += 1
print(f"Filtering Complete: {passed_genes} genes retained.")
process_expression_data("raw_counts.tsv", "filtered_expression.tsv")3. Cleaning and Preprocessing Data
Genomic datasets frequently contain incomplete entries, duplicate identifiers, or inconsistent chromosome names (e.g. chr1 vs 1). Normalizing these ensures downstream compatibility with tools like GATK and IGV.
4. Regular Expressions in Biology (re module)
Regular expressions allow precise pattern matching across nucleotide motifs, restriction enzyme sites, and identifier parsing.
import re
# 1. Matching degenerate IUPAC motifs (EcoRI: GAATTC)
ecori_pattern = re.compile(r"GAATTC", re.IGNORECASE)
sequence = "ATGCGAATTCAGCTAGAATTCGG"
for match in ecori_pattern.finditer(sequence):
print(f"EcoRI Cleavage Site at position {match.start()}..{match.end()}")
# 2. Extracting Depth (DP) and Allele Frequency (AF) from VCF INFO field
info_field = "AC=2;AF=0.048;AN=42;DP=182;FS=1.2;MQ=59.4"
dp_match = re.search(r"DP=(\d+)", info_field)
af_match = re.search(r"AF=([0-9.]+)", info_field)
depth = int(dp_match.group(1)) if dp_match else 0
af = float(af_match.group(1)) if af_match else 0.0
print(f"Variant Metrics -> Read Depth: {depth}x | Allele Frequency: {af*100:.2f}%")5. Manipulating Data
Filtering, sorting, and combining patient cohorts by somatic mutation status and sequencing depth thresholds.
6. Plotting Data Basics
Previewing sequencing coverage distributions, quality curves, and insert size distributions using standard Python scripting.
Common Pitfalls & Troubleshooting Checklist
- Java Heap Exhaustion (GATK / Picard): If processes terminate with
java.lang.OutOfMemoryError, set your environment variable explicitly:export _JAVA_OPTIONS="-Xmx16g -Djava.io.tmpdir=/tmp". - Index Timestamp Mismatch: If tools complain that a BAM index is missing or out of date, regenerate with
samtools index -@ 4 sample.bam. - Conda Channel Conflicts: Avoid ABI incompatibilities between bioconda and conda-forge by setting strict priority:
conda config --set channel_priority strict. - Chromosome Naming Contamination: Always verify whether reference FASTA headers use
chr1or1; divergent naming causes silent alignment drops.