Motifs¶
The Motif class, and the functions for creating, converting and modifying motifs. Motifs are immutable, so every function here returns new motifs rather than changing the ones it is given, and most also accept a list of motifs and return a list. The column functions at the end work on a single position of a motif, as a list or array of numbers.
Motif
¶
A sequence motif.
Motifs are immutable. The functions in the package, such as
convert_type() or
trim_motifs(), return new motifs, and
replace() returns a copy with some of the metadata changed. New motifs
are made with create_motif() or read from
a file.
Examples:
>>> import universalmotif as um
>>> motif = um.example_motifs()[3]
>>> print(motif)
Motif name: G-box
Family: bZIP
Organism: synthetic
Alphabet: DNA
Type: PCM
Strands: +-
Total IC: 7.9
Pseudocount: 1
Consensus: NCACGTGN
Target sites: 71
N C A C G T G N
A 17 6 62 0 2 0 12 27
C 9 62 3 69 1 15 1 10
G 23 1 2 0 64 3 58 21
T 22 2 4 2 4 53 0 13
The matrix has one row per letter and one column per position:
>>> motif.letters, motif.matrix.shape
(['A', 'C', 'G', 'T'], (4, 8))
>>> motif.matrix[:, 2].tolist()
[62.0, 3.0, 2.0, 4.0]
Metadata is changed by making a copy:
>>> renamed = motif.replace(name="G-box copy", organism=None)
>>> renamed.name, renamed.organism, motif.name
('G-box copy', None, 'G-box')
consensus
property
¶
consensus: str | None
One IUPAC letter per position, or None for custom alphabets. Gaps are not shown, so it has one letter per column even for a gapped motif, whose printout marks the gaps with dots. For a CWM each letter describes the shape of its column, however small its contributions.
strand
property
¶
strand: str
"+", "-" or "+-". Only DNA and RNA motifs have a reverse
strand, so for other alphabets it has no effect.
replace
¶
replace(
*,
name: str = ...,
altname: str | None = ...,
family: str | None = ...,
organism: str | None = ...,
nsites: float | None = ...,
pseudocount: float = ...,
bkg: Sequence[float] | Mapping[str, float] = ...,
bkgsites: float | None = ...,
strand: str = ...,
pval: float | None = ...,
qval: float | None = ...,
eval: float | None = ...,
extrainfo: Mapping[str, str] | None = ...,
) -> Motif
Return a copy with some metadata replaced.
The matrix, type and alphabet cannot be changed this way. Changing
the pseudocount, background or nsites recomputes the total IC
and the consensus.
create_motif
¶
create_motif(
input: object = None,
alphabet: str | None = None,
type: MotifType | None = None,
*,
letters: Sequence[str] | None = None,
name: str = "motif",
pseudocount: float = 0.0,
bkg: Sequence[float]
| Mapping[str, float]
| None = None,
nsites: int | None = None,
altname: str | None = None,
family: str | None = None,
organism: str | None = None,
bkgsites: float | None = None,
strand: str = "+-",
pval: float | None = None,
qval: float | None = None,
eval: float | None = None,
extrainfo: Mapping[str, str] | None = None,
seed: int | None = None,
) -> Motif
Create a motif.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input
|
object
|
What to build the motif from:
|
None
|
alphabet
|
str | None
|
|
None
|
type
|
MotifType | None
|
Type of the returned motif, a PPM by default. A matrix with negative
values needs its type given, as |
None
|
letters
|
Sequence[str] | None
|
Row letters of a matrix |
None
|
name
|
str
|
Motif metadata. |
'motif'
|
altname
|
str
|
Motif metadata. |
'motif'
|
family
|
str
|
Motif metadata. |
'motif'
|
organism
|
str
|
Motif metadata. |
'motif'
|
nsites
|
str
|
Motif metadata. |
'motif'
|
bkgsites
|
str
|
Motif metadata. |
'motif'
|
strand
|
str
|
Motif metadata. |
'motif'
|
pval
|
str
|
Motif metadata. |
'motif'
|
qval
|
str
|
Motif metadata. |
'motif'
|
eval
|
str
|
Motif metadata. |
'motif'
|
extrainfo
|
str
|
Motif metadata. |
'motif'
|
pseudocount
|
float
|
Pseudocount used when converting between types. |
0.0
|
bkg
|
Sequence[float] | Mapping[str, float] | None
|
Background frequencies, either one value per letter in alphabet order or a mapping from letters to values. Uniform by default. |
None
|
seed
|
int | None
|
Random seed, for random motifs only. |
None
|
Examples:
A motif from a consensus sequence:
>>> import universalmotif as um
>>> motif = um.create_motif("TATAWAW", name="TATA", nsites=10)
>>> print(motif)
Motif name: TATA
Alphabet: DNA
Type: PPM
Strands: +-
Total IC: 12
Pseudocount: 0
Consensus: TATAWAW
Target sites: 10
T A T A W A W
A 0 1 0 1 0.5 1 0.5
C 0 0 0 0 0.0 0 0.0
G 0 0 0 0 0.0 0 0.0
T 1 0 1 0 0.5 0 0.5
A count matrix from aligned sites:
>>> import universalmotif as um
>>> motif = um.create_motif(["TATAAA", "TATAAT", "TATATA"], type="PCM")
>>> motif.nsites, motif.consensus
(3.0, 'TATAWW')
>>> motif.matrix.tolist()
[[0.0, 3.0, 0.0, 3.0, 2.0, 2.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [3.0, 0.0, 3.0, 0.0, 1.0, 1.0]]
convert_type
¶
convert_type(
motifs: Motif,
type: MotifType,
pseudocount: float | None = ...,
relative_entropy: bool = ...,
) -> Motif
convert_type(
motifs: Iterable[Motif],
type: MotifType,
pseudocount: float | None = ...,
relative_entropy: bool = ...,
) -> list[Motif]
convert_type(
motifs, type, pseudocount=None, relative_entropy=False
)
Convert motifs to another type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
motifs
|
A motif or an iterable of motifs. |
required | |
type
|
|
required | |
pseudocount
|
Replaces each motif's own pseudocount. |
None
|
|
relative_entropy
|
For ICMs, compute heights as relative entropy against the background instead of Shannon information content. |
False
|
Notes
A motif without nsites is treated as having 100 sites when counts are
needed. The consensus and total IC are kept from the input motif.
Examples:
>>> import universalmotif as um
>>> ap1 = um.example_motifs()[1]
>>> ap1.type, ap1.consensus
('PCM', 'NTGASTCAN')
>>> ppm = um.convert_type(ap1, "PPM")
>>> ppm.matrix[:, 1].round(3).tolist()
[0.065, 0.025, 0.005, 0.905]
>>> [m.type for m in um.convert_type(um.example_motifs()[:3], "PWM")]
['PWM', 'PWM', 'PWM']
summarize_motifs
¶
summarize_motifs(
motifs, na_rm: bool = True
) -> pl.DataFrame
Summarize motifs as a polars DataFrame, one row per motif.
With na_rm=True, columns that are empty for every motif are dropped.
Examples:
>>> import universalmotif as um
>>> summary = um.summarize_motifs(um.example_motifs())
>>> summary.shape
(12, 8)
>>> summary.columns
['name', 'family', 'organism', 'consensus', 'alphabet', 'strand', 'icscore', 'nsites']
>>> summary["consensus"].to_list()[:4]
['KTATAWAWRN', 'NTGASTCAN', 'TGACGTCA', 'NCACGTGN']
>>> [round(x, 2) for x in summary["icscore"].to_list()[:4]]
[8.83, 9.65, 10.73, 7.9]
filter_motifs
¶
filter_motifs(
motifs: Iterable[Motif],
name: str | Iterable[str] | None = None,
altname: str | Iterable[str] | None = None,
family: str | Iterable[str] | None = None,
organism: str | Iterable[str] | None = None,
width: int | None = None,
alphabet: str | Iterable[str] | None = None,
type: str | Iterable[str] | None = None,
icscore: float | None = None,
nsites: float | None = None,
strand: str | Iterable[str] | None = None,
pval: float | None = None,
qval: float | None = None,
eval: float | None = None,
extrainfo: Mapping[str, str] | None = None,
) -> list[Motif]
Keep the motifs that pass every filter given.
Text filters keep motifs whose value is one of those given. width,
icscore and nsites are minimums, and pval, qval and
eval maximums; motifs without the value are dropped. extrainfo
keeps motifs with every one of its entries.
Examples:
>>> import universalmotif as um
>>> motifs = um.example_motifs()
>>> [m.name for m in um.filter_motifs(motifs, family="bZIP")]
['AP-1', 'CRE', 'G-box']
>>> [m.name for m in um.filter_motifs(motifs, family=["bZIP", "ETS"], icscore=10)]
['CRE', 'ETS']
normalize
¶
normalize(motifs)
Re-apply each motif's pseudocount by converting to counts and back.
A pseudocount of zero is replaced by 1. PCMs are returned unchanged.
Examples:
>>> import universalmotif as um
>>> motif = um.create_motif(["TGACGTCA"] * 10)
>>> motif.pseudocount, motif.matrix[:, 0].tolist()
(0.0, [0.0, 0.0, 0.0, 1.0])
>>> normalized = um.normalize(motif)
>>> normalized.pseudocount, normalized.matrix[:, 0].round(3).tolist()
(1.0, [0.023, 0.023, 0.023, 0.932])
round_motif
¶
round_motif(motifs, pct_tolerance: float | str = 0.05)
Set small probabilities to zero.
Probabilities below pct_tolerance, given as a fraction or as a
string such as "5%", are set to zero and each column is
renormalized. The motif is
returned in its original type.
Examples:
>>> import universalmotif as um
>>> motif = um.create_motif(
... [[0.9, 0.02], [0.04, 0.48], [0.03, 0.48], [0.03, 0.02]], alphabet="DNA"
... )
>>> um.round_motif(motif).matrix.tolist()
[[1.0, 0.0], [0.0, 0.5], [0.0, 0.5], [0.0, 0.0]]
>>> um.round_motif(motif, "3%").matrix.tolist()
[[0.9, 0.0], [0.04, 0.5], [0.03, 0.5], [0.03, 0.0]]
motif_rc
¶
motif_rc(motifs, ignore_alphabet: bool = False)
Reverse complement DNA or RNA motifs.
With ignore_alphabet=True other alphabets are accepted, and their
rows and columns are simply reversed.
Examples:
>>> import universalmotif as um
>>> wbox = um.example_motifs()[5]
>>> wbox.consensus, um.motif_rc(wbox).consensus
('GBTTGACYS', 'SRGTCAAVC')
switch_alph
¶
switch_alph(motifs)
Switch motifs between the DNA and RNA alphabets.
Examples:
>>> import universalmotif as um
>>> rna = um.switch_alph(um.create_motif("TGACGTCA"))
>>> rna.alphabet, rna.consensus
('RNA', 'UGACGUCA')
trim_motifs
¶
trim_motifs(
motifs,
min_ic: float = 0.25,
trim_from: TrimFrom = "both",
)
Trim low information content columns from the edges of motifs.
Columns with an information content below min_ic are removed from the
chosen ends until a column at or above it is reached. Motifs that are
trimmed away completely are dropped from a list with a warning; a single
motif that is trimmed away raises ValueError.
Examples:
>>> import universalmotif as um
>>> ap1 = um.example_motifs()[1]
>>> ap1.consensus, um.trim_motifs(ap1).consensus
('NTGASTCAN', 'TGASTCA')
>>> um.trim_motifs(ap1, trim_from="left").consensus
'TGASTCAN'
trim_cwm
¶
trim_cwm(
motifs,
trim_threshold: float = 0.3,
abs_threshold: float | None = None,
trim_from: TrimFrom = "both",
)
Trim the edges of contribution weight matrices.
A column is kept if the sum of its absolute values reaches the cutoff,
which is abs_threshold if given and otherwise trim_threshold
times the largest column sum. Everything between the first and last kept
columns is retained.
Examples:
>>> import universalmotif as um
>>> cwm = um.create_motif(
... [[0.01, 0.0, 0.6, 0.0, 0.02],
... [0.0, 0.5, 0.0, 0.0, 0.0],
... [0.02, 0.0, 0.0, 0.7, 0.0],
... [0.0, 0.0, -0.1, 0.0, 0.01]],
... alphabet="DNA",
... type="CWM",
... )
>>> trimmed = um.trim_cwm(cwm)
>>> trimmed.width
3
>>> trimmed.matrix.tolist()
[[0.0, 0.6, 0.0], [0.5, 0.0, 0.0], [0.0, 0.0, 0.7], [0.0, -0.1, 0.0]]
add_gap
¶
add_gap(
motifs,
gaploc: int | Sequence[int] | None = None,
mingap: int | Sequence[int] = 1,
maxgap: int | Sequence[int] = 5,
)
Add gaps to motifs. Gap i comes after the first gaploc[i]
positions, in the middle of the motif by default, and spans
mingap[i] to maxgap[i] positions; shorter lists are recycled.
Examples:
>>> import universalmotif as um
>>> motif = um.create_motif("TGACGTCA")
>>> gapped = um.add_gap(motif, gaploc=4, mingap=1, maxgap=5)
>>> gapped.isgapped, gapped.gaploc, gapped.mingap, gapped.maxgap
(True, [4], [1], [5])
ungap
¶
ungap(motifs, delete: bool = False)
Turn motifs' gaps off. Unless delete, the gap positions and sizes
are kept, and passing them back to add_gap(),
as in add_gap(m, gaploc=m.gaploc, mingap=m.mingap, maxgap=m.maxgap),
turns the gaps on again.
Examples:
>>> import universalmotif as um
>>> gapped = um.add_gap(um.create_motif("TGACGTCA"), gaploc=4)
>>> off = um.ungap(gapped)
>>> off.isgapped, off.gaploc
(False, [4])
>>> um.ungap(gapped, delete=True).gaploc
[]
shuffle_motifs
¶
shuffle_motifs(
motifs: Motif | Iterable[Motif],
k: int = 2,
seed: int | None = None,
)
Shuffle motif columns among the motifs, in blocks of k columns.
The new motifs keep the original widths and types, with " [shuffled]"
added to their names. With k above 1, columns move in blocks of
k and leftover columns stay at the ends.
Examples:
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:3]
>>> shuffled = um.shuffle_motifs(motifs, seed=1)
>>> [(m.name, m.width) for m in shuffled]
[('TATA-box [shuffled]', 10), ('AP-1 [shuffled]', 9), ('CRE [shuffled]', 8)]
>>> [m.consensus for m in shuffled]
['KASTAACNNW', 'AWRTATCTG', 'ANCAGTTG']
get_consensus
¶
get_consensus(
position: Sequence[float],
alphabet: Literal["DNA", "RNA"] = "DNA",
type: MotifType = "PPM",
pseudocount: float = 1.0,
) -> str
IUPAC consensus letter of a DNA or RNA column, with its values in A, C, G and T or U order.
Examples:
>>> import universalmotif as um
>>> columns = [[0.9, 0.05, 0.05, 0.0], [0.5, 0.0, 0.5, 0.0], [0.25, 0.25, 0.25, 0.25]]
>>> [um.get_consensus(col) for col in columns]
['A', 'R', 'N']
>>> um.get_consensus([1, 0, 1, 18], alphabet="RNA", type="PCM")
'U'
get_consensus_aa
¶
get_consensus_aa(
position: Sequence[float],
type: MotifType = "PPM",
pseudocount: float = 0.0,
) -> str
Consensus letter of an amino acid column, including B, Z, J and X.
Examples:
The column is in alphabetical order of the one-letter codes, so here D and N share the probability:
>>> import universalmotif as um
>>> col = [0.0] * 20
>>> col[2] = col[11] = 0.5
>>> um.get_consensus_aa(col)
'B'
consensus_to_ppm
¶
consensus_to_ppm(letter: str) -> np.ndarray
The DNA probability column for an IUPAC letter. A single base gets
0.997, leaving 0.001 for each of the others, unlike a motif made by
create_motif() from a consensus string,
whose columns hold exact zeros.
Examples:
>>> import universalmotif as um
>>> um.consensus_to_ppm("W").tolist()
[0.499, 0.001, 0.001, 0.499]
>>> um.consensus_to_ppm("N").tolist()
[0.25, 0.25, 0.25, 0.25]
consensus_to_ppm_aa
¶
consensus_to_ppm_aa(letter: str) -> np.ndarray
The amino acid probability column for a consensus letter.
Examples:
>>> import universalmotif as um
>>> col = um.consensus_to_ppm_aa("B")
>>> letters = "ACDEFGHIKLMNPQRSTVWY"
>>> {a: round(float(p), 3) for a, p in zip(letters, col) if p > 0.1}
{'D': 0.491, 'N': 0.491}
position_icscore
¶
position_icscore(
position: Sequence[float],
bkg: Sequence[float] | None = None,
type: MotifType = "PPM",
pseudocount: float = 1.0,
nsites: float = 100.0,
relative_entropy: bool = False,
) -> float
Information content of a single motif column.
PPM columns are rounded to nsites counts and given the pseudocount
before their information content is taken, as they are for a motif's
total IC, so with the defaults a column of pure A gives about 1.93 bits
rather than 2; set pseudocount=0 for the information content of the
column as it is. PCM columns get the pseudocount directly.
Examples:
>>> import universalmotif as um
>>> um.position_icscore([0.25, 0.25, 0.25, 0.25])
0.0
>>> um.position_icscore([1.0, 0.0, 0.0, 0.0], pseudocount=0)
2.0
>>> round(um.position_icscore([0.5, 0.0, 0.0, 0.5], pseudocount=0), 3)
1.0
average_ic
¶
average_ic(
motifs: Motif | Iterable[Motif], average: str = "a.mean"
) -> float | npt.NDArray[np.float64]
The average information content of the motifs' positions, taken as
the arithmetic mean with "a.mean", the geometric mean with
"g.mean", the median with "median" or the Fisher z-transform
with "fzt".
Examples:
>>> import universalmotif as um
>>> motifs = um.example_motifs()
>>> round(um.average_ic(motifs[0]), 3)
0.883
>>> um.average_ic(motifs[:3], average="median").round(3).tolist()
[0.865, 1.215, 1.395]
pcm_to_ppm
¶
pcm_to_ppm(
position: Sequence[float], pseudocount: float = 0.0
) -> npt.NDArray[np.float64]
Convert a column of counts to probabilities, spreading a positive
pseudocount evenly across the letters.
Examples:
>>> import universalmotif as um
>>> um.pcm_to_ppm([3, 1, 0, 4]).tolist()
[0.375, 0.125, 0.0, 0.5]
>>> um.pcm_to_ppm([3, 1, 0, 4], pseudocount=1).round(3).tolist()
[0.361, 0.139, 0.028, 0.472]
ppm_to_pcm
¶
ppm_to_pcm(
position: Sequence[float], nsites: float = 100.0
) -> npt.NDArray[np.float64]
Convert a column of probabilities to whole counts summing to
nsites.
Examples:
>>> import universalmotif as um
>>> um.ppm_to_pcm([0.25, 0.1, 0.05, 0.6], nsites=20).tolist()
[5.0, 2.0, 1.0, 12.0]
ppm_to_pwm
¶
ppm_to_pwm(
position: Sequence[float],
bkg: Sequence[float] | None = None,
pseudocount: float = 1.0,
nsites: float = 100.0,
smooth: bool = True,
) -> npt.NDArray[np.float64]
Convert a column of probabilities to log2 odds against bkg, which
is uniform by default. With smooth, the pseudocount is first applied
by converting to nsites counts and back.
Examples:
>>> import universalmotif as um
>>> um.ppm_to_pwm([0.7, 0.1, 0.1, 0.1], smooth=False).round(3).tolist()
[1.485, -1.322, -1.322, -1.322]
>>> um.ppm_to_pwm([0.7, 0.1, 0.1, 0.1]).round(3).tolist()
[1.476, -1.301, -1.301, -1.301]
pwm_to_ppm
¶
pwm_to_ppm(
position: Sequence[float],
bkg: Sequence[float] | None = None,
) -> npt.NDArray[np.float64]
Convert a column of log2 odds back to probabilities.
Examples:
>>> import universalmotif as um
>>> um.pwm_to_ppm([1.485, -1.322, -1.322, -1.322]).round(3).tolist()
[0.7, 0.1, 0.1, 0.1]
ppm_to_icm
¶
ppm_to_icm(
position: Sequence[float],
bkg: Sequence[float] | None = None,
relative_entropy: bool = False,
) -> npt.NDArray[np.float64]
Convert a column of probabilities to information content, as relative
entropy against bkg if asked. With relative entropy, letters less
common than in the background get 0 rather than a negative height.
Examples:
>>> import universalmotif as um
>>> um.ppm_to_icm([0.7, 0.1, 0.1, 0.1]).round(3).tolist()
[0.45, 0.064, 0.064, 0.064]
>>> um.ppm_to_icm([0.7, 0.1, 0.1, 0.1], relative_entropy=True).round(3).tolist()
[1.04, 0.0, 0.0, 0.0]
icm_to_ppm
¶
icm_to_ppm(
position: Sequence[float],
) -> npt.NDArray[np.float64]
Convert a column of information content back to probabilities.
Examples:
>>> import universalmotif as um
>>> icm = um.ppm_to_icm([0.7, 0.1, 0.1, 0.1])
>>> um.icm_to_ppm(icm).round(3).tolist()
[0.7, 0.1, 0.1, 0.1]