Reading and writing¶
Readers and writers for motif files, and for FASTA files of sequences. Readers always return a list of motifs, even when the file holds just one. Gzipped FASTA files, bgzip included, are read as they are.
read_meme
¶
read_meme(
file: PathLike,
skip: int = 0,
readsites: bool = False,
readsites_meta: bool = False,
cwm: bool = False,
) -> list[Motif] | dict[str, object]
Read MEME motifs, from MEME, DREME or STREME output or minimal MEME files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
readsites
|
bool
|
Also read the sites listed in the BLOCKS sections of MEME output. |
False
|
readsites_meta
|
bool
|
Also read the per-site P-value tables and the combined block diagrams of MEME output. |
False
|
cwm
|
bool
|
Read the matrices as contribution weight matrices, as TF-MoDISco writes them, instead of probabilities. |
False
|
Returns:
| Type | Description |
|---|---|
list[Motif] | dict
|
A list of motifs, or with
|
Examples:
>>> import universalmotif as um
>>> motifs = um.read_meme(um.example_file("meme"))
>>> [m.name for m in motifs]
['TATA-box', 'AP-1', 'CRE', 'G-box']
>>> motifs[1].type, motifs[1].nsites, motifs[1].consensus
('PPM', 49.0, 'NTGASTCAN')
read_jaspar
¶
read_jaspar(file: PathLike, skip: int = 0) -> list[Motif]
Read JASPAR count matrices.
Rows may be labelled, as in A [ 1 2 3 ], or bare numbers, which are
read as DNA when there are four rows and as amino acids when there are
twenty. Motifs are returned as PCMs
with nsites set to the largest column sum. skip drops lines from
the start of the file.
Examples:
>>> import universalmotif as um
>>> motifs = um.read_jaspar(um.example_file("jaspar"))
>>> [m.name for m in motifs]
['TATA-box', 'AP-1', 'CRE', 'G-box']
>>> motifs[0].type, motifs[0].nsites, motifs[0].consensus
('PCM', 23.0, 'KTATAWAWRN')
read_homer
¶
read_homer(file: PathLike, skip: int = 0) -> list[Motif]
Read HOMER motifs.
The log-odds detection threshold is kept in extrainfo as
"logodds.threshold", and site counts and P-values are read from
HOMER's statistics column when present.
Examples:
>>> import universalmotif as um
>>> motifs = um.read_homer(um.example_file("homer"))
>>> [(m.name, m.type) for m in motifs[:2]]
[('TATA-box', 'PPM'), ('AP-1', 'PPM')]
>>> round(float(motifs[0].extrainfo["logodds.threshold"]), 2)
7.46
read_transfac
¶
read_transfac(file: PathLike, skip: int = 0) -> list[Motif]
Read TRANSFAC matrices, returned as PCMs.
The accession in AC is used as the name if there is one, with the
identifier in ID or the factor name in NA as the alternate name;
HC and OS give the family and organism.
Examples:
>>> import universalmotif as um
>>> motifs = um.read_transfac(um.example_file("transfac"))
>>> [(m.name, m.family) for m in motifs]
[('TATA-box', 'TBP'), ('AP-1', 'bZIP'), ('CRE', 'bZIP'), ('G-box', 'bZIP')]
>>> motifs[0].type, motifs[0].organism
('PCM', 'synthetic')
read_cisbp
¶
read_cisbp(file: PathLike, skip: int = 0) -> list[Motif]
Read CIS-BP motifs, using the TF, TF Name, Family and
Species fields as the name, alternate name, family and organism.
Examples:
>>> import universalmotif as um
>>> motifs = um.read_cisbp(um.example_file("cisbp"))
>>> [m.name for m in motifs]
['TATA-box', 'AP-1', 'CRE', 'G-box']
>>> motifs[0].type, motifs[0].family, motifs[0].organism
('PPM', 'TBP', 'synthetic')
read_uniprobe
¶
read_uniprobe(file: PathLike, skip: int = 0) -> list[Motif]
Read UniPROBE motifs from minimal files or from full output, whose
enrichment scores are kept in extrainfo.
Examples:
>>> import universalmotif as um
>>> motifs = um.read_uniprobe(um.example_file("uniprobe"))
>>> [(m.name, m.consensus) for m in motifs[:2]]
[('TATA-box', 'KTATAWAWRN'), ('AP-1', 'NTGASTCAN')]
read_matrix
¶
read_matrix(
file: PathLike,
skip: int = 0,
type: MotifType | None = None,
positions: Positions = "columns",
alphabet: str = "DNA",
sep: str | None = "",
headers: bool | str = True,
rownames: bool = False,
comment: str | None = None,
) -> list[Motif]
Read motifs stored as plain matrices of numbers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
type
|
MotifType | None
|
The matrix type; detected from the values by default. |
None
|
positions
|
Positions
|
Whether positions are |
'columns'
|
alphabet
|
str
|
Alphabet of the motifs; the letters' rows or columns are read in its order. |
'DNA'
|
sep
|
str | None
|
Line separating motifs; |
''
|
headers
|
bool | str
|
|
True
|
rownames
|
bool
|
Whether the values come with letter labels, which are skipped. |
False
|
comment
|
str | None
|
Lines starting with this string are ignored. |
None
|
Examples:
The example file has each motif's name on its own line, followed by one row of counts per letter:
>>> import universalmotif as um
>>> motifs = um.read_matrix(um.example_file("matrix"))
>>> [m.name for m in motifs]
['TATA-box', 'AP-1', 'CRE', 'G-box']
>>> motifs[0].type, motifs[0].consensus
('PCM', 'KTATAWAWRN')
write_meme
¶
write_meme(
motifs: Motif | Iterable[Motif],
file: PathLike,
version: int = 5,
bkg: Sequence[float] | None = None,
strand: str | None = None,
overwrite: bool = False,
append: bool = False,
cwm: bool = False,
) -> None
Write motifs in MEME format.
Motifs are converted to PPMs, unless cwm=True, in which case every
motif must be a CWM and is written as is. bkg gives the background in
the order letters are written, which for DNA is A, C, G, T; by default the
motifs' shared background is used, or a uniform one if they differ.
strand overrides the strands: line. When appending to an existing
file only the motifs are written. Equal signs in names become periods and
spaces become dashes.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.meme"
... um.write_meme(motifs, path)
... lines = path.read_text().splitlines()
... back = um.read_meme(path)
>>> lines[:3]
['MEME version 5', '', 'ALPHABET= ACGT']
>>> [(m.name, m.type) for m in back]
[('TATA-box', 'PPM'), ('AP-1', 'PPM')]
write_jaspar
¶
write_jaspar(
motifs: Motif | Iterable[Motif],
file: PathLike,
overwrite: bool = False,
append: bool = False,
) -> None
Write DNA, RNA or amino acid motifs as JASPAR count matrices.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.jaspar"
... um.write_jaspar(motifs, path)
... for line in path.read_text().splitlines()[:5]:
... print(line)
>TATA-box
A [ 2 2 23 1 18 11 20 8 10 7 ]
C [ 1 0 0 1 3 1 3 1 0 3 ]
G [ 11 0 0 1 1 1 0 1 13 5 ]
T [ 9 21 0 20 1 10 0 13 0 8 ]
write_homer
¶
write_homer(
motifs: Motif | Iterable[Motif],
file: PathLike,
threshold: float | Sequence[float] = 0.8,
threshold_type: Literal[
"logodds", "logodds_abs", "pvalue"
] = "logodds",
overwrite: bool = False,
append: bool = False,
) -> None
Write DNA or RNA motifs in HOMER format.
The detection threshold in each header is on HOMER's own score scale:
HOMER scores a site as the sum of ln(p / 0.25) over the probabilities
written in the file, floored at 0.001. With threshold_type="logodds"
it is threshold times the motif's maximum score, with
"logodds_abs" it is threshold itself, and with "pvalue" it is
a score that random sites from a uniform background reach with at most
that probability. Give one value or one per motif.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.motif"
... um.write_homer(motifs, path, threshold=0.9)
... back = um.read_homer(path)
>>> [(m.name, round(float(m.extrainfo["logodds.threshold"]), 2)) for m in back]
[('TATA-box', 8.39), ('AP-1', 8.34)]
write_transfac
¶
write_transfac(
motifs: Motif | Iterable[Motif],
file: PathLike,
overwrite: bool = False,
append: bool = False,
name_tag: str = "ID",
altname_tag: str = "NA",
) -> None
Write DNA or RNA motifs as TRANSFAC count matrices. name_tag and
altname_tag choose the fields used for the name and alternate name.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.transfac"
... um.write_transfac(motifs, path)
... for line in path.read_text().splitlines()[:3]:
... print(line)
... back = um.read_transfac(path)
ID TATA-box
HC TBP
OS synthetic
>>> [(m.name, m.type, m.nsites) for m in back]
[('TATA-box', 'PCM', 23.0), ('AP-1', 'PCM', 49.0)]
write_cisbp
¶
write_cisbp(
motifs: Motif | Iterable[Motif],
file: PathLike,
overwrite: bool = False,
append: bool = False,
) -> None
Write DNA or RNA motifs in CIS-BP format, as probability matrices.
The name, alternate name, family and organism go in the TF,
TF Name, Family and Species fields. Files written here can be
read back with read_cisbp().
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.txt"
... um.write_cisbp(motifs, path)
... back = um.read_cisbp(path)
>>> [(m.name, m.type, m.family) for m in back]
[('TATA-box', 'PPM', 'TBP'), ('AP-1', 'PPM', 'bZIP')]
write_uniprobe
¶
write_uniprobe(
motifs: Motif | Iterable[Motif],
file: PathLike,
overwrite: bool = False,
append: bool = False,
) -> None
Write DNA motifs in the minimal UniPROBE format: a name line followed
by A:, C:, G: and T: rows of probabilities.
Files written here can be read back with
read_uniprobe().
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.txt"
... um.write_uniprobe(motifs, path)
... back = um.read_uniprobe(path)
>>> [(m.name, m.type, m.consensus) for m in back]
[('TATA-box', 'PPM', 'KTATAWAWRN'), ('AP-1', 'PPM', 'NTGASTCAN')]
write_matrix
¶
write_matrix(
motifs: Motif | Iterable[Motif],
file: PathLike,
positions: Positions = "columns",
rownames: bool = False,
type: MotifType | None = None,
sep: str | None = "",
headers: bool | str = True,
overwrite: bool = False,
append: bool = False,
digits: int = 6,
) -> None
Write motifs as plain matrices of numbers.
headers writes the name on its own line when True, prefixed with a
string when given one, or not at all when False. sep is written
after each motif unless it is None, type converts the motifs first,
and digits sets the number of decimal places.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> motifs = um.example_motifs()[:2]
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "motifs.txt"
... um.write_matrix(motifs, path, headers=">", digits=1)
... lines = path.read_text().splitlines()
... back = um.read_matrix(path, headers=">")
>>> lines[0], lines[1].split()
('>TATA-box', ['2.0', '2.0', '23.0', '1.0', '18.0', '11.0', '20.0', '8.0', '10.0', '7.0'])
>>> [(m.name, m.type) for m in back]
[('TATA-box', 'PCM'), ('AP-1', 'PCM')]
meme_alph
¶
meme_alph(
core: str,
file: str | PathLike[str] | None = None,
complements: str | None = None,
ambiguity: Mapping[str, str] | None = None,
like: str | None = None,
alph_name: str | None = None,
letter_names: Mapping[str, str] | None = None,
colours: Mapping[str, str] | None = None,
) -> str | None
Write a MEME custom alphabet definition.
core holds the alphabet's letters and complements their
complements, if it has any. ambiguity maps ambiguous letters to the
core letters they stand for, like is "DNA", "RNA" or
"PROTEIN" for alphabets that extend one, and letter_names and
colours describe each letter, with colours as hexadecimal RGB. The
definition is returned as text, or written to file if one is given.
Examples:
>>> import universalmotif as um
>>> text = um.meme_alph("ACGT", complements="TGCA", like="DNA", alph_name="MyDNA")
>>> print(text, end="")
ALPHABET "MyDNA" DNA-LIKE
# Core symbols
A ~ T
C ~ G
read_fasta
¶
read_fasta(
file: PathLike,
skip: int = 0,
packed: bool = False,
alphabet: str = "DNA",
trim_names: bool = False,
) -> dict[str, str] | PackedSequences
Read a FASTA file into a dict from names to sequences, where each name
is the whole header line after >, or with trim_names only its
first word, as most tools use. The file is read line by line, so a
genome needs little more memory than its sequences, and the first
skip lines are ignored. Gzipped files, bgzip included, are recognized
from their first bytes and decompressed as they are read. When names
repeat, the last sequence with each name is kept, with a warning.
With packed, the sequences are packed for scanning as they are read
and returned as PackedSequences for
alphabet, which is "DNA", "RNA", "AA" or the letters of a
custom alphabet. Packed sequences hold a genome in a quarter of the
memory, and names must then be unique. To scan a
file just once, pass its path to scan_sequences() instead.
Examples:
>>> import universalmotif as um
>>> seqs = um.read_fasta(um.example_file("fasta"))
>>> len(seqs), len(seqs["target_001"])
(100, 1000)
>>> packed = um.read_fasta(um.example_file("fasta"), packed=True)
>>> len(packed), packed.alphabet
(100, 'DNA')
write_fasta
¶
write_fasta(
sequences: Mapping[str, str] | Sequence[str],
file: PathLike,
width: int = 80,
overwrite: bool = False,
append: bool = False,
) -> None
Write sequences to a FASTA file, wrapping lines at width
characters, or not at all when it is 0. Unnamed sequences are called
seq1, seq2 and so on. A path ending in .gz is written
gzipped, as it is for every writer in the package.
Examples:
>>> import tempfile
>>> from pathlib import Path
>>> import universalmotif as um
>>> with tempfile.TemporaryDirectory() as tmp:
... path = Path(tmp) / "seqs.fa"
... um.write_fasta({"first": "ACGTACGTAC", "second": "TTGA"}, path, width=4)
... print(path.read_text(), end="")
>first
ACGT
ACGT
AC
>second
TTGA
PackedSequences
¶
PackedSequences(inner: _PackedSequences)
Bases: Mapping[str, str]
Named sequences packed for scanning, from read_fasta() with
packed=True or from pack_sequences().
DNA and RNA take two bits per letter, so a human genome needs about
0.8 GB instead of 3.1 GB as text; other alphabets take a byte per
letter. scan_sequences() scans them as they are, which makes them
the way to scan one large set of sequences many times.
They are also a read-only mapping from names to sequences, so they can
go wherever a dict of sequences can. Each sequence is then decoded when
it is looked up. DNA, RNA and protein letters come back in upper case,
so soft-masking is lost, while the letters of custom alphabets and any
other letters, such as N, come back as they were.
Examples:
>>> import universalmotif as um
>>> packed = um.pack_sequences({"chr1": "ACGTNNacgt", "chr2": "TTGACC"})
>>> list(packed), packed.lengths
(['chr1', 'chr2'], {'chr1': 10, 'chr2': 6})
>>> packed["chr1"]
'ACGTNNACGT'
subseq
¶
subseq(name: str, start: int, end: int) -> str
Positions start up to but not including end of the
sequence called name, counted from 0 as in a slice, decoding only
those.
Examples:
>>> import universalmotif as um
>>> packed = um.pack_sequences({"chr1": "ACGTNNACGT"})
>>> packed.subseq("chr1", 2, 8)
'GTNNAC'
pack_sequences
¶
pack_sequences(
sequences: str | Sequence[str] | Mapping[str, str],
alphabet: str = "DNA",
nthreads: int = 1,
) -> PackedSequences
Pack sequences for scanning as PackedSequences.
alphabet is "DNA", "RNA", "AA" or the letters of a custom
alphabet, and must match the motifs the sequences will be scanned with.
Unnamed sequences are called seq1, seq2 and so on; names must be
unique.
Examples:
>>> import universalmotif as um
>>> seqs = um.example_sequences()
>>> packed = um.pack_sequences(seqs)
>>> len(packed), packed.alphabet
(100, 'DNA')
>>> wbox = um.example_motifs()[5]
>>> um.scan_sequences(wbox, packed).equals(um.scan_sequences(wbox, seqs))
True