fasta_lake.quality#
fasta_lake.quality
#
Protein sequence quality filtering.
Optional filters that remove low-quality sequences BEFORE peptide matching.
Each filter is independently toggleable via SequenceQualityConfig.
These filters target common database artifacts:
- Poly-runs: Assembly artifacts producing
AAAAAAAAAA...sequences - Low-complexity: Sequences dominated by 1-2 amino acids (low entropy)
- Fragments: Truncated ORFs lacking start methionine
Design Principles
- No hidden transformations: every rejection is logged with reason
- Ambiguous residues may be replaced with X when explicitly configured
- Defaults and individual filter switches are defined by SequenceQualityConfig
QualityReport
dataclass
#
Report from sequence quality filtering.
Tracks how many sequences were removed by each filter and why.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
total_input
|
int
|
Total sequences before filtering. |
0
|
passed
|
int
|
Sequences that passed all filters. |
0
|
rejected_length
|
int
|
Rejected for length (too short or too long). |
0
|
rejected_poly_run
|
int
|
Rejected for homopolymeric runs. |
0
|
rejected_low_complexity
|
int
|
Rejected for low sequence entropy. |
0
|
rejected_fragment
|
int
|
Rejected as likely fragments. |
0
|
rejection_details
|
dict[str, str]
|
Mapping of rejected protein_id -> rejection reason. |
dict()
|
Source code in fasta_lake/quality.py
summary
#
Return summary dict for JSON stats output.
Source code in fasta_lake/quality.py
normalise_residues
#
Clean a protein sequence: strip trailing stop symbols, replace non-canonical ambiguous residues (B/J/Z) with X. Preserve selenocysteine and pyrrolysine.
Returns:
| Name | Type | Description |
|---|---|---|
cleaned |
str
|
Normalised uppercase sequence. |
n_replaced |
int
|
Count of ambiguous residues and internal stop symbols replaced. Trailing stop symbols are stripped and are not counted. |
Source code in fasta_lake/quality.py
noncanonical_fraction
#
Fraction of residues outside the 22 genetically encoded amino acids.
has_poly_run
#
Check if a sequence contains a homopolymeric run.
A homopolymeric run is run_length or more consecutive identical
amino acids (e.g., AAAAAAAAAA). These are typically assembly
artifacts or low-quality ORF predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Protein sequence (uppercase). |
required |
run_length
|
int
|
Minimum consecutive identical residues to trigger. Default: 10. |
10
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the sequence contains a poly-run. |
Examples:
>>> has_poly_run("MSKAAAAAAAAAAAGELFT", run_length=10)
True
>>> has_poly_run("MSKGEELFT", run_length=10)
False
Source code in fasta_lake/quality.py
compute_sequence_entropy
#
Compute normalized Shannon entropy of a protein sequence.
Measures amino acid diversity on a 0-1 scale:
- 0.0: completely uniform (e.g.,
AAAAAAA) - 1.0: maximum diversity (all 20 amino acids equally represented)
Uses log base 20 (number of canonical amino acids) for normalization, so the twenty-residue alphabet has maximum 1. Values can exceed 1 when additional residue symbols occur; the filtering scale is unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Protein sequence (uppercase). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Shannon entropy divided by log2(20). |
Examples:
>>> compute_sequence_entropy("AAAAAAAAAA")
0.0
>>> compute_sequence_entropy("ACDEFGHIKLMNPQRSTVWY") # all 20 AAs
1.0
Source code in fasta_lake/quality.py
is_fragment
#
Check if a protein is likely a fragment.
A fragment is defined as a sequence that:
1. Does NOT start with methionine (M)
2. Is shorter than length_threshold amino acids
Long sequences without M-start are less likely to be fragments (could be legitimate mature proteins with signal peptide removed).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Protein sequence (uppercase). |
required |
length_threshold
|
int
|
Maximum length for fragment classification. Sequences shorter than this AND lacking M-start are flagged. Default: 100. |
100
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the sequence is likely a fragment. |
Examples:
>>> is_fragment("SKGEELFT", length_threshold=100)
True
>>> is_fragment("MSKGEELFT", length_threshold=100)
False
>>> is_fragment("SKGEELFT" * 20, length_threshold=100) # 160 aa
False
Source code in fasta_lake/quality.py
filter_sequences_by_quality
#
filter_sequences_by_quality(proteins: dict[str, str], config: SequenceQualityConfig | None = None) -> tuple[dict[str, str], QualityReport]
Apply sequence quality filters to a protein dictionary.
Filters are applied in order: length -> poly-run -> low-complexity -> fragment. A sequence rejected by an earlier filter is not checked by later filters (first rejection wins).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proteins
|
dict[str, str]
|
Mapping of protein ID -> sequence. |
required |
config
|
SequenceQualityConfig or None
|
Quality filter configuration. If None, uses defaults (no optional filters enabled, only length filtering). |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
filtered |
dict[str, str]
|
Proteins that passed all filters. |
report |
QualityReport
|
Detailed rejection report. |
Examples:
>>> from fasta_lake.config import SequenceQualityConfig
>>> cfg = SequenceQualityConfig(reject_poly_runs=True, poly_run_length=5)
>>> proteins = {"p1": "MSKGEELFT", "p2": "MAAAAAAAAAGEELFT"}
>>> filtered, report = filter_sequences_by_quality(proteins, cfg)
>>> "p1" in filtered
True
>>> "p2" in filtered
False
Source code in fasta_lake/quality.py
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | |