Skip to content

crispr_analysis_utils.guide_alignment.iupac

crispr_analysis_utils.guide_alignment.iupac

IUPAC nucleotide codes: matching, complements, and PAM patterns.

A PAM pattern such as NGG is a string of IUPAC codes. A genomic base matches a code when it is one of the bases the code stands for; N matches anything, a genomic N included, and a genomic N matches nothing else.

IUPAC_BASES module-attribute

IUPAC_BASES: dict[str, frozenset[str]] = {'A': frozenset('A'), 'C': frozenset('C'), 'G': frozenset('G'), 'T': frozenset('T'), 'R': frozenset('AG'), 'Y': frozenset('CT'), 'S': frozenset('CG'), 'W': frozenset('AT'), 'K': frozenset('GT'), 'M': frozenset('AC'), 'B': frozenset('CGT'), 'D': frozenset('AGT'), 'H': frozenset('ACT'), 'V': frozenset('ACG'), 'N': frozenset('ACGT')}

The bases each IUPAC nucleotide code stands for.

base_matches

base_matches(base: str, code: str) -> bool

Whether a genomic base matches an IUPAC code.

Parameters:

  • base (str) –

    One genomic base. Case is ignored.

  • code (str) –

    One IUPAC nucleotide code. Case is ignored.

Returns:

  • bool –

    True when code is N, or when base is one of the bases code stands for. A genomic N, or any character other than A, C, G and T, matches only N.

Raises:

  • ValueError –

    If code is not a single IUPAC nucleotide code.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def base_matches(base: str, code: str) -> bool:
    """Whether a genomic base matches an IUPAC code.

    Parameters
    ----------
    base
        One genomic base. Case is ignored.
    code
        One IUPAC nucleotide code. Case is ignored.

    Returns
    -------
    bool
        True when `code` is ``N``, or when `base` is one of the bases `code`
        stands for. A genomic ``N``, or any character other than A, C, G and
        T, matches only ``N``.

    Raises
    ------
    ValueError
        If `code` is not a single IUPAC nucleotide code.
    """
    code = code.upper()
    if len(code) != 1 or code not in IUPAC_BASES:
        raise ValueError(f"{code!r} is not an IUPAC nucleotide code.")
    return code == "N" or base.upper() in IUPAC_BASES[code]

pattern_matches

pattern_matches(sequence: str, pattern: str) -> bool

Whether a genomic sequence matches an IUPAC pattern, base by base.

Parameters:

  • sequence (str) –

    Genomic bases. Case is ignored.

  • pattern (str) –

    IUPAC codes, such as NGG. Case is ignored.

Returns:

  • bool –

    True when the two have the same length and every base matches the code at its position (see base_matches). A sequence shorter than the pattern, such as a flank cut short by the end of a contig, never matches.

Raises:

  • ValueError –

    If pattern holds a character that is not an IUPAC nucleotide code.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def pattern_matches(sequence: str, pattern: str) -> bool:
    """Whether a genomic sequence matches an IUPAC pattern, base by base.

    Parameters
    ----------
    sequence
        Genomic bases. Case is ignored.
    pattern
        IUPAC codes, such as ``NGG``. Case is ignored.

    Returns
    -------
    bool
        True when the two have the same length and every base matches the
        code at its position (see `base_matches`). A sequence shorter than the
        pattern, such as a flank cut short by the end of a contig, never
        matches.

    Raises
    ------
    ValueError
        If `pattern` holds a character that is not an IUPAC nucleotide code.
    """
    pattern = validate_pattern(pattern)
    if len(sequence) != len(pattern):
        return False
    return all(
        code == "N" or base in IUPAC_BASES[code]
        for base, code in zip(sequence.upper(), pattern, strict=True)
    )

complement

complement(sequence: str) -> str

Complement an IUPAC sequence, keeping its case.

Parameters:

  • sequence (str) –

    IUPAC nucleotide codes, in either case.

Returns:

  • str –

    Each code replaced by its complement (A-T, C-G, R-Y, K-M, B-V, D-H; S, W and N are their own complements). Characters outside the IUPAC alphabet are left unchanged.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def complement(sequence: str) -> str:
    """Complement an IUPAC sequence, keeping its case.

    Parameters
    ----------
    sequence
        IUPAC nucleotide codes, in either case.

    Returns
    -------
    str
        Each code replaced by its complement (A-T, C-G, R-Y, K-M, B-V, D-H;
        S, W and N are their own complements). Characters outside the IUPAC
        alphabet are left unchanged.
    """
    return sequence.translate(_COMPLEMENT)

