This vignette will introduce the universalmotif class
and its structure, the import and export of motifs in R, basic motif
manipulation, creation, and visualization. For an introduction to
sequence motifs, see the introductory vignette. For
sequence-related utilities, see the sequences vignette. For motif
comparisons and P-values, see the motif comparisons and P-values
vignette.
The universalmotif package stores motifs using the
universalmotif class. The most basic
universalmotif object exposes the name,
alphabet, type, type,
strand, icscore, consensus, and
motif slots; furthermore, the pseudocount and
bkg slots are also stored but not shown.
universalmotif class motifs can be PCM, PPM, PWM, or ICM
type.
library(universalmotif)
data(examplemotif)
examplemotif
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 11.54
#> Pseudocount: 1
#> Consensus: TATAWAW
#>
#> 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.5A brief description of all the available slots:
name: motif namealtname: (optional) alternative motif namefamily: (optional) a word representing the
transcription factor or matrix familyorganism: (optional) organism of originmotif: the actual motif matrixalphabet: motif alphabettype: motif ‘type’, one of PCM, PPM, PWM, ICM; see the
introductory
vignetteicscore: (generated automatically) Sum of information
content for the motifnsites: (optional) number of sites the motif was
created frompseudocount: this value to added to the motif matrix
during certain type conversions; this is necessary to avoid
-Inf values from appearing in PWM type motifsbkg: a named vector of probabilities which represent
the background letter frequenciesbkgsites: (optional) total number of background
sequences from motif creationconsensus: (generated automatically) for DNA/RNA/AA
motifs, the motif consensusstrand: strand motif can be found onpval: (optional) P-value from de novo motif
searchqval: (optional) Q-value from de novo motif
searcheval: (optional) E-value from de novo motif
searchmultifreq: (optional) higher-order motif
representations.extrainfo: (optional) any extra motif information that
cannot fit in the existing slotsThe other slots will be shown as they are filled.
library(universalmotif)
data(examplemotif)
## The various slots can be accessed individually using `[`
examplemotif["consensus"]
#> [1] "TATAWAW"
## To change a slot, use `[<-`
examplemotif["family"] <- "My motif family"
examplemotif
#>
#> Motif name: motif
#> Family: My motif family
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 11.54
#> Pseudocount: 1
#> Consensus: TATAWAW
#>
#> 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.5Though the slots can easily be changed manually with
[<-, a number of safeguards have been put in place for
some of the slots which will prevent incorrect values from being
introduced.
library(universalmotif)
data(examplemotif)
## The consensus slot is dependent on the motif matrix
examplemotif["consensus"]
#> [1] "TATAWAW"
## Changing this would mean it no longer matches the motif
examplemotif["consensus"] <- "GGGAGAG"
#> Error in .local(x, i, ..., value): this slot is unmodifiable with [<-
## Another example of trying to change a protected slot:
examplemotif["strand"] <- "x"
#> Error: * strand must be one of +, -, +-Below the exposed metadata slots, the actual ‘motif’ matrix is shown. Each position is its own column: row names showing the alphabet letters, and the column names showing the consensus letter at each position.
The universalmotif package aims to unify most of the
motif-related Bioconductor packages by providing the
convert_motifs() function. This allows for easy transition
between supported packages (see ?convert_motifs for a
complete list of supported packages). Should you ever come across a
motif class from another Bioconductor package which is not supported by
the universalmotif package, but believe it should be, then
feel free to bring it up with me.
The convert_motifs function is embedded in most of the
universalmotif functions, meaning that compatible motif
classes from other packages can be used without needed to manually
convert them first. However keep in mind some conversions are final.
Furthermore, internally, all motifs regardless of class are handled as
universalmotif objects, even if the returning class is not.
This will result in at times slightly different objects (though usually
no information should be lost).
library(universalmotif)
library(MotifDb)
data(examplemotif)
data(MA0003.2)
## convert from a `universalmotif` motif to another class
convert_motifs(examplemotif, "TFBSTools-PWMatrix")
#> Note: motif [motif] has an empty nsites slot, using 100.
#> An object of class PWMatrix
#> ID:
#> Name: motif
#> Matrix Class: Unknown
#> strand: *
#> Pseudocounts: 1
#> Tags:
#> list()
#> Background:
#> A C G T
#> 0.25 0.25 0.25 0.25
#> Matrix:
#> T A T A W A W
#> A -6.658211 1.989247 -6.658211 1.989247 0.9928402 1.989247 0.9928402
#> C -6.658211 -6.658211 -6.658211 -6.658211 -6.6582115 -6.658211 -6.6582115
#> G -6.658211 -6.658211 -6.658211 -6.658211 -6.6582115 -6.658211 -6.6582115
#> T 1.989247 -6.658211 1.989247 -6.658211 0.9928402 -6.658211 0.9928402
## convert to universalmotif
convert_motifs(MA0003.2)
#>
#> Motif name: TFAP2A
#> Alternate name: MA0003.2
#> Family: Helix-Loop-Helix
#> Organism: 9606
#> Alphabet: DNA
#> Type: PCM
#> Strands: +
#> Total IC: 12.9
#> Pseudocount: 1
#> Consensus: NNNNGCCYSAGGSCA
#> Target sites: 5098
#> Extra info: [centrality_logp] -4343
#> [family] Helix-Loop-Helix
#> [medline] 10497269
#> ...
#>
#> N N N N G C C Y S A G G S C A
#> A 1387 2141 727 1517 56 0 0 62 346 3738 460 0 116 451 3146
#> C 1630 1060 1506 519 1199 5098 4762 1736 2729 236 0 0 1443 3672 690
#> G 851 792 884 985 3712 0 0 85 1715 920 4638 5098 3455 465 168
#> T 1230 1105 1981 2077 131 0 336 3215 308 204 0 0 84 510 1094
## convert between two packages
convert_motifs(MotifDb[1], "TFBSTools-ICMatrix")
#> Note: motif [ABF2] has an empty nsites slot, using 100.
#> [[1]]
#> An object of class ICMatrix
#> ID: badis.ABF2
#> Name: ABF2
#> Matrix Class: Unknown
#> strand: *
#> Pseudocounts: 1
#> Schneider correction: FALSE
#> Tags:
#> $dataSource
#> [1] "ScerTF"
#>
#> Background:
#> A C G T
#> 0.25 0.25 0.25 0.25
#> Matrix:
#> T C T A G A
#> A 0.08997357 0.02119039 0.02119039 1.64861232 0.02119039 1.43716039
#> C 0.08997357 1.64861232 0.02119039 0.02119039 0.02119039 0.03430887
#> G 0.02188546 0.02119039 0.02119039 0.02119039 1.64861232 0.03430887
#> T 0.78058151 0.02119039 1.64861232 0.02119039 0.02119039 0.03430887The universalmotif package offers a number of
read_*() functions to allow for easy import of various
motif formats. These include:
read_cisbp(): CIS-BP (Weirauch
et al. 2014)read_homer(): HOMER (Heinz et al. 2010)read_jaspar(): JASPAR (Khan et
al. 2018)read_matrix(): generic reader for simply formatted
motifsread_meme(): MEME (Bailey et al. 2009)read_motifs(): native universalmotif
format (not recommended; use saveRDS() instead)read_transfac(): TRANSFAC (Wingender et al. 1996)read_uniprobe(): UniPROBE (Hume
et al. 2015)These functions should work natively with these formats, but if you
are generating your own motifs in one of these formats than it must
adhere quite strictly to the format. An example of each of these is
included in this package (see
system.file("extdata", package="universalmotif")). If you
know of additional motif formats which are not supported in the
universalmotif package that you believe should be, or of
any mistakes in the way the universalmotif package parses
supported formats, then please let me know.
Compatible motif classes can be written to disk using:
write_homer()write_jaspar()write_matrix()write_meme()write_motifs()write_transfac()The write_matrix() function, similar to its
read_matrix() counterpart, can write motifs as simple
matrices with an optional header. Additionally, please keep in mind
format limitations. For example, multiple MEME motifs
written to a single file will all share the same alphabet, with
identical background letter frequencies.
Though universalmotif class motifs can be created using
the new constructor, the universalmotif
package provides the create_motif() function which aims to
provide a simpler interface to motif creation. The
universalmotif class was initially designed to work
natively with DNA, RNA, and amino acid motifs. Currently though, it can
handle any custom alphabet just as easily. The only downsides to custom
alphabets is the lack of support for certain slots such as the
consensus and strand slots.
The create_motif() function will be introduced here only
briefly; see ?create_motif for details.
Should you wish to make use of the universalmotif
functions starting from a motif class unsupported by
convert_motifs(), you can instead manually create
universalmotif class motifs using the
create_motif() function and the motif matrix.
motif.matrix <- matrix(c(0.7, 0.1, 0.1, 0.1,
0.7, 0.1, 0.1, 0.1,
0.1, 0.7, 0.1, 0.1,
0.1, 0.7, 0.1, 0.1,
0.1, 0.1, 0.7, 0.1,
0.1, 0.1, 0.7, 0.1,
0.1, 0.1, 0.1, 0.7,
0.1, 0.1, 0.1, 0.7), nrow = 4)
motif <- create_motif(motif.matrix, alphabet = "RNA", name = "My motif",
pseudocount = 1, nsites = 20, strand = "+")
## The 'type', 'icscore' and 'consensus' slots will be filled for you
motif
#>
#> Motif name: My motif
#> Alphabet: RNA
#> Type: PPM
#> Strands: +
#> Total IC: 4.68
#> Pseudocount: 1
#> Consensus: AACCGGUU
#> Target sites: 20
#>
#> A A C C G G U U
#> A 0.7 0.7 0.1 0.1 0.1 0.1 0.1 0.1
#> C 0.1 0.1 0.7 0.7 0.1 0.1 0.1 0.1
#> G 0.1 0.1 0.1 0.1 0.7 0.7 0.1 0.1
#> U 0.1 0.1 0.1 0.1 0.1 0.1 0.7 0.7As a brief aside: if you have a motif formatted simply as a matrix,
you can still use it with the universalmotif package
functions natively without creating a motif with
create_motif(), as convert_motifs() also has
the ability to handle motifs formatted simply as matrices. However it is
much safer to first format the motif beforehand with
create_motif().
If all you have is a particular consensus sequence in mind, you can
easily create a full motif using create_motif(). This can
be convenient if you’d like to create a quick motif to use with an
external program such as from the MEME suite or
HOMER. Note that ambiguity letters can be used with single
strings.
motif <- create_motif("CCNSNGG", nsites = 50, pseudocount = 1)
## Now to disk:
## write_meme(motif, "meme_motif.txt")
motif
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 8.39
#> Pseudocount: 1
#> Consensus: CCNSNGG
#> Target sites: 50
#>
#> C C N S N G G
#> A 0.00 0.00 0.22 0.0 0.22 0.00 0.00
#> C 0.99 0.99 0.26 0.5 0.26 0.00 0.00
#> G 0.00 0.00 0.26 0.5 0.26 0.99 0.99
#> T 0.00 0.00 0.26 0.0 0.26 0.00 0.00If you wish to, it’s easy to create random motifs. The values within
the motif are generated using rgamma() to avoid creating
low information content motifs. If background probabilities are not
provided, then they are generated with rpois().
create_motif()
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 11.26
#> Pseudocount: 0
#> Consensus: GKMGAGAGYG
#>
#> G K M G A G A G Y G
#> A 0.00 0.06 0.28 0.00 0.79 0.07 0.87 0.11 0.05 0.00
#> C 0.01 0.01 0.60 0.03 0.17 0.24 0.12 0.03 0.26 0.00
#> G 0.92 0.55 0.10 0.96 0.04 0.53 0.01 0.86 0.07 0.99
#> T 0.07 0.38 0.03 0.00 0.01 0.15 0.00 0.00 0.62 0.00You can change the probabilities used to generate the values within the motif matrix:
create_motif(bkg = c(A = 0.2, C = 0.4, G = 0.2, T = 0.2))
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 11.25
#> Pseudocount: 0
#> Consensus: DCAYACCSMC
#>
#> D C A Y A C C S M C
#> A 0.35 0.00 0.99 0.04 0.94 0.00 0.15 0.01 0.40 0.25
#> C 0.02 0.91 0.01 0.56 0.01 0.98 0.80 0.53 0.59 0.52
#> G 0.34 0.08 0.00 0.00 0.03 0.01 0.02 0.46 0.01 0.08
#> T 0.29 0.01 0.00 0.40 0.02 0.01 0.03 0.00 0.00 0.15With a custom alphabet:
create_motif(alphabet = "QWERTY")
#>
#> Motif name: motif
#> Alphabet: EQRTWY
#> Type: PPM
#> Total IC: 14.44
#> Pseudocount: 0
#>
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> E 0.46 0.23 0.82 0.03 0.88 0.82 0.00 0.00 0.03 0.03
#> Q 0.03 0.71 0.00 0.21 0.00 0.00 0.00 0.16 0.00 0.43
#> R 0.02 0.05 0.18 0.60 0.00 0.00 0.63 0.14 0.96 0.00
#> T 0.00 0.00 0.00 0.04 0.00 0.01 0.00 0.51 0.00 0.35
#> W 0.28 0.00 0.00 0.11 0.10 0.00 0.26 0.00 0.00 0.20
#> Y 0.22 0.01 0.00 0.00 0.02 0.16 0.11 0.19 0.00 0.00There are several packages which offer motif visualization
capabilities, such as seqLogo, motifStack, and
ggseqlogo. The universalmotif package has its
own implementation via the function view_motifs(), which
renders motifs using the ggplot2 package (similar to
ggseqlogo). Here I will briefly show how to use these to
visualize universalmotif class motifs.
library(universalmotif)
data(examplemotif)
## With the native `view_motifs` function:
view_motifs(examplemotif)The view_motifs() function generates ggplot
objects; feel free to manipulate them as such. For example, flipping the
position numbers for larger motifs (where the text spacing can become
tight):
view_motifs(create_motif(15)) +
ggplot2::theme(
axis.text.x = ggplot2::element_text(angle = 90, hjust = 1)
)A large number of options are available for tuning the way motifs are
plotted in view_motifs(). Visit the documentation for more
information.
Using the other Bioconductor packages to view
universalmotif motifs is fairly easy as well:
## For all the following examples, simply passing the functions a PPM is
## sufficient
motif <- convert_type(examplemotif, "PPM")
## Only need the matrix itself
motif <- motif["motif"]
## seqLogo:
seqLogo::seqLogo(motif)
## motifStack:
motifStack::plotMotifLogo(motif)
#> also installing the dependency 'jpeg'
#> Loading required namespace: Cairo
#> Warning in checkValidSVG(doc, warn = warn): This picture may not have been
#> generated by Cairo graphics; errors may result
#> Loading required namespace: Cairo
#> Warning in checkValidSVG(doc, warn = warn): This picture may not have been
#> generated by Cairo graphics; errors may result
#> Loading required namespace: Cairo
#> Warning in checkValidSVG(doc, warn = warn): This picture may not have been
#> generated by Cairo graphics; errors may result
#> Loading required namespace: Cairo
#> Warning in checkValidSVG(doc, warn = warn): This picture may not have been
#> generated by Cairo graphics; errors may result
## ggseqlogo:
ggseqlogo::ggseqlogo(motif)
#> Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
#> of ggplot2 3.3.4.
#> ℹ The deprecated feature was likely used in the ggseqlogo package.
#> Please report the issue at <https://github.com/omarwagih/ggseqlogo/issues>.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
#> Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
#> ℹ Please use tidy evaluation idioms with `aes()`.
#> ℹ See also `vignette("ggplot2-in-packages")` for more information.
#> ℹ The deprecated feature was likely used in the ggseqlogo package.
#> Please report the issue at <https://github.com/omarwagih/ggseqlogo/issues>.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.The motifStack package allows for a number of different
motif stacking visualizations. The universalmotif package,
while not capable of emulating most of these, still offers basic
stacking via view_motifs(). The motifs are aligned using
compare_motifs().
library(universalmotif)
library(MotifDb)
motifs <- convert_motifs(MotifDb[50:54])
view_motifs(motifs, show.positions.once = FALSE, names.pos = "right")The logo plotting capabilities of view_motifs() can be
used for any kind of arbitrary text logo. All you need is a numeric
matrix (the heights of the characters), with the desired characters as
row names. The following example is taken from the
view_logo() documentation.
library(universalmotif)
data(examplemotif)
## Start from a numeric matrix:
toplot <- examplemotif["motif"]
# Adjust the character heights as you wish (negative values are possible):
toplot[4] <- 2
toplot[20] <- -0.5
# Mix and match the number of characters per letter/position:
rownames(toplot)[1] <- "AA"
toplot <- toplot[c(1, 4), ]
toplot
#> T A T A W A W
#> AA 0 1 0 1 0.5 1 0.5
#> T 2 0 1 0 -0.5 0 0.5
view_logo(toplot)Though PCM, PPM, PWM, and ICM type motifs are still widely used today, a few ‘next generation’ motif formats have been proposed. These wish to add another layer of information to motifs: positional interdependence. To illustrate this, consider the following sequences:
| # | Sequence |
|---|---|
| 1 | CAAAACC |
| 2 | CAAAACC |
| 3 | CAAAACC |
| 4 | CTTTTCC |
| 5 | CTTTTCC |
| 6 | CTTTTCC |
This becomes the following PPM:
| Position | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| A | 0.0 | 0.5 | 0.5 | 0.5 | 0.5 | 0.0 | 0.0 |
| C | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 1.0 |
| G | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| T | 0.0 | 0.5 | 0.5 | 0.5 | 0.5 | 0.0 | 0.0 |
Based on the PPM representation, all three of CAAAACC, CTTTTCC, and CTATACC are equally likely. Though looking at the starting sequences, should CTATACC really be considered so? For transcription factor binding sites, this sometimes is not the case. By incorporating this type of information into the motif, it can allow for increased accuracy in motif searching. A few example implementations of this include: TFFM by Mathelier and Wasserman (2013), BaMM by Siebert and Soding (2016), and KSM by Guo et al. (2018).
The universalmotif package implements its own, rather
simplified, version of this concept. Plainly, the standard PPM has been
extended to include k-letter frequencies, with
k being any number higher than 1. For example, the 2-letter
version of the table @ref(tab:ppm2) motif would be:
| Position | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| AA | 0.0 | 0.5 | 0.5 | 0.5 | 0.0 | 0.0 |
| AC | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 |
| AG | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| AT | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| CA | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| CC | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 |
| CG | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| CT | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| GA | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| GC | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| GG | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| GT | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| TA | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| TC | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 |
| TG | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| TT | 0.0 | 0.5 | 0.5 | 0.5 | 0.0 | 0.0 |
This format shows the probability of each letter combined with the
probability of the letter in the next position. The seventh column has
been dropped, since it is not needed: the information in the sixth
column is sufficient, and there is no eighth position to draw 2-letter
probabilities from. Now, the probability of getting CTATACC is no longer
equal to CTTTTCC and CAAAACC. This information is kept in the
multifreq slot of universalmotif class motifs.
To add this information, use the add_multifreq()
function.
library(universalmotif)
motif <- create_motif("CWWWWCC", nsites = 6)
sequences <- DNAStringSet(rep(c("CAAAACC", "CTTTTCC"), 3))
motif.k2 <- add_multifreq(motif, sequences, add.k = 2)
## Alternatively:
# motif.k2 <- create_motif(sequences, add.multifreq = 2)
motif.k2
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 10
#> Pseudocount: 0
#> Consensus: CWWWWCC
#> Target sites: 6
#> k-letter freqs: 2
#>
#> C W W W W C C
#> A 0 0.5 0.5 0.5 0.5 0 0
#> C 1 0.0 0.0 0.0 0.0 1 1
#> G 0 0.0 0.0 0.0 0.0 0 0
#> T 0 0.5 0.5 0.5 0.5 0 0To plot these motifs, use view_motifs():
This information is most useful with functions such as
scan_sequences() and enrich_motifs(). Though
other tools in the universalmotif can work with
multifreq motifs (such as motif_pvalue(),
compare_motifs()), keep in mind they are not as well
supported as regular motifs (getting P-values from
multifreq motifs is exponentially slower, and P-values from
using compare_motifs() for multifreq motifs
are not available by default). See the sequences vignette for using
scan_sequences() with the multifreq slot.
universalmotif_df data
structureFor those who enjoy using the tidyverse functions for data handling,
motifs can additionally represented as the modified
data.frame format: universalmotif_df. This
format allows one to modify motif slots for multiples motifs
simultaneously using the universalmotif_df columns, and
then return to a list of motifs afterwards to resume use with
universalmotif package functions. A few key functions have
been provided in relation to this format:
to_df(): Generate a universalmotif_df
object from a list of motifs.update_motifs(): After modifying the
universalmotif_df object, apply these modifications to the
actual universalmotif objects (contained within the
motif column).to_list(): Return to a list of
universalmotif objects for use with
universalmotif package functions. Note that it is not
required to use update_motifs() before using
to_list(), as modifications will be checked for and applied
if found.requires_update(): Boolean check as to whether the
universalmotif objects and the
universalmotif_df columns differ and require either a
update_motifs() or to_list() call to re-sync
them.library(universalmotif)
library(MotifDb)
## Obtain a `universalmotif_df` object
motifs <- to_df(MotifDb)
head(motifs)
#> motif name altname organism consensus alphabet strand icscore
#> 1 <mot:ABF2> ABF2 badis.ABF2 Scerevisiae TCTAGA DNA +- 9.371235
#> 2 <mot:CAT8> CAT8 badis.CAT8 Scerevisiae CCGGAN DNA +- 7.538740
#> 3 <mot:CST6> CST6 badis.CST6 Scerevisiae TGACGT DNA +- 9.801864
#> 4 <mot:ECM23> ECM23 badis.ECM23 Scerevisiae AGATC DNA +- 6.567494
#> 5 <mot:EDS1> EDS1 badis.EDS1 Scerevisiae GGAANAA DNA +- 9.314287
#> 6 <mot:FKH2> FKH2 badis.FKH2 Scerevisiae GTAAACA DNA +- 11.525400
#> type pseudocount bkg dataSource
#> 1 PPM 1 0.25, 0..... ScerTF
#> 2 PPM 1 0.25, 0..... ScerTF
#> 3 PPM 1 0.25, 0..... ScerTF
#> 4 PPM 1 0.25, 0..... ScerTF
#> 5 PPM 1 0.25, 0..... ScerTF
#> 6 PPM 1 0.25, 0..... ScerTF
#>
#> [Hidden empty columns: family, nsites, bkgsites, pval, qval, eval.]Some tidy manipulation:
library(dplyr)
motifs <- motifs %>%
mutate(bkg = case_when(
organism == "Athaliana" ~ list(c(A = 0.32, C = 0.18, G = 0.18, T = 0.32)),
TRUE ~ list(c(A = 0.25, C = 0.25, G = 0.25, T = 0.25))
))
head(filter(motifs, organism == "Athaliana"))
#> motif name altname family organism consensus alphabet
#> 1 * <mot:ORA59> ORA59 M0005_1.02 AP2 Athaliana MGCCGCCN DNA
#> 2 * <mot:WIN1> WIN1 M0006_1.02 AP2 Athaliana NCRCCGCNNN DNA
#> 3 * <mot:AT1G..> AT1G22985 M0007_1.02 AP2 Athaliana NNGCCGNNN DNA
#> 4 * <mot:TEM1> TEM1 M0008_1.02 AP2 Athaliana WATGTTGC DNA
#> 5 * <mot:ERF11> ERF11 M0009_1.02 AP2 Athaliana NNGCCGNNNN DNA
#> 6 * <mot:RAP2.6> RAP2.6 M0010_1.02 AP2 Athaliana NNGCCGNN DNA
#> strand icscore type pseudocount bkg dataSource
#> 1 +- 11.351632 PPM 1 0.32, 0..... cisbp_1.02
#> 2 +- 6.509679 PPM 1 0.32, 0..... cisbp_1.02
#> 3 +- 5.155725 PPM 1 0.32, 0..... cisbp_1.02
#> 4 +- 11.182383 PPM 1 0.32, 0..... cisbp_1.02
#> 5 +- 5.148803 PPM 1 0.32, 0..... cisbp_1.02
#> 6 +- 4.227144 PPM 1 0.32, 0..... cisbp_1.02
#>
#> [Hidden empty columns: nsites, bkgsites, pval, qval, eval.]
#> [Rows marked with * are changed. Run update_motifs() or to_list() to apply
#> changes.]Feel free to add columns as well. You can add 1d vectors which will
be added to the extrainfo slots of motifs. (Note that they
will be coerced to character vectors!)
motifs <- motifs %>%
mutate(MotifIndex = 1:n())
head(motifs)
#> motif name altname organism consensus alphabet strand
#> 1 * <mot:ABF2> ABF2 badis.ABF2 Scerevisiae TCTAGA DNA +-
#> 2 * <mot:CAT8> CAT8 badis.CAT8 Scerevisiae CCGGAN DNA +-
#> 3 * <mot:CST6> CST6 badis.CST6 Scerevisiae TGACGT DNA +-
#> 4 * <mot:ECM23> ECM23 badis.ECM23 Scerevisiae AGATC DNA +-
#> 5 * <mot:EDS1> EDS1 badis.EDS1 Scerevisiae GGAANAA DNA +-
#> 6 * <mot:FKH2> FKH2 badis.FKH2 Scerevisiae GTAAACA DNA +-
#> icscore type pseudocount bkg dataSource MotifIndex
#> 1 9.371235 PPM 1 0.25, 0..... ScerTF 1
#> 2 7.538740 PPM 1 0.25, 0..... ScerTF 2
#> 3 9.801864 PPM 1 0.25, 0..... ScerTF 3
#> 4 6.567494 PPM 1 0.25, 0..... ScerTF 4
#> 5 9.314287 PPM 1 0.25, 0..... ScerTF 5
#> 6 11.525400 PPM 1 0.25, 0..... ScerTF 6
#>
#> [Hidden empty columns: family, nsites, bkgsites, pval, qval, eval.]
#> [Rows marked with * are changed. Run update_motifs() or to_list() to apply
#> changes.]
to_list(motifs)[[1]]
#>
#> Motif name: ABF2
#> Alternate name: badis.ABF2
#> Organism: Scerevisiae
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 9.37
#> Pseudocount: 1
#> Consensus: TCTAGA
#> Extra info: [dataSource] ScerTF
#> [MotifIndex] 1
#>
#> T C T A G A
#> A 0.09 0.01 0.01 0.97 0.01 0.94
#> C 0.09 0.97 0.01 0.01 0.01 0.02
#> G 0.02 0.01 0.01 0.01 0.97 0.02
#> T 0.80 0.01 0.97 0.01 0.01 0.02If during the course of your manipulation you’ve generated temporary
columns which you wish to drop, you can set
extrainfo = FALSE to discard all extra columns. Be careful
though, this will discard any previously existing extrainfo
data as well.
to_list(motifs, extrainfo = FALSE)[[1]]
#> Discarding unknown slot(s) 'dataSource', 'MotifIndex' (set `extrainfo=TRUE`
#> to preserve these).
#>
#> Motif name: ABF2
#> Alternate name: badis.ABF2
#> Organism: Scerevisiae
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 9.37
#> Pseudocount: 1
#> Consensus: TCTAGA
#>
#> T C T A G A
#> A 0.09 0.01 0.01 0.97 0.01 0.94
#> C 0.09 0.97 0.01 0.01 0.01 0.02
#> G 0.02 0.01 0.01 0.01 0.97 0.02
#> T 0.80 0.01 0.97 0.01 0.01 0.02A number of convenience functions are included for manipulating motifs.
For DNA, RNA and AA motifs, the universalmotif will
automatically generate a consensus string slot.
Furthermore, create_motif() can generate motifs from
consensus strings. The internal functions for these have been made
available:
consensus_to_ppm()consensus_to_ppmAA()get_consensus()get_consensusAA()Filter a list of motifs, using the universalmotif slots
with filter_motifs().
library(universalmotif)
library(MotifDb)
## Let us extract all of the Arabidopsis and C. elegans motifs
motifs <- filter_motifs(MotifDb, organism = c("Athaliana", "Celegans"))
#> motifs converted to class 'universalmotif'
## Only keeping motifs with sufficient information content and length:
motifs <- filter_motifs(motifs, icscore = 10, width = 10)
head(summarise_motifs(motifs))
#> name altname family organism consensus alphabet strand icscore
#> 1 ERF1 M0025_1.02 AP2 Athaliana NMGCCGCCRN DNA +- 12.40700
#> 2 ATERF6 M0027_1.02 AP2 Athaliana NTGCCGGCGB DNA +- 11.77649
#> 3 ATCBF3 M0032_1.02 AP2 Athaliana ATGTCGGYNN DNA +- 10.66970
#> 4 AT2G18300 M0155_1.02 bHLH Athaliana NNNGCACGTGNN DNA +- 11.50133
#> 5 bHLH104 M0159_1.02 bHLH Athaliana GGCACGTGCC DNA +- 16.05350
#> 6 hlh-16 M0173_1.02 bHLH Celegans NNNCAATATKGNN DNA +- 10.32432
#> nsites
#> 1 NA
#> 2 NA
#> 3 NA
#> 4 NA
#> 5 NA
#> 6 NAGet a random set of sequences which are created using the
probabilities of the motif matrix, in effect generating motif sites,
with sample_sites().
library(universalmotif)
data(examplemotif)
sample_sites(examplemotif)
#> DNAStringSet object of length 100:
#> width seq
#> [1] 7 TATAAAA
#> [2] 7 TATAAAT
#> [3] 7 TATAAAA
#> [4] 7 TATAAAA
#> [5] 7 TATAAAT
#> ... ... ...
#> [96] 7 TATAAAT
#> [97] 7 TATAAAT
#> [98] 7 TATATAT
#> [99] 7 TATATAA
#> [100] 7 TATAAAAShuffle a set of motifs with shuffle_motifs(). The
original shuffling implementation is taken from the linear
shuffling method of shuffle_sequences(), described in the
sequences vignette.
library(universalmotif)
library(MotifDb)
motifs <- convert_motifs(MotifDb[1:50])
head(summarise_motifs(motifs))
#> name altname organism consensus alphabet strand icscore
#> 1 ABF2 badis.ABF2 Scerevisiae TCTAGA DNA +- 9.371235
#> 2 CAT8 badis.CAT8 Scerevisiae CCGGAN DNA +- 7.538740
#> 3 CST6 badis.CST6 Scerevisiae TGACGT DNA +- 9.801864
#> 4 ECM23 badis.ECM23 Scerevisiae AGATC DNA +- 6.567494
#> 5 EDS1 badis.EDS1 Scerevisiae GGAANAA DNA +- 9.314287
#> 6 FKH2 badis.FKH2 Scerevisiae GTAAACA DNA +- 11.525400
motifs.shuffled <- shuffle_motifs(motifs, k = 3)
head(summarise_motifs(motifs.shuffled))
#> name consensus alphabet strand icscore
#> 1 ABF2 [shuffled] TRTTCC DNA +- 8.139182
#> 2 CAT8 [shuffled] CTCGCG DNA +- 8.129913
#> 3 CST6 [shuffled] YCCCBT DNA +- 7.446174
#> 4 ECM23 [shuffled] TCKGG DNA +- 5.691992
#> 5 EDS1 [shuffled] CTGCWGG DNA +- 5.723874
#> 6 FKH2 [shuffled] TCCCSRR DNA +- 7.425516Motif matches in a set of sequences are typically obtained using logodds scores. Several functions are exposed to reveal some of the internal work that goes on.
get_matches(): show all possible sequence matches above
a certain scoreget_scores(): obtain all possible scores from all
possible sequence matchesmotif_score(): translate score thresholds to logodds
scoresprob_match(): return probabilities for sequence
matchesscore_match(): return logodds scores for sequence
matcheslibrary(universalmotif)
data(examplemotif)
examplemotif
#>
#> Motif name: motif
#> Alphabet: DNA
#> Type: PPM
#> Strands: +-
#> Total IC: 11.54
#> Pseudocount: 1
#> Consensus: TATAWAW
#>
#> 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
## Get the min and max possible scores:
motif_score(examplemotif)
#> 0% 100%
#> -46.606 11.929
## Show matches above a score of 10:
get_matches(examplemotif, 10)
#> [1] "TATAAAA" "TATATAA" "TATAAAT" "TATATAT"
## Get the probability of a match:
prob_match(examplemotif, "TTTTTTT", allow.zero = FALSE)
#> [1] 6.103516e-05
## Score a specific sequence:
score_match(examplemotif, "TTTTTTT")
#> [1] -14.012
## Take a look at the distribution of scores:
plot(density(get_scores(examplemotif), bw = 5))While convert_type() will take care of switching the
current type for universalmotif objects, the individual
type conversion functions are also available for personal use. These
are:
icm_to_ppm()pcm_to_ppm()ppm_to_icm()ppm_to_pcm()ppm_to_pwm()pwm_to_ppm()These functions take a one dimensional vector. To use these for matrices:
library(universalmotif)
m <- create_motif(type = "PCM")["motif"]
m
#> M G G Y A G Y G G M
#> A 54 1 5 23 64 0 6 2 0 41
#> C 46 18 22 42 20 5 50 1 21 57
#> G 0 81 72 0 0 78 0 97 58 0
#> T 0 0 1 35 16 17 44 0 21 2
apply(m, 2, pcm_to_ppm)
#> M G G Y A G Y G G M
#> [1,] 0.54 0.01 0.05 0.23 0.64 0.00 0.06 0.02 0.00 0.41
#> [2,] 0.46 0.18 0.22 0.42 0.20 0.05 0.50 0.01 0.21 0.57
#> [3,] 0.00 0.81 0.72 0.00 0.00 0.78 0.00 0.97 0.58 0.00
#> [4,] 0.00 0.00 0.01 0.35 0.16 0.17 0.44 0.00 0.21 0.02Additionally, the position_icscore() can be used to get
the total information content per position:
#> R version 4.5.2 (2025-10-31)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.3 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=C
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] TFBSTools_1.49.0 cowplot_1.2.0 dplyr_1.1.4
#> [4] ggtree_4.1.1 ggplot2_4.0.1 MotifDb_1.51.0
#> [7] GenomicRanges_1.63.0 Biostrings_2.79.2 Seqinfo_1.1.0
#> [10] XVector_0.51.0 IRanges_2.45.0 S4Vectors_0.49.0
#> [13] BiocGenerics_0.57.0 generics_0.1.4 universalmotif_1.29.0
#> [16] bookdown_0.45
#>
#> loaded via a namespace (and not attached):
#> [1] DBI_1.2.3 bitops_1.0-9
#> [3] rlang_1.1.6 magrittr_2.0.4
#> [5] ade4_1.7-23 matrixStats_1.5.0
#> [7] compiler_4.5.2 RSQLite_2.4.4
#> [9] png_0.1-8 systemfonts_1.3.1
#> [11] vctrs_0.6.5 pwalign_1.7.0
#> [13] pkgconfig_2.0.3 crayon_1.5.3
#> [15] fastmap_1.2.0 motifStack_1.55.0
#> [17] labeling_0.4.3 caTools_1.18.3
#> [19] splitstackshape_1.4.8 Rsamtools_2.27.0
#> [21] rmarkdown_2.30 DirichletMultinomial_1.53.0
#> [23] purrr_1.2.0 bit_4.6.0
#> [25] xfun_0.54 ggseqlogo_0.2
#> [27] cachem_1.1.0 cigarillo_1.1.0
#> [29] aplot_0.2.9 jsonlite_2.0.0
#> [31] blob_1.2.4 DelayedArray_0.37.0
#> [33] BiocParallel_1.45.0 jpeg_0.1-11
#> [35] parallel_4.5.2 R6_2.6.1
#> [37] bslib_0.9.0 RColorBrewer_1.1-3
#> [39] rtracklayer_1.69.1 jquerylib_0.1.4
#> [41] Rcpp_1.1.0 SummarizedExperiment_1.41.0
#> [43] knitr_1.50 base64enc_0.1-3
#> [45] Matrix_1.7-4 tidyselect_1.2.1
#> [47] dichromat_2.0-0.1 abind_1.4-8
#> [49] yaml_2.3.10 codetools_0.2-20
#> [51] curl_7.0.0 lattice_0.22-7
#> [53] tibble_3.3.0 Biobase_2.71.0
#> [55] treeio_1.35.0 withr_3.0.2
#> [57] S7_0.2.1 evaluate_1.0.5
#> [59] gridGraphics_0.5-1 pillar_1.11.1
#> [61] MatrixGenerics_1.23.0 ggfun_0.2.0
#> [63] grImport2_0.3-3 RCurl_1.98-1.17
#> [65] scales_1.4.0 tidytree_0.4.6
#> [67] gtools_3.9.5 glue_1.8.0
#> [69] gdtools_0.4.4 lazyeval_0.2.2
#> [71] maketools_1.3.2 seqLogo_1.77.0
#> [73] tools_4.5.2 TFMPvalue_0.0.9
#> [75] BiocIO_1.21.0 sys_3.4.3
#> [77] data.table_1.17.8 BSgenome_1.79.1
#> [79] GenomicAlignments_1.47.0 ggiraph_0.9.2
#> [81] buildtools_1.0.0 fs_1.6.6
#> [83] XML_3.99-0.20 grid_4.5.2
#> [85] tidyr_1.3.1 ape_5.8-1
#> [87] nlme_3.1-168 patchwork_1.3.2
#> [89] restfulr_0.0.16 cli_3.6.5
#> [91] rappdirs_0.3.3 fontBitstreamVera_0.1.1
#> [93] S4Arrays_1.11.1 gtable_0.3.6
#> [95] yulab.utils_0.2.1 sass_0.4.10
#> [97] digest_0.6.39 fontquiver_0.2.1
#> [99] SparseArray_1.11.6 ggplotify_0.1.3
#> [101] rjson_0.2.23 htmlwidgets_1.6.4
#> [103] farver_2.1.2 memoise_2.0.1
#> [105] htmltools_0.5.8.1 lifecycle_1.0.4
#> [107] httr_1.4.7 fontLiberation_0.1.0
#> [109] bit64_4.6.0-1 MASS_7.3-65
universalmotif_df data
structure