Summarise Genetic Variants
Contents
Summarise Genetic Variants¶
Introduction¶
This notebook recreates Supplementary Table 3 from the Pf7 paper.
This table summarises the quantities and types of genetic variants found in the Pf7 dataset.
This notebook should take approximately two minutes to run.
Setup¶
Install the malariagen Python package:
!pip install -q --no-warn-conflicts malariagen_data
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.2/133.2 kB 2.7 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1/3.1 MB 42.6 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.4/10.4 MB 94.4 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.6/3.6 MB 66.3 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 302.5/302.5 kB 25.3 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 138.7/138.7 kB 16.8 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 20.9/20.9 MB 66.4 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.1/8.1 MB 105.4 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 206.9/206.9 kB 17.2 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 233.6/233.6 kB 19.4 MB/s eta 0:00:00
?25h Preparing metadata (setup.py) ... ?25l?25hdone
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.7/6.7 MB 76.8 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.6/1.6 MB 59.9 MB/s eta 0:00:00
?25h Building wheel for asciitree (setup.py) ... ?25l?25hdone
Import the required libraries
import numpy as np
import dask
import dask.array as da
from dask.diagnostics.progress import ProgressBar
import malariagen_data
import pandas as pd
import collections
from google.colab import drive
Access Pf7 Data¶
release_data = malariagen_data.Pf7()
variant_data = release_data.variant_calls()
variant_data
<xarray.Dataset> Dimensions: (variants: 10145661, alleles: 7, samples: 20864, ploidy: 2) Coordinates: variant_position (variants) int32 dask.array<chunksize=(8388608,), meta=np.ndarray> variant_chrom (variants) object dask.array<chunksize=(4194304,), meta=np.ndarray> sample_id (samples) object dask.array<chunksize=(20864,), meta=np.ndarray> Dimensions without coordinates: variants, alleles, samples, ploidy Data variables: variant_allele (variants, alleles) object dask.array<chunksize=(699051, 1), meta=np.ndarray> variant_filter_pass (variants) bool dask.array<chunksize=(10145661,), meta=np.ndarray> variant_is_snp (variants) bool dask.array<chunksize=(10145661,), meta=np.ndarray> variant_numalt (variants) int32 dask.array<chunksize=(8388608,), meta=np.ndarray> variant_CDS (variants) bool dask.array<chunksize=(10145661,), meta=np.ndarray> call_genotype (variants, samples, ploidy) int8 dask.array<chunksize=(167773, 100, 2), meta=np.ndarray> call_AD (variants, samples, alleles) int16 dask.array<chunksize=(23968, 100, 7), meta=np.ndarray>
This tells us we have 10,145,661 variants across 20,864 samples
The variants data include:
Information on where in the genome variants occur (variant_position, variant_chrom)
Which variants occur in which samples (sample_id)
Characteristics of the variants: - Did they pass QC? (variant_filter_pass) - Are they a single nucleotide polymorphism (variant_is_snp) - Do they occur in regions of the genome which encode genes? (variant_CDS)
Extract variables of interest for table¶
Here we define a dictionary which maps items from variant_data to new variables in a new dataframe ‘df_variants’
# Take data from the variant_data xarray object, process it, and create a new dataframe named df_variants containing variant information
variants_dict = collections.OrderedDict()
variants_dict['Type'] = pd.Series(variant_data['variant_is_snp'][:]).map({True: 'SNP', False: 'non-SNP'})
variants_dict['Multiallelic'] = pd.Series(variant_data['variant_numalt'][:] == 1).map({True: 'Bi-allelic', False: 'Multi-allelic'})
variants_dict['Coding'] = pd.Series(variant_data['variant_CDS'][:]).map({True: 'Coding', False: 'Non-coding'})
variants_dict['is_pass'] = variant_data['variant_filter_pass'][:]
variants_dict['num_alleles'] = variant_data['variant_numalt'][:] + 1
variants_dict['Chrom'] = pd.Series(variant_data['variant_chrom'][:])
df_variants = pd.DataFrame.from_dict(variants_dict)
df_variants.loc[df_variants['Type']=='non-SNP', 'Multiallelic'] = ''
print(df_variants.shape)
display(df_variants.head(3))
(10145661, 6)
Type | Multiallelic | Coding | is_pass | num_alleles | Chrom | |
---|---|---|---|---|---|---|
0 | non-SNP | Non-coding | False | 2 | Pf3D7_01_v3 | |
1 | non-SNP | Non-coding | False | 4 | Pf3D7_01_v3 | |
2 | non-SNP | Non-coding | False | 3 | Pf3D7_01_v3 |
Make a new dataframe for the final table¶
Here we summarise the quantities of each type of variant
# Aggregate the df_variants dataframe into a df of variant summaries
pd.DataFrame({
"Discovered variant positions": df_variants.groupby(['Type', 'Multiallelic', 'Coding'])['Type'].count(),
"Pass variant positions": df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Multiallelic', 'Coding'])['Type'].count(),
"% pass": (
df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Multiallelic', 'Coding'])['Type'].count() /
df_variants.groupby(['Type', 'Multiallelic', 'Coding'])['Type'].count()
),
"Alleles per pass position": df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Multiallelic', 'Coding'])['num_alleles'].mean(),
}).loc[:, ["Discovered variant positions", "Pass variant positions", "% pass", "Alleles per pass position"]]
Discovered variant positions | Pass variant positions | % pass | Alleles per pass position | |||
---|---|---|---|---|---|---|
Type | Multiallelic | Coding | ||||
SNP | Bi-allelic | Coding | 2215203 | 1668246 | 0.753089 | 2.000000 |
Non-coding | 1360243 | 845642 | 0.621685 | 2.000000 | ||
Multi-allelic | Coding | 476423 | 395788 | 0.830749 | 3.094869 | |
Non-coding | 345932 | 216045 | 0.624530 | 3.097526 | ||
non-SNP | Coding | 1931286 | 798903 | 0.413664 | 3.451484 | |
Non-coding | 3816574 | 1944035 | 0.509367 | 3.790311 |
Make the final table¶
Format the “% pass column” so that values look like percentages
Round the “Alleles per pass position” to one decimal place
Add a totals row
df_variant_summary = pd.concat([
pd.DataFrame({
"Discovered variant positions": df_variants.groupby(['Type', 'Coding', 'Multiallelic'])['Type'].count(),
"Pass variant positions": df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Coding', 'Multiallelic'])['Type'].count(),
"% pass": (
(df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Coding', 'Multiallelic'])['Type'].count() /
df_variants.groupby(['Type', 'Coding', 'Multiallelic'])['Type'].count())*100
).apply(lambda x: "{:.0f}%".format(x)), # Formatting % pass with 0 decimal places and %,
"Alleles per pass position": df_variants.loc[df_variants['is_pass']].groupby(['Type', 'Coding', 'Multiallelic'])['num_alleles'].mean().apply(lambda x: "{:.1f}".format(x)), # Formatting Alleles per pass position with 1 decimal place,
})
.reset_index()
,
pd.DataFrame({
"Discovered variant positions": df_variants.groupby(lambda x: 'Total')['Type'].count(), # Adding a totals row
"Pass variant positions": df_variants.loc[df_variants['is_pass']].groupby(lambda x: 'Total')['Type'].count(),
"% pass": (
(df_variants.loc[df_variants['is_pass']].groupby(lambda x: 'Total')['Type'].count() /
df_variants.groupby(lambda x: 'Total')['Type'].count())*100
).apply(lambda x: "{:.0f}%".format(x)),
"Alleles per pass position": df_variants.loc[df_variants['is_pass']].groupby(lambda x: 'Total')['num_alleles'].mean().apply(lambda x: "{:.1f}".format(x)), # Formatting Alleles per pass position with 1 decimal place,
})
.reset_index()
.rename(columns={'index': 'Type'}),
], sort=False).loc[:, ["Type", "Coding", "Multiallelic", "Discovered variant positions", "Pass variant positions", "% pass", "Alleles per pass position"]]
df_variant_summary = df_variant_summary.fillna("")
df_variant_summary = df_variant_summary.set_index(['Type', 'Coding', 'Multiallelic'])
df_variant_summary
Discovered variant positions | Pass variant positions | % pass | Alleles per pass position | |||
---|---|---|---|---|---|---|
Type | Coding | Multiallelic | ||||
SNP | Coding | Bi-allelic | 2215203 | 1668246 | 75% | 2.0 |
Multi-allelic | 476423 | 395788 | 83% | 3.1 | ||
Non-coding | Bi-allelic | 1360243 | 845642 | 62% | 2.0 | |
Multi-allelic | 345932 | 216045 | 62% | 3.1 | ||
non-SNP | Coding | 1931286 | 798903 | 41% | 3.5 | |
Non-coding | 3816574 | 1944035 | 51% | 3.8 | ||
Total | 10145661 | 5868659 | 58% | 2.9 |
Summary of discovered variant positions. We divide variant positions into those containing single nucleotide polymorphisms (SNPs) and non-SNPs (indels and combinations of SNPs and indels at the same position). We then further sub-divide each of these into those within exons (coding) and those in intronic or intergenic regions (non-coding). We further sub-divide SNPs into those containing only two alleles (bi-allelic) or those containing three or more alleles (multi-allelic). Discovered variant positions are unique positions in the reference genome where either SNP or indel variation was discovered by our analysis pipeline. Pass variant positions are the subset of discovered positions that passed our quality filters. Alleles per pass position shows the mean number of distinct alleles at each pass position; biallelic variants have two alleles by definition
Write the table to a file¶
We can output this to a location in Google Drive
First we need to connect Google Drive by running the following:
# You will need to authorise Google Colab access to Google Drive
drive.mount('/content/drive')
Mounted at /content/drive
# This will send the file to your Google Drive, where you can download it from if needed
# Change the file path if you wish to send the file to a specific location
# Change the file name if you wish to call it something else
df_variant_summary.to_excel('/content/drive/My Drive/Pf7_supp_table3.xlsx')