reverse_complement

reverse_complement(sequence: str) -> str

Reverse-complement an IUPAC sequence, keeping its case.

Parameters:

  • sequence (str) –

    IUPAC nucleotide codes, in either case.

Returns:

  • str –

    The complement (see complement) read in reverse.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def reverse_complement(sequence: str) -> str:
    """Reverse-complement an IUPAC sequence, keeping its case.

    Parameters
    ----------
    sequence
        IUPAC nucleotide codes, in either case.

    Returns
    -------
    str
        The complement (see `complement`) read in reverse.
    """
    return complement(sequence)[::-1]

count_ambiguous

count_ambiguous(pattern: str) -> int

Count the characters of a pattern that are not A, C, G or T.

Parameters:

  • pattern (str) –

    IUPAC codes, such as NGG. Case is ignored.

Returns:

  • int –

    The number of positions holding a code other than A, C, G and T, such as the N of NGG.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def count_ambiguous(pattern: str) -> int:
    """Count the characters of a pattern that are not A, C, G or T.

    Parameters
    ----------
    pattern
        IUPAC codes, such as ``NGG``. Case is ignored.

    Returns
    -------
    int
        The number of positions holding a code other than A, C, G and T,
        such as the ``N`` of ``NGG``.
    """
    return sum(code not in "ACGT" for code in pattern.upper())

validate_pattern

validate_pattern(pattern: str) -> str

Uppercase an IUPAC pattern and check its alphabet.

Parameters:

  • pattern (str) –

    IUPAC codes, such as ngg.

Returns:

  • str –

    The pattern, uppercased.

Raises:

  • ValueError –

    If the pattern is empty or holds a character that is not an IUPAC nucleotide code.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def validate_pattern(pattern: str) -> str:
    """Uppercase an IUPAC pattern and check its alphabet.

    Parameters
    ----------
    pattern
        IUPAC codes, such as ``ngg``.

    Returns
    -------
    str
        The pattern, uppercased.

    Raises
    ------
    ValueError
        If the pattern is empty or holds a character that is not an IUPAC
        nucleotide code.
    """
    upper = pattern.upper()
    if not upper:
        raise ValueError("A PAM pattern must not be empty.")
    invalid = sorted(set(upper) - IUPAC_BASES.keys())
    if invalid:
        raise ValueError(
            f"PAM pattern {pattern!r} holds characters that are not IUPAC "
            f"nucleotide codes: {', '.join(map(repr, invalid))}."
        )
    return upper

validate_pams

validate_pams(pams: Iterable[str]) -> tuple[str, ...]

Check a list of PAM patterns: the primary PAM first, then the alternatives.

Parameters:

  • pams (Iterable[str]) –

    IUPAC patterns, such as ["NGG", "NAG", "NGA"]. The first is the primary PAM.

Returns:

  • tuple of str –

    The patterns, uppercased, in the order given.

Raises:

  • TypeError –

    If pams is a single string rather than a list of patterns.

  • ValueError –

    If the list is empty, a pattern is empty or holds a character that is not an IUPAC nucleotide code, a pattern appears twice, or the patterns differ in length.

Source code in src/crispr_analysis_utils/guide_alignment/iupac.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def validate_pams(pams: Iterable[str]) -> tuple[str, ...]:
    """Check a list of PAM patterns: the primary PAM first, then the alternatives.

    Parameters
    ----------
    pams
        IUPAC patterns, such as ``["NGG", "NAG", "NGA"]``. The first is the
        primary PAM.

    Returns
    -------
    tuple of str
        The patterns, uppercased, in the order given.

    Raises
    ------
    TypeError
        If `pams` is a single string rather than a list of patterns.
    ValueError
        If the list is empty, a pattern is empty or holds a character that is
        not an IUPAC nucleotide code, a pattern appears twice, or the patterns
        differ in length.
    """
    if isinstance(pams, str):
        raise TypeError("pams must be a list of PAM patterns, not a single string.")
    patterns = tuple(validate_pattern(pam) for pam in pams)
    if not patterns:
        raise ValueError("At least one PAM pattern is required.")
    repeated = sorted({p for p in patterns if patterns.count(p) > 1})
    if repeated:
        raise ValueError(f"PAM patterns appear more than once: {', '.join(repeated)}.")
    if len({len(p) for p in patterns}) > 1:
        raise ValueError(
            f"PAM patterns must all have the same length: {', '.join(patterns)}."
        )
    return patterns