SamReader est un analyseur en ligne de commande écrit en Python. Il lit un fichier SAM (Sequence Alignment/Map), vérifie sa structure, classe les alignements primaires au niveau des reads et des paires, puis produit soit un résumé, soit un dossier complet contenant les données regroupées, les diagnostics, les métadonnées et trois figures.
Le programme accepte aussi les SAM compressés ou placés dans des archives. Le format est reconnu à partir du contenu binaire, et non à partir de l'extension du fichier.
Version du programme : 0.1.0
Python : 3.10 ou version ultérieure
Interface principale :SamReader.py
- Objectif et périmètre
- Démarrage rapide
- Concepts biologiques et format SAM
- Choix de classification
- Architecture du projet
- Données et formats pris en charge
- Installation
- Utilisation détaillée
- Dossier de résultats
- Résultats de référence
- Discussion des résultats
- Validation et tests
- Erreurs, sécurité et résolution de problèmes
- Limites et hors périmètre
- Utilisation comme module Python
- Licence
Un fichier SAM décrit des alignements de séquences, appelées ici reads, sur une séquence de référence. Dans un fichier réel, il faut distinguer plusieurs situations :
- un read peut être aligné sur toute sa longueur utile ;
- une partie de ses bases peut avoir été coupée de l'alignement ;
- le read peut ne pas être aligné ;
- deux reads peuvent constituer une paire valide ;
- une paire peut être incomplète ou incohérente ;
- une même séquence peut posséder des alignements primaire, secondaire ou supplémentaire ;
- le SAM peut être brut, compressé, archivé ou porter une extension trompeuse.
SamReader transforme ces situations en catégories explicites, exclusives et reproductibles. Il répond ainsi à quatre besoins :
- valider le contenu avant de calculer des statistiques ;
- classer chaque alignement primaire selon une règle documentée ;
- regrouper les paires et signaler leurs anomalies ;
- exporter des résultats lisibles par un humain et réutilisables par un programme.
Deux modes sont proposés :
- le mode résumé écrit seulement les compteurs dans le terminal ou dans un fichier texte ;
- le mode dossier de résultats produit les résumés, les reads classés, les paires, les diagnostics, les métadonnées et les figures.
Le comportement a été conçu autour des principes suivants :
- l'extension ne prouve jamais qu'une entrée est un SAM ;
- une ligne invalide interrompt le traitement au lieu d'être ignorée ;
- chaque read primaire appartient à un seul statut individuel ;
- chaque paire valide appartient à une seule catégorie de paire ;
- les alignements secondaires et supplémentaires ne contaminent pas les statistiques principales ;
- les lignes SAM exportées restent identiques aux lignes d'origine, y compris leurs champs optionnels ;
- une destination existante n'est jamais écrasée ;
- un dossier incomplet n'est jamais publié comme un résultat valide.
Depuis la racine du projet :
conda env create --file environment.yml
conda activate samreaderAnalyser la petite fixture de démonstration et afficher son résumé :
python SamReader.py \
--input datas/classification_cases.sam \
--no-progressGénérer un dossier complet de résultats :
python SamReader.py \
--input datas/classification_cases.sam \
--results-dir classification_results \
--progressLa destination classification_results ne doit pas exister avant la commande.
Après une exécution réussie, elle contient 18 fichiers répartis entre les
résumés, les reads, les paires, les diagnostics et les figures.
Pour afficher toutes les options :
python SamReader.py --helpUn read est une séquence issue d'un processus de séquençage. Un alignement décrit la manière dont cette séquence est placée sur une référence. Une ligne SAM est plus précisément un enregistrement d'alignement : un même read peut être représenté par plusieurs lignes si un aligneur fournit des alignements secondaires ou supplémentaires.
Dans ce projet, le mot « read » désigne généralement un enregistrement SAM primaire lorsqu'il est question des compteurs individuels. Cette convention est importante : SamReader ne tente pas de reconstruire une molécule biologique à partir de tous ses alignements possibles.
Un fichier SAM est un fichier texte tabulé. Il peut commencer par un en-tête, puis contient les alignements :
@HD VN:1.6
@SQ SN:chr1 LN:1000
read_1 99 chr1 101 60 100M = 301 300 ACTG... IIII...
Les tabulations ont été représentées par des espaces dans cet exemple pour en faciliter la lecture. Dans un véritable SAM, les colonnes sont séparées par le caractère de tabulation.
SamReader reconnaît les types d'en-tête suivants :
@HD: métadonnées générales du fichier ;@SQ: description d'une séquence de référence ;@RG: groupe de reads ;@PG: programme ayant produit ou transformé le SAM ;@CO: commentaire.
Les lignes d'en-tête doivent précéder les alignements. Un SAM composé seulement d'un en-tête valide est accepté et produit des compteurs nuls. Un fichier totalement vide est refusé, car son contenu ne permet pas de l'identifier comme un SAM.
Chaque ligne d'alignement contient au moins onze champs :
| Nº | Champ | Rôle simplifié | Utilisation dans SamReader |
|---|---|---|---|
| 1 | QNAME |
identifiant du read ou de la paire | regroupement des deux membres d'une paire |
| 2 | FLAG |
masque de bits décrivant l'enregistrement | statut non aligné, primaire et position dans la paire |
| 3 | RNAME |
nom de la référence | validation structurelle |
| 4 | POS |
première position alignée sur la référence | validation numérique |
| 5 | MAPQ |
confiance de l'aligneur dans le placement | conservé, mais non utilisé pour la classification |
| 6 | CIGAR |
description compacte de l'alignement | validation et détection du clipping |
| 7 | RNEXT |
référence du partenaire | validation structurelle |
| 8 | PNEXT |
position du partenaire | validation numérique |
| 9 | TLEN |
longueur observée du fragment | validation numérique |
| 10 | SEQ |
séquence du read | validation de longueur et de contenu |
| 11 | QUAL |
qualités des bases | validation de longueur et de caractères |
Les champs situés après QUAL sont des champs optionnels. SamReader les
préserve dans les fichiers de reads générés, mais n'interprète pas leur
sémantique.
FLAG est un entier utilisé comme masque de bits. Plusieurs propriétés peuvent
donc être vraies simultanément.
| Bit | Valeur | Signification utile dans ce projet |
|---|---|---|
0x1 |
1 | le read appartient à une paire |
0x4 |
4 | le read lui-même n'est pas aligné |
0x40 |
64 | premier membre de la paire |
0x80 |
128 | second membre de la paire |
0x100 |
256 | alignement secondaire |
0x800 |
2048 | alignement supplémentaire |
Le bit 0x2, souvent appelé properly paired, n'est pas utilisé pour décider
si un read est entier ou partiel. Il traduit une décision de l'aligneur sur la
paire, alors que la classification de SamReader décrit la couverture du read
par son propre CIGAR.
Un CIGAR est une succession de couples « longueur + opération ». Par exemple,
5S95M signifie que cinq bases sont soft-clippées, puis que 95 positions sont
alignées.
| Opération | Définition | Consomme des bases de SEQ |
Consomme la référence | Rend le read partiel ici |
|---|---|---|---|---|
M |
correspondance ou différence | oui | oui | non |
I |
insertion par rapport à la référence | oui | non | non |
D |
délétion par rapport à la référence | non | oui | non |
N |
région sautée sur la référence | non | oui | non |
S |
soft clipping | oui | non | oui |
H |
hard clipping | non, les bases sont absentes de SEQ |
non | oui |
P |
remplissage | non | non | non |
= |
correspondance explicite | oui | oui | non |
X |
différence explicite | oui | oui | non |
Cette distinction explique deux exemples parfois contre-intuitifs :
50M1I49Mconsomme 100 bases du read, mais 99 positions de référence ;55M1D45Mconsomme 100 bases du read, mais 101 positions de référence.
Dans les deux cas, les 100 bases observées du read sont décrites dans le chemin d'alignement. L'insertion et la délétion signalent une différence entre le read et la référence ; elles ne signifient pas qu'une extrémité du read a été retirée de l'alignement. Elles restent donc classées comme alignements entiers selon le choix du projet.
À l'inverse, 5S95M et 95M5S indiquent explicitement qu'une partie du read
n'est pas intégrée à l'alignement principal. Le hard clipping porte la même
information, mais les bases coupées ne sont même plus présentes dans SEQ.
SamReader réserve les statistiques principales aux alignements primaires :
- un alignement
0x100est secondaire ; - un alignement
0x800est supplémentaire ; - un alignement ne portant aucun de ces bits est primaire.
Les alignements secondaire et supplémentaire sont conservés dans les diagnostics du mode complet, mais ne sont pas comptés parmi les trois statuts de reads ni dans les catégories de paires.
En single-end, chaque fragment est représenté par un read individuel. Il est classé parmi les reads, mais ne contribue pas aux statistiques de paires.
En paired-end, deux reads partagent normalement le même QNAME. Les bits
0x40 et 0x80 indiquent respectivement le premier et le second membre. Les
deux statuts individuels sont combinés pour obtenir une catégorie de paire.
Chaque alignement primaire appartient à exactement l'un des trois statuts.
flowchart TD
A[Alignement primaire validé] --> B{FLAG contient 0x4 ?}
B -- Oui --> U[NON_ALIGNE]
B -- Non --> C{CIGAR contient S ou H ?}
C -- Oui --> P[PARTIELLEMENT_ALIGNE]
C -- Non --> E[ENTIEREMENT_ALIGNE]
| Statut | Condition exacte | Exemples de CIGAR |
|---|---|---|
NON_ALIGNE |
le bit 0x4 est présent ; cette règle est prioritaire |
généralement * |
PARTIELLEMENT_ALIGNE |
pas de bit 0x4, et présence de S ou H |
5S95M, 95M5S, 10H90M |
ENTIEREMENT_ALIGNE |
pas de bit 0x4, ni S ni H |
100M, 50M1I49M, 55M1D45M, 50=1X49= |
La priorité du bit 0x4 évite de présenter comme partiellement aligné un read
que le SAM déclare explicitement non aligné, même si son CIGAR est inhabituel.
SamReader ne décide pas qu'un read est entier à partir :
- de
MAPQ: une faible confiance n'est pas synonyme de clipping ; - du bit
0x2: il décrit la cohérence de la paire selon l'aligneur ; - d'un CIGAR égal à
100M: tous les reads ne mesurent pas 100 bases ; - du nombre de différences, insertions ou délétions : ces événements restent décrits par l'alignement.
Cette stratégie mesure donc la complétude géométrique du read dans son alignement, pas la qualité, l'identité ni la vérité biologique du placement.
L'ordre des membres ne change pas la catégorie. Un couple « entier + partiel »
est donc classé ENTIER_PARTIEL, que le read partiel soit le premier ou le
second membre.
| Premier statut | Second statut | Catégorie |
|---|---|---|
| entier | entier | ENTIER_ENTIER |
| entier | partiel | ENTIER_PARTIEL |
| entier | non aligné | ENTIER_NON_ALIGNE |
| partiel | partiel | PARTIEL_PARTIEL |
| partiel | non aligné | PARTIEL_NON_ALIGNE |
| non aligné | non aligné | NON_ALIGNE_NON_ALIGNE |
La matrice symétrique correspondante est :
| Entier | Partiel | Non aligné | |
|---|---|---|---|
| Entier | ENTIER_ENTIER |
ENTIER_PARTIEL |
ENTIER_NON_ALIGNE |
| Partiel | ENTIER_PARTIEL |
PARTIEL_PARTIEL |
PARTIEL_NON_ALIGNE |
| Non aligné | ENTIER_NON_ALIGNE |
PARTIEL_NON_ALIGNE |
NON_ALIGNE_NON_ALIGNE |
Une paire valide doit contenir exactement deux alignements primaires de même
QNAME, l'un marqué 0x40, l'autre 0x80.
SamReader compte séparément :
- une paire incomplète lorsqu'un seul membre primaire apparié est présent ;
- une paire incohérente lorsque les indicateurs ne permettent pas de former
un premier et un second membre uniques, par exemple deux premiers membres,
deux seconds membres, aucun numéro de membre ou plus de deux alignements
primaires pour le même
QNAME.
Un read associé à une paire anormale reste valide au niveau individuel. Il est donc inclus dans son statut de read, mais aucune catégorie de paire valide ne lui est attribuée.
.
├── SamReader.py # programme et point d'entrée unique
├── environment.yml # environnement Conda reproductible
├── README.md # documentation d'installation et d'usage
├── LICENCE # licence CC BY-SA 4.0
├── datas/
│ ├── classification_cases.sam # petite fixture exhaustive
│ └── mapping.sam # jeu de validation de grande taille
└── tests/
├── expected_results.json # contrat chiffré de la petite fixture
├── EXPECTED_RESULTS.md # description lisible de ce contrat
├── helpers.py # création des variantes compressées
└── test_*.py # suite unittest
SamReader.py reste volontairement le seul point d'entrée. Les responsabilités
sont néanmoins séparées en classes et fonctions internes afin que validation,
classification, progression et écriture puissent être testées indépendamment.
flowchart LR
I[Fichier fourni] --> D[Détection par signature]
D --> C[Décompression récursive]
C --> A[Sélection du membre d'archive]
A --> V[Décodage UTF-8 et validation SAM]
V --> R[Classification des reads primaires]
R --> P[Regroupement et classification des paires]
R --> S[Compteurs et progression]
P --> S
S --> M{Mode choisi}
M -- Résumé --> T[stdout ou summary.txt]
M -- Dossier complet --> O[Reads, paires, diagnostics et résumés]
O --> F[Figures PNG]
F --> X[Publication atomique du dossier]
| Composant | Responsabilité principale |
|---|---|
SamRecord |
parser et valider les champs obligatoires d'un alignement |
MappingStatus |
représenter les trois statuts individuels |
ReadPair et PairCategory |
combiner deux statuts dans l'une des six catégories |
AnalysisSummary |
conserver les compteurs finaux |
analyze_sam_lines() |
parcourir et analyser un flux textuel SAM |
open_sam_input() |
préparer une entrée brute, compressée ou archivée |
ProgressReporter |
afficher des compteurs temporisés sur stderr |
ProgressSampler |
conserver au maximum 500 points pour la figure de progression |
ResultWriter |
écrire les reads, les paires et les diagnostics |
generate_figures() |
générer les trois PNG avec le moteur non interactif Agg |
generate_results() |
construire puis publier atomiquement le dossier complet |
Les alignements sont lus ligne par ligne. Les séquences complètes du SAM ne
sont pas accumulées en mémoire. Pour chaque paire, seules les métadonnées utiles
sont conservées : QNAME, numéro du membre et statut de classification.
La mémoire dépend donc principalement du nombre de paires distinctes encore
référencées, et non de la somme des longueurs de toutes les séquences. Les
QNAME d'une paire n'ont pas besoin d'être adjacents dans le fichier, mais cette
souplesse implique de conserver les métadonnées des paires jusqu'à la fin du
parsing.
Pour les couches compressées, des fichiers temporaires sont utilisés afin de ne pas charger le contenu décompressé en mémoire. L'espace disque temporaire doit donc être suffisant pour les données décompressées.
En mode complet, SamReader :
- refuse une destination déjà existante ;
- crée un dossier temporaire voisin de la destination ;
- analyse le SAM et écrit toutes les catégories ;
- vérifie que les effectifs écrits correspondent aux compteurs ;
- génère les résumés, métadonnées et figures ;
- renomme le dossier temporaire vers le nom final.
Si une étape échoue, le dossier temporaire est supprimé et aucun dossier final partiel n'est publié.
SamReader inspecte la signature binaire de l'entrée. Un fichier nommé
experiment.sam peut donc être refusé s'il contient des données binaires non
SAM, tandis qu'un SAM valide nommé simplement experiment est accepté.
Après chaque décompression, le contenu obtenu est inspecté de nouveau. Cette récursion permet de traiter plusieurs couches, par exemple :
reads.sam → gzip → bzip2 → reads.sam.gz.bz2
La profondeur maximale est fixée à cinq couches.
| Famille | Formats | Extensions usuelles indicatives | Remarque |
|---|---|---|---|
| texte | SAM UTF-8 non compressé | .sam ou aucune |
l'extension n'est pas utilisée comme preuve |
| gzip | gzip et BGZF lus séquentiellement | .gz, .bgz, .bgzf |
pris en charge par la bibliothèque standard |
| bzip2 | bzip2 | .bz2 |
pris en charge par la bibliothèque standard |
| LZMA | xz et LZMA-Alone | .xz, .lzma |
les deux signatures sont distinguées |
| Zstandard | flux Zstandard | .zst, .zstd |
nécessite le paquet zstandard |
| archive | ZIP | .zip |
les membres chiffrés ne sont pas pris en charge |
| archive | TAR | .tar |
seuls les fichiers réguliers sont acceptés |
| archive compressée | tar.gz, tar.bz2, tar.xz, tar.zst |
.tgz, .tbz2, .txz, .tzst, etc. |
détectée comme plusieurs couches |
| multi-compression | combinaisons récursives des formats précédents | par exemple .sam.gz.bz2 |
maximum de cinq couches |
BGZF est une variante structurée de gzip fréquemment utilisée en bioinformatique. SamReader le lit comme un flux gzip séquentiel ; il n'exploite pas son indexation aléatoire.
Sans --member, SamReader analyse le contenu des membres :
- si un unique SAM valide est trouvé, il est sélectionné automatiquement ;
- si aucun SAM valide n'est trouvé, l'analyse échoue ;
- si plusieurs SAM valides sont trouvés, l'analyse échoue et demande
--member.
--member doit contenir le chemin exact du membre dans l'archive, par exemple
nested/sample.sam. Le nom du membre n'a pas besoin de finir par .sam si son
contenu est valide.
Les archives ne sont jamais extraites implicitement dans le dossier de travail. SamReader refuse notamment :
- les chemins absolus ;
- les chemins contenant
..; - les liens symboliques et liens matériels ;
- les membres spéciaux qui ne sont pas des fichiers réguliers ;
- les noms dupliqués ;
- les membres ZIP chiffrés ;
- les archives corrompues ou tronquées.
| Fichier | Taille | Contenu | Rôle |
|---|---|---|---|
datas/classification_cases.sam |
2 402 octets | 3 en-têtes, 37 alignements dont 35 primaires | fixture courte couvrant les statuts, CIGAR, paires et anomalies |
datas/mapping.sam |
103 614 988 octets, soit environ 98,8 Mio | 2 en-têtes et 351 330 alignements primaires | validation réaliste des compteurs, performances, mémoire et sorties |
classification_cases.sam est une donnée de test construite pour contenir :
- les trois statuts individuels ;
- les six catégories de paires ;
- les catégories mixtes dans les deux ordres ;
- les opérations CIGAR
M,I,D,N,S,H,=,XetP; - un read single-end ;
- une paire incomplète et deux incohérentes ;
- un alignement secondaire et un supplémentaire.
mapping.sam contient 350 000 séquences de 100 bases et 1 330 séquences de
150 bases. Son en-tête désigne une référence nommée Reference. Le dépôt ne
documente pas l'origine biologique, le protocole de séquençage ni les paramètres
expérimentaux complets de ce fichier. Les résultats peuvent donc être discutés
comme validation informatique de la classification, mais une interprétation
biologique approfondie nécessiterait ces métadonnées.
Les résultats attendus de la petite fixture sont décrits dans
tests/EXPECTED_RESULTS.md et sérialisés dans tests/expected_results.json.
Cloner le dépôt et se placer à sa racine :
git clone https://github.com/RAVAO-Ravo/fun_sam_reader.git
cd fun_sam_reader- Conda, Miniconda ou une distribution compatible ;
- un système capable d'exécuter Python 3.10 ou plus récent ;
- assez d'espace temporaire pour décompresser l'entrée ;
- un terminal pour l'utilisation en ligne de commande.
Le programme ne requiert pas de compilateur et n'utilise pas pysam.
Créer l'environnement décrit par le projet :
conda env create --file environment.ymlPuis l'activer :
conda activate samreaderVérifier l'installation :
python --version
python -c "import matplotlib, zstandard; print(matplotlib.__version__, zstandard.__version__)"
python SamReader.py --help| Dépendance | Version minimale | Usage |
|---|---|---|
| Python | 3.10 | parsing, validation, archives, CLI et tests |
| Matplotlib | 3.7 | génération des figures PNG avec le backend Agg |
| zstandard | 0.21 | lecture des flux .zst et tar.zst |
Les autres formats reposent sur la bibliothèque standard : gzip, bz2,
lzma, zipfile et tarfile.
Le projet utilise uniquement environment.yml. Il ne fournit pas de
requirements.txt.
Si l'environnement samreader existe déjà, ses dépendances peuvent être
resynchronisées avec :
conda env update --name samreader --file environment.yml --pruneSamReader.py [-h] -i INPUT [-o OUTPUT | --results-dir RESULTS_DIR]
[--member MEMBER] [--progress | --no-progress]
| Option | Description |
|---|---|
-i, --input |
entrée SAM brute, compressée ou archivée ; obligatoire |
-o, --output |
écrit un nouveau fichier de résumé textuel |
--results-dir |
génère le dossier complet de résultats |
--member |
sélectionne le chemin exact d'un membre d'archive |
--progress |
force l'affichage périodique sur stderr |
--no-progress |
désactive la progression |
-h, --help |
affiche l'aide |
--output et --results-dir sont mutuellement exclusifs.
python SamReader.py \
--input datas/classification_cases.sam \
--no-progressLe résumé est écrit sur stdout. Cette séparation permet de le rediriger ou de
le transmettre à un autre outil sans y mélanger la progression.
python SamReader.py \
--input datas/classification_cases.sam \
--output classification_summary.txt \
--no-progressclassification_summary.txt doit être absent avant l'exécution. SamReader
refuse de l'écraser.
python SamReader.py \
--input datas/classification_cases.sam \
--results-dir classification_results \
--progressLe dossier parent doit exister, mais classification_results lui-même doit
être absent.
La commande ne change pas selon le compresseur :
python SamReader.py \
--input sample.sam.gz \
--output sample_summary.txt \
--progressLa même syntaxe convient par exemple à sample.sam.bz2, sample.sam.xz ou
sample.sam.zst.
python SamReader.py \
--input sequencing_run.tar.bz2 \
--results-dir sequencing_run_resultsLe membre est sélectionné automatiquement si lui seul contient un SAM valide.
python SamReader.py \
--input sequencing_run.zip \
--member nested/sample_A.sam \
--results-dir sample_A_resultsSans --member, une archive contenant plusieurs SAM valides est considérée
comme ambiguë et provoque une erreur de sélection.
Par défaut, la progression est activée lorsque stderr est connecté à un
terminal interactif. Elle est désactivée automatiquement lorsque ce n'est pas
le cas, sauf si --progress est fourni.
Une ligne ressemble à :
Lignes: 351,332 | Primaires: 351,330 | Entiers: 349,968 | Partiels: 47 | Non alignés: 1,315 | Paires: 175,665 | Temps: 00:00:51 | Débit: 6,813.8 reads/s
Les valeurs sont mises à jour au plus environ une fois par seconde :
- lignes parcourues ;
- alignements primaires ;
- reads entiers, partiels et non alignés ;
- paires provisoirement classées ;
- durée ;
- débit en reads par seconde.
La progression est écrite sur stderr. Le résumé ou le message final reste sur
stdout.
classification_results/
├── summary.txt
├── summary.tsv
├── run_metadata.json
├── reads/
│ ├── entierement_alignes.sam.gz
│ ├── partiellement_alignes.sam.gz
│ └── non_alignes.sam.gz
├── pairs/
│ ├── entier_entier.tsv.gz
│ ├── entier_partiel.tsv.gz
│ ├── entier_non_aligne.tsv.gz
│ ├── partiel_partiel.tsv.gz
│ ├── partiel_non_aligne.tsv.gz
│ └── non_aligne_non_aligne.tsv.gz
├── diagnostics/
│ ├── paires_incompletes.tsv
│ ├── paires_incoherentes.tsv
│ └── alignements_ignores.tsv
└── figures/
├── reads_par_statut.png
├── paires_par_categorie.png
└── progression_classification.png
Les fichiers sont créés même lorsqu'une catégorie vaut zéro. L'arborescence reste ainsi stable et exploitable automatiquement.
| Fichier | Contenu | Usage principal |
|---|---|---|
summary.txt |
résumé en français | lecture humaine |
summary.tsv |
table section, category, count |
import dans R, Python, tableur ou pipeline |
run_metadata.json |
entrée, membre, couches, paramètres, durée, version et compteurs | traçabilité et automatisation |
Extrait de summary.tsv :
section category count
general header_lines 3
reads ENTIEREMENT_ALIGNE 15
pairs ENTIER_PARTIEL 2
diagnostics INCOMPLETE_PAIRS 1run_metadata.json contient notamment :
- le chemin absolu de l'entrée ;
- le membre demandé et la source finalement sélectionnée ;
- les couches détectées, dans l'ordre extérieur vers intérieur ;
- la profondeur maximale autorisée ;
- les dates de début et de fin et la durée ;
- la version de SamReader ;
- les noms des figures et le nombre d'échantillons de progression ;
- une représentation structurée de tous les compteurs.
Les trois fichiers reads/*.sam.gz :
- contiennent uniquement les alignements primaires du statut correspondant ;
- recopient les lignes d'en-tête SAM dans chaque fichier ;
- préservent intégralement les onze champs obligatoires et les champs optionnels ;
- utilisent une compression gzip.
Ils constituent donc des sous-SAM directement réutilisables par des outils qui acceptent un SAM gzip séquentiel.
Chaque fichier pairs/*.tsv.gz possède les colonnes suivantes :
qname first_status second_statusIl contient une ligne par paire valide. Les statuts du premier et du second membre sont conservés même si la catégorie elle-même est indépendante de l'ordre.
paires_incompletes.tsv et paires_incoherentes.tsv utilisent les colonnes :
qname member_count mate_numbers statusesalignements_ignores.tsv décrit les alignements secondaires et
supplémentaires :
line_number qname flag reasonsCes fichiers permettent de distinguer une absence biologique ou technique de mapping d'un problème de structure des paires.
Matplotlib utilise le backend non interactif Agg, ce qui permet de générer les
figures sur un serveur sans interface graphique.
| Figure | Représentation | Détails de lecture |
|---|---|---|
reads_par_statut.png |
barres des trois statuts | affiche les effectifs exacts et les pourcentages |
paires_par_categorie.png |
barres des six catégories | affiche chaque valeur, y compris zéro |
progression_classification.png |
courbes cumulées pendant le parsing | permet de voir quand les statuts apparaissent dans le fichier |
Lorsque les effectifs positifs diffèrent d'un facteur d'au moins 100, une échelle symlog est utilisée. Elle rend visibles les petites catégories tout en conservant la valeur zéro.
La progression n'enregistre pas un point par read. Elle utilise un échantillonnage adaptatif limité à 500 points : lorsque la limite approche, les anciens points sont espacés davantage, et le dernier point reste toujours le compte final exact.
La petite fixture sert de contrat fonctionnel. Elle contient 37 alignements, dont 35 primaires.
| Statut | Effectif |
|---|---|
ENTIEREMENT_ALIGNE |
15 |
PARTIELLEMENT_ALIGNE |
11 |
NON_ALIGNE |
9 |
| Total | 35 |
| Catégorie | Effectif |
|---|---|
ENTIER_ENTIER |
1 |
ENTIER_PARTIEL |
2 |
ENTIER_NON_ALIGNE |
2 |
PARTIEL_PARTIEL |
1 |
PARTIEL_NON_ALIGNE |
2 |
NON_ALIGNE_NON_ALIGNE |
1 |
| Total | 9 |
| Mesure | Effectif |
|---|---|
| reads single-end | 11 |
| paires incomplètes | 1 |
| paires incohérentes | 2 |
| alignements secondaires ignorés | 1 |
| alignements supplémentaires ignorés | 1 |
Les 351 330 alignements sont primaires et forment 175 665 paires valides. Aucun read single-end et aucune paire anormale ne sont présents.
| Statut | Effectif | Pourcentage |
|---|---|---|
ENTIEREMENT_ALIGNE |
349 968 | 99,6123 % |
PARTIELLEMENT_ALIGNE |
47 | 0,0134 % |
NON_ALIGNE |
1 315 | 0,3743 % |
| Total | 351 330 | 100 % |
| Catégorie | Effectif | Pourcentage |
|---|---|---|
ENTIER_ENTIER |
174 968 | 99,6032 % |
ENTIER_PARTIEL |
32 | 0,0182 % |
ENTIER_NON_ALIGNE |
0 | 0 % |
PARTIEL_PARTIEL |
0 | 0 % |
PARTIEL_NON_ALIGNE |
15 | 0,0085 % |
NON_ALIGNE_NON_ALIGNE |
650 | 0,3700 % |
| Total | 175 665 | 100 % |
Ces valeurs ont été vérifiées sur l'entrée brute et sur les variantes compressées prises en charge.
Dans mapping.sam, tous les alignements primaires appartiennent à des paires
valides. Les statistiques individuelles se reconstruisent donc exactement à
partir des catégories de paires :
reads entiers = 2 × 174 968 + 32 = 349 968
reads partiels = 32 + 15 = 47
reads non alignés = 2 × 650 + 15 = 1 315
Cette égalité constitue un contrôle indépendant de l'absence de double comptage. Elle montre aussi que les 1 315 reads non alignés se répartissent en 650 paires entièrement non alignées et 15 paires associant un read non aligné à un read partiel.
La très grande majorité des reads, 99,6123 %, ne contient ni soft clipping ni
hard clipping et n'est pas marquée non alignée. De même, 99,6032 % des paires
sont ENTIER_ENTIER.
Les événements rares restent visibles grâce aux effectifs exacts et à l'échelle adaptée des figures :
- 47 reads seulement sont partiellement alignés ;
- 1 315 reads sont non alignés ;
- aucune paire
ENTIER_NON_ALIGNEn'est observée ; - aucune paire
PARTIEL_PARTIELn'est observée ; - 15 paires sont
PARTIEL_NON_ALIGNE.
Une catégorie nulle n'est ni supprimée ni considérée comme une erreur. Son absence peut être informative et l'arborescence stable facilite les analyses comparatives entre plusieurs échantillons.
Le terme ENTIEREMENT_ALIGNE ne signifie pas « alignement biologiquement
correct » ou « alignement parfait ». Un read peut contenir des mismatches, des
insertions, des délétions ou une faible valeur MAPQ tout en appartenant à cette
catégorie.
Inversement, un read partiel n'est pas nécessairement de mauvaise qualité : le clipping peut provenir d'un adaptateur, d'une variation structurale, d'une séquence chimérique, d'une limite de la référence ou d'une décision normale de l'aligneur.
Une interprétation biologique des proportions demanderait au minimum :
- l'origine de l'échantillon ;
- le protocole et la plateforme de séquençage ;
- la préparation des reads ;
- la référence utilisée ;
- le logiciel d'alignement et ses paramètres ;
- les distributions de
MAPQet les champs optionnels pertinents.
Ces informations ne sont pas toutes documentées dans le dépôt. Les résultats présentés ici doivent donc être compris avant tout comme une validation robuste du système de classification choisi.
Sur le jeu mapping.sam, l'activation de la progression temporisée a produit le
même résumé que l'exécution silencieuse. Lors d'une mesure indicative, elle a
ajouté environ 8,6 % au temps d'analyse. Cette valeur dépend de la machine, du
système de fichiers et du terminal ; elle ne constitue pas une garantie de
performance.
Un contrôle avec 10 000 alignements et des séquences logiquement multipliées par
1 000, de 0,2 Mo à 200 Mo de SEQ + QUAL, a augmenté le pic mémoire suivi
d'environ 6,8 % seulement. Ce résultat est cohérent avec l'architecture en flux.
La mémoire continue toutefois de croître avec le nombre de QNAME de paires,
car leurs métadonnées doivent être conservées jusqu'à la fin.
Après activation de l'environnement :
python -m unittest discover -vLa suite actuelle contient 78 tests couvrant :
- le parsing du CIGAR ;
- les trois statuts individuels ;
- les six catégories de paires ;
- les ordres des catégories mixtes ;
- les reads single-end, paires incomplètes et incohérentes ;
- les alignements secondaires et supplémentaires ;
- les champs SAM invalides ;
- les entrées brutes, compressées, multicompressées et archivées ;
- Zstandard, BGZF, ZIP et TAR ;
- la sélection et la sécurité des membres d'archive ;
- la CLI, les codes de sortie et la progression ;
- l'écriture atomique du dossier de résultats ;
- la structure et la lisibilité des trois PNG ;
- la borne mémoire de l'échantillonnage de progression.
python -m py_compile SamReader.pypython SamReader.py \
--input datas/classification_cases.sam \
--no-progressLe résultat doit correspondre à tests/expected_results.json et aux tableaux de
la section Résultats de référence.
| Code | Signification |
|---|---|
| 0 | exécution réussie |
| 2 | utilisation incorrecte de la CLI, produite par argparse |
| 3 | entrée absente, binaire, non UTF-8 ou SAM invalide |
| 4 | sélection d'archive impossible ou ambiguë |
| 5 | erreur de création ou de publication d'une sortie |
Une erreur structurelle indique autant que possible :
- le chemin de l'entrée ;
- le membre d'archive concerné ;
- le numéro de ligne ;
- la cause, par exemple un CIGAR invalide ou un nombre de colonnes insuffisant.
Exemple de forme générale :
Erreur d'entrée : sample.sam, line 42: an alignment requires at least 11 columns
Le traitement s'arrête immédiatement afin d'éviter de publier des compteurs calculés sur une entrée partiellement valide.
SamReader n'écrase ni un fichier --output, ni un dossier --results-dir.
Choisir un nouveau chemin ou déplacer explicitement l'ancien résultat avant de
relancer l'analyse.
Relancer la commande avec le chemin exact du membre :
python SamReader.py \
--input sequencing_run.zip \
--member nested/sample_A.sam \
--no-progressLe mode résumé n'importe pas Matplotlib. Le mode dossier complet en a besoin pour les figures. Resynchroniser l'environnement :
conda env update --name samreader --file environment.yml --pruneCette erreur ne concerne que les entrées Zstandard. Vérifier l'environnement :
python -c "import zstandard; print(zstandard.__version__)"Changer l'extension ne convertit pas un fichier. Un BAM renommé .sam reste un
BAM et sera refusé. Il faut convertir les formats binaires avec un outil dédié
avant de lancer SamReader.
La version actuelle ne prend pas en charge :
- BAM et CRAM, qui sont des formats d'alignement distincts et non de simples compressions de SAM ;
- les archives RAR et 7z ;
- l'analyse de plusieurs SAM d'une archive en une seule exécution ;
- les membres ZIP chiffrés ;
- une interface graphique ou un serveur web ;
- l'interprétation biologique de
MAPQ, des variants ou des champs optionnels ; - le calcul de couverture le long de la référence ;
- le tri ou l'indexation des alignements.
Autres limites à connaître :
- le texte SAM doit être décodable en UTF-8 ;
- l'imbrication est limitée à cinq couches ;
- aucun plafond explicite de taille décompressée n'est appliqué ; il faut donc rester prudent avec des archives provenant d'une source non fiable ;
- l'espace temporaire peut approcher la taille des couches décompressées ;
- la mémoire dépend du nombre de paires distinctes, même si elle ne dépend pas de la somme de toutes les séquences ;
- seuls les champs SAM nécessaires sont validés sémantiquement ; les champs optionnels sont préservés, mais pas interprétés ;
- la définition de « partiellement aligné » est un choix de ce projet fondé sur
SetH, pas une catégorie imposée par la spécification SAM.
La CLI est l'interface recommandée pour une analyse reproductible. Les principales fonctions peuvent néanmoins être importées :
from SamReader import MappingStatus, analyze_path
summary = analyze_path("datas/classification_cases.sam", progress=False)
print(summary.primary_records)
print(summary.read_counts[MappingStatus.ENTIEREMENT_ALIGNE])
print(summary.as_dict())Pour produire le dossier complet depuis Python :
from SamReader import generate_results
summary = generate_results(
"datas/classification_cases.sam",
"classification_results",
progress=True,
)Comme avec la CLI, la destination ne doit pas exister.
Le projet est distribué sous la licence Creative Commons
Attribution-ShareAlike 4.0 International, abrégée CC BY-SA 4.0. Le texte
fourni avec le projet se trouve dans LICENCE.
Cette licence autorise notamment à :
- copier et redistribuer le matériel ;
- adapter, transformer et construire à partir du matériel ;
- utiliser tout format ou médium.
Sous réserve notamment de :
- fournir une attribution appropriée ;
- indiquer les modifications effectuées ;
- fournir un lien vers la licence ;
- redistribuer les adaptations sous la même licence.
La licence n'apporte aucune garantie et ne remplace pas les autres droits qui
pourraient s'appliquer aux données. Pour les conditions juridiquement complètes,
consulter le fichier LICENCE et le
texte officiel CC BY-SA 4.0.