Contents
During Google Summer of Code 2025, I worked with Wellcome Sanger Tree of Life on a MalariaGEN project to classify malaria mosquito species from genomic data. The work used data from the Vector Observatory and focused on making classification practical when each sample contains a large number of genetic markers. My approach divided the genome into partitions, trained a Random Forest for each partition, and combined predictions from a selected subset of models.
Classification problem
Closely related mosquitoes in the Anopheles gambiae complex can be difficult to distinguish visually. Species identification matters because behaviour and insecticide susceptibility can differ within the group. The project used single nucleotide polymorphisms, or SNPs, as features for classification. Its engineering challenge was to retain useful signals from these markers while avoiding the cost of loading and training on the entire dataset at once.
The dataset contained roughly 22,000 samples, with markers distributed across the chromosome arms 2L, 2R, 3L, 3R, and X. At that scale, the representation and access pattern were important parts of the model design. Processing the dataset as one dense matrix would make memory consumption difficult to manage, while reducing the data indiscriminately could discard regions useful for distinguishing species. Partitioning offered a way to evaluate genomic regions separately before deciding which ones to use together.
Genotype representation
For an individual sample, a diploid genotype can be represented as an n × 2 array, with two allele values at each of n positions. Before training, I standardized equivalent genotype encodings and handled missing values, represented by −1, using genotype frequencies at the corresponding SNP. These preparation steps made the inputs consistent across partitions. Imputation is still an estimate, so it should not be interpreted as recovering the true missing genotype or as automatically eliminating bias.


The data was stored in Zarr, allowing compressed chunks to be accessed without reading the complete dataset into memory. Each partition could therefore be loaded and prepared independently. This separated the storage problem from the training loop and made it possible to work on individual regions within the available memory. It also provided a consistent access pattern for prediction, where only the selected genomic regions were needed.

Partitioned training
I divided the genome into approximately 150 segments, typically spanning around one million base pairs, and trained a separate Random Forest on each segment. For training, I used a balanced subset of about 1,000 samples to reduce computational cost and represent the species in the dataset. Stratified cross-validation maintained class proportions within the evaluation folds, and F1 scores were used to compare the predictive performance of different partitions.

Training individual models made the contribution of each region easier to inspect. A partition with a strong validation score was a candidate for the ensemble, while a weaker partition could be omitted. This did not mean that every partition corresponded to an independent biological signal; it provided an operational method for selecting useful regions under the project’s evaluation procedure. The training loop recorded model performance so that selection could be examined separately from fitting the individual forests.

Ensemble prediction
The prediction stage ranked partitions by their validation F1 scores and combined a selected group of models. Each selected model predicted a species for the new sample, and majority voting produced the final classification. Restricting prediction to those regions reduced the number of partitions that needed to be loaded and processed. The design also made ensemble size an explicit parameter that could be evaluated instead of assuming that every trained model should participate.

In the experiment that added partitions in ranked order, performance was strongest around the top 50–60 partitions. Adding lower-ranked regions did not consistently improve the result and could reduce it. This supported using a subset for this dataset and evaluation, rather than establishing a universal optimum of 50 models. A different sample distribution or validation design could change the appropriate selection.

Evaluation and interpretation
The reported cross-validation scores for the top partitions ranged from 98.47% to 99.98%. These figures describe the project’s internal evaluation and should not be treated as a guarantee for new populations or sequencing conditions. I also inspected feature importances in a strong model to examine which SNPs influenced its predictions. That inspection helped connect model behaviour to genomic regions, but feature importance alone does not demonstrate causality or establish that the classifier will generalize to every setting.

The resulting tool was packaged as an open-source Python library with trained models, a command-line interface, a programmatic API, and five supporting notebooks. The code and documentation describe how to load data and run predictions. The main contribution was a usable workflow for a large genomic dataset: chunked storage controlled data access, partitioned training made regional models manageable, and ensemble selection limited the work required at prediction time.