universalmotif 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. universalmotif-0.1.0/LICENSE +21 -0
  2. universalmotif-0.1.0/PKG-INFO +219 -0
  3. universalmotif-0.1.0/README.md +178 -0
  4. universalmotif-0.1.0/pyproject.toml +89 -0
  5. universalmotif-0.1.0/python/universalmotif/__init__.py +219 -0
  6. universalmotif-0.1.0/python/universalmotif/__main__.py +5 -0
  7. universalmotif-0.1.0/python/universalmotif/_analysis.py +407 -0
  8. universalmotif-0.1.0/python/universalmotif/_api.py +512 -0
  9. universalmotif-0.1.0/python/universalmotif/_biopython.py +183 -0
  10. universalmotif-0.1.0/python/universalmotif/_cli.py +1181 -0
  11. universalmotif-0.1.0/python/universalmotif/_compare.py +480 -0
  12. universalmotif-0.1.0/python/universalmotif/_core.pyi +531 -0
  13. universalmotif-0.1.0/python/universalmotif/_data.py +88 -0
  14. universalmotif-0.1.0/python/universalmotif/_deep.py +280 -0
  15. universalmotif-0.1.0/python/universalmotif/_display.py +195 -0
  16. universalmotif-0.1.0/python/universalmotif/_enrich.py +370 -0
  17. universalmotif-0.1.0/python/universalmotif/_genome.py +233 -0
  18. universalmotif-0.1.0/python/universalmotif/_genomic.py +574 -0
  19. universalmotif-0.1.0/python/universalmotif/_intervals.py +190 -0
  20. universalmotif-0.1.0/python/universalmotif/_io.py +619 -0
  21. universalmotif-0.1.0/python/universalmotif/_logo.py +221 -0
  22. universalmotif-0.1.0/python/universalmotif/_matches.py +231 -0
  23. universalmotif-0.1.0/python/universalmotif/_packed.py +131 -0
  24. universalmotif-0.1.0/python/universalmotif/_plot.py +885 -0
  25. universalmotif-0.1.0/python/universalmotif/_sequences.py +686 -0
  26. universalmotif-0.1.0/python/universalmotif/_utils.py +624 -0
  27. universalmotif-0.1.0/python/universalmotif/data/example.cisbp +55 -0
  28. universalmotif-0.1.0/python/universalmotif/data/example.jaspar +24 -0
  29. universalmotif-0.1.0/python/universalmotif/data/example.matrix +24 -0
  30. universalmotif-0.1.0/python/universalmotif/data/example.meme +56 -0
  31. universalmotif-0.1.0/python/universalmotif/data/example.motif +39 -0
  32. universalmotif-0.1.0/python/universalmotif/data/example.transfac +59 -0
  33. universalmotif-0.1.0/python/universalmotif/data/example.uniprobe +24 -0
  34. universalmotif-0.1.0/python/universalmotif/data/example_controls.fa.gz +0 -0
  35. universalmotif-0.1.0/python/universalmotif/data/example_motifs.transfac +179 -0
  36. universalmotif-0.1.0/python/universalmotif/data/example_targets.fa.gz +0 -0
  37. universalmotif-0.1.0/python/universalmotif/data/roboto.json +1 -0
  38. universalmotif-0.1.0/python/universalmotif/databases.py +513 -0
  39. universalmotif-0.1.0/python/universalmotif/py.typed +0 -0
  40. universalmotif-0.1.0/universalmotif-py/.gitignore +10 -0
  41. universalmotif-0.1.0/universalmotif-py/CHANGELOG.md +46 -0
  42. universalmotif-0.1.0/universalmotif-py/Cargo.lock +1106 -0
  43. universalmotif-0.1.0/universalmotif-py/Cargo.toml +25 -0
  44. universalmotif-0.1.0/universalmotif-py/LICENSE +21 -0
  45. universalmotif-0.1.0/universalmotif-py/README.md +178 -0
  46. universalmotif-0.1.0/universalmotif-py/src/analysis.rs +592 -0
  47. universalmotif-0.1.0/universalmotif-py/src/arrow.rs +66 -0
  48. universalmotif-0.1.0/universalmotif-py/src/enrichment.rs +259 -0
  49. universalmotif-0.1.0/universalmotif-py/src/genome.rs +63 -0
  50. universalmotif-0.1.0/universalmotif-py/src/genomic.rs +189 -0
  51. universalmotif-0.1.0/universalmotif-py/src/io.rs +293 -0
  52. universalmotif-0.1.0/universalmotif-py/src/lib.rs +682 -0
  53. universalmotif-0.1.0/universalmotif-py/src/packed.rs +127 -0
  54. universalmotif-0.1.0/universalmotif-py/src/plot.rs +74 -0
  55. universalmotif-0.1.0/universalmotif-py/src/sequences.rs +347 -0
  56. universalmotif-0.1.0/universalmotif-py/src/utils.rs +225 -0
  57. universalmotif-0.1.0/universalmotif-py/tests/conftest.py +133 -0
  58. universalmotif-0.1.0/universalmotif-py/tests/data/databases/h11.meme +44 -0
  59. universalmotif-0.1.0/universalmotif-py/tests/data/databases/h11_annotation.tsv +3 -0
  60. universalmotif-0.1.0/universalmotif-py/tests/data/databases/h12_annotation.jsonl +2 -0
  61. universalmotif-0.1.0/universalmotif-py/tests/data/databases/jaspar_page.txt +10 -0
  62. universalmotif-0.1.0/universalmotif-py/tests/data/databases/jaspar_records.json +204 -0
  63. universalmotif-0.1.0/universalmotif-py/tests/test_alphabets.py +103 -0
  64. universalmotif-0.1.0/universalmotif-py/tests/test_analysis.py +119 -0
  65. universalmotif-0.1.0/universalmotif-py/tests/test_biopython.py +74 -0
  66. universalmotif-0.1.0/universalmotif-py/tests/test_cli.py +291 -0
  67. universalmotif-0.1.0/universalmotif-py/tests/test_compare.py +170 -0
  68. universalmotif-0.1.0/universalmotif-py/tests/test_data.py +33 -0
  69. universalmotif-0.1.0/universalmotif-py/tests/test_databases.py +154 -0
  70. universalmotif-0.1.0/universalmotif-py/tests/test_deep.py +138 -0
  71. universalmotif-0.1.0/universalmotif-py/tests/test_display.py +66 -0
  72. universalmotif-0.1.0/universalmotif-py/tests/test_enrich.py +211 -0
  73. universalmotif-0.1.0/universalmotif-py/tests/test_fixes.py +96 -0
  74. universalmotif-0.1.0/universalmotif-py/tests/test_gapped.py +52 -0
  75. universalmotif-0.1.0/universalmotif-py/tests/test_genome.py +168 -0
  76. universalmotif-0.1.0/universalmotif-py/tests/test_genomic.py +216 -0
  77. universalmotif-0.1.0/universalmotif-py/tests/test_intervals.py +107 -0
  78. universalmotif-0.1.0/universalmotif-py/tests/test_io.py +203 -0
  79. universalmotif-0.1.0/universalmotif-py/tests/test_matches.py +81 -0
  80. universalmotif-0.1.0/universalmotif-py/tests/test_motif.py +187 -0
  81. universalmotif-0.1.0/universalmotif-py/tests/test_packed.py +108 -0
  82. universalmotif-0.1.0/universalmotif-py/tests/test_plot.py +190 -0
  83. universalmotif-0.1.0/universalmotif-py/tests/test_reference.py +160 -0
  84. universalmotif-0.1.0/universalmotif-py/tests/test_sequences.py +243 -0
  85. universalmotif-0.1.0/universalmotif-py/tests/test_stub_doctests.py +35 -0
  86. universalmotif-0.1.0/universalmotif-py/tests/test_utils.py +235 -0
  87. universalmotif-0.1.0/universalmotif-py/uv.lock +5300 -0
  88. universalmotif-0.1.0/universalmotif-rs/CHANGELOG.md +35 -0
  89. universalmotif-0.1.0/universalmotif-rs/Cargo.lock +296 -0
  90. universalmotif-0.1.0/universalmotif-rs/Cargo.toml +26 -0
  91. universalmotif-0.1.0/universalmotif-rs/DIVERGENCES.md +147 -0
  92. universalmotif-0.1.0/universalmotif-rs/LICENSE +21 -0
  93. universalmotif-0.1.0/universalmotif-rs/README.md +108 -0
  94. universalmotif-0.1.0/universalmotif-rs/examples/bench.rs +406 -0
  95. universalmotif-0.1.0/universalmotif-rs/src/alphabet.rs +111 -0
  96. universalmotif-0.1.0/universalmotif-rs/src/background.rs +560 -0
  97. universalmotif-0.1.0/universalmotif-rs/src/cdf.rs +189 -0
  98. universalmotif-0.1.0/universalmotif-rs/src/cluster.rs +545 -0
  99. universalmotif-0.1.0/universalmotif-rs/src/compare.rs +941 -0
  100. universalmotif-0.1.0/universalmotif-rs/src/complexity.rs +242 -0
  101. universalmotif-0.1.0/universalmotif-rs/src/convert.rs +459 -0
  102. universalmotif-0.1.0/universalmotif-rs/src/coocc.rs +296 -0
  103. universalmotif-0.1.0/universalmotif-rs/src/create.rs +413 -0
  104. universalmotif-0.1.0/universalmotif-rs/src/dedup.rs +89 -0
  105. universalmotif-0.1.0/universalmotif-rs/src/enrich.rs +289 -0
  106. universalmotif-0.1.0/universalmotif-rs/src/error.rs +27 -0
  107. universalmotif-0.1.0/universalmotif-rs/src/finder.rs +904 -0
  108. universalmotif-0.1.0/universalmotif-rs/src/genome.rs +577 -0
  109. universalmotif-0.1.0/universalmotif-rs/src/io/cisbp.rs +167 -0
  110. universalmotif-0.1.0/universalmotif-rs/src/io/fasta.rs +322 -0
  111. universalmotif-0.1.0/universalmotif-rs/src/io/homer.rs +189 -0
  112. universalmotif-0.1.0/universalmotif-rs/src/io/jaspar.rs +150 -0
  113. universalmotif-0.1.0/universalmotif-rs/src/io/matrix.rs +242 -0
  114. universalmotif-0.1.0/universalmotif-rs/src/io/meme.rs +732 -0
  115. universalmotif-0.1.0/universalmotif-rs/src/io/mod.rs +170 -0
  116. universalmotif-0.1.0/universalmotif-rs/src/io/transfac.rs +185 -0
  117. universalmotif-0.1.0/universalmotif-rs/src/io/uniprobe.rs +125 -0
  118. universalmotif-0.1.0/universalmotif-rs/src/kmer.rs +453 -0
  119. universalmotif-0.1.0/universalmotif-rs/src/lib.rs +117 -0
  120. universalmotif-0.1.0/universalmotif-rs/src/mask.rs +109 -0
  121. universalmotif-0.1.0/universalmotif-rs/src/matrix.rs +151 -0
  122. universalmotif-0.1.0/universalmotif-rs/src/merge.rs +347 -0
  123. universalmotif-0.1.0/universalmotif-rs/src/metrics.rs +329 -0
  124. universalmotif-0.1.0/universalmotif-rs/src/motif.rs +918 -0
  125. universalmotif-0.1.0/universalmotif-rs/src/ops.rs +332 -0
  126. universalmotif-0.1.0/universalmotif-rs/src/packed.rs +671 -0
  127. universalmotif-0.1.0/universalmotif-rs/src/peaks.rs +261 -0
  128. universalmotif-0.1.0/universalmotif-rs/src/proximity.rs +736 -0
  129. universalmotif-0.1.0/universalmotif-rs/src/pvalue.rs +253 -0
  130. universalmotif-0.1.0/universalmotif-rs/src/ranges.rs +426 -0
  131. universalmotif-0.1.0/universalmotif-rs/src/sample.rs +255 -0
  132. universalmotif-0.1.0/universalmotif-rs/src/scan/gapped.rs +337 -0
  133. universalmotif-0.1.0/universalmotif-rs/src/scan/mod.rs +1859 -0
  134. universalmotif-0.1.0/universalmotif-rs/src/scoring.rs +370 -0
  135. universalmotif-0.1.0/universalmotif-rs/src/shuffle.rs +513 -0
  136. universalmotif-0.1.0/universalmotif-rs/src/stats.rs +302 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Benjamin Jean-Marie Tremblay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: universalmotif
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
7
+ Requires-Dist: numpy>=1.24
8
+ Requires-Dist: polars>=1.0
9
+ Requires-Dist: anndata>=0.10 ; extra == 'anndata'
10
+ Requires-Dist: scipy>=1.11 ; extra == 'anndata'
11
+ Requires-Dist: pybigtools>=0.3 ; extra == 'bigwig'
12
+ Requires-Dist: biopython>=1.81 ; extra == 'biopython'
13
+ Requires-Dist: h5py>=3.0 ; extra == 'h5py'
14
+ Requires-Dist: pandas>=2.0 ; extra == 'pandas'
15
+ Requires-Dist: pyarrow>=14 ; extra == 'pandas'
16
+ Requires-Dist: matplotlib>=3.8 ; extra == 'plot'
17
+ Requires-Dist: polars-bio>=0.36 ; python_full_version >= '3.11' and python_full_version < '3.15' and extra == 'polars-bio'
18
+ Requires-Dist: pyranges1>=1.0 ; python_full_version >= '3.12' and extra == 'pyranges'
19
+ Requires-Dist: scipy>=1.11 ; extra == 'scipy'
20
+ Provides-Extra: anndata
21
+ Provides-Extra: bigwig
22
+ Provides-Extra: biopython
23
+ Provides-Extra: h5py
24
+ Provides-Extra: pandas
25
+ Provides-Extra: plot
26
+ Provides-Extra: polars-bio
27
+ Provides-Extra: pyranges
28
+ Provides-Extra: scipy
29
+ License-File: LICENSE
30
+ Summary: Sequence motif manipulation and analysis, a Python port of universalmotif
31
+ Keywords: bioinformatics,motif,pwm,genomics
32
+ Author: Benjamin Jean-Marie Tremblay
33
+ License-Expression: MIT
34
+ Requires-Python: >=3.10
35
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
36
+ Project-URL: Changelog, https://bjmt.github.io/universalmotif-py/changelog/
37
+ Project-URL: Documentation, https://bjmt.github.io/universalmotif-py/
38
+ Project-URL: Issues, https://github.com/bjmt/universalmotif-py/issues
39
+ Project-URL: Repository, https://github.com/bjmt/universalmotif-py
40
+
41
+ # universalmotif (Python)
42
+
43
+ A Python package for working with sequence motifs: creating them, converting
44
+ between their representations, reading and writing them in the common file
45
+ formats, and using them to scan, compare and test sequences. It is a port of
46
+ the [universalmotif](https://bioconductor.org/packages/universalmotif/)
47
+ R/Bioconductor package, built on its Rust version,
48
+ [universalmotif-rs](https://github.com/bjmt/universalmotif-rs), which does
49
+ the heavy lifting, so most of it is quick and can use several threads.
50
+ Motifs are `Motif` objects, matrices come back as numpy arrays, and tables
51
+ come back as [polars](https://pola.rs/) DataFrames.
52
+
53
+ The [documentation](https://bjmt.github.io/universalmotif-py/) has guides
54
+ covering everything from motif basics to a full ChIP-seq analysis, and a
55
+ reference for every function. The package is at an early version, and its
56
+ interface may still change.
57
+
58
+ ## Installation
59
+
60
+ The package needs Python 3.10 or later and can be installed from PyPI:
61
+
62
+ ```sh
63
+ pip install universalmotif
64
+ ```
65
+
66
+ Wheels are built for Linux (x86_64 and aarch64), macOS (Intel and Apple
67
+ Silicon) and Windows (x86_64). Elsewhere pip builds the package from source,
68
+ which needs a [Rust toolchain](https://rustup.rs/).
69
+
70
+ The only required dependencies are numpy and polars. Everything else is an
71
+ optional extra, needed only by the functions that use it, so that for
72
+ example `pip install "universalmotif[plot]"` adds matplotlib for drawing
73
+ logos:
74
+
75
+ | Extra | Brings in | For |
76
+ | --- | --- | --- |
77
+ | `plot` | matplotlib | logos, trees and the other plots |
78
+ | `scipy` | SciPy | sparse motif match matrices |
79
+ | `anndata` | AnnData, SciPy | match matrices stored with single-cell ATAC data |
80
+ | `biopython` | Biopython | converting to and from `Bio.motifs` |
81
+ | `h5py` | h5py | reading TF-MoDISco results |
82
+ | `bigwig` | pybigtools | bigWig signal, and backgrounds matched on it |
83
+ | `pyranges` | PyRanges | converting tables to PyRanges objects |
84
+ | `polars-bio` | polars-bio | preparing tables for polars-bio |
85
+ | `pandas` | pandas, pyarrow | pandas DataFrames, and logomaker export |
86
+
87
+ ## A quick look
88
+
89
+ Using the example motifs and sequences that come with the package:
90
+
91
+ ```python
92
+ import universalmotif as um
93
+
94
+ # A motif from a few aligned sites, and its log-odds matrix.
95
+ ap1 = um.create_motif(["TGACTCA", "TGAGTCA", "TGACTAA"], name="AP-1", pseudocount=1)
96
+ print(um.convert_type(ap1, "PWM").matrix)
97
+
98
+ # Scan sequences for the example motifs, and test them for enrichment
99
+ # against a set of control sequences.
100
+ motifs = um.example_motifs()
101
+ targets = um.example_sequences("targets")
102
+ controls = um.example_sequences("controls")
103
+ hits = um.scan_sequences(motifs, targets, pvalue=1e-4)
104
+ enriched = um.enrich_motifs(motifs, targets, controls)
105
+
106
+ # How similar are the motifs, and what do they look like?
107
+ similar = um.compare_motifs(motifs, compare_to="AP-1")
108
+ um.view_motifs(motifs[:4]).savefig("logos.png")
109
+
110
+ # Look for new motifs in the targets.
111
+ found = um.motif_finder(targets, controls, seed=1)
112
+ ```
113
+
114
+ ## What it can do
115
+
116
+ - **Motifs:** create them from sites, consensus strings or matrices, convert
117
+ between counts, probabilities, log-odds, information content and
118
+ contribution scores, and trim, round, merge, reverse complement and filter
119
+ them.
120
+ - **Files:** MEME, JASPAR, HOMER, TRANSFAC, CIS-BP, UniPROBE and plain
121
+ matrices, and FASTA for sequences, gzipped or not.
122
+ - **Databases:** motifs fetched from JASPAR, HOCOMOCO and CIS-BP and kept in
123
+ a local cache, and conversion to and from Biopython.
124
+ - **Scanning:** exact P-value thresholds, both strands, any alphabet and
125
+ gapped motifs, with whole genomes scanned from a FASTA file in a few
126
+ hundred MB.
127
+ - **Comparison:** similarity with P-values, scored either on the overlap or
128
+ on the whole query, merging of similar motifs, and trees.
129
+ - **Statistics:** enrichment, co-occurrence, positional enrichment,
130
+ proximity to genomic anchors, composition-matched backgrounds and *de
131
+ novo* motif discovery.
132
+ - **Genomes:** FASTA, bgzip and 2bit genomes read a piece at a time, the
133
+ sequences of peaks, bigWig signal over them, and hits moved back to
134
+ genomic coordinates, with ranges exchanged with PyRanges and polars-bio.
135
+ - **Single-cell ATAC and deep learning:** sparse peak by motif matrices for
136
+ AnnData and pychromVAR, motifs as convolution filters and back, one-hot
137
+ sequences, and TF-MoDISco results.
138
+ - **Sequences:** shuffling that keeps k-mer counts, random sequences, k-mer
139
+ backgrounds, complexity, masking and sliding windows.
140
+ - **Plots:** sequence logos, motif trees and plots of the results above,
141
+ with motifs shown as logos in Jupyter and SVG logos for reports.
142
+
143
+ Scanning, comparison, merging, clustering, enrichment and logos work for
144
+ amino acid and custom alphabets as well as DNA and RNA.
145
+
146
+ ## Command line
147
+
148
+ The package also installs a command line, as both `universalmotif` and
149
+ `umotif`, which runs the main functions on files for shell scripts and
150
+ workflow managers:
151
+
152
+ ```sh
153
+ umotif fetch jaspar --tax-group plants --release 2024 -o jaspar.meme
154
+ umotif extract -g genome.fa.gz -r peaks.narrowPeak -o peaks.fa
155
+ umotif bkg -g genome.fa.gz -r peaks.narrowPeak --seed 1 -o bkg.fa
156
+ umotif enrich jaspar.meme -s peaks.fa -b bkg.fa -p 4 -o enriched.tsv
157
+ ```
158
+
159
+ `umotif --help` lists all 19 commands.
160
+
161
+ ## Differences from the R package
162
+
163
+ Function names follow the R package, with underscores in place of dots, so
164
+ `min.ic` becomes `min_ic`. Where the R package has both a full and a `_lite`
165
+ version of a function, this package provides the lite one under the plain
166
+ name. Readers always return a list of motifs, and MEME site tables come
167
+ back as polars DataFrames. Functions that use random numbers take a `seed`,
168
+ but seeds do not reproduce the R package's results, since the two use
169
+ different random number generators.
170
+
171
+ Bugs found in the R package while porting it are fixed here rather than
172
+ reproduced.
173
+ [DIVERGENCES.md](https://github.com/bjmt/universalmotif-rs/blob/main/DIVERGENCES.md)
174
+ in the Rust crate lists the other differences, including the few features
175
+ left out on purpose.
176
+
177
+ ## Documentation
178
+
179
+ The documentation lives at <https://bjmt.github.io/universalmotif-py/>. It is built with MkDocs from `docs/`,
180
+ with the API reference generated from the docstrings and guides written as
181
+ notebooks. To build it and preview it locally:
182
+
183
+ ```sh
184
+ uv sync --group docs --no-install-project
185
+ uv run --no-sync maturin develop --release
186
+ uv run --no-sync mkdocs serve
187
+ ```
188
+
189
+ ## Development
190
+
191
+ Development uses [uv](https://docs.astral.sh/uv/) and
192
+ [maturin](https://www.maturin.rs/), with the Rust crate checked out next to
193
+ this one, since the package builds against it and the tests read the R
194
+ reference values kept there:
195
+
196
+ ```sh
197
+ git clone https://github.com/bjmt/universalmotif-rs.git
198
+ git clone https://github.com/bjmt/universalmotif-py.git
199
+ cd universalmotif-py
200
+ uv sync --group dev --no-install-project
201
+ uv run --no-sync maturin develop --release
202
+ uv run --no-sync pytest
203
+ ```
204
+
205
+ ## Citation
206
+
207
+ If this package has been useful for your research, please cite the article
208
+ describing universalmotif:
209
+
210
+ Tremblay BJM (2024). universalmotif: An R package for biological motif
211
+ analysis. *Journal of Open Source Software* **9**, 7012.
212
+ DOI: [10.21105/joss.07012](https://doi.org/10.21105/joss.07012)
213
+
214
+ ## Licence
215
+
216
+ MIT. The letter shapes used in logos are outlines of the Roboto font,
217
+ released under the Apache License 2.0, and were exported from the R
218
+ package's `fontDFroboto` with `scripts/make_font.R`.
219
+
@@ -0,0 +1,178 @@
1
+ # universalmotif (Python)
2
+
3
+ A Python package for working with sequence motifs: creating them, converting
4
+ between their representations, reading and writing them in the common file
5
+ formats, and using them to scan, compare and test sequences. It is a port of
6
+ the [universalmotif](https://bioconductor.org/packages/universalmotif/)
7
+ R/Bioconductor package, built on its Rust version,
8
+ [universalmotif-rs](https://github.com/bjmt/universalmotif-rs), which does
9
+ the heavy lifting, so most of it is quick and can use several threads.
10
+ Motifs are `Motif` objects, matrices come back as numpy arrays, and tables
11
+ come back as [polars](https://pola.rs/) DataFrames.
12
+
13
+ The [documentation](https://bjmt.github.io/universalmotif-py/) has guides
14
+ covering everything from motif basics to a full ChIP-seq analysis, and a
15
+ reference for every function. The package is at an early version, and its
16
+ interface may still change.
17
+
18
+ ## Installation
19
+
20
+ The package needs Python 3.10 or later and can be installed from PyPI:
21
+
22
+ ```sh
23
+ pip install universalmotif
24
+ ```
25
+
26
+ Wheels are built for Linux (x86_64 and aarch64), macOS (Intel and Apple
27
+ Silicon) and Windows (x86_64). Elsewhere pip builds the package from source,
28
+ which needs a [Rust toolchain](https://rustup.rs/).
29
+
30
+ The only required dependencies are numpy and polars. Everything else is an
31
+ optional extra, needed only by the functions that use it, so that for
32
+ example `pip install "universalmotif[plot]"` adds matplotlib for drawing
33
+ logos:
34
+
35
+ | Extra | Brings in | For |
36
+ | --- | --- | --- |
37
+ | `plot` | matplotlib | logos, trees and the other plots |
38
+ | `scipy` | SciPy | sparse motif match matrices |
39
+ | `anndata` | AnnData, SciPy | match matrices stored with single-cell ATAC data |
40
+ | `biopython` | Biopython | converting to and from `Bio.motifs` |
41
+ | `h5py` | h5py | reading TF-MoDISco results |
42
+ | `bigwig` | pybigtools | bigWig signal, and backgrounds matched on it |
43
+ | `pyranges` | PyRanges | converting tables to PyRanges objects |
44
+ | `polars-bio` | polars-bio | preparing tables for polars-bio |
45
+ | `pandas` | pandas, pyarrow | pandas DataFrames, and logomaker export |
46
+
47
+ ## A quick look
48
+
49
+ Using the example motifs and sequences that come with the package:
50
+
51
+ ```python
52
+ import universalmotif as um
53
+
54
+ # A motif from a few aligned sites, and its log-odds matrix.
55
+ ap1 = um.create_motif(["TGACTCA", "TGAGTCA", "TGACTAA"], name="AP-1", pseudocount=1)
56
+ print(um.convert_type(ap1, "PWM").matrix)
57
+
58
+ # Scan sequences for the example motifs, and test them for enrichment
59
+ # against a set of control sequences.
60
+ motifs = um.example_motifs()
61
+ targets = um.example_sequences("targets")
62
+ controls = um.example_sequences("controls")
63
+ hits = um.scan_sequences(motifs, targets, pvalue=1e-4)
64
+ enriched = um.enrich_motifs(motifs, targets, controls)
65
+
66
+ # How similar are the motifs, and what do they look like?
67
+ similar = um.compare_motifs(motifs, compare_to="AP-1")
68
+ um.view_motifs(motifs[:4]).savefig("logos.png")
69
+
70
+ # Look for new motifs in the targets.
71
+ found = um.motif_finder(targets, controls, seed=1)
72
+ ```
73
+
74
+ ## What it can do
75
+
76
+ - **Motifs:** create them from sites, consensus strings or matrices, convert
77
+ between counts, probabilities, log-odds, information content and
78
+ contribution scores, and trim, round, merge, reverse complement and filter
79
+ them.
80
+ - **Files:** MEME, JASPAR, HOMER, TRANSFAC, CIS-BP, UniPROBE and plain
81
+ matrices, and FASTA for sequences, gzipped or not.
82
+ - **Databases:** motifs fetched from JASPAR, HOCOMOCO and CIS-BP and kept in
83
+ a local cache, and conversion to and from Biopython.
84
+ - **Scanning:** exact P-value thresholds, both strands, any alphabet and
85
+ gapped motifs, with whole genomes scanned from a FASTA file in a few
86
+ hundred MB.
87
+ - **Comparison:** similarity with P-values, scored either on the overlap or
88
+ on the whole query, merging of similar motifs, and trees.
89
+ - **Statistics:** enrichment, co-occurrence, positional enrichment,
90
+ proximity to genomic anchors, composition-matched backgrounds and *de
91
+ novo* motif discovery.
92
+ - **Genomes:** FASTA, bgzip and 2bit genomes read a piece at a time, the
93
+ sequences of peaks, bigWig signal over them, and hits moved back to
94
+ genomic coordinates, with ranges exchanged with PyRanges and polars-bio.
95
+ - **Single-cell ATAC and deep learning:** sparse peak by motif matrices for
96
+ AnnData and pychromVAR, motifs as convolution filters and back, one-hot
97
+ sequences, and TF-MoDISco results.
98
+ - **Sequences:** shuffling that keeps k-mer counts, random sequences, k-mer
99
+ backgrounds, complexity, masking and sliding windows.
100
+ - **Plots:** sequence logos, motif trees and plots of the results above,
101
+ with motifs shown as logos in Jupyter and SVG logos for reports.
102
+
103
+ Scanning, comparison, merging, clustering, enrichment and logos work for
104
+ amino acid and custom alphabets as well as DNA and RNA.
105
+
106
+ ## Command line
107
+
108
+ The package also installs a command line, as both `universalmotif` and
109
+ `umotif`, which runs the main functions on files for shell scripts and
110
+ workflow managers:
111
+
112
+ ```sh
113
+ umotif fetch jaspar --tax-group plants --release 2024 -o jaspar.meme
114
+ umotif extract -g genome.fa.gz -r peaks.narrowPeak -o peaks.fa
115
+ umotif bkg -g genome.fa.gz -r peaks.narrowPeak --seed 1 -o bkg.fa
116
+ umotif enrich jaspar.meme -s peaks.fa -b bkg.fa -p 4 -o enriched.tsv
117
+ ```
118
+
119
+ `umotif --help` lists all 19 commands.
120
+
121
+ ## Differences from the R package
122
+
123
+ Function names follow the R package, with underscores in place of dots, so
124
+ `min.ic` becomes `min_ic`. Where the R package has both a full and a `_lite`
125
+ version of a function, this package provides the lite one under the plain
126
+ name. Readers always return a list of motifs, and MEME site tables come
127
+ back as polars DataFrames. Functions that use random numbers take a `seed`,
128
+ but seeds do not reproduce the R package's results, since the two use
129
+ different random number generators.
130
+
131
+ Bugs found in the R package while porting it are fixed here rather than
132
+ reproduced.
133
+ [DIVERGENCES.md](https://github.com/bjmt/universalmotif-rs/blob/main/DIVERGENCES.md)
134
+ in the Rust crate lists the other differences, including the few features
135
+ left out on purpose.
136
+
137
+ ## Documentation
138
+
139
+ The documentation lives at <https://bjmt.github.io/universalmotif-py/>. It is built with MkDocs from `docs/`,
140
+ with the API reference generated from the docstrings and guides written as
141
+ notebooks. To build it and preview it locally:
142
+
143
+ ```sh
144
+ uv sync --group docs --no-install-project
145
+ uv run --no-sync maturin develop --release
146
+ uv run --no-sync mkdocs serve
147
+ ```
148
+
149
+ ## Development
150
+
151
+ Development uses [uv](https://docs.astral.sh/uv/) and
152
+ [maturin](https://www.maturin.rs/), with the Rust crate checked out next to
153
+ this one, since the package builds against it and the tests read the R
154
+ reference values kept there:
155
+
156
+ ```sh
157
+ git clone https://github.com/bjmt/universalmotif-rs.git
158
+ git clone https://github.com/bjmt/universalmotif-py.git
159
+ cd universalmotif-py
160
+ uv sync --group dev --no-install-project
161
+ uv run --no-sync maturin develop --release
162
+ uv run --no-sync pytest
163
+ ```
164
+
165
+ ## Citation
166
+
167
+ If this package has been useful for your research, please cite the article
168
+ describing universalmotif:
169
+
170
+ Tremblay BJM (2024). universalmotif: An R package for biological motif
171
+ analysis. *Journal of Open Source Software* **9**, 7012.
172
+ DOI: [10.21105/joss.07012](https://doi.org/10.21105/joss.07012)
173
+
174
+ ## Licence
175
+
176
+ MIT. The letter shapes used in logos are outlines of the Roboto font,
177
+ released under the Apache License 2.0, and were exported from the R
178
+ package's `fontDFroboto` with `scripts/make_font.R`.
@@ -0,0 +1,89 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.9,<2"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "universalmotif"
7
+ version = "0.1.0"
8
+ description = "Sequence motif manipulation and analysis, a Python port of universalmotif"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "Benjamin Jean-Marie Tremblay" }]
14
+ keywords = ["bioinformatics", "motif", "pwm", "genomics"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Rust",
18
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
19
+ ]
20
+ dependencies = ["numpy>=1.24", "polars>=1.0"]
21
+
22
+ [project.urls]
23
+ Documentation = "https://bjmt.github.io/universalmotif-py/"
24
+ Repository = "https://github.com/bjmt/universalmotif-py"
25
+ Changelog = "https://bjmt.github.io/universalmotif-py/changelog/"
26
+ Issues = "https://github.com/bjmt/universalmotif-py/issues"
27
+
28
+ [project.scripts]
29
+ universalmotif = "universalmotif._cli:main"
30
+ umotif = "universalmotif._cli:main"
31
+
32
+ [project.optional-dependencies]
33
+ anndata = ["anndata>=0.10", "scipy>=1.11"]
34
+ bigwig = ["pybigtools>=0.3"]
35
+ biopython = ["biopython>=1.81"]
36
+ h5py = ["h5py>=3.0"]
37
+ pandas = ["pandas>=2.0", "pyarrow>=14"]
38
+ plot = ["matplotlib>=3.8"]
39
+ polars-bio = ["polars-bio>=0.36; python_version >= '3.11' and python_version < '3.15'"]
40
+ pyranges = ["pyranges1>=1.0; python_version >= '3.12'"]
41
+ scipy = ["scipy>=1.11"]
42
+
43
+ [dependency-groups]
44
+ dev = [
45
+ "anndata>=0.10",
46
+ "biopython>=1.81",
47
+ "h5py>=3.0",
48
+ "maturin>=1.9,<2",
49
+ "matplotlib>=3.8",
50
+ "pandas>=2.0",
51
+ "polars-bio>=0.36; python_version >= '3.11' and python_version < '3.15'",
52
+ "pybigtools>=0.3",
53
+ "pyranges1>=1.0; python_version >= '3.12'",
54
+ "pytest>=8",
55
+ "scipy>=1.15.3",
56
+ ]
57
+ docs = [
58
+ "maturin>=1.9,<2",
59
+ "matplotlib>=3.8",
60
+ # MkDocs 2 drops the plugin system these rely on.
61
+ "mkdocs>=1.6,<2",
62
+ "mkdocs-material>=9.5",
63
+ "mkdocstrings[python]>=0.27",
64
+ "mkdocs-jupyter>=0.25",
65
+ "ipykernel>=6.29",
66
+ "ruff>=0.6",
67
+ ]
68
+
69
+ [tool.maturin]
70
+ module-name = "universalmotif._core"
71
+ features = ["pyo3/extension-module"]
72
+ # The docs, guides and CI files are not needed to build or use the package.
73
+ exclude = [
74
+ { path = "docs/**/*", format = "sdist" },
75
+ { path = "hooks/**/*", format = "sdist" },
76
+ { path = "scripts/**/*", format = "sdist" },
77
+ { path = ".github/**/*", format = "sdist" },
78
+ { path = "mkdocs.yml", format = "sdist" },
79
+ ]
80
+ manifest-path = "universalmotif-py/Cargo.toml"
81
+ python-source = "python"
82
+
83
+ [tool.pytest.ini_options]
84
+ testpaths = ["tests", "python/universalmotif"]
85
+ # Docstring examples run as tests too.
86
+ addopts = "--doctest-modules"
87
+ # The package warns about inputs that silently give empty or odd results, so
88
+ # a warning in a test is an error unless the test expects it.
89
+ filterwarnings = ["error::UserWarning"]