diff --git a/docs/articles/vignette-01-tutorial.html b/docs/articles/vignette-01-tutorial.html index e3d47add..18b6d7f9 100644 --- a/docs/articles/vignette-01-tutorial.html +++ b/docs/articles/vignette-01-tutorial.html @@ -776,7 +776,7 @@
#> ╔═══════════════════════╗
#> ║TreeSequence ║
#> ╠═══════════════╤═══════╣
-#> ║Trees │ 145║
+#> ║Trees │ 126║
#> ╟───────────────┼───────╢
#> ║Sequence Length│ 100000║
#> ╟───────────────┼───────╢
@@ -789,15 +789,15 @@ Running the simulation#> ╔═══════════╤═════╤═════════╤════════════╗
#> ║Table │Rows │Size │Has Metadata║
#> ╠═══════════╪═════╪═════════╪════════════╣
-#> ║Edges │18831│588.5 KiB│ No║
+#> ║Edges │18746│585.8 KiB│ No║
#> ╟───────────┼─────┼─────────┼────────────╢
-#> ║Individuals│12881│ 1.2 MiB│ Yes║
+#> ║Individuals│12909│ 1.2 MiB│ Yes║
#> ╟───────────┼─────┼─────────┼────────────╢
#> ║Migrations │ 0│ 8 Bytes│ No║
#> ╟───────────┼─────┼─────────┼────────────╢
#> ║Mutations │ 0│ 1.2 KiB│ No║
#> ╟───────────┼─────┼─────────┼────────────╢
-#> ║Nodes │18437│684.9 KiB│ Yes║
+#> ║Nodes │18402│683.6 KiB│ Yes║
#> ╟───────────┼─────┼─────────┼────────────╢
#> ║Populations│ 6│ 2.6 KiB│ Yes║
#> ╟───────────┼─────┼─────────┼────────────╢
diff --git a/docs/articles/vignette-02-grid-model.html b/docs/articles/vignette-02-grid-model.html
index 3efbcaac..d281f9bb 100644
--- a/docs/articles/vignette-02-grid-model.html
+++ b/docs/articles/vignette-02-grid-model.html
@@ -285,15 +285,15 @@ Simple two-dimensional grid model#> ╔═══════════╤════╤═════════╤════════════╗
#> ║Table │Rows│Size │Has Metadata║
#> ╠═══════════╪════╪═════════╪════════════╣
-#> ║Edges │9078│283.7 KiB│ No║
+#> ║Edges │9065│283.3 KiB│ No║
#> ╟───────────┼────┼─────────┼────────────╢
-#> ║Individuals│6312│618.0 KiB│ Yes║
+#> ║Individuals│6310│617.8 KiB│ Yes║
#> ╟───────────┼────┼─────────┼────────────╢
#> ║Migrations │ 0│ 8 Bytes│ No║
#> ╟───────────┼────┼─────────┼────────────╢
#> ║Mutations │ 0│ 1.2 KiB│ No║
#> ╟───────────┼────┼─────────┼────────────╢
-#> ║Nodes │9103│338.5 KiB│ Yes║
+#> ║Nodes │9090│338.0 KiB│ Yes║
#> ╟───────────┼────┼─────────┼────────────╢
#> ║Populations│ 25│ 5.7 KiB│ Yes║
#> ╟───────────┼────┼─────────┼────────────╢
@@ -383,7 +383,7 @@ More customized spatial modelmap <- world(xrange = xrange, yrange = yrange, crs = "EPSG:31970")
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpixWAgq/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpW0jrjp/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
diff --git a/docs/articles/vignette-05-tree-sequences.html b/docs/articles/vignette-05-tree-sequences.html
index aa1e5179..664ed664 100644
--- a/docs/articles/vignette-05-tree-sequences.html
+++ b/docs/articles/vignette-05-tree-sequences.html
@@ -368,7 +368,7 @@ Scheduling of sampling events#> ╟───────────┼──────┼─────────┼────────────╢
#> ║Populations│ 4│338 Bytes│ Yes║
#> ╟───────────┼──────┼─────────┼────────────╢
-#> ║Provenances│ 1│ 7.1 KiB│ No║
+#> ║Provenances│ 1│ 7.2 KiB│ No║
#> ╟───────────┼──────┼─────────┼────────────╢
#> ║Sites │ 0│ 16 Bytes│ No║
#> ╚═══════════╧══════╧═════════╧════════════╝
@@ -425,7 +425,7 @@ #> [1] "/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//Rtmp2b5Yn8/file169ac334eac4f"
+#> [1] "/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//Rtmpvid5yL/file22c71c88b37b"
In case have the tree-sequence output saved in a custom location on
disk, we can load the tree sequence using the slendr function
ts_read()
. If we’re dealing with a tree sequence produced
@@ -468,7 +468,7 @@
After we run this script in SLiM, we can use slendr to load the output tree sequence (saved to @@ -395,7 +395,7 @@
We can then load and simplify the output tree sequence in just as we did above in this vignette (or anywhere in the slendr diff --git a/docs/articles/vignette-09-paper.html b/docs/articles/vignette-09-paper.html index 227d1bb1..c3032f49 100644 --- a/docs/articles/vignette-09-paper.html +++ b/docs/articles/vignette-09-paper.html @@ -362,7 +362,7 @@
map <- world(xrange = c(-13, 70), yrange = c(18, 65), crs = 3035)
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmphFFapt/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpAIdLH5/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
@@ -398,7 +398,7 @@ Script from panel A
map <- world(xrange = c(-13, 70), yrange = c(18, 65), crs = 3035)
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmphFFapt/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpAIdLH5/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
@@ -643,22 +643,22 @@ Run time of each code exam
ex1
-5.345070
+5.507727
mins
ex2
-11.470225
+12.246325
mins
ex3
-1.943762
+2.140199
mins
ex4
-0.377528
+0.392370
secs
diff --git a/docs/articles/vignette-11-extensions.html b/docs/articles/vignette-11-extensions.html
index 193da782..626f837b 100644
--- a/docs/articles/vignette-11-extensions.html
+++ b/docs/articles/vignette-11-extensions.html
@@ -259,9 +259,10 @@ Referring to populat
used on the R side of things) using the Eidos function
population()
provided by the built-in SLiM script of
slendr. For instance, let’s assume we compiled a toy model of
-human demography from the vignette #4 and ran it in the
-SLiMgui using slim(..., method = "gui")
. We can then open
-an Eidos console in the GUI and type the following:
+human demography from the vignette
+#4 and ran it in the SLiMgui using
+slim(..., method = "gui")
. We can then open an Eidos
+console in the GUI and type the following:
> // get a SLiM object corresponding to the "AFR" population
> population("AFR")
Subpopulation<p0>
@@ -488,13 +489,13 @@ Savi
To demonstrate how these two functions might be used in practice,
let’s say we wanted to build on the (by default purely neutral!) model
-of African and Eurasian history from this vignette
-discussed above, and say that we want to add a beneficial mutation to
-the “EUR” population at time 15 ky ago. We could utilize the few bits of
-Eidos code introduced so far to define the following slendr
-extension snippet (for now let’s ignore the question of how to actually
-add this to a slendr simulation and focus on the SLiM extension
-code in isolation):
+of African and Eurasian history from this
+vignette discussed above, and say that we want to add a beneficial
+mutation to the “EUR” population at time 15 ky ago. We could utilize the
+few bits of Eidos code introduced so far to define the following
+slendr extension snippet (for now let’s ignore the question of
+how to actually add this to a slendr simulation and focus on
+the SLiM extension code in isolation):
function (void) add_mutation(s pop_name, f selection_coef) {
// sample the first target carrier chromosome of the new mutation...
target = sample(population(pop_name).genomes, 1);
@@ -598,7 +599,8 @@ Putting
slendr’s R interface.
Let’s say that we defined the following model of modern human
demographic history in slendr. This is exactly the same example as the
-one we show in vignette #4:
+one we show in vignette
+#4:
diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml
index 2c1301cc..8aef7f6c 100644
--- a/docs/pkgdown.yml
+++ b/docs/pkgdown.yml
@@ -1,4 +1,4 @@
-pandoc: 3.1.12.1
+pandoc: '3.2'
pkgdown: 2.0.7
pkgdown_sha: ~
articles:
@@ -14,7 +14,7 @@ articles:
vignette-09-paper: vignette-09-paper.html
vignette-10-tracts: vignette-10-tracts.html
vignette-11-extensions: vignette-11-extensions.html
-last_built: 2024-11-19T10:45Z
+last_built: 2024-11-19T14:18Z
urls:
reference: https://www.slendr.net/reference
article: https://www.slendr.net/articles
diff --git a/docs/reference/join.html b/docs/reference/join.html
index bd600f05..70b5ad6a 100644
--- a/docs/reference/join.html
+++ b/docs/reference/join.html
@@ -134,7 +134,7 @@ Examples
real_map <- world(xrange = c(-15, 40), yrange = c(30, 60), crs = "EPSG:3035")
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpINF2s0/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpHBk5HM/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
diff --git a/docs/reference/region.html b/docs/reference/region.html
index ef20e056..f48caec1 100644
--- a/docs/reference/region.html
+++ b/docs/reference/region.html
@@ -157,7 +157,7 @@ Examples
real_map <- world(xrange = c(-15, 40), yrange = c(30, 60), crs = "EPSG:3035")
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpINF2s0/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpHBk5HM/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
diff --git a/docs/reference/ts_recapitate.html b/docs/reference/ts_recapitate.html
index cbe8e983..4e5439d6 100644
--- a/docs/reference/ts_recapitate.html
+++ b/docs/reference/ts_recapitate.html
@@ -163,7 +163,7 @@ Examples#> ╟───────────────┼────────╢
#> ║Sample Nodes │ 26║
#> ╟───────────────┼────────╢
-#> ║Total Size │77.5 KiB║
+#> ║Total Size │77.6 KiB║
#> ╚═══════════════╧════════╝
#> ╔═══════════╤════╤════════╤════════════╗
#> ║Table │Rows│Size │Has Metadata║
diff --git a/docs/reference/world.html b/docs/reference/world.html
index 10e577b2..35fe9cd3 100644
--- a/docs/reference/world.html
+++ b/docs/reference/world.html
@@ -168,7 +168,7 @@ Examples
real_map <- world(xrange = c(-15, 40), yrange = c(30, 60), crs = "EPSG:3035")
#> Reading layer `ne_110m_land' from data source
-#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpINF2s0/naturalearth/ne_110m_land.shp'
+#> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpHBk5HM/naturalearth/ne_110m_land.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 127 features and 3 fields
#> Geometry type: POLYGON
diff --git a/docs/search.json b/docs/search.json
index bce0d65d..9eb50f22 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -1 +1 @@
-[{"path":"https://www.slendr.net/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2022 Martin Petr Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"quick-installation","dir":"Articles","previous_headings":"","what":"Quick installation","title":"Installation instructions","text":"slendr available CRAN R package repository. , can install simply executing install.packages(\"slendr\") R console. want (need) get development version, can install directly GitHub executing devtools::install_github(\"bodkan/slendr\") via R package devtools (can gen devtools running install.packages(\"devtools\")). fact, decide try slendr, please make sure update regularly keep eye changelog regular basis! can find information latest bugfixes potential breaking changes. install slendr, calling library(slendr) check software dependencies available. , R package provide brief helpful guide resolve potential issues. rest vignette talks necessary software dependencies bit detail. Please note slendr extensively tested macOS Linux moment. However, experimental support runnig SLiM msprime simulations Windows analyzing tree-sequence outputs using tskit interface platform well. Feedback issues using slendr Windows highly appreciated.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"software-dependencies","dir":"Articles","previous_headings":"","what":"Software dependencies","title":"Installation instructions","text":"slendr relies three main software dependencies: geospatial data analysis R package sf (encoding spatial slendr models analysing spatial tree-sequence data), forward population genetic simulator SLiM (forward simulations), Python modules tskit, msprime, pyslim (coalescent simulations tree-sequence analysis), also pandas used internally simulation back ends. three widely used respective fields , , easily obtainable major operating systems (see information troubleshoot potential problems). Note depending use case, three sets dependencies necessarily needed. ’re going running forward spatial simulations, don’t need SLiM geospatial R packages sf, stars, rnaturalearth. applies also animation spatial models using gganimate R package. slendr install dependencies. need functionality, install respective R packages manually. vignette, briefly explain get slendr’s software dependencies installed. said, note normal circumstances (exception SLiM), manual installation individual dependencies required.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"sf-stars-rnaturalearth","dir":"Articles","previous_headings":"Software dependencies","what":"sf, stars, rnaturalearth","title":"Installation instructions","text":"R package sf heart geospatial data analysis R. available CRAN can installed major platforms executing install.packages(\"sf\") R session. applies stars rnaturalearth. first load slendr via library(slendr), ’re missing three geospatial R packages, notified instructed can easily obtain CRAN using single call install.packages(). said, sf depends number geospatial libraries depending exact setup Linux macOS machine, libraries missing. Luckily, easy install via Homebrew (macOS) via appropriate package manager Linux distribution (Ubuntu, Fedora, etc.). Detailed instructions operating system can found . ’re problems installation three packages, look help .","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"macos","dir":"Articles","previous_headings":"Software dependencies > sf, stars, rnaturalearth","what":"macOS","title":"Installation instructions","text":"One user recently installed slendr fresh macOS system reported needed install libgit2 order able install package devtools devtools::install_github(\"bodkan/slendr\") step described top page. Additionally, install couple C/C++ libraries well (dependencies sf package). end, able successfully install slendr running: Note assumes Homebrew package manager already setup Mac. ’re beginning computational scientist using Mac, strongly encourage install Homebrew. Sooner later need specific Linux/unix program anyway, Homebrew way get (Mac unix machine, without Homebrew poor one default).","code":"brew install libgit2 udunits gdal proj"},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"linux","dir":"Articles","previous_headings":"Software dependencies > sf, stars, rnaturalearth","what":"Linux","title":"Installation instructions","text":"Testing slendr installation fresh, pristine Debian installation dependencies previously installed, run following: ’s unlikely need (might need packages non-Debian distributions), got slendr dependencies running completely clean system. Might good start case trouble Linux machine. Windows special treatment necessary get slendr running. install slendr via install.packages(\"slendr\"), get binary version package dependencies without need compiling sources.","code":"sudo apt-get install libudunits2-dev libssl-dev libgdal-dev libgsl-dev libgit2-dev libfontconfig1-dev libharfbuzz-dev libfribidi-dev"},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"slim","dir":"Articles","previous_headings":"Software dependencies","what":"SLiM","title":"Installation instructions","text":"forward population genetic software SLiM available major software platforms. complete installation instructions can found . Mac, recommend installing SLiM via pkg installer available direct download website. Linux, can either install SLiM via appropriate package manager Linux distribution (see SLiM manual information), can easily compile . install SLiM Windows, please follow instructions section 2.3.1 SLiM manual. Note although SLiM also available conda, comes without SLiMgui! Note slendr requires SLiM 4.0 work earlier version. , running library(slendr) inform potential issues SLiM installation.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"adding-slim-to-the-path","dir":"Articles","previous_headings":"Software dependencies > SLiM","what":"Adding SLiM to the $PATH","title":"Installation instructions","text":"order slendr able find SLiM installation, R must able find slim binary (slim.exe case Windows) called $PATH variable. easiest way verify true call Sys.(\"slim\") (Sys.(\"slim.exe\") Windows) R session. instance, Mac, get : Windows, get (followed section 2.3.1 SLiM manual describing “official” way install SLiM recommend follow well): , hand, might get something like — empty string: means $PATH R configured properly R (slendr) won’t able find SLiM $PATH. add SLiM $PATH ? Probably convenient way editing .Renviron file. precise location file depends operation system can automatically get open text editor using command (might install.packages(\"usethis\") first): , can either add following (note ’s $ line!): Alternatively, already $PATH contents specified R session, can get calling Sys.getenv(\"PATH\") R console, grab entire string get way, append path installed SLiM using appropriate delimiter (: Linux/macOS, ; Windows) string. case, edit might look something like : instance, Mac, .Renviron file contains line (note last item /usr/local/bin matches path slim showed ): testing Windows machine, .Renviron file (, look path C:/msys64/mingw64/bin;$PATH\" matches slim.exe binary shown ): important check R can find SLiM using Sys.() command described . gives positive result, won’t able use slendr’s spatial SLiM simulations. ’re struggling , search advice related .Renviron $PATH online.","code":"> Sys.which(\"slim\") slim \"/usr/local/bin/slim\" > Sys.which(\"slim.exe\") slim.exe \"C:\\\\msys64\\\\mingw64\\\\bin\\\\slim.exe\" > Sys.which(\"slim\") slim \"\" usethis::edit_r_environ() PATH=\"\" PATH=\"\" PATH=\"/opt/homebrew/bin:/opt/homebrew/sbin:/Users/mp/.my_local/bin:/Users/mp/.my_local/AdmixTools/bin:/Library/TeX/texbin:/opt/homebrew/opt/gnu-sed/libexec/gnubin:/opt/homebrew/opt/coreutils/libexec/gnubin:/usr/local/bin\" PATH=\"C:\\\\msys64\\\\usr\\\\bin;C:\\\\rtools43\\\\x86_64-w64-mingw32.static.posix\\\\bin;C:\\\\rtools43\\\\usr\\\\bin;C:\\\\Program Files (x86)\\\\R\\\\R-4.3.2\\\\bin\\\\x64;C:/msys64/mingw64/bin\""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"fallback-options","dir":"Articles","previous_headings":"Software dependencies > SLiM > Adding SLiM to the $PATH","what":"Fallback options","title":"Installation instructions","text":"don’t want deal editing .Renviron file, able set path SLiM using command Sys.setenv(PATH = \"\"). beginning slendr R scripts though. Alternatively, slim() function argument slim_path= can specify full path slim slim.exe binaries directly (directory ’s case $PATH! full path slim/slim.exe binary ).","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"python","dir":"Articles","previous_headings":"Software dependencies","what":"Python","title":"Installation instructions","text":"order able run coalescent simulations process tree-sequence files, slendr needs Python modules tskit, msprime, pyslim (also needs pandas library). Setting isolated Python environment specific version Python packages (important avoid clashes among different Python programs needed system) can bit hassle users. especially true R users might use Python daily work. order make sure R package appropriate version Python available, correct versions Python module dependencies, slendr provides dedicated function setup_env() automatically downloads completely separate Python distribution installs required versions tskit, msprime, pyslim modules dedicated virtual environment. Moreover, Python installation virtual environment entirely isolated Python configurations already present user’s system, avoiding potential conflicts versions Python Python modules required slendr. Next time call library(slendr), need activate environment automatically calling init_env(). ’re comfortable Python don’t need worry beyond calling setup_env() init_env(), interaction Python necessary working slendr R.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"failing-conda","dir":"Articles","previous_headings":"Software dependencies > Python","what":"Failing conda?","title":"Installation instructions","text":"order support Windows, slendr uses conda download Python interpreter explained . Given fact, run setup_env(), slendr tries leverage conda present install Python dependencies (msprime, tskit, pyslim, pandas) via conda . Unfortunately, conda can break frustratingly many random reasons completely trips setup_env(). run issue, fallback option install Python dependencies msprime, tskit, pyslim, pandas via pip , unlike conda, works practically every time. setup_env() fails conda-related reason, first clear broken environment calling clear_env(), restart R session, call setup_env(pip = TRUE) instead default setup_env(). Note might require install GSL numerical library, ’s trivial issue macOS (brew install gsl) Linux (Ubuntu, instance, sudo apt-get install libgsl-dev).","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"information-for-python-experts","dir":"Articles","previous_headings":"Software dependencies > Python","what":"Information for Python experts","title":"Installation instructions","text":"case wondering slendr accomplish : slendr’s Python interface implemented using R package reticulate. embeds Python interpreter inside R session, enabling high-performance interoperability languages without need user intervention.","code":""},{"path":"https://www.slendr.net/articles/vignette-00-installation.html","id":"modulenotfounderror-no-module-named-tskit-error","dir":"Articles","previous_headings":"Software dependencies > Python","what":"ModuleNotFoundError: No module named 'tskit' error","title":"Installation instructions","text":"’re running error, means slendr prevented activating internal Python virtual environment. likely either didn’t run setup_env() described , forgot run init_env() attempting simulate tree sequence data, Python environment got somehow corrupted. cause slendr’s internal machinery fail pick ’s Python dependencies tree-sequence simulation analysis, leading module named 'tskit' error. reporting error GitHub, please carefully read writeup put together user reported error. contains required information slendr’s Python environment activation works , ’re running error. issue almost always caused problem outside slendr’s influence, ’s important understand ’s going reporting often assumed slendr bug reality isn’t.","code":""},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"motivation","dir":"Articles","previous_headings":"","what":"Motivation","title":"Introduction and basic tutorial","text":"motivation starting project create programmable simulation framework add explicit spatial dimension population genetics models. Specifically, original idea able take models one — representing simplified view history anatomically modern humans (AMH) West Eurasia last ~50 thousand years (comprehensive overview can found review Lazaridis)—design tool makes possible simulate models explicit geographical context capture processes similar following figure (taken study Haak et al. 2015): reason probably clear. lot studying history humans species focused reconstructing population movements, expansions gene flow events, happen geographic context. fact, geographic component often interested (.e., “ancestors population come ?”, “route fast migrate?”, etc.). However, goes beyond just simulating demographic history. instance, selection pressure driving adaptation can often spatially heterogeneous: members population occupying one part continent exposed different environmental pressure individuals elsewhere, allele frequency distributions shaped adaptation process reflect spatial heterogeneity accordingly. framework enables simulation explicitly spatial genomic data situations allow us build realistic models test specific hypotheses, goals simply possible using non-spatial simulation methods. R package slendr introduced vignette presents framework. Internally, package two independent tightly interconnected units: R interface provides set functional primitives (“mini-language” sorts) encoding various features spatio-temporal models: population migrations, expansions gene flow, happening real geographic landscape defined freely available cartographic data. Populations represented simple R objects easily visualized spatial boundaries, making possible build complex models interactively set small simple building blocks. SLiM simulation back end represented built-generic SLiM script designed read spatio-temporal model configuration parameters objects established step 1. , tailor simulation run user-defined model. Alternatively, slendr also supports executing standard population genetics models random-mating setting. means models need explicit geographic map can simulated either built-SLiM back end script, efficient msprime back end also provided package. important design objective make integration parts 1. 2. appear completely seamless. Even extremely complex models, model building execution (.e., simulation) can performed without leaving convenience R interface RStudio. simulation complexities happen automatically hood knowledge SLiM required. fact, motto slendr package “Write complex spatiotemporal population genetics models simple R script.”","code":""},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"geospatial-data","dir":"Articles","previous_headings":"","what":"Geospatial data","title":"Introduction and basic tutorial","text":"Geospatial analysis deep complex topic, dozens libraries programs designed deal fact Earth three-dimensional object forced plot geographical objects (, case, simulate data) two-dimensional plane. Luckily, technical issues Coordinate Reference Systems, transformations manipulation geometric objects (shifting population boundaries, expansions, etc.) pretty much solved now. Unfortunately, dealing issues practice quite challenging requires non-trivial degree domain expertise. Programming even simple task geospatial data analysis also often requires lot code. R package designed provide collection primitives (“mini-language” sorts) programming population dynamics (splits, movement, gene flow, expansion spatial boundaries) across space time without explicitly deal challenges inherent geospatial analyses.","code":""},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"installation-and-setup","dir":"Articles","previous_headings":"","what":"Installation and setup","title":"Introduction and basic tutorial","text":"slendr R package available CRAN can installed simply running install.packages(\"slendr\"). need run latest development version (instance, need latest bugfixes), can get via R package devtools executing devtools::install_github(\"bodkan/slendr\") R terminal. can find detailed installation instructions vignette. get slendr installed, just need load : dependencies (SLiM necessary Python modules) missing, get informative message proceed.","code":"library(slendr) # activate the internal Python environment needed for simulation and # tree-sequence processing init_env() #> The interface to all required Python modules has been activated."},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"defining-the-overall-world-map","dir":"Articles","previous_headings":"","what":"Defining the overall world map","title":"Introduction and basic tutorial","text":"anything else, need define section map world provide context downstream spatio-temporal manipulation population ranges. principle, source geospatial data can manipulated using simple features (sf) infrastructure used. now slendr package implicitly uses Natural Earth project data (vectorized form!), internally loads using rnaturalearth interface. first slendr function look map(). function load map entire world vectorized format zoom specified section world. Note call , specify coordinates zoom geographical Coordinate Reference System (CRS), longitude/latitude, also specified want perform downstream manipulation spatial population maps projected CRS (Lambert Azimuthal Equal-Area projection) appropriate representing wider European continent used tutorial. course, different CRS projections used based part world want simulate. Describing intricacies coordinate reference systems beyond scope tutorial, ’re interested learning encourage read freely available textbook dedicated topic. approach slendr: let user specify everything easy--understand longitude/latitude geographical CRS (can read map, making easy define spatial boundaries trajectories movement), internal data structures final exported spatial maps internally handled projected CRS, important make sure distances proportions distorted. Internally, map object currently normal sf class object without additional components. unlike slendr objects described , also sf objects carry additional internal components. Note summary object says “projected CRS: ETRS89-extended / LAEA Europe”. means world map indeed transformed projected CRS specified .","code":"map <- world( xrange = c(-13, 70), # min-max longitude yrange = c(18, 65), # min-max latitude crs = \"EPSG:3035\" # coordinate reference system (CRS) for West Eurasia ) map #> slendr 'map' object #> ------------------- #> map: internal coordinate reference system EPSG 3035 #> spatial limits (in degrees longitude and latitude): #> - vertical -13 ... 70 #> - horizontal 18 ... 65"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"plotting-geographic-features-and-population-ranges","dir":"Articles","previous_headings":"","what":"Plotting geographic features and population ranges","title":"Introduction and basic tutorial","text":"slendr package implements plotting function called plot_map(). order make easier convenient iteratively build complex models. function can intelligently decide (based given input arguments) right way present data user, helps define models quickly without relying lower-level mechanisms sf package. see examples plot_map() action .","code":""},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"defining-smaller-geographic-regions","dir":"Articles","previous_headings":"","what":"Defining smaller geographic regions","title":"Introduction and basic tutorial","text":"addition overall spatial map context, can also define smaller geographic boundaries. mostly useful whenever want restrict population’s movement (spatial population expansion) smaller region map intuitive geographic meaning (.e., Anatolia, West Eurasia, etc.). Note objects created population boundaries (yet anyway)! simply labels generic geographic boundaries can used later. attached population point. , object returned region() function actually sf object, carries additional annotation name region (“Anatolia”): However, object also carries additional class annotations purpose internal slendr machinery: Furthermore, note region() calls specified map object defined beginning. object added hidden attribute slendr object represents context geospatial transformations, expansions, plots. can use generic plot_map() function plot geographic regions context defined section world map: Note map object longer explicitly specified. needed, class objects provided plot_map() function must carry “map” attribute. fact, object must carry map context — slendr complains whenever case. can check component really , although hidden, using built-attr function verify map object created beginning:","code":"africa <- region( \"Africa\", map, polygon = list(c(-18, 20), c(38, 20), c(30, 33), c(20, 33), c(10, 38), c(-6, 35)) ) europe <- region( \"Europe\", map, polygon = list( c(-8, 35), c(-5, 36), c(10, 38), c(20, 35), c(25, 35), c(33, 45), c(20, 58), c(-5, 60), c(-15, 50) ) ) anatolia <- region( \"Anatolia\", map, polygon = list(c(28, 35), c(40, 35), c(42, 40), c(30, 43), c(27, 40), c(25, 38)) ) anatolia #> slendr 'region' object #> ---------------------- #> name: Anatolia #> #> map: internal coordinate reference system EPSG 3035 class(anatolia) #> [1] \"slendr\" \"slendr_region\" \"sf\" \"data.frame\" plot_map(africa, europe, anatolia, title = \"Geographic regions\") all(attr(europe, \"map\") == map) #> [1] TRUE all(attr(anatolia, \"map\") == map) #> [1] TRUE"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"defining-spatial-population-boundaries","dir":"Articles","previous_headings":"","what":"Defining spatial population boundaries","title":"Introduction and basic tutorial","text":"One aims slendr package formalize specification spatial population boundaries changes time. core function population(), takes population name, time want enforce population’s boundary, effective population size population time, map object described . also specify existing population specified population split (explicitly say ’s ancestral population). specifying actual spatial boundaries, several options.","code":""},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"polygon-population-ranges","dir":"Articles","previous_headings":"Defining spatial population boundaries","what":"Polygon population ranges","title":"Introduction and basic tutorial","text":"can define detailed population boundaries using polygon geometry object region object created region() function , using polygon = argument population(). , reminder, note coordinates described context geographic CRS. First, let’s create African ancestors modern humans. restrict spatial boundary African population africa region defined :","code":"afr <- population(\"AFR\", time = 52000, N = 3000, map = map, polygon = africa) plot_map(afr)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"circular-population-ranges","dir":"Articles","previous_headings":"Defining spatial population boundaries","what":"Circular population ranges","title":"Introduction and basic tutorial","text":"want simulate abstract simple population boundary, can specify circular range center radius arguments instead polygon. distance units slendr package specified coordinate system given “world creation”. instance, EPSG 3035 (’re using ) specifies distances meters. define location population non-Africans right split African ancestors: call plot_map() function returned object, option either plot population range “raw” form “intersected” form, case raw boundary intersected “background” landscape (removing large bodies water, etc.). intersected form ultimately exported serialized format (see ) loaded spatial map SLiM. plot_map() function renders intersected population ranges default.","code":"ooa <- population( \"OOA\", parent = afr, time = 51000, N = 500, remove = 25000, center = c(33, 30), radius = 400e3 ) plot_map(ooa, intersect = TRUE, title = \"'Intersected' population range\")"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"population-movement-across-a-landscape","dir":"Articles","previous_headings":"","what":"Population movement across a landscape","title":"Introduction and basic tutorial","text":"describe directional population movement, can use function move(). accepts coordinates destination points along way (trajectory) duration migration, automatically generates number intermediate spatial maps along trajectory movement produce reasonable degree spatial continuity (number can also specified manually). can inspect object returned move() function see now contains just first YAM population range 7000 years ago, also ranges intermediate locations: Checking result visually , see: Let’s create population Eastern Hunter Gatherers (EHG), split first non-Africans 28000 years ago: ’re , let’s also create population Western Hunter Gatherers (WHG). people living region eventually became present day Europeans receiving gene flow groups time (see ), call “EUR” simplify modeling code little bit:","code":"ooa <- ooa %>% move( trajectory = list(c(40, 30), c(50, 30), c(60, 40)), start = 50000, end = 40000 ) ooa #> slendr 'population' object #> -------------------------- #> name: OOA #> habitat: terrestrial #> #> number of spatial maps: 28 #> map: internal coordinate reference system EPSG 3035 #> scheduled removal at time 25000 #> #> population history overview: #> - time 51000: split from AFR (N = 500) #> - time 50000-40000: movement across a landscape plot_map(ooa, title = \"Intermediate migration maps\") ehg <- population( \"EHG\", parent = ooa, time = 28000, N = 1000, remove = 6000, polygon = list( c(26, 55), c(38, 53), c(48, 53), c(60, 53), c(60, 60), c(48, 63), c(38, 63), c(26, 60)) ) eur <- population( # European population name = \"EUR\", parent = ehg, time = 25000, N = 2000, polygon = europe )"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"spatial-population-expansion","dir":"Articles","previous_headings":"","what":"Spatial population expansion","title":"Introduction and basic tutorial","text":"can simulate expanding range population using function expand_range(), accepts parameters specifying many kilometers boundary expand (argument), long expansion take (duration argument) many intermediate spatial map snapshots exported representing expansion (snapshots argument). instance, let’s represent expansion Anatolian farmers, also split OOA population 28000 years ago time split EHG population. Note use optional parameter, polygon, restricts expansion Europe, instead around Anatolia: Note , principle, specify entire spatio-temporal history population single pipeline using pipe operator %>%. , can inspect object returned expand_range() function see contains spatial maps (“snapshots”) expansion process across time: can () check results visually: visually see really going behind scenes, can also plot raw, non-intersected form expansion : can see population Anatolian farmers point invades spatial boundary EUR population. , doesn’t imply gene flow. section gene flow , see slendr implements gene flow overlapping (non-overlapping) populations. Let’s add couple populations migrations move implementing gene flow . Yamnaya steppe herders:","code":"ana <- population( # Anatolian farmers name = \"ANA\", time = 28000, N = 3000, parent = ooa, remove = 4000, center = c(34, 38), radius = 500e3, polygon = anatolia ) %>% expand_range( # expand the range by 2.500 km by = 2500e3, start = 10000, end = 7000, polygon = join(europe, anatolia) ) ana #> slendr 'population' object #> -------------------------- #> name: ANA #> habitat: terrestrial #> #> number of spatial maps: 16 #> map: internal coordinate reference system EPSG 3035 #> scheduled removal at time 4000 #> #> population history overview: #> - time 28000: split from OOA (N = 3000) #> - time 10000-7000: range expansion plot_map(ana, title = \"Anatolian expansion into Europe\") plot_map(ana, title = \"Anatolian expansion into Europe (not intersected)\", intersect = FALSE) yam <- population( # Yamnaya steppe population name = \"YAM\", time = 7000, N = 500, parent = ehg, remove = 2500, polygon = list(c(26, 50), c(38, 49), c(48, 50), c(48, 56), c(38, 59), c(26, 56)) ) %>% move( trajectory = c(15, 50), start = 5000, end = 3000, snapshots = 8 ) plot_map(yam)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"plotting-multiple-slendr-objects","dir":"Articles","previous_headings":"","what":"Plotting multiple slendr objects","title":"Introduction and basic tutorial","text":"addition plotting individual population ranges, generic function plot_map() can handle combination population ranges, can also partition individual facets. useful visual inspection specified model, looking potential issues export individual spatio-temporal maps. Obviously, lot multi-dimensional information: see better way explore slendr model interactively.","code":"plot_map(afr, ooa, ehg, eur, ana, yam)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"defining-gene-flow-events","dir":"Articles","previous_headings":"","what":"Defining gene flow events","title":"Introduction and basic tutorial","text":"way slendr implements gene flow events calling gene_flow() function. function straightforward interface, shown . One thing note default, populations gene flow events must overlapping spatial ranges order simulate gene flow. probably rather obvious, populations can’t mix space-time don’t overlap given point space-time. example, look spatial boundaries plotted , ’ll see European African populations don’t overlap population ranges. try instruct slendr simulate geneflow , get error: error message instructs us visually verify case, can done slendr’s plot_map() function optional parameter pop_facets = F (set TRUE default). Many models include multiple gene flow events, can collect simple R list: gene_flow() function simply returns data frame collecting geneflow parameters compile_model() step :","code":"gf <- gene_flow(from = eur, to = afr, rate = 0.1, start = 20000, end = 15000) Error: No overlap between population ranges of EUR and AFR at time 20000. Please check the spatial maps of both populations by running `plot_map(eur, afr)` and adjust them accordingly. Alternatively, in case this makes sense for your model, you can add `overlap = F` which will instruct slendr to simulate gene flow without spatial overlap between populations. gf <- list( gene_flow(from = ana, to = yam, rate = 0.5, start = 6500, end = 6400, overlap = FALSE), gene_flow(from = ana, to = eur, rate = 0.5, start = 8000, end = 6000), gene_flow(from = yam, to = eur, rate = 0.75, start = 4000, end = 3000) ) gf #> [[1]] #> from_name to_name tstart tend rate overlap #> 1 ANA YAM 6500 6400 0.5 FALSE #> #> [[2]] #> from_name to_name tstart tend rate overlap #> 1 ANA EUR 8000 6000 0.5 TRUE #> #> [[3]] #> from_name to_name tstart tend rate overlap #> 1 YAM EUR 4000 3000 0.75 TRUE"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"compile-the-whole-model-and-load-it-in-slim","dir":"Articles","previous_headings":"","what":"Compile the whole model and load it in SLiM","title":"Introduction and basic tutorial","text":"crucial function slendr compile_model(). takes population ranges defined across space time, together list gene flow events (optional, since models won’t include gene flow), proceeds converting vectorized spatial ranges raster bitmaps. Furthermore, compiles information split times, \\(N_e\\) values, gene flow directions, times, rates series tables. saved automatically dedicated directory format understood back end SLiM script provided slendr (). files model directory look like? Ideally, user never worry ; fact, whole purpose slendr let work much higher level abstraction without worrying low-level details. said, might find useful see things look like behind curtain… First , can inspect contents directory see , indeed, contain defined spatial maps (now PNG files, SLiM requires). also contains series tab-delimited configuration tables. tables contain summaries model parameters defined graphically , namely: table population splits: table geneflow events: finally, table populations whose spatial maps updated throughout simulation, well times updates (table rather large, ’re showing ). object returned compile_model() function (called model ) binds information together. fact, easier debugging sanity checking, carries locations tables (well important information) inside , elements list: model$splits, model$geneflows, etc. case ’d want separate model specification running different scripts, slendr includes function read_model() just purpose:","code":"model_dir <- paste0(tempfile(), \"_tutorial-model\") model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), # populations defined above gene_flow = gf, # gene-flow events defined above generation_time = 30, resolution = 10e3, # resolution in meters per pixel competition = 130e3, mating = 100e3, # spatial interaction in SLiM dispersal = 70e3, # how far will offspring end up from their parents path = model_dir ) list.files(model_dir, pattern = \"*.jpg\") #> character(0) read.table(file.path(model_dir, \"populations.tsv\"), header = TRUE) #> pop parent N tsplit_gen tsplit_orig tremove_gen tremove_orig #> 1 AFR __pop_is_ancestor 3000 1 52000 -1 -1 #> 2 OOA AFR 500 34 51000 901 25000 #> 3 EHG OOA 1000 801 28000 1534 6000 #> 4 ANA OOA 3000 801 28000 1601 4000 #> 5 EUR EHG 2000 901 25000 -1 -1 #> 6 YAM EHG 500 1501 7000 1651 2500 #> pop_id parent_id #> 1 0 -1 #> 2 1 0 #> 3 2 1 #> 4 3 1 #> 5 4 2 #> 6 5 2 read.table(file.path(model_dir, \"geneflow.tsv\"), header = TRUE) #> from to rate overlap tstart_gen tstart_orig tend_gen tend_orig from_id to_id #> 1 ANA YAM 0.50 0 1518 6500 1521 6400 3 5 #> 2 ANA EUR 0.50 1 1468 8000 1534 6000 3 4 #> 3 YAM EUR 0.75 1 1601 4000 1634 3000 5 4 loaded_model <- read_model(model_dir)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"visualize-the-entire-history-of-splits-and-gene-flow","dir":"Articles","previous_headings":"","what":"Visualize the entire history of splits and gene flow","title":"Introduction and basic tutorial","text":"code snippets , defined simple history European populations last 50000 years. history includes population splits gene flow events, well demographic changes. slendr tries make formal specification spatio-temporal population dynamics concise possible, hard really visualize everything happen SLiM side simulation starts just code alone. purpose, package includes function named plot_model() takes information relationships populations (.e., population gene flow objects defined ) plots form -called admixture graph (see discussion admixture graph concept). One important thing note unlike traditional admixture graphs node/population present , full slendr graph single population can participate many gene flow events course history. visualized assigning color population, different nodes color represent snapshots time demographic event affecting population happens.","code":"plot_model(model, proportions = TRUE)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"interactive-exploration-of-spatio-temporal-models","dir":"Articles","previous_headings":"","what":"Interactive exploration of spatio-temporal models","title":"Introduction and basic tutorial","text":"slightly fancier way visualize models implemented function explore_model(). function accepts compiled model parameter spawns R shiny-based browser app makes possible click time snapshots interactively visualize spatial maps time point.","code":"explore_model(model)"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"running-the-simulation","dir":"Articles","previous_headings":"","what":"Running the simulation","title":"Introduction and basic tutorial","text":"way feed entire serialized model SLiM slim() function, understands format model directory created compile_model() function generates SLiM script (using back end skeleton script part package can found calling system.file(\"scripts/script.slim\", package = \"slendr\"), case ’d like peek internals). output slendr simulation tree-sequence file (produced SLiM simulation behind scenes). tree sequence automatically loaded R returned user: lots slendr allows , terms simulation , also terms analyzing tree-sequence data. list provides list additional resources might want look .","code":"ts <- slim(model, sequence_length = 100000, recombination_rate = 1e-8) ts #> ╔═══════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════╣ #> ║Trees │ 145║ #> ╟───────────────┼───────╢ #> ║Sequence Length│ 100000║ #> ╟───────────────┼───────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼───────╢ #> ║Sample Nodes │ 10000║ #> ╟───────────────┼───────╢ #> ║Total Size │2.7 MiB║ #> ╚═══════════════╧═══════╝ #> ╔═══════════╤═════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪═════╪═════════╪════════════╣ #> ║Edges │18831│588.5 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Individuals│12881│ 1.2 MiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Mutations │ 0│ 1.2 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Nodes │18437│684.9 KiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Populations│ 6│ 2.6 KiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Provenances│ 1│ 41.8 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧═════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-01-tutorial.html","id":"more-information","dir":"Articles","previous_headings":"","what":"More information","title":"Introduction and basic tutorial","text":"vignette described basic features slendr package already quite long. much slendr demonstrated . instance: can tweak parameters influencing dispersal dynamics (“clumpy” populations , far offspring can migrate parents, etc.) change dynamics evolve time. See vignette information. can use slendr program non-spatial models, means standard, Wright-Fisher demographic model can simulated lines R code , instance, plugged Approximate Bayesian Computation pipeline analyses leveraging readily available R packages. can find vignette much detailed example vignette SLiM msprime back ends. can build complex spatial models still abstract (assuming real geographic location), including traditional simulations demes lattice structure. complete example shown vignette. SLiM saves data .trees tree-sequence file format, thanks R package reticulate interfacing Python code incredible power tskit pyslim process simulated data massive scale right fingertips, within convenient environment R. See much detailed example vignette vignette extensive tutorial feature.","code":""},{"path":"https://www.slendr.net/articles/vignette-02-grid-model.html","id":"simple-two-dimensional-grid-model","dir":"Articles","previous_headings":"","what":"Simple two-dimensional grid model","title":"Demes on a regular spatial grid","text":"First, let’s load slendr R package create two-dimensional abstract world map: Next, define helper function ) create single slendr population object, b) place population appropriate coordinate lattice world map based numeric identifier population (runs 1 \\(n \\times n\\) , n total number demes along one side regular grid): defined population construction function, let’s build model. Let’s say want create regular grid n × n populations, N individuals population: Let’s plot whole spatial population configuration, make sure set things correctly: far, way model specified, population stuck circular “island”. can change programming gene flow events using slendr function gene_flow(). , let’s first program simple helper function generate gene flow events according neighborhood relationships two-dimensional grid, allowing population exchange migrants neighbors (making sure coordinates population stay within grid using simple modulo arithmetic population index ). Let’s test function. gene flow events population lower left corner grid (, first population series)? everything works, population allowed exchange migrants neighbor right (population number 2) neighbor . Looks right! Let’s generate entire set continuous gene flow events: total number individual gene flow events : Finally, can compile whole model: familiar SLiM manual recognize model described section 5.3.3. Finally, can run simulation using slim() function.","code":"library(slendr) init_env() #> The interface to all required Python modules has been activated. map <- world( xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\" ) create_pop <- function(i, n_side, map, N, radius) { # get dimensions of the world map dim <- c(diff(attr(map, \"xrange\")), diff(attr(map, \"yrange\"))) # position of the i-th population on the two-dimensional lattice grid coords <- c((i - 1) %% n_side, (i - 1) %/% n_side) center <- coords / n_side * dim + dim / (2 * n_side) pop <- tryCatch({ population( name = sprintf(\"pop%d\", i), N = N, time = 1, map = map, center = center + c(attr(map, \"xrange\")[1], attr(map, \"yrange\")[1]), radius = radius ) }, error = function(e) NULL) pop } n <- 5 populations <- seq(1, n * n) %>% lapply(create_pop, n_side = n, map = map, N = 100, radius = 40) do.call(plot_map, populations) + ggplot2::theme(legend.position = \"none\") set_geneflow <- function(i, n_side, rate, start, end, populations) { pop <- populations[[i]] # get the position of the i-th population on the n*n grid coords <- c((i - 1) %% n_side, (i - 1) %/% n_side) # get coordinates of the i-th population's neighbors on the grid neighbor_pos <- list( c(coords[1] - 1, coords[2]), c(coords[1] + 1, coords[2]), c(coords[1], coords[2] + 1), c(coords[1], coords[2] - 1) ) # generate geneflow events for population coordinates inside the grid geneflows <- lapply(neighbor_pos, function(pos) { if (any(pos < 0 | pos >= n_side)) return(NULL) neighbor <- populations[[pos[2] * n_side + pos[1] + 1]] if (is.null(neighbor)) return(NULL) rbind( gene_flow(from = pop, to = neighbor, rate = rate, start = start, end = end, overlap = FALSE), gene_flow(from = neighbor, to = pop, rate = rate, start = start, end = end, overlap = FALSE) ) }) %>% do.call(rbind, .) geneflows } set_geneflow(1, n, rate = 0.1, start = 2, end = 1000, populations) #> from_name to_name tstart tend rate overlap #> 1 pop1 pop2 2 1000 0.1 FALSE #> 2 pop2 pop1 2 1000 0.1 FALSE #> 3 pop1 pop6 2 1000 0.1 FALSE #> 4 pop6 pop1 2 1000 0.1 FALSE geneflows <- seq(1, n * n) %>% lapply(set_geneflow, n, rate = 0.05, start = 2, end = 1000, populations) %>% do.call(rbind, .) %>% unique # filter out duplicate events due to symmetries nrow(geneflows) #> [1] 80 model <- compile_model( populations = populations, gene_flow = geneflows, generation_time = 1, resolution = 10, competition = 10, mating = 10, dispersal = 10, simulation_length = 1000 ) ts <- slim(model, sequence_length = 10000, recombination_rate = 0) # simulate a single 10kb locus ts #> ╔═══════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════╣ #> ║Trees │ 1║ #> ╟───────────────┼───────╢ #> ║Sequence Length│ 10000║ #> ╟───────────────┼───────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼───────╢ #> ║Sample Nodes │ 5000║ #> ╟───────────────┼───────╢ #> ║Total Size │1.4 MiB║ #> ╚═══════════════╧═══════╝ #> ╔═══════════╤════╤═════════╤════════════╗ #> ║Table │Rows│Size │Has Metadata║ #> ╠═══════════╪════╪═════════╪════════════╣ #> ║Edges │9078│283.7 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Individuals│6312│618.0 KiB│ Yes║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Mutations │ 0│ 1.2 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Nodes │9103│338.5 KiB│ Yes║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Populations│ 25│ 5.7 KiB│ Yes║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Provenances│ 1│ 41.7 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-02-grid-model.html","id":"population-grid-on-a-real-geographic-landscape","dir":"Articles","previous_headings":"","what":"Population grid on a real geographic landscape","title":"Demes on a regular spatial grid","text":"can take things one step . wanted similar thing (.e. simulate regularly spaced demes) real geographic context? Let’s zoom interesting part world create grid demes using helper function create_pop defined (population boundary 300 km diameter): course, lay regular grid across map world, population boundaries fall outside African continent. solve issue, go list populations filter least 50% area land, using another helper function: Let’s plot layout population grid real geographic background: Next, probably set scenario gene flow subpopulations; perhaps interested studying selected allele spreads continent based factors interest. , simulate data spatial model, first compile_model() run SLiM via slim() function. Given process described example , won’t repeating .","code":"map <- world( xrange = c(-25, 55), yrange = c(-32, 35), crs = 4326 ) n <- 20 populations <- seq(1, n * n) %>% lapply(create_pop, n_side = n, map = map, N = 100, radius = 1.5) continent <- region( map = map, polygon = list( c(-10, 35), c(-20, 20), c(-15, 8), c(-10, 5), c(0, 2), c(20, -40), c(35, -32), c(50, -25), c(55, -10), c(50, 0), c(53, 13), c(45, 10), c(37, 20), c(32, 30), c(16, 38), c(0, 38) ) ) check_area <- function(pop, map, continent) { if (is.null(pop)) return(NULL) # total population area pop_area <- area(pop)$area # population area overlapping a map map_area <- area(overlap(pop, map)) # population area overlapping African continent continent_area <- area(overlap(pop, continent)) # at least 50% of population's boundary be on land, and it must fall # on to the African continent itself if (continent_area == 0 || (map_area / pop_area) < 0.5) return(NULL) else return(pop) } filtered <- lapply(populations, check_area, map, continent) %>% Filter(Negate(is.null), .) do.call(plot_map, filtered) + ggplot2::theme(legend.position = \"none\")"},{"path":"https://www.slendr.net/articles/vignette-02-grid-model.html","id":"more-customized-spatial-model","dir":"Articles","previous_headings":"","what":"More customized spatial model","title":"Demes on a regular spatial grid","text":"want introduce spatiality model need manual control position subpopulation, can course customize spatial layout much detail. Consider followin map South America: Let’s lay demes scattered, irregular fashion. illustration purposes, define population’s geographic range circle radius 200 km. Importantly, note introduced one ancestral population population associated location world map! want simulate data coalescent backend, formally encode population genealogies eventually coalesce. words, models specified run msprime() back end contain isolated demes formally descend single ancestor run models msprime, get error infinite coalescent times. formally introduce ancestral population : Furthermore, let’s say fairly good idea complex interaction/gene-flow network deme, can encode like (instead forcing regular arrangement demes gene-flow interactions previous examples): Now can compile model `twist, however: try simulate data non-spatial way using msprime, skip serialization model data disk, skipping spatial interaction dispersal parameters. words, care simulating demographic model traditional, Wright-Fisher, random-mating demes gene flow , dispersal within individual deme. ? Mostly just demonstrate possible! However, certainly situations “half-spatial” model useful – instance, situation within-deme individual dynamics interest, location subpopulation . can make sure demographic model specified correctly quickly visualizing : can also plot spatial organization demes map glory, including gene flow events (one arrow per unidirectional gene flow). Note get warning message stating (non-spatial) ancestral population won’t visualized map: Finally, can simulate tree sequence model! Notice simulating data using msprime(), effectively dropping continuous space dimension model. situation, map serves visual aid, making easier set complex “spatial” position demes map.","code":"xrange <- c(-90, -20) yrange <- c(-58, 15) map <- world(xrange = xrange, yrange = yrange, crs = \"EPSG:31970\") #> Reading layer `ne_110m_land' from data source #> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpixWAgq/naturalearth/ne_110m_land.shp' #> using driver `ESRI Shapefile' #> Simple feature collection with 127 features and 3 fields #> Geometry type: POLYGON #> Dimension: XY #> Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513 #> Geodetic CRS: WGS 84 plot_map(map) # non-spatial ancestral population p_anc <- population(\"p_anc\", N = 1000, time = 1) # spatial populations p1 <- population(\"p1\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-75, 0), radius = 200e3) p2 <- population(\"p2\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-60, 5), radius = 200e3) p3 <- population(\"p3\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-65, -5), radius = 200e3) p4 <- population(\"p4\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-60, -20), radius = 200e3) p5 <- population(\"p5\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-65, -35), radius = 200e3) p6 <- population(\"p6\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-69, -42), radius = 200e3) p7 <- population(\"p7\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-51, -10), radius = 200e3) p8 <- population(\"p8\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-45, -15), radius = 200e3) p9 <- population(\"p9\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-71, -12), radius = 200e3) p10 <- population(\"p10\", N = 1000, time = 500, parent = p_anc, map = map, center = c(-55, -25), radius = 200e3) gf <- list( gene_flow(p1, p2, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p2, p1, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p1, p3, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p3, p1, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p2, p3, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p3, p2, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p2, p7, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p7, p2, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p3, p7, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p7, p3, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p7, p8, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p8, p7, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p4, p7, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p7, p4, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p4, p5, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p5, p4, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p5, p6, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p6, p5, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p3, p4, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p4, p3, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p1, p9, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p9, p1, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p3, p9, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p9, p3, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p4, p9, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p9, p4, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p10, p4, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p4, p10, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p10, p8, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p8, p10, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p10, p5, 0.1, 1000, 2000, overlap = FALSE), gene_flow(p5, p10, 0.1, 1000, 2000, overlap = FALSE) ) model <- compile_model( populations = list(p_anc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10), gene_flow = gf, generation_time = 1, simulation_length = 5000, serialize = FALSE ) #> Warning: Model containing a mix of spatial and non-spatial populations will be compiled. #> Although this is definitely supported, make sure this is really what you want. #> Warning: Spatial models must be serialized to disk for SLiM to simulate data from. #> Compiled like this, your model can only be simulated with msprime. plot_model(model) plot_map(model, gene_flow = TRUE) #> Warning: All gene-flow event will be visualized at once. If you wish to visualize #> gene flows at a particular point in time, use the `time` argument. #> Warning: Non-spatial populations in your model won't be visualized ts <- msprime(model, sequence_length = 10e6, recombination_rate = 1e-8, random_seed = 42) ts #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 34057║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 10000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 22000║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 9.4 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │182742│ 5.6 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 11000│300.8 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │ 74344│ 2.0 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 11│605 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 1│ 10.5 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-04-nonspatial-models.html","id":"extracting-parameters-from-a-model-or-tree-sequences","dir":"Articles","previous_headings":"","what":"Extracting parameters from a model or tree sequences","title":"Traditional, non-spatial models","text":"situations (model parameters drawn random distributions need know parameters used simulations), function extract_parameters() can used. function peeks slendr tree sequence object extract parameters original slendr model: completeness, although isn’t relevant example either, function can also get parameters compiled model. instance, can check parameters used compile built-slendr introgression model running: can see, extract_parameters() returns list data frames, one data frame aspect demographic model (applicable).","code":"extract_parameters(ts_msprime) #> $splits #> pop parent N time remove #> 1 AFR 3000 100000 NA #> 2 OOA AFR 500 60000 23000 #> 3 EHG OOA 1000 28000 6000 #> 5 ANA OOA 3000 28000 4000 #> 4 EUR EHG 2000 25000 NA #> 6 YAM EHG 500 7000 2500 #> #> $gene_flows #> from to start end rate #> 1 ANA YAM 6500 6400 0.50 #> 2 ANA EUR 8000 6000 0.50 #> 3 YAM EUR 4000 3000 0.75 #> #> $resizes #> pop how N time end #> 1 OOA step 2000 40000 NA #> 2 EUR exponential 10000 5000 0 introgression_model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) extract_parameters(introgression_model) #> $splits #> pop parent N time remove #> 1 CH 10 6500000 NA #> 2 AFR CH 10 6000000 NA #> 3 NEA AFR 10 600000 40000 #> 4 EUR AFR 5000 70000 NA #> #> $gene_flows #> from to start end rate #> 1 NEA EUR 55000 45000 0.03"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"setting-up-python-environment","dir":"Articles","previous_headings":"","what":"Setting up Python environment","title":"Tree-sequence processing and statistics","text":"First, order able interface tskit pyslim using reticulate package (run simulations using msprime, ), need working Python environment required Python modules pyslim, tskit msprime already installed. setting Python environments can quite hassle, slendr provides single function setup_env() make things easier. call without arguments, slendr automatically download, install, setup completely separate Python environment (based “miniconda” distribution) just slendr activate background. important stress setup_env() interfere way Python installations might already computer. Python installation environment entirely isolated used just purpose slendr workflows. Python environment set , can activate calling: can use another built-function check_env() make sure slendr installed configured correct environment us: Now ’re good go ready simulate analyse tree sequence outputs R!","code":"setup_env() init_env() #> The interface to all required Python modules has been activated. check_env() #> Summary of the currently active Python environment: #> #> Python binary: /Users/mp/Library/r-miniconda-arm64/envs/Python-3.12_msprime-1.3.3_tskit-0.5.8_pyslim-1.0.4_tspop-0.0.2/bin/python #> Python version: 3.12.7 | packaged by conda-forge | (main, Oct 4 2024, 15:57:01) [Clang 17.0.6 ] #> #> slendr requirements: #> - tskit: version ✓ #> - msprime: version ✓ #> - pyslim: version 1.0.4 ✓ #> - tspop: present ✓"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"model-of-neanderthal-introgression-into-eurasians","dir":"Articles","previous_headings":"Setting up Python environment","what":"Model of Neanderthal introgression into Eurasians","title":"Tree-sequence processing and statistics","text":"First, let’s set simple non-spatial model Neanderthal introgression using slendr. essentially procedure shown another vignette introducing non-spatial slendr models. different spatial model, except left map argument calling population(). ’s toy model visualized “demographic graph” sorts (.e., tree-like structure specifying population splits additional edges representing gene flow events). particularly illuminating simple example, ’s always worth keeping mind graph embedded within every slendr model can always invoked make sure model ’re setting correct:","code":"library(ggplot2) library(dplyr) #> #> Attaching package: 'dplyr' #> The following objects are masked from 'package:stats': #> #> filter, lag #> The following objects are masked from 'package:base': #> #> intersect, setdiff, setequal, union set.seed(314159) # create the ancestor of everyone and a chimpanzee outgroup # (we set both N = 1 to reduce the computational time for this model) chimp <- population(\"CH\", time = 6.5e6, N = 1000) # two populations of anatomically modern humans: Africans and Europeans afr <- population(\"AFR\", parent = chimp, time = 6e6, N = 10000) eur <- population(\"EUR\", parent = afr, time = 70e3, N = 5000) # Neanderthal population splitting at 600 ky ago from modern humans # (becomes extinct by 40 ky ago) nea <- population(\"NEA\", parent = afr, time = 600e3, N = 1000, remove = 40e3) # 3% Neanderthal introgression into Europeans between 55-50 ky ago gf <- gene_flow(from = nea, to = eur, rate = 0.03, start = 55000, end = 45000) model <- compile_model( populations = list(chimp, nea, afr, eur), gene_flow = gf, generation_time = 30, path = paste0(tempfile(), \"_introgression\") ) cowplot::plot_grid( plot_model(model, sizes = FALSE), plot_model(model, sizes = FALSE, log = TRUE), nrow = 1 )"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"scheduling-of-sampling-events","dir":"Articles","previous_headings":"","what":"Scheduling of sampling events","title":"Tree-sequence processing and statistics","text":"Now defined model, sample data ? Ideally, like schedule sampling events given time, sampling defined number individuals given population. slendr provides function schedule_sampling() serves define sampling schedule automatically enforces populations already (.e. appearance simulation) still (removed simulation) present sampled . example, want sample two Neanderthal individuals (older one Altai Neanderthal published Pruefer et al. 2014, younger one Vindija Neanderthal published Pruefer et al., 2017). two genomes need estimate Neanderthal ancestry proportion using -called \\(f_4\\)-ratio statistic (, also see Petr et al., PNAS 2019): can see, schedule_sampling() function simply accepts vector times remembering schedule, list pairs (, ) encoding populations many individuals remembered time points given times vector. Next, want sample present-day individuals: outgroup representing chimpanzee, couple Africans Europeans: can see , schedule_sampling() function returns plain old data frame simple structure three columns: time, population name, number individuals. means can define sampling events using whatever input data might already available (radiocarbon-dated ancient DNA samples Excel sheet publication). instance, lot interest estimate trajectory Neanderthal ancestry Europe time using ancient DNA data anatomically modern human individuals (also called early modern humans, EMH) across last couple tens thousands years. can simulate something close available EMH ancient DNA data set last 50 thousand years running : samples single ancient European individuals randomly chosen times 40 10 ky ago. One nice feature schedule_sampling() function schedules sampling events population, population present simulation given time. makes possible simply take wide time range sampling, specify populations sizes samples, let function generate sampling events populations present time. reason stricter control sampling required, behavior can switched setting strict = TRUE like : Now already model object ready, can simulate data , sampling individuals according sampling schedule. Although use slim() function shown previous vignettes, case run simulation msprime() coalescent back end. , model non-spatial using coalescent simulator much efficient forward simulation. Switching msprime SLiM back ends slendr demonstrated much detail dedicated vignette. simulation back end utilized msprime() function (well slim() function) produces tree-sequence output immediately loaded ready downstream analysis. Note bind individual sampling schedule data frames using rbind function provided base R (show , sampling schedule really just data frame can manipulate ).","code":"nea_samples <- schedule_sampling(model, times = c(70000, 40000), list(nea, 1)) nea_samples #> # A tibble: 2 × 7 #> time pop n y_orig x_orig y x #> #> 1 40000 NEA 1 NA NA NA NA #> 2 70000 NEA 1 NA NA NA NA present_samples <- schedule_sampling(model, times = 0, list(chimp, 1), list(afr, 5), list(eur, 10)) present_samples #> # A tibble: 3 × 7 #> time pop n y_orig x_orig y x #> #> 1 0 CH 1 NA NA NA NA #> 2 0 AFR 5 NA NA NA NA #> 3 0 EUR 10 NA NA NA NA emh_samples <- schedule_sampling(model, times = runif(n = 40, min = 10000, max = 40000), list(eur, 1)) emh_samples #> # A tibble: 40 × 7 #> time pop n y_orig x_orig y x #> #> 1 10320 EUR 1 NA NA NA NA #> 2 11188 EUR 1 NA NA NA NA #> 3 11396 EUR 1 NA NA NA NA #> 4 11529 EUR 1 NA NA NA NA #> 5 11927 EUR 1 NA NA NA NA #> 6 12675 EUR 1 NA NA NA NA #> 7 13689 EUR 1 NA NA NA NA #> 8 13744 EUR 1 NA NA NA NA #> 9 14775 EUR 1 NA NA NA NA #> 10 16362 EUR 1 NA NA NA NA #> # ℹ 30 more rows # this attempts to sample a Neanderthal individual at a point when Neanderthals # are already extinct, resulting in an error schedule_sampling(model, times = 10000, list(nea, 1), strict = TRUE) Error: Cannot schedule sampling for 'NEA' at time 10000 because the population will not be present in the simulation at that point. Consider running this function with `strict = FALSE` which will automatically retain only valid sampling events. ts <- msprime( model, sequence_length = 100e6, recombination_rate = 1e-8, samples = rbind(nea_samples, present_samples, emh_samples), random_seed = 314159, verbose = TRUE ) ts #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 240041║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 116║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 40.0 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │916588│ 28.0 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 58│ 1.6 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │189654│ 5.1 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 1│ 7.1 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"r-interface-for-tskit-and-pyslim","dir":"Articles","previous_headings":"","what":"R interface for tskit and pyslim","title":"Tree-sequence processing and statistics","text":"Tree-sequences one revolutionary developments population genetics last couple decades number reasons. One possibility store extremely large data sets succinctly encoding entire evolutionary history sample individuals series correlated tree genealogies along genome. Going much detail topic clearly beyond scope tutorial, especially everything explain much better elsewhere. Instead, demonstrate rest vignette can access manipulate tree-sequence outputs generated slendr models perform various statistics using Python modules tskit pyslim directly slendr, without leave R! key magical R package reticulate creates seamless binding Python modules R. means even don’t know Python, slendr allows quite lot tree-sequences R. course, proficient Python user, needs said tree-sequence file generated slendr & SLiM, can easily perform every conceivable analysis directly using tskit. intention show can continue working tree-sequence files R even run entire slendr simulation.","code":""},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"loading-and-processing-tree-sequence-output-files","dir":"Articles","previous_headings":"","what":"Loading and processing tree-sequence output files","title":"Tree-sequence processing and statistics","text":"default, msprime() slim() run produce tree-sequence object (saved temporary file) immediately load . Thus, use-cases, explicit loading simulated tree sequence needed. said, sake completeness, let’s run simulation save tree-sequence file . case tree-sequence output saved custom location disk, can load tree sequence using slendr function ts_read(). ’re dealing tree sequence produced SLiM back end (case ), can also instruct function simplify tree-sequence individuals explicitly sampled (recall sampling schedule set schedule_sampling() function ). Note provide model object generated compile_model() order model annotation information simulated tree-sequence data (, loading): surprisingly, get output got printed tree sequence returned msprime() function. shows normal circumstances, loading output manually via ts_read() needed. try simplify msprime-generated tree sequence, get warning. tree sequence already simplified form, definition coming coalescent simulator. default, simplification trims tree sequence remembered individuals (.e. explicitly scheduled sampling), true every msprime tree sequence. Alternatively, can also narrow simplification defined set individuals using simplify_to = argument. Internally, simplification implemented dedicated function ts_simplify() can always call explicitly, like : Similarly, slendr provides function ts_recapitate() performs [recapitation]https://tskit.dev/pyslim/docs/latest/tutorial.html#recapitation). , needed msprime tree sequence, fully coalesced (recapitated) definition. current tree sequence object, simply get warning informing us ’re attempting something effect: can make sure tree sequence fully coalesced calling another slendr function ts_coalesced(). useful dealing slim()-produced tree sequences: might noticed simulate mutations SLiM run. computational efficiency. Luckily, tree-sequence contains complete history sample individuals makes easy sprinkle mutations genealogies simulation . can add mutations given rate running: processed simulated tree sequence, can calculate basic statistics simulated data. However, , first like note everything rest vignette (.e. whenever call function prefix ts_*() slendr), interfacing tskit Python module hood. goal capture analyses one might want perform tree-sequences R wrap neat interface indistinguishable R function—, , reason reticulate created first place (making various Python data science modules appear regular R packages).","code":"output_file <- tempfile() ts <- msprime( model, sequence_length = 100e6, recombination_rate = 1e-8, samples = rbind(nea_samples, present_samples, emh_samples), random_seed = 314159 ) ts_write(ts, output_file) output_file #> [1] \"/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//Rtmp2b5Yn8/file169ac334eac4f\" ts <- ts_read(output_file, model) ts #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 240041║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 116║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 40.0 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │916588│ 28.0 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 58│ 1.6 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │189654│ 5.1 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 1│ 7.1 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝ ts_simplify(ts) #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 238681║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 116║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 36.7 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │864772│ 26.4 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 58│ 1.6 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │139203│ 3.7 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 2│ 7.7 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝ ts_small <- ts_simplify(ts, simplify_to = c(\"CH_1\", \"NEA_1\", \"NEA_2\", \"AFR_1\", \"AFR_2\", \"EUR_20\", \"EUR_50\")) ts_small #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 132496║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 14║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 19.0 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │441206│ 13.5 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 7│220 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │ 79249│ 2.1 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 2│ 7.7 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝ ts <- ts_recapitate(ts, recombination_rate = 1e-8, Ne = 10000) ts_coalesced(ts) #> [1] TRUE ts <- ts_mutate(ts, mutation_rate = 1e-8, random_seed = 314159) ts #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 240041║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 116║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 76.8 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │916588│ 28.0 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 58│ 1.6 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │623249│ 22.0 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │189654│ 5.1 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 2│ 7.9 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │621331│ 14.8 MiB│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"visualisation-of-trees-and-tree-sequences","dir":"Articles","previous_headings":"","what":"Visualisation of trees and tree-sequences","title":"Tree-sequence processing and statistics","text":"Now introduce function ts_phylo() can used extract one tree tree-sequence (either -th tree sequence, tree overlapping -th position simulated genome, depending value mode argument) convert phylo class, standard format phylogenetic trees R world. phylo format, see packages ape, phangorn, phytools. type tree object R console, can verify got ordinary phylo object: means whole R phylogenetic ecosystem disposal analyze trees. instance can use powerful package ggtree plot tree just extracted:","code":"# extract the 42nd tree in the tree sequence tree <- ts_phylo(ts_small, 42 - 1) #> Starting checking the validity of tree... #> Found number of tips: n = 14 #> Found number of nodes: m = 13 #> Done. tree #> #> Phylogenetic tree with 14 tips and 13 internal nodes. #> #> Tip labels: #> 13 (EUR_50), 12 (EUR_50), 11 (CH_1), 10 (CH_1), 9 (AFR_2), 8 (AFR_2), ... #> Node labels: #> 77386, 70, 2699, 5175, 5199, 6642, ... #> #> Rooted; includes branch lengths. library(ggtree) #> ggtree v3.10.1 For help: https://yulab-smu.top/treedata-book/ #> #> If you use the ggtree package suite in published research, please cite #> the appropriate paper(s): #> #> Guangchuang Yu, David Smith, Huachen Zhu, Yi Guan, Tommy Tsan-Yuk Lam. #> ggtree: an R package for visualization and annotation of phylogenetic #> trees with their covariates and other associated data. Methods in #> Ecology and Evolution. 2017, 8(1):28-36. doi:10.1111/2041-210X.12628 #> #> Guangchuang Yu. Data Integration, Manipulation and Visualization of #> Phylogenetic Trees (1st edition). Chapman and Hall/CRC. 2022, #> doi:10.1201/9781003279242 #> #> G Yu. Data Integration, Manipulation and Visualization of Phylogenetic #> Trees (1st ed.). Chapman and Hall/CRC. 2022. ISBN: 9781032233574 ggtree(tree) + geom_point2(aes(subset = !isTip)) + # points for internal nodes geom_tiplab() + # sample labels for tips hexpand(0.1) # make more space for the tip labels library(ape) plot(tree) nodelabels()"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"accessing-tskit-functionality-directly","dir":"Articles","previous_headings":"","what":"Accessing tskit functionality directly","title":"Tree-sequence processing and statistics","text":"mentioned previous section, goal vignette show use slendr perform main tree-sequence operations using convenient R interface tskit. However, always keep mind restricted subset tskit functionality slendr translated R (.e. functions prefix ts_). Thanks incredible R package reticulate, can access Python methods object variables directly, using $ operator. example, instead calling function ts_coalesced() tree-sequence , check trees coalesced running following snippet instead (note inefficient ’re operation first one hundred trees): believe makes sense use R interface whenever possible (even makes many operations little bit convenient). However, functionality slendr missing, can always resort accessing Python objects directly just demonstrated. can verify methods attributes Python tree-sequence object still accessible R: fact, recognize elements output examples involving ts_ functions vignette! short, blackbox—slendr provides slightly convenient layer tskit R users.","code":"# iterate over all trees in the tree-sequence and check if each # has only one root (i.e. is fully coalesced) - note that Python # lists are 0-based, which is something we need to take care of all(sapply(seq_len(ts$num_trees)[1:100], function(i) ts$at_index(i - 1)$num_roots == 1)) names(ts) #> [1] \"alignments\" \"allele_frequency_spectrum\" #> [3] \"as_fasta\" \"as_nexus\" #> [5] \"as_vcf\" \"aslist\" #> [7] \"at\" \"at_index\" #> [9] \"breakpoints\" \"check_index\" #> [11] \"coalescence_time_distribution\" \"coiterate\" #> [13] \"count_topologies\" \"decapitate\" #> [15] \"delete_intervals\" \"delete_sites\" #> [17] \"diffs\" \"discrete_genome\" #> [19] \"discrete_time\" \"divergence\" #> [21] \"divergence_matrix\" \"diversity\" #> [23] \"draw_svg\" \"draw_text\" #> [25] \"dump\" \"dump_tables\" #> [27] \"dump_text\" \"edge\" #> [29] \"edge_diffs\" \"edges\" #> [31] \"edges_child\" \"edges_left\" #> [33] \"edges_parent\" \"edges_right\" #> [35] \"edgesets\" \"equals\" #> [37] \"f2\" \"f3\" #> [39] \"f4\" \"file_uuid\" #> [41] \"first\" \"Fst\" #> [43] \"genealogical_nearest_neighbours\" \"general_stat\" #> [45] \"genetic_relatedness\" \"genetic_relatedness_weighted\" #> [47] \"genotype_matrix\" \"get_ll_tree_sequence\" #> [49] \"get_num_mutations\" \"get_num_nodes\" #> [51] \"get_num_records\" \"get_num_sites\" #> [53] \"get_num_trees\" \"get_pairwise_diversity\" #> [55] \"get_population\" \"get_sample_size\" #> [57] \"get_samples\" \"get_sequence_length\" #> [59] \"get_time\" \"haplotypes\" #> [61] \"has_reference_sequence\" \"ibd_segments\" #> [63] \"impute_unknown_mutations_time\" \"indexes_edge_insertion_order\" #> [65] \"indexes_edge_removal_order\" \"individual\" #> [67] \"individual_locations\" \"individual_populations\" #> [69] \"individual_times\" \"individuals\" #> [71] \"individuals_flags\" \"individuals_location\" #> [73] \"individuals_population\" \"individuals_time\" #> [75] \"kc_distance\" \"keep_intervals\" #> [77] \"last\" \"ll_tree_sequence\" #> [79] \"load\" \"load_tables\" #> [81] \"ltrim\" \"max_root_time\" #> [83] \"max_time\" \"mean_descendants\" #> [85] \"metadata\" \"metadata_schema\" #> [87] \"migration\" \"migrations\" #> [89] \"migrations_dest\" \"migrations_left\" #> [91] \"migrations_node\" \"migrations_right\" #> [93] \"migrations_source\" \"migrations_time\" #> [95] \"min_time\" \"mutation\" #> [97] \"mutations\" \"mutations_node\" #> [99] \"mutations_parent\" \"mutations_site\" #> [101] \"mutations_time\" \"nbytes\" #> [103] \"newick_trees\" \"node\" #> [105] \"nodes\" \"nodes_flags\" #> [107] \"nodes_individual\" \"nodes_population\" #> [109] \"nodes_time\" \"num_edges\" #> [111] \"num_individuals\" \"num_migrations\" #> [113] \"num_mutations\" \"num_nodes\" #> [115] \"num_populations\" \"num_provenances\" #> [117] \"num_samples\" \"num_sites\" #> [119] \"num_trees\" \"pairwise_diversity\" #> [121] \"parse_windows\" \"population\" #> [123] \"populations\" \"provenance\" #> [125] \"provenances\" \"records\" #> [127] \"reference_sequence\" \"rtrim\" #> [129] \"sample_count_stat\" \"sample_size\" #> [131] \"samples\" \"segregating_sites\" #> [133] \"sequence_length\" \"simplify\" #> [135] \"site\" \"sites\" #> [137] \"sites_position\" \"split_edges\" #> [139] \"subset\" \"table_metadata_schemas\" #> [141] \"tables\" \"tables_dict\" #> [143] \"Tajimas_D\" \"time_units\" #> [145] \"to_macs\" \"to_nexus\" #> [147] \"trait_correlation\" \"trait_covariance\" #> [149] \"trait_linear_model\" \"trait_regression\" #> [151] \"trees\" \"trim\" #> [153] \"union\" \"variants\" #> [155] \"write_fasta\" \"write_nexus\" #> [157] \"write_vcf\" \"Y1\" #> [159] \"Y2\" \"Y3\""},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"calculating-f-statistics","dir":"Articles","previous_headings":"","what":"Calculating f-statistics","title":"Tree-sequence processing and statistics","text":"addition revolutionary breakthrough terms computation efficiency, many statistics often interested population genetics natural consequence direct access tree sequence genealogies, simply genealogies capture true demographic history sample. , can’t go much detail encourage take look paper Ralph et al. duality statistics expressed terms branch lengths traditional summaries based samples genetic variation. instance, functions ts_f2(), ts_f3(), ts_f4() ts_f4ratio() calculate well-known set Patterson’s \\(f\\)-statistics: functions accept mode = argument, specifying whether statistics calculated using mutation site patterns (mode = \"site\", default), branch lengths (mode = \"branch\"), node (mode = \"node\"), well windows argument, similarly “multiway” statistics implemented tskit. See relevant sections official tskit documentation topic. Note previous chunk referred individuals names (numeric IDs nodes tskit Python). allow readability make easier see individuals based specified sampling schedule (names assigned individuals based order sampling). can get overview individuals scheduled sampling (.e. permanently remembered) names helper function ts_samples(): said, like run statistics nodes rather individuals, can simply using integer IDs instead character names function’s interface.","code":"# f2 is a measure of the branch length connecting A and B ts_f2(ts, A = \"EUR_1\", B = \"AFR_1\") #> # A tibble: 1 × 3 #> A B f2 #> #> 1 EUR_1 AFR_1 0.0000448 # f4 is a measure of the drift shared between A and B after their split from C ts_f3(ts, A = \"EUR_1\", B = \"AFR_1\", C = \"CH_1\") #> # A tibble: 1 × 4 #> A B C f3 #> #> 1 EUR_1 AFR_1 CH_1 0.0000130 # this value should be very close to zero (no introgression in Africans) ts_f4(ts, \"AFR_1\", \"AFR_2\", \"NEA_1\", \"CH_1\", mode = \"branch\") #> # A tibble: 1 × 5 #> W X Y Z f4 #> #> 1 AFR_1 AFR_2 NEA_1 CH_1 -118. # this value should be significantly negative (many more ABBA sites # compared to BABA site due to the introgression into Europeans) ts_f4(ts, \"AFR_1\", \"EUR_1\", \"NEA_1\", \"CH_1\", mode = \"branch\") #> # A tibble: 1 × 5 #> W X Y Z f4 #> #> 1 AFR_1 EUR_1 NEA_1 CH_1 -741. ts_samples(ts) #> # A tibble: 58 × 3 #> name time pop #> #> 1 NEA_1 70000 NEA #> 2 NEA_2 40000 NEA #> 3 EUR_1 39171 EUR #> 4 EUR_2 39134 EUR #> 5 EUR_3 37739 EUR #> 6 EUR_4 36531 EUR #> 7 EUR_5 36362 EUR #> 8 EUR_6 35945 EUR #> 9 EUR_7 33900 EUR #> 10 EUR_8 33854 EUR #> # ℹ 48 more rows"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"estimating-neanderthal-ancestry-proportions","dir":"Articles","previous_headings":"","what":"Estimating Neanderthal ancestry proportions","title":"Tree-sequence processing and statistics","text":"Let’s try put new tools practice estimate proportion Neanderthal ancestry Africans Europeans simulated data. can using Patterson’s \\(f_4\\)-ratio statistic implemented ts_f4ratio() function slendr (can find information particular version statistic Petr et al., PNAS 2019): now summarise inferred Neanderthal distribution populations, see Neanderthal ancestry Africans (expected model–Africans receive Neanderthal introgression pulse) small proportion Neanderthal ancestry Europeans (consistent 3% introgression pulse simulated ): exactly specified model configuration , suggesting simulations work . can see quite bit noise ’s simulated small amount sequence. can also plot trajectory Neanderthal ancestry Europe time-window simulated ancient present-day DNA samples: , result consistent empirical estimates Neanderthal ancestry using ancient DNA data (see Petr et al., PNAS 2019).","code":"# first get a table of simulated African and European individuals in the tree-sequence inds <- ts_samples(ts) %>% dplyr::filter(pop %in% c(\"AFR\", \"EUR\")) # estimate the amounts of Neanderthal ancestry in these individuals and add # these values to the table inds$ancestry <- ts_f4ratio(ts, X = inds$name, \"NEA_1\", \"NEA_2\", \"AFR_1\", \"CH_1\")$alpha ggplot(inds, aes(pop, ancestry, fill = pop)) + geom_boxplot() + geom_jitter() + labs(y = \"Neanderthal ancestry proportion\", x = \"\") + theme(legend.position = \"none\") + coord_cartesian(ylim = c(0, 0.1)) dplyr::filter(inds, pop == \"EUR\") %>% ggplot(aes(time, ancestry)) + geom_point() + geom_smooth(method = \"lm\", linetype = 2, color = \"red\", linewidth = 0.5) + xlim(40000, 0) + coord_cartesian(ylim = c(0, 0.1)) + labs(x = \"time [years ago]\", y = \"Neanderthal ancestry proportion\") #> `geom_smooth()` using formula = 'y ~ x'"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"admixtools-analyses","dir":"Articles","previous_headings":"","what":"ADMIXTOOLS analyses","title":"Tree-sequence processing and statistics","text":"case like verify f-statistics results using venerable ADMIXTOOLS software (see linked paper formally introduced statistics first place), can convert tree-sequence data file format called EIGENSTRAT using ts_eigenstrat() function. file conversion internally handled R package admixr returns EIGENSTRAT object ties individual EIGENSTRAT file components together (see tutorial admixr extensive overview). admixr R package running automated ADMIXTOOLS analyses entirely R makes types analyses convenient. Running admixr analysis easy plugging object admixr function. instance, can estimate proportion Neanderthal ancestry couple individuals \\(X\\) like (admixr calls proportion alpha): fact, lets compare values obtained tskit admixr/ADMIXTOOLS individuals: correspondence two looks good! 🎉 , note large amount variance around expected value 3% ancestry due extremely small amount sequence data simulated .","code":"snps <- ts_eigenstrat(ts, prefix = file.path(tempdir(), \"eigenstrat\", \"data\")) #> 1282 multiallelic sites (0.206% out of 621331 total) detected and removed library(admixr) f4ratio(data = snps, X = c(\"EUR_1\", \"EUR_2\", \"AFR_2\"), A = \"NEA_1\", B = \"NEA_2\", C = \"AFR_1\", O = \"CH_1\") #> # A tibble: 3 × 8 #> A B X C O alpha stderr Zscore #> #> 1 NEA_1 NEA_2 EUR_1 AFR_1 CH_1 0.0218 0.00506 4.30 #> 2 NEA_1 NEA_2 EUR_2 AFR_1 CH_1 0.0328 0.00788 4.16 #> 3 NEA_1 NEA_2 AFR_2 AFR_1 CH_1 0.00408 0.00208 1.96 europeans <- inds[inds$pop == \"EUR\", ]$name # tskit result result_ts <- ts_f4ratio(ts, X = europeans, A = \"NEA_1\", B = \"NEA_2\", C = \"AFR_1\", O = \"CH_1\") %>% select(alpha_ts = alpha) # result obtained by admixr/ADMIXTOOLS result_admixr <- f4ratio(snps, X = europeans, A = \"NEA_1\", B = \"NEA_2\", C = \"AFR_1\", O = \"CH_1\") %>% select(alpha_admixr = alpha) bind_cols(result_admixr, result_ts) %>% ggplot(aes(alpha_ts, alpha_admixr)) + geom_point() + geom_abline(slope = 1, linetype = 2, color = \"red\", linewidth = 0.5) + labs(x = \"f4-ratio statistic calculated with admixr/ADMIXTOOLS\", y = \"f4-ratio statistic calculated with tskit\")"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"vcf-output","dir":"Articles","previous_headings":"","what":"VCF output","title":"Tree-sequence processing and statistics","text":"case need process simulated data software, can use function ts_vcf() save simulated genotypes VCF format: can also specify subset individuals saved VCF:","code":"ts_vcf(ts, path = file.path(tempdir(), \"output.vcf.gz\")) ts_vcf(ts, path = file.path(tempdir(), \"output_subset.vcf.gz\"), individuals = c(\"CH_1\", \"NEA_1\", \"EUR_1\", \"AFR_1\"))"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"other-statistics","dir":"Articles","previous_headings":"","what":"Other statistics","title":"Tree-sequence processing and statistics","text":"follows brief overview statistics implemented tskit slendr provides easy--use R interface. see, goal functions get result using single function call, making convenient quick interactive exploratory analyses simulated data right R console. continue use simulated Neanderthal introgression tree-sequence data examples.","code":""},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"f_st","dir":"Articles","previous_headings":"Other statistics","what":"\\(F_{st}\\)","title":"Tree-sequence processing and statistics","text":"\\(F_{st}\\) statistic implemented function ts_fst(). single genome-wide \\(F_{st}\\) calculated (.e. window-based calculation), ts_fst() returns simple three-column data frame case non-named list sample sets provided, set names generated automatically: course, much less readable encourage name sample sets appropriately. case two sample sets specified, pairwise statistics computed: many statistics implemented tskit, ts_fst() accepts windows argument, specifying breakpoints windows. case, Fst column resulting data frame called “list-column”, item column vector \\(F_{st}\\) values, one per window. List-columns can little confusing new R users, highly encourage get used allow extremely concise elegant handling structured data within normal data frames (can start introduction). instance, window-based \\(F_st\\) values afr-vs-eur calculation (first row table ):","code":"ts_fst(ts, sample_sets = list(afr = c(\"AFR_1\", \"AFR_2\", \"AFR_3\"), eur = c(\"EUR_1\", \"EUR_2\"))) #> # A tibble: 1 × 3 #> x y Fst #> #> 1 afr eur 0.0495 ts_fst(ts, sample_sets = list(c(\"AFR_1\", \"AFR_2\", \"AFR_3\"), c(\"EUR_1\", \"EUR_2\"))) #> # A tibble: 1 × 3 #> x y Fst #> #> 1 set_1 set_2 0.0495 ts_fst(ts, sample_sets = list(afr = c(\"AFR_1\", \"AFR_2\", \"AFR_3\"), eur = c(\"EUR_1\", \"EUR_2\"), nea = c(\"NEA_1\", \"NEA_2\"))) #> # A tibble: 3 × 3 #> x y Fst #> #> 1 afr eur 0.0495 #> 2 afr nea 0.555 #> 3 eur nea 0.541 # define breakpoints between 20 windows breakpoints <- seq(0, ts$sequence_length, length.out = 21) # calculate window-based Fst statistic win_fst <- ts_fst( ts, windows = breakpoints, sample_sets = list(afr = c(\"AFR_1\", \"AFR_2\", \"AFR_3\"), eur = c(\"EUR_1\", \"EUR_2\"), nea = c(\"NEA_1\", \"NEA_2\")) ) # we get 20 values for each parwise calculation win_fst #> # A tibble: 3 × 3 #> x y Fst #> #> 1 afr eur #> 2 afr nea #> 3 eur nea win_fst[1, ]$Fst #> $`1` #> [1] 0.04241163 0.06803338 0.04129167 0.06031210 0.05516414 0.03059425 #> [7] 0.03320629 0.05755971 0.05271680 0.04346970 0.03659568 0.05432308 #> [13] 0.05913699 0.05905198 0.04119386 0.04389669 0.05086942 0.06181126 #> [19] 0.03929837 0.06373172"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"tajimas-d","dir":"Articles","previous_headings":"Other statistics","what":"Tajima’s \\(D\\)","title":"Tree-sequence processing and statistics","text":"function ts_tajima() nearly interface ts_fst() shown . non-window version calculated, get single genome-wide values sample set (named non-named list character vectors individual names): window-based version, function returns D column list column vectors \\(\\)-th element Tajima’s D value \\(\\)-th window:","code":"ts_tajima(ts, list(afr = c(\"AFR_1\", \"AFR_2\", \"AFR_3\"), eur = c(\"EUR_1\", \"EUR_2\"))) #> # A tibble: 2 × 2 #> set D #> #> 1 afr -0.000701 #> 2 eur -0.0140 ts_tajima(ts, list(afr = c(\"AFR_1\", \"AFR_2\"), eur = c(\"EUR_1\", \"EUR_2\")), windows = breakpoints) #> # A tibble: 2 × 2 #> set D #> #> 1 afr #> 2 eur "},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"diversity","dir":"Articles","previous_headings":"Other statistics","what":"Diversity","title":"Tree-sequence processing and statistics","text":"can calculate diversity within given groups individuals function ts_diversity(). instance, even extremely simplified example, expect highest levels diversity Africans, followed Europeans, Neanderthals “degenerate” single individual outgroup “chimpanzee”. true? Let’s find . First extract individuals populations, creating list character vectors group (functions ts_diversity() expects input): Now can calculate diversity population sort results increasing order diversity: Great! matches expectations. simulated chimp “population” one individual, expect essentially diversity millions years evolution.","code":"# get sampled individuals from all populations sample_sets <- ts_samples(ts) %>% split(., .$pop) %>% lapply(function(pop) pop$name) sample_sets #> $AFR #> [1] \"AFR_1\" \"AFR_2\" \"AFR_3\" \"AFR_4\" \"AFR_5\" #> #> $CH #> [1] \"CH_1\" #> #> $EUR #> [1] \"EUR_1\" \"EUR_2\" \"EUR_3\" \"EUR_4\" \"EUR_5\" \"EUR_6\" \"EUR_7\" \"EUR_8\" #> [9] \"EUR_9\" \"EUR_10\" \"EUR_11\" \"EUR_12\" \"EUR_13\" \"EUR_14\" \"EUR_15\" \"EUR_16\" #> [17] \"EUR_17\" \"EUR_18\" \"EUR_19\" \"EUR_20\" \"EUR_21\" \"EUR_22\" \"EUR_23\" \"EUR_24\" #> [25] \"EUR_25\" \"EUR_27\" \"EUR_26\" \"EUR_28\" \"EUR_29\" \"EUR_30\" \"EUR_31\" \"EUR_32\" #> [33] \"EUR_33\" \"EUR_34\" \"EUR_35\" \"EUR_36\" \"EUR_37\" \"EUR_38\" \"EUR_39\" \"EUR_40\" #> [41] \"EUR_41\" \"EUR_42\" \"EUR_43\" \"EUR_44\" \"EUR_45\" \"EUR_46\" \"EUR_47\" \"EUR_48\" #> [49] \"EUR_49\" \"EUR_50\" #> #> $NEA #> [1] \"NEA_1\" \"NEA_2\" ts_diversity(ts, sample_sets) %>% dplyr::arrange(diversity) #> # A tibble: 4 × 2 #> set diversity #> #> 1 CH 0.0000431 #> 2 NEA 0.0000455 #> 3 EUR 0.000394 #> 4 AFR 0.000400"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"divergence","dir":"Articles","previous_headings":"Other statistics","what":"Divergence","title":"Tree-sequence processing and statistics","text":"can calculate pairwise divergence groups individuals using function ts_divergence(). Given model, expect lowest divergence two modern human groups AFR EUR, Neanderthals two modern humans, three groups (AFR, EUR NEA) equal, much deeper divergence outgroup chimpanzee CH. sorting table based value divergence column, can see results fit expectations.","code":"ts_divergence(ts, sample_sets) %>% arrange(divergence) #> # A tibble: 6 × 3 #> x y divergence #> #> 1 AFR EUR 0.000449 #> 2 EUR NEA 0.000757 #> 3 AFR NEA 0.000780 #> 4 CH NEA 0.00401 #> 5 CH EUR 0.00402 #> 6 AFR CH 0.00402"},{"path":"https://www.slendr.net/articles/vignette-05-tree-sequences.html","id":"more-information","dir":"Articles","previous_headings":"","what":"More information","title":"Tree-sequence processing and statistics","text":"couple examples statistical functions implemented tskit provide native R interface slendr. can find tree-sequence statistics reference manual project website. statistics tskit library implemented, intend expand selection provided slendr near future. functionality like use project missing slendr, please don’t hesitate let us now creating issue GitHub page. Finally, like see examples tskit interface action, take look vignette describes switching SLiM msprime back ends slendr package.","code":""},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Spatially annotated tree sequences","text":"main selling point slendr R package programming complex spatially explicit population genetic models. use SLiM simulation engine, can store simulated data efficiently tree sequence format allows us run large population-scale simulations. previous vignettes, described can specify spatial population dynamics can access tree sequence data calculate population genetic statistics (focusing non-spatial models simplicity). Now ’s time show work simulated tree sequence spatial context.","code":""},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"model-specification","dir":"Articles","previous_headings":"","what":"Model specification","title":"Spatially annotated tree sequences","text":"Let’s first load required R libraries: begin specifying spatial model. use demographic model modern human history West Eurasia, extensively discussed introductory tutorial main landing page. complete model definition script, without comments: sanity check defined demography correctly, can plot graph summarizing population divergences geneflow events calling plot_model(model): completeness, (slightly busy) overview spatial population ranges defined :","code":"library(slendr) library(dplyr) library(ggplot2) init_env() seed <- 314159 set.seed(seed) # simulated world map map <- world( xrange = c(-13, 70), # min-max longitude yrange = c(18, 65), # min-max latitude crs = \"EPSG:3035\" # coordinate reference system (CRS) for West Eurasia ) # couple of broad geographic regions africa <- region( \"Africa\", map, polygon = list(c(-18, 20), c(38, 20), c(30, 33), c(20, 33), c(10, 38), c(-6, 35)) ) europe <- region( \"Europe\", map, polygon = list( c(-8, 35), c(-5, 36), c(10, 38), c(20, 35), c(25, 35), c(33, 45), c(20, 58), c(-5, 60), c(-15, 50) ) ) anatolia <- region( \"Anatolia\", map, polygon = list(c(28, 35), c(40, 35), c(42, 40), c(30, 43), c(27, 40), c(25, 38)) ) # define population histories # African ancestral population afr <- population( \"AFR\", time = 52000, N = 3000, map = map, polygon = africa ) # population of the first migrants out of Africa ooa <- population( \"OOA\", parent = afr, time = 51000, N = 500, remove = 25000, center = c(33, 30), radius = 400e3 ) %>% move( trajectory = list(c(40, 30), c(50, 30), c(60, 40)), start = 50000, end = 40000, snapshots = 20 ) # Eastern hunter-gatherers ehg <- population( \"EHG\", parent = ooa, time = 28000, N = 1000, remove = 6000, polygon = list( c(26, 55), c(38, 53), c(48, 53), c(60, 53), c(60, 60), c(48, 63), c(38, 63), c(26, 60)) ) # European population eur <- population(name = \"EUR\", parent = ehg, time = 25000, N = 2000, polygon = europe) # Anatolian farmers ana <- population( name = \"ANA\", time = 28000, N = 3000, parent = ooa, remove = 4000, center = c(34, 38), radius = 500e3, polygon = anatolia ) %>% expand_range( by = 2500e3, start = 10000, end = 7000, polygon = join(europe, anatolia), snapshots = 20 ) # expand the range by 2.500 km # Yamnaya steppe population yam <- population( name = \"YAM\", time = 7000, N = 500, parent = ehg, remove = 2500, polygon = list(c(26, 50), c(38, 49), c(48, 50), c(48, 56), c(38, 59), c(26, 56)) ) %>% move(trajectory = list(c(15, 50)), start = 5000, end = 3000, snapshots = 10) # geneflow events gf <- list( gene_flow(from = ana, to = yam, rate = 0.5, start = 6000, end = 5000, overlap = FALSE), gene_flow(from = ana, to = eur, rate = 0.5, start = 8000, end = 6000), gene_flow(from = yam, to = eur, rate = 0.75, start = 4000, end = 3000) ) # compile the spatial model model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, resolution = 10e3, competition = 150e3, mating = 120e3, dispersal = 90e3 ) plot_model(model) plot_map(afr, ooa, ehg, eur, ana, yam)"},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"scheduling-sampling-events-and-simulation","dir":"Articles","previous_headings":"","what":"Scheduling sampling events and simulation","title":"Spatially annotated tree sequences","text":"Now schedule sampling single individual population every two thousand years, starting 40 thousand years ago way present (feature discussed basic tree sequence overview): Finally, can simulate data model process output tree sequence (recapitate simplify ):","code":"# one ancient individual every two thousand years ancient <- schedule_sampling(model, times = seq(40000, 1, by = -500), list(ooa, 1), list(ehg, 1), list(eur, 1), list(ana, 1), list(yam, 1)) # present-day Africans and Europeans present <- schedule_sampling(model, times = 0, list(afr, 5), list(eur, 30)) samples <- rbind(ancient, present) ts <- slim( model, sequence_length = 100e3, recombination_rate = 1e-8, burnin = 200e3, samples = samples, method = \"batch\", random_seed = 314159, max_attempts = 1 ) %>% ts_recapitate(recombination_rate = 1e-8, Ne = 10000, random_seed = seed) %>% ts_simplify() ts #> ╔═════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═════════╣ #> ║Trees │ 101║ #> ╟───────────────┼─────────╢ #> ║Sequence Length│ 100000║ #> ╟───────────────┼─────────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼─────────╢ #> ║Sample Nodes │ 432║ #> ╟───────────────┼─────────╢ #> ║Total Size │217.9 KiB║ #> ╚═══════════════╧═════════╝ #> ╔═══════════╤════╤════════╤════════════╗ #> ║Table │Rows│Size │Has Metadata║ #> ╠═══════════╪════╪════════╪════════════╣ #> ║Edges │1241│38.8 KiB│ No║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Individuals│ 730│73.1 KiB│ Yes║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Mutations │ 0│ 1.2 KiB│ No║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Nodes │ 948│35.8 KiB│ Yes║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Populations│ 7│ 2.7 KiB│ Yes║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Provenances│ 3│45.0 KiB│ No║ #> ╟───────────┼────┼────────┼────────────╢ #> ║Sites │ 0│16 Bytes│ No║ #> ╚═══════════╧════╧════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"extracting-spatial-tree-sequence-information","dir":"Articles","previous_headings":"","what":"Extracting spatial tree sequence information","title":"Spatially annotated tree sequences","text":"showed basic tutorial, important function data exploration ts_nodes(). function extracts information individuals nodes recorded tree sequence object loaded annotated slendr : completeness, also functions ts_individuals(), ts_nodes() ts_edges() extract tree sequence tables “raw” unprocessed form, ts_nodes() much convenient data exploration analyses. First, combined information low-level tables individuals nodes single table importantly, model generated data spatial model, ts_nodes() automatically annotates node/individual tables position node space (real projected coordinates) time. means can spatial data analysis directly table returned ts_nodes(). Even better, although can see returned object belongs slendr’s class slendr_ts_nodes, internally stored spatial sf object. means can use functionality powerful R package sf well many packages geospatial analyses directly data: Typing object R console presents user-friendly summary spatio-temporal data extracted tree sequence: first part summary, see many individuals (sampled retained) nodes present tree sequence together additional useful information, including section internally stored sf object. crucial point—**can always use internal sf object spatial data directly*. data returned ts_nodes() internally transformed projected CRS used model, can use returned object data class sf. instance, beginning vignette, specified world map model represented projected CRS (EPSG 3035) can verify typing: fact ts_nodes() result just another sf object makes easy visualize overlay contents map, see .","code":"data <- ts_nodes(ts) class(data) #> [1] \"slendr\" \"slendr_nodes\" \"sf\" \"tbl_df\" \"tbl\" #> [6] \"data.frame\" data #> Simple feature collection with 948 features and 12 fields (with 2 geometries empty) #> Geometry type: POINT #> Dimension: XY #> Bounding box: xmin: 2721964 ymin: 428651.3 xmax: 8613304 ymax: 4959957 #> Projected CRS: ETRS89-extended / LAEA Europe #> # A tibble: 948 × 13 #> name pop node_id time time_tskit location sampled #> #> 1 AFR_1 AFR 362 0 0 (3665253 847038.2) TRUE #> 2 AFR_1 AFR 363 0 0 (3665253 847038.2) TRUE #> 3 AFR_2 AFR 364 0 0 (4877281 428651.3) TRUE #> 4 AFR_2 AFR 365 0 0 (4877281 428651.3) TRUE #> 5 AFR_3 AFR 366 0 0 (3491753 724345.8) TRUE #> 6 AFR_3 AFR 367 0 0 (3491753 724345.8) TRUE #> 7 AFR_4 AFR 368 0 0 (3754934 1216666) TRUE #> 8 AFR_4 AFR 369 0 0 (3754934 1216666) TRUE #> 9 AFR_5 AFR 370 0 0 (3806357 1063276) TRUE #> 10 AFR_5 AFR 371 0 0 (3806357 1063276) TRUE #> # ℹ 938 more rows #> # ℹ 6 more variables: remembered , retained , alive , #> # pedigree_id , ind_id , pop_id map #> slendr 'map' object #> ------------------- #> map: internal coordinate reference system EPSG 3035 #> spatial limits (in degrees longitude and latitude): #> - vertical -13 ... 70 #> - horizontal 18 ... 65"},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"using-the-simple-features-interface","dir":"Articles","previous_headings":"","what":"Using the simple features interface","title":"Spatially annotated tree sequences","text":"’s hard overstate powerful R ecosystem around sf package . However, getting familiar package geospatial analysis general can bit hurdle, especially novice users takes time get familiar many new concepts. Although many slendr features encoding programming spatial models handling simulated tree sequence data discussed far designed abstract away complexities underlying low-level details let focus problem hand, spatial data analysis unfortunately whole another matter. Luckily, data generated slendr different source spatial data great free resources disposal. bottom line : spatio-temporal data extracted tree sequences slendr different normal sf object. resource find manipulating, plotting, analysing sf data can applied slendr results well. remainder vignette look couple examples.","code":""},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"plotting-locations-of-simulated-sampled-individuals","dir":"Articles","previous_headings":"","what":"Plotting locations of simulated sampled individuals","title":"Spatially annotated tree sequences","text":"Every spatial object slendr internally class sf. flexibility ggplot2 sf packages means can overlay locations sampled individuals (saved sf format ts_nodes()) top world map (also sf object): sf simple features objects (, extension, even slendr_spatial objects) internally stored normal data frames couple bells whistles top , powerful tools manipulating tabular data disposal. example, let’s say wanted split sampled individuals tree sequence epochs plot individually using standard ggplot2 features. simply first , adding new column specifying epoch simulated individual belong: chunk code simply adds new column epoch sf spatial data frame object called epochs . can use ggplot2 function geom_sf plot locations sampled individuals map, facet corresponding one epoch (warning can safely ignored): hope little excursion handling slendr spatial objects (, extension, sf objects) standard data frame manipulation functions ggplot2 visualisation convinced great flexibility analysing spatial slendr data. best introduction -called “tidy” data analysis, encourage read freely-available book R Data Science.","code":"sampled_data <- ts_nodes(ts) %>% filter(sampled) ggplot() + geom_sf(data = map, fill = \"lightgray\", color = NA) + geom_sf(data = sampled_data, aes(shape = pop, color = time)) + ggtitle(\"Locations of simulated sampled individuals\") + scale_color_continuous(type = \"viridis\") + theme_bw() epochs <- sampled_data %>% mutate(epoch = cut(time, breaks = c(40000, 30000, 10000, 4000, 0)), epoch = ifelse(is.na(epoch), 0, epoch), epoch = factor(epoch, labels = c(\"present\", \"(present, 4 ky]\", \"(4 ky, 10 ky]\", \"(10 ky, 30 y]\", \"(30 ky, 40 ky]\"))) ggplot() + geom_sf(data = map, fill = \"lightgray\", color = NA) + geom_sf(data = epochs, aes(shape = pop, color = pop)) + facet_wrap(~ epoch) + ggtitle(\"Locations of simulated sampled individuals in different epochs\") + theme_bw()"},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"extracting-spatio-temporal-ancestral-relationships","dir":"Articles","previous_headings":"","what":"Extracting spatio-temporal ancestral relationships","title":"Spatially annotated tree sequences","text":"Perhaps even useful plotting locations simulated individuals accessing locations (times) ancestors particular tree sequence node (“focal node”). Starting focal node individual, can trace geographical location nodes lineage going back way root function ts_ancestors(). record time location every individual happens ancestor least one sampled individual, means know true location every node tree sequence. simplest use case determining locations times every single node genealogical history individual along tree sequence (possible recover ancestral relationships multiple samples ): function starts given node (, name sampled diploid individual provided, two nodes), extracts information parent nodes node entire tree sequence, records locations times, proceeds one level “higher” genealogical history gather information parents parent nodes, etc., reaches root node. result process another sf object row table encodes information single branch genealogy “focal” node individual (example, \"EUR_25\"): row table, two columns location parent_location carry spatial location node (node_id) parent node (parent_id), respectively, columns time parent_time (times nodes) pop parent_pop (populations nodes belong). column connection contains sf geometry object line connecting two nodes coordinate reference system “model world”. column focal_id tells us focal node’s genealogy rows table belong , level column shows deep genealogical past branch (.e. row table) belong . table contains complete information spatio-temporal relationships nodes genealogy given focal sample. spirit demonstrating slendr tree sequence tables interact sf ggplot2 environments, let’s look immediate parent nodes two nodes sampled individual (.e. nodes level 1) using filter function R package dplyr: mentioned , three columns encoding spatial information: location parent_location carry information location child parent node (POINT class), connection object (LINESTRING class) contains line connecting two nodes (branch tree sequence also spatial connection). can plot three spatial features (two points line) individually map: figure can see red focal node immediate parents tree sequence genealogy (coalescent sense, immediate parents individual!). case ’re surprised see two parents, recall recombination events make history encoded sample complicated can involve ancestors “move ” tree sample, just two ancestors. Looking example detail, can see one node (chromosome) individual “EUR_67” two ancestors, covering portion individuals chromosome, chromosome covered single ancestor (columns left_pos right_pos): convenient way analysis companion function ts_ancestors() called plot_ancestors(). function accepts sf object spatial branching data created ts_ancestors() plots paths nodes map leading focal node root(s) tree sequence (instead just paths immediate parents shown previous figure). case, working single diploid individual, get two sets paths nodes (chromosomes) plot two facets: can compare result animation recapitulates simulation, presented first vignette. comparing spatial tree sequence figure animation, can immediately notice several things: spatial tree sequence paths trace ancestry single European individual back Africa. fact, also see cluster past ancestral nodes (.e. concentrated coalescent events) place Africa (OOA) migrant population settled around 40,000 thousand years ago (yellow population animation). One chromosome traces ancestry EHG population indicated green square expected programmed Yamnaya migration (descending EHG) east central Europe contribute significant part ancestry present-day Europeans (compare demographic graph top vignette). also see chromosome traces ancestry Anatolia (blue crosses). makes sense, simulated European ancestry part Anatolian. Let’s look spatial ancestry another sample. instance, know simulated history Anatolian population model much simpler. According demographic graph , Anatolians split ancestral population Eurasians Anatolia expanded wave Europe. sampled following individuals: Can see hint spatial dynamics Anatolians spatio-temporal distribution ancestral node locations one sampled individuals? Let’s pick last individual immediately plot spatial ancestry tidyverse-style using pipe operator %>%: might expect given late age sample, position map (red crossed circle) Anatolia Europe represents one descendants migrants moved Anatolia Europe. can clearly seen position parental nodes tree sequence: nodes represent real individuals lived point past, can see , indeed, lived Anatolia.","code":"ind <- \"EUR_67\" lineages <- ts_ancestors(ts, ind, verbose = TRUE) #> Collecting ancestors of EUR_67 [1/1]... #> #> Generating data about spatial relationships of nodes... lineages #> Simple feature collection with 134 features and 12 fields #> Active geometry column: connection #> Geometry type: LINESTRING #> Dimension: XY #> Bounding box: xmin: 2721964 ymin: 546923.8 xmax: 8506059 ymax: 4652439 #> Projected CRS: ETRS89-extended / LAEA Europe #> # A tibble: 134 × 15 #> name pop node_id level child_id parent_id child_time parent_time child_pop #> * #> 1 EUR_… EUR 406 1 406 446 0 1300 EUR #> 2 EUR_… EUR 406 2 446 469 1300 3550 EUR #> 3 EUR_… EUR 406 3 469 488 3550 5890 EUR #> 4 EUR_… EUR 406 4 488 494 5890 6970 EUR #> 5 EUR_… EUR 406 4 488 570 5890 13690 EUR #> 6 EUR_… EUR 406 5 494 570 6970 13690 EUR #> 7 EUR_… EUR 406 5 570 675 13690 21370 EUR #> 8 EUR_… EUR 406 6 675 750 21370 25960 EUR #> 9 EUR_… EUR 406 7 750 756 25960 26230 EHG #> 10 EUR_… EUR 406 8 756 792 26230 28000 EHG #> # ℹ 124 more rows #> # ℹ 6 more variables: parent_pop , child_location , #> # parent_location , connection , left_pos , #> # right_pos filter(lineages, level == 1) #> Simple feature collection with 2 features and 12 fields #> Active geometry column: connection #> Geometry type: LINESTRING #> Dimension: XY #> Bounding box: xmin: 3898723 ymin: 2562183 xmax: 4274254 ymax: 2895750 #> Projected CRS: ETRS89-extended / LAEA Europe #> # A tibble: 2 × 15 #> name pop node_id level child_id parent_id child_time parent_time child_pop #> * #> 1 EUR_67 EUR 406 1 406 446 0 1300 EUR #> 2 EUR_67 EUR 407 1 407 503 0 7810 EUR #> # ℹ 6 more variables: parent_pop , child_location , #> # parent_location , connection , left_pos , #> # right_pos level1_branches <- ts_ancestors(ts, \"EUR_67\") %>% filter(level == 1) ggplot() + geom_sf(data = map, fill = \"lightgray\", color = NA) + geom_sf(data = level1_branches[, ]$child_location, shape = 13, size = 3, color = \"red\") + geom_sf(data = level1_branches[, ]$connection, linetype = 3) + geom_sf(data = level1_branches[, ]$parent_location, shape = 20, color = \"blue\") + theme_bw() + ggtitle(\"Parent nodes (blue) of a focal individual (red)\") as_tibble(level1_branches)[, c(\"name\", \"node_id\", \"child_id\", \"parent_id\", \"left_pos\", \"right_pos\")] #> # A tibble: 2 × 6 #> name node_id child_id parent_id left_pos right_pos #> #> 1 EUR_67 406 406 446 0 100000 #> 2 EUR_67 407 407 503 0 100000 ggplot() + geom_sf(data = map) + geom_sf(data = lineages, size = 0.5, alpha = 0.2) + geom_sf(data = sf::st_set_geometry(lineages, \"parent_location\"), aes(shape = parent_pop, color = parent_pop)) + geom_sf(data = filter(ts_nodes(ts), name == ind), size = 3) + guides(alpha = \"none\") + coord_sf(expand = 0) + labs(x = \"longitude\", y = \"latitude\") + facet_grid(. ~ node_id) + ggtitle(\"Ancestry encoded by two nodes (chromosomes) of EUR_67\") ts_samples(ts) %>% filter(pop == \"ANA\") #> # A tibble: 48 × 3 #> name time pop #> #> 1 ANA_1 27500 ANA #> 2 ANA_2 27000 ANA #> 3 ANA_3 26500 ANA #> 4 ANA_4 26000 ANA #> 5 ANA_5 25500 ANA #> 6 ANA_6 25000 ANA #> 7 ANA_7 24500 ANA #> 8 ANA_8 24000 ANA #> 9 ANA_9 23500 ANA #> 10 ANA_10 23000 ANA #> # ℹ 38 more rows lineages <- ts_ancestors(ts, \"ANA_45\") ggplot() + geom_sf(data = map) + geom_sf(data = lineages, size = 0.5, alpha = 0.2) + geom_sf(data = sf::st_set_geometry(lineages, \"parent_location\"), aes(shape = parent_pop, color = parent_pop)) + geom_sf(data = filter(ts_nodes(ts), name == \"ANA_45\"), size = 3) + guides(alpha = \"none\") + coord_sf(expand = 0) + labs(x = \"longitude\", y = \"latitude\") + facet_grid(. ~ node_id) + ggtitle(\"Ancestry encoded by two nodes (chromosomes) of ANA_45\")"},{"path":"https://www.slendr.net/articles/vignette-06-locations.html","id":"calculating-distances-and-other-statistics-using-the-sf-package","dir":"Articles","previous_headings":"","what":"Calculating distances and other statistics using the sf package","title":"Spatially annotated tree sequences","text":"can summarise spatial ancestral dynamics figures using statistics? Lets take one look sf object locations times ancestral nodes sampled individuals, focusing following subset columns: can use standard dplyr table manipulation functions compute distances connected notes times separate (.e. branch lengths traditional phylogenetic sense). can use two quantities compute fast () movement ancestral individuals different time periods history sample: Let’s also convert data (absolute distances distance per generation – .e., “speed”) long format easier plotting side side: Let’s try summarise information distances “traveled” nodes different time period fitting spline (rather plotting raw data individual nodes):","code":"lineages <- ts_samples(ts) %>% pull(name) %>% ts_ancestors(ts, x = .) select(lineages, connection, child_time, parent_time) #> Simple feature collection with 23928 features and 2 fields #> Geometry type: LINESTRING #> Dimension: XY #> Bounding box: xmin: 2721964 ymin: 428651.3 xmax: 8613304 ymax: 4959957 #> Projected CRS: ETRS89-extended / LAEA Europe #> # A tibble: 23,928 × 3 #> connection child_time parent_time #> #> 1 (8103641 3334456, 7956278 3180199) 40000 40330 #> 2 (7956278 3180199, 8036310 1911729) 40330 43900 #> 3 (7956278 3180199, 8031610 2059804) 40330 44020 #> 4 (8036310 1911729, 8031610 2059804) 43900 44020 #> 5 (8031610 2059804, 7687313 1689532) 44020 45970 #> 6 (7687313 1689532, 7175192 1476174) 45970 47740 #> 7 (7687313 1689532, 6867500 1338048) 45970 48730 #> 8 (7175192 1476174, 6867500 1338048) 47740 48730 #> 9 (6867500 1338048, 6754277 1091742) 48730 49810 #> 10 (6754277 1091742, 3290558 497125.8) 49810 60400 #> # ℹ 23,918 more rows distances <- lineages %>% mutate(branch_length = abs(parent_time - child_time) / model$generation_time, distance = sf::st_length(connection) %>% units::set_units(km) %>% as.numeric(), speed = distance / branch_length, epoch = cut(parent_time, breaks = c(Inf, seq(60000, 0, by = -3000)), dig.lab = 10, include.lowest = TRUE)) %>% as_tibble() %>% # strip away the spatial annotation select(name, pop, node_id, branch_length, distance, speed, parent_pop, parent_time, child_pop, child_time, epoch) distances_long <- distances %>% filter(child_time < 60000) %>% filter(!pop %in% c(\"AFR\", \"OOA\")) %>% tidyr::pivot_longer(cols = c(distance, speed), names_to = \"stat\", values_to = \"value\") %>% mutate(facet = case_when( stat == \"distance\" ~ \"absolute distance of a node from parent\", stat == \"speed\" ~ \"distance traveled by a node per generation\")) distances_long %>% ggplot(aes(child_time, value, color = child_pop)) + geom_smooth(method = \"loess\", aes(group = child_pop)) + geom_hline(yintercept = 0, linetype = 2, linewidth = 0.5) + labs(y = \"kilometers\", x = \"time [years ago]\") + theme(axis.text.x = element_text(hjust = 1, angle = 45), legend.position = \"bottom\") + facet_wrap(~ facet, scales = \"free_y\") + guides(color = guide_legend(\"ancestral node population\")) #> `geom_smooth()` using formula = 'y ~ x'"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"defining-a-model","dir":"Articles","previous_headings":"Detecting gene flow from msprime and SLiM tree sequences","what":"Defining a model","title":"Simulating data with SLiM and msprime backends","text":"now define simple non-spatial model gene flow populations (also known population admixture introgression). involve essentially procedure shown another vignette introducing non-spatial slendr models. Note different normally specify spatial model, except left map argument making population() calls. demonstrate additional features slendr interface tskit Python library, perform gene flow detection test using -called \\(f_4\\) \\(f_4\\)-ratio statistics (briefly introduces vignette). make things little interesting, define two population models: one model without gene flow, another includes gene flow. defining model expressing \\(f\\)-statistics use nomenclature used first study Patterson et al. described \\(f_4-ratio\\) statistic first place. ’re familiar statistical tests gene flow, recommend take look relevant sections linked paper. shiny_graph Let’s start first defining populations splits established figure : Note Ne populations x1 x2 set much higher rest. set \\(N_e\\) populations lower values speed forward SLiM simulations (won’t affect results ’re interested anyway). Higher values \\(N_e\\) x1 x2 populations ensure effect drift acting two populations much smaller. simulate later measure proportion ancestry population b x1, ensure ancestry proportion drift far away expectation make interesting patterns stand clearly. (course, done demonstration purposes speed SLiM simulations making \\(N_e\\) populations smaller. practice, running kinds simulations using msprime back end.)","code":"seq_len <- 100e6 # amount of sequence to simulate rec_rate <- 1e-8 # uniform recombination rate mut_rate <- 1e-8 # mutation rate o <- population(\"outgroup\", time = 1, N = 100) c <- population(\"c\", time = 2500, N = 100, parent = o) a <- population(\"a\", time = 3000, N = 100, parent = c) b <- population(\"b\", time = 3500, N = 100, parent = a) x1 <- population(\"x1\", time = 3800, N = 5000, parent = c) x2 <- population(\"x2\", time = 4000, N = 5000, parent = x1)"},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"compiling-the-model-and-simulating-data","dir":"Articles","previous_headings":"","what":"Compiling the model and simulating data","title":"Simulating data with SLiM and msprime backends","text":"now use populations compile two models: first model without gene flow (left panel figure ), second model include 10% gene flow b x1 (right panel figure ). also schedule sampling couple individuals end simulation: 50 individuals populations x1 x2 capture bit natural variation b ancestry x1, one individual rest. running simulations, let’s first make sure models set correctly: Now run models (without gene flow) two slendr backends, SLiM msprime: Note using exactly model configuration object simulation runs! fact, even function interface looks nearly exactly . doesn’t matter specific details demographic models , slendr interpret correctly regardless back end simulation engine choose use.","code":"# no gene flow model model_nogf <- compile_model(populations = list(a, b, x1, x2, c, o), generation_time = 1, simulation_length = 4500) samples <- schedule_sampling( model_nogf, times = 4500, list(a, 1), list(b, 1), list(x1, 50), list(x2, 50), list(c, 1), list(o, 1) ) # model with gene flow gf <- gene_flow(from = b, to = x1, start = 4100, end = 4400, rate = 0.1) model_gf <- compile_model(populations = list(a, b, x1, x2, c, o), gene_flow = gf, generation_time = 1, simulation_length = 4500) samples <- schedule_sampling( model_gf, times = 4500, list(a, 1), list(b, 1), list(x1, 50), list(x2, 50), list(c, 1), list(o, 1) ) plot_model(model_nogf, sizes = FALSE) plot_model(model_gf, sizes = FALSE, proportions = TRUE) # model without gene flow slim_nogf <- slim(model_nogf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) msprime_nogf <- msprime(model_nogf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) # model with b -> x1 gene flow slim_gf <- slim(model_gf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) msprime_gf <- msprime(model_gf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed)"},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"comparing-the-outputs-of-msprime-and-slim-runs-of-a-slendr-model","dir":"Articles","previous_headings":"Compiling the model and simulating data","what":"Comparing the outputs of msprime and SLiM runs of a slendr model","title":"Simulating data with SLiM and msprime backends","text":"run introgression models two simulation back ends, let’s load SLiM msprime tree sequence outputs compare contents (hoping !). can see , tree sequence summary data loaded processed slendr’s ts_read() function (data produced SLiM backend) similar got “manually” msprime-produced tree sequence using custom defined functions. somehow obvious nice cheap sanity check indicating tree sequence data structure produced two backends demographic model almost .","code":"# SLiM outputs -- we can use built-in slendr functions for those slim_nogf <- slim_nogf %>% ts_recapitate(Ne = 10, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) slim_gf <- slim_gf %>% ts_recapitate(Ne = 10, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) # msprime outputs (note that recapitation and simplification doesn't make # sense here because we already have fully coalesced genealogies for our # individuals of interest msprime_nogf <- ts_mutate(msprime_nogf, mut_rate, random_seed = seed) msprime_gf <- ts_mutate(msprime_gf, mut_rate, random_seed = seed) slim_nogf #> ╔═════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═════════╣ #> ║Trees │ 248158║ #> ╟───────────────┼─────────╢ #> ║Sequence Length│100000000║ #> ╟───────────────┼─────────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼─────────╢ #> ║Sample Nodes │ 21008║ #> ╟───────────────┼─────────╢ #> ║Total Size │ 70.6 MiB║ #> ╚═══════════════╧═════════╝ #> ╔═══════════╤══════╤════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪════════╪════════════╣ #> ║Edges │972815│29.7 MiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Individuals│131789│12.6 MiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Mutations │257348│ 9.1 MiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Nodes │157230│ 5.7 MiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Populations│ 7│ 2.8 KiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Provenances│ 3│45.1 KiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Sites │257040│ 6.1 MiB│ No║ #> ╚═══════════╧══════╧════════╧════════════╝ msprime_nogf #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 62745║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 208║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 14.4 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │247454│ 7.6 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 104│ 2.9 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 69172│ 2.4 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │ 32685│893.7 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 6│414 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 2│ 3.9 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 69144│ 1.6 MiB│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"comparing-results-of-population-genetics-statistics","dir":"Articles","previous_headings":"Compiling the model and simulating data","what":"Comparing results of population genetics statistics","title":"Simulating data with SLiM and msprime backends","text":"loaded msprime SLiM tree sequences generated model, let’s see can get comparable results calculate statistics interest. Let’s first extract individuals populations x1 x2 estimate values \\(f_4(c, x1 \\textrm{ } x2; b, o)\\) (significantly negative individuals population x1 due ’s ancestry coming b introgression models consistent zero x2) \\(f_4\\textrm{-ratio}(, b, \\textrm{ } x2; c, o)\\), estimates proportion b-like ancestry x1 x2: Now, let’s repeat analysis tree sequences produced msprime backend two slendr demographic models: Finally can proceed plotting SLiM msprime backend results two models compare together. noted , despite fact SLiM forward simulator msprime coalescent simulator, population genetic expectations gene flow gene flow models match closely (barring level uncertainty expected due randomness population genetic processes play). Things seem looking good behave expected! couple observations: model without gene flow, \\(f_4\\) statistic value zero x1 x2 populations. expected, \\(f_4\\) ~0 consistent relationship lineages conforming standard phylogenetic tree (admixture edges). Essentially, \\(f_4\\) hypothesis test “tree-ness” data. model includes gene flow b x1, \\(f_4\\) value significantly negative. means simple tree hypothesis can rejected gene flow must occurred b x1. Note test reveal direction gene flow, ’s presence. Consistently \\(f_4\\) results, \\(f_4-ratio\\) estimates match expectations. Specifically, statistic estimates around 10% ancestry b x1 gene flow model 0% ancestry cases. importantly, results obtained SLiM msprime back ends nearly exactly . means (non-spatial models) can use SLiM msprime backend interchangeably.","code":"# extract vector of names of the \"test individuals\" in populations `x1` and `x2` X <- ts_samples(slim_gf) %>% filter(pop %in% c(\"x1\", \"x2\")) %>% pull(name) X #> [1] \"x1_1\" \"x1_2\" \"x1_3\" \"x1_4\" \"x1_5\" \"x1_6\" \"x1_7\" \"x1_8\" \"x1_9\" #> [10] \"x1_10\" \"x1_11\" \"x1_12\" \"x1_13\" \"x1_14\" \"x1_15\" \"x1_16\" \"x1_17\" \"x1_18\" #> [19] \"x1_19\" \"x1_20\" \"x1_21\" \"x1_22\" \"x1_23\" \"x1_24\" \"x1_25\" \"x1_26\" \"x1_27\" #> [28] \"x1_28\" \"x1_29\" \"x1_30\" \"x1_31\" \"x1_32\" \"x1_33\" \"x1_34\" \"x1_35\" \"x1_36\" #> [37] \"x1_37\" \"x1_38\" \"x1_39\" \"x1_40\" \"x1_41\" \"x1_42\" \"x1_43\" \"x1_44\" \"x1_45\" #> [46] \"x1_46\" \"x1_47\" \"x1_48\" \"x1_49\" \"x1_50\" \"x2_1\" \"x2_2\" \"x2_3\" \"x2_4\" #> [55] \"x2_5\" \"x2_6\" \"x2_7\" \"x2_8\" \"x2_9\" \"x2_10\" \"x2_11\" \"x2_12\" \"x2_13\" #> [64] \"x2_14\" \"x2_15\" \"x2_16\" \"x2_17\" \"x2_18\" \"x2_19\" \"x2_20\" \"x2_21\" \"x2_22\" #> [73] \"x2_23\" \"x2_24\" \"x2_25\" \"x2_26\" \"x2_27\" \"x2_28\" \"x2_29\" \"x2_30\" \"x2_31\" #> [82] \"x2_32\" \"x2_33\" \"x2_34\" \"x2_35\" \"x2_36\" \"x2_37\" \"x2_38\" \"x2_39\" \"x2_40\" #> [91] \"x2_41\" \"x2_42\" \"x2_43\" \"x2_44\" \"x2_45\" \"x2_46\" \"x2_47\" \"x2_48\" \"x2_49\" #> [100] \"x2_50\" # calculate f4-statistics on individuals of `x1` and `x2` populations using data # from the two models (a model with no gene flow and a gene flow model) -- we use # map_dfr to iterate across all individuals from `X_individuals` and binding all # resulting data frames into a single data frame df_slim_f4 <- rbind( map_dfr(X, ~ ts_f4(slim_nogf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"no gene flow\"), map_dfr(X, ~ ts_f4(slim_gf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"gene flow\") ) %>% select(X, f4, model) %>% mutate(simulator = \"SLiM backend\") # compute the proportions of `b` ancestry in `x1` (expected 10%) and `x2` # (expected 0% because this population did not receive any gene flow from `b`) df_slim_f4ratio <- rbind( ts_f4ratio(slim_nogf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"no gene flow\"), ts_f4ratio(slim_gf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"gene flow\") ) %>% select(X, alpha, model) %>% mutate(simulator = \"SLiM backend\") df_msprime_f4 <- rbind( map_dfr(X, ~ ts_f4(msprime_nogf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"no gene flow\"), map_dfr(X, ~ ts_f4(msprime_gf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"gene flow\") ) %>% select(X, f4, model) %>% mutate(simulator = \"msprime backend\") # compute the proportions of `b` ancestry in `x1` (expected 10%) and `x2` # (expected 0% because this population did not receive any gene flow from `b`) df_msprime_f4ratio <- rbind( ts_f4ratio(msprime_nogf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"no gene flow\"), ts_f4ratio(msprime_gf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"gene flow\") ) %>% select(X, alpha, model) %>% mutate(simulator = \"msprime backend\") df_f4 <- rbind(df_slim_f4, df_msprime_f4) %>% mutate(population = ifelse(grepl(\"x1_\", X), \"x1 (received gene flow)\", \"x2 (no gene flow)\")) ggplot(df_f4, aes(f4, fill = population)) + geom_histogram(bins = 50) + facet_grid(simulator ~ model) + geom_vline(xintercept = 0, linetype = 2) + labs(y = \"number of individuals\", x = \"f4 statistic\", title = \"f4(c, x1 or x2; b, outgroup)\", subtitle = \"f4 ~0 is consistent with no gene flow, negative value indicates gene flow with 'b'\") + theme(legend.position = \"bottom\") df_f4ratio <- rbind(df_slim_f4ratio, df_msprime_f4ratio) %>% mutate(population = ifelse(grepl(\"x1_\", X), \"x1 (received gene flow)\", \"x2 (no gene flow)\")) ggplot(df_f4ratio, aes(alpha, fill = population)) + geom_histogram(bins = 30) + facet_grid(simulator ~ model) + geom_vline(xintercept = 0.1, linetype = 2) + labs(y = \"number of individuals\", x = \"ancestry proportion (f4-ratio statistic)\", title = \"f4-ratio estimate of 'b' ancestry calculated from simulated data\", subtitle = \"f4-ratio = f4(a, outgroup; x1 or x2, c) / f4(a, outgroup; b, c)\") + theme(legend.position = \"bottom\")"},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"computing-allele-frequency-spectra","dir":"Articles","previous_headings":"","what":"Computing allele frequency spectra","title":"Simulating data with SLiM and msprime backends","text":"Let’s look one example. Imagine following five demographic models describing changes population size abstract population: models population size change events , depending specifics project, defined using different time units. work radiocarbon-dated samples ancient DNA might used thinking population history units “years present”—case, “present” time (bottom figure) time 0 earlier events specified “5000 years present”, etc. hand, interested modelling theoretical population, might want concern quite happy starting simulation “generation 1” continue specified “generation X”. slendr package makes extremely easy, can specify times whatever direction units want, long kept consistent populations events model. rather unique feature among popgen simulation software programs either make express model “generations forward direction”, explicitly convert time generations going backwards. previous section vignette shown SLiM msprime can used simulation backend. remainder vignette present additional analysis, demonstrating fact can express time models forward backward direction still run models slendr’s SLiM (forward simulator) msprime (backward simulator). Specifically, define five demographic models figure twice, different orientations time – forward backward. compute allele frequency spectrum simulation (five models, forward backward) verify forward backward pairs give result regardless whether use SLiM back end msprime backend.","code":""},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"model-definition","dir":"Articles","previous_headings":"Computing allele frequency spectra","what":"Model definition","title":"Simulating data with SLiM and msprime backends","text":"First, let’s define five population histories forward time direction: Now let’s flip flow time around define population histories units “time ago”: convenience, write helper function , given population history object (ten objects just ), compile slendr model, run SLiM msprime back ends, load tree sequence, compute allele frequency spectrum (AFS) . save us lots code repetition (always good thing), minimize chance copy-pasting bugs (definitely good thing!) compare AFS patterns across different models two slendr simulation back ends (according population genetics theory). Now function doesn’t anything special. simply wraps standard slendr simulation pipeline (1. create population(s); 2. compile model object; 3. simulate model; 4. load results; 5. analyse data) single function. Now whole pipeline, can generate simulated data bind result single data frame: Finally, can plot allele frequency spectra models, two time directions, two simulation back ends: , couple things note: two models involve population contraction (single step exponential collapse), see decrease low frequency variants. hand, models population expansion show high proportion low frequency variants. model constant population size shows intermediate pattern. nice validation simulation works expected exactly predicted population genetics theory. forward backward time models give exactly result. unexpected anything else bug slendr. However, although unsurprising, hope makes clear free use whatever time unit specification want encode slendr models. non-spatial models, results obtained msprime SLiM back ends consistent one another. , makes sense although completely different software, population genetic theory governing shape allele frequency spectra applies equally . fact, example just implemented important part slendr unit test suite verifies simulations created slendr correct.","code":"N <- 1000 N_factor <- 5 # by what factor should Ne change seq_len <- 50e6 rec_rate <- 1e-8 mut_rate <- 1e-8 # constant Ne model forward_const <- population(\"const\", time = 1, N = N) # decreasing step Ne model forward_decr <- population(\"decr\", time = 1, N = N, map = FALSE) %>% resize(time = 2000, N = N / N_factor, how = \"step\") # increasing step Ne model forward_incr <- population(\"inc\", time = 1, N = N) %>% resize(time = 2000, N = N * N_factor, how = \"step\") # exponential increase in size forward_exp_incr <- population(\"exp_inc\", time = 1, N = N) %>% resize(time = 2000, end = 3000, N = N * N_factor, how = \"exponential\") # exponential decrease in size forward_exp_decr <- population(\"exp_decr\", time = 1, N = N) %>% resize(time = 2000, end = 3000, N = N / N_factor, how = \"exponential\") # constant Ne model backward_const <- population(\"const\", time = 5000, N = N) # decreasing step Ne model backward_decr <- population(\"decr\", time = 5000, N = N) %>% resize(time = 3000, N = N / N_factor, how = \"step\") # increasing step Ne model backward_incr <- population(\"inc\", time = 5000, N = N) %>% resize(time = 3000, N = N * N_factor, how = \"step\") # exponential increase in size backward_exp_incr <- population(\"exp_inc\", time = 5000, N = N) %>% resize(time = 3000, end = 2000, N = N * N_factor, how = \"exponential\") # exponential decrease in size backward_exp_decr <- population(\"exp_decr\", time = 5000, N = N) %>% resize(time = 3000, end = 2000, N = N / N_factor, how = \"exponential\") compile_run_afs <- function(model_name, pop, seed = 42) { # maximum length of the simulation (necessary for forward models which start # in generation 1) simulation_length <- 5000 # define sampling times given the direction of time if (attr(pop, \"history\")[[1]]$time == 1) { sampling_time <- simulation_length direction <- \"forward\" } else { sampling_time <- 0 direction <- \"backward\" } # compile model model <- compile_model(pop, generation_time = 15, direction = direction, simulation_length = simulation_length) samples <- schedule_sampling(model, times = sampling_time, list(pop, 50)) # run the model in SLiM ts_slim <- slim(model, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed, verbose = FALSE) # run the same model in msprim ts_msprime <- msprime(model, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed, verbose = FALSE) # load the SLiM tree sequence ts_slim <- ts_recapitate(ts_slim, Ne = N, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) # load the msprime tree sequence ts_msprime <- ts_mutate(ts_msprime, mut_rate, random_seed = seed) # compute the AFS from the SLiM and msprime tree sequences and bind the # results (derived allele counts per frequency bin) in a data frame msprime_afs <- ts_afs(ts_msprime, polarised = TRUE)[-1] slim_afs <- ts_afs(ts_slim, polarised = TRUE)[-1] rbind( data.frame(simulator = \"msprime\", model = model_name, f = msprime_afs), data.frame(simulator = \"SLiM\", model = model_name, f = slim_afs) ) %>% group_by(simulator, model) %>% mutate(n = 1:n(), direction = direction) %>% ungroup() } afs <- bind_rows( compile_run_afs(\"constant\", forward_const), compile_run_afs(\"constant\", backward_const), compile_run_afs(\"step contraction\", forward_decr), compile_run_afs(\"step contraction\", backward_decr), compile_run_afs(\"step increase\", forward_incr), compile_run_afs(\"step increase\", backward_incr), compile_run_afs(\"exponential decrease\", forward_exp_decr), compile_run_afs(\"exponential decrease\", backward_exp_decr), compile_run_afs(\"exponential increase\", forward_exp_incr), compile_run_afs(\"exponential increase\", backward_exp_incr) ) %>% mutate(model = factor( model, levels = c(\"step contraction\", \"constant\", \"step increase\", \"exponential decrease\", \"exponential increase\")) ) ggplot(afs, aes(n, f, color = direction, linetype = simulator)) + geom_line(stat = \"identity\") + facet_wrap(~ model) + labs(x = \"number of derived alleles\", y = \"frequency\", title = \"Site frequency spectra obtained from five demographic models\", subtitle = \"Each model was specified in forward or backward direction of time and executed by two different backend scripts in slendr (SLiM and msprime)\") + guides(color = guide_legend(\"direction of\\ntime in slendr\"), linetype = guide_legend(\"slendr backend\\nengine used\")) + scale_linetype_manual(values = c(3, 2)) + scale_x_continuous(breaks = c(1, seq(20, 100, 20)), limits = c(1, 100)) + theme(legend.position = \"bottom\")"},{"path":"https://www.slendr.net/articles/vignette-07-backends.html","id":"conclusion","dir":"Articles","previous_headings":"","what":"Conclusion","title":"Simulating data with SLiM and msprime backends","text":"example shows standard, non-spatial demographic model like implement simulate R, slendr gives way efficiently using ’s msprime back end addition SLiM back end used throughout documentation.","code":""},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"defining-a-model","dir":"Articles","previous_headings":"Detecting gene flow from msprime and SLiM tree sequences","what":"Defining a model","title":"Simulations using SLiM and msprime engines","text":"now define simple non-spatial model gene flow populations (also known population admixture introgression). involve essentially procedure shown another vignette introducing non-spatial slendr models. Note different normally specify spatial model, except left map argument making population() calls. demonstrate additional features slendr interface tskit Python library, perform gene flow detection test using -called \\(f_4\\) \\(f_4\\)-ratio statistics (briefly introduces vignette). make things little interesting, define two population models: one model without gene flow, another includes gene flow. defining model expressing \\(f\\)-statistics use nomenclature used first study Patterson et al. described \\(f_4-ratio\\) statistic first place. ’re familiar statistical tests gene flow, recommend take look relevant sections linked paper. shiny_graph Let’s start first defining populations splits established figure : Note Ne populations x1 x2 set much higher rest. set \\(N_e\\) populations lower values speed forward SLiM simulations (won’t affect results ’re interested anyway). Higher values \\(N_e\\) x1 x2 populations ensure effect drift acting two populations much smaller. simulate later measure proportion ancestry population b x1, ensure ancestry proportion drift far away expectation make interesting patterns stand clearly. (course, done demonstration purposes speed SLiM simulations making \\(N_e\\) populations smaller. practice, running kinds simulations using msprime back end.)","code":"seq_len <- 100e6 # amount of sequence to simulate rec_rate <- 1e-8 # uniform recombination rate mut_rate <- 1e-8 # mutation rate o <- population(\"outgroup\", time = 1, N = 100) c <- population(\"c\", time = 2500, N = 100, parent = o) a <- population(\"a\", time = 3000, N = 100, parent = c) b <- population(\"b\", time = 3500, N = 100, parent = a) x1 <- population(\"x1\", time = 3800, N = 5000, parent = c) x2 <- population(\"x2\", time = 4000, N = 5000, parent = x1)"},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"compiling-the-model-and-simulating-data","dir":"Articles","previous_headings":"","what":"Compiling the model and simulating data","title":"Simulations using SLiM and msprime engines","text":"now use populations compile two models: first model without gene flow (left panel figure ), second model include 10% gene flow b x1 (right panel figure ). also schedule sampling couple individuals end simulation: 50 individuals populations x1 x2 capture bit natural variation b ancestry x1, one individual rest. running simulations, let’s first make sure models set correctly: Now run models (without gene flow) two slendr backends, SLiM msprime: Note using exactly model configuration object simulation runs! fact, even function interface looks nearly exactly . doesn’t matter specific details demographic models , slendr interpret correctly regardless back end simulation engine choose use.","code":"# no gene flow model model_nogf <- compile_model(populations = list(a, b, x1, x2, c, o), generation_time = 1, simulation_length = 4500) samples <- schedule_sampling( model_nogf, times = 4500, list(a, 1), list(b, 1), list(x1, 50), list(x2, 50), list(c, 1), list(o, 1) ) # model with gene flow gf <- gene_flow(from = b, to = x1, start = 4100, end = 4400, rate = 0.1) model_gf <- compile_model(populations = list(a, b, x1, x2, c, o), gene_flow = gf, generation_time = 1, simulation_length = 4500) samples <- schedule_sampling( model_gf, times = 4500, list(a, 1), list(b, 1), list(x1, 50), list(x2, 50), list(c, 1), list(o, 1) ) plot_model(model_nogf, sizes = FALSE) plot_model(model_gf, sizes = FALSE, proportions = TRUE) # model without gene flow slim_nogf <- slim(model_nogf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) msprime_nogf <- msprime(model_nogf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) # model with b -> x1 gene flow slim_gf <- slim(model_gf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed) msprime_gf <- msprime(model_gf, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed)"},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"comparing-the-outputs-of-msprime-and-slim-runs-of-a-slendr-model","dir":"Articles","previous_headings":"Compiling the model and simulating data","what":"Comparing the outputs of msprime and SLiM runs of a slendr model","title":"Simulations using SLiM and msprime engines","text":"run introgression models two simulation back ends, let’s load SLiM msprime tree sequence outputs compare contents (hoping !). can see , tree sequence summary data loaded processed slendr’s ts_read() function (data produced SLiM backend) similar got “manually” msprime-produced tree sequence using custom defined functions. somehow obvious nice cheap sanity check indicating tree sequence data structure produced two backends demographic model almost .","code":"# SLiM outputs -- we can use built-in slendr functions for those slim_nogf <- slim_nogf %>% ts_recapitate(Ne = 10, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) slim_gf <- slim_gf %>% ts_recapitate(Ne = 10, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) # msprime outputs (note that recapitation and simplification doesn't make # sense here because we already have fully coalesced genealogies for our # individuals of interest msprime_nogf <- ts_mutate(msprime_nogf, mut_rate, random_seed = seed) msprime_gf <- ts_mutate(msprime_gf, mut_rate, random_seed = seed) slim_nogf #> ╔═════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═════════╣ #> ║Trees │ 248158║ #> ╟───────────────┼─────────╢ #> ║Sequence Length│100000000║ #> ╟───────────────┼─────────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼─────────╢ #> ║Sample Nodes │ 21008║ #> ╟───────────────┼─────────╢ #> ║Total Size │ 70.6 MiB║ #> ╚═══════════════╧═════════╝ #> ╔═══════════╤══════╤════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪════════╪════════════╣ #> ║Edges │972815│29.7 MiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Individuals│131789│12.6 MiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Mutations │257348│ 9.1 MiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Nodes │157230│ 5.7 MiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Populations│ 7│ 2.8 KiB│ Yes║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Provenances│ 3│45.1 KiB│ No║ #> ╟───────────┼──────┼────────┼────────────╢ #> ║Sites │257040│ 6.1 MiB│ No║ #> ╚═══════════╧══════╧════════╧════════════╝ msprime_nogf #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 62745║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 208║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 14.4 MiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤══════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪══════╪═════════╪════════════╣ #> ║Edges │247454│ 7.6 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Individuals│ 104│ 2.9 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Mutations │ 69172│ 2.4 MiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Nodes │ 32685│893.7 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Populations│ 6│414 Bytes│ Yes║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Provenances│ 2│ 3.9 KiB│ No║ #> ╟───────────┼──────┼─────────┼────────────╢ #> ║Sites │ 69144│ 1.6 MiB│ No║ #> ╚═══════════╧══════╧═════════╧════════════╝"},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"comparing-results-of-population-genetics-statistics","dir":"Articles","previous_headings":"Compiling the model and simulating data","what":"Comparing results of population genetics statistics","title":"Simulations using SLiM and msprime engines","text":"loaded msprime SLiM tree sequences generated model, let’s see can get comparable results calculate statistics interest. Let’s first extract individuals populations x1 x2 estimate values \\(f_4(c, x1 \\textrm{ } x2; b, o)\\) (significantly negative individuals population x1 due ’s ancestry coming b introgression models consistent zero x2) \\(f_4\\textrm{-ratio}(, b, \\textrm{ } x2; c, o)\\), estimates proportion b-like ancestry x1 x2: Now, let’s repeat analysis tree sequences produced msprime backend two slendr demographic models: Finally can proceed plotting SLiM msprime backend results two models compare together. noted , despite fact SLiM forward simulator msprime coalescent simulator, population genetic expectations gene flow gene flow models match closely (barring level uncertainty expected due randomness population genetic processes play). Things seem looking good behave expected! couple observations: model without gene flow, \\(f_4\\) statistic value zero x1 x2 populations. expected, \\(f_4\\) ~0 consistent relationship lineages conforming standard phylogenetic tree (admixture edges). Essentially, \\(f_4\\) hypothesis test “tree-ness” data. model includes gene flow b x1, \\(f_4\\) value significantly negative. means simple tree hypothesis can rejected gene flow must occurred b x1. Note test reveal direction gene flow, ’s presence. Consistently \\(f_4\\) results, \\(f_4-ratio\\) estimates match expectations. Specifically, statistic estimates around 10% ancestry b x1 gene flow model 0% ancestry cases. importantly, results obtained SLiM msprime back ends nearly exactly . means (non-spatial models) can use SLiM msprime backend interchangeably.","code":"# extract vector of names of the \"test individuals\" in populations `x1` and `x2` X <- ts_samples(slim_gf) %>% filter(pop %in% c(\"x1\", \"x2\")) %>% pull(name) X #> [1] \"x1_1\" \"x1_2\" \"x1_3\" \"x1_4\" \"x1_5\" \"x1_6\" \"x1_7\" \"x1_8\" \"x1_9\" #> [10] \"x1_10\" \"x1_11\" \"x1_12\" \"x1_13\" \"x1_14\" \"x1_15\" \"x1_16\" \"x1_17\" \"x1_18\" #> [19] \"x1_19\" \"x1_20\" \"x1_21\" \"x1_22\" \"x1_23\" \"x1_24\" \"x1_25\" \"x1_26\" \"x1_27\" #> [28] \"x1_28\" \"x1_29\" \"x1_30\" \"x1_31\" \"x1_32\" \"x1_33\" \"x1_34\" \"x1_35\" \"x1_36\" #> [37] \"x1_37\" \"x1_38\" \"x1_39\" \"x1_40\" \"x1_41\" \"x1_42\" \"x1_43\" \"x1_44\" \"x1_45\" #> [46] \"x1_46\" \"x1_47\" \"x1_48\" \"x1_49\" \"x1_50\" \"x2_1\" \"x2_2\" \"x2_3\" \"x2_4\" #> [55] \"x2_5\" \"x2_6\" \"x2_7\" \"x2_8\" \"x2_9\" \"x2_10\" \"x2_11\" \"x2_12\" \"x2_13\" #> [64] \"x2_14\" \"x2_15\" \"x2_16\" \"x2_17\" \"x2_18\" \"x2_19\" \"x2_20\" \"x2_21\" \"x2_22\" #> [73] \"x2_23\" \"x2_24\" \"x2_25\" \"x2_26\" \"x2_27\" \"x2_28\" \"x2_29\" \"x2_30\" \"x2_31\" #> [82] \"x2_32\" \"x2_33\" \"x2_34\" \"x2_35\" \"x2_36\" \"x2_37\" \"x2_38\" \"x2_39\" \"x2_40\" #> [91] \"x2_41\" \"x2_42\" \"x2_43\" \"x2_44\" \"x2_45\" \"x2_46\" \"x2_47\" \"x2_48\" \"x2_49\" #> [100] \"x2_50\" # calculate f4-statistics on individuals of `x1` and `x2` populations using data # from the two models (a model with no gene flow and a gene flow model) -- we use # map_dfr to iterate across all individuals from `X_individuals` and binding all # resulting data frames into a single data frame df_slim_f4 <- rbind( map_dfr(X, ~ ts_f4(slim_nogf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"no gene flow\"), map_dfr(X, ~ ts_f4(slim_gf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"gene flow\") ) %>% select(X, f4, model) %>% mutate(simulator = \"SLiM backend\") # compute the proportions of `b` ancestry in `x1` (expected 10%) and `x2` # (expected 0% because this population did not receive any gene flow from `b`) df_slim_f4ratio <- rbind( ts_f4ratio(slim_nogf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"no gene flow\"), ts_f4ratio(slim_gf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"gene flow\") ) %>% select(X, alpha, model) %>% mutate(simulator = \"SLiM backend\") df_msprime_f4 <- rbind( map_dfr(X, ~ ts_f4(msprime_nogf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"no gene flow\"), map_dfr(X, ~ ts_f4(msprime_gf, \"c_1\", .x, \"b_1\", \"outgroup_1\")) %>% mutate(model = \"gene flow\") ) %>% select(X, f4, model) %>% mutate(simulator = \"msprime backend\") # compute the proportions of `b` ancestry in `x1` (expected 10%) and `x2` # (expected 0% because this population did not receive any gene flow from `b`) df_msprime_f4ratio <- rbind( ts_f4ratio(msprime_nogf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"no gene flow\"), ts_f4ratio(msprime_gf, X, \"a_1\", \"b_1\", \"c_1\", \"outgroup_1\") %>% mutate(model = \"gene flow\") ) %>% select(X, alpha, model) %>% mutate(simulator = \"msprime backend\") df_f4 <- rbind(df_slim_f4, df_msprime_f4) %>% mutate(population = ifelse(grepl(\"x1_\", X), \"x1 (received gene flow)\", \"x2 (no gene flow)\")) ggplot(df_f4, aes(f4, fill = population)) + geom_histogram(bins = 50) + facet_grid(simulator ~ model) + geom_vline(xintercept = 0, linetype = 2) + labs(y = \"number of individuals\", x = \"f4 statistic\", title = \"f4(c, x1 or x2; b, outgroup)\", subtitle = \"f4 ~0 is consistent with no gene flow, negative value indicates gene flow with 'b'\") + theme(legend.position = \"bottom\") df_f4ratio <- rbind(df_slim_f4ratio, df_msprime_f4ratio) %>% mutate(population = ifelse(grepl(\"x1_\", X), \"x1 (received gene flow)\", \"x2 (no gene flow)\")) ggplot(df_f4ratio, aes(alpha, fill = population)) + geom_histogram(bins = 30) + facet_grid(simulator ~ model) + geom_vline(xintercept = 0.1, linetype = 2) + labs(y = \"number of individuals\", x = \"ancestry proportion (f4-ratio statistic)\", title = \"f4-ratio estimate of 'b' ancestry calculated from simulated data\", subtitle = \"f4-ratio = f4(a, outgroup; x1 or x2, c) / f4(a, outgroup; b, c)\") + theme(legend.position = \"bottom\")"},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"computing-allele-frequency-spectra","dir":"Articles","previous_headings":"","what":"Computing allele frequency spectra","title":"Simulations using SLiM and msprime engines","text":"Let’s look one example. Imagine following five demographic models describing changes population size abstract population: models population size change events , depending specifics project, defined using different time units. work radiocarbon-dated samples ancient DNA might used thinking population history units “years present”—case, “present” time (bottom figure) time 0 earlier events specified “5000 years present”, etc. hand, interested modelling theoretical population, might want concern quite happy starting simulation “generation 1” continue specified “generation X”. slendr package makes extremely easy, can specify times whatever direction units want, long kept consistent populations events model. rather unique feature among popgen simulation software programs either make express model “generations forward direction”, explicitly convert time generations going backwards. previous section vignette shown SLiM msprime can used simulation backend. remainder vignette present additional analysis, demonstrating fact can express time models forward backward direction still run models slendr’s SLiM (forward simulator) msprime (backward simulator). Specifically, define five demographic models figure twice, different orientations time – forward backward. compute allele frequency spectrum simulation (five models, forward backward) verify forward backward pairs give result regardless whether use SLiM back end msprime backend.","code":""},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"model-definition","dir":"Articles","previous_headings":"Computing allele frequency spectra","what":"Model definition","title":"Simulations using SLiM and msprime engines","text":"First, let’s define five population histories forward time direction: Now let’s flip flow time around define population histories units “time ago”: convenience, write helper function , given population history object (ten objects just ), compile slendr model, run SLiM msprime back ends, load tree sequence, compute allele frequency spectrum (AFS) . save us lots code repetition (always good thing), minimize chance copy-pasting bugs (definitely good thing!) compare AFS patterns across different models two slendr simulation back ends (according population genetics theory). Now function doesn’t anything special. simply wraps standard slendr simulation pipeline (1. create population(s); 2. compile model object; 3. simulate model; 4. load results; 5. analyse data) single function. Now whole pipeline, can generate simulated data bind result single data frame: Finally, can plot allele frequency spectra models, two time directions, two simulation back ends: , couple things note: two models involve population contraction (single step exponential collapse), see decrease low frequency variants. hand, models population expansion show high proportion low frequency variants. model constant population size shows intermediate pattern. nice validation simulation works expected exactly predicted population genetics theory. forward backward time models give exactly result. unexpected anything else bug slendr. However, although unsurprising, hope makes clear free use whatever time unit specification want encode slendr models. non-spatial models, results obtained msprime SLiM back ends consistent one another. , makes sense although completely different software, population genetic theory governing shape allele frequency spectra applies equally . fact, example just implemented important part slendr unit test suite verifies simulations created slendr correct.","code":"N <- 1000 N_factor <- 5 # by what factor should Ne change seq_len <- 50e6 rec_rate <- 1e-8 mut_rate <- 1e-8 # constant Ne model forward_const <- population(\"const\", time = 1, N = N) # decreasing step Ne model forward_decr <- population(\"decr\", time = 1, N = N, map = FALSE) %>% resize(time = 2000, N = N / N_factor, how = \"step\") # increasing step Ne model forward_incr <- population(\"inc\", time = 1, N = N) %>% resize(time = 2000, N = N * N_factor, how = \"step\") # exponential increase in size forward_exp_incr <- population(\"exp_inc\", time = 1, N = N) %>% resize(time = 2000, end = 3000, N = N * N_factor, how = \"exponential\") # exponential decrease in size forward_exp_decr <- population(\"exp_decr\", time = 1, N = N) %>% resize(time = 2000, end = 3000, N = N / N_factor, how = \"exponential\") # constant Ne model backward_const <- population(\"const\", time = 5000, N = N) # decreasing step Ne model backward_decr <- population(\"decr\", time = 5000, N = N) %>% resize(time = 3000, N = N / N_factor, how = \"step\") # increasing step Ne model backward_incr <- population(\"inc\", time = 5000, N = N) %>% resize(time = 3000, N = N * N_factor, how = \"step\") # exponential increase in size backward_exp_incr <- population(\"exp_inc\", time = 5000, N = N) %>% resize(time = 3000, end = 2000, N = N * N_factor, how = \"exponential\") # exponential decrease in size backward_exp_decr <- population(\"exp_decr\", time = 5000, N = N) %>% resize(time = 3000, end = 2000, N = N / N_factor, how = \"exponential\") compile_run_afs <- function(model_name, pop, seed = 42) { # maximum length of the simulation (necessary for forward models which start # in generation 1) simulation_length <- 5000 # define sampling times given the direction of time if (attr(pop, \"history\")[[1]]$time == 1) { sampling_time <- simulation_length direction <- \"forward\" } else { sampling_time <- 0 direction <- \"backward\" } # compile model model <- compile_model(pop, generation_time = 15, direction = direction, simulation_length = simulation_length) samples <- schedule_sampling(model, times = sampling_time, list(pop, 50)) # run the model in SLiM ts_slim <- slim(model, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed, verbose = FALSE) # run the same model in msprim ts_msprime <- msprime(model, sequence_length = seq_len, recombination_rate = rec_rate, samples = samples, random_seed = seed, verbose = FALSE) # load the SLiM tree sequence ts_slim <- ts_recapitate(ts_slim, Ne = N, recombination_rate = rec_rate, random_seed = seed) %>% ts_mutate(mut_rate, random_seed = seed) # load the msprime tree sequence ts_msprime <- ts_mutate(ts_msprime, mut_rate, random_seed = seed) # compute the AFS from the SLiM and msprime tree sequences and bind the # results (derived allele counts per frequency bin) in a data frame msprime_afs <- ts_afs(ts_msprime, polarised = TRUE)[-1] slim_afs <- ts_afs(ts_slim, polarised = TRUE)[-1] rbind( data.frame(simulator = \"msprime\", model = model_name, f = msprime_afs), data.frame(simulator = \"SLiM\", model = model_name, f = slim_afs) ) %>% group_by(simulator, model) %>% mutate(n = 1:n(), direction = direction) %>% ungroup() } afs <- bind_rows( compile_run_afs(\"constant\", forward_const), compile_run_afs(\"constant\", backward_const), compile_run_afs(\"step contraction\", forward_decr), compile_run_afs(\"step contraction\", backward_decr), compile_run_afs(\"step increase\", forward_incr), compile_run_afs(\"step increase\", backward_incr), compile_run_afs(\"exponential decrease\", forward_exp_decr), compile_run_afs(\"exponential decrease\", backward_exp_decr), compile_run_afs(\"exponential increase\", forward_exp_incr), compile_run_afs(\"exponential increase\", backward_exp_incr) ) %>% mutate(model = factor( model, levels = c(\"step contraction\", \"constant\", \"step increase\", \"exponential decrease\", \"exponential increase\")) ) ggplot(afs, aes(n, f, color = direction, linetype = simulator)) + geom_line(stat = \"identity\") + facet_wrap(~ model) + labs(x = \"number of derived alleles\", y = \"frequency\", title = \"Site frequency spectra obtained from five demographic models\", subtitle = \"Each model was specified in forward or backward direction of time and executed by two different backend scripts in slendr (SLiM and msprime)\") + guides(color = guide_legend(\"direction of\\ntime in slendr\"), linetype = guide_legend(\"slendr backend\\nengine used\")) + scale_linetype_manual(values = c(3, 2)) + scale_x_continuous(breaks = c(1, seq(20, 100, 20)), limits = c(1, 100)) + theme(legend.position = \"bottom\")"},{"path":"https://www.slendr.net/articles/vignette-07-engines.html","id":"conclusion","dir":"Articles","previous_headings":"","what":"Conclusion","title":"Simulations using SLiM and msprime engines","text":"example shows standard, non-spatial demographic model like implement simulate R, slendr gives way efficiently using ’s msprime back end addition SLiM back end used throughout documentation.","code":""},{"path":"https://www.slendr.net/articles/vignette-08-nonslendr-tskit.html","id":"non-spatial-slim-tree-sequences","dir":"Articles","previous_headings":"","what":"Non-spatial SLiM tree sequences","title":"Analyzing non-slendr tree sequences","text":"Consider following SLiM script, creates couple populations (different \\(N_e\\)) splitting ancestral population p1 (lets save /tmp/nonspatial.slim): run script SLiM, can use slendr load output tree sequence (saved /tmp/nonspatial-slim.trees), simplify , overlay mutations using standard functionality originally developed slendr tree sequences. Note command use loading slendr tree sequences, except direct ts_read() function straight tree-sequence output file rather using ts_read() format used working standard slendr simulations. way, slendr can extract information individual’s names, nodes, population assignments, etc. just slendr tree sequence function ts_nodes(). standard slendr models, function loads “raw” node individual tree-sequences tables, performs couple join operations, presents whole thing nice unified form interactive data analysis (can also include spatial information—see ): Moving tskit statistics, can use data table extract list nodes belonging population (various tskit tree-sequence statistics operate , slendr follows design). computing nucleotide diversity four populations using ts_diversity() function, first creating list lists node IDs (.e. chromosomes) individuals population: Just slendr tree sequences (demonstrated paper) can get individual trees , extracted phylogenetic format provided ape R package. first simplify tree sequence even just 10 nodes make things manageable: R tree object, can use packages like ggtree visualize tree (phylogenetic package work ). Note nodes ‘ape phylo’ trees must conform strict format (must labelled 1...N), extract information node IDs tskit tree-sequence data able plot tree.","code":"initialize() { setSeed(42); initializeTreeSeq(); initializeMutationRate(0); initializeMutationType(\"m1\", 0.5, \"f\", 0.0); initializeGenomicElementType(\"g1\", m1, 1.0); initializeGenomicElement(g1, 0, 1e6); initializeRecombinationRate(1e-8); } 1 early() { sim.addSubpop(\"p1\", 10); } 1000 early() { sim.addSubpopSplit(\"p2\", 500, p1); } 3000 early() { sim.addSubpopSplit(\"p3\", 2500, p1); } 5000 early() { sim.addSubpopSplit(\"p4\", 10000, p1); } 6000 late() { sim.treeSeqOutput(\"/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//RtmpEAs99n/file16b9c2b7dee77\"); } ts <- ts_read(nonspatial_trees_file) %>% ts_simplify() %>% ts_mutate(mutation_rate = 1e-7, random_seed = SEED) data <- ts_nodes(ts) %>% dplyr::filter(sampled) data #> # A tibble: 26,020 × 11 #> pop node_id time time_tskit sampled remembered retained alive pedigree_id #> #> 1 p1 0 0 0 TRUE FALSE FALSE TRUE 20073000 #> 2 p1 1 0 0 TRUE FALSE FALSE TRUE 20073000 #> 3 p1 2 0 0 TRUE FALSE FALSE TRUE 20073001 #> 4 p1 3 0 0 TRUE FALSE FALSE TRUE 20073001 #> 5 p1 4 0 0 TRUE FALSE FALSE TRUE 20073002 #> 6 p1 5 0 0 TRUE FALSE FALSE TRUE 20073002 #> 7 p1 6 0 0 TRUE FALSE FALSE TRUE 20073003 #> 8 p1 7 0 0 TRUE FALSE FALSE TRUE 20073003 #> 9 p1 8 0 0 TRUE FALSE FALSE TRUE 20073004 #> 10 p1 9 0 0 TRUE FALSE FALSE TRUE 20073004 #> # ℹ 26,010 more rows #> # ℹ 2 more variables: ind_id , pop_id sample_sets <- split(data$node_id, data$pop) # compute nucleotide diversity in each population # (any other ts_*() tskit R interface function should work) ts_diversity(ts, sample_sets) #> # A tibble: 4 × 2 #> set diversity #> #> 1 p1 0.00000755 #> 2 p2 0.000241 #> 3 p3 0.000463 #> 4 p4 0.000201 samples <- sample(data$node_id, 10) ts_small <- ts_simplify(ts, simplify_to = samples) # extract the 42nd tree in the genealogy to an R 'phylo' format tree <- ts_phylo(ts_small, 42 - 1) #> Starting checking the validity of tree... #> Found number of tips: n = 10 #> Found number of nodes: m = 9 #> Done. tree #> #> Phylogenetic tree with 10 tips and 9 internal nodes. #> #> Tip labels: #> 1, 0, 9, 4, 8, 7, ... #> Node labels: #> 45, 28, 22, 31, 35, 36, ... #> #> Rooted; includes branch lengths. library(ggtree) #> ggtree v3.10.1 For help: https://yulab-smu.top/treedata-book/ #> #> If you use the ggtree package suite in published research, please cite #> the appropriate paper(s): #> #> Guangchuang Yu, David Smith, Huachen Zhu, Yi Guan, Tommy Tsan-Yuk Lam. #> ggtree: an R package for visualization and annotation of phylogenetic #> trees with their covariates and other associated data. Methods in #> Ecology and Evolution. 2017, 8(1):28-36. doi:10.1111/2041-210X.12628 #> #> Guangchuang Yu. Data Integration, Manipulation and Visualization of #> Phylogenetic Trees (1st edition). Chapman and Hall/CRC. 2022, #> doi:10.1201/9781003279242 #> #> LG Wang, TTY Lam, S Xu, Z Dai, L Zhou, T Feng, P Guo, CW Dunn, BR #> Jones, T Bradley, H Zhu, Y Guan, Y Jiang, G Yu. treeio: an R package #> for phylogenetic tree input and output with richly annotated and #> associated data. Molecular Biology and Evolution. 2020, 37(2):599-603. #> doi: 10.1093/molbev/msz240 #> #> Attaching package: 'ggtree' #> The following object is masked from 'package:magrittr': #> #> inset labels <- ts_nodes(tree) %>% select(node = phylo_id, tskit_id = node_id) ggtree(tree, branch.length = \"none\") %<+% labels + geom_label(aes(label = tskit_id)) library(ape) plot(tree, show.tip.label = FALSE) nodelabels() tiplabels()"},{"path":"https://www.slendr.net/articles/vignette-08-nonslendr-tskit.html","id":"msprime-non-slendr-tree-sequences","dir":"Articles","previous_headings":"","what":"msprime (non-slendr) tree sequences","title":"Analyzing non-slendr tree sequences","text":"applies also msprime tree sequences (really surprising, given ’s tskit hood). can start Python: can proceed loading msprime tree sequence R analyze slendr functionality:","code":"import msprime ts = msprime.sim_ancestry(100) ts.dump() ts <- ts_read(msprime_trees_file) ts_nodes(ts) #> # A tibble: 199 × 7 #> pop ind_id node_id time time_tskit sampled pop_id #> #> 1 0 NA 0 0 0 TRUE 0 #> 2 0 NA 1 0 0 TRUE 0 #> 3 0 NA 2 0 0 TRUE 0 #> 4 0 NA 3 0 0 TRUE 0 #> 5 0 NA 4 0 0 TRUE 0 #> 6 0 NA 5 0 0 TRUE 0 #> 7 0 NA 6 0 0 TRUE 0 #> 8 0 NA 7 0 0 TRUE 0 #> 9 0 NA 8 0 0 TRUE 0 #> 10 0 NA 9 0 0 TRUE 0 #> # ℹ 189 more rows"},{"path":"https://www.slendr.net/articles/vignette-08-nonslendr-tskit.html","id":"spatial-slim-non-slendr-tree-sequences","dir":"Articles","previous_headings":"","what":"Spatial SLiM (non-slendr) tree sequences","title":"Analyzing non-slendr tree sequences","text":"Furthermore, generalized interface also supports slendr’s spatial tree-sequence features, bells whistles. instance, lets take following spatial SLiM script (modified SLiM manual) execute SLiM usual way: can load simplify output tree sequence just vignette (anywhere slendr documentation): Finally, can access spatio-temporal data embedded output tree sequence standard slendr way (note spatial sf column location POINT data type): get tree sequence converted spatial sf data format, can use standard geospatial packages use spatial data analysis methods packages provide. briefly demonstrate means, can trivially plot location recorded node: can also collect spatio-temporal ancestry information particular node (.e. times locations ancestors way root, “link” plot signifying parent-child edge somewhere along tree sequence) plot 2D surface (x y dimensions [0, 1]). plot little chaotic, hopefully conveys idea (“focal node” 0 highlighted red). essentially plot last figure paper.","code":"initialize() { setSeed(42); initializeSLiMOptions(keepPedigrees=T, dimensionality=\"xy\"); initializeTreeSeq(); initializeMutationRate(1e-7); initializeMutationType(\"m1\", 0.5, \"f\", 0.0); initializeGenomicElementType(\"g1\", m1, 1.0); initializeGenomicElement(g1, 0, 1e6); initializeRecombinationRate(1e-8); } 1 early() { sim.addSubpop(\"p1\", 500); // initial positions are random in ([0,1], [0,1]) p1.individuals.x = runif(p1.individualCount); p1.individuals.y = runif(p1.individualCount); } modifyChild() { // draw a child position near the first parent, within bounds do child.x = parent1.x + rnorm(1, 0, 0.02); while ((child.x < 0.0) | (child.x > 1.0)); do child.y = parent1.y + rnorm(1, 0, 0.02); while ((child.y < 0.0) | (child.y > 1.0)); return T; } 1: late() { sim.treeSeqRememberIndividuals(sim.subpopulations.individuals, permanent = F); } 10000 late() { sim.treeSeqOutput(\"/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//RtmpEAs99n/file16b9c46c6fc67\"); } ts <- ts_read(spatial_trees_file) %>% ts_simplify() data <- ts_nodes(ts) data #> Simple feature collection with 1955 features and 11 fields #> Geometry type: POINT #> Dimension: XY #> Bounding box: xmin: 0.07649281 ymin: 0.0006142841 xmax: 0.9984433 ymax: 0.9892587 #> CRS: NA #> # A tibble: 1,955 × 12 #> pop node_id time time_tskit location sampled remembered #> #> 1 p1 0 0 0 (0.904485 0.0777489) TRUE FALSE #> 2 p1 1 0 0 (0.904485 0.0777489) TRUE FALSE #> 3 p1 2 0 0 (0.668828 0.5246381) TRUE FALSE #> 4 p1 3 0 0 (0.668828 0.5246381) TRUE FALSE #> 5 p1 4 0 0 (0.7707701 0.1518462) TRUE FALSE #> 6 p1 5 0 0 (0.7707701 0.1518462) TRUE FALSE #> 7 p1 6 0 0 (0.7617792 0.2616453) TRUE FALSE #> 8 p1 7 0 0 (0.7617792 0.2616453) TRUE FALSE #> 9 p1 8 0 0 (0.9807475 0.04223008) TRUE FALSE #> 10 p1 9 0 0 (0.9807475 0.04223008) TRUE FALSE #> # ℹ 1,945 more rows #> # ℹ 5 more variables: retained , alive , pedigree_id , #> # ind_id , pop_id ggplot() + geom_sf(data = data, aes(color = time), alpha = 0.5) ancestral_links <- ts_ancestors(ts, 0) ggplot() + geom_sf(data = ancestral_links, size = 0.5, aes(alpha = parent_time)) + geom_sf(data = sf::st_set_geometry(ancestral_links, \"parent_location\"), aes(color = parent_time)) + geom_sf(data = data[data$node_id == 0, ], size = 3, color = \"red\")"},{"path":"https://www.slendr.net/articles/vignette-08-nonslendr-tskit.html","id":"conclusion","dir":"Articles","previous_headings":"","what":"Conclusion","title":"Analyzing non-slendr tree sequences","text":"vignette gave brief overview using slendr’s R-tskit interface loading, processing, analyzing “pure” non-slendr tree sequences produced msprime SLiM scripts. Although touched upon basic features R-tskit interface standard tree sequences, important note far slendr concerned, matter tree sequence produced, long conforms tskit specification. means regardless source tree sequence data, able use slendr’s tskit functionality run analyses.","code":""},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"note","dir":"Articles","previous_headings":"","what":"Note","title":"Examples from the slendr paper","text":"Since publication slendr paper, new versions SLiM slendr released. Specifically, results paper based SLiM version 4.0.1 slendr version 0.5. Whenever new version SLiM released, ’s always chance aspect randomness simulation changes slightly, affects results even though random seed kept value. , figures paper (specifically figure Example 4 paper) look slightly different. However, results still relevant applicable, just presentation changed.","code":"# workaround for GitHub actions getting \"Killed\" due to out of memory issues # -- when running unit tests on GitHub, smaller amount of sequence will be # simulated if (Sys.getenv(\"RUNNER_OS\") != \"\") { scaling <- 10 # use 10X less sequence on GitHub actions } else { scaling <- 1 # but simulate normal amount of data otherwise } library(slendr) library(dplyr) #> #> Attaching package: 'dplyr' #> The following objects are masked from 'package:stats': #> #> filter, lag #> The following objects are masked from 'package:base': #> #> intersect, setdiff, setequal, union library(ggplot2) library(purrr) library(tidyr) library(cowplot) library(forcats) init_env() #> The interface to all required Python modules has been activated. SEED <- 42 set.seed(SEED) # placeholder \"figure\" where a code chunk will be pasted p_code <- ggplot() + geom_text(aes(x = 1, y = 1), label = \"code will be here\") + theme_void()"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"script-from-panel-a","dir":"Articles","previous_headings":"Example 1 (Figure 2)","what":"Script from panel A","title":"Examples from the slendr paper","text":"","code":"o <- population(\"o\", time = 1, N = 100) c <- population(\"c\", time = 2500, N = 100, parent = o) a <- population(\"a\", time = 2800, N = 100, parent = c) b <- population(\"b\", time = 3700, N = 100, parent = a) x1 <- population(\"x1\", time = 4000, N = 15000, parent = c) x2 <- population(\"x2\", time = 4300, N = 15000, parent = x1) gf <- gene_flow(from = b, to = x1, start = 5400, end = 5800, 0.1) model <- compile_model( populations = list(o, a, b, c, x1, x2), gene_flow = gf, generation_time = 1, simulation_length = 6000 ) plot_model(model, sizes = FALSE, proportions = TRUE) # panel B ts <- msprime(model, sequence_length = 100e6 / scaling, recombination_rate = 1e-8, random_seed = SEED) %>% ts_mutate(mutation_rate = 1e-8, random_seed = SEED) samples <- ts_samples(ts) %>% group_by(pop) %>% sample_n(100) # panel C divergence <- ts_divergence(ts, split(samples$name, samples$pop)) # panel D f4ratio <- ts_f4ratio( ts, X = filter(samples, pop %in% c(\"x1\", \"x2\"))$name, A = \"a_1\", B = \"b_1\", C = \"c_1\", O = \"o_1\" )"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"plotting-code","dir":"Articles","previous_headings":"Example 1 (Figure 2)","what":"Plotting code","title":"Examples from the slendr paper","text":"","code":"divergence <- divergence %>% mutate(pair = paste(x, \"-\", y)) f4ratio <- f4ratio %>% mutate(population = gsub(\"^(.*)_.*$\", \"\\\\1\", X), alpha = alpha * 100) p_ex1_divergence <- divergence %>% ggplot(aes(fct_reorder(pair, divergence), divergence)) + geom_point(size = 2.5) + xlab(\"population pair\") + ylab(\"pairwise divergence\") + theme_minimal() + theme(legend.position = \"bottom\", legend.text = element_text(size = 10), axis.text.x = element_text(hjust = 1, angle = 45, size = 8), axis.title.x = element_blank()) p_ex1_f4ratio <- f4ratio %>% ggplot(aes(population, alpha)) + geom_hline(yintercept = 0, linetype = 2) + geom_jitter(alpha = 0.5) + geom_boxplot(outlier.shape = NA, alpha = 0.7) + ylab(base::expression(italic(\"f\")[4]~\"-ratio ancestry proportion [%]\")) + theme_minimal() + coord_cartesian(ylim = c(0, 20)) + theme(legend.position = \"none\", axis.text.x = element_text(size = 11), axis.title.x = element_blank(), panel.grid.major.x = element_blank())#; p_ex3_f4ratio # let's avoid ggpubr as another dependency: # https://github.com/kassambara/ggpubr/blob/master/R/as_ggplot.R#L27 p_ex1_legend <- ggdraw() + draw_grob(grid::grobTree(get_legend(p_ex1_divergence))) #> Warning in get_plot_component(plot, \"guide-box\"): Multiple components found; #> returning the first one. To return all, use `return_all = TRUE`. p_ex1_model <- plot_model(model, sizes = FALSE, proportions = TRUE) p_ex1 <- plot_grid( p_code, plot_grid( p_ex1_model, plot_grid( p_ex1_divergence + theme(legend.position = \"none\"), p_ex1_f4ratio, ncol = 2, rel_widths = c(1, 0.8), labels = c(\"C\", \"D\") ), p_ex1_legend, nrow = 3, rel_heights = c(1, 1, 0.1), labels = \"B\" ), nrow = 1, labels = c(\"A\", \"\") ) p_ex1"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"script-from-panel-a-1","dir":"Articles","previous_headings":"Example 2 (Figure 3)","what":"Script from panel A","title":"Examples from the slendr paper","text":"","code":"map <- world(xrange = c(0, 10), yrange = c(0, 10), landscape = region(center = c(5, 5), radius = 5)) p1 <- population(\"pop1\", time = 1, N = 2000, map = map, competition = 0) p2 <- population(\"pop2\", time = 1, N = 2000, map = map, competition = 9) p3 <- population(\"pop3\", time = 1, N = 2000, map = map, competition = 6) p4 <- population(\"pop4\", time = 1, N = 2000, map = map, competition = 5) p5 <- population(\"pop5\", time = 1, N = 2000, map = map, competition = 4) p6 <- population(\"pop6\", time = 1, N = 2000, map = map, competition = 3) p7 <- population(\"pop7\", time = 1, N = 2000, map = map, competition = 2) p8 <- population(\"pop8\", time = 1, N = 2000, map = map, competition = 1) model <- compile_model( populations = list(p1, p2, p3, p4, p5, p6, p7, p8), generation_time = 1, simulation_length = 5000, resolution = 0.1, mating = 0.1, dispersal = 0.05 ) ts <- slim(model, sequence_length = 10e6 / scaling, recombination_rate = 1e-8, random_seed = SEED) %>% ts_simplify() %>% ts_mutate(mutation_rate = 1e-7, random_seed = SEED) #> Warning: Simplifying a non-recapitated tree sequence. Make sure this is what #> you really want locations <- ts_nodes(ts) %>% filter(time == max(time)) heterozygosity <- ts_samples(ts) %>% group_by(pop) %>% sample_n(100) %>% mutate(pi = ts_diversity(ts, name)$diversity)"},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"plotting-code-1","dir":"Articles","previous_headings":"Example 2 (Figure 3)","what":"Plotting code","title":"Examples from the slendr paper","text":"","code":"p_ex2_clustering <- ggplot() + geom_sf(data = map) + geom_sf(data = locations, aes(color = pop), size = 0.05, alpha = 0.25) + facet_grid(. ~ pop, switch = \"x\") + xlab(\"spatial distributions emerged in the simulation\") + theme( strip.background = element_blank(), strip.text = element_text(size = 11), panel.grid = element_blank(), axis.ticks = element_blank(), axis.text = element_blank(), panel.background = element_blank() ) + guides(color = \"none\") p_ex2_diversity <- ggplot(heterozygosity, aes(pop, pi, color = pop)) + geom_violin(color = \"black\") + geom_jitter(alpha = 0.5) + labs(y = \"individual heterozygosity\") + guides(color = \"none\") + theme_minimal() + theme(axis.title.x = element_blank(), axis.text.x = element_blank(), panel.grid.major.x = element_blank(), plot.margin = margin(t = 0.2, r = 0.2, b = -0.1, l = 0.2, \"cm\")) p_ex2 <- plot_grid( p_code, plot_grid( p_ex2_diversity, p_ex2_clustering + theme(plot.margin = margin(t = 0, r = 0.4, b = 0, l = 1.8, \"cm\")), nrow = 2, rel_heights = c(1, 0.5), labels = c(\"B\", \"C\") ), nrow = 2, labels = c(\"A\", \"\"), rel_heights = c(1.5, 1) ) p_ex2"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"script-from-panel-a-2","dir":"Articles","previous_headings":"Example 3 (Figure 4)","what":"Script from panel A","title":"Examples from the slendr paper","text":"","code":"map <- world(xrange = c(-13, 70), yrange = c(18, 65), crs = 3035) #> Reading layer `ne_110m_land' from data source #> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmphFFapt/naturalearth/ne_110m_land.shp' #> using driver `ESRI Shapefile' #> Simple feature collection with 127 features and 3 fields #> Geometry type: POLYGON #> Dimension: XY #> Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513 #> Geodetic CRS: WGS 84 R1 <- region( \"EHG range\", map, polygon = list(c(26, 55), c(38, 53), c(48, 53), c(60, 53), c(60, 60), c(48, 63), c(38, 63), c(26, 60)) ) R2 <- region( \"Europe\", map, polygon = list( c(-8, 35), c(-5, 36), c(10, 38), c(20, 35), c(25, 35), c(33, 45), c(20, 58), c(-5, 60), c(-15, 50) ) ) R3 <- region( \"Anatolia\", map, polygon = list(c(28, 35), c(40, 35), c(42, 40), c(30, 43), c(27, 40), c(25, 38)) ) R4 <- join(R2, R3) R5 <- region( \"YAM range\", map, polygon = list(c(26, 50), c(38, 49), c(48, 50), c(48, 56), c(38, 59), c(26, 56)) ) ooa_trajectory <- list(c(40, 30), c(50, 30), c(60, 40), c(45, 55)) map <- world(xrange = c(-13, 70), yrange = c(18, 65), crs = 3035) #> Reading layer `ne_110m_land' from data source #> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmphFFapt/naturalearth/ne_110m_land.shp' #> using driver `ESRI Shapefile' #> Simple feature collection with 127 features and 3 fields #> Geometry type: POLYGON #> Dimension: XY #> Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513 #> Geodetic CRS: WGS 84 ooa <- population( \"OOA\", time = 50000, N = 500, remove = 23000, map = map, center = c(33, 30), radius = 400e3 ) %>% move(trajectory = ooa_trajectory, start = 50000, end = 40000, snapshots = 30) ehg <- population( \"EHG\", time = 28000, N = 1000, parent = ooa, remove = 6000, map = map, polygon = R1 ) eur <- population( \"EUR\", time = 30000, N = 2000, parent = ooa, map = map, polygon = R2 ) %>% resize(N = 10000, time = 5000, end = 0, how = \"exponential\") ana <- population( \"ANA\", time = 25000, N = 4000, parent = ooa, remove = 3000, map = map, polygon = R3 ) %>% expand_range(by = 3e6, start = 10000, end = 7000, polygon = R4, snapshots = 15) yam <- population( \"YAM\", time = 7000, N = 600, parent = ehg, remove = 2500, map = map, polygon = R5 ) %>% move(trajectory = list(c(15, 50)), start = 5000, end = 3000, snapshots = 10) gf <- list( gene_flow(ana, to = yam, rate = 0.5, start = 6500, end = 5000), gene_flow(ana, to = eur, rate = 0.6, start = 8000, end = 6000), gene_flow(yam, to = eur, rate = 0.7, start = 3500, end = 3000) ) model <- compile_model( populations = list(ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, resolution = 10e3, competition = 150e3, mating = 120e3, dispersal = 90e3 ) samples <- schedule_sampling( model, times = seq(0, 50000, by = 1000), list(ehg, 20), list(ana, 20), list(yam, 20), list(eur, 20) ) plot_model(model, sizes = FALSE) plot_map(model) ts <- slim( model, burnin = 200000, samples = samples, random_seed = SEED, sequence_length = 200000, recombination_rate = 1e-8 )"},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"plotting-code-2","dir":"Articles","previous_headings":"Example 3 (Figure 4)","what":"Plotting code","title":"Examples from the slendr paper","text":"","code":"p_map <- plot_map(model) + theme(legend.position = \"bottom\") + guides(alpha = \"none\") p_ex3 <- plot_grid( p_code, plot_grid( plot_model(model, sizes = FALSE), p_map, labels = c(\"B\", \"C\"), nrow = 2, rel_heights = c(1, 1) ), ncol = 2, labels = c(\"A\", \"\"), rel_widths = c(1, 1.2) ) p_ex3"},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"script-from-panel-a-3","dir":"Articles","previous_headings":"Example 4 (Figure 5)","what":"Script from panel A","title":"Examples from the slendr paper","text":"","code":"ts_small <- ts_simplify(ts, simplify_to = c(\"EUR_578\", \"YAM_75\", \"ANA_163\", \"EHG_208\")) # tskit uses zero-based indexing tree <- ts_phylo(ts_small, i = 0 / scaling) #> Starting checking the validity of tree... #> Found number of tips: n = 8 #> Found number of nodes: m = 7 #> Done. nodes <- ts_nodes(tree) edges <- ts_edges(tree) ancestors <- ts_ancestors(ts, \"EUR_578\")"},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"plotting-code-3","dir":"Articles","previous_headings":"Example 4 (Figure 5)","what":"Plotting code","title":"Examples from the slendr paper","text":"explained top page, random number generation current SLiM slightly different version 4.0.1 available time writing paper. figure looks little different well (different random generation implies different movement individuals, different individuals sampled). record, ’s original figure used paper also originally part vignette:","code":"library(ggtree) #> ggtree v3.10.1 For help: https://yulab-smu.top/treedata-book/ #> #> If you use the ggtree package suite in published research, please cite #> the appropriate paper(s): #> #> Guangchuang Yu, David Smith, Huachen Zhu, Yi Guan, Tommy Tsan-Yuk Lam. #> ggtree: an R package for visualization and annotation of phylogenetic #> trees with their covariates and other associated data. Methods in #> Ecology and Evolution. 2017, 8(1):28-36. doi:10.1111/2041-210X.12628 #> #> Guangchuang Yu. Data Integration, Manipulation and Visualization of #> Phylogenetic Trees (1st edition). Chapman and Hall/CRC. 2022, #> doi:10.1201/9781003279242 #> #> Guangchuang Yu. Using ggtree to visualize data on tree-like structures. #> Current Protocols in Bioinformatics. 2020, 69:e96. doi:10.1002/cpbi.96 #> #> #> Attaching package: 'ggtree' #> The following object is masked from 'package:tidyr': #> #> expand # prepare annotation table for ggtree linking R phylo node ID (not tskit integer # ID!) of each node with its population name df <- as_tibble(nodes) %>% select(node = phylo_id, pop) abs_comma <- function (x, ...) { format(abs(x) / 1000, ..., scientific = FALSE, trim = TRUE) } highlight_nodes <- as_tibble(nodes) %>% dplyr::filter(name == \"EUR_578\") %>% .$phylo_id p_tree <- ggtree(tree, aes(color = pop, fill = pop)) %<+% df + geom_tiplab(align = TRUE, geom = \"label\", offset = 2000, color = \"white\", fontface = \"bold\", size = 2.7) + geom_tiplab(align = TRUE, geom = NULL, linetype = \"dotted\", size = 0) + geom_point2(aes(subset = (node %in% highlight_nodes)), color = \"black\", size = 2.7) + geom_label2(aes(label = label, subset = !isTip), color = \"black\", size = 2.7) + theme_tree2() + theme(legend.position = \"none\") + xlab(\"time before present [thousand years ago]\") + scale_x_continuous(limits = c(-80000, 31000), labels = abs_comma, breaks = -c(100, 80, 60, 40, 20, 0) * 1000) p_tree <- revts(p_tree) # nodes$label <- ifelse(is.na(nodes$name), nodes$node_id, nodes$name) nodes$label <- sapply(1:nrow(nodes), function(i) { if (is.na(nodes[i, ]$name)) nodes[i, ]$node_id else { ind <- nodes[i, ]$name paste(nodes[!is.na(nodes$name) & nodes$name == ind, ]$node_id, collapse = \"&\") } }) p_map <- ggplot() + geom_sf(data = map) + geom_sf(data = edges, aes(color = parent_pop), size = 0.5) + geom_sf_label(data = nodes[!nodes$sampled, ], aes(label = node_id, fill = pop), size = 3) + geom_sf_label(data = nodes[nodes$sampled, ], aes(label = label, fill = pop), size = 3, fontface = \"bold\", color = \"white\") + coord_sf(xlim = c(3177066.1, 7188656.9), ylim = c(757021.7, 5202983.3), expand = 0) + guides(fill = guide_legend(\"\", override.aes = aes(label = \"\"))) + guides(color = \"none\") + scale_colour_discrete(drop = FALSE) + scale_fill_discrete(drop = FALSE) + theme_bw() + theme(legend.position = \"bottom\", axis.title.x = element_blank(), axis.title.y = element_blank()) chrom_names <- stats::setNames( c(\"EUR_578 (node 6)\", \"EUR_578 (node 7)\"), unique(ancestors$node_id) ) p_ancestors <- ggplot() + geom_sf(data = map) + geom_sf(data = ancestors, size = 0.5, alpha = 0.25) + geom_sf(data = sf::st_set_geometry(ancestors, \"parent_location\"), aes(shape = parent_pop, color = parent_pop)) + geom_sf(data = filter(ts_nodes(ts), name == \"EUR_578\"), size = 3) + coord_sf(expand = 0) + labs(x = \"longitude\", y = \"latitude\") + theme_bw() + facet_grid(. ~ node_id, labeller = labeller(node_id = chrom_names)) + theme(legend.position = \"none\") p_legend <- ggdraw() + draw_grob(grid::grobTree(get_legend(p_map))) #> Warning in get_plot_component(plot, \"guide-box\"): Multiple components found; #> returning the first one. To return all, use `return_all = TRUE`. p_ex4 <- plot_grid( p_code, plot_grid(p_tree + theme(legend.position = \"none\"), p_map + theme(legend.position = \"none\"), labels = c(\"B\", \"C\"), rel_widths = c(1, 0.9)), p_ancestors, p_legend, labels = c(\"A\", \"\", \"D\", \"\"), nrow = 4, rel_heights = c(0.5, 1, 1, 0.1) ) p_ex4"},{"path":"https://www.slendr.net/articles/vignette-09-paper.html","id":"run-time-of-each-code-example-from-the-paper","dir":"Articles","previous_headings":"","what":"Run time of each code example from the paper","title":"Examples from the slendr paper","text":"following times measured 16’’ MacBook Pro Apple M1 Pro chip (2021 model), 32 GB RAM, running macOS Ventura Version 13.1. table , ex1, ex2, ex3, ex4 correspond runtimes code shown one four example figures slendr paper.","code":""},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"demographic-model","dir":"Articles","previous_headings":"","what":"Demographic model","title":"Extracting true ancestry tracts","text":"Let’s imagine following demographic model Neanderthal introgression ancestors non-Africans (represented “EUR” “PAP” populations, approximating European Papuan people living today), followed Denisovan introgression ancestors Papuans:","code":"anc_all <- population(\"ancestor_all\", time = 700e3, N = 10000, remove = 640e3) afr <- population(\"AFR\", parent = anc_all, time = 650e3, N = 10000) anc_arch <- population(\"ancestor_archaics\", parent = anc_all, time = 650e3, N = 10000, remove = 390e3) nea <- population(\"NEA\", parent = anc_arch, time = 400e3, N = 2000, remove = 30e3) den <- population(\"DEN\", parent = anc_arch, time = 400e3, N = 2000, remove = 30e3) nonafr <- population(\"nonAFR\", parent = afr, time = 100e3, N = 3000, remove = 39e3) eur <- population(\"EUR\", parent = nonafr, time = 45e3, N = 5000) pap <- population(\"PAP\", parent = nonafr, time = 45e3, N = 5000) gf <- list( gene_flow(from = nea, to = nonafr, rate = 0.03, start = 55000, end = 50000), gene_flow(from = den, to = pap, rate = 0.07, start = 35000, end = 30000) ) model <- compile_model( populations = list(anc_all, afr, anc_arch, nea, den, nonafr, eur, pap), gene_flow = gf, generation_time = 30, serialize = FALSE ) plot_model( model, sizes = FALSE, order = c(\"AFR\", \"EUR\", \"nonAFR\", \"PAP\", \"ancestor_all\", \"DEN\", \"ancestor_archaics\", \"NEA\") )"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"tree-sequence-simulation","dir":"Articles","previous_headings":"","what":"Tree sequence simulation","title":"Extracting true ancestry tracts","text":"Let’s now simulate 50Mb tree sequence model, recording 50 diploid individuals EUR PAP populations:","code":"samples <- schedule_sampling(model, times = 0, list(eur, 50), list(pap, 50)) ts <- msprime(model, sequence_length = 100e6, recombination_rate = 1e-8, samples = samples, random_seed = 42)"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"extracting-ancestry-tracts","dir":"Articles","previous_headings":"","what":"Extracting ancestry tracts","title":"Extracting true ancestry tracts","text":"order extract tracts Neanderthal Denisovan ancestry, can use slendr’s new function ts_tracts() serves simplified R-friendly interface Python method tspop.get_pop_ancestry(). important piece information used function -called “census time”, records time recording “ancestral population” identity node ancestral subsegment sample set. Please see excellent vignette tspop information inner workings algorithm. case, let’s extract ancestry tracts corresponding ancestral nodes present 55 thousand years ago – time corresponds moment start archaic introgression: table ancestry tracts looks like. expect, see column indicating name individual, left right coordinates tract individual, well population name source ancestry tract:","code":"nea_tracts <- ts_tracts(ts, census = 55000) #> #> PopAncestry summary #> Number of ancestral populations: 3 #> Number of sample chromosomes: 200 #> Number of ancestors: 44052 #> Total length of genomes: 20000000000.000000 #> Ancestral coverage: 20000000000.000000 den_tracts <- ts_tracts(ts, census = 35000) #> #> PopAncestry summary #> Number of ancestral populations: 3 #> Number of sample chromosomes: 200 #> Number of ancestors: 55373 #> Total length of genomes: 20000000000.000000 #> Ancestral coverage: 20000000000.000000 tracts <- bind_rows(nea_tracts, den_tracts) tracts #> # A tibble: 17,002 × 8 #> name node_id pop source_pop left right length source_pop_id #> #> 1 EUR_1 0 EUR NEA 781887 852668 70781 3 #> 2 EUR_1 0 EUR NEA 1451389 1463837 12448 3 #> 3 EUR_1 0 EUR NEA 1596995 1601441 4446 3 #> 4 EUR_1 0 EUR NEA 1629857 1689656 59799 3 #> 5 EUR_1 0 EUR NEA 3203711 3339565 135854 3 #> 6 EUR_1 0 EUR NEA 3850629 3947923 97294 3 #> 7 EUR_1 0 EUR NEA 6190700 6205627 14927 3 #> 8 EUR_1 0 EUR NEA 10394211 10426667 32456 3 #> 9 EUR_1 0 EUR NEA 13044685 13180258 135573 3 #> 10 EUR_1 0 EUR NEA 14858956 14912068 53112 3 #> # ℹ 16,992 more rows"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"summaries-of-ancestral-proportions","dir":"Articles","previous_headings":"","what":"Summaries of ancestral proportions","title":"Extracting true ancestry tracts","text":"summarise ancestry proportions target EUR PAP populations, see EUR population carries ~3% Neanderthal ancestry also true PAP population. However, also see Papuans carry 7% Denisovan ancestry. consistent model, also expectation empirical data. Let’s visualize proportions individual level:","code":"summary <- tracts %>% group_by(name, node_id, pop, source_pop) %>% summarise(prop = sum(length) / 100e6) #> `summarise()` has grouped output by 'name', 'node_id', 'pop'. You can override #> using the `.groups` argument. summary %>% group_by(pop, source_pop) %>% summarise(mean(prop)) %>% arrange(source_pop, pop) #> `summarise()` has grouped output by 'pop'. You can override using the `.groups` #> argument. #> # A tibble: 3 × 3 #> # Groups: pop [2] #> pop source_pop `mean(prop)` #> #> 1 EUR NEA 0.0303 #> 2 PAP NEA 0.0348 #> 3 PAP DEN 0.0739 summary %>% ggplot(aes(source_pop, prop, color = source_pop, fill = source_pop)) + geom_jitter() + coord_cartesian(ylim = c(0, 0.2)) + geom_hline(yintercept = c(0.03, 0.08), linetype = 2) + ylab(\"ancestry proportion\") + facet_wrap(~ pop) + ggtitle(\"Ancestry proportions in each individual\", \"(vertical lines represent 3% and 7% baseline expectations\")"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"chromosome-painting-of-ancestry-tracts","dir":"Articles","previous_headings":"","what":"“Chromosome painting” of ancestry tracts","title":"Extracting true ancestry tracts","text":"tracts object contains coordinates every single ancestry segment simulated individuals, can “paint” chromosome two archaic human ancestries: lining NEA & DEN ancestry tracts EUR PAP populations, can see common origin Neanderthal ancestry non-African populations manifests significant overlap NEA tracts populations.","code":"tracts %>% mutate(chrom = paste(name, \" (node\", node_id, \")\")) %>% ggplot(aes(x = left, xend = right, y = chrom, yend = chrom, color = source_pop)) + geom_segment(linewidth = 3) + theme_minimal() + labs(x = \"position [bp]\", y = \"haplotype\") + ggtitle(\"True ancestry tracts along each chromosome\") + theme(axis.text.y = element_blank(), panel.grid = element_blank()) + facet_grid(pop ~ ., scales = \"free_y\")"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"average-tract-lengths","dir":"Articles","previous_headings":"","what":"Average tract lengths:","title":"Extracting true ancestry tracts","text":"Let’s compute simple summaries tract lengths simulated data, compare theoretical expectations. Theoretical expectations (Racimo Slatkin 2015, Box 1) Neanderthal tracts: Denisovan tracts: can see, simulations far theoretical expectations, giving us confidence simulations (ancestry tract extraction algorithm) working expected.","code":"tracts %>% group_by(pop, source_pop) %>% summarise(mean(length)) #> `summarise()` has grouped output by 'pop'. You can override using the `.groups` #> argument. #> # A tibble: 3 × 3 #> # Groups: pop [2] #> pop source_pop `mean(length)` #> #> 1 EUR NEA 65765. #> 2 PAP NEA 69204. #> 3 PAP DEN 100317. m <- 0.03 t <- 52500 / 30 r <- 1e-8 mean_nea <- 1 / ((1 - m) * r * (t - 1)) mean_nea #> [1] 58943.84 m <- 0.07 t <- 37500 / 30 r <- 1e-8 mean_den <- 1 / ((1 - m) * r * (t - 1)) mean_den #> [1] 86090.38"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"distribution-of-ancestry-tract-lengths","dir":"Articles","previous_headings":"","what":"Distribution of ancestry tract lengths","title":"Extracting true ancestry tracts","text":"Finally, let’s plot distributions lengths ancestry tracts. case archaic human introgression well studied ’s perhaps exciting look figures. said, less well studied species, might interesting use kinds simulations inference introgression times proportions via Approximate Bayesian Computation another method:","code":"expectation_df <- data.frame( pop = c(\"EUR\", \"PAP\", \"PAP\"), source_pop = c(\"NEA\", \"NEA\", \"DEN\"), length = c(mean_nea, mean_nea, mean_den) ) p_densities <- tracts %>% ggplot(aes(length, color = source_pop)) + geom_density() + geom_vline(data = expectation_df, aes(xintercept = length, color = source_pop), linetype = 2) + facet_wrap(~ pop) + ggtitle(\"Distribution of tract lengths per different ancestries\") cowplot::plot_grid(p_densities, p_densities + scale_x_log10(), nrow = 2)"},{"path":"https://www.slendr.net/articles/vignette-10-tracts.html","id":"pure-msprime-tree-sequence","dir":"Articles","previous_headings":"","what":"Pure msprime tree sequence","title":"Extracting true ancestry tracts","text":"Finally, sanity check, let’s use pure msprime simulation example official tspop documentation test ts_tracts() behaves expected even standard msprime tree-sequence object. First, let’s run simulation code exactly : Let’s save msprime tree sequence disk can load R (.e., approximating might want want use ts_tracts() without running slendr simulation first): Now let’s move R , load tree sequence slendr extract ancestry tracts using ts_tracts(): setting squashed = FALSE, get full, un-squashed ancestry segments, appropriate ancestral node ID: comparing two tables pandas data frames tspop documentation, can see obtained results.","code":"import msprime pop_size = 500 sequence_length = 1e7 seed = 98765 rho = 3e-8 # Make the Demography object. demography = msprime.Demography() demography.add_population(name=\"RED\", initial_size=pop_size) #> Population(initial_size=500, growth_rate=0, name='RED', description='', extra_metadata={}, default_sampling_time=None, initially_active=None, id=0) demography.add_population(name=\"BLUE\", initial_size=pop_size) #> Population(initial_size=500, growth_rate=0, name='BLUE', description='', extra_metadata={}, default_sampling_time=None, initially_active=None, id=1) demography.add_population(name=\"ADMIX\", initial_size=pop_size) #> Population(initial_size=500, growth_rate=0, name='ADMIX', description='', extra_metadata={}, default_sampling_time=None, initially_active=None, id=2) demography.add_population(name=\"ANC\", initial_size=pop_size) #> Population(initial_size=500, growth_rate=0, name='ANC', description='', extra_metadata={}, default_sampling_time=None, initially_active=None, id=3) demography.add_admixture( time=100, derived=\"ADMIX\", ancestral=[\"RED\", \"BLUE\"], proportions=[0.5, 0.5] ) #> Admixture(time=100, derived='ADMIX', ancestral=['RED', 'BLUE'], proportions=[0.5, 0.5]) demography.add_census(time=100.01) # Census is here! #> CensusEvent(time=100.01) demography.add_population_split( time=1000, derived=[\"RED\", \"BLUE\"], ancestral=\"ANC\" ) #> PopulationSplit(time=1000, derived=['RED', 'BLUE'], ancestral='ANC') # Simulate. ts = msprime.sim_ancestry( samples={\"RED\": 0, \"BLUE\": 0, \"ADMIX\" : 2}, demography=demography, random_seed=seed, sequence_length=sequence_length, recombination_rate=rho ) import tempfile path = tempfile.NamedTemporaryFile(suffix=\".trees\").name ts.dump(path) sim_ts <- ts_read(reticulate::py$path) squashed_tracts <- ts_tracts(sim_ts, census = 100.01, squashed = TRUE) #> #> PopAncestry summary #> Number of ancestral populations: 2 #> Number of sample chromosomes: 4 #> Number of ancestors: 118 #> Total length of genomes: 40000000.000000 #> Ancestral coverage: 40000000.000000 head(squashed_tracts) #> # A tibble: 6 × 4 #> sample left right population #> #> 1 0 0 419848 0 #> 2 0 419848 483009 1 #> 3 0 483009 1475765 0 #> 4 0 1475765 2427904 1 #> 5 0 2427904 3635390 0 #> 6 0 3635390 4606954 1 tail(squashed_tracts) #> # A tibble: 6 × 4 #> sample left right population #> #> 1 3 7134130 7362300 1 #> 2 3 7362300 7369409 0 #> 3 3 7369409 7596783 1 #> 4 3 7596783 8289015 0 #> 5 3 8289015 8918727 1 #> 6 3 8918727 10000000 0 full_tracts <- ts_tracts(sim_ts, census = 100.01, squashed = FALSE) #> #> PopAncestry summary #> Number of ancestral populations: 2 #> Number of sample chromosomes: 4 #> Number of ancestors: 118 #> Total length of genomes: 40000000.000000 #> Ancestral coverage: 40000000.000000 head(full_tracts) #> # A tibble: 6 × 5 #> sample left right ancestor population #> #> 1 0 0 33027 74 0 #> 2 0 33027 155453 33 0 #> 3 0 155453 290542 46 0 #> 4 0 290542 419848 18 0 #> 5 0 419848 483009 83 1 #> 6 0 483009 1475765 28 0 tail(full_tracts) #> # A tibble: 6 × 5 #> sample left right ancestor population #> #> 1 3 8477625 8672850 94 1 #> 2 3 8672850 8849756 95 1 #> 3 3 8849756 8918727 131 1 #> 4 3 8918727 9165035 44 0 #> 5 3 9165035 9176562 47 0 #> 6 3 9176562 10000000 58 0"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Extending models with custom SLiM code","text":"slendr designed specifically purpose making easy possible program population genetic simulations spatio-temporal demographic histories using SLiM. primary goal means simulate arbitrarily complex spatial scenarios simulate data used development new spatial inference population genetic methods (benchmarking current methods). functionality briefly described vignette #1 detail vignette #6. spatial simulation features implemented, turned trivial support also “traditional”, non-spatial demographic models, described vignette #4 vignette #5 – tree-like models population divergences, gene-flow events, popularion resize events, like can see evolutionary biology textbook. non-spatial models can often implemented (executed) efficient coalescent setting, added possibility run compiled slendr model SLiM back-end script implemented msprime, hidden behind slendr function msprime(). Throughout time, slendr models purely neutral, without much planning extend simulations toward non-neutral scenarios. However, easy specification non-spatial demographic models simulation slendr/SLiM engine, many users asking possibility simulate non-neutral scenarios , generally, customization simplified genome architecture assumed slendr default (single chromosome uniform recombination purely neutral mutations). vignette describes can done slendr. even SLiM can things ? Fair question! , SLiM can obviously simulate nearly kind conceivable evolutionary model. One motivation supporting non-neutral customized genomic architecture mutation models slendr users find easier program complex “base” demographic models R (models complex history population divergences, gene-flow events, resizes), let slendr take care demographic history . provide custom SLiM code non-neutral evolution.**","code":""},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"disclaimers-and-caveats","dir":"Articles","previous_headings":"","what":"Disclaimers and caveats","title":"Extending models with custom SLiM code","text":"want use slendr/SLiM extension functionality described vignette, aware following restrictions guidelines: extension functionality primarily design support complex selection models using customized SLiM snippets still using slendr handling basic demographic modeling scaffold (population splits, resizes, gene flows, etc.). handling demographic events remains unchanged even slim() models, coalescent msprime() engine slendr (implements coalescent, neutral counterpart slendr models) changed means customize . assumed SLiM script bundled slendr stays unmodified. extension SLiM code customization snippets added top – customizing mutation types, genomic element types, recombination maps, custom fitness callbacks, etc. Functionality involving demographic events (splits, gene-flow, etc.) stay place . User-provided custom code allowed change functionality operates. Related point – slendr models assume (always assume) Wright-Fisher (WF) dynamics. Although perhaps necessarily problem simulating non-spatial selection models (.e. slendr models don’t include map), adding selection spatial models SLiM WF setting unlikely lead meaningful results. need non-WF models, use pure SLiM. slendr useful . built-SLiM engine slendr provides several utility Eidos functions make easy extend engine custom callbacks, give possibility refer slendr-specific model information. See list section .","code":""},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"slendr-slim-api","dir":"Articles","previous_headings":"","what":"slendr / SLiM “API”","title":"Extending models with custom SLiM code","text":"describe following slendr / SLiM “API” functions constants can use customize default SLiM script comes bundled slendr: population() tick() model_time() save_state() reset_state() write_log() SIMULATION_START SIMULATION_END SEQUENCE_LENGTH PATH Let’s first load required R libraries dive :","code":"library(slendr) init_env() library(dplyr) library(ggplot2) library(readr) seed <- 42 set.seed(seed)"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"referring-to-populations-population-eidos-function","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Referring to populations: population() Eidos function","title":"Extending models with custom SLiM code","text":"recap, program slendr model, can refer populations using symbolic names “AFR” “EUR” slendr R script following way: Similarly, simulation finished need analyze tree-sequence produced slendr model, can refer populations (individuals sampled populations) using symbolic names like : Along lines, ’re customizing slendr/SLiM simulation, can get Subpopulation SLiM object corresponding symbolic name slendr population (used R side things) using Eidos function population() provided built-SLiM script slendr. instance, let’s assume compiled toy model human demography vignette #4 ran SLiMgui using slim(..., method = \"gui\"). can open Eidos console GUI type following: way, name populations symbolic names “AFR” “OOA”, can refer population SLiM extension code using symbolic names (.e., don’t know whether population called p0 p2 SLiM side things). symbolic names R SLiM parts slendr. Additionally, population() Eidos function provides helpful error checking. instance, try get SLiM Subpopulation object corresponding slendr population exist point doesn’t yet exist running SLiM simulation particular time, get informative error: make typo try access population doesn’t exist slendr model , get another informative error message: function useful practice, though? purpose vignette show extend slendr non-neutral scenarios, , example, select random chromosome given population using population() function like (maybe add beneficial mutation). use bit code elaborate complete examples . , point population() Eidos function provided slendr SLiM codebase make easier refer components slendr model defined SLiM extension code.","code":"afr <- population(\"AFR\", time = 100000, N = 20000) eur <- population(\"EUR\", time = 60000, N = 2000, parent = afr) # <... compile model and simulate a tree sequence `ts` ...> # compute heterozygosity in the individual \"EUR\" ts_diversity(ts, \"EUR_1\") # compute genetic divergence between selected Africans and Europeans afr_samples <- c(\"AFR_1\", \"AFR_2\", \"AFR_3\") eur_samples <- c(\"EUR_1\", \"EUR_2\", \"EUR_3\") ts_divergence(ts, list(afr = afr_samples, eur = eur_samples)) > // get a SLiM object corresponding to the \"AFR\" population > population(\"AFR\") Subpopulation > > // get the number of genomes in the population > length(population(\"AFR\").genomes) 6000 > > // get both \"AFR\" and \"OOA\" subpopulation objects > population(c(\"AFR\", \"OOA\")) Subpopulation Subpopulation > population(\"YAM\") The following populations not present in tick 81 (slendr model time 97600): YAM > population(\"asdf\") Not all provided population identifiers are present in the model. Check your code to make sure that all of these are defined: asdf target_genome = sample(population(\"AFR\").genomes, 1); target_genome.addNewMutation(m0, selectionCoeff = 0.05, position = 1000000);"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"referring-to-times-tick-eidos-function","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Referring to times: tick() Eidos function","title":"Extending models with custom SLiM code","text":"Another useful feature slendr ability use arbitrary time units model definition. instance, creating “EUR” population snippet , wrote : numbers 100000 60000 split time meaningful , compile run slendr model like (run SLiMgui able demonstrate various Eidos functions) can interpret 90000 60000 “years present”, can quite convenient ’re building models using radiocarbon-dated fossil-dated ages, rather using traditional units generations (forward time SLiM, backwards time _msprime). However, time want refer “absolute time units” SLiM code, bit numerical conversion translate times “years present” SLiM’s “generations going forward”. Depending complexity model, can get tricky particularly burn-times, etc. tick() Eidos function makes possible use slendr model “natural time units” even SLiM side, without convert years present generations forward time, similarly population() function Eidos allows us refer slendr population symbolic names consistent way R SLiM . function takes time slendr time units – years present, generations backwards time, whatever used slendr R script – translates SLiM’s “ticks”. instance, example model modern human history, get tick number corresponding time 40 thousand years ago calling: Similarly consistency check performed population() Eidos function, tick() also makes sure time given lies within time window expected running simulation. instance, AFR-EUR toy model starts 90 thousand years ago, try get tick number corresponding million years ago, get : Indeed, tick-based time boundaries model : population(), can also perform slendr-time--tick conversion vectorized manner: Naturally, model use special time units (instance, times encoded generations, even forward time), tick() function becomes effectively identity function: Indeed, pop Eidos console SLiMgui model, can verify : model starts population “pop” created time point 1 ’s programmed run 1000 generations, meaning last generation simulation 1001. Still, tick() function can useful even slendr models use time units SLiM (generations forward time) provides useful boundary checking. instance, happens try get tick number model (runs generation 1 generation 1 + 1000): case, time point 0 invalid “predate” first generation toy model (1) time point 1000000 invalid “postdate” last generation model (1001). Additionally, tick() function automatically takes care offsetting tick count burn-period specified simulation: ’ll see thanks possibility using arbitrary integer expressions SLiM 4.2, can use tick() Eidos function easy straightforward scheduling custom callbacks.","code":"afr <- population(\"AFR\", time = 90000, N = 20000) eur <- population(\"EUR\", time = 60000, N = 2000, parent = afr) model <- compile_model(populations = list(afr, eur), generation_time = 30) slim(model, sequence_length = 100000, recombination_rate = 1e-8, method = \"gui\") > tick(40000) # what does time 40000 in a slendr model correspond to in SLiM ticks? 1668 > tick(1e6) Some of the times fall outside of the range of the slendr model: - oldest possible event: 90000 - youngest possible event: 0 The offending times were: 1000000 > // the very first time point of the simulation > tick(90e3) 1 > // the very last time point of the simulation > tick(0) 3001 > tick(c(90000, 0)) 1 3001 pop <- population(\"pop\", time = 1, N = 1000) simple_model <- compile_model(pop, generation_time = 1, simulation_length = 1000) slim(simple_model, sequence_length = 1000, recombination_rate = 0, method = \"gui\") > // no conversion needed for forward-time models expressed in units of generations > tick(1) 1 > tick(1001) 1001 > tick(c(0, 1e6)) Some of the times fall outside of the range of the slendr model: - oldest possible event: 1 - youngest possible event: 1001 The offending times were: 0, 1000000 pop <- population(\"pop\", time = 1, N = 1000) simple_model <- compile_model(pop, generation_time = 1, simulation_length = 1000) slim(simple_model, sequence_length = 1000, recombination_rate = 0, burnin = 100, method = \"gui\") > tick(1) // model time at 1 generation is shifted by 100 burnin 101 > tick(1001) // same for the final time point of 1001 generations 1101"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"referring-to-model-times-model_time-eidos-function","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Referring to model times: model_time() Eidos function","title":"Extending models with custom SLiM code","text":"function inverse tick() function. instance, want write information associated time stamp using time context slendr model (like “years present” demonstrated ) tick numbers. example, gives us model time (years ago) corresponding first tick EUR-AFR model : gives us model time corresponding last tick simulation (.e. present-day “0 years present”):","code":"> model_time(1) 90000 > model_time(3001) 0"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"logging-write_log-eidos-function","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Logging: write_log() Eidos function","title":"Extending models with custom SLiM code","text":"write_log() tiny helper function provided slendr’s SLiM back-end script serves print given string SLiM log output together appropriate tick number time. produces result following kind:","code":"> write_log(\"hello from the current event\") tick 18 (model time 89490): - hello from the current event"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"saving-and-re-starting-simulation-state-save_state-and-reset_state","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Saving and re-starting simulation state: save_state() and reset_state()","title":"Extending models with custom SLiM code","text":"Whenever ’re dealing simulations , say, trajectories beneficial alleles, usually take care situations allele interest gets lost simulation finishes running. cases, usually need reset simulation state just mutation added try . Section 9.2 venerable SLiM manual (“Making sweeps conditional fixation”) great example solve base SLiM. Although quite easy use approach slendr/SLiM extension snippets described , ’s one issue approach: SLiM’s sim.outputFull() sim.readFromPopulationFile() methods preserve important internal slendr tags assigned using .{set,get}Value() methods. require poking slendr’s SLiM codebase, confusing user. circumvent problem, slendr’s SLiM back-end script provides following functions: save_state(): saves full state SLiM simulation (just sim.outputFull() ), also saving slendr-specific values; reset_state(): restores full SLiM simulation state (including slendr specific tags values), chooses new random seed. latter performed restarting simulation using save_state() - reset_state() tandem practically always done order change outcome simulation. demonstrate two functions might used practice, let’s say wanted build (default purely neutral!) model African Eurasian history vignette discussed , say want add beneficial mutation “EUR” population time 15 ky ago. utilize bits Eidos code introduced far define following slendr extension snippet (now let’s ignore question actually add slendr simulation focus SLiM extension code isolation): Note simplicity don’t define mutation types, simply re-use m0 mutation type used slendr default. unlikely useful complex simulations look general solution defines custom mutation type purpose. point example show use save_state() reset_state() practice.","code":"function (void) add_mutation(s pop_name, f selection_coef) { // sample the first target carrier chromosome of the new mutation... target = sample(population(pop_name).genomes, 1); // ... and add the mutation to it mutation = target.addNewDrawnMutation(m0, position = 1); defineGlobal(\"BACKGROUND\", target.mutations); defineGlobal(\"FOCAL\", mutation); write_log(\"adding beneficial mutation to population \" + pop_name); } tick(15000) late() { // save simulation state in case we need to restart if the mutation is lost save_state(); add_mutation(\"pop\", s); } tick(15000):SIMULATION_END late() { segregating = sim.countOfMutationsOfType(m0) > 0; fixed = sum(sim.substitutions.mutationType == m0) == 1; // the mutation is not segregating and is not fixed either -- we must restart if (!segregating & !fixed) { write_log(\"mutation lost -- restarting\"); reset_state(); add_mutation(\"EUR\"); } }"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"global-constants","dir":"Articles","previous_headings":"slendr / SLiM “API”","what":"Global constants","title":"Extending models with custom SLiM code","text":"inspecting built-SLiM simulation script slendr, find contains number global constants. considered internal users shouldn’t rely code. However, one useful constant can see used snippet SEQUENCE_LENGTH – total amount sequence simulated (.e., number passed slim(, sequence_length = , ...) R code). course, specifying initialize() {...} callback genomic elements give flexibility might want skip specifying sequence_length = entirely, also show . Another useful pair slendr constants SIMULATION_START SIMULATION_END, specify time points (ticks!) given slendr model start end. Even relevant writing customized SLiM extension scripts slendr constant PATH, contains path directory files SLiM simulation results (tree-sequence file) saved. can specified setting parameter path calls slim() function like slim(..., path = ). means want save custom files (various tables allele frequencies, etc., .e. just tree-sequence file), don’t organize plumbing slendr R script SLiM extension code – can specify path = R use inside SLiM code PATH constant.","code":""},{"path":[]},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"putting-it-all-together-running-a-customized-slendr-simulation","dir":"Articles","previous_headings":"Practical examples","what":"Putting it all together: running a customized slendr simulation","title":"Extending models with custom SLiM code","text":"examples show can refer slendr population symbolic name SLiM side using population() Eidos function provided slendr built-SLiM script, can convert slendr-specific time units SLiM’s internal ticks using functions tick() model_time(). ’ve also learned save reset SLiM state slendr simulation using functions save_state() reset_state() write logging information using function write_log(). now demonstrate defining snippet SLiM code used customize complete slendr simulation. course, example ’ve chosen extremely trivial – can, principle, use feature available SLiM (long ’s compatible Wright-Fisher models, slendr assumes basis models). use customization practice? One way edit built-slendr SLiM script manually add SLiM code, brittle reproducible. ’s much better way ’s directly supported slendr’s R interface. Let’s say defined following model modern human demographic history slendr. exactly example one show vignette #4: Let’s also assume following “SLiM snippet file” (line gets path example SLiM extension packaged slendr): can include extension snippet standard slendr engine SLiM script providing path compile_model(): can check extension snippet really appended built-SLiM engine script running slim() function setting method = \"gui\" (look towards end script!): Note set ts = FALSE slim() call. way can switch generating tree sequence don’t care example. want save frequency trajectory beneficial allele saved ~/Desktop/trajectory.tsv. Checking file produced customized slim() simulation shows frequency trajectories indeed saved correctly: Speaking : one thing ’s little annoying extension snippet used example parameters hard-coded (selection coefficient, target population, even path frequency trajectory file). wanted run slendr script different values selection coefficient, examining behavior model depending population beneficial allele arise, ’d create multiple copies extension script. can better using slendr’s support substitution “templating”.","code":"library(slendr) init_env() #> The interface to all required Python modules has been activated. # African ancestral population afr <- population(\"AFR\", time = 90000, N = 3000) # first migrants out of Africa ooa <- population(\"OOA\", parent = afr, time = 60000, N = 500, remove = 23000) %>% resize(N = 2000, time = 40000, how = \"step\") # Eastern hunter-gatherers ehg <- population(\"EHG\", parent = ooa, time = 28000, N = 1000, remove = 6000) # European population eur <- population(\"EUR\", parent = ehg, time = 25000, N = 5000) # Anatolian farmers ana <- population(\"ANA\", time = 28000, N = 3000, parent = ooa, remove = 4000) # Yamnaya steppe population yam <- population(\"YAM\", time = 8000, N = 500, parent = ehg, remove = 2500) # define gene-flow events gf <- list( gene_flow(from = ana, to = yam, rate = 0.4, start = 7900, end = 7800), gene_flow(from = ana, to = eur, rate = 0.5, start = 6000, end = 5000), gene_flow(from = yam, to = eur, rate = 0.65, start = 4000, end = 3500) ) extension_path <- system.file(\"extdata\", \"extension_trajectory.txt\", package = \"slendr\") // Because we want to simulate non-neutral evolution, we have to provide a // custom initialization callback -- slendr will use it to replace its default // neutral genomic architecture (i.e. the initialize() {...} callback it uses // by default for neutral simulations). Note that we can refer to slendr's // constants SEQUENCE_LENGTH and RECOMBINATION_RATE, which will carry values // passed through from R via slendr's slim() R function. initialize() { // define some parameters of the model defineConstant(\"s\", 0.1); defineConstant(\"onset_time\", 15000); defineConstant(\"target_pop\", \"EUR\"); initializeMutationType(\"m1\", 0.5, \"f\", s); initializeGenomicElementType(\"g1\", m1, 1.0); initializeGenomicElement(g1, 0, SEQUENCE_LENGTH - 1); initializeMutationRate(0); initializeRecombinationRate(RECOMBINATION_RATE); defineConstant(\"traj_file\", paste0(PATH, \"trajectory.tsv\")); } function (void) add_mutation(void) { // sample one target carrier of the new mutation... target = sample(population(target_pop).genomes, 1); // ... and add the mutation in the middle of it mut = target.addNewDrawnMutation(m1, position = asInteger(SEQUENCE_LENGTH / 2)); // save the mutation for later reference defineGlobal(\"MUTATION\", mut); write_log(\"adding beneficial mutation to population \" + target_pop); // write the header of the output file writeFile(traj_file, \"time\\tfrequency\"); } tick(onset_time) late() { // save simulation state in case we need to restart if the mutation is lost // (save_state() is a built-in function provided by slendr for customization) save_state(); add_mutation(); } tick(onset_time):SIMULATION_END late() { // the mutation is not segregating and is not fixed either -- we must restart if (!MUTATION.isSegregating & !MUTATION.isFixed) { write_log(\"mutation lost -- restarting\"); // reload the simulation state from just before we added the beneficial // mutation above (reset_state() is another built-in slendr function) reset_state(); add_mutation(); } // compute the frequency of the mutation of interest frequency = population(\"EUR\").genomes.mutationFrequenciesInGenomes(); // save the current frequency to the output file writeFile(traj_file, model_time(community.tick) + \"\\t\" + frequency, append = T); } model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, extension = extension_path # <--- include the SLiM extension snippet ) slim(model, sequence_length = 1e6, recombination_rate = 0, ts = FALSE, method = \"gui\") $ head ~/Desktop/trajectory.tsv time frequency 15000 0.0001 14970 0.0005 14940 0.0007 14910 0.0008 14880 0.0005 14850 0.0007 14820 0.0009 14790 0.001 14760 0.0013"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"putting-it-all-together-parametrizing-a-customized-slendr-simulation","dir":"Articles","previous_headings":"Practical examples","what":"Putting it all together: parametrizing a customized slendr simulation","title":"Extending models with custom SLiM code","text":"easy parametrization customized slendr / SLiM models, slendr provides function substitute_values(). Simply speaking, rather hardcode values parameters extension SLiM snippet files, can indicate parameter value substituted file using simple syntax {{parameter_name}}. Take look new, flexible version snippet used look {{...}} template placeholders. version expanded study trajectory focal selected allele depending population originated time (rather hardcoding target population onset time directly customization script ). Substitute values parameters customization SLiM extension script: Missing parameter gives error immediately: plug parametrized script compile_model() just : can leverage flexibility solution run another version model, time adding mutation different population. fact, let’s check happens beneficial allele appears EHG, ANA, YAM populations. Note uses extension snippet, just substitutes different values placeholder parameters: can visualize different trajectories like . different allele frequency trajectories reflect complex demographic history European population, particularly dilution frequency due influx ancestry populations Europeans, shown demographic tree . simulations allele originated directly EUR another population EUR can trace ancestry ancient gene flow, trajectory allele leading fixation bit delayed, depending particular gene flow happened. Still, ’s strong selection reached fixation even prior gene flow gene flow happened strong proportion, reaches fixation much faster.","code":"extension_path <- system.file(\"extdata\", \"extension_trajectory_params.txt\", package = \"slendr\") // Define model constants (to be substituted) all in one place // (each {{placeholder}} will be replaced by a value passed from R). // Note that string constant template patterns are surrounded by \"quotes\"! initialize() { defineConstant(\"s\", {{s}}); defineConstant(\"onset_time\", {{onset_time}}); defineConstant(\"target_pop\", \"{{target_pop}}\"); defineConstant(\"origin_pop\", \"{{origin_pop}}\"); // compose a trajectory file based on given parameters defineConstant(\"traj_file\", PATH + \"/\" + \"traj_\" + target_pop + \"_\" + origin_pop + \".tsv\"); } // Because we want to simulate non-neutral evolution, we have to provide a // custom initialization callback -- slendr will use it to replace its default // neutral genomic architecture (i.e. the initialize() {...} callback it uses // by default for neutral simulations). Note that we can refer to slendr's // constants SEQUENCE_LENGTH and RECOMBINATION_RATE, which will carry values // passed through from R via slendr's slim() R function. initialize() { initializeMutationType(\"m1\", 0.5, \"f\", s); initializeGenomicElementType(\"g1\", m1, 1.0); initializeGenomicElement(g1, 0, SEQUENCE_LENGTH - 1); initializeMutationRate(0); initializeRecombinationRate(RECOMBINATION_RATE); } function (void) add_mutation(void) { // sample one target carrier of the new mutation... target = sample(population(origin_pop).genomes, 1); // ... and add the mutation in the middle of it mut = target.addNewDrawnMutation(m1, position = asInteger(SEQUENCE_LENGTH / 2)); // save the mutation for later reference defineGlobal(\"MUTATION\", mut); write_log(\"adding beneficial mutation to population \" + target_pop); writeFile(traj_file, \"time\\tfreq_origin\\tfreq_target\"); } tick(onset_time) late() { // save simulation state in case we need to restart if the mutation is lost save_state(); add_mutation(); } tick(onset_time):SIMULATION_END late() { // the mutation is not segregating and is not fixed either -- we must restart if (!MUTATION.isSegregating & !MUTATION.isFixed) { write_log(\"mutation lost -- restarting\"); reset_state(); add_mutation(); } // compute the frequency of the mutation of interest and save it (if the // mutation is missing at this time, save its frequency as NA) freq_origin = \"NA\"; freq_target = \"NA\"; if (population(origin_pop, check = T)) freq_origin = population(origin_pop).genomes.mutationFrequenciesInGenomes(); if (population(target_pop, check = T)) freq_target = population(target_pop).genomes.mutationFrequenciesInGenomes(); writeFile(traj_file, model_time(community.tick) + \"\\t\" + freq_origin + \"\\t\" + freq_target, append = T); } extension <- substitute_values( extension_path, s = 0.1, onset_time = 15000, origin_pop = \"EUR\", target_pop = \"EUR\" ) extension <- substitute_values( extension_path, onset_time = 15000, origin_pop = \"EUR\", target_pop = \"EUR\" ) # Error: The extension script contains the following unsubstituted patterns: {{s}} model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, extension = extension ) slim(model, sequence_length = 1e6, recombination_rate = 0, path = \"~/Desktop\", ts = FALSE, random_seed = 42) run_model <- function(origin_pop, onset_time) { extension <- substitute_values( extension_path, s = 0.1, onset_time = onset_time, origin_pop = origin_pop, target_pop = \"EUR\" ) model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, extension = extension ) slim(model, sequence_length = 1e6, recombination_rate = 0, path = \"~/Desktop\", ts = FALSE, random_seed = 42) } run_model(origin_pop = \"EUR\", onset_time = 15000) run_model(origin_pop = \"ANA\", onset_time = 15000) run_model(origin_pop = \"EHG\", onset_time = 15000) run_model(origin_pop = \"YAM\", onset_time = 8000) load_traj <- function(origin_pop) { df <- read.table(paste0(\"~/Desktop/traj_EUR_\", origin_pop, \".tsv\"), header = TRUE) df$origin <- origin_pop df$target <- \"EUR\" df } traj <- rbind(load_traj(\"EUR\"), load_traj(\"ANA\"), load_traj(\"EHG\"), load_traj(\"YAM\")) library(ggplot2) ggplot(traj) + geom_line(aes(time, freq_target, linetype = \"EUR\"), color = \"black\") + geom_line(aes(time, freq_origin, color = origin), linetype = \"dashed\") + xlim(15000, 0) + labs(title = \"Allele frequency in EUR given the origin in another population\", x = \"years before present\", y = \"allele frequency\", color = \"frequency\\nin original\\npopulation\", linetype = \"frequency\\nin target\\npopulation\") + scale_linetype_manual(values = c(\"solid\", \"dashed\")) + facet_wrap(~ origin) #> Warning: Removed 419 rows containing missing values or values outside the scale range #> (`geom_line()`)."},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"programming-slendrslim-extension-snippets-directly-in-r-scripts","dir":"Articles","previous_headings":"Practical examples","what":"Programming slendr/SLiM extension snippets directly in R scripts","title":"Extending models with custom SLiM code","text":"Thanks multiline string support implemented R 4.2, can specify SLiM extension code directly R string inside R script, instead include external file like previous examples. Including SLiM code directly way makes little easier iterate development can put everything single reproducible R script. defined SLiM snippet way, rest code work exactly way example just (thing changed code providing SLiM extension string directly instead providing path file):","code":"# extension \"template\" provided as a single string (this contains the same code # as the script used just above, except specified directly in R) extension_template <- r\"( // Because we want to simulate non-neutral evolution, we have to provide a // custom initialization callback -- slendr will use it to replace its default // neutral genomic architecture (i.e. the initialize() {...} callback it uses // by default for neutral simulations). Note that we can refer to slendr's // constants SEQUENCE_LENGTH and RECOMBINATION_RATE, which will carry values // passed through from R via slendr's slim() R function. initialize() { initializeMutationType(\"m1\", 0.5, \"f\", 0.0); initializeGenomicElementType(\"g1\", m1, 1.0); initializeGenomicElement(g1, 0, SEQUENCE_LENGTH - 1); initializeMutationRate(0); initializeRecombinationRate(RECOMBINATION_RATE); } // Define model constants (to be substituted) all in one place // (each {{placeholder}} will be replaced by a value passed from R). // Note that string constant template patterns are surrounded by \"quotes\"! initialize() { defineConstant(\"s\", {{s}}); defineConstant(\"onset_time\", {{onset_time}}); defineConstant(\"target_pop\", \"{{target_pop}}\"); defineConstant(\"origin_pop\", \"{{origin_pop}}\"); // compose the path to a trajectory file based on given parameters defineConstant(\"traj_file\", PATH + \"/\" + \"traj_\" + target_pop + \"_\" + origin_pop + \".tsv\"); } function (void) add_mutation(void) { // sample one target carrier of the new mutation... target = sample(population(origin_pop).genomes, 1); // ... and add the mutation in the middle of it mut = target.addNewMutation(m1, s, position = asInteger(SEQUENCE_LENGTH / 2)); // save the mutation for later reference defineGlobal(\"MUTATION\", mut); write_log(\"adding beneficial mutation to population \" + origin_pop); writeFile(traj_file, \"tick\\ttime\\tfreq_origin\\tfreq_target\"); } tick(onset_time) late() { // save simulation state in case we need to restart if the mutation is lost save_state(); add_mutation(); } tick(onset_time):SIMULATION_END late() { // the mutation is not segregating and is not fixed either -- we must restart if (!MUTATION.isSegregating & !MUTATION.isFixed) { write_log(\"mutation lost -- restarting\"); reset_state(); add_mutation(); } // compute the frequency of the mutation of interest and save it (if the // mutation is missing at this time, save its frequency as NA) freq_origin = \"NA\"; freq_target = \"NA\"; if (population(origin_pop, check = T)) freq_origin = population(origin_pop).genomes.mutationFrequenciesInGenomes(); if (population(target_pop, check = T)) freq_target = population(target_pop).genomes.mutationFrequenciesInGenomes(); writeFile(traj_file, community.tick + \"\\t\" + model_time(community.tick) + \"\\t\" + freq_origin + \"\\t\" + freq_target, append = T); } )\" run_model <- function(origin_pop, onset_time) { extension <- substitute_values( extension_template, # <--- template SLiM code string directly (not as a file!) s = 0.1, onset_time = onset_time, origin_pop = origin_pop, target_pop = \"EUR\" ) model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), gene_flow = gf, generation_time = 30, extension = extension ) slim(model, sequence_length = 1e6, recombination_rate = 0, path = \"~/Desktop\", ts = FALSE, random_seed = 42) } run_model(\"EUR\", onset_time = 15000) head(load_traj(\"EUR\")) #> tick time freq_origin freq_target origin target #> 1 2501 15000 1e-04 1e-04 EUR EUR #> 2 2502 14970 3e-04 3e-04 EUR EUR #> 3 2503 14940 4e-04 4e-04 EUR EUR #> 4 2504 14910 5e-04 5e-04 EUR EUR #> 5 2505 14880 4e-04 4e-04 EUR EUR #> 6 2506 14850 4e-04 4e-04 EUR EUR"},{"path":"https://www.slendr.net/articles/vignette-11-extensions.html","id":"customizing-genomic-architecture-selective-sweep-simulations","dir":"Articles","previous_headings":"Practical examples > Programming slendr/SLiM extension snippets directly in R scripts","what":"Customizing genomic architecture (selective sweep simulations)","title":"Extending models with custom SLiM code","text":"Customization slendr / SLiM models can extend also genomic architecture . altering initialization procedure providing custom initialize() {...} callback SLiM extension code entirely overrides setting (default just one) genomic element type recombination rate, users might want set entirely . means ’s possible avoid SEQUENCE_LENGTH RECOMBINATION_RATE arguments used slendr back-end engine provided via slim(..., sequence_length = ..., recombination_rate = ..., ...) arguments. Let’s take following slendr model Neanderthal introgression: Now put together SLiM extension code set non-neutral slendr simulation: put everything together compiling slendr model (now swaps slendr’s default neutrality non-neutral genomic architecture, including deleterious mutations): also record 10 Neanderthals 100 Africans Europeans tree sequence: Using compiled model, can simulate tree sequence using slim() function like standard slendr workflow. However, note able skip sequence_length = recombination_rate = arguments! non-neutral extension snippet already customizes though, ’s reason us instruct slim() front way: ’s suggested Neanderthals significantly lower \\(N_e\\) compared anatomically modern humans (AMH), might decreased efficacy negative selection deleterious variants compared AMH. Consequently, following Neanderthal admixture AMH, introgressed Neanderthal DNA AMH negative selection, particularly regions genome stronger selective constraints. implies compute proportion Neanderthal ancestry along sample European genomes, see clear dip amount surviving Neanderthal DNA function distance locus selection. customized SLiM snippet saved allele frequency Neanderthal marker Europeans, can just load data directly visualize : hypothesis clearly stands data! shouldn’t forget default data structure slendr tree sequence. gives us much flexibility terms statistics can compute, effectively can computed don’t need mutations genotype files calculate . instance, alternative direct computation allele frequencies SLiM run (saved frequencies.tsv), use simulated tree sequence quickly calculate genetic divergence Europeans Neanderthal windows along simulated genome. Indeed, clearly observe lower introgression levels (indicated increased European-Neanderthal divergence) around loci selection increased surviving introgression far loci, purifying selection removed Neanderthal introgressed DNA regions selective constraint.","code":"# create the ancestor of everyone and a chimpanzee outgroup # (we set both N = 1 to reduce the computational time for this model) chimp <- population(\"CH\", time = 6.5e6, N = 1000) # two populations of anatomically modern humans: Africans and Europeans afr <- population(\"AFR\", parent = chimp, time = 6e6, N = 10000) eur <- population(\"EUR\", parent = afr, time = 70e3, N = 5000) # Neanderthal population splitting at 600 ky ago from modern humans # (becomes extinct by 40 ky ago) nea <- population(\"NEA\", parent = afr, time = 600e3, N = 1000, remove = 40e3) # 5% Neanderthal introgression into Europeans between 55-50 ky ago gf <- gene_flow(from = nea, to = eur, rate = 0.05, start = 55000, end = 45000) model <- compile_model( populations = list(chimp, nea, afr, eur), gene_flow = gf, generation_time = 30 ) extension <- r\"( initialize() { // model parameters to be substitute_values()'d from R below defineConstant(\"gene_length\", {{gene_length}}); defineConstant(\"n_genes\", {{n_genes}}); defineConstant(\"n_markers\", {{n_markers}}); defineConstant(\"introgression_time\", {{introgression_time}}); defineConstant(\"freq_file\", PATH + \"/{{freq_file}}\"); // total length of the genome to be simulated defineConstant(\"total_length\", n_genes * gene_length); // positions of neutral Neanderthal markers along the genome defineConstant(\"neutral_pos\", seq(0, total_length - 1, by = gene_length / n_markers)); // positions of deleterious mutations in the center of each gene defineConstant(\"selected_pos\", seq(gene_length / 2, total_length - 1, by = gene_length)); } // Because we want to simulate non-neutral evolution, we have to provide a // custom initialization callback -- slendr will use it to replace its default // neutral genomic architecture (i.e. the initialize() {...} callback it uses // by default for neutral simulations). initialize() { initializeMutationType(\"m0\", 0.5, \"f\", 0.0); // neutral Neanderthal marker mutation type initializeMutationType(\"m1\", 0.5, \"f\", {{s}}); // deleterious Neanderthal mutation type initializeGenomicElementType(\"g1\", m0, 1.0); // genomic type of 'genes' genes = c(); // gene start-end coordinates breakpoints = c(); // recombination breakpoints rates = c(); // recombination rates // compute coordinates of genes, as well as the recombination map breakpoints // between each gene start = 0; for (i in seqLen(n_genes)) { // end of the next gene end = start + gene_length - 1; genes = c(genes, start, end); // uniform recombination within a gene, followed by a 0.5 recombination breakpoint rates = c(rates, RECOMBINATION_RATE, 0.5); breakpoints = c(breakpoints, end, end + 1); // start of the following genes start = end + 1; } // odd elements --> starts of genes gene_starts = integerMod(seqAlong(genes), 2) == 0; // even elements --> ends of genes gene_ends = integerMod(seqAlong(genes), 2) == 1; // set up all the genes at once initializeGenomicElement(g1, genes[gene_starts], genes[gene_ends]); // set up the recombination map initializeRecombinationRate(rates, breakpoints); // no mutation rate (we will add neutral variation after the SLiM run) initializeMutationRate(0); } // Add Neanderthal-specific mutations one tick prior to the introgression tick(introgression_time) - 1 late() { // get all Neanderthal chromosomes just prior to the introgression target = population(\"NEA\").genomes; write_log(\"adding neutral Neanderthal markers\"); mutations = target.addNewDrawnMutation(m0, position = asInteger(neutral_pos)); defineConstant(\"MARKERS\", target.mutations); write_log(\"adding deleterious Neanderthal mutation\"); target.addNewDrawnMutation(m1, position = asInteger(selected_pos)); } // At the end, write Neanderthal ancestry proportions along the genome to a file // (in our R analysis code we will compare the results of this with the // equivalent computation using a tree sequence) SIMULATION_END late() { df = DataFrame( \"gene\", asInteger(MARKERS.position / gene_length), \"pos\", MARKERS.position, \"freq\", sim.mutationFrequencies(population(\"EUR\"), MARKERS) ); writeFile(freq_file, df.serialize(format = \"tsv\")); } )\" %>% substitute_values( introgression_time = 55000, s = -0.003, gene_length = 5e6, n_genes = 200, n_markers = 100, freq_file = \"frequencies.tsv\" ) model <- compile_model( populations = list(nea, eur), gene_flow = gf, generation_time = 30, extension = extension ) nea_samples <- schedule_sampling(model, times = 50000, list(nea, 1)) modern_samples <- schedule_sampling(model, times = 0, list(eur, 100)) samples <- rbind(nea_samples, modern_samples) x = Sys.time() data_dir <- \"~/Projects/introgression_data/\" slim(model, recombination_rate = 1e-8, samples = samples, path = data_dir) y = Sys.time() y - x # Time difference of 45.62365 mins n_genes <- 200 gene_length <- 5e6 window_length <- 100e3 freqs <- read_tsv(\"~/Projects/introgression_data/frequencies.tsv\") #> Rows: 40000000 Columns: 3 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: \"\\t\" #> dbl (3): gene, pos, freq #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. freqs %>% mutate(pos = pos %% 5e6) %>% group_by(pos) %>% summarise(freq = 100 * mean(freq)) %>% ggplot() + geom_line(aes(pos, freq)) + geom_vline(xintercept = gene_length / 2, linetype = 2) + labs(x = \"coordinate along a gene\", y = \"Neanderthal ancestry proportion [%]\", title = \"Proportion of Neanderthal ancestry in Europeans along 5Mb independent genes\", subtitle = \"(dashed line indicates introgressed deleterious Neanderthal allele)\") + coord_cartesian(ylim = c(0, 3)) # load a tree sequence and extract the names of recorded individuals ts <- ts_read(file = \"~/Projects/introgression_data/slim.trees\", model) samples <- ts_names(ts, split = \"pop\") samples #> $EUR #> [1] \"EUR_1\" \"EUR_2\" \"EUR_3\" \"EUR_4\" \"EUR_5\" \"EUR_6\" \"EUR_7\" #> [8] \"EUR_8\" \"EUR_9\" \"EUR_10\" \"EUR_11\" \"EUR_12\" \"EUR_13\" \"EUR_14\" #> [15] \"EUR_15\" \"EUR_16\" \"EUR_17\" \"EUR_18\" \"EUR_19\" \"EUR_20\" \"EUR_21\" #> [22] \"EUR_22\" \"EUR_23\" \"EUR_24\" \"EUR_25\" \"EUR_26\" \"EUR_27\" \"EUR_28\" #> [29] \"EUR_29\" \"EUR_30\" \"EUR_31\" \"EUR_32\" \"EUR_33\" \"EUR_34\" \"EUR_35\" #> [36] \"EUR_36\" \"EUR_37\" \"EUR_38\" \"EUR_39\" \"EUR_40\" \"EUR_41\" \"EUR_42\" #> [43] \"EUR_43\" \"EUR_44\" \"EUR_45\" \"EUR_46\" \"EUR_47\" \"EUR_48\" \"EUR_49\" #> [50] \"EUR_50\" \"EUR_51\" \"EUR_52\" \"EUR_53\" \"EUR_54\" \"EUR_55\" \"EUR_56\" #> [57] \"EUR_57\" \"EUR_58\" \"EUR_59\" \"EUR_60\" \"EUR_61\" \"EUR_62\" \"EUR_63\" #> [64] \"EUR_64\" \"EUR_65\" \"EUR_66\" \"EUR_67\" \"EUR_68\" \"EUR_69\" \"EUR_70\" #> [71] \"EUR_71\" \"EUR_72\" \"EUR_73\" \"EUR_74\" \"EUR_75\" \"EUR_76\" \"EUR_77\" #> [78] \"EUR_78\" \"EUR_79\" \"EUR_80\" \"EUR_81\" \"EUR_82\" \"EUR_83\" \"EUR_84\" #> [85] \"EUR_85\" \"EUR_86\" \"EUR_87\" \"EUR_88\" \"EUR_89\" \"EUR_90\" \"EUR_91\" #> [92] \"EUR_92\" \"EUR_93\" \"EUR_94\" \"EUR_95\" \"EUR_96\" \"EUR_97\" \"EUR_98\" #> [99] \"EUR_99\" \"EUR_100\" #> #> $NEA #> [1] \"NEA_1\" # compute coordinates of sliding windows along the genome windows <- seq(from = 0, to = n_genes * gene_length - 1, by = window_length) head(windows) #> [1] 0e+00 1e+05 2e+05 3e+05 4e+05 5e+05 tail(windows) #> [1] 999400000 999500000 999600000 999700000 999800000 999900000 # compute divergence from the tree sequence in each window separately divergence <- ts_divergence(ts, samples, windows = windows, mode = \"branch\")$divergence[[1]] # compute average divergence at each position in a gene, and a 95% C.I. div_df <- tibble( pop = \"eur\", pos = windows %% gene_length, div = divergence ) %>% group_by(pop, pos) %>% summarise( mean = mean(div), n = n(), std = sd(div), ci_low = mean - 2 * std / sqrt(n), ci_up = mean + 2 * std / sqrt(n) ) #> `summarise()` has grouped output by 'pop'. You can override using the `.groups` #> argument. ggplot(div_df) + geom_ribbon(aes(pos, ymin = ci_low, ymax = ci_up), fill = \"grey70\") + geom_line(aes(pos, mean)) + geom_vline(aes(xintercept = gene_length / 2), linetype = 2) + labs(x = \"coordinate along a gene\", y = \"divergence to Neanderthal\", title = \"Divergence of Europeans to a Neanderthal genome along 5Mb independent genes\", subtitle = \"(dashed line indicates introgressed deleterious Neanderthal allele)\")"},{"path":"https://www.slendr.net/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Martin Petr. Author, maintainer.","code":""},{"path":"https://www.slendr.net/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Petr M (2024). slendr: Simulation Framework Spatiotemporal Population Genetics. R package version 1.0.0, https://github.com/bodkan/slendr.","code":"@Manual{, title = {slendr: A Simulation Framework for Spatiotemporal Population Genetics}, author = {Martin Petr}, year = {2024}, note = {R package version 1.0.0}, url = {https://github.com/bodkan/slendr}, }"},{"path":[]},{"path":"https://www.slendr.net/index.html","id":"overview-","dir":"","previous_headings":"","what":"Overview","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"slendr toolbox running population genomic simulations entirely R. original motivation developing provide framework simulating spatially-explicit genomic data real geographic landscapes, however, grown much since : slendr can now simulate data traditional, non-spatial demographic models using msprime simulation engine, even supports selection scenarios via user-defined SLiM estension snippets. addition model definition simulation, slendr also provides set function efficient analysis tree-sequence genomic data, utilizing tskit module underlying computation. page briefly summarizes slendr’s important features. detailed description slendr architecture extensive set practical code examples can found paper PCI journal website.","code":""},{"path":"https://www.slendr.net/index.html","id":"citing-slendr","dir":"","previous_headings":"","what":"Citing slendr","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"slendr paper now published Peer Community Journal! use slendr work, please cite : Petr, Martin; Haller, Benjamin C.; Ralph, Peter L.; Racimo, Fernando. slendr: framework spatio-temporal population genomic simulations geographic landscapes. Peer Community Journal, Volume 3 (2023), article . e121. doi : 10.24072/pcjournal.354. Citations help justify development fixing bugs! Thank ! ❤️","code":""},{"path":"https://www.slendr.net/index.html","id":"main-features","dir":"","previous_headings":"","what":"Main features","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"brief summary slendr’s important features. R package allows : Program demographic models, including population splits, population size changes, gene-flow events using extremely simple declarative language entirely R (see vignette example model-definition interface). Even complex models can written little code require bare minimum R programming knowledge (thing user needs know call R function R data frame look like). Execute slendr models using efficient, tailor-made SLiM msprime simulation scripts bundled R package. simulation engines save outputs form efficient tree-sequence data structure. SLiM msprime programming needed! Load, process, analyse tree-sequence outputs via slendr’s built-R interface tree-sequence library tskit. can compute many population genetic statistics R immediately simulation finishes directly output tree sequences, without convert files formats (VCF, EIGENSTRAT) analysis different software. Although originally assumed neutrality simulated models, slendr now provides simple extension mechanism customization SLiM-based models using user-defined SLiM code. Simulations selection models arbitrary complexity now entirely possible. Schedule sampling events specify many individuals’ genomes, populations, times (optionally, locations) recorded simulation engine (SLiM msprime) output tree-sequence files. Encode complex models population movements landscape (see brief example model , extended explanation tutorial). knowledge cartographic geospatial analysis concepts needed. Simulate dynamic spatial demographic models using SLiM’s continuous-space simulation capabilities directly R (, SLiM programming required). outputs simulations saved tree sequences can analysed using standard R geospatial data analysis libraries. slendr performs conversion tree sequence tables appropriate spatial R data type automatically. Specify within-population individual dispersal dynamics R interface leveraging SLiM’s individual interaction parameters implemented SLiM back-end script. Utilizing flexibility R wealth libraries statistics, geospatial analysis graphics, combining power population genetic simulation frameworks SLiM msprime, slendr R package makes possible write entire simulation analytic pipelines without need leave R environment.","code":""},{"path":"https://www.slendr.net/index.html","id":"testing-the-r-package-in-an-online-rstudio-session","dir":"","previous_headings":"","what":"Testing the R package in an online RStudio session","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"can open RStudio session test examples vignettes directly web browser clicking button (installation needed!): case RStudio instance appears starting slowly, please patient (Binder freely available service limited computational resources provided community). Binder crashes, try reloading web page, restart cloud session. get browser-based RStudio session, can navigate vignettes/ directory test examples !","code":""},{"path":"https://www.slendr.net/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"slendr now available CRAN means can install simply entering install.packages(\"slendr\") R console. like test latest features software (perhaps need bug fixes), can install devtools::install_github(\"bodkan/slendr\") (note requires R package devtools). ⚠️⚠️⚠️ Note: default slendr installation longer hard dependency geospatial R packages sf, stars, rnaturalearth. indend use slendr spatial genomic simulations data analysis, run install.packages(c(\"sf\", \"stars\", \"rnaturalearth\")) first. ⚠️⚠️⚠️ software active development! like stay updated: Click “Watch” button project’s GitHub website. Follow social media posting progress updates (can find links homepage). time time, take look changelog post updates new features, breaking changes, etc.","code":""},{"path":"https://www.slendr.net/index.html","id":"example","dir":"","previous_headings":"","what":"Example","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"brief demonstration kind spatial model slendr originally designed simulate data . Please note although spatially-explicit population genetic model, slendr extensive support traditional, non-spatial simulations well. Furthermore, example shows specify simulate model R. doesn’t show analyse tree-sequence outputs compute population genetic statistics (important feature demonstrated tutorial).","code":""},{"path":"https://www.slendr.net/index.html","id":"id_1-setup-the-spatial-context","dir":"","previous_headings":"Example","what":"1. Setup the spatial context","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"Imagine wanted simulate spatio-temporal genomic data toy model history modern humans West Eurasia Africa migration. First, define spatial context simulation. represent “world” occupied populations model. can visualize defined world map using function plot_map provided package. Although example use real Earth landscape, map can completely abstract (either blank user-defined landscape features continents, islands, corridors barriers).","code":"library(slendr) # this sets up internal Python environment and needs to be ran only once! # (do not put this in your R scripts, run this command in the R console # after you (re-)install slendr) setup_env() # activate the internal Python environment needed for simulation and # tree-sequence processing init_env() map <- world( xrange = c(-13, 70), # min-max longitude yrange = c(18, 65), # min-max latitude crs = \"EPSG:3035\" # coordinate reference system (CRS) for West Eurasia ) plot_map(map)"},{"path":"https://www.slendr.net/index.html","id":"id_2-define-broader-geographic-regions","dir":"","previous_headings":"Example","what":"2. Define broader geographic regions","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"order make definitions population ranges () easier, can define smaller regions map using function region. Note coordinates slendr specified geographic coordinate system (.e., degrees longitude latitude), internally represented projected CRS (case, EPSG 3035 specified ). makes easier us define spatial features simply reading coordinates regular map internal projected CRS makes simulations accurate (distances shapes distorted can use CRS tailored region world working ). projected CRS takes care projection part world ’re interested three-dimensional Earth surface two-dimensional map. , can use generic plot_map function visualize objects, making sure specified correctly:","code":"africa <- region( \"Africa\", map, polygon = list(c(-18, 20), c(38, 20), c(30, 33), c(20, 33), c(10, 38), c(-6, 35)) ) europe <- region( \"Europe\", map, polygon = list( c(-8, 35), c(-5, 36), c(10, 38), c(20, 35), c(25, 35), c(33, 45), c(20, 58), c(-5, 60), c(-15, 50) ) ) anatolia <- region( \"Anatolia\", map, polygon = list(c(28, 35), c(40, 35), c(42, 40), c(30, 43), c(27, 40), c(25, 38)) ) plot_map(africa, europe, anatolia)"},{"path":"https://www.slendr.net/index.html","id":"id_3-define-demographic-history-and-population-boundaries","dir":"","previous_headings":"Example","what":"3. Define demographic history and population boundaries","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"important function slendr package population(), used define names, split times, sizes spatial ranges populations. , specify times years present, distances kilometers. makes sense models, times can also given forward direction. also note functions move() expand_range() designed take slendr population object schedule spatial dynamics appropriate times model simulation (happen later step). Note order make example executable reasonable time extremely old laptop, decreased sizes populations unrealistic levels. speed SLiM simulation later step. can use function plot_map get “compressed” overview spatio-temporal range dynamics encoded model far (prior simulation ).","code":"afr <- population( # African ancestral population \"AFR\", time = 52000, N = 3000, map = map, polygon = africa ) ooa <- population( # population of the first migrants out of Africa \"OOA\", parent = afr, time = 51000, N = 500, remove = 25000, center = c(33, 30), radius = 400e3 ) %>% move( trajectory = list(c(40, 30), c(50, 30), c(60, 40)), start = 50000, end = 40000, snapshots = 20 ) ehg <- population( # Eastern hunter-gatherers \"EHG\", parent = ooa, time = 28000, N = 1000, remove = 6000, polygon = list( c(26, 55), c(38, 53), c(48, 53), c(60, 53), c(60, 60), c(48, 63), c(38, 63), c(26, 60)) ) eur <- population( # European population name = \"EUR\", parent = ehg, time = 25000, N = 2000, polygon = europe ) ana <- population( # Anatolian farmers name = \"ANA\", time = 28000, N = 3000, parent = ooa, remove = 4000, center = c(34, 38), radius = 500e3, polygon = anatolia ) %>% expand_range( # expand the range by 2.500 km by = 2500e3, start = 10000, end = 7000, polygon = join(europe, anatolia), snapshots = 20 ) yam <- population( # Yamnaya steppe population name = \"YAM\", time = 7000, N = 500, parent = ehg, remove = 2500, polygon = list(c(26, 50), c(38, 49), c(48, 50), c(48, 56), c(38, 59), c(26, 56)) ) %>% move(trajectory = list(c(15, 50)), start = 5000, end = 3000, snapshots = 10) plot_map(afr, ooa, ehg, eur, ana, yam)"},{"path":"https://www.slendr.net/index.html","id":"id_4-define-gene-flow-events","dir":"","previous_headings":"Example","what":"4. Define gene-flow events","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"default, populations slendr mix even overlapping. order schedule gene-flow event two populations, can use function gene_flow. want specify multiple events , can collect events simple R list:","code":"gf <- list( gene_flow(from = ana, to = yam, rate = 0.5, start = 6500, end = 6400, overlap = FALSE), gene_flow(from = ana, to = eur, rate = 0.5, start = 8000, end = 6000), gene_flow(from = yam, to = eur, rate = 0.75, start = 4000, end = 3000) )"},{"path":"https://www.slendr.net/index.html","id":"id_5-compile-the-model-to-a-set-of-configuration-files","dir":"","previous_headings":"Example","what":"5. Compile the model to a set of configuration files","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"run simulation, compile individual model components (population objects gene-flow events) single R object, specifying additional model parameters. Additionally, performs internal consistency checks, making sure model parameters (split times, gene flow times, etc.) make sense (potentially quite computationally costly) simulation even run. Compiled model kept R object can passed different functions. example, simulate data slim() engine (also run simulation coalescent engine via msprime() function).","code":"model <- compile_model( populations = list(afr, ooa, ehg, eur, ana, yam), # populations defined above gene_flow = gf, generation_time = 30, resolution = 10e3, # resolution in meters per pixel competition = 130e3, mating = 100e3, # spatial interaction parameters dispersal = 70e3, # how far can offspring end up from their parents )"},{"path":"https://www.slendr.net/index.html","id":"id_6-visualize-the-model","dir":"","previous_headings":"Example","what":"6. Visualize the model","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"package provides R shiny-based browser app explore_model() checking model dynamics interactively visually. complex models, much better static spatial plots one showed step 2 : function two modes: Plotting (“playing”) spatial map dynamics: Displaying demographic history graph (splits gene-flow events) embedded specified model:","code":"explore_model(model)"},{"path":"https://www.slendr.net/index.html","id":"id_7-run-the-model-in-slim","dir":"","previous_headings":"Example","what":"7. Run the model in SLiM","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"Finally, can execute compiled model SLiM. run simulation batch mode, also run SLiMgui setting method = \"gui\". allow us inspect spatial simulation happens real time. slim function generates complete SLiM script tailored run spatial model defined . saves , user, tremendous amount time, don’t write new SLiM code every time design new demographic model. output simulation run slendr model always tree sequence, loaded object ts_slim. specified , slendr’s SLiM backend simulate 10 Mb sequence individual, produce tree sequence output simulation run can analysed many built-population genetic functions. default, individuals living end simulation recorded samples tree sequence. specific set samples (ancient modern) needed, can defined accordingly using dedicated function. Note although defined spatial model, just easily simulated standard, non-spatial data running model slendr’s msprime() back end without need make changes: quick overview SLiM simulation run summarised GIF animation. , please note simulation extremely simplified. simulated small number individuals population, also didn’t specify dispersal dynamics populations look clumped. point, either compute population genetic statistics interest perhaps analyse spatial features genealogies simulated model.","code":"ts_slim <- slim(model, sequence_length = 10e6, recombination_rate = 1e-8, method = \"batch\", random_seed = 314159) ts_msprime <- msprime(model, sequence_length = 10e6, recombination_rate = 1e-8) animate_model(model = model, file = locations_file, steps = 50, width = 500, height = 300) #> Error in eval(expr, envir, enclos): R: UnableToWritePixelCache `/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T//RtmpyLwlps/magick-UHJ_3VsqZdrkY5sTCDUuYTjjSKmibs4k': No space left on device @ error/cache.c/WritePixelCachePixels/6023"},{"path":"https://www.slendr.net/index.html","id":"further-information","dir":"","previous_headings":"","what":"Further information","title":"A Simulation Framework for Spatiotemporal Population Genetics","text":"example provides brief incomplete overview full functionality slendr package. much slendr demonstrated . instance: can tweak parameters influencing dispersal dynamics (“clumpy” populations , far can offspring migrate parents, etc.) define change time. instance, can see animation , African population forms single “blob” really isn’t spread across entire population range. Tweaking dispersal parameters show vignette helps avoid . can use slendr program non-spatial models, means conceivable traditional, random-mating demographic model can simulated lines R code. can learn vignette (detail vignette). SLiM simulations can often quite slow compared coalescent counterparts, also provide functionality allowing simulate slendr models (without change!) using built-msprime back end script. See vignette tutorial works. can extend traditional, non-spatial SLiM-based slendr models customized SLiM code simulate various selection scenarios supported SLiM still leveraging slendr’s easy--use interface programming demographic models. can build complex spatial models still abstract (assuming real geographic location), including traditional simulations demes lattice structure. complete example shown vignette. slendr & SLiM save data tree-sequence file format, thanks R package reticulate interfacing Python code, full power tskit pyslim manipulating tree-sequence data right fingertips, within convenient environment R. extended example can found vignette. spatially explicit population models, slendr package automatically converts simulated output data format makes possible analyse many available R packages geospatial data analysis. brief description functionality can found vignette. can find complete reproducible code behind examples paper dedicated R vignette .","code":""},{"path":"https://www.slendr.net/reference/animate_model.html","id":null,"dir":"Reference","previous_headings":"","what":"Animate the simulated population dynamics — animate_model","title":"Animate the simulated population dynamics — animate_model","text":"Animate simulated population dynamics","code":""},{"path":"https://www.slendr.net/reference/animate_model.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Animate the simulated population dynamics — animate_model","text":"","code":"animate_model(model, file, steps, gif = NULL, width = 800, height = 560)"},{"path":"https://www.slendr.net/reference/animate_model.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Animate the simulated population dynamics — animate_model","text":"model Compiled slendr_model model object file Path table saved individual locations steps many frames animation ? gif Path output GIF file (animation object returned default) width, height Dimensions animation pixels","code":""},{"path":"https://www.slendr.net/reference/animate_model.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Animate the simulated population dynamics — animate_model","text":"gif = NULL, return gganimate animation object. Otherwise GIF file saved value returned.","code":""},{"path":"https://www.slendr.net/reference/area.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate the area covered by the given slendr object — area","title":"Calculate the area covered by the given slendr object — area","text":"Calculate area covered given slendr object","code":""},{"path":"https://www.slendr.net/reference/area.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate the area covered by the given slendr object — area","text":"","code":"area(x)"},{"path":"https://www.slendr.net/reference/area.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate the area covered by the given slendr object — area","text":"x Object class slendr","code":""},{"path":"https://www.slendr.net/reference/area.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate the area covered by the given slendr object — area","text":"Area covered input object. slendr_pop given, table population range area time point returned. slendr_region slendr_world object specified, total area covered object's spatial boundary returned.","code":""},{"path":"https://www.slendr.net/reference/area.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate the area covered by the given slendr object — area","text":"","code":"region_a <- region(\"A\", center = c(20, 50), radius = 20) region_b <- region(\"B\", polygon = list(c(50, 40), c(70, 40), c(70, 60), c(50, 60))) plot_map(region_a, region_b) # note that area won't be *exactly* equal to pi*r^2: # https://stackoverflow.com/a/65280376 area(region_a) #> [1] 1256.063 area(region_b) #> [1] 400"},{"path":"https://www.slendr.net/reference/as.phylo.slendr_phylo.html","id":null,"dir":"Reference","previous_headings":"","what":"Convert an annotated slendr_phylo object to a phylo object — as.phylo.slendr_phylo","title":"Convert an annotated slendr_phylo object to a phylo object — as.phylo.slendr_phylo","text":"function servers workaround around ggtree error: Error UseMethod(\".phylo\") : applicable method '.phylo' applied object class \"c('phylo', 'slendr_phylo')\"","code":""},{"path":"https://www.slendr.net/reference/as.phylo.slendr_phylo.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Convert an annotated slendr_phylo object to a phylo object — as.phylo.slendr_phylo","text":"","code":"# S3 method for slendr_phylo as.phylo(x, ...)"},{"path":"https://www.slendr.net/reference/as.phylo.slendr_phylo.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Convert an annotated slendr_phylo object to a phylo object — as.phylo.slendr_phylo","text":"x Tree object class slendr_phylo ... Additional (unused) arguments .phylo S3 method","code":""},{"path":"https://www.slendr.net/reference/as.phylo.slendr_phylo.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Convert an annotated slendr_phylo object to a phylo object — as.phylo.slendr_phylo","text":"Standard phylogenetic tree object implemented R package ape","code":""},{"path":"https://www.slendr.net/reference/check_dependencies.html","id":null,"dir":"Reference","previous_headings":"","what":"Check that the required dependencies are available for slendr to work — check_dependencies","title":"Check that the required dependencies are available for slendr to work — check_dependencies","text":"Check required dependencies available slendr work","code":""},{"path":"https://www.slendr.net/reference/check_dependencies.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check that the required dependencies are available for slendr to work — check_dependencies","text":"","code":"check_dependencies(python = FALSE, slim = FALSE, quit = FALSE)"},{"path":"https://www.slendr.net/reference/check_dependencies.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check that the required dependencies are available for slendr to work — check_dependencies","text":"python slendr Python environment required? slim SLiM required? quit R interpreter quit required slendr dependencies missing? option (turned default, set FALSE) used mainly avoiding running slendr man page examples machines lack dependencies. set TRUE, logical value returned.","code":""},{"path":"https://www.slendr.net/reference/check_dependencies.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check that the required dependencies are available for slendr to work — check_dependencies","text":"quit = TRUE, values returned, quit = FALSE, scalar logical value returned indicating whether dependencies present.","code":""},{"path":"https://www.slendr.net/reference/check_env.html","id":null,"dir":"Reference","previous_headings":"","what":"Check that the active Python environment is setup for slendr — check_env","title":"Check that the active Python environment is setup for slendr — check_env","text":"function inspects Python environment activated reticulate package prints versions slendr Python dependencies console.","code":""},{"path":"https://www.slendr.net/reference/check_env.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check that the active Python environment is setup for slendr — check_env","text":"","code":"check_env(verbose = TRUE)"},{"path":"https://www.slendr.net/reference/check_env.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check that the active Python environment is setup for slendr — check_env","text":"verbose log message printed? FALSE, logical value returned (invisibly).","code":""},{"path":"https://www.slendr.net/reference/check_env.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check that the active Python environment is setup for slendr — check_env","text":"Either TRUE (slendr Python environment present) FALSE (slendr Python environment present).","code":""},{"path":"https://www.slendr.net/reference/check_env.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Check that the active Python environment is setup for slendr — check_env","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. check_env() #> Summary of the currently active Python environment: #> #> Python binary: /Users/mp/Library/r-miniconda-arm64/envs/Python-3.12_msprime-1.3.3_tskit-0.5.8_pyslim-1.0.4_tspop-0.0.2/bin/python #> Python version: 3.12.7 | packaged by conda-forge | (main, Oct 4 2024, 15:57:01) [Clang 17.0.6 ] #> #> slendr requirements: #> - tskit: version ✓ #> - msprime: version ✓ #> - pyslim: version 1.0.4 ✓ #> - tspop: present ✓"},{"path":"https://www.slendr.net/reference/clear_env.html","id":null,"dir":"Reference","previous_headings":"","what":"Remove the automatically created slendr Python environment — clear_env","title":"Remove the automatically created slendr Python environment — clear_env","text":"Remove automatically created slendr Python environment","code":""},{"path":"https://www.slendr.net/reference/clear_env.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Remove the automatically created slendr Python environment — clear_env","text":"","code":"clear_env(force = FALSE, all = FALSE)"},{"path":"https://www.slendr.net/reference/clear_env.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Remove the automatically created slendr Python environment — clear_env","text":"force Ask deleting environment? (present past) slendr Python environments removed (default FALSE) just current environment?","code":""},{"path":"https://www.slendr.net/reference/clear_env.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Remove the automatically created slendr Python environment — clear_env","text":"return value, called side effects","code":""},{"path":"https://www.slendr.net/reference/compile_model.html","id":null,"dir":"Reference","previous_headings":"","what":"Compile a slendr demographic model — compile_model","title":"Compile a slendr demographic model — compile_model","text":"First, compiles vectorized population spatial maps series binary raster PNG files, format SLiM understands uses define population boundaries. extracts demographic model defined user (.e. population divergences gene flow events) series tables later used built-SLiM script program timing simulation events.","code":""},{"path":"https://www.slendr.net/reference/compile_model.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Compile a slendr demographic model — compile_model","text":"","code":"compile_model( populations, generation_time, gene_flow = list(), direction = NULL, simulation_length = NULL, serialize = TRUE, path = NULL, overwrite = FALSE, force = FALSE, description = \"\", time_units = NULL, resolution = NULL, competition = NULL, mating = NULL, dispersal = NULL, extension = NULL )"},{"path":"https://www.slendr.net/reference/compile_model.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Compile a slendr demographic model — compile_model","text":"populations Object(s) slendr_pop class (multiple objects need specified list) generation_time Generation time (model time units) gene_flow Gene flow events generated gene_flow function (either list data.frame objects format defined gene_flow function, single data.frame) direction Intended direction time. normal circumstances parameter inferred model need set manually. simulation_length Total length simulation (required forward time models, optional models specified backward time units default run \"present time\") serialize model files serialized disk? , R model object returned files created. speeds simulation msprime prevents using SLiM back end. path Output directory model configuration files loaded backend SLiM script. NULL, model configuration files saved temporary directory. overwrite Completely delete specified directory, case already exists, create new one? force Force deletion model directory already present? Useful non-interactive uses. interactive mode, user asked confirm deletion manually. description Optional short description model time_units Units time model event times interpreted. specified generation_time set 1, set \"generations\", otherwise value \"model time units\". resolution many distance units per pixel? competition, mating Maximum spatial competition mating choice distance dispersal Standard deviation normal distribution parent-offspring distance extension Path SLiM script used extending slendr's built-SLiM simulation engine. can either file snippet Eidos code, string containing code directly. Regardless, provided snippet appended contents bundled slendr SLiM script.","code":""},{"path":"https://www.slendr.net/reference/compile_model.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Compile a slendr demographic model — compile_model","text":"Compiled slendr_model model object encapsulates information specified model (populations involved, much gene flow occur, spatial resolution map, spatial dispersal mating parameters used SLiM simulation, applicable)","code":""},{"path":"https://www.slendr.net/reference/compile_model.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Compile a slendr demographic model — compile_model","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/distance.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate the distance between a pair of spatial boundaries — distance","title":"Calculate the distance between a pair of spatial boundaries — distance","text":"Calculate distance pair spatial boundaries","code":""},{"path":"https://www.slendr.net/reference/distance.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate the distance between a pair of spatial boundaries — distance","text":"","code":"distance(x, y, measure, time = NULL)"},{"path":"https://www.slendr.net/reference/distance.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate the distance between a pair of spatial boundaries — distance","text":"x, y Objects class slendr measure measure distance? can either 'border' (distance borders x y) 'center' (distance centroids). time Time closest spatial maps x y represent slendr_pop population boundaries (ignored general slendr_region objects)","code":""},{"path":"https://www.slendr.net/reference/distance.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate the distance between a pair of spatial boundaries — distance","text":"coordinate reference system specified, distance projected units (.e. meters) returned. Otherwise function returns normal Euclidean distance.","code":""},{"path":"https://www.slendr.net/reference/distance.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate the distance between a pair of spatial boundaries — distance","text":"","code":"# create two regions on a blank abstract landscape region_a <- region(\"A\", center = c(20, 50), radius = 20) region_b <- region(\"B\", center = c(80, 50), radius = 20) plot_map(region_a, region_b) # compute the distance between the centers of both population ranges distance(region_a, region_b, measure = \"center\") #> [1] 60 # compute the distance between the borders of both population ranges distance(region_a, region_b, measure = \"border\") #> [1] 20"},{"path":"https://www.slendr.net/reference/expand_range.html","id":null,"dir":"Reference","previous_headings":"","what":"Expand the population range — expand_range","title":"Expand the population range — expand_range","text":"Expands spatial population range specified distance given time-window","code":""},{"path":"https://www.slendr.net/reference/expand_range.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Expand the population range — expand_range","text":"","code":"expand_range( pop, by, end, start, overlap = 0.8, snapshots = NULL, polygon = NULL, lock = FALSE, verbose = TRUE )"},{"path":"https://www.slendr.net/reference/expand_range.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Expand the population range — expand_range","text":"pop Object class slendr_pop many units distance expand ? start, end expansion start/end? overlap Minimum overlap subsequent spatial boundaries snapshots number intermediate snapshots (overrides overlap parameter) polygon Geographic region restrict expansion lock Maintain density individuals. FALSE (default), number individuals population change. TRUE, number individuals simulated changed (increased decreased) appropriately, match new population range area. verbose Report progress generating intermediate spatial boundaries?","code":""},{"path":"https://www.slendr.net/reference/expand_range.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Expand the population range — expand_range","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/expand_range.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Expand the population range — expand_range","text":"Note slendr models accomodate SLiM msprime back ends, population sizes times events rounded nearest integer value.","code":""},{"path":"https://www.slendr.net/reference/expand_range.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Expand the population range — expand_range","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/explore_model.html","id":null,"dir":"Reference","previous_headings":"","what":"Open an interactive browser of the spatial model — explore_model","title":"Open an interactive browser of the spatial model — explore_model","text":"Open interactive browser spatial model","code":""},{"path":"https://www.slendr.net/reference/explore_model.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Open an interactive browser of the spatial model — explore_model","text":"","code":"explore_model(model)"},{"path":"https://www.slendr.net/reference/explore_model.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Open an interactive browser of the spatial model — explore_model","text":"model Compiled slendr_model model object","code":""},{"path":"https://www.slendr.net/reference/explore_model.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Open an interactive browser of the spatial model — explore_model","text":"return value, called order start interactive browser-based interface explore dynamics slendr model","code":""},{"path":"https://www.slendr.net/reference/extract_parameters.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","title":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","text":"function extract slendr model parameters used compile given model object simulate tree sequence","code":""},{"path":"https://www.slendr.net/reference/extract_parameters.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","text":"","code":"extract_parameters(data)"},{"path":"https://www.slendr.net/reference/extract_parameters.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","text":"data Either object class slendr_ts slendr_model","code":""},{"path":"https://www.slendr.net/reference/extract_parameters.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","text":"list data frames containing parameters model used compiling model object","code":""},{"path":"https://www.slendr.net/reference/extract_parameters.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Extract information from a compiled model or a simulated tree sequence — extract_parameters","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model and simulate a tree sequence from it model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) ts <- msprime(model, sequence_length = 1e5, recombination_rate = 0) # extract model parameters from a compiled model object as a list of data frames extract_parameters(model) #> $splits #> pop parent N time remove #> 1 CH 10 6500000 NA #> 2 AFR CH 10 6000000 NA #> 3 NEA AFR 10 600000 40000 #> 4 EUR AFR 5000 70000 NA #> #> $gene_flows #> from to start end rate #> 1 NEA EUR 55000 45000 0.03 #> # the function can also extract parameters of a model which simulated a # tree sequence extract_parameters(ts) #> $splits #> pop parent N time remove #> 1 CH 10 6500000 NA #> 2 AFR CH 10 6000000 NA #> 3 NEA AFR 10 600000 40000 #> 4 EUR AFR 5000 70000 NA #> #> $gene_flows #> from to start end rate #> 1 NEA EUR 55000 45000 0.03 #>"},{"path":"https://www.slendr.net/reference/gene_flow.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a gene-flow event between two populations — gene_flow","title":"Define a gene-flow event between two populations — gene_flow","text":"Define gene-flow event two populations","code":""},{"path":"https://www.slendr.net/reference/gene_flow.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a gene-flow event between two populations — gene_flow","text":"","code":"gene_flow(from, to, rate, start, end, overlap = TRUE)"},{"path":"https://www.slendr.net/reference/gene_flow.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a gene-flow event between two populations — gene_flow","text":", Objects class slendr_pop rate Scalar value range (0, 1] specifying proportion migration given time period start, end Start end gene-flow event overlap Require spatial overlap admixing populations? (default TRUE)","code":""},{"path":"https://www.slendr.net/reference/gene_flow.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a gene-flow event between two populations — gene_flow","text":"Object class data.frame containing parameters specified gene-flow event.","code":""},{"path":"https://www.slendr.net/reference/gene_flow.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a gene-flow event between two populations — gene_flow","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/get_env.html","id":null,"dir":"Reference","previous_headings":"","what":"Get the name of the current slendr Python environment — get_env","title":"Get the name of the current slendr Python environment — get_env","text":"Get name current slendr Python environment","code":""},{"path":"https://www.slendr.net/reference/get_env.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get the name of the current slendr Python environment — get_env","text":"","code":"get_env()"},{"path":"https://www.slendr.net/reference/get_env.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get the name of the current slendr Python environment — get_env","text":"Name slendr Python environment","code":""},{"path":"https://www.slendr.net/reference/init_env.html","id":null,"dir":"Reference","previous_headings":"","what":"Activate slendr's own dedicated Python environment — init_env","title":"Activate slendr's own dedicated Python environment — init_env","text":"function attempts activate dedicated slendr Miniconda Python environment previously set via setup_env.","code":""},{"path":"https://www.slendr.net/reference/init_env.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Activate slendr's own dedicated Python environment — init_env","text":"","code":"init_env(quiet = FALSE)"},{"path":"https://www.slendr.net/reference/init_env.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Activate slendr's own dedicated Python environment — init_env","text":"quiet informative messages printed console? Default FALSE.","code":""},{"path":"https://www.slendr.net/reference/init_env.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Activate slendr's own dedicated Python environment — init_env","text":"return value, called side effects","code":""},{"path":"https://www.slendr.net/reference/join.html","id":null,"dir":"Reference","previous_headings":"","what":"Merge two spatial slendr objects into one — join","title":"Merge two spatial slendr objects into one — join","text":"Merge two spatial slendr objects one","code":""},{"path":"https://www.slendr.net/reference/join.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Merge two spatial slendr objects into one — join","text":"","code":"join(x, y, name = NULL)"},{"path":"https://www.slendr.net/reference/join.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Merge two spatial slendr objects into one — join","text":"x Object class slendr y Object class slendr name Optional name resulting geographic region. missing, name constructed function arguments.","code":""},{"path":"https://www.slendr.net/reference/join.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Merge two spatial slendr objects into one — join","text":"Object class slendr_region encodes standard spatial object class sf several additional attributes (importantly corresponding slendr_map object, applicable).","code":""},{"path":"https://www.slendr.net/reference/join.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Merge two spatial slendr objects into one — join","text":"","code":"# create a blank abstract world 1000x1000 distance units in size blank_map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # it is possible to construct custom landscapes (islands, corridors, etc.) island1 <- region(\"island1\", polygon = list(c(10, 30), c(50, 30), c(40, 50), c(0, 40))) island2 <- region(\"island2\", polygon = list(c(60, 60), c(80, 40), c(100, 60), c(80, 80))) island3 <- region(\"island3\", center = c(20, 80), radius = 10) archipelago <- island1 %>% join(island2) %>% join(island3) custom_map <- world(xrange = c(1, 100), c(1, 100), landscape = archipelago) # real Earth landscapes can be defined using freely-available Natural Earth # project data and with the possibility to specify an appropriate Coordinate # Reference System, such as this example of a map of Europe real_map <- world(xrange = c(-15, 40), yrange = c(30, 60), crs = \"EPSG:3035\") #> Reading layer `ne_110m_land' from data source #> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpINF2s0/naturalearth/ne_110m_land.shp' #> using driver `ESRI Shapefile' #> Simple feature collection with 127 features and 3 fields #> Geometry type: POLYGON #> Dimension: XY #> Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513 #> Geodetic CRS: WGS 84"},{"path":"https://www.slendr.net/reference/move.html","id":null,"dir":"Reference","previous_headings":"","what":"Move the population to a new location in a given amount of time — move","title":"Move the population to a new location in a given amount of time — move","text":"function defines displacement population along given trajectory given time frame","code":""},{"path":"https://www.slendr.net/reference/move.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Move the population to a new location in a given amount of time — move","text":"","code":"move( pop, trajectory, end, start, overlap = 0.8, snapshots = NULL, verbose = TRUE )"},{"path":"https://www.slendr.net/reference/move.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Move the population to a new location in a given amount of time — move","text":"pop Object class slendr_pop trajectory List two-dimensional vectors (longitude, latitude) specifying migration trajectory start, end Start/end points population migration overlap Minimum overlap subsequent spatial boundaries snapshots number intermediate snapshots (overrides overlap parameter) verbose Show progress searching number sufficient snapshots?","code":""},{"path":"https://www.slendr.net/reference/move.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Move the population to a new location in a given amount of time — move","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/move.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Move the population to a new location in a given amount of time — move","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/msprime.html","id":null,"dir":"Reference","previous_headings":"","what":"Run a slendr model in msprime — msprime","title":"Run a slendr model in msprime — msprime","text":"function execute built-msprime script run compiled slendr demographic model.","code":""},{"path":"https://www.slendr.net/reference/msprime.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Run a slendr model in msprime — msprime","text":"","code":"msprime( model, sequence_length, recombination_rate, samples = NULL, random_seed = NULL, verbose = FALSE, debug = FALSE, run = TRUE, path = NULL )"},{"path":"https://www.slendr.net/reference/msprime.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Run a slendr model in msprime — msprime","text":"model Model object created compile function sequence_length Total length simulated sequence (base-pairs) recombination_rate Recombination rate simulated sequence (recombinations per basepair per generation) samples data frame times given number individuals remembered tree-sequence (see schedule_sampling function can generate sampling schedule correct format). missing, individuals present end simulation recorded final tree-sequence file. random_seed Random seed (NULL, seed generated 0 maximum integer number available) verbose Write log information SLiM run console (default FALSE)? debug Write msprime's debug log console (default FALSE)? run msprime engine run? FALSE, command line msprime command printed (returned invisibly character vector) executed. path Path directory simulation result files saved. NULL, directory automatically created temporary directory. TRUE, path also returned function. string given, assumed path directory simulation results saved. case, function return path invisibly. Note tree-sequence file simulated (along files, potentially), tree-sequence file (named 'msprime.trees' default) explicitly loaded using ts_read().","code":""},{"path":"https://www.slendr.net/reference/msprime.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Run a slendr model in msprime — msprime","text":"tree-sequence object loaded via Python-R reticulate interface function ts_read (internally represented Python object tskit.trees.TreeSequence). path argument set, return path single-element character vector.","code":""},{"path":"https://www.slendr.net/reference/msprime.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Run a slendr model in msprime — msprime","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) # afr and eur objects would normally be created before slendr model compilation, # but here we take them out of the model object already compiled for this # example (in a standard slendr simulation pipeline, this wouldn't be necessary) afr <- model$populations[[\"AFR\"]] eur <- model$populations[[\"EUR\"]] chimp <- model$populations[[\"CH\"]] # schedule the sampling of a couple of ancient and present-day individuals # given model at 20 ky, 10 ky, 5ky ago and at present-day (time 0) modern_samples <- schedule_sampling(model, times = 0, list(afr, 10), list(eur, 100), list(chimp, 1)) ancient_samples <- schedule_sampling(model, times = c(40000, 30000, 20000, 10000), list(eur, 1)) # sampling schedules are just data frames and can be merged easily samples <- rbind(modern_samples, ancient_samples) # run a simulation using the msprime back end from a compiled slendr model object ts <- msprime(model, sequence_length = 1e5, recombination_rate = 0, samples = samples) # simulated tree-sequence object can be saved to a file using ts_write()... ts_file <- normalizePath(tempfile(fileext = \".trees\"), winslash = \"/\", mustWork = FALSE) ts_write(ts, ts_file) # ... and, at a later point, loaded by ts_read() ts <- ts_read(ts_file, model) summary(ts) #> ╔═══════════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════════╣ #> ║Trees │ 1║ #> ╟───────────────┼───────────╢ #> ║Sequence Length│ 100000║ #> ╟───────────────┼───────────╢ #> ║Time Units │generations║ #> ╟───────────────┼───────────╢ #> ║Sample Nodes │ 230║ #> ╟───────────────┼───────────╢ #> ║Total Size │ 39.4 KiB║ #> ╚═══════════════╧═══════════╝ #> ╔═══════════╤════╤═════════╤════════════╗ #> ║Table │Rows│Size │Has Metadata║ #> ╠═══════════╪════╪═════════╪════════════╣ #> ║Edges │ 471│ 14.7 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Individuals│ 115│ 3.2 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Mutations │ 0│ 16 Bytes│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Nodes │ 472│ 12.9 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Populations│ 4│338 Bytes│ Yes║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Provenances│ 1│ 3.0 KiB│ No║ #> ╟───────────┼────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧════╧═════════╧════════════╝ #>"},{"path":"https://www.slendr.net/reference/overlap.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate the overlap of two slendr objects — overlap","title":"Generate the overlap of two slendr objects — overlap","text":"Generate overlap two slendr objects","code":""},{"path":"https://www.slendr.net/reference/overlap.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate the overlap of two slendr objects — overlap","text":"","code":"overlap(x, y, name = NULL)"},{"path":"https://www.slendr.net/reference/overlap.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate the overlap of two slendr objects — overlap","text":"x Object class slendr y Object class slendr name Optional name resulting geographic region. missing, name constructed function arguments.","code":""},{"path":"https://www.slendr.net/reference/overlap.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate the overlap of two slendr objects — overlap","text":"Object class slendr_region encodes standard spatial object class sf several additional attributes (importantly corresponding slendr_map object, applicable).","code":""},{"path":"https://www.slendr.net/reference/pipe.html","id":null,"dir":"Reference","previous_headings":"","what":"Pipe operator — %>%","title":"Pipe operator — %>%","text":"Pipe operator","code":""},{"path":"https://www.slendr.net/reference/pipe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Pipe operator — %>%","text":"","code":"lhs %>% rhs"},{"path":"https://www.slendr.net/reference/pipe.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Pipe operator — %>%","text":"See magrittr::%>% details.","code":""},{"path":"https://www.slendr.net/reference/plot_map.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot slendr geographic features on a map — plot_map","title":"Plot slendr geographic features on a map — plot_map","text":"Plots objects three slendr spatial classes (slendr_map, slendr_region, slendr_pop).","code":""},{"path":"https://www.slendr.net/reference/plot_map.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot slendr geographic features on a map — plot_map","text":"","code":"plot_map( ..., time = NULL, gene_flow = FALSE, graticules = \"original\", intersect = TRUE, show_map = TRUE, title = NULL, interpolated_maps = NULL )"},{"path":"https://www.slendr.net/reference/plot_map.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot slendr geographic features on a map — plot_map","text":"... Objects classes slendr_map, slendr_region, slendr_pop time Plot concrete time point gene_flow Indicate geneflow events arrow graticules Plot graticules original Coordinate Reference System (longitude-latitude), internal CRS (meters)? intersect Intersect population boundaries landscape geographic boundaries (default TRUE)? show_map Show underlying world map title Title plot interpolated_maps Interpolated spatial boundaries populations time points (used plotting using explore shiny app)","code":""},{"path":"https://www.slendr.net/reference/plot_map.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot slendr geographic features on a map — plot_map","text":"ggplot2 object visualized slendr map","code":""},{"path":"https://www.slendr.net/reference/plot_model.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot demographic history encoded in a slendr model — plot_model","title":"Plot demographic history encoded in a slendr model — plot_model","text":"Plot demographic history encoded slendr model","code":""},{"path":"https://www.slendr.net/reference/plot_model.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot demographic history encoded in a slendr model — plot_model","text":"","code":"plot_model( model, sizes = TRUE, proportions = FALSE, gene_flow = TRUE, log = FALSE, order = NULL, file = NULL, samples = NULL, ... )"},{"path":"https://www.slendr.net/reference/plot_model.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot demographic history encoded in a slendr model — plot_model","text":"model Compiled slendr_model model object sizes population size changes visualized? proportions gene flow proportions visualized (FALSE default prevent cluttering overplotting) gene_flow gene-flow arrows visualized (default TRUE). log y-axis plotted log scale? Useful models long time-scales. order Order populations along x-axis, given character vector population names. NULL (default), default plotting algorithm used, ordering populations ancestral recent using -order tree traversal. file Output file figure saved via ggsave samples Sampling schedule visualized model ... Optional argument passed ggsave","code":""},{"path":"https://www.slendr.net/reference/plot_model.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot demographic history encoded in a slendr model — plot_model","text":"ggplot2 object visualized slendr model","code":""},{"path":"https://www.slendr.net/reference/plot_model.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Plot demographic history encoded in a slendr model — plot_model","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model with an already simulated tree sequence path <- system.file(\"extdata/models/introgression\", package = \"slendr\") model <- read_model(path) plot_model(model, sizes = FALSE, log = TRUE)"},{"path":"https://www.slendr.net/reference/population.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a population — population","title":"Define a population — population","text":"Defines parameters population (non-spatial spatial).","code":""},{"path":"https://www.slendr.net/reference/population.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a population — population","text":"","code":"population( name, time, N, parent = NULL, map = FALSE, center = NULL, radius = NULL, polygon = NULL, remove = NULL, intersect = TRUE, competition = NA, mating = NA, dispersal = NA, dispersal_fun = NULL, aquatic = FALSE )"},{"path":"https://www.slendr.net/reference/population.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a population — population","text":"name Name population time Time population's first appearance N Number individuals time first appearance parent Parent population object NULL (indicates population ancestor, first population \"lineage\") map Object type slendr_map defines world context (created using world function). value FALSE provided, non-spatial model run. center Two-dimensional vector specifying center circular range radius Radius circular range polygon List vector pairs, defining corners polygon range geographic region class slendr_region polygon coordinates extracted (see region() function) remove Time population removed intersect Intersect population's boundaries landscape features? competition, mating Maximum spatial competition mating choice distance dispersal Standard deviation normal distribution distance offspring disperses parent dispersal_fun Distribution function governing dispersal offspring. One \"normal\", \"uniform\", \"cauchy\", \"exponential\", \"brownian\" (vertical horizontal displacements drawn normal distribution independently). aquatic species aquatic (FALSE default, .e. terrestrial species)?","code":""},{"path":"https://www.slendr.net/reference/population.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a population — population","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/population.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Define a population — population","text":"four ways specify spatial boundary: ) circular range specified using center coordinate radius, ii) polygon specified list two-dimensional vector coordinates, iii) polygon ii), defined (named) using region function, iv) just world map specified (circular polygon range parameters set default NULL value), population allowed occupy entire landscape. Note slendr models accomodate SLiM msprime back ends, population sizes split times rounded nearest integer value.","code":""},{"path":"https://www.slendr.net/reference/population.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a population — population","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/print.slendr_pop.html","id":null,"dir":"Reference","previous_headings":"","what":"Print a short summary of a slendr object — print.slendr_pop","title":"Print a short summary of a slendr object — print.slendr_pop","text":"spatial objects slendr package internally represented Simple Features (sf) objects. fact hidden circumstances , goal slendr package provide functionality much higher level (population boundaries, geographic regions, instead individual polygons \"low-level\" geometric objects), without users worry low-level details involved handling spatial geometries. However, full sf object representation can always printed calling x[].","code":""},{"path":"https://www.slendr.net/reference/print.slendr_pop.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Print a short summary of a slendr object — print.slendr_pop","text":"","code":"# S3 method for slendr_pop print(x, ...) # S3 method for slendr_region print(x, ...) # S3 method for slendr_map print(x, ...) # S3 method for slendr_model print(x, ...)"},{"path":"https://www.slendr.net/reference/print.slendr_pop.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Print a short summary of a slendr object — print.slendr_pop","text":"x Object class slendr (either slendr_pop, slendr_map, slendr_region, slendr_table) ... Additional arguments passed print","code":""},{"path":"https://www.slendr.net/reference/print.slendr_pop.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Print a short summary of a slendr object — print.slendr_pop","text":"return value, used printing","code":""},{"path":"https://www.slendr.net/reference/print.slendr_ts.html","id":null,"dir":"Reference","previous_headings":"","what":"Print tskit's summary table of the Python tree-sequence object — print.slendr_ts","title":"Print tskit's summary table of the Python tree-sequence object — print.slendr_ts","text":"Print tskit's summary table Python tree-sequence object","code":""},{"path":"https://www.slendr.net/reference/print.slendr_ts.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Print tskit's summary table of the Python tree-sequence object — print.slendr_ts","text":"","code":"# S3 method for slendr_ts print(x, ...)"},{"path":"https://www.slendr.net/reference/print.slendr_ts.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Print tskit's summary table of the Python tree-sequence object — print.slendr_ts","text":"x Tree object class slendr_phylo ... Additional arguments normally passed print (used case)","code":""},{"path":"https://www.slendr.net/reference/print.slendr_ts.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Print tskit's summary table of the Python tree-sequence object — print.slendr_ts","text":"return value, simply prints tskit summary table terminal","code":""},{"path":"https://www.slendr.net/reference/read_model.html","id":null,"dir":"Reference","previous_headings":"","what":"Read a previously serialized model configuration — read_model","title":"Read a previously serialized model configuration — read_model","text":"Reads configuration tables model data location previously compiled compile function.","code":""},{"path":"https://www.slendr.net/reference/read_model.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Read a previously serialized model configuration — read_model","text":"","code":"read_model(path)"},{"path":"https://www.slendr.net/reference/read_model.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Read a previously serialized model configuration — read_model","text":"path Directory required configuration files","code":""},{"path":"https://www.slendr.net/reference/read_model.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Read a previously serialized model configuration — read_model","text":"Compiled slendr_model model object encapsulates information specified model (populations involved, much gene flow occur, spatial resolution map, spatial dispersal mating parameters used SLiM simulation, applicable)","code":""},{"path":"https://www.slendr.net/reference/read_model.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Read a previously serialized model configuration — read_model","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model with an already simulated tree sequence path <- system.file(\"extdata/models/introgression\", package = \"slendr\") model <- read_model(path) plot_model(model, sizes = FALSE, log = TRUE)"},{"path":"https://www.slendr.net/reference/region.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a geographic region — region","title":"Define a geographic region — region","text":"Creates geographic region (polygon) given map gives name. can used define objects can reused multiple places slendr script (region arguments population) without repeatedly define polygon coordinates.","code":""},{"path":"https://www.slendr.net/reference/region.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a geographic region — region","text":"","code":"region(name = NULL, map = NULL, center = NULL, radius = NULL, polygon = NULL)"},{"path":"https://www.slendr.net/reference/region.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a geographic region — region","text":"name Name geographic region map Object type sf defines map center Two-dimensional vector specifying center circular range radius Radius circular range polygon List vector pairs, defining corners polygon range geographic region class slendr_region polygon coordinates extracted (see region() function)","code":""},{"path":"https://www.slendr.net/reference/region.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a geographic region — region","text":"Object class slendr_region encodes standard spatial object class sf several additional attributes (importantly corresponding slendr_map object, applicable).","code":""},{"path":"https://www.slendr.net/reference/region.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a geographic region — region","text":"","code":"# create a blank abstract world 1000x1000 distance units in size blank_map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # it is possible to construct custom landscapes (islands, corridors, etc.) island1 <- region(\"island1\", polygon = list(c(10, 30), c(50, 30), c(40, 50), c(0, 40))) island2 <- region(\"island2\", polygon = list(c(60, 60), c(80, 40), c(100, 60), c(80, 80))) island3 <- region(\"island3\", center = c(20, 80), radius = 10) archipelago <- island1 %>% join(island2) %>% join(island3) custom_map <- world(xrange = c(1, 100), c(1, 100), landscape = archipelago) # real Earth landscapes can be defined using freely-available Natural Earth # project data and with the possibility to specify an appropriate Coordinate # Reference System, such as this example of a map of Europe real_map <- world(xrange = c(-15, 40), yrange = c(30, 60), crs = \"EPSG:3035\") #> Reading layer `ne_110m_land' from data source #> `/private/var/folders/70/b_q2zdh116b9pfg29p03sx600000gn/T/RtmpINF2s0/naturalearth/ne_110m_land.shp' #> using driver `ESRI Shapefile' #> Simple feature collection with 127 features and 3 fields #> Geometry type: POLYGON #> Dimension: XY #> Bounding box: xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513 #> Geodetic CRS: WGS 84"},{"path":"https://www.slendr.net/reference/reproject.html","id":null,"dir":"Reference","previous_headings":"","what":"Reproject coordinates between coordinate systems — reproject","title":"Reproject coordinates between coordinate systems — reproject","text":"Converts coordinates compiled raster map (.e. pixel units) different Geographic Coordinate Systems (CRS).","code":""},{"path":"https://www.slendr.net/reference/reproject.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Reproject coordinates between coordinate systems — reproject","text":"","code":"reproject( from, to, x = NULL, y = NULL, coords = NULL, model = NULL, add = FALSE, input_prefix = \"\", output_prefix = \"new\" )"},{"path":"https://www.slendr.net/reference/reproject.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Reproject coordinates between coordinate systems — reproject","text":", Either CRS code accepted GDAL, valid integer EPSG value, object class crs, value \"raster\" (converting /pixel coordinates), \"world\" (converting /whatever CRS set underlying map) x, y Coordinates two dimensions (missing, coordinates expected data.frame specified coords parameter columns \"x\" \"y\") coords data.frame-like object coordinates columns \"x\" \"y\" model Object class slendr_model add Add column coordinates input data.frame coords (coordinates otherwise returned separate object)? input_prefix, output_prefix Input output prefixes data frame columns spatial coordinates","code":""},{"path":"https://www.slendr.net/reference/reproject.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Reproject coordinates between coordinate systems — reproject","text":"Data.frame converted two-dimensional coordinates given input","code":""},{"path":"https://www.slendr.net/reference/reproject.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Reproject coordinates between coordinate systems — reproject","text":"","code":"lon_lat_df <- data.frame(x = c(30, 0, 15), y = c(60, 40, 10)) reproject( from = \"epsg:4326\", to = \"epsg:3035\", coords = lon_lat_df, add = TRUE # add converted [lon,lat] coordinates as a new column ) #> # A tibble: 3 × 4 #> x y newx newy #> #> 1 30 60 5422493. 4256803. #> 2 0 40 3465349. 1934879. #> 3 15 10 4907297. -1328914."},{"path":"https://www.slendr.net/reference/resize.html","id":null,"dir":"Reference","previous_headings":"","what":"Change the population size — resize","title":"Change the population size — resize","text":"Resizes population starting current value N individuals specified value","code":""},{"path":"https://www.slendr.net/reference/resize.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Change the population size — resize","text":"","code":"resize(pop, N, how, time, end = NULL)"},{"path":"https://www.slendr.net/reference/resize.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Change the population size — resize","text":"pop Object class slendr_pop N Population size change change population size (options \"step\" \"exponential\") time Time population size change end End population size change period (used exponential change events)","code":""},{"path":"https://www.slendr.net/reference/resize.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Change the population size — resize","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/resize.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Change the population size — resize","text":"case exponential size change, final N larger current size, population exponentially growing specified time period reaches N individuals. N smaller, population shrink exponentially. Note slendr models accomodate SLiM msprime back ends, population sizes split times rounded nearest integer value.","code":""},{"path":"https://www.slendr.net/reference/resize.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Change the population size — resize","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/schedule_sampling.html","id":null,"dir":"Reference","previous_headings":"","what":"Define sampling events for a given set of populations — schedule_sampling","title":"Define sampling events for a given set of populations — schedule_sampling","text":"Schedule sampling events specified times , optionally, given set locations landscape","code":""},{"path":"https://www.slendr.net/reference/schedule_sampling.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define sampling events for a given set of populations — schedule_sampling","text":"","code":"schedule_sampling(model, times, ..., locations = NULL, strict = FALSE)"},{"path":"https://www.slendr.net/reference/schedule_sampling.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define sampling events for a given set of populations — schedule_sampling","text":"model Object class slendr_model times Integer vector times (model time units) schedule remembering individuals tree-sequence ... Lists two elements (slendr_pop population object- The interface to all required Python modules has been activated. # load an example model with an already simulated tree sequence path <- system.file(\"extdata/models/introgression\", package = \"slendr\") model <- read_model(path) # afr and eur objects would normally be created before slendr model compilation, # but here we take them out of the model object already compiled for this # example (in a standard slendr simulation pipeline, this wouldn't be necessary) afr <- model$populations[[\"AFR\"]] eur <- model$populations[[\"EUR\"]] # schedule the recording of 10 African and 100 European individuals from a # given model at 20 ky, 10 ky, 5ky ago and at present-day (time 0) schedule <- schedule_sampling( model, times = c(20000, 10000, 5000, 0), list(afr, 10), list(eur, 100) ) # the result of `schedule_sampling` is a simple data frame (note that the locations # of sampling locations have `NA` values because the model is non-spatial) schedule #> # A tibble: 8 × 7 #> time pop n y_orig x_orig y x #> #> 1 0 AFR 10 NA NA NA NA #> 2 0 EUR 100 NA NA NA NA #> 3 5000 AFR 10 NA NA NA NA #> 4 5000 EUR 100 NA NA NA NA #> 5 10000 AFR 10 NA NA NA NA #> 6 10000 EUR 100 NA NA NA NA #> 7 20000 AFR 10 NA NA NA NA #> 8 20000 EUR 100 NA NA NA NA"},{"path":"https://www.slendr.net/reference/set_dispersal.html","id":null,"dir":"Reference","previous_headings":"","what":"Change dispersal parameters — set_dispersal","title":"Change dispersal parameters — set_dispersal","text":"Changes either competition interactive distance, mating choice distance, dispersal offspring parent","code":""},{"path":"https://www.slendr.net/reference/set_dispersal.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Change dispersal parameters — set_dispersal","text":"","code":"set_dispersal( pop, time, competition = NA, mating = NA, dispersal = NA, dispersal_fun = NULL )"},{"path":"https://www.slendr.net/reference/set_dispersal.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Change dispersal parameters — set_dispersal","text":"pop Object class slendr_pop time Time population size change competition, mating Maximum spatial competition mating choice distance dispersal Standard deviation normal distribution distance offspring disperses parent dispersal_fun Distribution function governing dispersal offspring. One \"normal\", \"uniform\", \"cauchy\", \"exponential\", \"brownian\" (vertical horizontal displacements drawn normal distribution independently).","code":""},{"path":"https://www.slendr.net/reference/set_dispersal.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Change dispersal parameters — set_dispersal","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/set_dispersal.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Change dispersal parameters — set_dispersal","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/set_range.html","id":null,"dir":"Reference","previous_headings":"","what":"Update the population range — set_range","title":"Update the population range — set_range","text":"function allows manual control spatial map changes addition expand move functions","code":""},{"path":"https://www.slendr.net/reference/set_range.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Update the population range — set_range","text":"","code":"set_range( pop, time, center = NULL, radius = NULL, polygon = NULL, lock = FALSE )"},{"path":"https://www.slendr.net/reference/set_range.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Update the population range — set_range","text":"pop Object class slendr_pop time Time change center Two-dimensional vector specifying center circular range radius Radius circular range polygon List vector pairs, defining corners polygon range (see also region argument) geographic region class slendr_region polygon coordinates extracted lock Maintain density individuals. FALSE (default), number individuals population change. TRUE, number individuals simulated changed (increased decreased) appropriately, match new population range area.","code":""},{"path":"https://www.slendr.net/reference/set_range.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Update the population range — set_range","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/set_range.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Update the population range — set_range","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/setup_env.html","id":null,"dir":"Reference","previous_headings":"","what":"Setup a dedicated Python virtual environment for slendr — setup_env","title":"Setup a dedicated Python virtual environment for slendr — setup_env","text":"function automatically download Python miniconda distribution dedicated R-Python interface. also create slendr-specific Python environment required Python dependencies.","code":""},{"path":"https://www.slendr.net/reference/setup_env.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Setup a dedicated Python virtual environment for slendr — setup_env","text":"","code":"setup_env(quiet = FALSE, agree = FALSE, pip = FALSE)"},{"path":"https://www.slendr.net/reference/setup_env.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Setup a dedicated Python virtual environment for slendr — setup_env","text":"quiet informative messages printed console? Default FALSE. agree Automatically agree questions? pip pip used instead conda installing slendr's Python dependencies?","code":""},{"path":"https://www.slendr.net/reference/setup_env.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Setup a dedicated Python virtual environment for slendr — setup_env","text":"return value, called side effects","code":""},{"path":"https://www.slendr.net/reference/shrink_range.html","id":null,"dir":"Reference","previous_headings":"","what":"Shrink the population range — shrink_range","title":"Shrink the population range — shrink_range","text":"Shrinks spatial population range specified distance given time-window","code":""},{"path":"https://www.slendr.net/reference/shrink_range.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Shrink the population range — shrink_range","text":"","code":"shrink_range( pop, by, end, start, overlap = 0.8, snapshots = NULL, lock = FALSE, verbose = TRUE )"},{"path":"https://www.slendr.net/reference/shrink_range.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Shrink the population range — shrink_range","text":"pop Object class slendr_pop many units distance shrink ? start, end boundary shrinking start/end? overlap Minimum overlap subsequent spatial boundaries snapshots number intermediate snapshots (overrides overlap parameter) lock Maintain density individuals. FALSE (default), number individuals population change. TRUE, number individuals simulated changed (increased decreased) appropriately, match new population range area. verbose Report progress generating intermediate spatial boundaries?","code":""},{"path":"https://www.slendr.net/reference/shrink_range.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Shrink the population range — shrink_range","text":"Object class slendr_pop, contains population parameters name, time appearance simulation, parent population (), spatial parameters map spatial boundary.","code":""},{"path":"https://www.slendr.net/reference/shrink_range.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Shrink the population range — shrink_range","text":"Note slendr models accomodate SLiM msprime back ends, population sizes split times rounded nearest integer value.","code":""},{"path":"https://www.slendr.net/reference/shrink_range.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Shrink the population range — shrink_range","text":"","code":"# spatial definitions ----------------------------------------------------- # create a blank abstract world 1000x1000 distance units in size map <- world(xrange = c(0, 1000), yrange = c(0, 1000), landscape = \"blank\") # create a circular population with the center of a population boundary at # [200, 800] and a radius of 100 distance units, 1000 individuals at time 1 # occupying a map just specified pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) # printing a population object to a console shows a brief summary pop1 #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 1 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) # create another population occupying a polygon range, splitting from pop1 # at a given time point (note that specifying a map is not necessary because # it is \"inherited\" from the parent) pop2 <- population(\"pop2\", N = 100, time = 50, parent = pop1, polygon = list(c(100, 100), c(320, 30), c(500, 200), c(500, 400), c(300, 450), c(100, 400))) pop3 <- population(\"pop3\", N = 200, time = 80, parent = pop2, center = c(800, 800), radius = 200) # move \"pop1\" to another location along a specified trajectory and saved the # resulting object to the same variable (the number of intermediate spatial # snapshots can be also determined automatically by leaving out the # `snapshots = ` argument) pop1_moved <- move(pop1, start = 100, end = 200, snapshots = 6, trajectory = list(c(600, 820), c(800, 400), c(800, 150))) pop1_moved #> slendr 'population' object #> -------------------------- #> name: pop1 #> habitat: terrestrial #> #> number of spatial maps: 10 #> map: abstract spatial landscape with custom features #> stays until the end of the simulation #> #> population history overview: #> - time 1: created as an ancestral population (N = 1000) #> - time 100-200: movement across a landscape # many slendr functions are pipe-friendly, making it possible to construct # pipelines which construct entire history of a population pop1 <- population(\"pop1\", N = 1000, time = 1, map = map, center = c(200, 800), radius = 100) %>% move(start = 100, end = 200, snapshots = 6, trajectory = list(c(400, 800), c(600, 700), c(800, 400), c(800, 150))) %>% set_range(time = 300, polygon = list( c(400, 0), c(1000, 0), c(1000, 600), c(900, 400), c(800, 250), c(600, 100), c(500, 50)) ) # population ranges can expand by a given distance in all directions pop2 <- expand_range(pop2, by = 200, start = 50, end = 150, snapshots = 3) # we can check the positions of all populations interactively by plotting their # ranges together on a single map plot_map(pop1, pop2, pop3) # gene flow events -------------------------------------------------------- # individual gene flow events can be saved to a list gf <- list( gene_flow(from = pop1, to = pop3, start = 150, end = 200, rate = 0.15), gene_flow(from = pop1, to = pop2, start = 300, end = 330, rate = 0.25) ) # compilation ------------------------------------------------------------- # compile model components in a serialized form to dist, returning a single # slendr model object (in practice, the resolution should be smaller) model <- compile_model( populations = list(pop1, pop2, pop3), generation_time = 1, resolution = 100, simulation_length = 500, competition = 5, mating = 5, dispersal = 1 )"},{"path":"https://www.slendr.net/reference/slendr.html","id":null,"dir":"Reference","previous_headings":"","what":"A Simulation Framework for Spatiotemporal Population Genetics — slendr","title":"A Simulation Framework for Spatiotemporal Population Genetics — slendr","text":"framework simulating spatially explicit genomic data leverages real cartographic information programmatic visual encoding spatiotemporal population dynamics real geographic landscapes. Population genetic models automatically executed 'SLiM' software behind scenes, using custom built-simulation 'SLiM' script. Additionally, fully abstract spatial models tied specific geographic location supported, users can also simulate data standard, non-spatial, random-mating models. can simulated either 'SLiM' built-back-end script, using efficient coalescent population genetics simulator 'msprime' custom-built 'Python' script bundled R package. Simulated genomic data saved tree-sequence format can loaded, manipulated, summarised using tree-sequence functionality via R interface 'Python' module 'tskit'. Complete model configuration, simulation analysis pipelines can therefore constructed without need leave R environment, eliminating friction disparate tools population genetic simulations data analysis.","code":""},{"path":"https://www.slendr.net/reference/slendr.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"A Simulation Framework for Spatiotemporal Population Genetics — slendr","text":"can find installation instructions, reference manual, tutorials https://www.slendr.net.","code":""},{"path":[]},{"path":"https://www.slendr.net/reference/slendr.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"A Simulation Framework for Spatiotemporal Population Genetics — slendr","text":"Maintainer: Martin Petr contact@bodkan.net (ORCID)","code":""},{"path":"https://www.slendr.net/reference/slim.html","id":null,"dir":"Reference","previous_headings":"","what":"Run a slendr model in SLiM — slim","title":"Run a slendr model in SLiM — slim","text":"function execute SLiM script generated compile function compilation slendr demographic model.","code":""},{"path":"https://www.slendr.net/reference/slim.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Run a slendr model in SLiM — slim","text":"","code":"slim( model, sequence_length, recombination_rate, samples = NULL, ts = TRUE, path = NULL, random_seed = NULL, method = c(\"batch\", \"gui\"), verbose = FALSE, run = TRUE, slim_path = NULL, burnin = 0, max_attempts = 1, spatial = !is.null(model$world), coalescent_only = TRUE, locations = NULL )"},{"path":"https://www.slendr.net/reference/slim.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Run a slendr model in SLiM — slim","text":"model Model object created compile function sequence_length Total length simulated sequence (base-pairs) recombination_rate Recombination rate simulated sequence (recombinations per basepair per generation) samples data frame times given number individuals remembered tree-sequence (see schedule_sampling function can generate sampling schedule correct format). missing, individuals present end simulation recorded final tree-sequence file. ts tree sequence simulated model? path Path directory simulation result files saved. NULL, directory automatically created temporary directory. TRUE, path also returned function. string given, assumed path directory simulation results saved. case, function return path invisibly. Note tree-sequence file simulated (along files, potentially), tree-sequence file (named 'slim.trees' default) explicitly loaded using ts_read(). random_seed Random seed (NULL, seed generated 0 maximum integer number available) method run script? (\"gui\" - open SLiMgui, \"batch\" - run command line) verbose Write log information SLiM run console (default FALSE)? run SLiM engine run? FALSE, command line SLiM command printed (returned invisibly character vector) executed. slim_path Path appropriate SLiM binary (useful slim binary $PATH). Note argument must specified function run Windows. burnin Length burnin (model's time units, .e. years) max_attempts many attempts made place offspring near one parents? Serves prevent infinite loops SLiM backend. Default value 1. spatial model executed spatial mode? default, world map specified model definition, simulation proceed spatial mode. coalescent_only initializeTreeSeq(retainCoalescentOnly = <...>) set TRUE (default) FALSE? See \"retainCoalescentOnly\" SLiM manual detail. locations NULL, locations saved. Otherwise, path file locations individual throughout simulation saved (likely use animate_model).","code":""},{"path":"https://www.slendr.net/reference/slim.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Run a slendr model in SLiM — slim","text":"tree-sequence object loaded via Python-R reticulate interface function ts_read (internally represented Python object tskit.trees.TreeSequence). path argument set, specifying directory results saved, function return path single-element character vector.","code":""},{"path":"https://www.slendr.net/reference/slim.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Run a slendr model in SLiM — slim","text":"arguments sequence_length recombination_rate can omitted slendr models utilizing customized initialization genomic architecture. cases, users may either provide hard-coded values directly SLiM's initializeGenomicElement() initializeRecombinationRate() functions utilize slendr's templating functionality provided substitute() function. ts = TRUE, returning value function depends whether path argument set. user provide path output files saved, path returned (invisibly). mostly intended support simulations customized user models. path set user, assumed tree-sequence object desired sole return value function (ts = TRUE) automatically loaded simulation finishes, (ts = FALSE) customized files produced simulation, user loading files (path needed).","code":""},{"path":"https://www.slendr.net/reference/slim.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Run a slendr model in SLiM — slim","text":"","code":"check_dependencies(python = TRUE, slim = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) # afr and eur objects would normally be created before slendr model compilation, # but here we take them out of the model object already compiled for this # example (in a standard slendr simulation pipeline, this wouldn't be necessary) afr <- model$populations[[\"AFR\"]] eur <- model$populations[[\"EUR\"]] chimp <- model$populations[[\"CH\"]] # schedule the sampling of a couple of ancient and present-day individuals # given model at 20 ky, 10 ky, 5ky ago and at present-day (time 0) modern_samples <- schedule_sampling(model, times = 0, list(afr, 5), list(eur, 5), list(chimp, 1)) ancient_samples <- schedule_sampling(model, times = c(30000, 20000, 10000), list(eur, 1)) # sampling schedules are just data frames and can be merged easily samples <- rbind(modern_samples, ancient_samples) # run a simulation using the SLiM back end from a compiled slendr model object and return # a tree-sequence object as a result ts <- slim(model, sequence_length = 1e5, recombination_rate = 0, samples = samples) # simulated tree-sequence object can be saved to a file using ts_write()... ts_file <- normalizePath(tempfile(fileext = \".trees\"), winslash = \"/\", mustWork = FALSE) ts_write(ts, ts_file) # ... and, at a later point, loaded by ts_read() ts <- ts_read(ts_file, model) ts #> ╔═══════════════════════╗ #> ║TreeSequence ║ #> ╠═══════════════╤═══════╣ #> ║Trees │ 1║ #> ╟───────────────┼───────╢ #> ║Sequence Length│ 100000║ #> ╟───────────────┼───────╢ #> ║Time Units │ ticks║ #> ╟───────────────┼───────╢ #> ║Sample Nodes │ 10046║ #> ╟───────────────┼───────╢ #> ║Total Size │2.6 MiB║ #> ╚═══════════════╧═══════╝ #> ╔═══════════╤═════╤═════════╤════════════╗ #> ║Table │Rows │Size │Has Metadata║ #> ╠═══════════╪═════╪═════════╪════════════╣ #> ║Edges │18361│573.8 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Individuals│12858│ 1.2 MiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Migrations │ 0│ 8 Bytes│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Mutations │ 0│ 1.2 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Nodes │18362│682.1 KiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Populations│ 4│ 2.5 KiB│ Yes║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Provenances│ 1│ 41.8 KiB│ No║ #> ╟───────────┼─────┼─────────┼────────────╢ #> ║Sites │ 0│ 16 Bytes│ No║ #> ╚═══════════╧═════╧═════════╧════════════╝ #>"},{"path":"https://www.slendr.net/reference/substitute.html","id":null,"dir":"Reference","previous_headings":"","what":"Substitute values of parameters in a given slendr/SLiM extension — substitute","title":"Substitute values of parameters in a given slendr/SLiM extension — substitute","text":"Substitute values parameters given slendr/SLiM extension","code":""},{"path":"https://www.slendr.net/reference/substitute.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Substitute values of parameters in a given slendr/SLiM extension — substitute","text":"","code":"substitute(template, ...)"},{"path":"https://www.slendr.net/reference/substitute.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Substitute values of parameters in a given slendr/SLiM extension — substitute","text":"template Either path extension script file, string containing entire SLiM extension code ... Named function arguments interpreted key=value pairs used argument substitution","code":""},{"path":"https://www.slendr.net/reference/substitute.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Substitute values of parameters in a given slendr/SLiM extension — substitute","text":"Path file saved extension script containing substituted values","code":""},{"path":"https://www.slendr.net/reference/substitute_values.html","id":null,"dir":"Reference","previous_headings":"","what":"Substitute values of parameters in a SLiM extension template — substitute_values","title":"Substitute values of parameters in a SLiM extension template — substitute_values","text":"Substitute values templated {{parameters}} given SLiM extension template","code":""},{"path":"https://www.slendr.net/reference/substitute_values.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Substitute values of parameters in a SLiM extension template — substitute_values","text":"","code":"substitute_values(template, ...)"},{"path":"https://www.slendr.net/reference/substitute_values.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Substitute values of parameters in a SLiM extension template — substitute_values","text":"template Either path extension script file, string containing entire SLiM extension code ... Named function arguments interpreted key=value pairs used argument substitution","code":""},{"path":"https://www.slendr.net/reference/substitute_values.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Substitute values of parameters in a SLiM extension template — substitute_values","text":"Path file saved extension script containing substituted values","code":""},{"path":"https://www.slendr.net/reference/substitute_values.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Substitute values of parameters in a SLiM extension template — substitute_values","text":"file multi-line string given template contains parameters specified {{param}} \"param\" can arbitrary variable name, function substitutes templated {{parameter}} given values. modified template used extend built-slendr SLiM script, allowing customization default behavior (commonly replacing assumption neutrality non-neutral scenarios, simulations natural selection).","code":""},{"path":"https://www.slendr.net/reference/subtract.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate the difference between two slendr objects — subtract","title":"Generate the difference between two slendr objects — subtract","text":"Generate difference two slendr objects","code":""},{"path":"https://www.slendr.net/reference/subtract.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate the difference between two slendr objects — subtract","text":"","code":"subtract(x, y, name = NULL)"},{"path":"https://www.slendr.net/reference/subtract.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate the difference between two slendr objects — subtract","text":"x Object class slendr y Object class slendr name Optional name resulting geographic region. missing, name constructed function arguments.","code":""},{"path":"https://www.slendr.net/reference/subtract.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate the difference between two slendr objects — subtract","text":"Object class slendr_region encodes standard spatial object class sf several additional attributes (importantly corresponding slendr_map object, applicable).","code":""},{"path":"https://www.slendr.net/reference/summary.slendr_nodes.html","id":null,"dir":"Reference","previous_headings":"","what":"Summarise the contents of a ts_nodes result — summary.slendr_nodes","title":"Summarise the contents of a ts_nodes result — summary.slendr_nodes","text":"Summarise contents ts_nodes result","code":""},{"path":"https://www.slendr.net/reference/summary.slendr_nodes.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Summarise the contents of a ts_nodes result — summary.slendr_nodes","text":"","code":"# S3 method for slendr_nodes summary(object, ...)"},{"path":"https://www.slendr.net/reference/summary.slendr_nodes.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Summarise the contents of a ts_nodes result — summary.slendr_nodes","text":"object Data frame produced function ts_nodes ... Additional formal arguments summary method (unused )","code":""},{"path":"https://www.slendr.net/reference/summary.slendr_nodes.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Summarise the contents of a ts_nodes result — summary.slendr_nodes","text":"Used output terminal","code":""},{"path":"https://www.slendr.net/reference/ts_afs.html","id":null,"dir":"Reference","previous_headings":"","what":"Compute the allele frequency spectrum (AFS) — ts_afs","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"function computes AFS respect given set individuals nodes.","code":""},{"path":"https://www.slendr.net/reference/ts_afs.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"","code":"ts_afs( ts, sample_sets = NULL, mode = c(\"site\", \"branch\", \"node\"), windows = NULL, span_normalise = FALSE, polarised = TRUE )"},{"path":"https://www.slendr.net/reference/ts_afs.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"ts Tree sequence object class slendr_ts sample_sets list (optionally named list) character vectors individual names (one vector per set). NULL, allele frequency spectrum individuals tree sequence computed. mode mode calculation (\"sites\" \"branch\") windows Coordinates breakpoints windows. first coordinate (0) last coordinate (equal ts$sequence_length) added automatically) span_normalise Argument passed tskit's allele_frequency_spectrum method polarised TRUE (default) allele frequency spectrum folded (.e. counts assume knowledge allele ancestral, derived, known simulation)","code":""},{"path":"https://www.slendr.net/reference/ts_afs.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"Allele frequency spectrum values given sample set. Note contents first last elements AFS might surprise . Read links description detail tskit handles things.","code":""},{"path":"https://www.slendr.net/reference/ts_afs.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"information format result dimensions, particular interpretation first last element AFS (complete = TRUE), please see tskit manual https://tskit.dev/tskit/docs/stable/python-api.html example section dedicated AFS https://tskit.dev/tutorials/analysing_tree_sequences.html#allele-frequency-spectra.","code":""},{"path":"https://www.slendr.net/reference/ts_afs.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Compute the allele frequency spectrum (AFS) — ts_afs","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model with an already simulated tree sequence slendr_ts <- system.file(\"extdata/models/introgression_slim.trees\", package = \"slendr\") model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) # load the tree-sequence object from disk ts <- ts_read(slendr_ts, model) %>% ts_mutate(mutation_rate = 1e-8, random_seed = 42) samples <- ts_samples(ts) %>% .[.$pop %in% c(\"AFR\", \"EUR\"), ] # compute AFS for the given set of individuals ts_afs(ts, sample_sets = list(samples$name)) #> [1] 1018 73 21 7 0 6 2 0 0 0 13 0 0 0 0 #> [16] 4 0 4 6 16 955"},{"path":"https://www.slendr.net/reference/ts_ancestors.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","title":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","text":"Extract (spatio-)temporal ancestral history given nodes/individuals","code":""},{"path":"https://www.slendr.net/reference/ts_ancestors.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","text":"","code":"ts_ancestors(ts, x, verbose = FALSE, complete = TRUE)"},{"path":"https://www.slendr.net/reference/ts_ancestors.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","text":"ts Tree sequence object class slendr_ts x Either individual name integer node ID verbose Report progress ancestry path generation? complete every individual tree sequence need complete metadata recorded? TRUE, individuals/nodes complete metadata included reconstruction ancestral relationships. instance, nodes added coalescent recapitation phase included spatial information associated .","code":""},{"path":"https://www.slendr.net/reference/ts_ancestors.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","text":"table ancestral nodes given tree-sequence node way root tree sequence","code":""},{"path":"https://www.slendr.net/reference/ts_ancestors.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Extract (spatio-)temporal ancestral history for given nodes/individuals — ts_ancestors","text":"","code":"check_dependencies(python = TRUE, quit = TRUE) # dependencies must be present init_env() #> The interface to all required Python modules has been activated. # load an example model with an already simulated tree sequence slendr_ts <- system.file(\"extdata/models/introgression_slim.trees\", package = \"slendr\") model <- read_model(path = system.file(\"extdata/models/introgression\", package = \"slendr\")) # load the tree-sequence object from disk ts <- ts_read(slendr_ts, model) # find the complete ancestry information for a given individual ts_ancestors(ts, \"EUR_1\", verbose = TRUE) #> Collecting ancestors of EUR_1 [1/1]... #> #> Generating data about spatial relationships of nodes... #> # A tibble: 207 × 12 #> name pop node_id level child_id parent_id child_time parent_time child_pop #>