Que fait polars-bio ?
Aperçu
polars-bio est une bibliothèque Python haute performance pour les opérations sur intervalles génomiques et les entrées/sorties de fichiers bioinformatiques, construite sur Polars, Apache Arrow et Apache DataFusion. Elle offre une API familière centrée sur les DataFrames pour l'arithmétique d'intervalles (chevauchement, plus proche voisin, fusion, couverture, complément, soustraction) ainsi que la lecture et l'écriture de formats bioinformatiques courants (BED, VCF, BAM, CRAM, GFF/GTF, FASTA, FASTQ).
Propositions de valeur clés :
- 6-38x plus rapide que bioframe sur des benchmarks génomiques réels
- Support du streaming/hors cœur pour les grands génomes via DataFusion
- Entrées/sorties natif cloud (S3, GCS, Azure) avec prédicat pushdown
- Deux styles d'API : fonctionnelle (
pb.overlap(df1, df2)) et chaînage de méthodes (df1.lazy().pb.overlap(df2)) - Interface SQL pour les données génomiques via le moteur SQL DataFusion
Quand utiliser cette compétence
Utilisez cette compétence quand :
- Vous effectuez des opérations sur intervalles génomiques (chevauchement, plus proche voisin, fusion, couverture, complément, soustraction)
- Vous lisez/écrivez des formats de fichiers bioinformatiques (BED, VCF, BAM, CRAM, GFF/GTF, FASTA, FASTQ)
- Vous traitez de grands ensembles de données génomiques qui ne tiennent pas en mémoire (mode streaming)
- Vous exécutez des requêtes SQL sur des fichiers de données génomiques
- Vous migrez de bioframe vers une alternative plus rapide
- Vous calculez la profondeur de lecture/pileup à partir de fichiers BAM/CRAM
- Vous travaillez avec des DataFrames Polars contenant des intervalles génomiques
Démarrage rapide
Installation
Nécessite Python 3.11–3.14 (voir PyPI).
uv pip install "polars-bio==0.31.0"
Pour la compatibilité pandas (pandas ≥3.0) :
uv pip install "polars-bio[pandas]==0.31.0"
Exemple de chevauchement basique
import polars as pl
import polars_bio as pb
# Créez deux DataFrames d'intervalles
df1 = pl.DataFrame({
"chrom": ["chr1", "chr1", "chr1"],
"start": [1, 5, 22],
"end": [6, 9, 30],
})
df2 = pl.DataFrame({
"chrom": ["chr1", "chr1"],
"start": [3, 25],
"end": [8, 28],
})
# API fonctionnelle (retourne un LazyFrame par défaut)
result = pb.overlap(df1, df2)
result_df = result.collect()
# Obtenez directement un DataFrame
result_df = pb.overlap(df1, df2, output_type="polars.DataFrame")
# API de chaînage de méthodes (via l'accesseur .pb sur LazyFrame)
result = df1.lazy().pb.overlap(df2)
result_df = result.collect()
Lecture d'un fichier BED
import polars_bio as pb
# Lecture immédiate (charge tout le fichier)
df = pb.read_bed("regions.bed")
# Analyse paresseuse (streaming, pour les gros fichiers)
lf = pb.scan_bed("regions.bed")
result = lf.collect()
Capacités principales
1. Opérations sur intervalles génomiques
polars-bio fournit 8 opérations principales d'intervalles pour l'arithmétique des plages génomiques. Toutes les opérations acceptent des DataFrames Polars avec les colonnes chrom, start, end (configurables). Toutes les opérations retournent un LazyFrame par défaut (utilisez output_type="polars.DataFrame" pour des résultats immédiats).
Opérations :
overlap/count_overlaps- Trouver ou compter les intervalles se chevauchant entre deux ensembles (overlap_output="left"retourne uniquement les correspondances de df1 depuis 0.30.0)nearest- Trouver les intervalles les plus proches (avec paramètres configurablesk,overlap,distance)merge- Fusionner les intervalles qui se chevauchent ou sont contigus au sein d'un ensemblecluster- Attribuer des identifiants de cluster aux intervalles qui se chevauchentcoverage- Calculer les comptages de couverture par intervalle (opération avec deux entrées)complement- Trouver les lacunes entre les intervalles dans un génomesubtract- Supprimer les parties d'intervalles qui chevauchent un autre ensemble
Exemple :
import polars_bio as pb
# Trouver les intervalles se chevauchant (retourne LazyFrame)
result = pb.overlap(df1, df2, suffixes=("_1", "_2"))
# Compter les chevauchements par intervalle
counts = pb.count_overlaps(df1, df2)
# Fusionner les intervalles qui se chevauchent
merged = pb.merge(df1)
# Trouver les intervalles les plus proches
nearest = pb.nearest(df1, df2)
# Collecter tout résultat LazyFrame en DataFrame
result_df = result.collect()
Référence : Voir references/interval_operations.md pour la documentation détaillée de toutes les opérations, les paramètres, les schémas de sortie et les considérations de performance.
2. Entrées/Sorties de fichiers bioinformatiques
Lisez et écrivez des formats bioinformatiques courants avec les fonctions read_*, scan_*, write_* et sink_*. Prend en charge le stockage cloud (S3, GCS, Azure) et la compression (GZIP, BGZF).
Formats supportés :
- BED - Intervalles génomiques (
read_bed,scan_bed,write_*via générique) - VCF - Variants génétiques (
read_vcf,scan_vcf,write_vcf,sink_vcf) - VCF Zarr - Magasins Zarr prêts à l'analyse (
read_vcf_zarr,scan_vcf_zarr; chemins de répertoires locaux) - BAM - Lectures alignées (
read_bam,scan_bam,write_bam,sink_bam) - CRAM - Alignements compressés (
read_cram,scan_cram,write_cram,sink_cram) - GFF - Annotations de gènes (
read_gff,scan_gff) - GTF - Annotations de gènes (
read_gtf,scan_gtf) - FASTA - Séquences de référence (
read_fasta,scan_fasta,write_fasta,sink_fasta) - FASTQ - Lectures de séquençage (
read_fastq,scan_fastq,write_fastq,sink_fastq) - SAM - Alignements texte (
read_sam,scan_sam,write_sam,sink_sam) - Hi-C pairs - Contacts chromatinaux (
read_pairs,scan_pairs)
Exemple :
import polars_bio as pb
# Lire un fichier VCF
variants = pb.read_vcf("samples.vcf.gz")
# Analyse paresseuse d'un fichier BAM (streaming)
alignments = pb.scan_bam("aligned.bam")
# Lire des annotations GFF
genes = pb.read_gff("annotations.gff3")
# Stockage cloud (paramètres individuels, pas un dict)
df = pb.read_bed("s3://bucket/regions.bed",
allow_anonymous=True)
Référence : Voir references/file_io.md pour les schémas de colonnes par format, les paramètres, les options de stockage cloud et le support de la compression.
3. Traitement de données SQL
Enregistrez des fichiers bioinformatiques en tant que tables et interrogez-les en utilisant le moteur SQL DataFusion. Combine la puissance de SQL avec les lecteurs génomiques de polars-bio.
import polars as pl
import polars_bio as pb
# Enregistrer des fichiers en tant que tables SQL (chemin en premier, nom= mot-clé)
pb.register_vcf("samples.vcf.gz", name="variants")
pb.register_bed("target_regions.bed", name="regions")
# Interroger avec SQL (retourne LazyFrame)
result = pb.sql("SELECT chrom, start, end, ref, alt FROM variants WHERE qual > 30")
result_df = result.collect()
# Enregistrer un Polars DataFrame en tant que table SQL
pb.from_polars("my_intervals", df)
result = pb.sql("SELECT * FROM my_intervals WHERE chrom = 'chr1'").collect()
Référence : Voir references/sql_processing.md pour les fonctions d'enregistrement, la syntaxe SQL et les exemples.
4. Opérations de pileup
Calculez la profondeur de lecture par base à partir de fichiers BAM/CRAM avec un calcul de profondeur prenant en compte CIGAR.
import polars_bio as pb
# Calculer la profondeur à travers un fichier BAM
depth_lf = pb.depth("aligned.bam")
depth_df = depth_lf.collect()
# Avec filtre de qualité
depth_lf = pb.depth("aligned.bam", min_mapping_quality=20)
Référence : Voir references/pileup_operations.md pour les paramètres et les schémas d'intégration.
Concepts clés
Systèmes de coordonnées
polars-bio utilise par défaut des coordonnées basées sur 1 (convention génomique). Cela peut être changé globalement :
import polars_bio as pb
# Passer aux coordonnées demi-ouvertes basées sur 0 (le défaut est basé sur 1 / False)
pb.set_option("datafusion.bio.coordinate_system_zero_based", True)
# Revenir au basé sur 1 (par défaut)
pb.set_option("datafusion.bio.coordinate_system_zero_based", False)
Les fonctions d'E/S acceptent également use_zero_based pour définir les métadonnées de coordonnées sur le DataFrame résultant :
# Lire BED avec des métadonnées explicites basées sur 0
df = pb.read_bed("regions.bed", use_zero_based=True)
Important : Les fichiers BED sont toujours demi-ouverts basés sur 0 dans le format de fichier. polars-bio gère automatiquement la conversion lors de la lecture des fichiers BED. Les métadonnées de coordonnées sont attachées aux DataFrames par les fonctions d'E/S et propagées à travers les opérations.
Deux styles d'API
API fonctionnelle - fonctions autonomes, entrées explicites :
result = pb.overlap(df1, df2, suffixes=("_1", "_2"))
merged = pb.merge(df)
API de chaînage de méthodes - via l'accesseur .pb sur LazyFrames (pas DataFrames) :
result = df1.lazy().pb.overlap(df2)
merged = df.lazy().pb.merge()
Important : L'accesseur .pb pour les opérations d'intervalles n'est disponible que sur LazyFrame. Sur DataFrame, .pb fournit uniquement les opérations d'écriture (write_bam, write_vcf, etc.).
Le chaînage de méthodes permet des pipelines fluides :
# Enchaîner les opérations d'intervalles (remarque : overlap génère des colonnes suffixées,
# donc renommer avant merge qui attend chrom/start/end)
result = (
df1.lazy()
.pb.overlap(df2)
.filter(pl.col("start_2") > 1000)
.select(
pl.col("chrom_1").alias("chrom"),
pl.col("start_1").alias("start"),
pl.col("end_1").alias("end"),
)
.pb.merge()
.collect()
)
Architecture sonde-construction
Pour les opérations avec deux entrées (overlap, nearest, count_overlaps, coverage), polars-bio utilise une stratégie de jointure sonde-construction :
- Le premier DataFrame est la sonde (itération)
- Le deuxième DataFrame est la construction (indexé pour la recherche)
Pour des performances optimales, passez le DataFrame le plus grand comme premier argument (sonde) et le plus petit comme second (construction).
Conventions de colonnes
Par défaut, polars-bio s'attend à des colonnes nommées chrom, start, end. Des noms de colonnes personnalisés peuvent être spécifiés via des listes :
result = pb.overlap(
df1, df2,
cols1=["chromosome", "begin", "finish"],
cols2=["chr", "pos_start", "pos_end"],
)
Types de retour et collecte des résultats
Toutes les opérations d'intervalles et pb.sql() retournent un LazyFrame par défaut. Utilisez .collect() pour matérialiser les résultats, ou passez output_type="polars.DataFrame" pour une évaluation immédiate :
# Paresseux (par défaut) - collectez quand nécessaire
result_lf = pb.overlap(df1, df2)
result_df = result_lf.collect()
# Immédiat - obtenez le DataFrame directement
result_df = pb.overlap(df1, df2, output_type="polars.DataFrame")
Traitement en streaming et hors cœur
Pour les ensembles de données plus grands que la RAM disponible, utilisez les fonctions scan_* et l'exécution en streaming :
# Analyser les fichiers paresseusement
lf = pb.scan_bed("large_intervals.bed")
# Traiter avec le streaming Polars (nécessite polars ≥1.37, fourni avec polars-bio)
result = lf.collect(engine="streaming")
Le streaming DataFusion est activé par défaut pour les opérations d'intervalles, traitant les données par lots sans charger l'ensemble du jeu de données en mémoire.
Pièges courants
-
Accesseur
.pbsur DataFrame vs LazyFrame : Les opérations d'intervalles (overlap, merge, etc.) ne sont disponibles que surLazyFrame.pb.DataFrame.pbn'a que des méthodes d'écriture. Utilisez.lazy()pour convertir avant d'enchaîner les opérations d'intervalles. -
Retours LazyFrame : Toutes les opérations d'intervalles et
pb.sql()retournentLazyFramepar défaut. N'oubliez pas.collect()ou utilisezoutput_type="polars.DataFrame". -
Inadéquations des noms de colonnes : polars-bio s'attend à
chrom,start,endpar défaut. Utilisez les paramètrescols1/cols2(sous forme de listes) si vos colonnes ont des noms différents. -
Métadonnées du système de coordonnées : Les opérations d'intervalles lisent les métadonnées de coordonnées des fonctions d'E/S ou du
config_metadu DataFrame. Pour les DataFrames construits manuellement, définissezdf.config_meta.set(coordinate_system_zero_based=True)(basé sur 0) ouFalse(basé sur 1). Si les métadonnées sont manquantes, polars-bio utilise le paramètre globaldatafusion.bio.coordinate_system_zero_based(avec un avertissement). Définissezpb.set_option("datafusion.bio.coordinate_system_check", True)pour leverMissingCoordinateSystemErrorà la place. Des systèmes incompatibles entre les entrées lèventCoordinateSystemMismatchError. -
L'ordre sonde-construction est important : Pour overlap, nearest et coverage, le premier DataFrame est sondé contre le second. Échanger les arguments change quels intervalles apparaissent dans les colonnes de sortie gauche vs droite, et peut affecter les performances.
-
Limite de position INT32 : Les positions génomiques sont stockées comme des entiers 32 bits, limitant les coordonnées à environ 2,1 milliards. Cela est suffisant pour tous les génomes connus mais peut poser problème avec des espaces de coordonnées personnalisés.
-
Exigences d'index BAM :
read_bametscan_bamnécessitent un fichier index.baià côté du BAM. Créez-en un avecsamtools indexs'il manque. -
Exécution parallèle désactivée par défaut : Le parallélisme DataFusion est par défaut à 1 partition. Activez-le pour les grands ensembles de données :
pb.set_option("datafusion.execution.target_partitions", 8) -
CRAM a des fonctions séparées : Utilisez
read_cram/scan_cram/register_crampour les fichiers CRAM (pasread_bam). Les fonctions CRAM nécessitent un paramètrereference_path.
Meilleures pratiques
-
Utilisez
scan_*pour les gros fichiers : Préférezscan_bed,scan_vcf, etc. àread_*pour les fichiers plus grands que la RAM disponible. Les fonctions Scan permettent le streaming et le prédicat pushdown. -
Configurez le parallélisme pour les grands ensembles de données :
import os pb.set_option("datafusion.execution.target_partitions", os.cpu_count()) -
Utilisez la compression BGZF : Les fichiers compressés BGZF (
.bed.gz,.vcf.gz) supportent la décompression parallèle par blocs, significativement plus rapide que GZIP simple. -
Sélectionnez les colonnes tôt : Lorsque seules certaines colonnes sont nécessaires, sélectionnez-les tôt pour réduire l'utilisation mémoire :
df = pb.read_vcf("large.vcf.gz").select("chrom", "start", "end", "ref", "alt") -
Utilisez les chemins cloud directement : Passez les URI S3/GCS/Azure directement aux fonctions read/scan/register au lieu de télécharger d'abord les fichiers. L'accès authentifié utilise les identifiants de votre SDK cloud (
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY,GOOGLE_APPLICATION_CREDENTIALS, valeurs par défaut Azure) uniquement lorsque ces chemins cloud sont accédés :df = pb.read_bed("s3://my-bucket/regions.bed", allow_anonymous=True) -
Préférez l'API fonctionnelle pour les opérations uniques, le chaînage de méthodes pour les pipelines : Utilisez
pb.overlap()pour les opérations ponctuelles et.lazy().pb.overlap()lors de la construction de pipelines multi-étapes.
Ressources
references/
Documentation détaillée pour chaque capacité principale :
-
interval_operations.md - Les 8 opérations d'intervalles avec paramètres, exemples, schémas de sortie et conseils de performance. Référence principale pour l'arithmétique des plages génomiques.
-
file_io.md - Tableau des formats supportés, schémas de colonnes par format...