Scanning¶
Scanning sequences for motif hits. Sequences can be strings, lists or dicts of strings, PackedSequences, or the path to a FASTA file, as described under scan_sequences().
scan_sequences
¶
scan_sequences(
motifs: Motif | Iterable[Motif],
sequences: str
| Sequence[str]
| Mapping[str, str]
| PathLike[str],
pvalue: float = 0.0001,
rc: bool = True,
nthreads: int = 1,
no_overlaps: bool = False,
no_overlaps_by: str = "pvalue",
no_overlaps_by_strand: bool = False,
no_overlaps_by_motif: bool = False,
use_gaps: bool = True,
correct_gaps: bool = False,
) -> pl.DataFrame
Scan sequences for motif hits.
Motifs and sequences of any alphabet can be scanned. Each motif's score
threshold comes from the pvalue cutoff, and every window scoring at
least that much is reported with its P-value. Only DNA and RNA have a
reverse strand, which is scanned with rc.
A motif too short or too degenerate to reach the pvalue cutoff can
have no hits, and is named in a warning with the smallest P-value it can
give. There are warnings too when DNA motifs meet sequences that look
like RNA or the other way round, and when a string that names a file is
given as the sequence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequences
|
str | Sequence[str] | Mapping[str, str] | PathLike[str]
|
A sequence, a list of sequences, or a mapping from names to
sequences such as the result of
|
required |
rc
|
bool
|
Also scan the reverse strand. |
True
|
no_overlaps
|
bool
|
Remove overlapping hits with
|
False
|
nthreads
|
int
|
Threads to use; 0 means all available. |
1
|
use_gaps
|
bool
|
Scan gapped motifs, such as those made with
|
True
|
correct_gaps
|
bool
|
Bonferroni-correct gapped motifs' P-values for the number of
combinations of gap sizes tried: hits must reach |
False
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
A DataFrame with one row per hit and the columns |
Examples:
>>> import universalmotif as um
>>> wbox = um.example_motifs()[5]
>>> hits = um.scan_sequences(wbox, um.example_sequences())
>>> hits.shape
(30, 11)
>>> hits.select("sequence", "start", "end", "strand", "match").row(0)
('target_004', 448, 457, '-', 'GTTTGACCG')
>>> round(hits["pvalue"][0], 7)
3.43e-05
dedup_hits
¶
dedup_hits(
hits: DataFrame,
by: str = "pvalue",
reverse: bool = False,
ignore_strand: bool = False,
ignore_motif: bool = False,
seq: str = "sequence",
motif: str = "motif",
strand: str = "strand",
) -> pl.DataFrame
Remove overlapping hits.
Hits are grouped by sequence, motif and strand. Within a group, overlapping
hits are taken in order of the by column and each is kept unless it
overlaps one already kept. Lower values come first for columns whose name
starts with pval and higher values otherwise; reverse flips this,
and ties go to the earlier row. ignore_strand and ignore_motif
compare hits across strands and motifs. start and end columns are
required, with the end left out as in a slice.
Examples:
A palindromic G-box site matches on both strands at the same place:
>>> import universalmotif as um
>>> gbox = um.example_motifs()[3]
>>> hits = um.scan_sequences(gbox, "TTACACGTGTTAAACCACGTGGTT", pvalue=1e-3)
>>> hits.select("start", "end", "strand").rows()
[(2, 10, '+'), (2, 10, '-'), (14, 22, '+'), (14, 22, '-')]
>>> um.dedup_hits(hits, ignore_strand=True).select("start", "end", "strand").rows()
[(2, 10, '+'), (14, 22, '+')]
Motif match matrices¶
Which motifs match which sequences, as a sparse matrix for single-cell ATAC
analyses, without keeping the hits themselves. These need the scipy extra,
and AnnData output the anndata extra.
match_motifs
¶
match_motifs(
motifs: Motif | Iterable[Motif],
sequences: str
| Sequence[str]
| Mapping[str, str]
| PackedSequences,
pvalue: float = 5e-05,
value: str = "matches",
rc: bool = True,
no_overlaps: bool = False,
use_gaps: bool = True,
nthreads: int = 1,
) -> MotifMatches
Which motifs match which sequences, as a sparse matrix.
This is the motif annotation used by chromVAR and similar single-cell
ATAC methods: the sequences, usually peaks, are scanned as by
scan_sequences() with the pvalue
cutoff, and only a summary of the hits is kept for each sequence and
motif, so that hundreds of thousands of peaks and hundreds of motifs
take no more memory than the matrix itself. value is "matches"
for 1 where a motif has a hit, "counts" for the number of hits, or
"scores" for the best score of any hit. Palindromic sites are hit on
both strands, so no_overlaps drops overlapping hits of each motif,
on either strand, before they are counted, keeping the one with the
lowest P-value.
Needs the scipy extra.
Returns:
| Type | Description |
|---|---|
MotifMatches
|
The matrix with one row per sequence and one column per motif, and their names. |
Examples:
>>> import universalmotif as um
>>> motifs = um.example_motifs()
>>> counts = um.match_motifs(motifs, um.example_sequences(), value="counts")
>>> counts.matrix.shape, counts.motifs[3]
((100, 12), 'G-box')
>>> int(counts.matrix[:, 3].sum()), int(counts.matrix[:, 3].max())
(18, 2)
The G-box is close to a palindrome, so some of its sites are hit on both
strands, which no_overlaps counts once:
>>> once = um.match_motifs(motifs, um.example_sequences(), value="counts", no_overlaps=True)
>>> int(once.matrix[:, 3].sum()), int(once.matrix[:, 3].max())
(12, 1)
MotifMatches
dataclass
¶
MotifMatches(
matrix: Any,
sequences: list[str],
motifs: list[str],
value: str,
)
A sparse matrix of motif matches in sequences, from
match_motifs().
matrix is a SciPy CSR array with one row per sequence and one column
per motif, named by sequences and motifs. Its values are those
asked for with value: 1 for a match, the number of hits, or the best
score, and pairs without a hit are left out.
Examples:
>>> import universalmotif as um
>>> matches = um.match_motifs(um.example_motifs()[3:6], um.example_sequences())
>>> matches
MotifMatches(100 sequences x 3 motifs, 32 matches)
>>> matches.matrix.sum(axis=0).tolist()
[12, 3, 17]
to_polars
¶
to_polars() -> pl.DataFrame
The matches as a long DataFrame with columns sequence,
motif and value, one row per sequence and motif with a hit.
Examples:
>>> import universalmotif as um
>>> matches = um.match_motifs(um.example_motifs()[3:6], um.example_sequences())
>>> matches.to_polars().head(2).rows()
[('target_004', 'W-box', 1.0), ('target_005', 'W-box', 1.0)]
to_anndata
¶
to_anndata()
The matches as an AnnData object, with the sequences as
observations and the motifs as variables. Needs the anndata
extra.
Examples:
>>> import universalmotif as um
>>> matches = um.match_motifs(um.example_motifs()[3:6], um.example_sequences())
>>> adata = matches.to_anndata()
>>> adata.shape
(100, 3)
add_motif_matches
¶
add_motif_matches(
adata: Any,
motifs: Motif | Iterable[Motif],
sequences: Sequence[str]
| Mapping[str, str]
| None = None,
key: str = "motif_match",
names_key: str = "motif_name",
dense: bool = True,
**kwargs: Any,
) -> None
Add motif matches to an AnnData object of peaks, as pychromVAR expects.
adata has the peaks as its variables, as in a cells by peaks matrix.
Their sequences come from sequences, either a list in the order of
the peaks or a mapping from peak names, and by default from
adata.uns["peak_seq"], where pychromVAR's add_peak_seq() keeps
them. The peaks by motifs matrix from
match_motifs(), given kwargs, is
stored in adata.varm[key], as a dense array unless dense is
false, and the motif names in adata.uns[names_key]. With the
defaults, the result can go straight to pychromVAR's
compute_deviations().
Needs the scipy extra.
Examples:
>>> import universalmotif as um
>>> um.add_motif_matches(adata, um.example_motifs())
>>> adata.varm["motif_match"].shape
(100, 12)