├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ └── check-bioc.yml ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── AllClasses.R ├── AllGenerics.R ├── AllHelperFunctions.R ├── branchID.R ├── conversion.R ├── embedCurves.R ├── getCurves.R ├── getLineages.R ├── plotting.R ├── predict.R └── slingshot.R ├── README.md ├── codecov.yml ├── data-raw └── slingshotExample.R ├── data └── slingshotExample.rda ├── inst ├── CITATION ├── REFERENCES.bib └── slingshot_sticker.png ├── man ├── SlingshotDataSet-class.Rd ├── SlingshotDataSet.Rd ├── as.PseudotimeOrdering.Rd ├── as.SlingshotDataSet.Rd ├── embedCurves.Rd ├── getCurves.Rd ├── getLineages.Rd ├── newSlingshotDataSet.Rd ├── pairs-SlingshotDataSet.Rd ├── plot-SlingshotDataSet.Rd ├── plot3d-SlingshotDataSet.Rd ├── predict.SlingshotDataSet.Rd ├── slingBranchGraph.Rd ├── slingBranchID.Rd ├── slingClusterLabels.Rd ├── slingCurves.Rd ├── slingLineages.Rd ├── slingMST.Rd ├── slingParams.Rd ├── slingPseudotime.Rd ├── slingReducedDim.Rd ├── slingshot.Rd └── slingshotExample.Rd ├── tests ├── testthat.R └── testthat │ └── test_slingshot.R └── vignettes ├── bibFile.bib ├── conditionsVignette.Rmd └── vignette.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | #---------------------------- 2 | # Git and SVN related 3 | ^.svn 4 | ^.git 5 | README\.md 6 | NEWS\.md 7 | 8 | # Travis-CI et al. 9 | ^\.travis 10 | ^travis-tool\.sh$ 11 | ^pkg-build\.sh$ 12 | ^appveyor\.yml$ 13 | ^covr-utils.R$ 14 | ^\.coveralls\.R$ 15 | 16 | # R related 17 | ^cran-comments\..*$ 18 | ^vignettes/.*\.(pdf|PDF)$ 19 | ^vignettes/.*\.(r|R)$ 20 | ^vignettes/\.install_extras$ 21 | ^Makefile$ 22 | ^incl 23 | ^NAMESPACE,.*\.txt$ 24 | ^nohup.*$ 25 | ^\.devel 26 | ^\.test 27 | ^\.check 28 | ^.*\.Rproj$ 29 | ^\.Rproj\.user$ 30 | ^codecov\.yml$ 31 | ^data-raw 32 | ^\.github$ 33 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/check-bioc.yml: -------------------------------------------------------------------------------- 1 | ## Read more about GitHub actions the features of this GitHub Actions workflow 2 | ## at https://lcolladotor.github.io/biocthis/articles/biocthis.html#use_bioc_github_action 3 | ## 4 | ## For more details, check the biocthis developer notes vignette at 5 | ## https://lcolladotor.github.io/biocthis/articles/biocthis_dev_notes.html 6 | ## 7 | ## You can add this workflow to other packages using: 8 | ## > biocthis::use_bioc_github_action() 9 | ## 10 | ## Using GitHub Actions exposes you to many details about how R packages are 11 | ## compiled and installed in several operating system.s 12 | ### If you need help, please follow the steps listed at 13 | ## https://github.com/r-lib/actions#where-to-find-help 14 | ## 15 | ## If you found an issue specific to biocthis's GHA workflow, please report it 16 | ## with the information that will make it easier for others to help you. 17 | ## Thank you! 18 | 19 | ## Acronyms: 20 | ## * GHA: GitHub Action 21 | ## * OS: operating system 22 | 23 | on: 24 | push: 25 | pull_request: 26 | 27 | name: R-CMD-check-bioc 28 | 29 | ## These environment variables control whether to run GHA code later on that is 30 | ## specific to testthat, covr, and pkgdown. 31 | ## 32 | ## If you need to clear the cache of packages, update the number inside 33 | ## cache-version as discussed at https://github.com/r-lib/actions/issues/86. 34 | ## Note that you can always run a GHA test without the cache by using the word 35 | ## "/nocache" in the commit message. 36 | env: 37 | has_testthat: 'true' 38 | run_covr: 'true' 39 | run_pkgdown: 'false' 40 | has_RUnit: 'false' 41 | cache-version: 'cache-v1' 42 | run_docker: 'false' 43 | 44 | jobs: 45 | build-check: 46 | runs-on: ${{ matrix.config.os }} 47 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 48 | container: ${{ matrix.config.cont }} 49 | ## Environment variables unique to this job. 50 | 51 | strategy: 52 | fail-fast: false 53 | matrix: 54 | config: 55 | - { os: ubuntu-latest, r: '4.2', bioc: '3.15', cont: "bioconductor/bioconductor_docker:RELEASE_3_15", rspm: "https://packagemanager.rstudio.com/cran/__linux__/jammy/latest" } 56 | - { os: macOS-latest, r: '4.2', bioc: '3.15'} 57 | - { os: windows-latest, r: '4.2', bioc: '3.15'} 58 | ## Check https://github.com/r-lib/actions/tree/master/examples 59 | ## for examples using the http-user-agent 60 | env: 61 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 62 | RSPM: ${{ matrix.config.rspm }} 63 | NOT_CRAN: true 64 | TZ: UTC 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 67 | 68 | steps: 69 | 70 | ## Set the R library to the directory matching the 71 | ## R packages cache step further below when running on Docker (Linux). 72 | - name: Set R Library home on Linux 73 | if: runner.os == 'Linux' 74 | run: | 75 | mkdir /__w/_temp/Library 76 | echo ".libPaths('/__w/_temp/Library')" > ~/.Rprofile 77 | 78 | ## Most of these steps are the same as the ones in 79 | ## https://github.com/r-lib/actions/blob/master/examples/check-standard.yaml 80 | ## If they update their steps, we will also need to update ours. 81 | - name: Checkout Repository 82 | uses: actions/checkout@v3 83 | 84 | ## R is already included in the Bioconductor docker images 85 | - name: Setup R from r-lib 86 | if: runner.os != 'Linux' 87 | uses: r-lib/actions/setup-r@v2 88 | with: 89 | r-version: ${{ matrix.config.r }} 90 | http-user-agent: ${{ matrix.config.http-user-agent }} 91 | 92 | ## pandoc is already included in the Bioconductor docker images 93 | - name: Setup pandoc from r-lib 94 | if: runner.os != 'Linux' 95 | uses: r-lib/actions/setup-pandoc@v2 96 | 97 | - name: Query dependencies 98 | run: | 99 | install.packages('remotes') 100 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 101 | shell: Rscript {0} 102 | 103 | - name: Restore R package cache 104 | if: "!contains(github.event.head_commit.message, '/nocache') && runner.os != 'Linux'" 105 | uses: actions/cache@v3 106 | with: 107 | path: ${{ env.R_LIBS_USER }} 108 | key: ${{ env.cache-version }}-${{ runner.os }}-biocversion-RELEASE_3_15-r-4.2-${{ hashFiles('.github/depends.Rds') }} 109 | restore-keys: ${{ env.cache-version }}-${{ runner.os }}-biocversion-RELEASE_3_15-r-4.2- 110 | 111 | - name: Cache R packages on Linux 112 | if: "!contains(github.event.head_commit.message, '/nocache') && runner.os == 'Linux' " 113 | uses: actions/cache@v3 114 | with: 115 | path: /home/runner/work/_temp/Library 116 | key: ${{ env.cache-version }}-${{ runner.os }}-biocversion-RELEASE_3_15-r-4.2-${{ hashFiles('.github/depends.Rds') }} 117 | restore-keys: ${{ env.cache-version }}-${{ runner.os }}-biocversion-RELEASE_3_15-r-4.2- 118 | 119 | - name: Install Linux system dependencies 120 | if: runner.os == 'Linux' 121 | run: | 122 | sysreqs=$(Rscript -e 'cat("apt-get update -y && apt-get install -y", paste(gsub("apt-get install -y ", "", remotes::system_requirements("ubuntu", "20.04")), collapse = " "))') 123 | echo $sysreqs 124 | sudo -s eval "$sysreqs" 125 | 126 | - name: Install macOS system dependencies 127 | if: matrix.config.os == 'macOS-latest' 128 | run: | 129 | ## Enable installing XML from source if needed 130 | brew install libxml2 131 | echo "XML_CONFIG=/usr/local/opt/libxml2/bin/xml2-config" >> $GITHUB_ENV 132 | 133 | ## Required to install magick as noted at 134 | ## https://github.com/r-lib/usethis/commit/f1f1e0d10c1ebc75fd4c18fa7e2de4551fd9978f#diff-9bfee71065492f63457918efcd912cf2 135 | brew install imagemagick@6 136 | 137 | ## For textshaping, required by ragg, and required by pkgdown 138 | brew install harfbuzz fribidi 139 | 140 | ## For installing usethis's dependency gert 141 | brew install libgit2 142 | 143 | ## Required for tcltk 144 | brew install xquartz --cask 145 | 146 | - name: Install Windows system dependencies 147 | if: runner.os == 'Windows' 148 | run: | 149 | ## Edit below if you have any Windows system dependencies 150 | shell: Rscript {0} 151 | 152 | - name: Install BiocManager 153 | run: | 154 | message(paste('****', Sys.time(), 'installing BiocManager ****')) 155 | remotes::install_cran("BiocManager") 156 | shell: Rscript {0} 157 | 158 | - name: Set BiocVersion 159 | run: | 160 | BiocManager::install(version = "${{ matrix.config.bioc }}", ask = FALSE, force = TRUE) 161 | shell: Rscript {0} 162 | 163 | - name: Install dependencies pass 1 164 | run: | 165 | ## Try installing the package dependencies in steps. First the local 166 | ## dependencies, then any remaining dependencies to avoid the 167 | ## issues described at 168 | ## https://stat.ethz.ch/pipermail/bioc-devel/2020-April/016675.html 169 | ## https://github.com/r-lib/remotes/issues/296 170 | ## Ideally, all dependencies should get installed in the first pass. 171 | 172 | ## Set the repos source depending on the OS 173 | ## Alternatively use https://storage.googleapis.com/bioconductor_docker/packages/ 174 | ## though based on https://bit.ly/bioc2021-package-binaries 175 | ## the Azure link will be the main one going forward. 176 | gha_repos <- if( 177 | .Platform$OS.type == "unix" && Sys.info()["sysname"] != "Darwin" 178 | ) c( 179 | "AnVIL" = "https://bioconductordocker.blob.core.windows.net/packages/3.15/bioc", 180 | BiocManager::repositories() 181 | ) else BiocManager::repositories() 182 | 183 | ## For running the checks 184 | message(paste('****', Sys.time(), 'installing rcmdcheck and BiocCheck ****')) 185 | install.packages(c("rcmdcheck", "BiocCheck"), repos = gha_repos) 186 | 187 | ## Pass #1 at installing dependencies 188 | ## This pass uses AnVIL-powered fast binaries 189 | ## details at https://github.com/nturaga/bioc2021-bioconductor-binaries 190 | ## The speed gains only apply to the docker builds. 191 | message(paste('****', Sys.time(), 'pass number 1 at installing dependencies: local dependencies ****')) 192 | remotes::install_local(dependencies = TRUE, repos = gha_repos, build_vignettes = FALSE, upgrade = TRUE) 193 | continue-on-error: true 194 | shell: Rscript {0} 195 | 196 | - name: Install dependencies pass 2 197 | run: | 198 | ## Pass #2 at installing dependencies 199 | ## This pass does not use AnVIL and will thus update any packages 200 | ## that have seen been updated in Bioconductor 201 | message(paste('****', Sys.time(), 'pass number 2 at installing dependencies: any remaining dependencies ****')) 202 | remotes::install_local(dependencies = TRUE, repos = BiocManager::repositories(), build_vignettes = TRUE, upgrade = TRUE, force = TRUE) 203 | shell: Rscript {0} 204 | 205 | - name: Install BiocGenerics 206 | if: env.has_RUnit == 'true' 207 | run: | 208 | ## Install BiocGenerics 209 | BiocManager::install("BiocGenerics") 210 | shell: Rscript {0} 211 | 212 | - name: Install covr 213 | if: github.ref == 'refs/heads/devel' && env.run_covr == 'true' && runner.os == 'Linux' 214 | run: | 215 | remotes::install_cran("covr") 216 | shell: Rscript {0} 217 | 218 | - name: Install pkgdown 219 | if: github.ref == 'refs/heads/devel' && env.run_pkgdown == 'true' && runner.os == 'Linux' 220 | run: | 221 | remotes::install_cran("pkgdown") 222 | shell: Rscript {0} 223 | 224 | - name: Session info 225 | run: | 226 | options(width = 100) 227 | pkgs <- installed.packages()[, "Package"] 228 | sessioninfo::session_info(pkgs, include_base = TRUE) 229 | shell: Rscript {0} 230 | 231 | - name: Run CMD check 232 | env: 233 | _R_CHECK_CRAN_INCOMING_: false 234 | DISPLAY: 99.0 235 | run: | 236 | options(crayon.enabled = TRUE) 237 | rcmdcheck::rcmdcheck( 238 | args = c("--no-manual", "--no-vignettes", "--timings"), 239 | build_args = c("--no-manual", "--keep-empty-dirs", "--no-resave-data"), 240 | error_on = "warning", 241 | check_dir = "check" 242 | ) 243 | shell: Rscript {0} 244 | 245 | ## Might need an to add this to the if: && runner.os == 'Linux' 246 | - name: Reveal testthat details 247 | if: env.has_testthat == 'true' 248 | run: find . -name testthat.Rout -exec cat '{}' ';' 249 | 250 | - name: Run RUnit tests 251 | if: env.has_RUnit == 'true' 252 | run: | 253 | BiocGenerics:::testPackage() 254 | shell: Rscript {0} 255 | 256 | - name: Run BiocCheck 257 | env: 258 | DISPLAY: 99.0 259 | run: | 260 | BiocCheck::BiocCheck( 261 | dir('check', 'tar.gz$', full.names = TRUE), 262 | `quit-with-status` = TRUE, 263 | `no-check-R-ver` = TRUE, 264 | `no-check-bioc-help` = TRUE 265 | ) 266 | shell: Rscript {0} 267 | 268 | - name: Test coverage 269 | if: github.ref == 'refs/heads/devel' && env.run_covr == 'true' && runner.os == 'Linux' 270 | run: | 271 | covr::codecov() 272 | shell: Rscript {0} 273 | 274 | - name: Install package 275 | if: github.ref == 'refs/heads/devel' && env.run_pkgdown == 'true' && runner.os == 'Linux' 276 | run: R CMD INSTALL . 277 | 278 | - name: Build pkgdown site 279 | if: github.ref == 'refs/heads/devel' && env.run_pkgdown == 'true' && runner.os == 'Linux' 280 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 281 | shell: Rscript {0} 282 | ## Note that you need to run pkgdown::deploy_to_branch(new_process = FALSE) 283 | ## at least one locally before this will work. This creates the gh-pages 284 | ## branch (erasing anything you haven't version controlled!) and 285 | ## makes the git history recognizable by pkgdown. 286 | 287 | - name: Install deploy dependencies 288 | if: github.ref == 'refs/heads/devel' && env.run_pkgdown == 'true' && runner.os == 'Linux' 289 | run: | 290 | apt-get update && apt-get -y install rsync 291 | 292 | - name: Deploy pkgdown site to GitHub pages 🚀 293 | if: github.ref == 'refs/heads/devel' && env.run_pkgdown == 'true' && runner.os == 'Linux' 294 | uses: JamesIves/github-pages-deploy-action@releases/v4 295 | with: 296 | clean: false 297 | branch: gh-pages 298 | folder: docs 299 | 300 | - name: Upload check results 301 | if: failure() 302 | uses: actions/upload-artifact@master 303 | with: 304 | name: ${{ runner.os }}-biocversion-RELEASE_3_15-r-4.2-results 305 | path: check 306 | 307 | 308 | ## Code adapted from 309 | ## https://github.com/waldronlab/cBioPortalData/blob/e0440a4445f0cc731e426363a76faa22ee5e0f9d/.github/workflows/devel_check_dock.yml#L65-L92 310 | docker-build-and-push: 311 | runs-on: ubuntu-latest 312 | needs: build-check 313 | steps: 314 | - name: Checkout Repository 315 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel'" 316 | uses: actions/checkout@v3 317 | 318 | - name: Register repo name 319 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel'" 320 | id: reg_repo_name 321 | run: | 322 | echo CONT_IMG_NAME=$(echo ${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV 323 | 324 | - name: Set up QEMU 325 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel'" 326 | uses: docker/setup-qemu-action@v2 327 | 328 | - name: Set up Docker Buildx 329 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel'" 330 | uses: docker/setup-buildx-action@v2 331 | 332 | - name: Login to Docker Hub 333 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel'" 334 | uses: docker/login-action@v2 335 | with: 336 | username: ${{ secrets.DOCKERHUB_USERNAME }} 337 | password: ${{ secrets.DOCKERHUB_TOKEN }} 338 | ## Note that DOCKERHUB_TOKEN is really a token for your dockerhub 339 | ## account, not your actual dockerhub account password. You can get it 340 | ## from https://hub.docker.com/settings/security. 341 | ## Check https://github.com/docker/build-push-action/tree/v4.0.0 342 | ## for more details. 343 | ## Alternatively, try checking 344 | ## https://seandavi.github.io/BuildABiocWorkshop/articles/HOWTO_BUILD_WORKSHOP.html. 345 | 346 | - name: Build and Push Docker 347 | if: "!contains(github.event.head_commit.message, '/nodocker') && env.run_docker == 'true' && github.ref == 'refs/heads/devel' && success()" 348 | uses: docker/build-push-action@v4 349 | with: 350 | context: . 351 | push: true 352 | tags: > 353 | ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CONT_IMG_NAME }}:latest, 354 | ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.CONT_IMG_NAME }}:devel 355 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | vignettes/figure 5 | *.Rproj 6 | vignettes/R_cache 7 | vignettes/slingshot_cache 8 | vignettes/vignette_cache 9 | vignettes/R_figure 10 | vignettes/slingshot_files 11 | vignettes/slingshot.html 12 | tests/testthat/Rplots.pdf 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: slingshot 2 | Title: Tools for ordering single-cell sequencing 3 | Version: 2.7.0 4 | Description: Provides functions for inferring continuous, branching 5 | lineage structures in low-dimensional data. Slingshot was designed 6 | to model developmental trajectories in single-cell RNA sequencing 7 | data and serve as a component in an analysis pipeline after 8 | dimensionality reduction and clustering. It is flexible enough to 9 | handle arbitrarily many branching events and allows for the 10 | incorporation of prior knowledge through supervised graph 11 | construction. 12 | Authors@R: c( 13 | person("Kelly", "Street", email = "street.kelly@gmail.com", role = c("aut", "cre", "cph")), 14 | person("Davide","Risso", role = "aut",email = "risso.davide@gmail.com"), 15 | person("Diya","Das", role = "aut", email = "diyadas@berkeley.edu"), 16 | person("Sandrine","Dudoit", role = "ths", email = "sandrine@stat.berkeley.edu"), 17 | person("Koen","Van den Berge", role = "ctb"), 18 | person( 19 | "Robrecht", 20 | "Cannoodt", 21 | email = "rcannood@gmail.com", 22 | role = c("ctb"), 23 | comment = c(ORCID = "0000-0003-3641-729X", github = "rcannood") 24 | )) 25 | License: Artistic-2.0 26 | Depends: 27 | R (>= 4.0), 28 | princurve (>= 2.0.4), 29 | stats, 30 | TrajectoryUtils 31 | Imports: 32 | graphics, 33 | grDevices, 34 | igraph, 35 | matrixStats, 36 | methods, 37 | S4Vectors, 38 | SingleCellExperiment, 39 | SummarizedExperiment 40 | Suggests: 41 | BiocGenerics, 42 | BiocStyle, 43 | clusterExperiment, 44 | DelayedMatrixStats, 45 | knitr, 46 | mclust, 47 | mgcv, 48 | RColorBrewer, 49 | rgl, 50 | rmarkdown, 51 | testthat, 52 | uwot, 53 | covr 54 | VignetteBuilder: knitr 55 | LazyData: false 56 | RoxygenNote: 7.2.0 57 | Encoding: UTF-8 58 | biocViews: 59 | Clustering, 60 | DifferentialExpression, 61 | GeneExpression, 62 | RNASeq, 63 | Sequencing, 64 | Software, 65 | Sequencing, 66 | SingleCell, 67 | Transcriptomics, 68 | Visualization 69 | BugReports: https://github.com/kstreet13/slingshot/issues 70 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(lines,SlingshotDataSet) 4 | S3method(pairs,SlingshotDataSet) 5 | S3method(plot,SlingshotDataSet) 6 | export(SlingshotDataSet) 7 | export(as.PseudotimeOrdering) 8 | export(as.SlingshotDataSet) 9 | export(embedCurves) 10 | export(getCurves) 11 | export(getLineages) 12 | export(newSlingshotDataSet) 13 | export(plot3d.SlingshotDataSet) 14 | export(slingAvgPseudotime) 15 | export(slingBranchGraph) 16 | export(slingBranchID) 17 | export(slingClusterLabels) 18 | export(slingCurveWeights) 19 | export(slingCurves) 20 | export(slingLineages) 21 | export(slingMST) 22 | export(slingParams) 23 | export(slingPseudotime) 24 | export(slingReducedDim) 25 | export(slingshot) 26 | exportClasses(SlingshotDataSet) 27 | exportMethods(SlingshotDataSet) 28 | exportMethods(as.PseudotimeOrdering) 29 | exportMethods(as.SlingshotDataSet) 30 | exportMethods(embedCurves) 31 | exportMethods(getCurves) 32 | exportMethods(getLineages) 33 | exportMethods(newSlingshotDataSet) 34 | exportMethods(predict) 35 | exportMethods(reducedDim) 36 | exportMethods(reducedDims) 37 | exportMethods(show) 38 | exportMethods(slingAvgPseudotime) 39 | exportMethods(slingBranchGraph) 40 | exportMethods(slingBranchID) 41 | exportMethods(slingClusterLabels) 42 | exportMethods(slingCurveWeights) 43 | exportMethods(slingCurves) 44 | exportMethods(slingLineages) 45 | exportMethods(slingMST) 46 | exportMethods(slingParams) 47 | exportMethods(slingPseudotime) 48 | exportMethods(slingReducedDim) 49 | exportMethods(slingshot) 50 | import(TrajectoryUtils) 51 | import(graphics) 52 | import(matrixStats) 53 | import(methods) 54 | import(princurve) 55 | import(stats) 56 | importClassesFrom(SingleCellExperiment,SingleCellExperiment) 57 | importFrom(S4Vectors,"metadata<-") 58 | importFrom(S4Vectors,metadata) 59 | importFrom(SingleCellExperiment,"reducedDims<-") 60 | importFrom(SingleCellExperiment,colData) 61 | importFrom(SingleCellExperiment,reducedDim) 62 | importFrom(SingleCellExperiment,reducedDims) 63 | importFrom(SummarizedExperiment,"assay<-") 64 | importFrom(SummarizedExperiment,"colData<-") 65 | importFrom(SummarizedExperiment,assay) 66 | importFrom(grDevices,dev.flush) 67 | importFrom(grDevices,dev.hold) 68 | importFrom(graphics,lines) 69 | importFrom(igraph,V) 70 | importFrom(princurve,project_to_curve) 71 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # changes in version 2.0.0 2 | 3 | * Added a `NEWS.md` file to track changes to the package. 4 | 5 | * Changed default output of most functions from `SlingshotDataSet` to `PseudotimeOrdering` and added conversion functions between them and `SingleCellExperiment`. 6 | 7 | * `getLineages` now relies on `createClusterMST` 8 | 9 | * Removed `plotGenePseudotime` 10 | 11 | * added `as.df` option to `slingCurves` and `slingMST`, which provide relevant 12 | information for plotting as `data.frame` objects. This should help those 13 | plotting Slingshot results with `ggplot`, especially for our `traffic` package. 14 | 15 | * updated all documentation. 16 | 17 | -------------------------------------------------------------------------------- /R/AllClasses.R: -------------------------------------------------------------------------------- 1 | #' @title Class \code{SlingshotDataSet} 2 | #' @aliases SlingshotDataSet-class 3 | #' 4 | #' @description This was the original class for storing \code{slingshot} 5 | #' results, but we now generally reommend using the 6 | #' \code{\link{PseudotimeOrdering}} class, instead. Most \code{slingshot} 7 | #' functions will still work with \code{SlingshotDataSet} objects, but will 8 | #' return \code{PseudotimeOrdering} objects, by default. To update old 9 | #' \code{SlingshotDataSet} objects, we have provided the 10 | #' \code{\link{as.PseudotimeOrdering}} conversion function. The only functions 11 | #' that require \code{SlingshotDataSet} objects are the plotting functions. 12 | #' 13 | #' @description The \code{SlingshotDataSet} class holds data relevant for 14 | #' performing lineage inference with the \code{slingshot} package, primarily a 15 | #' reduced dimensional representation of the data and a set of cluster labels. 16 | #' 17 | #' @slot reducedDim matrix. An \code{n} by \code{p} numeric matrix or data frame 18 | #' giving the coordinates of the cells in a reduced dimensionality space. 19 | #' @slot clusterLabels matrix or character. An \code{n} by \code{K} matrix of 20 | #' weights indicating each cell's cluster assignment or a character vector of 21 | #' cluster assignments, which will be converted into a binary matrix. 22 | #' @slot lineages list. A list with each element a character vector of cluster 23 | #' names representing a lineage as an ordered set of clusters. 24 | #' @slot adjacency matrix. A binary matrix describing the adjacency 25 | #' between clusters induced by the minimum spanning tree. 26 | #' @slot curves list. A list of \code{\link[princurve]{principal_curve}} objects 27 | #' produced by \code{\link{getCurves}}. 28 | #' @slot slingParams list. Additional parameters used by Slingshot. These may 29 | #' specify how the minimum spanning tree on clusters was constructed: 30 | #' \itemize{ 31 | #' \item{\code{start.clus}}{ character. The label of the root cluster, or a 32 | #' vector of cluster labels giving the root clusters of each disjoint 33 | #' component of the graph.} 34 | #' \item{\code{end.clus}}{ character. Vector of cluster labels indicating 35 | #' terminal clusters.} 36 | #' \item{\code{start.given}}{ logical. A logical value 37 | #' indicating whether the initial state was pre-specified.} 38 | #' \item{\code{end.given}}{ logical. A vector of logical values indicating 39 | #' whether each terminal state was pre-specified} 40 | #' \item{\code{omega}}{ numeric or logical. Granularity parameter determining 41 | #' the maximum edge length for building the MST. See 42 | #' \code{\link{getLineages}}.} 43 | #' \item{\code{omega_scale}}{ numeric. Scaling factor used for setting maximum 44 | #' edge length when \code{omega = TRUE}. See \code{\link{getLineages}}.} } 45 | #' They may also specify how simultaneous principal curves were constructed 46 | #' (for a complete listing, see \code{\link{getCurves}}: 47 | #' \itemize{ 48 | #' \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 49 | #' whether and how much to shrink branching lineages toward their shared 50 | #' average curve.} 51 | #' \item{\code{extend}}{ character. Specifies the method for handling 52 | #' root and leaf clusters of lineages when constructing the initial, 53 | #' piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 54 | #' See \code{\link{getCurves}} for details.} 55 | #' \item{\code{reweight}}{ logical. 56 | #' Indicates whether to allow cells shared 57 | #' between lineages to be reweighted during curve-fitting. If \code{TRUE}, 58 | #' cells shared between lineages will be iteratively reweighted based on the 59 | #' quantiles of their projection distances to each curve.} 60 | #' \item{\code{reassign}}{ logical. 61 | #' Indicates whether to reassign cells to lineages at each 62 | #' iteration. If \code{TRUE}, cells will be added to a lineage when their 63 | #' projection distance to the curve is less than the median distance for all 64 | #' cells currently assigned to the lineage. Additionally, shared cells will be 65 | #' removed from a lineage if their projection distance to the curve is above 66 | #' the 90th percentile and their weight along the curve is less than 67 | #' \code{0.1}.} 68 | #' \item{\code{shrink.method}}{ character. 69 | #' Denotes how to determine the amount of shrinkage for a branching lineage. 70 | #' Accepted values are the same as for \code{kernel} in the \code{density} 71 | #' function (default is \code{"cosine"}), as well as \code{"tricube"} and 72 | #' \code{"density"}. See \code{\link{getCurves}} for details.} 73 | #' \item{approx_points}{ numeric. Number of points to use in estimating 74 | #' curves. See \code{\link{getCurves}} for details.} \item{allow.breaks}{ 75 | #' logical. Whether to allow curves that diverge very early on in a trajectory 76 | #' to have different starting points.} 77 | #' \item{Other parameters specified by 78 | #' \code{\link[princurve]{principal_curve}}}. } 79 | #' 80 | #' @return The accessor functions \code{reducedDim}, \code{clusterLabels}, 81 | #' \code{lineages}, \code{adjacency}, \code{curves}, 82 | #' and \code{slingParams} return the corresponding elements of a 83 | #' \code{SlingshotDataSet}. The functions \code{slingPseudotime} and 84 | #' \code{slingCurveWeights} extract useful output elements of a 85 | #' \code{SlingshotDataSet}, provided that curves have already been fit with 86 | #' either \code{slingshot} or \code{getCurves}. 87 | #' 88 | #' @seealso \code{\link{PseudotimeOrdering}} 89 | #' 90 | #' @import princurve 91 | #' @import methods 92 | #' @export 93 | setClass( 94 | Class = "SlingshotDataSet", 95 | slots = list( 96 | reducedDim = "matrix", 97 | clusterLabels = "matrix", 98 | lineages = "list", 99 | adjacency = "matrix", 100 | curves = "list", 101 | slingParams = "list" 102 | ) 103 | ) 104 | 105 | setValidity("SlingshotDataSet", function(object) { 106 | X <- reducedDim(object) 107 | n <- nrow(X) 108 | p <- ncol(X) 109 | if(!is.numeric(X)) { 110 | return("Reduced dimensional coordinates must be numeric.") 111 | } 112 | if(nrow(X)==0){ 113 | return('reducedDim has zero rows.') 114 | } 115 | if(ncol(X)==0){ 116 | return('reducedDim has zero columns.') 117 | } 118 | if(nrow(slingClusterLabels(object)) != n){ 119 | return(paste('Reduced dimensional coordinates and cluster labels', 120 | 'contain different numbers of cells.')) 121 | } 122 | # something requires row and column names. Princurve? 123 | if(is.null(rownames(reducedDim(object)))){ 124 | rownames(reducedDim(object)) <- paste('Cell', 125 | seq_len(nrow(reducedDim(object))), 126 | sep='-') 127 | } 128 | if(is.null(colnames(reducedDim(object)))){ 129 | colnames(reducedDim(object)) <- paste('Dim', 130 | seq_len(ncol(reducedDim(object))), 131 | sep='-') 132 | } 133 | 134 | # if lineages present 135 | if(length(slingLineages(object)) > 0){ 136 | L <- length(slingLineages(object)) 137 | clus.names <- colnames(slingClusterLabels(object)) 138 | K <- length(clus.names) 139 | if(any(vapply(slingLineages(object),class,'') != 'character')){ 140 | return("lineages must be a list of character vectors.") 141 | } 142 | if(!all(vapply(slingLineages(object), 143 | function(lin){all(lin %in% clus.names)}, TRUE))){ 144 | return(paste0("lineages must be a list of character vectors ", 145 | "composed of cluster names.")) 146 | } 147 | if(!is.numeric(slingMST(object))) { 148 | return("adjacency matrix must be numeric or logical.") 149 | } 150 | if(any(dim(slingMST(object)) != K)){ 151 | return(paste("adjacency matrix must be square with number of", 152 | "dimensions equal to number of clusters")) 153 | } 154 | if(! is.null(slingParams(object)$start.clus)){ 155 | if(!all(slingParams(object)$start.clus %in% clus.names)){ 156 | return("Specified starting cluster not found in cluster labels") 157 | } 158 | } 159 | if(! is.null(slingParams(object)$end.clus)){ 160 | if(!all(slingParams(object)$end.clus %in% clus.names)){ 161 | return(paste0("Specified terminal cluster(s) not found in ", 162 | "cluster labels")) 163 | } 164 | } 165 | if(! is.null(slingParams(object)$dist.fun)){ 166 | if(!is.function(slingParams(object)$dist.fun)){ 167 | return("Pairwise cluster distance function must be a function.") 168 | } 169 | } 170 | if(! is.null(slingParams(object)$omega)){ 171 | if(length(slingParams(object)$omega) > 1){ 172 | return('omega must be NULL or length 1') 173 | } 174 | if(is.na(as.numeric(slingParams(object)$omega))){ 175 | stop('omega must be numeric, logical, or NULL') 176 | } 177 | if(slingParams(object)$omega < 0){ 178 | stop('omega must be non-negative') 179 | } 180 | } 181 | } 182 | 183 | # if curves present 184 | if(length(slingCurves(object)) > 0){ 185 | if(length(slingLineages(object)) > 0){ 186 | L <- length(slingLineages(object)) 187 | if(length(slingCurves(object)) != L){ 188 | return("Number of curves does not match number of lineages") 189 | } 190 | } 191 | L <- length(slingCurves(object)) 192 | if(any(vapply(slingCurves(object),class,'') != 'principal_curve')){ 193 | return("curves must be a list of principal_curve objects.") 194 | } 195 | if(!is.null(slingParams(object)$shrink)){ 196 | if(slingParams(object)$shrink < 0 | 197 | slingParams(object)$shrink > 1){ 198 | stop("shrink argument must be logical or numeric between ", 199 | "0 and 1.") 200 | } 201 | } 202 | if(!is.null(slingParams(object)$extend)){ 203 | if(! slingParams(object)$extend %in% c('y','n','pc1')){ 204 | stop("extend argument must be one of 'y', 'n', or 'pc1'.") 205 | } 206 | } 207 | if(!is.null(slingParams(object)$reweight)){ 208 | if(!is.logical(slingParams(object)$reweight)){ 209 | stop("reweight argument must be logical.") 210 | } 211 | } 212 | if(!is.null(slingParams(object)$reassign)){ 213 | if(!is.logical(slingParams(object)$reassign)){ 214 | stop("reassign argument must be logical.") 215 | } 216 | } 217 | } 218 | return(TRUE) 219 | }) 220 | -------------------------------------------------------------------------------- /R/AllGenerics.R: -------------------------------------------------------------------------------- 1 | #' @title Initialize an object of class \code{SlingshotDataSet} 2 | #' @name newSlingshotDataSet 3 | #' @docType methods 4 | #' 5 | #' @description Constructs a \code{SlingshotDataSet} object. Additional helper 6 | #' methods for manipulating \code{SlingshotDataSet} objects are also 7 | #' described below. We now recommend using 8 | #' \code{\link[TrajectoryUtils]{PseudotimeOrdering}} objects, instead. 9 | #' 10 | #' @param reducedDim matrix. An \code{n} by \code{p} numeric matrix or data 11 | #' frame giving the coordinates of the cells in a reduced dimensionality 12 | #' space. 13 | #' @param clusterLabels character. A character vector of length \code{n} 14 | #' denoting each cell's cluster label. 15 | #' @param ... additional components of a \code{SlingshotDataSet} to specify. 16 | #' This may include any of the following: 17 | #' @param lineages list. A list with each element a character vector of cluster 18 | #' names representing a lineage as an ordered set of clusters. 19 | #' @param adjacency matrix. A binary matrix describing the connectivity 20 | #' between clusters induced by the minimum spanning tree. 21 | #' @param slingParams list. Additional parameters used by Slingshot. These may 22 | #' specify how the minimum spanning tree on clusters was constructed: 23 | #' \itemize{ 24 | #' \item{\code{start.clus}}{ character. The label of the root cluster.} 25 | #' \item{\code{end.clus}}{ character. Vector of cluster labels indicating the 26 | #' terminal clusters.} 27 | #' \item{\code{start.given}}{ logical. A logical value 28 | #' indicating whether the initial state was pre-specified.} 29 | #' \item{\code{end.given}}{ logical. A vector of logical values indicating 30 | #' whether each terminal state was pre-specified} 31 | #' \item{\code{dist}}{ matrix. A 32 | #' numeric matrix of pairwise cluster distances.} } 33 | #' They may also specify how simultaneous principal curves were constructed: 34 | #' \itemize{ 35 | #' \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 36 | #' whether and how much to shrink branching lineages toward their shared 37 | #' average curve.} 38 | #' \item{\code{extend}}{ character. Specifies the method for handling 39 | #' root and leaf clusters of lineages when constructing the initial, 40 | #' piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 41 | #' See \code{\link{getCurves}} for details.} 42 | #' \item{\code{reweight}}{ logical. 43 | #' Indicates whether to allow cells shared 44 | #' between lineages to be reweighted during curve-fitting. If \code{TRUE}, 45 | #' cells shared between lineages will be iteratively reweighted based on the 46 | #' quantiles of their projection distances to each curve.} 47 | #' \item{\code{reassign}}{ logical. 48 | #' Indicates whether to reassign cells to 49 | #' lineages at each iteration. If \code{TRUE}, cells will be added to a 50 | #' lineage when their projection distance to the curve is less than the median 51 | #' distance for all cells currently assigned to the lineage. Additionally, 52 | #' shared cells will be removed from a lineage if their projection distance to 53 | #' the curve is above the 90th percentile and their weight along the curve is 54 | #' less than \code{0.1}.} 55 | #' \item{\code{shrink.method}}{ character. 56 | #' Denotes how to determine the amount of shrinkage for a branching lineage. 57 | #' Accepted values are the same as for \code{kernel} in the \code{density} 58 | #' function (default is \code{"cosine"}), as well as \code{"tricube"} and 59 | #' \code{"density"}. See \code{\link{getCurves}} for details.} 60 | #' \item{Other parameters specified by 61 | #' \code{\link[princurve]{principal_curve}}}. } 62 | #' @param curves list. A list of \code{\link[princurve]{principal_curve}} 63 | #' objects produced by \code{\link{getCurves}}. 64 | #' 65 | #' @return A \code{SlingshotDataSet} object with all specified values. 66 | #' 67 | #' @seealso \code{\link[TrajectoryUtils]{PseudotimeOrdering}} 68 | #' 69 | #' @examples 70 | #' rd <- matrix(data=rnorm(100), ncol=2) 71 | #' cl <- sample(letters[seq_len(5)], 50, replace = TRUE) 72 | #' sds <- newSlingshotDataSet(rd, cl) 73 | #' 74 | #' @import princurve 75 | #' @import methods 76 | #' @export 77 | setGeneric( 78 | name = "newSlingshotDataSet", 79 | signature = c('reducedDim','clusterLabels'), 80 | def = function(reducedDim, clusterLabels, ...) { 81 | standardGeneric("newSlingshotDataSet") 82 | } 83 | ) 84 | 85 | #' @title Extract Slingshot output 86 | #' @name SlingshotDataSet 87 | #' @description This is a convenience function to extract a 88 | #' \code{SlingshotDataSet} from an object containing \code{\link{slingshot}} 89 | #' output. However, we now recommend using a 90 | #' \code{\link[TrajectoryUtils]{PseudotimeOrdering}} object, in most cases. 91 | #' The \code{SlingshotDataSet} is, however, still used for plotting purposes. 92 | #' @param data an object containing \code{slingshot} output. 93 | #' @param ... additional arguments to pass to object-specific methods. 94 | #' @return A \code{SlingshotDataSet} object containing the output of 95 | #' \code{slingshot}. 96 | #' 97 | #' @seealso \code{\link[TrajectoryUtils]{PseudotimeOrdering}}, 98 | #' \code{\link{as.SlingshotDataSet}} 99 | #' 100 | #' @examples 101 | #' data("slingshotExample") 102 | #' rd <- slingshotExample$rd 103 | #' cl <- slingshotExample$cl 104 | #' library(SingleCellExperiment) 105 | #' u <- matrix(rpois(140*50, 5), nrow = 50) 106 | #' sce <- SingleCellExperiment(assays = list(counts = u), 107 | #' reducedDims = SimpleList(PCA = rd), 108 | #' colData = data.frame(clus = cl)) 109 | #' sce <- slingshot(sce, clusterLabels = 'clus', reducedDim = 'PCA') 110 | #' SlingshotDataSet(sce) 111 | #' 112 | #' @export 113 | setGeneric( 114 | name = "SlingshotDataSet", 115 | signature = c('data'), 116 | def = function(data, ...) { 117 | standardGeneric("SlingshotDataSet") 118 | } 119 | ) 120 | 121 | #' @title Infer Lineage Structure from Clustered Samples 122 | #' @name getLineages 123 | #' @param ... Additional arguments to specify how lineages are constructed from 124 | #' clusters. 125 | #' @export 126 | setGeneric( 127 | name = "getLineages", 128 | signature = c('data','clusterLabels'), 129 | def = function(data, 130 | clusterLabels, ...) { 131 | standardGeneric("getLineages") 132 | } 133 | ) 134 | 135 | #' @title Construct Simultaneous Principal Curves 136 | #' @name getCurves 137 | #' @export 138 | setGeneric( 139 | name = "getCurves", 140 | signature = 'data', 141 | def = function(data, ...) { 142 | standardGeneric("getCurves") 143 | } 144 | ) 145 | 146 | #' @title Perform trajectory inference with Slingshot 147 | #' @description Perform trajectory inference with Slingshot 148 | #' @name slingshot 149 | #' @export 150 | setGeneric( 151 | name = "slingshot", 152 | signature = c('data', 'clusterLabels'), 153 | def = function(data, 154 | clusterLabels, ...) { 155 | standardGeneric("slingshot") 156 | } 157 | ) 158 | 159 | #' @title Extract the Slingshot lineages 160 | #' @name slingLineages 161 | #' 162 | #' @description Extract lineages (represented by ordered sets of clusters) 163 | #' identified by \code{\link{slingshot}}. 164 | #' 165 | #' @param x an object containing \code{\link{slingshot}} output. 166 | #' @return A list of lineages, represented by ordered sets of clusters. 167 | #' @examples 168 | #' data("slingshotExample") 169 | #' rd <- slingshotExample$rd 170 | #' cl <- slingshotExample$cl 171 | #' pto <- slingshot(rd, cl, start.clus = '1') 172 | #' slingLineages(pto) 173 | #' @export 174 | setGeneric(name = "slingLineages", 175 | signature = "x", 176 | def = function(x) standardGeneric("slingLineages")) 177 | 178 | #' @title Extract dimensionality reduction used by Slingshot 179 | #' @name slingReducedDim 180 | #' 181 | #' @description Extract the dimensionality reduction used by 182 | #' \code{\link{slingshot}}. 183 | #' 184 | #' @param x an object containing \code{\link{slingshot}} output. 185 | #' @return A matrix of coordinates. 186 | #' @examples 187 | #' data("slingshotExample") 188 | #' rd <- slingshotExample$rd 189 | #' cl <- slingshotExample$cl 190 | #' pto <- slingshot(rd, cl, start.clus = '1') 191 | #' slingReducedDim(pto) 192 | #' @export 193 | setGeneric(name = "slingReducedDim", 194 | signature = "x", 195 | def = function(x) standardGeneric("slingReducedDim")) 196 | 197 | #' @title Extract cluster labels used by Slingshot 198 | #' @name slingClusterLabels 199 | #' 200 | #' @description Extract the cluster labels used by \code{\link{slingshot}}. 201 | #' 202 | #' @param x an object containing \code{\link{slingshot}} output. 203 | #' @return Typically returns a matrix of cluster assignment weights 204 | #' (\code{#cells} by \code{#clusters}). Rarely, a vector of cluster labels. 205 | #' @examples 206 | #' data("slingshotExample") 207 | #' rd <- slingshotExample$rd 208 | #' cl <- slingshotExample$cl 209 | #' pto <- slingshot(rd, cl, start.clus = '1') 210 | #' slingClusterLabels(pto) 211 | #' @export 212 | setGeneric(name = "slingClusterLabels", 213 | signature = "x", 214 | def = function(x) standardGeneric("slingClusterLabels")) 215 | 216 | #' @title Extract Slingshot minimum spanning tree 217 | #' @name slingMST 218 | #' @description Extract the minimum spanning tree from an object containing 219 | #' \code{\link{slingshot}} output. 220 | #' 221 | #' @param x an object containing \code{\link{slingshot}} output. 222 | #' @param ... additional parameters to be passed to object-specific methods. 223 | #' @return In most cases, output is an \code{\link[igraph]{igraph}} object 224 | #' representing the MST. If \code{x} is a \code{SlingshotDataSet}, then output 225 | #' is an adjacency matrix representing the MST. 226 | #' @examples 227 | #' data("slingshotExample") 228 | #' rd <- slingshotExample$rd 229 | #' cl <- slingshotExample$cl 230 | #' pto <- slingshot(rd, cl, start.clus = '1') 231 | #' slingMST(pto) 232 | #' @export 233 | setGeneric(name = "slingMST", 234 | signature = "x", 235 | def = function(x, ...) standardGeneric("slingMST")) 236 | 237 | #' @title Methods for parameters used by Slingshot 238 | #' @name slingParams 239 | #' @description Extracts additional control parameters used by Slingshot in 240 | #' lineage inference and fitting simultaneous principal curves. 241 | #' 242 | #' @param x an object containing \code{\link{slingshot}} output. 243 | #' @return The list of additional parameters used by Slingshot. These include 244 | #' parameters related to the cluster-based minimum spanning tree: 245 | #' \itemize{ 246 | #' \item{\code{start.clus}}{ character. The label of the root cluster, or a 247 | #' vector of cluster labels giving the root clusters of each disjoint 248 | #' component of the graph.} 249 | #' \item{\code{end.clus}}{ character. Vector of cluster labels indicating 250 | #' terminal clusters.} 251 | #' \item{\code{start.given}}{ logical. A logical value 252 | #' indicating whether the initial state was pre-specified.} 253 | #' \item{\code{end.given}}{ logical. A vector of logical values indicating 254 | #' whether each terminal state was pre-specified} 255 | #' \item{\code{omega}}{ numeric or logical. Granularity parameter determining 256 | #' the maximum edge length for building the MST. See 257 | #' \code{\link{getLineages}}.} 258 | #' \item{\code{omega_scale}}{ numeric. Scaling factor used for setting maximum 259 | #' edge length when \code{omega = TRUE}. See \code{\link{getLineages}}.} } 260 | #' They may also specify how simultaneous principal curves were constructed 261 | #' (for a complete listing, see \code{\link{getCurves}}: 262 | #' \itemize{ 263 | #' \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 264 | #' whether and how much to shrink branching lineages toward their shared 265 | #' average curve.} 266 | #' \item{\code{extend}}{ character. Specifies the method for handling 267 | #' root and leaf clusters of lineages when constructing the initial, 268 | #' piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 269 | #' See \code{\link{getCurves}} for details.} 270 | #' \item{\code{reweight}}{ logical. 271 | #' Indicates whether to allow cells shared 272 | #' between lineages to be reweighted during curve-fitting. If \code{TRUE}, 273 | #' cells shared between lineages will be iteratively reweighted based on the 274 | #' quantiles of their projection distances to each curve.} 275 | #' \item{\code{reassign}}{ logical. 276 | #' Indicates whether to reassign cells to lineages at each 277 | #' iteration. If \code{TRUE}, cells will be added to a lineage when their 278 | #' projection distance to the curve is less than the median distance for all 279 | #' cells currently assigned to the lineage. Additionally, shared cells will be 280 | #' removed from a lineage if their projection distance to the curve is above 281 | #' the 90th percentile and their weight along the curve is less than 282 | #' \code{0.1}.} 283 | #' \item{\code{shrink.method}}{ character. 284 | #' Denotes how to determine the amount of shrinkage for a branching lineage. 285 | #' Accepted values are the same as for \code{kernel} in the \code{density} 286 | #' function (default is \code{"cosine"}), as well as \code{"tricube"} and 287 | #' \code{"density"}. See \code{\link{getCurves}} for details.} 288 | #' \item{approx_points}{ numeric. Number of points to use in estimating 289 | #' curves. See \code{\link{getCurves}} for details.} \item{allow.breaks}{ 290 | #' logical. Whether to allow curves that diverge very early on in a trajectory 291 | #' to have different starting points.} 292 | #' \item{Other parameters specified by 293 | #' \code{\link[princurve]{principal_curve}}}. } 294 | #' 295 | #' @examples 296 | #' data("slingshotExample") 297 | #' rd <- slingshotExample$rd 298 | #' cl <- slingshotExample$cl 299 | #' pto <- slingshot(rd, cl, start.clus = '1') 300 | #' slingParams(pto) 301 | #' @export 302 | setGeneric(name = "slingParams", 303 | signature = "x", 304 | def = function(x) standardGeneric("slingParams")) 305 | 306 | #' @title Extract simultaneous principal curves 307 | #' @name slingCurves 308 | #' @description Extract the simultaneous principal curves from an object 309 | #' containing \code{\link{slingshot}} output. 310 | #' 311 | #' @param x an object containing \code{\link{slingshot}} output. 312 | #' @param ... additional parameters to be passed to object-specific methods. 313 | #' @return A list of smooth lineage curves, each of which is a 314 | #' \code{\link[princurve]{principal_curve}} object. 315 | #' @examples 316 | #' data("slingshotExample") 317 | #' rd <- slingshotExample$rd 318 | #' cl <- slingshotExample$cl 319 | #' pto <- slingshot(rd, cl, start.clus = '1') 320 | #' slingCurves(pto) 321 | #' @export 322 | setGeneric(name = "slingCurves", 323 | signature = "x", 324 | def = function(x, ...) standardGeneric("slingCurves")) 325 | 326 | 327 | #' @title Get Slingshot pseudotime values 328 | #' @name slingPseudotime 329 | #' 330 | #' @description Extract the matrix of pseudotime values or cells' weights along 331 | #' each lineage. 332 | #' 333 | #' @param x an object containing \code{\link{slingshot}} output. 334 | #' @param ... additional parameters to be passed to object-specific methods. 335 | #' @return \code{slingPseudotime}: an \code{n} by \code{L} matrix representing 336 | #' each cell's pseudotime along each lineage. 337 | #' @examples 338 | #' data("slingshotExample") 339 | #' rd <- slingshotExample$rd 340 | #' cl <- slingshotExample$cl 341 | #' pto <- slingshot(rd, cl, start.clus = '1') 342 | #' slingPseudotime(pto) 343 | #' @export 344 | setGeneric(name = "slingPseudotime", 345 | signature = "x", 346 | def = function(x, ...) standardGeneric("slingPseudotime")) 347 | 348 | #' @rdname slingPseudotime 349 | #' @return \code{slingCurveWeights}: an \code{n} by \code{L} matrix of cell 350 | #' weights along each lineage. 351 | #' @examples 352 | #' slingCurveWeights(pto) 353 | #' @export 354 | setGeneric(name = "slingCurveWeights", 355 | signature = "x", 356 | def = function(x, ...) standardGeneric("slingCurveWeights")) 357 | 358 | #' @rdname slingPseudotime 359 | #' @return \code{slingAvgPseudotime}: a length \code{n} vector of average cell 360 | #' pseudotimes, where the average is a weighted average across lineages, 361 | #' weighted by the assignment weights. 362 | #' @examples 363 | #' slingAvgPseudotime(pto) 364 | #' @export 365 | setGeneric(name = "slingAvgPseudotime", 366 | signature = "x", 367 | def = function(x, ...) standardGeneric("slingAvgPseudotime")) 368 | 369 | #' @title Embed trajectory in new space 370 | #' @rdname embedCurves 371 | #' @export 372 | setGeneric(name = "embedCurves", 373 | signature = c("x", "newDimRed"), 374 | def = function(x, newDimRed, ...) standardGeneric("embedCurves")) 375 | 376 | #' @title Get slingshot branch labels 377 | #' @rdname slingBranchID 378 | #' @param ... additional arguments passed to object-specific methods. 379 | #' @export 380 | setGeneric(name = "slingBranchID", 381 | signature = c("x"), 382 | def = function(x, ...) standardGeneric("slingBranchID")) 383 | 384 | #' @title Construct graph of slingshot branch labels 385 | #' @rdname slingBranchGraph 386 | #' @param ... additional arguments passed to object-specific methods. 387 | #' @export 388 | setGeneric(name = "slingBranchGraph", 389 | signature = c("x"), 390 | def = function(x, ...) standardGeneric("slingBranchGraph")) 391 | 392 | #' @title Conversion to SlingshotDataSet 393 | #' @rdname as.SlingshotDataSet 394 | #' @param ... additional arguments passed to object-specific methods. 395 | #' @export 396 | setGeneric(name = "as.SlingshotDataSet", 397 | signature = c("x"), 398 | def = function(x, ...) standardGeneric("as.SlingshotDataSet")) 399 | 400 | #' @title Conversion to PseudotimeOrdering 401 | #' @rdname as.PseudotimeOrdering 402 | #' @param ... additional arguments passed to object-specific methods. 403 | #' @export 404 | setGeneric(name = "as.PseudotimeOrdering", 405 | signature = c("x"), 406 | def = function(x, ...) standardGeneric("as.PseudotimeOrdering")) 407 | 408 | 409 | 410 | 411 | -------------------------------------------------------------------------------- /R/branchID.R: -------------------------------------------------------------------------------- 1 | #' @rdname slingBranchID 2 | #' 3 | #' @description Summarizes the lineage assignment weights from \code{slingshot} 4 | #' results as a single vector. This is represented by a categorical variable 5 | #' indicating which lineage (or combination of lineages) each cell is assigned 6 | #' to. 7 | #' @param x an object containing \code{slingshot} output, generally either a 8 | #' \code{\link{PseudotimeOrdering}} or \code{\link{SingleCellExperiment}}. 9 | #' @param thresh weight threshold for assigning cells to lineages. A cell's 10 | #' weight on a certain lineage must be at least this value (default = 11 | #' \code{1/L}, for \code{L} lineages). 12 | #' @return a factor variable that assigns each cell to a particular lineage or 13 | #' set of lineages. 14 | #' 15 | #' @examples 16 | #' data("slingshotExample") 17 | #' rd <- slingshotExample$rd 18 | #' cl <- slingshotExample$cl 19 | #' pto <- slingshot(rd, cl) 20 | #' slingBranchID(pto) 21 | #' 22 | #' @export 23 | setMethod(f = "slingBranchID", 24 | signature = signature(x = "ANY"), 25 | definition = function(x, thresh = NULL){ 26 | L <- length(slingLineages(x)) 27 | if(is.null(thresh)){ 28 | thresh <- 1/L 29 | }else{ 30 | if(thresh < 0 | thresh > 1){ 31 | stop("'thresh' value must be between 0 and 1.") 32 | } 33 | } 34 | return(factor(apply(slingCurveWeights(x) >= thresh, 1, 35 | function(bin){ 36 | paste(which(bin), collapse = ',') 37 | }))) 38 | }) 39 | 40 | #' @rdname slingBranchGraph 41 | #' 42 | #' @description Builds a graph describing the relationships between the 43 | #' different branch assignments. 44 | #' @param x an object containing \code{slingshot} output, generally either a 45 | #' \code{\link{PseudotimeOrdering}} or \code{\link{SingleCellExperiment}}. 46 | #' @param thresh weight threshold for assigning cells to lineages. A cell's 47 | #' weight on a certain lineage must be greater than this value (default = 48 | #' \code{1/L}, for \code{L} lineages). 49 | #' @param max_node_size the \code{size} of the largest node in the graph, for 50 | #' plotting (all others will be drawn proportionally). Default is \code{100}. 51 | #' See \code{\link[igraph]{igraph.plotting}} for more details. 52 | #' @return an \code{igraph} object representing the relationships between 53 | #' lineages. 54 | #' 55 | #' @examples 56 | #' data("slingshotExample") 57 | #' rd <- slingshotExample$rd 58 | #' cl <- slingshotExample$cl 59 | #' pto <- slingshot(rd, cl) 60 | #' slingBranchGraph(pto) 61 | #' 62 | #' @export 63 | setMethod(f = "slingBranchGraph", 64 | signature = signature(x = "ANY"), 65 | definition = function(x, thresh = NULL, max_node_size = 100){ 66 | brID <- slingBranchID(x, thresh = thresh) 67 | nodes <- as.character(levels(brID)) 68 | which.lin <- strsplit(nodes, split='[,]') 69 | nlins <- vapply(which.lin, length, 0) 70 | maxL <- max(nlins) 71 | if(maxL == 1){ # only one lineage 72 | g <- igraph::graph_from_literal(1) 73 | igraph::vertex_attr(g, 'cells') <- length(brID) 74 | igraph::vertex_attr(g, 'size') <- max_node_size 75 | return(g) 76 | } 77 | if(length(nodes)==1){ # only one node, possibly multiple lineages 78 | m <- matrix(0, dimnames = list(nodes[1], nodes[1])) 79 | g <- igraph::graph_from_adjacency_matrix(m) 80 | igraph::vertex_attr(g, 'cells') <- length(brID) 81 | igraph::vertex_attr(g, 'size') <- max_node_size 82 | return(g) 83 | } 84 | 85 | el <- NULL 86 | # for each node n at level l 87 | for(l in seq(2,maxL)){ 88 | for(n in nodes[nlins==l]){ 89 | # find all descendants of n 90 | desc <- .under(n, nodes) 91 | for(d in desc){ 92 | if(l - nlins[which(nodes==d)] >= 2){ 93 | # check for intermediates 94 | granddesc <- unique(unlist(lapply(desc, .under, 95 | nodes))) 96 | if(! d %in% granddesc){ 97 | # add edge 98 | el <- rbind(el, c(n, d)) 99 | } 100 | }else{ 101 | # add edge 102 | el <- rbind(el, c(n, d)) 103 | } 104 | } 105 | } 106 | } 107 | g <- igraph::graph_from_edgelist(el) 108 | igraph::vertex_attr(g, 'cells') <- table(brID)[ 109 | igraph::vertex_attr(g)$name] 110 | igraph::vertex_attr(g, 'size') <- max_node_size * 111 | igraph::vertex_attr(g)$cells / 112 | max(igraph::vertex_attr(g)$cells) 113 | return(g) 114 | }) 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /R/conversion.R: -------------------------------------------------------------------------------- 1 | #' @rdname as.SlingshotDataSet 2 | #' @description This function converts objects that contain \code{slingshot} 3 | #' results into a \code{SlingshotDataSet}. 4 | #' @param x an object containing \code{slingshot} output. 5 | #' @param ... additional arguments to pass to object-specific methods. 6 | #' @return A \code{SlingshotDataSet} object containing the \code{slingshot} 7 | #' results from the original object, \code{x}. 8 | #' 9 | #' @seealso \code{\link[TrajectoryUtils]{PseudotimeOrdering}} 10 | #' 11 | #' @examples 12 | #' data("slingshotExample") 13 | #' rd <- slingshotExample$rd 14 | #' cl <- slingshotExample$cl 15 | #' pto <- slingshot(rd, cl, start.clus = '1') 16 | #' as.SlingshotDataSet(pto) 17 | #' 18 | #' @export 19 | setMethod( 20 | f = "as.SlingshotDataSet", 21 | signature = "PseudotimeOrdering", 22 | definition = function(x){ 23 | pto <- x 24 | if(length(slingCurves(pto)) > 0){ 25 | crvs <- slingCurves(pto) 26 | }else{ 27 | crvs <- list() 28 | } 29 | sds <- newSlingshotDataSet(reducedDim = slingReducedDim(pto), 30 | clusterLabels = slingClusterLabels(pto), 31 | lineages = slingLineages(pto), 32 | adjacency = as.matrix( 33 | igraph::as_adjacency_matrix( 34 | slingMST(pto))), 35 | curves = crvs, 36 | slingParams = slingParams(pto)) 37 | return(sds) 38 | }) 39 | 40 | #' @rdname as.SlingshotDataSet 41 | #' @export 42 | setMethod( 43 | f = "as.SlingshotDataSet", 44 | signature = "SingleCellExperiment", 45 | definition = function(x){ 46 | if("slingshot" %in% names(colData(x))){ 47 | return(as.SlingshotDataSet(colData(x)$slingshot)) 48 | } 49 | if("slingshot" %in% names(x@int_metadata)){ 50 | return(x@int_metadata$slingshot) 51 | } 52 | stop('No slingshot results found.') 53 | } 54 | ) 55 | 56 | #' @rdname as.SlingshotDataSet 57 | #' @export 58 | setMethod( 59 | f = "as.SlingshotDataSet", 60 | signature = "SlingshotDataSet", 61 | definition = function(x){ 62 | return(x) 63 | } 64 | ) 65 | 66 | 67 | #' @rdname as.PseudotimeOrdering 68 | #' @description This function converts objects that contain \code{slingshot} 69 | #' results into a \code{\link[TrajectoryUtils]{PseudotimeOrdering}}. 70 | #' @param x an object containing \code{slingshot} output. 71 | #' @param ... additional arguments to pass to object-specific methods. 72 | #' @return A \code{PseudotimeOrdering} object containing the \code{slingshot} 73 | #' results from the original object, \code{x}. 74 | #' 75 | #' @examples 76 | #' data("slingshotExample") 77 | #' rd <- slingshotExample$rd 78 | #' cl <- slingshotExample$cl 79 | #' library(SingleCellExperiment) 80 | #' u <- matrix(rpois(140*50, 5), nrow = 50) 81 | #' sce <- SingleCellExperiment(assays = list(counts = u), 82 | #' reducedDims = SimpleList(PCA = rd), 83 | #' colData = data.frame(clus = cl)) 84 | #' sce <- slingshot(sce, clusterLabels = 'clus', reducedDim = 'PCA') 85 | #' as.PseudotimeOrdering(sce) 86 | #' 87 | #' @import TrajectoryUtils 88 | #' @export 89 | setMethod( 90 | f = "as.PseudotimeOrdering", 91 | signature = "SlingshotDataSet", 92 | definition = function(x){ 93 | sds <- x 94 | if(length(slingCurves(sds)) > 0){ 95 | ps <- list(pseudotime = slingPseudotime(sds), 96 | weights = slingCurveWeights(sds)) 97 | meta <- list(lineages = slingLineages(sds), 98 | mst = igraph::graph_from_adjacency_matrix( 99 | slingMST(sds), mode = "undirected"), 100 | curves = slingCurves(sds), 101 | slingParams = slingParams(sds)) 102 | }else if(length(slingLineages(sds)) > 0){ 103 | cl <- slingClusterLabels(sds) 104 | wts <- vapply(slingLineages(sds), function(lin){ 105 | rowSums(cl[,colnames(cl) %in% lin]) 106 | }, rep(0,nrow(reducedDim(sds)))) 107 | pst <- wts 108 | pst[,] <- NA 109 | ps <- list(pseudotime = pst, 110 | weights = wts) 111 | meta <- list(lineages = slingLineages(sds), 112 | mst = igraph::graph_from_adjacency_matrix( 113 | slingMST(sds), 114 | mode = "undirected"), 115 | slingParams = slingParams(sds)) 116 | }else{ 117 | stop("'as.PseudotimeOrdering' coversion failed: ", 118 | "number of lineages could not be determined.") 119 | } 120 | 121 | # include cluster center coordinates 122 | centers <- rowmean(reducedDim(sds), slingClusterLabels(sds)) 123 | coord.list <- vector("list", nrow(centers)) 124 | names(coord.list) <- rownames(centers) 125 | for (r in rownames(centers)) { 126 | coord.list[[r]] <- centers[r,] 127 | } 128 | igraph::V(meta$mst)$coordinates <- 129 | coord.list[names(igraph::V(meta$mst))] 130 | 131 | pto <- PseudotimeOrdering(pathStats = ps, metadata = meta) 132 | cellData(pto)$reducedDim <- reducedDim(sds) 133 | cellData(pto)$clusterLabels <- slingClusterLabels(sds) 134 | return(pto) 135 | }) 136 | 137 | #' @rdname as.PseudotimeOrdering 138 | #' @export 139 | setMethod( 140 | f = "as.PseudotimeOrdering", 141 | signature = "SingleCellExperiment", 142 | definition = function(x){ 143 | if("slingshot" %in% names(colData(x))){ 144 | return(colData(x)$slingshot) 145 | } 146 | if("slingshot" %in% names(x@int_metadata)){ 147 | return(as.PseudotimeOrdering(x@int_metadata$slingshot)) 148 | } 149 | stop('No slingshot results found.') 150 | }) 151 | 152 | #' @rdname as.PseudotimeOrdering 153 | #' @export 154 | setMethod( 155 | f = "as.PseudotimeOrdering", 156 | signature = "PseudotimeOrdering", 157 | definition = function(x){ x }) -------------------------------------------------------------------------------- /R/embedCurves.R: -------------------------------------------------------------------------------- 1 | #' @rdname embedCurves 2 | #' 3 | #' @description This function takes the output of \code{\link{slingshot}} (or 4 | #' \code{\link{getCurves}}) and attempts to embed the curves in a different 5 | #' coordinate space than the one in which they were constructed. This should 6 | #' be considered a visualization tool, only. 7 | #' 8 | #' @param x an object containing \code{\link{slingshot}} output. 9 | #' @param newDimRed a matrix representing the new coordinate space in which to 10 | #' embed the curves. 11 | #' @param shrink logical or numeric between 0 and 1, determines whether and how 12 | #' much to shrink branching lineages toward their average prior to the split. 13 | #' @param stretch numeric factor by which curves can be extrapolated beyond 14 | #' endpoints. Default is \code{2}, see 15 | #' \code{\link[princurve]{principal_curve}}. 16 | #' @param approx_points numeric, whether curves should be approximated by a 17 | #' fixed number of points. If \code{FALSE} (or 0), no approximation will be 18 | #' performed and curves will contain as many points as the input data. If 19 | #' numeric, curves will be approximated by this number of points; preferably 20 | #' about 100 (see \code{\link[princurve]{principal_curve}}). 21 | #' @param smoother, choice of scatter plot smoother. Same as 22 | #' \code{\link[princurve]{principal_curve}}, but \code{"lowess"} option is 23 | #' replaced with \code{"loess"} for additional flexibility. 24 | #' @param shrink.method character denoting how to determine the appropriate 25 | #' amount of shrinkage for a branching lineage. Accepted values are the same 26 | #' as for \code{kernel} in \code{\link{density}} (default is \code{"cosine"}), 27 | #' as well as \code{"tricube"} and \code{"density"}. See 'Details' for more. 28 | #' @param ... Additional parameters to pass to scatter plot smoothing function, 29 | #' \code{smoother}. 30 | #' 31 | #' @details Many of the same parameters are used here as in \code{getCurves}. 32 | #' This function attempts to translate curves from one reduced dimensional 33 | #' space to another by predicting each dimension as a function of pseudotime 34 | #' (ie. the new curve is determined by a series of scatterplot smoothers 35 | #' predicting the coordinates in the new space as a function of pseudotime). 36 | #' Because the pseudotime values are not changed, this amounts to a single 37 | #' iteration of the iterative curve-fitting process used by \code{getCurves}. 38 | #' 39 | #' @details Note that non-linear dimensionality reduction techniques (such as 40 | #' tSNE and UMAP) may produce discontinuities not observed in other spaces. 41 | #' Use caution when embedding curves in these spaces. 42 | #' 43 | #' @return a \code{\link{PseudotimeOrdering}} object containing curves in the 44 | #' new space. 45 | #' 46 | #' @examples 47 | #' data("slingshotExample") 48 | #' rd <- slingshotExample$rd 49 | #' cl <- slingshotExample$cl 50 | #' pto <- slingshot(rd, cl, start.clus = '1') 51 | #' rd2 <- cbind(rd[,2] + rnorm(nrow(rd)), -rd[,1] + rnorm(nrow(rd))) 52 | #' pto.new <- embedCurves(pto, rd2) 53 | #' pto.new 54 | #' 55 | #' plot(rd2, col = cl, asp = 1) 56 | #' lines(SlingshotDataSet(pto.new), lwd = 3) 57 | #' 58 | #' @importFrom princurve project_to_curve 59 | #' @export 60 | setMethod(f = "embedCurves", 61 | signature = signature(x = "PseudotimeOrdering", 62 | newDimRed = "matrix"), 63 | definition = function(x, newDimRed, 64 | shrink = NULL, stretch = NULL, 65 | approx_points = NULL, smoother = NULL, 66 | shrink.method = NULL, ...){ 67 | # SETUP for checks 68 | pto <- x 69 | X <- slingReducedDim(pto) 70 | newX <- newDimRed 71 | 72 | # if new arguments are not provided, use existing arguments 73 | if(is.null(shrink)){ 74 | shrink <- slingParams(pto)$shrink 75 | } 76 | # some were not previously included in slingParams output, so we 77 | # assume the default values were used 78 | if(is.null(stretch)){ 79 | stretch <- ifelse(is.null(slingParams(pto)$stretch), 2, 80 | slingParams(pto)$stretch) 81 | } 82 | if(is.null(approx_points)){ 83 | approx_points <- ifelse( 84 | is.null(slingParams(pto)$approx_points), 85 | FALSE, slingParams(pto)$approx_points) 86 | } 87 | if(is.null(smoother)){ 88 | smoother <- ifelse( 89 | is.null(slingParams(pto)$smoother), 90 | 'smooth.spline', slingParams(pto)$smoother) 91 | } 92 | if(is.null(shrink.method)){ 93 | shrink.method <- slingParams(pto)$shrink.method 94 | } 95 | 96 | # CHECKS 97 | if(length(slingCurves(pto)) == 0){ 98 | stop("No slingshot curves found in original space.") 99 | } 100 | if(shrink < 0 | shrink > 1){ 101 | stop("'shrink' parameter must be logical or numeric between", 102 | " 0 and 1") 103 | } 104 | if(nrow(X)!=nrow(newX)){ 105 | stop("'newX' must have same number of rows as original", 106 | "'reducedDim'.") 107 | } 108 | if(any(is.na(newX))){ 109 | stop("'newX' cannot contain missing values.") 110 | } 111 | if(!all(apply(newX,2,is.numeric))){ 112 | stop("'newX' must only contain numeric values.") 113 | } 114 | if(is.null(rownames(newX))){ 115 | rownames(newX) <- rownames(X) 116 | } 117 | if(is.null(colnames(newX))){ 118 | colnames(newX) <- paste('Dim',seq_len(ncol(newX)),sep='-') 119 | } 120 | if(any(rownames(newX)=='')){ 121 | miss.ind <- which(rownames(newX) == '') 122 | rownames(newX)[miss.ind] <- rownames(X)[miss.ind] 123 | } 124 | if(any(colnames(newX)=='')){ 125 | miss.ind <- which(colnames(newX) == '') 126 | colnames(newX)[miss.ind] <- paste('Dim',miss.ind,sep='-') 127 | } 128 | 129 | # SETUP 130 | p.new <- ncol(newX) 131 | lineages <- slingLineages(pto) 132 | L <- length(grep("Lineage", names(lineages))) #number of lineages 133 | clusters <- colnames(slingClusterLabels(pto)) 134 | d <- dim(X); n <- d[1] 135 | 136 | C <- as.matrix(vapply(lineages[seq_len(L)], function(lin) { 137 | vapply(clusters, function(clID) { 138 | as.numeric(clID %in% lin) 139 | }, 0) 140 | }, rep(0,length(clusters)))) 141 | rownames(C) <- clusters 142 | segmnts <- unique(C[rowSums(C)>1,,drop = FALSE]) 143 | segmnts <- segmnts[order(rowSums(segmnts),decreasing = FALSE), , 144 | drop = FALSE] 145 | avg.order <- list() 146 | for(i in seq_len(nrow(segmnts))){ 147 | idx <- segmnts[i,] == 1 148 | avg.order[[i]] <- colnames(segmnts)[idx] 149 | new.col <- rowMeans(segmnts[,idx, drop = FALSE]) 150 | segmnts <- cbind(segmnts[, !idx, drop = FALSE],new.col) 151 | colnames(segmnts)[ncol(segmnts)] <- paste('average',i,sep='') 152 | } 153 | 154 | # DEFINE SMOOTHER FUNCTION 155 | smootherFcn <- switch(smoother, loess = function(lambda, xj, 156 | w = NULL, ...){ 157 | loess(xj ~ lambda, weights = w, ...)$fitted 158 | }, smooth.spline = function(lambda, xj, w = NULL, ..., df = 5, 159 | tol = 1e-4){ 160 | # fit <- smooth.spline(lambda, xj, w = w, ..., df = df, 161 | # tol = tol, keep.data = FALSE) 162 | fit <- tryCatch({ 163 | smooth.spline(lambda, xj, w = w, ..., df = df, 164 | tol = tol, keep.data = FALSE) 165 | }, error = function(e){ 166 | smooth.spline(lambda, xj, w = w, ..., df = df, 167 | tol = tol, keep.data = FALSE, spar = 1) 168 | }) 169 | predict(fit, x = lambda)$y 170 | }) 171 | 172 | pcurves <- slingCurves(pto) 173 | 174 | # for each curve, 175 | # construct a new curve by predicting each (new) dimension as a 176 | # function of pseudotime. 177 | for(l in seq_len(L)){ 178 | pcurve <- pcurves[[l]] 179 | ordL <- order(pcurve$lambda) 180 | s <- matrix(NA, nrow = n, ncol = p.new) 181 | 182 | if(approx_points > 0){ 183 | xout_lambda <- seq(min(pcurve$lambda), max(pcurve$lambda), 184 | length.out = approx_points) 185 | s <- matrix(NA, nrow = approx_points, ncol = p.new) 186 | } 187 | for(jj in seq_len(p.new)){ 188 | yjj <- smootherFcn(pcurve$lambda, newX[,jj], w = pcurve$w, 189 | ...)[ordL] 190 | if(approx_points > 0){ 191 | yjj <- approx(x = pcurve$lambda[ordL], y = yjj, 192 | xout = xout_lambda, ties = 'ordered')$y 193 | } 194 | s[, jj] <- yjj 195 | } 196 | new.pcurve <- project_to_curve(newX, s = s, stretch = stretch) 197 | if(approx_points > 0){ 198 | xout_lambda <- seq(min(new.pcurve$lambda), 199 | max(new.pcurve$lambda), 200 | length.out = approx_points) 201 | new.pcurve$s <- apply(new.pcurve$s, 2, function(sjj){ 202 | return(approx(x = new.pcurve$lambda[new.pcurve$ord], 203 | y = sjj[new.pcurve$ord], 204 | xout = xout_lambda, ties = 'ordered')$y) 205 | }) 206 | new.pcurve$ord <- seq_len(approx_points) 207 | } 208 | new.pcurve$dist_ind <- abs(new.pcurve$dist_ind) 209 | new.pcurve$lambda <- new.pcurve$lambda - 210 | min(new.pcurve$lambda, na.rm = TRUE) 211 | new.pcurve$w <- pcurve$w 212 | pcurves[[l]] <- new.pcurve 213 | } 214 | 215 | # shrink together lineages near shared cells 216 | if(shrink > 0){ 217 | if(max(rowSums(C)) > 1){ 218 | 219 | segmnts <- unique(C[rowSums(C)>1,,drop=FALSE]) 220 | segmnts <- segmnts[order(rowSums(segmnts), 221 | decreasing = FALSE), 222 | , drop = FALSE] 223 | seg.mix <- segmnts 224 | avg.lines <- list() 225 | pct.shrink <- list() 226 | 227 | # determine average curves and amount of shrinkage 228 | for(i in seq_along(avg.order)){ 229 | ns <- avg.order[[i]] 230 | to.avg <- lapply(ns,function(n){ 231 | if(grepl('Lineage',n)){ 232 | l.ind <- as.numeric(gsub('Lineage','',n)) 233 | return(pcurves[[l.ind]]) 234 | } 235 | if(grepl('average',n)){ 236 | a.ind <- as.numeric(gsub('average','',n)) 237 | return(avg.lines[[a.ind]]) 238 | } 239 | }) 240 | avg <- .avg_curves(to.avg, newX, stretch = stretch, 241 | approx_points = approx_points) 242 | avg.lines[[i]] <- avg 243 | common.ind <- rowMeans( 244 | vapply(to.avg, function(crv){ crv$w > 0 }, 245 | rep(TRUE, n))) == 1 246 | pct.shrink[[i]] <- lapply(to.avg,function(crv){ 247 | .percent_shrinkage(crv, common.ind, 248 | approx_points = approx_points, 249 | method = shrink.method) 250 | }) 251 | # check for degenerate case (if one curve won't be 252 | # shrunk, then the other curve shouldn't be, 253 | # either) 254 | all.zero <- vapply(pct.shrink[[i]], function(pij){ 255 | return(all(pij == 0)) 256 | }, TRUE) 257 | if(any(all.zero)){ 258 | pct.shrink[[i]] <- lapply(pct.shrink[[i]], 259 | function(pij){ 260 | pij[] <- 0 261 | return(pij) 262 | }) 263 | } 264 | } 265 | # do the shrinking in reverse order 266 | for(j in rev(seq_along(avg.lines))){ 267 | ns <- avg.order[[j]] 268 | avg <- avg.lines[[j]] 269 | to.shrink <- lapply(ns,function(n){ 270 | if(grepl('Lineage',n)){ 271 | l.ind <- as.numeric(gsub('Lineage','',n)) 272 | return(pcurves[[l.ind]]) 273 | } 274 | if(grepl('average',n)){ 275 | a.ind <- as.numeric(gsub('average','',n)) 276 | return(avg.lines[[a.ind]]) 277 | } 278 | }) 279 | shrunk <- lapply(seq_along(ns),function(jj){ 280 | crv <- to.shrink[[jj]] 281 | return(.shrink_to_avg(crv, avg, 282 | pct.shrink[[j]][[jj]] * shrink, 283 | newX, approx_points = approx_points, 284 | stretch = stretch)) 285 | }) 286 | for(jj in seq_along(ns)){ 287 | n <- ns[jj] 288 | if(grepl('Lineage',n)){ 289 | l.ind <- as.numeric(gsub('Lineage','',n)) 290 | pcurves[[l.ind]] <- shrunk[[jj]] 291 | } 292 | if(grepl('average',n)){ 293 | a.ind <- as.numeric(gsub('average','',n)) 294 | avg.lines[[a.ind]] <- shrunk[[jj]] 295 | } 296 | } 297 | } 298 | } 299 | } 300 | 301 | # use the new curves, but keep existing pseudotime, weights, etc. 302 | newCurves <- lapply(seq_len(L), function(l){ 303 | crv <- list(s = pcurves[[l]]$s, 304 | ord = pcurves[[l]]$ord, 305 | lambda = slingCurves(pto)[[l]]$lambda, 306 | dist_ind = slingCurves(pto)[[l]]$dist_ind, 307 | dist = slingCurves(pto)[[l]]$dist, 308 | w = slingCurves(pto)[[l]]$w) 309 | class(crv) <- "principal_curve" 310 | return(crv) 311 | }) 312 | 313 | params <- slingParams(pto) 314 | params$shrink <- shrink 315 | params$stretch <- stretch 316 | params$approx_points <- approx_points 317 | params$smoother <- smoother 318 | params$shrink.method <- shrink.method 319 | params$embedding <- TRUE 320 | 321 | out <- pto 322 | metadata(out)$curves <- newCurves 323 | metadata(out)$slingParams <- params 324 | cellData(out)$reducedDim <- newX 325 | 326 | validObject(out) 327 | return(out) 328 | }) 329 | 330 | 331 | #' @rdname embedCurves 332 | #' @export 333 | setMethod(f = "embedCurves", 334 | signature = signature(x = "SingleCellExperiment", 335 | newDimRed = "matrix"), 336 | definition = function(x, newDimRed, 337 | shrink = NULL, stretch = NULL, 338 | approx_points = NULL, smoother = NULL, 339 | shrink.method = NULL, ...){ 340 | embedCurves(x = as.PseudotimeOrdering(x), 341 | newDimRed = newDimRed, 342 | shrink = shrink, 343 | stretch = stretch, 344 | approx_points = approx_points, 345 | smoother = smoother, 346 | shrink.method = shrink.method, ...) 347 | }) 348 | 349 | #' @rdname embedCurves 350 | #' @importFrom SingleCellExperiment reducedDim 351 | #' @export 352 | setMethod(f = "embedCurves", 353 | signature = signature(x = "SingleCellExperiment", 354 | newDimRed = "character"), 355 | definition = function(x, newDimRed, 356 | shrink = NULL, stretch = NULL, 357 | approx_points = NULL, smoother = NULL, 358 | shrink.method = NULL, ...){ 359 | embedCurves(x = as.PseudotimeOrdering(x), 360 | newDimRed = reducedDim(x, newDimRed), 361 | shrink = shrink, 362 | stretch = stretch, 363 | approx_points = approx_points, 364 | smoother = smoother, 365 | shrink.method = shrink.method, ...) 366 | }) 367 | 368 | -------------------------------------------------------------------------------- /R/getLineages.R: -------------------------------------------------------------------------------- 1 | #' @rdname getLineages 2 | #' 3 | #' @description This function constructs the minimum spanning tree(s) on 4 | #' clusters of cells, the first step in Slingshot's trajectory inference 5 | #' procedure. Paths through the MST from an origin cluster to leaf node 6 | #' clusters are interpreted as lineages. 7 | #' 8 | #' @param data a data object containing the matrix of coordinates to be used for 9 | #' lineage inference. Supported types include \code{matrix}, 10 | #' \code{\link{SingleCellExperiment}}, \code{\link{SlingshotDataSet}}, and 11 | #' \code{\link[TrajectoryUtils]{PseudotimeOrdering}}. 12 | #' @param clusterLabels each cell's cluster assignment. This can be a single 13 | #' vector of labels, or a \code{#cells} by \code{#clusters} matrix 14 | #' representing weighted cluster assignment. Either representation may 15 | #' optionally include a \code{"-1"} group meaning "unclustered." 16 | #' @param reducedDim (optional) the dimensionality reduction to be used. Can be 17 | #' a matrix or a character identifying which element of 18 | #' \code{reducedDim(data)} is to be used. If multiple dimensionality 19 | #' reductions are present and this argument is not provided, the first element 20 | #' will be used by default. 21 | #' @param start.clus (optional) character, indicates the starting cluster(s) 22 | #' from which lineages will be drawn. 23 | #' @param end.clus (optional) character, indicates which cluster(s) will be 24 | #' forced to be leaf nodes in the graph. 25 | #' @param dist.method (optional) character, specifies the method for calculating 26 | #' distances between clusters. Default is \code{"slingshot"}, see 27 | #' \code{\link[TrajectoryUtils]{createClusterMST}} for details. 28 | #' @param use.median logical, whether to use the median (instead of mean) when 29 | #' calculating cluster centroid coordinates. 30 | #' @param omega (optional) numeric or logical, this granularity parameter 31 | #' determines the distance between every real cluster and the artificial 32 | #' cluster, \code{.OMEGA}. In practice, this makes \code{omega} the maximum 33 | #' allowable distance between two connected clusters. By default, \code{omega 34 | #' = Inf}. If \code{omega = TRUE}, the maximum edge length will be set to the 35 | #' median edge length of the unsupervised MST times a scaling factor 36 | #' (\code{omega_scale}, default \code{= 1.5}). This value is provided as a 37 | #' potentially useful rule of thumb for datasets with outlying clusters or 38 | #' multiple, distinct trajectories. See \code{outgroup} in 39 | #' \code{\link[TrajectoryUtils]{createClusterMST}}. 40 | #' @param omega_scale (optional) numeric, scaling factor to use when \code{omega 41 | #' = TRUE}. The maximum edge length will be set to the median edge length of 42 | #' the unsupervised MST times \code{omega_scale} (default \code{= 3}). See 43 | #' \code{outscale} in \code{\link[TrajectoryUtils]{createClusterMST}}. 44 | #' @param times numeric, vector of external times associated with either 45 | #' clusters or cells. See \code{\link[TrajectoryUtils]{defineMSTPaths}} for 46 | #' details. 47 | #' 48 | #' @details Given a reduced-dimension data matrix \code{n} by \code{p} and a set 49 | #' of cluster identities (potentially including a \code{"-1"} group for 50 | #' "unclustered"), this function infers a tree (or forest) structure on the 51 | #' clusters. This work is now mostly handled by the more general function, 52 | #' \code{\link[TrajectoryUtils]{createClusterMST}}. 53 | #' 54 | #' @details The graph of this structure is learned by fitting a (possibly 55 | #' constrained) minimum-spanning tree on the clusters, plus the artificial 56 | #' cluster, \code{.OMEGA}, which is a fixed distance away from every real 57 | #' cluster. This effectively limits the maximum branch length in the MST to 58 | #' the chosen distance, meaning that the output may contain multiple trees. 59 | #' 60 | #' @details Once the graph is known, lineages are identified in 61 | #' any tree with at least two clusters. For a given tree, if there is an 62 | #' annotated starting cluster, every possible path out of a starting cluster 63 | #' and ending in a leaf that isn't another starting cluster will be returned. 64 | #' If no starting cluster is annotated, one will be chosen by a heuristic 65 | #' method, but this is not recommended. 66 | #' 67 | #' @return An object of class \code{\link{PseudotimeOrdering}}. Although the 68 | #' final pseudotimes have not yet been calculated, the assay slot of this 69 | #' object contains two elements: \code{pseudotime}, a matrix of \code{NA} 70 | #' values; and \code{weights}, a preliminary matrix of lineage assignment 71 | #' weights. The \code{reducedDim} and \code{clusterLabels} matrices will be 72 | #' stored in the \code{\link[TrajectoryUtils]{cellData}}. Additionally, the 73 | #' \code{metadata} slot will contain an \code{\link[igraph]{igraph}} object 74 | #' named \code{mst}, a list of parameter values named \code{slingParams}, and 75 | #' a list of lineages (ordered sets of clusters) named \code{lineages}. 76 | #' 77 | #' @examples 78 | #' data("slingshotExample") 79 | #' rd <- slingshotExample$rd 80 | #' cl <- slingshotExample$cl 81 | #' pto <- getLineages(rd, cl, start.clus = '1') 82 | #' 83 | #' # plotting 84 | #' sds <- as.SlingshotDataSet(pto) 85 | #' plot(rd, col = cl, asp = 1) 86 | #' lines(sds, type = 'l', lwd = 3) 87 | #' 88 | #' @export 89 | #' 90 | #' @import TrajectoryUtils 91 | #' 92 | setMethod(f = "getLineages", 93 | signature = signature(data = "matrix", 94 | clusterLabels = "matrix"), 95 | definition = function(data, clusterLabels, reducedDim = NULL, 96 | start.clus = NULL, end.clus = NULL, 97 | dist.method = "slingshot", use.median = FALSE, 98 | omega = FALSE, omega_scale = 1.5, 99 | times = NULL, ...){ 100 | 101 | X <- as.matrix(data) 102 | clusterLabels <- as.matrix(clusterLabels) 103 | #################### 104 | ### CHECKS 105 | #################### 106 | if(nrow(X)==0){ 107 | stop('reducedDim has zero rows.') 108 | } 109 | if(ncol(X)==0){ 110 | stop('reducedDim has zero columns.') 111 | } 112 | if(nrow(X) != nrow(clusterLabels)){ 113 | stop('nrow(data) must equal nrow(clusterLabels).') 114 | } 115 | if(any(is.na(X))){ 116 | stop('reducedDim cannot contain missing values.') 117 | } 118 | if(!all(apply(X,2,is.numeric))){ 119 | stop('reducedDim must only contain numeric values.') 120 | } 121 | if (is.null(rownames(X)) & 122 | is.null(rownames(clusterLabels))) { 123 | rownames(X) <- paste('Cell', seq_len(nrow(X)), sep = '-') 124 | rownames(clusterLabels) <- 125 | paste('Cell', seq_len(nrow(X)), sep = '-') 126 | } 127 | if(is.null(colnames(X))){ 128 | colnames(X) <- paste('Dim',seq_len(ncol(X)),sep='-') 129 | } 130 | if(is.null(colnames(clusterLabels))) { 131 | colnames(clusterLabels) <- seq_len(ncol(clusterLabels)) 132 | } 133 | if(any(colnames(clusterLabels) == "")){ 134 | colnames(clusterLabels)[colnames(clusterLabels)==""] <- 135 | which(colnames(clusterLabels)=="") 136 | } 137 | if(any(rownames(X)=='')){ 138 | miss.ind <- which(rownames(X) == '') 139 | rownames(X)[miss.ind] <- paste('Cell',miss.ind,sep='-') 140 | } 141 | if(any(colnames(X)=='')){ 142 | miss.ind <- which(colnames(X) == '') 143 | colnames(X)[miss.ind] <- paste('Dim',miss.ind,sep='-') 144 | } 145 | if(is.null(rownames(clusterLabels)) & 146 | !is.null(rownames(X))){ 147 | rownames(clusterLabels) <- rownames(X) 148 | } 149 | if(is.null(rownames(X)) & 150 | !is.null(rownames(clusterLabels))){ 151 | rownames(X) <- rownames(clusterLabels) 152 | } 153 | if(any(rowSums(clusterLabels)>1)){ 154 | rs <- rowSums(clusterLabels) 155 | clusterLabels <- clusterLabels / rs 156 | } 157 | if(any(colSums(clusterLabels)==0)){ 158 | clusterLabels <- clusterLabels[, colSums(clusterLabels)!=0, 159 | drop = FALSE] 160 | } 161 | if(length(omega) > 1){ 162 | stop('omega must have length 1') 163 | } 164 | if(is.na(as.numeric(omega))){ 165 | stop('omega must be logical or numeric') 166 | } 167 | if(omega < 0){ 168 | stop('omega must be non-negative') 169 | } 170 | 171 | # set up, remove unclustered cells (-1's) 172 | clusterLabels <- clusterLabels[, colnames(clusterLabels) != -1, 173 | drop = FALSE] 174 | clusters <- colnames(clusterLabels) 175 | nclus <- length(clusters) 176 | if(!is.null(start.clus)){ 177 | start.clus <- as.character(start.clus) 178 | } 179 | if(!is.null(end.clus)){ 180 | end.clus <- as.character(end.clus) 181 | } 182 | 183 | ### make the MST / forest (multiple MSTs) 184 | if(nclus == 1){ 185 | dmat <- matrix(0) 186 | rownames(dmat) <- colnames(dmat) <- clusters 187 | g <- igraph::graph_from_adjacency_matrix(dmat, 188 | mode = "undirected", 189 | weighted = TRUE) 190 | igraph::V(g)$coordinates <- list(clusters = colMeans(X)) 191 | 192 | lineages <- list('Lineage1' = clusters) 193 | }else{ 194 | use <- which(rowSums(clusterLabels) > 0) 195 | g <- createClusterMST(x = X[use, ,drop=FALSE], 196 | clusters = clusterLabels[use, ,drop=FALSE], 197 | outgroup = omega, outscale = omega_scale, 198 | endpoints = end.clus, 199 | dist.method = dist.method, 200 | use.median = use.median, ...) 201 | 202 | 203 | # select root nodes (one per connected component of g) 204 | forest <- igraph::decompose(g) 205 | starts <- vapply(forest, function(tree){ 206 | if(length(igraph::V(tree)) == 1){ 207 | return(names(igraph::V(tree))) 208 | } 209 | if(any(start.clus %in% names(igraph::V(tree)))){ 210 | return(start.clus[start.clus %in% 211 | names(igraph::V(tree))][1]) 212 | } 213 | # otherwise, pick root based on highest average length of 214 | # the resulting lineages (~parsimony, maximizing shared parts) 215 | adj <- igraph::as_adjacency_matrix(tree, sparse = FALSE) 216 | leaves <- rownames(adj)[rowSums(adj) == 1] 217 | avg.lineage.length <- vapply(leaves,function(l){ 218 | ends <- leaves[leaves != l] 219 | paths <- igraph::shortest_paths(tree, from = l, to = ends, 220 | mode = 'out', 221 | output = 'vpath')$vpath 222 | mean(vapply(paths, length, 0)) 223 | }, 0) 224 | return(names(avg.lineage.length)[which.max(avg.lineage.length)]) 225 | }, FUN.VALUE = '') 226 | 227 | lineages <- TrajectoryUtils::defineMSTPaths(g, roots = starts, 228 | times = times, 229 | clusters = clusterLabels, 230 | use.median = use.median) 231 | 232 | # sort by number of clusters included 233 | lineages <- lineages[order(vapply(lineages, length, 0), 234 | decreasing = TRUE)] 235 | names(lineages) <- paste('Lineage',seq_along(lineages),sep='') 236 | } 237 | 238 | lineageControl <- list() 239 | first <- unique(vapply(lineages,function(l){ l[1] },'')) 240 | last <- unique(vapply(lineages,function(l){ l[length(l)] },'')) 241 | 242 | lineageControl$start.clus <- first 243 | lineageControl$end.clus <- last 244 | 245 | start.given <- first %in% start.clus 246 | end.given <- last %in% end.clus 247 | lineageControl$start.given <- start.given 248 | lineageControl$end.given <- end.given 249 | 250 | lineageControl$omega <- omega 251 | lineageControl$omega_scale <- omega_scale 252 | 253 | # cells x lineages weights matrix 254 | W <- vapply(seq_along(lineages),function(l){ 255 | rowSums(clusterLabels[, lineages[[l]], drop = FALSE]) 256 | }, rep(0,nrow(X))) # weighting matrix 257 | rownames(W) <- rownames(X) 258 | colnames(W) <- names(lineages) 259 | 260 | # empty pseudotime matrix (to be filled by getCurves) 261 | pst <- W 262 | pst[,] <- NA 263 | 264 | out <- PseudotimeOrdering(pathStats = list(pseudotime = pst, 265 | weights = W), 266 | metadata = list(lineages = lineages, 267 | mst = g, 268 | slingParams = lineageControl)) 269 | cellData(out)$reducedDim <- X 270 | cellData(out)$clusterLabels <- clusterLabels 271 | 272 | validObject(out) 273 | return(out) 274 | } 275 | ) 276 | 277 | #' @rdname getLineages 278 | #' @export 279 | setMethod(f = "getLineages", 280 | signature = signature(data = "matrix", clusterLabels = "character"), 281 | definition = function(data, clusterLabels, ...){ 282 | # CHECKS 283 | clusterLabels <- as.character(clusterLabels) 284 | X <- as.matrix(data) 285 | if(nrow(X) != length(clusterLabels)){ 286 | stop('nrow(data) must equal length(clusterLabels).') 287 | } 288 | if(any(is.na(clusterLabels))){ 289 | message("Cluster labels of 'NA' being treated as unclustered.") 290 | clusterLabels[is.na(clusterLabels)] <- '-1' 291 | } 292 | 293 | # convert clusterLabels into cluster weights matrix 294 | clusters <- unique(clusterLabels) 295 | clusWeight <- vapply(clusters,function(clID){ 296 | as.numeric(clusterLabels == clID) 297 | },rep(0,nrow(X))) 298 | colnames(clusWeight) <- clusters 299 | return(getLineages(data = data, clusterLabels = clusWeight, ...)) 300 | } 301 | ) 302 | 303 | #' @rdname getLineages 304 | #' @export 305 | setMethod(f = "getLineages", 306 | signature = signature(data = "matrix", clusterLabels = "ANY"), 307 | definition = function(data, clusterLabels, ...){ 308 | if(missing(clusterLabels)){ 309 | message('No cluster labels provided. Continuing with ', 310 | 'one cluster.') 311 | clusterLabels <- rep('1', nrow(data)) 312 | } 313 | if(! any(c(length(clusterLabels), nrow(clusterLabels)) == 314 | nrow(data))){ 315 | stop("clusterLabels must have length or number of rows equal', 316 | 'to nrow(data).") 317 | } 318 | return(getLineages(data = data, clusterLabels = clusterLabels, ...)) 319 | }) 320 | 321 | #' @rdname getLineages 322 | #' @export 323 | setMethod(f = "getLineages", 324 | signature = signature(data = "SlingshotDataSet", 325 | clusterLabels = "ANY"), 326 | definition = function(data, clusterLabels, ...){ 327 | return(getLineages(data = reducedDim(data), 328 | clusterLabels = slingClusterLabels(data), ...)) 329 | }) 330 | 331 | #' @rdname getLineages 332 | #' @export 333 | setMethod(f = "getLineages", 334 | signature = signature(data = "PseudotimeOrdering", 335 | clusterLabels = "ANY"), 336 | definition = function(data, clusterLabels, ...){ 337 | return(getLineages(data = cellData(data)$reducedDim, 338 | clusterLabels = cellData(data)$clusterLabels, 339 | ...)) 340 | }) 341 | 342 | #' @rdname getLineages 343 | #' @export 344 | setMethod(f = "getLineages", 345 | signature = signature(data = "data.frame", 346 | clusterLabels = "ANY"), 347 | definition = function(data, clusterLabels, ...){ 348 | RD <- as.matrix(data) 349 | rownames(RD) <- rownames(data) 350 | return(getLineages(data = RD, clusterLabels = clusterLabels, ...)) 351 | }) 352 | 353 | #' @rdname getLineages 354 | #' @export 355 | setMethod(f = "getLineages", 356 | signature = signature(data = "matrix", 357 | clusterLabels = "numeric"), 358 | definition = function(data, clusterLabels, ...){ 359 | return(getLineages(data = data, 360 | clusterLabels = as.character(clusterLabels), ...)) 361 | }) 362 | 363 | #' @rdname getLineages 364 | #' @export 365 | setMethod(f = "getLineages", 366 | signature = signature(data = "matrix", 367 | clusterLabels = "factor"), 368 | definition = function(data, clusterLabels, ...){ 369 | return(getLineages(data = data, 370 | clusterLabels = as.character(clusterLabels), ...)) 371 | }) 372 | 373 | #' @rdname getLineages 374 | #' @export 375 | setMethod(f = "getLineages", 376 | signature = signature(data = "SingleCellExperiment"), 377 | definition = function(data, clusterLabels, reducedDim = NULL, ...){ 378 | # SETUP 379 | # determine the cluster labels and reducedDim matrix 380 | if(is.null(reducedDim)){ 381 | if(length(reducedDims(data))==0){ 382 | stop('No dimensionality reduction found.') 383 | }else{ 384 | message('Dimensionality reduction not explicitly ', 385 | 'chosen. Continuing with ', 386 | names(reducedDims(data))[1]) 387 | rd <- reducedDims(data)[[1]] 388 | } 389 | } 390 | if(length(reducedDim)==1){ 391 | if(reducedDim %in% names(reducedDims(data))){ 392 | rd <- reducedDims(data)[[as.character(reducedDim)]] 393 | }else{ 394 | stop(reducedDim,' not found in reducedDims(data).') 395 | } 396 | }else{ 397 | if(!is.null(dim(reducedDim))){ 398 | rd <- reducedDim 399 | reducedDims(data)$slingReducedDim <- reducedDim 400 | } 401 | } 402 | 403 | if(missing(clusterLabels)){ 404 | message('No cluster labels provided. Continuing with one ', 405 | 'cluster.') 406 | cl <- rep('1', nrow(rd)) 407 | }else{ 408 | if(length(clusterLabels)==1){ 409 | if(clusterLabels %in% colnames(colData(data))){ 410 | cl <- colData(data)[[as.character(clusterLabels)]] 411 | }else{ 412 | stop(clusterLabels,' not found in colData(data).') 413 | } 414 | } 415 | if(length(clusterLabels)>1){ 416 | if(!is.null(dim(clusterLabels)) && 417 | length(dim(clusterLabels)) > 1 && 418 | all(dim(clusterLabels) > 1)){ 419 | cl <- as.matrix(clusterLabels) 420 | colnames(cl) <- paste0('sling_c',seq_len(ncol(cl))) 421 | }else{ 422 | cl <- as.character(clusterLabels) 423 | } 424 | } 425 | } 426 | # run slingshot 427 | pto <- getLineages(data = rd, clusterLabels = cl, 428 | reducedDim = NULL, ...) 429 | # combine getLineages output with SCE 430 | sce <- data 431 | colData(sce)$slingshot <- pto 432 | return(sce) 433 | }) 434 | 435 | 436 | -------------------------------------------------------------------------------- /R/predict.R: -------------------------------------------------------------------------------- 1 | #' @rdname predict.SlingshotDataSet 2 | #' @title Predict from a Slingshot model 3 | #' 4 | #' @description Map new observations onto simultaneous principal curves fitted 5 | #' by \code{slingshot}. 6 | #' 7 | #' @param object a \code{\link[TrajectoryUtils]{PseudotimeOrdering}} or 8 | #' \code{\link{SlingshotDataSet}} containing simultaneous principal curves to 9 | #' use for prediction. 10 | #' @param newdata a matrix or data frame of new points in the same 11 | #' reduced-dimensional space as the original input to \code{slingshot} (or 12 | #' \code{getLineages}). 13 | #' 14 | #' @details This function is a method for the generic function \code{predict} 15 | #' with inputs being either a \code{PseudotimeOrdering} or 16 | #' \code{SlingshotDataSet}. If no \code{newdata} argument is provided, it will 17 | #' return the original results, given by \code{object}. 18 | #' 19 | #' @return An object of the same type as \code{object}, based on the input 20 | #' \code{newdata}. New cells are treated as "unclustered", but other metadata 21 | #' is preserved. The \code{curves} slot represents the projections of each new 22 | #' cell onto the existing curves. As with standard \code{slingshot} output, 23 | #' the lineage-specific pseudotimes and assignment weights can be accessed via 24 | #' the functions \code{\link{slingPseudotime}} and 25 | #' \code{\link{slingCurveWeights}}. 26 | #' 27 | #' @seealso \code{\link{slingshot}} 28 | #' 29 | #' @examples 30 | #' data("slingshotExample") 31 | #' rd <- slingshotExample$rd 32 | #' cl <- slingshotExample$cl 33 | #' pto <- slingshot(rd, cl, start.clus = '1') 34 | #' 35 | #' x <- cbind(runif(100, min = -5, max = 10), runif(100, min = -4, max = 4)) 36 | #' predict(pto, x) 37 | #' 38 | #' @export 39 | setMethod(f = "predict", 40 | signature = signature(object = "PseudotimeOrdering"), 41 | definition = function(object, newdata = NULL){ 42 | # setup 43 | pto <- object 44 | if(is.null(newdata)){ 45 | return(pto) 46 | }else{ 47 | x <- as.matrix(newdata) 48 | } 49 | n0 <- nrow(slingReducedDim(pto)) 50 | curves <- slingCurves(pto) 51 | L <- length(curves) 52 | 53 | # checks 54 | if(ncol(x) != ncol(slingReducedDim(pto))){ 55 | stop('New data does not match original number of dimensions.\n', 56 | 'Original: ',ncol(slingReducedDim(pto)),' columns\n', 57 | 'New data: ',ncol(x),' columns') 58 | } 59 | if(nrow(x)==0){ 60 | stop('newdata has zero rows.') 61 | } 62 | if(any(is.na(x))){ 63 | stop('newdata cannot contain missing values.') 64 | } 65 | if(!all(apply(x,2,is.numeric))){ 66 | stop('newdata must only contain numeric values.') 67 | } 68 | if(is.null(rownames(x))){ 69 | rownames(x) <- paste('newCell', seq_len(nrow(x)), sep = '-') 70 | } 71 | if(is.null(colnames(x))){ 72 | colnames(x) <- colnames(slingReducedDim(pto)) 73 | } 74 | if(any(colnames(x) != colnames(slingReducedDim(pto)))){ 75 | colnames(x) <- colnames(slingReducedDim(pto)) 76 | } 77 | if(any(rownames(x)=='')){ 78 | miss.ind <- which(rownames(x) == '') 79 | rownames(x)[miss.ind] <- paste('newCell',miss.ind,sep='-') 80 | } 81 | 82 | D.orig <- vapply(curves, function(crv){ crv$dist_ind }, rep(0, n0)) 83 | W.orig <- vapply(curves, function(crv){ crv$w }, rep(0, n0)) 84 | 85 | ordD.orig <- order(D.orig) 86 | W.prob.orig <- W.orig/rowSums(W.orig) 87 | W.prob.orig[is.nan(W.prob.orig)] <- 0 88 | WrnkD.orig <- cumsum(W.prob.orig[ordD.orig]) / sum(W.prob.orig) 89 | WrnkD.orig[WrnkD.orig > 1] <- 1 90 | Z.orig <- D.orig 91 | Z.orig[ordD.orig] <- WrnkD.orig 92 | 93 | dists.tofit <- as.numeric(D.orig[W.orig > 0]) 94 | eps <- .5*min(dists.tofit[dists.tofit>0]) 95 | logdists.tofit <- log(dists.tofit + eps) 96 | quants.tofit <- as.numeric(Z.orig[W.orig > 0]) 97 | 98 | suppressWarnings({ 99 | fit <- glm(quants.tofit ~ logdists.tofit, family = 'binomial') 100 | }) 101 | 102 | crv.proj <- lapply(curves, function(crv){ 103 | project_to_curve(x, crv$s[crv$ord, , drop = FALSE], stretch = 0) 104 | }) 105 | 106 | D.proj <- vapply(crv.proj, function(crv){ crv$dist_ind }, 107 | rep(0,nrow(x))) 108 | Z.proj <- D.proj 109 | Z.proj[,] <- predict(fit, 110 | newdata = data.frame(logdists.tofit = as.numeric(log(D.proj+eps))), 111 | type = 'response') 112 | 113 | Z.prime.proj <- 1-Z.proj^2 114 | W.proj <- Z.prime.proj / rowMaxs(Z.prime.proj,na.rm = TRUE) 115 | W.proj[is.nan(W.proj)] <- 1 # handle 0/0 116 | W.proj[is.na(W.proj)] <- 0 117 | W.proj[W.proj > 1] <- 1 118 | W.proj[W.proj < 0] <- 0 119 | 120 | # add if z < .5 121 | idx <- Z.proj < .5 122 | W.proj[idx] <- 1 123 | # drop if z > .9 and w < .1 124 | ridx <- rowMaxs(Z.proj, na.rm = TRUE) > .9 & 125 | rowMins(W.proj, na.rm = TRUE) < .1 126 | W0 <- W.proj[ridx, ] 127 | Z0 <- Z.proj[ridx, ] 128 | W0[!is.na(Z0) & Z0 > .9 & W0 < .1] <- 0 129 | W.proj[ridx, ] <- W0 130 | 131 | for(l in seq_len(L)){ 132 | crv.proj[[l]]$w <- W.proj[,l] 133 | } 134 | 135 | # new cells are all "unclustered" 136 | cl.mat <- matrix(0, nrow = nrow(x), 137 | ncol = ncol(slingClusterLabels(pto))) 138 | colnames(cl.mat) <- colnames(slingClusterLabels(pto)) 139 | rownames(cl.mat) <- rownames(x) 140 | 141 | pst <- vapply(crv.proj, function(pc) { 142 | t <- pc$lambda 143 | t[pc$w == 0] <- NA 144 | return(t) 145 | }, rep(0,nrow(x))) 146 | 147 | out <- PseudotimeOrdering(list(pseudotime = pst, weights = W.proj), 148 | metadata = metadata(pto)) 149 | metadata(out)$curves <- crv.proj 150 | cellData(out)$reducedDim <- x 151 | cellData(out)$clusterLabels <- cl.mat 152 | 153 | return(out) 154 | }) 155 | 156 | #' @rdname predict.SlingshotDataSet 157 | #' @export 158 | setMethod(f = "predict", 159 | signature = signature(object = "SlingshotDataSet"), 160 | definition = function(object, newdata = NULL){ 161 | pto <- as.PseudotimeOrdering(object) 162 | pred <- predict(pto, newdata) 163 | sds <- as.SlingshotDataSet(pred) 164 | return(sds) 165 | }) 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R package: slingshot 2 | [![R build status](https://github.com/kstreet13/slingshot/workflows/R-CMD-check-bioc/badge.svg)](https://github.com/kstreet13/slingshot/actions) 3 | [![Coverage Status](https://img.shields.io/codecov/c/github/kstreet13/slingshot/master.svg)](https://codecov.io/github/kstreet13/slingshot?branch=master) 4 | 5 | 6 | 7 | Provides functions for inferring continuous, branching lineage structures in low-dimensional data. Slingshot was designed to model developmental trajectories in single-cell RNA sequencing data and serve as a component in an analysis pipeline after dimensionality reduction and clustering. It is flexible enough to handle arbitrarily many branching events and allows for the incorporation of prior knowledge through supervised graph construction. 8 | 9 | ## Installation 10 | 11 | ```r 12 | if (!requireNamespace("BiocManager", quietly=TRUE)) 13 | install.packages("BiocManager") 14 | BiocManager::install("kstreet13/slingshot") 15 | ``` 16 | 17 | ## Python Version 18 | An implementation of the Slingshot algorithm in `python` can be found here: https://github.com/mossjacob/pyslingshot 19 | 20 | ## Issues and bug reports 21 | 22 | Please use https://github.com/kstreet13/slingshot/issues to submit issues, bug reports, and comments. 23 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /data-raw/slingshotExample.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | 3 | # provide initial code on how the example dataset was generated 4 | tmp <- tempfile() 5 | download.file("https://github.com/kstreet13/slingshot/raw/d6df7c6f4232c2ee8819d93e644754794b738c81/data/slingshotExample.RData", tmp) 6 | load(tmp) 7 | 8 | # save data object with usethis::use_data 9 | slingshotExample <- list(rd = rd, cl = cl) 10 | usethis::use_data(slingshotExample, overwrite = TRUE) 11 | -------------------------------------------------------------------------------- /data/slingshotExample.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kstreet13/slingshot/3fa552f37148a24a9ef4f7bb5ec00e594a39d8cf/data/slingshotExample.rda -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | bibentry("Article", 2 | title = "Slingshot: cell lineage and pseudotime inference for single-cell transcriptomics", 3 | author = c(person("Kelly", "Street"), 4 | person("Davide", "Risso"), 5 | person("Russell B.", "Fletcher"), 6 | person("Diya", "Das"), 7 | person("John", "Ngai"), 8 | person("Nir", "Yosef"), 9 | person("Elizabeth", "Purdom"), 10 | person("Sandrine", "Dudoit")), 11 | year = 2018, 12 | pages = 477, 13 | journal = "BMC Genomics", 14 | url = "https://doi.org/10.1186/s12864-018-4772-0", 15 | textVersion = 16 | paste("K. Street, D. Risso, RB. Fletcher, D. Das, J. Ngai, N. Yosef, E. Purdom, and S. Dudoit","(2018).", 17 | "Slingshot: cell lineage and pseudotime inference for single-cell transcriptomics. BMC Genomics.") 18 | ) 19 | -------------------------------------------------------------------------------- /inst/REFERENCES.bib: -------------------------------------------------------------------------------- 1 | @Article{HastieStuetzle89, 2 | author = {T. Hastie and W. Stuetzle}, 3 | title = {Principal curves}, 4 | journal = JASA, 5 | year = {1989}, 6 | OPTkey = {}, 7 | volume = {84}, 8 | number = {406}, 9 | pages = {502--516}, 10 | OPTmonth = {}, 11 | OPTnote = {}, 12 | OPTannote = {} 13 | } 14 | -------------------------------------------------------------------------------- /inst/slingshot_sticker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kstreet13/slingshot/3fa552f37148a24a9ef4f7bb5ec00e594a39d8cf/inst/slingshot_sticker.png -------------------------------------------------------------------------------- /man/SlingshotDataSet-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllClasses.R, R/AllHelperFunctions.R 3 | \docType{class} 4 | \name{SlingshotDataSet-class} 5 | \alias{SlingshotDataSet-class} 6 | \alias{show,SlingshotDataSet-method} 7 | \alias{reducedDim,SlingshotDataSet,ANY-method} 8 | \alias{reducedDims,SlingshotDataSet-method} 9 | \title{Class \code{SlingshotDataSet}} 10 | \usage{ 11 | \S4method{show}{SlingshotDataSet}(object) 12 | 13 | \S4method{reducedDim}{SlingshotDataSet,ANY}(x) 14 | 15 | \S4method{reducedDims}{SlingshotDataSet}(x) 16 | } 17 | \arguments{ 18 | \item{object}{a \code{SlingshotDataSet} object.} 19 | 20 | \item{x}{a \code{SlingshotDataSet} object.} 21 | } 22 | \value{ 23 | The accessor functions \code{reducedDim}, \code{clusterLabels}, 24 | \code{lineages}, \code{adjacency}, \code{curves}, 25 | and \code{slingParams} return the corresponding elements of a 26 | \code{SlingshotDataSet}. The functions \code{slingPseudotime} and 27 | \code{slingCurveWeights} extract useful output elements of a 28 | \code{SlingshotDataSet}, provided that curves have already been fit with 29 | either \code{slingshot} or \code{getCurves}. 30 | } 31 | \description{ 32 | This was the original class for storing \code{slingshot} 33 | results, but we now generally reommend using the 34 | \code{\link{PseudotimeOrdering}} class, instead. Most \code{slingshot} 35 | functions will still work with \code{SlingshotDataSet} objects, but will 36 | return \code{PseudotimeOrdering} objects, by default. To update old 37 | \code{SlingshotDataSet} objects, we have provided the 38 | \code{\link{as.PseudotimeOrdering}} conversion function. The only functions 39 | that require \code{SlingshotDataSet} objects are the plotting functions. 40 | 41 | The \code{SlingshotDataSet} class holds data relevant for 42 | performing lineage inference with the \code{slingshot} package, primarily a 43 | reduced dimensional representation of the data and a set of cluster labels. 44 | } 45 | \section{Methods (by generic)}{ 46 | \itemize{ 47 | \item \code{show}: a short summary of a \code{SlingshotDataSet} 48 | object. 49 | 50 | \item \code{reducedDim}: returns the matrix representing the reduced 51 | dimensional dataset. 52 | }} 53 | 54 | \section{Slots}{ 55 | 56 | \describe{ 57 | \item{\code{reducedDim}}{matrix. An \code{n} by \code{p} numeric matrix or data frame 58 | giving the coordinates of the cells in a reduced dimensionality space.} 59 | 60 | \item{\code{clusterLabels}}{matrix or character. An \code{n} by \code{K} matrix of 61 | weights indicating each cell's cluster assignment or a character vector of 62 | cluster assignments, which will be converted into a binary matrix.} 63 | 64 | \item{\code{lineages}}{list. A list with each element a character vector of cluster 65 | names representing a lineage as an ordered set of clusters.} 66 | 67 | \item{\code{adjacency}}{matrix. A binary matrix describing the adjacency 68 | between clusters induced by the minimum spanning tree.} 69 | 70 | \item{\code{curves}}{list. A list of \code{\link[princurve]{principal_curve}} objects 71 | produced by \code{\link{getCurves}}.} 72 | 73 | \item{\code{slingParams}}{list. Additional parameters used by Slingshot. These may 74 | specify how the minimum spanning tree on clusters was constructed: 75 | \itemize{ 76 | \item{\code{start.clus}}{ character. The label of the root cluster, or a 77 | vector of cluster labels giving the root clusters of each disjoint 78 | component of the graph.} 79 | \item{\code{end.clus}}{ character. Vector of cluster labels indicating 80 | terminal clusters.} 81 | \item{\code{start.given}}{ logical. A logical value 82 | indicating whether the initial state was pre-specified.} 83 | \item{\code{end.given}}{ logical. A vector of logical values indicating 84 | whether each terminal state was pre-specified} 85 | \item{\code{omega}}{ numeric or logical. Granularity parameter determining 86 | the maximum edge length for building the MST. See 87 | \code{\link{getLineages}}.} 88 | \item{\code{omega_scale}}{ numeric. Scaling factor used for setting maximum 89 | edge length when \code{omega = TRUE}. See \code{\link{getLineages}}.} } 90 | They may also specify how simultaneous principal curves were constructed 91 | (for a complete listing, see \code{\link{getCurves}}: 92 | \itemize{ 93 | \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 94 | whether and how much to shrink branching lineages toward their shared 95 | average curve.} 96 | \item{\code{extend}}{ character. Specifies the method for handling 97 | root and leaf clusters of lineages when constructing the initial, 98 | piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 99 | See \code{\link{getCurves}} for details.} 100 | \item{\code{reweight}}{ logical. 101 | Indicates whether to allow cells shared 102 | between lineages to be reweighted during curve-fitting. If \code{TRUE}, 103 | cells shared between lineages will be iteratively reweighted based on the 104 | quantiles of their projection distances to each curve.} 105 | \item{\code{reassign}}{ logical. 106 | Indicates whether to reassign cells to lineages at each 107 | iteration. If \code{TRUE}, cells will be added to a lineage when their 108 | projection distance to the curve is less than the median distance for all 109 | cells currently assigned to the lineage. Additionally, shared cells will be 110 | removed from a lineage if their projection distance to the curve is above 111 | the 90th percentile and their weight along the curve is less than 112 | \code{0.1}.} 113 | \item{\code{shrink.method}}{ character. 114 | Denotes how to determine the amount of shrinkage for a branching lineage. 115 | Accepted values are the same as for \code{kernel} in the \code{density} 116 | function (default is \code{"cosine"}), as well as \code{"tricube"} and 117 | \code{"density"}. See \code{\link{getCurves}} for details.} 118 | \item{approx_points}{ numeric. Number of points to use in estimating 119 | curves. See \code{\link{getCurves}} for details.} \item{allow.breaks}{ 120 | logical. Whether to allow curves that diverge very early on in a trajectory 121 | to have different starting points.} 122 | \item{Other parameters specified by 123 | \code{\link[princurve]{principal_curve}}}. }} 124 | }} 125 | 126 | \seealso{ 127 | \code{\link{PseudotimeOrdering}} 128 | } 129 | -------------------------------------------------------------------------------- /man/SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{SlingshotDataSet} 4 | \alias{SlingshotDataSet} 5 | \alias{SlingshotDataSet,SingleCellExperiment-method} 6 | \alias{SlingshotDataSet,SlingshotDataSet-method} 7 | \alias{SlingshotDataSet,PseudotimeOrdering-method} 8 | \title{Extract Slingshot output} 9 | \usage{ 10 | SlingshotDataSet(data, ...) 11 | 12 | \S4method{SlingshotDataSet}{SingleCellExperiment}(data) 13 | 14 | \S4method{SlingshotDataSet}{SlingshotDataSet}(data) 15 | 16 | \S4method{SlingshotDataSet}{PseudotimeOrdering}(data) 17 | } 18 | \arguments{ 19 | \item{data}{an object containing \code{slingshot} output.} 20 | 21 | \item{...}{additional arguments to pass to object-specific methods.} 22 | } 23 | \value{ 24 | A \code{SlingshotDataSet} object containing the output of 25 | \code{slingshot}. 26 | } 27 | \description{ 28 | This is a convenience function to extract a 29 | \code{SlingshotDataSet} from an object containing \code{\link{slingshot}} 30 | output. However, we now recommend using a 31 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}} object, in most cases. 32 | The \code{SlingshotDataSet} is, however, still used for plotting purposes. 33 | } 34 | \examples{ 35 | data("slingshotExample") 36 | rd <- slingshotExample$rd 37 | cl <- slingshotExample$cl 38 | library(SingleCellExperiment) 39 | u <- matrix(rpois(140*50, 5), nrow = 50) 40 | sce <- SingleCellExperiment(assays = list(counts = u), 41 | reducedDims = SimpleList(PCA = rd), 42 | colData = data.frame(clus = cl)) 43 | sce <- slingshot(sce, clusterLabels = 'clus', reducedDim = 'PCA') 44 | SlingshotDataSet(sce) 45 | 46 | } 47 | \seealso{ 48 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}}, 49 | \code{\link{as.SlingshotDataSet}} 50 | } 51 | -------------------------------------------------------------------------------- /man/as.PseudotimeOrdering.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/conversion.R 3 | \name{as.PseudotimeOrdering} 4 | \alias{as.PseudotimeOrdering} 5 | \alias{as.PseudotimeOrdering,SlingshotDataSet-method} 6 | \alias{as.PseudotimeOrdering,SingleCellExperiment-method} 7 | \alias{as.PseudotimeOrdering,PseudotimeOrdering-method} 8 | \title{Conversion to PseudotimeOrdering} 9 | \usage{ 10 | as.PseudotimeOrdering(x, ...) 11 | 12 | \S4method{as.PseudotimeOrdering}{SlingshotDataSet}(x) 13 | 14 | \S4method{as.PseudotimeOrdering}{SingleCellExperiment}(x) 15 | 16 | \S4method{as.PseudotimeOrdering}{PseudotimeOrdering}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{slingshot} output.} 20 | 21 | \item{...}{additional arguments to pass to object-specific methods.} 22 | } 23 | \value{ 24 | A \code{PseudotimeOrdering} object containing the \code{slingshot} 25 | results from the original object, \code{x}. 26 | } 27 | \description{ 28 | This function converts objects that contain \code{slingshot} 29 | results into a \code{\link[TrajectoryUtils]{PseudotimeOrdering}}. 30 | } 31 | \examples{ 32 | data("slingshotExample") 33 | rd <- slingshotExample$rd 34 | cl <- slingshotExample$cl 35 | library(SingleCellExperiment) 36 | u <- matrix(rpois(140*50, 5), nrow = 50) 37 | sce <- SingleCellExperiment(assays = list(counts = u), 38 | reducedDims = SimpleList(PCA = rd), 39 | colData = data.frame(clus = cl)) 40 | sce <- slingshot(sce, clusterLabels = 'clus', reducedDim = 'PCA') 41 | as.PseudotimeOrdering(sce) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /man/as.SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/conversion.R 3 | \name{as.SlingshotDataSet} 4 | \alias{as.SlingshotDataSet} 5 | \alias{as.SlingshotDataSet,PseudotimeOrdering-method} 6 | \alias{as.SlingshotDataSet,SingleCellExperiment-method} 7 | \alias{as.SlingshotDataSet,SlingshotDataSet-method} 8 | \title{Conversion to SlingshotDataSet} 9 | \usage{ 10 | as.SlingshotDataSet(x, ...) 11 | 12 | \S4method{as.SlingshotDataSet}{PseudotimeOrdering}(x) 13 | 14 | \S4method{as.SlingshotDataSet}{SingleCellExperiment}(x) 15 | 16 | \S4method{as.SlingshotDataSet}{SlingshotDataSet}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{slingshot} output.} 20 | 21 | \item{...}{additional arguments to pass to object-specific methods.} 22 | } 23 | \value{ 24 | A \code{SlingshotDataSet} object containing the \code{slingshot} 25 | results from the original object, \code{x}. 26 | } 27 | \description{ 28 | This function converts objects that contain \code{slingshot} 29 | results into a \code{SlingshotDataSet}. 30 | } 31 | \examples{ 32 | data("slingshotExample") 33 | rd <- slingshotExample$rd 34 | cl <- slingshotExample$cl 35 | pto <- slingshot(rd, cl, start.clus = '1') 36 | as.SlingshotDataSet(pto) 37 | 38 | } 39 | \seealso{ 40 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}} 41 | } 42 | -------------------------------------------------------------------------------- /man/embedCurves.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/embedCurves.R 3 | \name{embedCurves} 4 | \alias{embedCurves} 5 | \alias{embedCurves,PseudotimeOrdering,matrix-method} 6 | \alias{embedCurves,SingleCellExperiment,matrix-method} 7 | \alias{embedCurves,SingleCellExperiment,character-method} 8 | \title{Embed trajectory in new space} 9 | \usage{ 10 | embedCurves(x, newDimRed, ...) 11 | 12 | \S4method{embedCurves}{PseudotimeOrdering,matrix}( 13 | x, 14 | newDimRed, 15 | shrink = NULL, 16 | stretch = NULL, 17 | approx_points = NULL, 18 | smoother = NULL, 19 | shrink.method = NULL, 20 | ... 21 | ) 22 | 23 | \S4method{embedCurves}{SingleCellExperiment,matrix}( 24 | x, 25 | newDimRed, 26 | shrink = NULL, 27 | stretch = NULL, 28 | approx_points = NULL, 29 | smoother = NULL, 30 | shrink.method = NULL, 31 | ... 32 | ) 33 | 34 | \S4method{embedCurves}{SingleCellExperiment,character}( 35 | x, 36 | newDimRed, 37 | shrink = NULL, 38 | stretch = NULL, 39 | approx_points = NULL, 40 | smoother = NULL, 41 | shrink.method = NULL, 42 | ... 43 | ) 44 | } 45 | \arguments{ 46 | \item{x}{an object containing \code{\link{slingshot}} output.} 47 | 48 | \item{newDimRed}{a matrix representing the new coordinate space in which to 49 | embed the curves.} 50 | 51 | \item{...}{Additional parameters to pass to scatter plot smoothing function, 52 | \code{smoother}.} 53 | 54 | \item{shrink}{logical or numeric between 0 and 1, determines whether and how 55 | much to shrink branching lineages toward their average prior to the split.} 56 | 57 | \item{stretch}{numeric factor by which curves can be extrapolated beyond 58 | endpoints. Default is \code{2}, see 59 | \code{\link[princurve]{principal_curve}}.} 60 | 61 | \item{approx_points}{numeric, whether curves should be approximated by a 62 | fixed number of points. If \code{FALSE} (or 0), no approximation will be 63 | performed and curves will contain as many points as the input data. If 64 | numeric, curves will be approximated by this number of points; preferably 65 | about 100 (see \code{\link[princurve]{principal_curve}}).} 66 | 67 | \item{smoother, }{choice of scatter plot smoother. Same as 68 | \code{\link[princurve]{principal_curve}}, but \code{"lowess"} option is 69 | replaced with \code{"loess"} for additional flexibility.} 70 | 71 | \item{shrink.method}{character denoting how to determine the appropriate 72 | amount of shrinkage for a branching lineage. Accepted values are the same 73 | as for \code{kernel} in \code{\link{density}} (default is \code{"cosine"}), 74 | as well as \code{"tricube"} and \code{"density"}. See 'Details' for more.} 75 | } 76 | \value{ 77 | a \code{\link{PseudotimeOrdering}} object containing curves in the 78 | new space. 79 | } 80 | \description{ 81 | This function takes the output of \code{\link{slingshot}} (or 82 | \code{\link{getCurves}}) and attempts to embed the curves in a different 83 | coordinate space than the one in which they were constructed. This should 84 | be considered a visualization tool, only. 85 | } 86 | \details{ 87 | Many of the same parameters are used here as in \code{getCurves}. 88 | This function attempts to translate curves from one reduced dimensional 89 | space to another by predicting each dimension as a function of pseudotime 90 | (ie. the new curve is determined by a series of scatterplot smoothers 91 | predicting the coordinates in the new space as a function of pseudotime). 92 | Because the pseudotime values are not changed, this amounts to a single 93 | iteration of the iterative curve-fitting process used by \code{getCurves}. 94 | 95 | Note that non-linear dimensionality reduction techniques (such as 96 | tSNE and UMAP) may produce discontinuities not observed in other spaces. 97 | Use caution when embedding curves in these spaces. 98 | } 99 | \examples{ 100 | data("slingshotExample") 101 | rd <- slingshotExample$rd 102 | cl <- slingshotExample$cl 103 | pto <- slingshot(rd, cl, start.clus = '1') 104 | rd2 <- cbind(rd[,2] + rnorm(nrow(rd)), -rd[,1] + rnorm(nrow(rd))) 105 | pto.new <- embedCurves(pto, rd2) 106 | pto.new 107 | 108 | plot(rd2, col = cl, asp = 1) 109 | lines(SlingshotDataSet(pto.new), lwd = 3) 110 | 111 | } 112 | -------------------------------------------------------------------------------- /man/getCurves.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/getCurves.R 3 | \name{getCurves} 4 | \alias{getCurves} 5 | \alias{getCurves,PseudotimeOrdering-method} 6 | \alias{getCurves,SingleCellExperiment-method} 7 | \alias{getCurves,SlingshotDataSet-method} 8 | \title{Construct Simultaneous Principal Curves} 9 | \usage{ 10 | getCurves(data, ...) 11 | 12 | \S4method{getCurves}{PseudotimeOrdering}( 13 | data, 14 | shrink = TRUE, 15 | extend = "y", 16 | reweight = TRUE, 17 | reassign = TRUE, 18 | thresh = 0.001, 19 | maxit = 15, 20 | stretch = 2, 21 | approx_points = NULL, 22 | smoother = "smooth.spline", 23 | shrink.method = "cosine", 24 | allow.breaks = TRUE, 25 | ... 26 | ) 27 | 28 | \S4method{getCurves}{SingleCellExperiment}(data, ...) 29 | 30 | \S4method{getCurves}{SlingshotDataSet}(data, ...) 31 | } 32 | \arguments{ 33 | \item{data}{a data object containing lineage information provided by 34 | \code{\link{getLineages}}, to be used for constructing simultaneous 35 | principal curves. Supported types include 36 | \code{\link{SingleCellExperiment}}, \code{\link{SlingshotDataSet}}, and 37 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}} (recommended).} 38 | 39 | \item{...}{Additional parameters to pass to scatter plot smoothing function, 40 | \code{smoother}.} 41 | 42 | \item{shrink}{logical or numeric between 0 and 1, determines whether and how 43 | much to shrink branching lineages toward their average prior to the split 44 | (default \code{= TRUE}).} 45 | 46 | \item{extend}{character, how to handle root and leaf clusters of lineages 47 | when constructing the initial, piece-wise linear curve. Accepted values are 48 | \code{'y'} (default), \code{'n'}, and \code{'pc1'}. See 'Details' for more.} 49 | 50 | \item{reweight}{logical, whether to allow cells shared between lineages to be 51 | reweighted during curve fitting. If \code{TRUE} (default), cells shared 52 | between lineages will be iteratively reweighted based on the quantiles of 53 | their projection distances to each curve. See 'Details' for more.} 54 | 55 | \item{reassign}{logical, whether to reassign cells to lineages at each 56 | iteration. If \code{TRUE} (default), cells will be added to a lineage when 57 | their projection distance to the curve is less than the median distance for 58 | all cells currently assigned to the lineage. Additionally, shared cells 59 | will be removed from a lineage if their projection distance to the curve is 60 | above the 90th percentile and their weight along the curve is less than 61 | \code{0.1}.} 62 | 63 | \item{thresh}{numeric, determines the convergence criterion. Percent change 64 | in the total distance from cells to their projections along curves must be 65 | less than \code{thresh}. Default is \code{0.001}, similar to 66 | \code{\link[princurve]{principal_curve}}.} 67 | 68 | \item{maxit}{numeric, maximum number of iterations (default \code{= 15}), see 69 | \code{\link[princurve]{principal_curve}}.} 70 | 71 | \item{stretch}{numeric factor by which curves can be extrapolated beyond 72 | endpoints. Default is \code{2}, see 73 | \code{\link[princurve]{principal_curve}}.} 74 | 75 | \item{approx_points}{numeric, whether curves should be approximated by a 76 | fixed number of points. If \code{FALSE} (or 0), no approximation will be 77 | performed and curves will contain as many points as the input data. If 78 | numeric, curves will be approximated by this number of points (default 79 | \code{= 150} or \code{#cells}, whichever is smaller). See 'Details' and 80 | \code{\link[princurve]{principal_curve}} for more.} 81 | 82 | \item{smoother}{choice of scatter plot smoother. Same as 83 | \code{\link[princurve]{principal_curve}}, but \code{"lowess"} option is 84 | replaced with \code{"loess"} for additional flexibility.} 85 | 86 | \item{shrink.method}{character denoting how to determine the appropriate 87 | amount of shrinkage for a branching lineage. Accepted values are the same 88 | as for \code{kernel} in \code{\link{density}} (default is \code{"cosine"}), 89 | as well as \code{"tricube"} and \code{"density"}. See 'Details' for more.} 90 | 91 | \item{allow.breaks}{logical, determines whether curves that branch very close 92 | to the origin should be allowed to have different starting points.} 93 | } 94 | \value{ 95 | An updated \code{\link{PseudotimeOrdering}} object containing the 96 | pseudotime estimates and lineage assignment weights in the \code{assays}. 97 | It will also include the original information provided by 98 | \code{getLineages}, as well as the following new elements in the 99 | \code{metadata}: \itemize{ \item{\code{curves}} {A list of 100 | \code{\link[princurve]{principal_curve}} objects.} 101 | \item{\code{slingParams}} {Additional parameters used for fitting 102 | simultaneous principal curves.}} 103 | } 104 | \description{ 105 | This function constructs simultaneous principal curves, the 106 | second step in Slingshot's trajectory inference procedure. It takes a 107 | (specifically formatted) \code{\link[TrajectoryUtils]{PseudotimeOrdering}} 108 | object, as is returned by the first step, \code{\link{getLineages}}. The 109 | output is another \code{PseudotimeOrdering} object, containing the 110 | simultaneous principal curves, pseudotime estimates, and lineage assignment 111 | weights. 112 | } 113 | \details{ 114 | This function constructs simultaneous principal curves (one per 115 | lineage). Cells are mapped to curves by orthogonal projection and 116 | pseudotime is estimated by the arclength along the curve (also called 117 | \code{lambda}, in the \code{\link[princurve]{principal_curve}} objects). 118 | 119 | When there is only a single lineage, the curve-fitting algorithm is 120 | nearly identical to that of \code{\link[princurve]{principal_curve}}. When 121 | there are multiple lineages and \code{shrink > 0}, an additional step 122 | is added to the iterative procedure, forcing curves to be similar in the 123 | neighborhood of shared points (ie., before they branch). 124 | 125 | The \code{approx_points} argument, which sets the number of points 126 | to be used for each curve, can have a large effect on computation time. Due 127 | to this consideration, we set the default value to \code{150} whenever the 128 | input dataset contains more than that many cells. This setting should help 129 | with exploratory analysis while having little to no impact on the final 130 | curves. To disable this behavior and construct curves with the maximum 131 | number of points, set \code{approx_points = FALSE}. 132 | 133 | The \code{extend} argument determines how to construct the 134 | piece-wise linear curve used to initiate the recursive algorithm. The 135 | initial curve is always based on the lines between cluster centers and if 136 | \code{extend = 'n'}, this curve will terminate at the center of the 137 | endpoint clusters. Setting \code{extend = 'y'} will allow the first and 138 | last segments to extend beyond the cluster center to the orthogonal 139 | projection of the furthest point. Setting \code{extend = 'pc1'} is similar 140 | to \code{'y'}, but uses the first principal component of the cluster to 141 | determine the direction of the curve beyond the cluster center. These 142 | options typically have limited impact on the final curve, but can 143 | occasionally help with stability issues. 144 | 145 | When \code{shink = TRUE}, we compute a percent shrinkage curve, 146 | \eqn{w_l(t)}, for each lineage, a non-increasing function of pseudotime 147 | that determines how much that lineage should be shrunk toward a shared 148 | average curve. We set \eqn{w_l(0) = 1} (complete shrinkage), so that the 149 | curves will always perfectly overlap the average curve at pseudotime 150 | \code{0}. The weighting curve decreases from \code{1} to \code{0} over the 151 | non-outlying pseudotime values of shared cells (where outliers are defined 152 | by the \code{1.5*IQR} rule). The exact shape of the curve in this region is 153 | controlled by \code{shrink.method}, and can follow the shape of any 154 | standard kernel function's cumulative density curve (or more precisely, 155 | survival curve, since we require a decreasing function). Different choices 156 | of \code{shrink.method} to have no discernable impact on the final curves, 157 | in most cases. 158 | 159 | When \code{reweight = TRUE}, weights for shared cells are based on 160 | the quantiles of their projection distances onto each curve. The 161 | distances are ranked and converted into quantiles between \code{0} and 162 | \code{1}, which are then transformed by \code{1 - q^2}. Each cell's weight 163 | along a given lineage is the ratio of this value to the maximum value for 164 | this cell across all lineages. 165 | } 166 | \examples{ 167 | data("slingshotExample") 168 | rd <- slingshotExample$rd 169 | cl <- slingshotExample$cl 170 | pto <- getLineages(rd, cl, start.clus = '1') 171 | pto <- getCurves(pto) 172 | 173 | # plotting 174 | sds <- as.SlingshotDataSet(pto) 175 | plot(rd, col = cl, asp = 1) 176 | lines(sds, type = 'c', lwd = 3) 177 | 178 | } 179 | \references{ 180 | Hastie, T., and Stuetzle, W. (1989). "Principal Curves." 181 | \emph{Journal of the American Statistical Association}, 84:502--516. 182 | } 183 | \seealso{ 184 | \code{\link{slingshot}} 185 | } 186 | -------------------------------------------------------------------------------- /man/getLineages.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/getLineages.R 3 | \name{getLineages} 4 | \alias{getLineages} 5 | \alias{getLineages,matrix,matrix-method} 6 | \alias{getLineages,matrix,character-method} 7 | \alias{getLineages,matrix,ANY-method} 8 | \alias{getLineages,SlingshotDataSet,ANY-method} 9 | \alias{getLineages,PseudotimeOrdering,ANY-method} 10 | \alias{getLineages,data.frame,ANY-method} 11 | \alias{getLineages,matrix,numeric-method} 12 | \alias{getLineages,matrix,factor-method} 13 | \alias{getLineages,SingleCellExperiment,ANY-method} 14 | \title{Infer Lineage Structure from Clustered Samples} 15 | \usage{ 16 | getLineages(data, clusterLabels, ...) 17 | 18 | \S4method{getLineages}{matrix,matrix}( 19 | data, 20 | clusterLabels, 21 | reducedDim = NULL, 22 | start.clus = NULL, 23 | end.clus = NULL, 24 | dist.method = "slingshot", 25 | use.median = FALSE, 26 | omega = FALSE, 27 | omega_scale = 1.5, 28 | times = NULL, 29 | ... 30 | ) 31 | 32 | \S4method{getLineages}{matrix,character}(data, clusterLabels, ...) 33 | 34 | \S4method{getLineages}{matrix,ANY}(data, clusterLabels, ...) 35 | 36 | \S4method{getLineages}{SlingshotDataSet,ANY}(data, clusterLabels, ...) 37 | 38 | \S4method{getLineages}{PseudotimeOrdering,ANY}(data, clusterLabels, ...) 39 | 40 | \S4method{getLineages}{data.frame,ANY}(data, clusterLabels, ...) 41 | 42 | \S4method{getLineages}{matrix,numeric}(data, clusterLabels, ...) 43 | 44 | \S4method{getLineages}{matrix,factor}(data, clusterLabels, ...) 45 | 46 | \S4method{getLineages}{SingleCellExperiment,ANY}(data, clusterLabels, reducedDim = NULL, ...) 47 | } 48 | \arguments{ 49 | \item{data}{a data object containing the matrix of coordinates to be used for 50 | lineage inference. Supported types include \code{matrix}, 51 | \code{\link{SingleCellExperiment}}, \code{\link{SlingshotDataSet}}, and 52 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}}.} 53 | 54 | \item{clusterLabels}{each cell's cluster assignment. This can be a single 55 | vector of labels, or a \code{#cells} by \code{#clusters} matrix 56 | representing weighted cluster assignment. Either representation may 57 | optionally include a \code{"-1"} group meaning "unclustered."} 58 | 59 | \item{...}{Additional arguments to specify how lineages are constructed from 60 | clusters.} 61 | 62 | \item{reducedDim}{(optional) the dimensionality reduction to be used. Can be 63 | a matrix or a character identifying which element of 64 | \code{reducedDim(data)} is to be used. If multiple dimensionality 65 | reductions are present and this argument is not provided, the first element 66 | will be used by default.} 67 | 68 | \item{start.clus}{(optional) character, indicates the starting cluster(s) 69 | from which lineages will be drawn.} 70 | 71 | \item{end.clus}{(optional) character, indicates which cluster(s) will be 72 | forced to be leaf nodes in the graph.} 73 | 74 | \item{dist.method}{(optional) character, specifies the method for calculating 75 | distances between clusters. Default is \code{"slingshot"}, see 76 | \code{\link[TrajectoryUtils]{createClusterMST}} for details.} 77 | 78 | \item{use.median}{logical, whether to use the median (instead of mean) when 79 | calculating cluster centroid coordinates.} 80 | 81 | \item{omega}{(optional) numeric or logical, this granularity parameter 82 | determines the distance between every real cluster and the artificial 83 | cluster, \code{.OMEGA}. In practice, this makes \code{omega} the maximum 84 | allowable distance between two connected clusters. By default, \code{omega 85 | = Inf}. If \code{omega = TRUE}, the maximum edge length will be set to the 86 | median edge length of the unsupervised MST times a scaling factor 87 | (\code{omega_scale}, default \code{= 1.5}). This value is provided as a 88 | potentially useful rule of thumb for datasets with outlying clusters or 89 | multiple, distinct trajectories. See \code{outgroup} in 90 | \code{\link[TrajectoryUtils]{createClusterMST}}.} 91 | 92 | \item{omega_scale}{(optional) numeric, scaling factor to use when \code{omega 93 | = TRUE}. The maximum edge length will be set to the median edge length of 94 | the unsupervised MST times \code{omega_scale} (default \code{= 3}). See 95 | \code{outscale} in \code{\link[TrajectoryUtils]{createClusterMST}}.} 96 | 97 | \item{times}{numeric, vector of external times associated with either 98 | clusters or cells. See \code{\link[TrajectoryUtils]{defineMSTPaths}} for 99 | details.} 100 | } 101 | \value{ 102 | An object of class \code{\link{PseudotimeOrdering}}. Although the 103 | final pseudotimes have not yet been calculated, the assay slot of this 104 | object contains two elements: \code{pseudotime}, a matrix of \code{NA} 105 | values; and \code{weights}, a preliminary matrix of lineage assignment 106 | weights. The \code{reducedDim} and \code{clusterLabels} matrices will be 107 | stored in the \code{\link[TrajectoryUtils]{cellData}}. Additionally, the 108 | \code{metadata} slot will contain an \code{\link[igraph]{igraph}} object 109 | named \code{mst}, a list of parameter values named \code{slingParams}, and 110 | a list of lineages (ordered sets of clusters) named \code{lineages}. 111 | } 112 | \description{ 113 | This function constructs the minimum spanning tree(s) on 114 | clusters of cells, the first step in Slingshot's trajectory inference 115 | procedure. Paths through the MST from an origin cluster to leaf node 116 | clusters are interpreted as lineages. 117 | } 118 | \details{ 119 | Given a reduced-dimension data matrix \code{n} by \code{p} and a set 120 | of cluster identities (potentially including a \code{"-1"} group for 121 | "unclustered"), this function infers a tree (or forest) structure on the 122 | clusters. This work is now mostly handled by the more general function, 123 | \code{\link[TrajectoryUtils]{createClusterMST}}. 124 | 125 | The graph of this structure is learned by fitting a (possibly 126 | constrained) minimum-spanning tree on the clusters, plus the artificial 127 | cluster, \code{.OMEGA}, which is a fixed distance away from every real 128 | cluster. This effectively limits the maximum branch length in the MST to 129 | the chosen distance, meaning that the output may contain multiple trees. 130 | 131 | Once the graph is known, lineages are identified in 132 | any tree with at least two clusters. For a given tree, if there is an 133 | annotated starting cluster, every possible path out of a starting cluster 134 | and ending in a leaf that isn't another starting cluster will be returned. 135 | If no starting cluster is annotated, one will be chosen by a heuristic 136 | method, but this is not recommended. 137 | } 138 | \examples{ 139 | data("slingshotExample") 140 | rd <- slingshotExample$rd 141 | cl <- slingshotExample$cl 142 | pto <- getLineages(rd, cl, start.clus = '1') 143 | 144 | # plotting 145 | sds <- as.SlingshotDataSet(pto) 146 | plot(rd, col = cl, asp = 1) 147 | lines(sds, type = 'l', lwd = 3) 148 | 149 | } 150 | -------------------------------------------------------------------------------- /man/newSlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \docType{methods} 4 | \name{newSlingshotDataSet} 5 | \alias{newSlingshotDataSet} 6 | \alias{newSlingshotDataSet,data.frame,ANY-method} 7 | \alias{newSlingshotDataSet,matrix,numeric-method} 8 | \alias{newSlingshotDataSet,matrix,factor-method} 9 | \alias{newSlingshotDataSet,matrix,ANY-method} 10 | \alias{newSlingshotDataSet,matrix,character-method} 11 | \alias{newSlingshotDataSet,matrix,matrix-method} 12 | \title{Initialize an object of class \code{SlingshotDataSet}} 13 | \usage{ 14 | newSlingshotDataSet(reducedDim, clusterLabels, ...) 15 | 16 | \S4method{newSlingshotDataSet}{data.frame,ANY}(reducedDim, clusterLabels, ...) 17 | 18 | \S4method{newSlingshotDataSet}{matrix,numeric}(reducedDim, clusterLabels, ...) 19 | 20 | \S4method{newSlingshotDataSet}{matrix,factor}(reducedDim, clusterLabels, ...) 21 | 22 | \S4method{newSlingshotDataSet}{matrix,ANY}(reducedDim, clusterLabels, ...) 23 | 24 | \S4method{newSlingshotDataSet}{matrix,character}(reducedDim, clusterLabels, ...) 25 | 26 | \S4method{newSlingshotDataSet}{matrix,matrix}( 27 | reducedDim, 28 | clusterLabels, 29 | lineages = list(), 30 | adjacency = matrix(NA, 0, 0), 31 | curves = list(), 32 | slingParams = list() 33 | ) 34 | } 35 | \arguments{ 36 | \item{reducedDim}{matrix. An \code{n} by \code{p} numeric matrix or data 37 | frame giving the coordinates of the cells in a reduced dimensionality 38 | space.} 39 | 40 | \item{clusterLabels}{character. A character vector of length \code{n} 41 | denoting each cell's cluster label.} 42 | 43 | \item{...}{additional components of a \code{SlingshotDataSet} to specify. 44 | This may include any of the following:} 45 | 46 | \item{lineages}{list. A list with each element a character vector of cluster 47 | names representing a lineage as an ordered set of clusters.} 48 | 49 | \item{adjacency}{matrix. A binary matrix describing the connectivity 50 | between clusters induced by the minimum spanning tree.} 51 | 52 | \item{curves}{list. A list of \code{\link[princurve]{principal_curve}} 53 | objects produced by \code{\link{getCurves}}.} 54 | 55 | \item{slingParams}{list. Additional parameters used by Slingshot. These may 56 | specify how the minimum spanning tree on clusters was constructed: 57 | \itemize{ 58 | \item{\code{start.clus}}{ character. The label of the root cluster.} 59 | \item{\code{end.clus}}{ character. Vector of cluster labels indicating the 60 | terminal clusters.} 61 | \item{\code{start.given}}{ logical. A logical value 62 | indicating whether the initial state was pre-specified.} 63 | \item{\code{end.given}}{ logical. A vector of logical values indicating 64 | whether each terminal state was pre-specified} 65 | \item{\code{dist}}{ matrix. A 66 | numeric matrix of pairwise cluster distances.} } 67 | They may also specify how simultaneous principal curves were constructed: 68 | \itemize{ 69 | \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 70 | whether and how much to shrink branching lineages toward their shared 71 | average curve.} 72 | \item{\code{extend}}{ character. Specifies the method for handling 73 | root and leaf clusters of lineages when constructing the initial, 74 | piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 75 | See \code{\link{getCurves}} for details.} 76 | \item{\code{reweight}}{ logical. 77 | Indicates whether to allow cells shared 78 | between lineages to be reweighted during curve-fitting. If \code{TRUE}, 79 | cells shared between lineages will be iteratively reweighted based on the 80 | quantiles of their projection distances to each curve.} 81 | \item{\code{reassign}}{ logical. 82 | Indicates whether to reassign cells to 83 | lineages at each iteration. If \code{TRUE}, cells will be added to a 84 | lineage when their projection distance to the curve is less than the median 85 | distance for all cells currently assigned to the lineage. Additionally, 86 | shared cells will be removed from a lineage if their projection distance to 87 | the curve is above the 90th percentile and their weight along the curve is 88 | less than \code{0.1}.} 89 | \item{\code{shrink.method}}{ character. 90 | Denotes how to determine the amount of shrinkage for a branching lineage. 91 | Accepted values are the same as for \code{kernel} in the \code{density} 92 | function (default is \code{"cosine"}), as well as \code{"tricube"} and 93 | \code{"density"}. See \code{\link{getCurves}} for details.} 94 | \item{Other parameters specified by 95 | \code{\link[princurve]{principal_curve}}}. }} 96 | } 97 | \value{ 98 | A \code{SlingshotDataSet} object with all specified values. 99 | } 100 | \description{ 101 | Constructs a \code{SlingshotDataSet} object. Additional helper 102 | methods for manipulating \code{SlingshotDataSet} objects are also 103 | described below. We now recommend using 104 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}} objects, instead. 105 | } 106 | \examples{ 107 | rd <- matrix(data=rnorm(100), ncol=2) 108 | cl <- sample(letters[seq_len(5)], 50, replace = TRUE) 109 | sds <- newSlingshotDataSet(rd, cl) 110 | 111 | } 112 | \seealso{ 113 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}} 114 | } 115 | -------------------------------------------------------------------------------- /man/pairs-SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotting.R 3 | \name{pairs-SlingshotDataSet} 4 | \alias{pairs-SlingshotDataSet} 5 | \alias{pairs.SlingshotDataSet} 6 | \title{Pairs plot of Slingshot output} 7 | \usage{ 8 | \method{pairs}{SlingshotDataSet}( 9 | x, 10 | type = NULL, 11 | show.constraints = FALSE, 12 | col = NULL, 13 | pch = 16, 14 | cex = 1, 15 | lwd = 2, 16 | ..., 17 | labels, 18 | horInd = seq_len(nc), 19 | verInd = seq_len(nc), 20 | lower.panel = FALSE, 21 | upper.panel = TRUE, 22 | diag.panel = NULL, 23 | text.panel = textPanel, 24 | label.pos = 0.5 + has.diag/3, 25 | line.main = 3, 26 | cex.labels = NULL, 27 | font.labels = 1, 28 | row1attop = TRUE, 29 | gap = 1 30 | ) 31 | } 32 | \arguments{ 33 | \item{x}{a \code{SlingshotDataSet} with results to be plotted.} 34 | 35 | \item{type}{character, the type of output to be plotted, can be one of 36 | \code{"lineages"}, \code{curves}, or \code{both} (by partial matching), see 37 | Details for more.} 38 | 39 | \item{show.constraints}{logical, whether or not the user-specified initial 40 | and terminal clusters should be specially denoted by green and red dots, 41 | respectively.} 42 | 43 | \item{col}{character, color vector for points.} 44 | 45 | \item{pch}{integer or character specifying the plotting symbol, see 46 | \code{\link{par}}.} 47 | 48 | \item{cex}{numeric, amount by which points should be magnified, see 49 | \code{\link{par}}.} 50 | 51 | \item{lwd}{numeric, the line width, see \code{\link{par}}.} 52 | 53 | \item{...}{additional parameters for \code{plot} or \code{axis}, see 54 | \code{\link[graphics]{pairs}}.} 55 | 56 | \item{labels}{character, the names of the variables, see 57 | \code{\link[graphics]{pairs}}.} 58 | 59 | \item{horInd}{see \code{\link[graphics]{pairs}}.} 60 | 61 | \item{verInd}{see \code{\link[graphics]{pairs}}.} 62 | 63 | \item{lower.panel}{see \code{\link[graphics]{pairs}}.} 64 | 65 | \item{upper.panel}{see \code{\link[graphics]{pairs}}.} 66 | 67 | \item{diag.panel}{see \code{\link[graphics]{pairs}}.} 68 | 69 | \item{text.panel}{see \code{\link[graphics]{pairs}}.} 70 | 71 | \item{label.pos}{see \code{\link[graphics]{pairs}}.} 72 | 73 | \item{line.main}{see \code{\link[graphics]{pairs}}.} 74 | 75 | \item{cex.labels}{see \code{\link[graphics]{pairs}}.} 76 | 77 | \item{font.labels}{see \code{\link[graphics]{pairs}}.} 78 | 79 | \item{row1attop}{see \code{\link[graphics]{pairs}}.} 80 | 81 | \item{gap}{see \code{\link[graphics]{pairs}}.} 82 | } 83 | \value{ 84 | returns \code{NULL}. 85 | } 86 | \description{ 87 | A tool for quickly visualizing lineages inferred by 88 | \code{slingshot}. 89 | } 90 | \details{ 91 | If \code{type == 'lineages'}, straight line connectors between 92 | cluster centers will be plotted. If \code{type == 'curves'}, simultaneous 93 | principal curves will be plotted. 94 | 95 | When \code{type} is not specified, the function will first check the 96 | \code{curves} slot and plot the curves, if present. Otherwise, 97 | \code{lineages} will be plotted, if present. 98 | } 99 | \examples{ 100 | data("slingshotExample") 101 | rd <- slingshotExample$rd 102 | cl <- slingshotExample$cl 103 | pto <- slingshot(rd, cl, start.clus = "1") 104 | pairs(SlingshotDataSet(pto)) 105 | 106 | } 107 | -------------------------------------------------------------------------------- /man/plot-SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotting.R 3 | \name{plot-SlingshotDataSet} 4 | \alias{plot-SlingshotDataSet} 5 | \alias{plot.SlingshotDataSet} 6 | \alias{plot,SlingshotDataSet,ANY-method} 7 | \alias{lines.SlingshotDataSet} 8 | \title{Plot Slingshot output} 9 | \usage{ 10 | \method{plot}{SlingshotDataSet}( 11 | x, 12 | type = NULL, 13 | linInd = NULL, 14 | show.constraints = FALSE, 15 | add = FALSE, 16 | dims = seq_len(2), 17 | asp = 1, 18 | cex = 2, 19 | lwd = 2, 20 | col = 1, 21 | ... 22 | ) 23 | 24 | \method{lines}{SlingshotDataSet}(x, type = NULL, dims = seq_len(2), ...) 25 | } 26 | \arguments{ 27 | \item{x}{a \code{SlingshotDataSet} with results to be plotted.} 28 | 29 | \item{type}{character, the type of output to be plotted, can be one of 30 | \code{"lineages"}, \code{"curves"}, or \code{"both"} (by partial matching), 31 | see Details for more.} 32 | 33 | \item{linInd}{integer, an index indicating which lineages should be plotted 34 | (default is to plot all lineages). If \code{col} is a vector, it will be 35 | subsetted by \code{linInd}.} 36 | 37 | \item{show.constraints}{logical, whether or not the user-specified initial 38 | and terminal clusters should be specially denoted by green and red dots, 39 | respectively.} 40 | 41 | \item{add}{logical, indicates whether the output should be added to an 42 | existing plot.} 43 | 44 | \item{dims}{numeric, which dimensions to plot (default is \code{1:2}).} 45 | 46 | \item{asp}{numeric, the y/x aspect ratio, see \code{\link{plot.window}}.} 47 | 48 | \item{cex}{numeric, amount by which points should be magnified, see 49 | \code{\link{par}}.} 50 | 51 | \item{lwd}{numeric, the line width, see \code{\link{par}}.} 52 | 53 | \item{col}{character or numeric, color(s) for lines, see \code{\link{par}}.} 54 | 55 | \item{...}{additional parameters to be passed to \code{\link{lines}}.} 56 | } 57 | \value{ 58 | returns \code{NULL}. 59 | } 60 | \description{ 61 | Tools for visualizing lineages inferred by \code{slingshot}. 62 | } 63 | \details{ 64 | If \code{type == 'lineages'}, straight line connectors between 65 | cluster centers will be plotted. If \code{type == 'curves'}, simultaneous 66 | principal curves will be plotted. 67 | 68 | When \code{type} is not specified, the function will first check the 69 | \code{curves} slot and plot the curves, if present. Otherwise, 70 | \code{lineages} will be plotted, if present. 71 | } 72 | \examples{ 73 | data("slingshotExample") 74 | rd <- slingshotExample$rd 75 | cl <- slingshotExample$cl 76 | pto <- slingshot(rd, cl, start.clus = "1") 77 | plot(SlingshotDataSet(pto), type = 'b') 78 | 79 | # add to existing plot 80 | sds <- as.SlingshotDataSet(pto) 81 | plot(rd, col = 'grey50', asp = 1) 82 | lines(sds, lwd = 3) 83 | 84 | } 85 | -------------------------------------------------------------------------------- /man/plot3d-SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plotting.R 3 | \name{plot3d-SlingshotDataSet} 4 | \alias{plot3d-SlingshotDataSet} 5 | \alias{plot3d.SlingshotDataSet} 6 | \title{Plot Slingshot output in 3D} 7 | \usage{ 8 | plot3d.SlingshotDataSet( 9 | x, 10 | type = NULL, 11 | linInd = NULL, 12 | add = FALSE, 13 | dims = seq_len(3), 14 | aspect = "iso", 15 | size = 10, 16 | col = 1, 17 | ... 18 | ) 19 | } 20 | \arguments{ 21 | \item{x}{a \code{SlingshotDataSet} with results to be plotted.} 22 | 23 | \item{type}{character, the type of output to be plotted, can be one of 24 | \code{"lineages"}, \code{curves}, or \code{both} (by partial matching), see 25 | Details for more.} 26 | 27 | \item{linInd}{integer, an index indicating which lineages should be plotted 28 | (default is to plot all lineages). If \code{col} is a vector, it will be 29 | subsetted by \code{linInd}.} 30 | 31 | \item{add}{logical, indicates whether the output should be added to an 32 | existing plot.} 33 | 34 | \item{dims}{numeric, which dimensions to plot (default is \code{1:3}).} 35 | 36 | \item{aspect}{either a logical indicating whether to adjust the aspect ratio 37 | or a new ratio, see \code{\link[rgl:plot3d]{plot3d}}.} 38 | 39 | \item{size}{numeric, size of points for MST (default is \code{10}), see 40 | \code{\link[rgl:plot3d]{plot3d}}.} 41 | 42 | \item{col}{character or numeric, color(s) for lines, see \code{\link{par}}.} 43 | 44 | \item{...}{additional parameters to be passed to \code{lines3d}.} 45 | } 46 | \value{ 47 | returns \code{NULL}. 48 | } 49 | \description{ 50 | Tools for visualizing lineages inferred by \code{slingshot}. 51 | } 52 | \details{ 53 | If \code{type == 'lineages'}, straight line connectors between 54 | cluster centers will be plotted. If \code{type == 'curves'}, simultaneous 55 | principal curves will be plotted. 56 | 57 | When \code{type} is not specified, the function will first check the 58 | \code{curves} slot and plot the curves, if present. Otherwise, 59 | \code{lineages} will be plotted, if present. 60 | } 61 | \examples{ 62 | \donttest{ 63 | library(rgl) 64 | data("slingshotExample") 65 | rd <- slingshotExample$rd 66 | cl <- slingshotExample$cl 67 | rd <- cbind(rd, rnorm(nrow(rd))) 68 | pto <- slingshot(rd, cl, start.clus = "1") 69 | sds <- SlingshotDataSet(pto) 70 | plot3d.SlingshotDataSet(sds, type = 'b') 71 | 72 | # add to existing plot 73 | plot3d(rd, col = 'grey50', aspect = 'iso') 74 | plot3d.SlingshotDataSet(sds, lwd = 3, add = TRUE) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /man/predict.SlingshotDataSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predict.R 3 | \name{predict,PseudotimeOrdering-method} 4 | \alias{predict,PseudotimeOrdering-method} 5 | \alias{predict,SlingshotDataSet-method} 6 | \title{Predict from a Slingshot model} 7 | \usage{ 8 | \S4method{predict}{PseudotimeOrdering}(object, newdata = NULL) 9 | 10 | \S4method{predict}{SlingshotDataSet}(object, newdata = NULL) 11 | } 12 | \arguments{ 13 | \item{object}{a \code{\link[TrajectoryUtils]{PseudotimeOrdering}} or 14 | \code{\link{SlingshotDataSet}} containing simultaneous principal curves to 15 | use for prediction.} 16 | 17 | \item{newdata}{a matrix or data frame of new points in the same 18 | reduced-dimensional space as the original input to \code{slingshot} (or 19 | \code{getLineages}).} 20 | } 21 | \value{ 22 | An object of the same type as \code{object}, based on the input 23 | \code{newdata}. New cells are treated as "unclustered", but other metadata 24 | is preserved. The \code{curves} slot represents the projections of each new 25 | cell onto the existing curves. As with standard \code{slingshot} output, 26 | the lineage-specific pseudotimes and assignment weights can be accessed via 27 | the functions \code{\link{slingPseudotime}} and 28 | \code{\link{slingCurveWeights}}. 29 | } 30 | \description{ 31 | Map new observations onto simultaneous principal curves fitted 32 | by \code{slingshot}. 33 | } 34 | \details{ 35 | This function is a method for the generic function \code{predict} 36 | with inputs being either a \code{PseudotimeOrdering} or 37 | \code{SlingshotDataSet}. If no \code{newdata} argument is provided, it will 38 | return the original results, given by \code{object}. 39 | } 40 | \examples{ 41 | data("slingshotExample") 42 | rd <- slingshotExample$rd 43 | cl <- slingshotExample$cl 44 | pto <- slingshot(rd, cl, start.clus = '1') 45 | 46 | x <- cbind(runif(100, min = -5, max = 10), runif(100, min = -4, max = 4)) 47 | predict(pto, x) 48 | 49 | } 50 | \seealso{ 51 | \code{\link{slingshot}} 52 | } 53 | -------------------------------------------------------------------------------- /man/slingBranchGraph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/branchID.R 3 | \name{slingBranchGraph} 4 | \alias{slingBranchGraph} 5 | \alias{slingBranchGraph,ANY-method} 6 | \title{Construct graph of slingshot branch labels} 7 | \usage{ 8 | slingBranchGraph(x, ...) 9 | 10 | \S4method{slingBranchGraph}{ANY}(x, thresh = NULL, max_node_size = 100) 11 | } 12 | \arguments{ 13 | \item{x}{an object containing \code{slingshot} output, generally either a 14 | \code{\link{PseudotimeOrdering}} or \code{\link{SingleCellExperiment}}.} 15 | 16 | \item{...}{additional arguments passed to object-specific methods.} 17 | 18 | \item{thresh}{weight threshold for assigning cells to lineages. A cell's 19 | weight on a certain lineage must be greater than this value (default = 20 | \code{1/L}, for \code{L} lineages).} 21 | 22 | \item{max_node_size}{the \code{size} of the largest node in the graph, for 23 | plotting (all others will be drawn proportionally). Default is \code{100}. 24 | See \code{\link[igraph]{igraph.plotting}} for more details.} 25 | } 26 | \value{ 27 | an \code{igraph} object representing the relationships between 28 | lineages. 29 | } 30 | \description{ 31 | Builds a graph describing the relationships between the 32 | different branch assignments. 33 | } 34 | \examples{ 35 | data("slingshotExample") 36 | rd <- slingshotExample$rd 37 | cl <- slingshotExample$cl 38 | pto <- slingshot(rd, cl) 39 | slingBranchGraph(pto) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /man/slingBranchID.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/branchID.R 3 | \name{slingBranchID} 4 | \alias{slingBranchID} 5 | \alias{slingBranchID,ANY-method} 6 | \title{Get slingshot branch labels} 7 | \usage{ 8 | slingBranchID(x, ...) 9 | 10 | \S4method{slingBranchID}{ANY}(x, thresh = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{an object containing \code{slingshot} output, generally either a 14 | \code{\link{PseudotimeOrdering}} or \code{\link{SingleCellExperiment}}.} 15 | 16 | \item{...}{additional arguments passed to object-specific methods.} 17 | 18 | \item{thresh}{weight threshold for assigning cells to lineages. A cell's 19 | weight on a certain lineage must be at least this value (default = 20 | \code{1/L}, for \code{L} lineages).} 21 | } 22 | \value{ 23 | a factor variable that assigns each cell to a particular lineage or 24 | set of lineages. 25 | } 26 | \description{ 27 | Summarizes the lineage assignment weights from \code{slingshot} 28 | results as a single vector. This is represented by a categorical variable 29 | indicating which lineage (or combination of lineages) each cell is assigned 30 | to. 31 | } 32 | \examples{ 33 | data("slingshotExample") 34 | rd <- slingshotExample$rd 35 | cl <- slingshotExample$cl 36 | pto <- slingshot(rd, cl) 37 | slingBranchID(pto) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /man/slingClusterLabels.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingClusterLabels} 4 | \alias{slingClusterLabels} 5 | \alias{slingClusterLabels,PseudotimeOrdering-method} 6 | \alias{slingClusterLabels,SlingshotDataSet-method} 7 | \alias{slingClusterLabels,SingleCellExperiment-method} 8 | \title{Extract cluster labels used by Slingshot} 9 | \usage{ 10 | slingClusterLabels(x) 11 | 12 | \S4method{slingClusterLabels}{PseudotimeOrdering}(x) 13 | 14 | \S4method{slingClusterLabels}{SlingshotDataSet}(x) 15 | 16 | \S4method{slingClusterLabels}{SingleCellExperiment}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | } 21 | \value{ 22 | Typically returns a matrix of cluster assignment weights 23 | (\code{#cells} by \code{#clusters}). Rarely, a vector of cluster labels. 24 | } 25 | \description{ 26 | Extract the cluster labels used by \code{\link{slingshot}}. 27 | } 28 | \examples{ 29 | data("slingshotExample") 30 | rd <- slingshotExample$rd 31 | cl <- slingshotExample$cl 32 | pto <- slingshot(rd, cl, start.clus = '1') 33 | slingClusterLabels(pto) 34 | } 35 | -------------------------------------------------------------------------------- /man/slingCurves.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingCurves} 4 | \alias{slingCurves} 5 | \alias{slingCurves,PseudotimeOrdering-method} 6 | \alias{slingCurves,SingleCellExperiment-method} 7 | \alias{slingCurves,SlingshotDataSet-method} 8 | \title{Extract simultaneous principal curves} 9 | \usage{ 10 | slingCurves(x, ...) 11 | 12 | \S4method{slingCurves}{PseudotimeOrdering}(x, as.df = FALSE) 13 | 14 | \S4method{slingCurves}{SingleCellExperiment}(x, ...) 15 | 16 | \S4method{slingCurves}{SlingshotDataSet}(x, as.df = FALSE) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | 21 | \item{...}{additional parameters to be passed to object-specific methods.} 22 | 23 | \item{as.df}{logical, whether to format the output as a \code{data.frame}, 24 | suitable for plotting with \code{ggplot}.} 25 | } 26 | \value{ 27 | A list of smooth lineage curves, each of which is a 28 | \code{\link[princurve]{principal_curve}} object. 29 | } 30 | \description{ 31 | Extract the simultaneous principal curves from an object 32 | containing \code{\link{slingshot}} output. 33 | } 34 | \examples{ 35 | data("slingshotExample") 36 | rd <- slingshotExample$rd 37 | cl <- slingshotExample$cl 38 | pto <- slingshot(rd, cl, start.clus = '1') 39 | slingCurves(pto) 40 | } 41 | -------------------------------------------------------------------------------- /man/slingLineages.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingLineages} 4 | \alias{slingLineages} 5 | \alias{slingLineages,PseudotimeOrdering-method} 6 | \alias{slingLineages,SingleCellExperiment-method} 7 | \alias{slingLineages,SlingshotDataSet-method} 8 | \title{Extract the Slingshot lineages} 9 | \usage{ 10 | slingLineages(x) 11 | 12 | \S4method{slingLineages}{PseudotimeOrdering}(x) 13 | 14 | \S4method{slingLineages}{SingleCellExperiment}(x) 15 | 16 | \S4method{slingLineages}{SlingshotDataSet}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | } 21 | \value{ 22 | A list of lineages, represented by ordered sets of clusters. 23 | } 24 | \description{ 25 | Extract lineages (represented by ordered sets of clusters) 26 | identified by \code{\link{slingshot}}. 27 | } 28 | \examples{ 29 | data("slingshotExample") 30 | rd <- slingshotExample$rd 31 | cl <- slingshotExample$cl 32 | pto <- slingshot(rd, cl, start.clus = '1') 33 | slingLineages(pto) 34 | } 35 | -------------------------------------------------------------------------------- /man/slingMST.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingMST} 4 | \alias{slingMST} 5 | \alias{slingMST,PseudotimeOrdering-method} 6 | \alias{slingMST,SingleCellExperiment-method} 7 | \alias{slingMST,SlingshotDataSet-method} 8 | \title{Extract Slingshot minimum spanning tree} 9 | \usage{ 10 | slingMST(x, ...) 11 | 12 | \S4method{slingMST}{PseudotimeOrdering}(x, as.df = FALSE) 13 | 14 | \S4method{slingMST}{SingleCellExperiment}(x, ...) 15 | 16 | \S4method{slingMST}{SlingshotDataSet}(x, as.df = FALSE) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | 21 | \item{...}{additional parameters to be passed to object-specific methods.} 22 | 23 | \item{as.df}{logical, whether to format the output as a \code{data.frame}, 24 | suitable for plotting with \code{ggplot}.} 25 | } 26 | \value{ 27 | In most cases, output is an \code{\link[igraph]{igraph}} object 28 | representing the MST. If \code{x} is a \code{SlingshotDataSet}, then output 29 | is an adjacency matrix representing the MST. 30 | } 31 | \description{ 32 | Extract the minimum spanning tree from an object containing 33 | \code{\link{slingshot}} output. 34 | } 35 | \examples{ 36 | data("slingshotExample") 37 | rd <- slingshotExample$rd 38 | cl <- slingshotExample$cl 39 | pto <- slingshot(rd, cl, start.clus = '1') 40 | slingMST(pto) 41 | } 42 | -------------------------------------------------------------------------------- /man/slingParams.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingParams} 4 | \alias{slingParams} 5 | \alias{slingParams,PseudotimeOrdering-method} 6 | \alias{slingParams,SingleCellExperiment-method} 7 | \alias{slingParams,SlingshotDataSet-method} 8 | \title{Methods for parameters used by Slingshot} 9 | \usage{ 10 | slingParams(x) 11 | 12 | \S4method{slingParams}{PseudotimeOrdering}(x) 13 | 14 | \S4method{slingParams}{SingleCellExperiment}(x) 15 | 16 | \S4method{slingParams}{SlingshotDataSet}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | } 21 | \value{ 22 | The list of additional parameters used by Slingshot. These include 23 | parameters related to the cluster-based minimum spanning tree: 24 | \itemize{ 25 | \item{\code{start.clus}}{ character. The label of the root cluster, or a 26 | vector of cluster labels giving the root clusters of each disjoint 27 | component of the graph.} 28 | \item{\code{end.clus}}{ character. Vector of cluster labels indicating 29 | terminal clusters.} 30 | \item{\code{start.given}}{ logical. A logical value 31 | indicating whether the initial state was pre-specified.} 32 | \item{\code{end.given}}{ logical. A vector of logical values indicating 33 | whether each terminal state was pre-specified} 34 | \item{\code{omega}}{ numeric or logical. Granularity parameter determining 35 | the maximum edge length for building the MST. See 36 | \code{\link{getLineages}}.} 37 | \item{\code{omega_scale}}{ numeric. Scaling factor used for setting maximum 38 | edge length when \code{omega = TRUE}. See \code{\link{getLineages}}.} } 39 | They may also specify how simultaneous principal curves were constructed 40 | (for a complete listing, see \code{\link{getCurves}}: 41 | \itemize{ 42 | \item{\code{shrink}}{ logical or numeric between 0 and 1. Determines 43 | whether and how much to shrink branching lineages toward their shared 44 | average curve.} 45 | \item{\code{extend}}{ character. Specifies the method for handling 46 | root and leaf clusters of lineages when constructing the initial, 47 | piece-wise linear curve. Accepted values are 'y' (default), 'n', and 'pc1'. 48 | See \code{\link{getCurves}} for details.} 49 | \item{\code{reweight}}{ logical. 50 | Indicates whether to allow cells shared 51 | between lineages to be reweighted during curve-fitting. If \code{TRUE}, 52 | cells shared between lineages will be iteratively reweighted based on the 53 | quantiles of their projection distances to each curve.} 54 | \item{\code{reassign}}{ logical. 55 | Indicates whether to reassign cells to lineages at each 56 | iteration. If \code{TRUE}, cells will be added to a lineage when their 57 | projection distance to the curve is less than the median distance for all 58 | cells currently assigned to the lineage. Additionally, shared cells will be 59 | removed from a lineage if their projection distance to the curve is above 60 | the 90th percentile and their weight along the curve is less than 61 | \code{0.1}.} 62 | \item{\code{shrink.method}}{ character. 63 | Denotes how to determine the amount of shrinkage for a branching lineage. 64 | Accepted values are the same as for \code{kernel} in the \code{density} 65 | function (default is \code{"cosine"}), as well as \code{"tricube"} and 66 | \code{"density"}. See \code{\link{getCurves}} for details.} 67 | \item{approx_points}{ numeric. Number of points to use in estimating 68 | curves. See \code{\link{getCurves}} for details.} \item{allow.breaks}{ 69 | logical. Whether to allow curves that diverge very early on in a trajectory 70 | to have different starting points.} 71 | \item{Other parameters specified by 72 | \code{\link[princurve]{principal_curve}}}. } 73 | } 74 | \description{ 75 | Extracts additional control parameters used by Slingshot in 76 | lineage inference and fitting simultaneous principal curves. 77 | } 78 | \examples{ 79 | data("slingshotExample") 80 | rd <- slingshotExample$rd 81 | cl <- slingshotExample$cl 82 | pto <- slingshot(rd, cl, start.clus = '1') 83 | slingParams(pto) 84 | } 85 | -------------------------------------------------------------------------------- /man/slingPseudotime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingPseudotime} 4 | \alias{slingPseudotime} 5 | \alias{slingCurveWeights} 6 | \alias{slingAvgPseudotime} 7 | \alias{slingPseudotime,PseudotimeOrdering-method} 8 | \alias{slingPseudotime,SingleCellExperiment-method} 9 | \alias{slingPseudotime,SlingshotDataSet-method} 10 | \alias{slingCurveWeights,PseudotimeOrdering-method} 11 | \alias{slingCurveWeights,SingleCellExperiment-method} 12 | \alias{slingCurveWeights,SlingshotDataSet-method} 13 | \alias{slingAvgPseudotime,ANY-method} 14 | \title{Get Slingshot pseudotime values} 15 | \usage{ 16 | slingPseudotime(x, ...) 17 | 18 | slingCurveWeights(x, ...) 19 | 20 | slingAvgPseudotime(x, ...) 21 | 22 | \S4method{slingPseudotime}{PseudotimeOrdering}(x, na = TRUE) 23 | 24 | \S4method{slingPseudotime}{SingleCellExperiment}(x, na = TRUE) 25 | 26 | \S4method{slingPseudotime}{SlingshotDataSet}(x, na = TRUE) 27 | 28 | \S4method{slingCurveWeights}{PseudotimeOrdering}(x, as.probs = FALSE) 29 | 30 | \S4method{slingCurveWeights}{SingleCellExperiment}(x, as.probs = FALSE) 31 | 32 | \S4method{slingCurveWeights}{SlingshotDataSet}(x, as.probs = FALSE) 33 | 34 | \S4method{slingAvgPseudotime}{ANY}(x) 35 | } 36 | \arguments{ 37 | \item{x}{an object containing \code{\link{slingshot}} output.} 38 | 39 | \item{...}{additional parameters to be passed to object-specific methods.} 40 | 41 | \item{na}{logical. If \code{TRUE} (default), cells that are not assigned to a 42 | lineage will have a pseudotime value of \code{NA}. Otherwise, their 43 | arclength along each curve will be returned.} 44 | 45 | \item{as.probs}{logical. If \code{FALSE} (default), output will be the 46 | weights used to construct the curves, appropriate for downstream analysis 47 | of individual lineages (ie. a cell shared between two lineages can have two 48 | weights of \code{1}). If \code{TRUE}, output will be scaled to represent 49 | probabilistic assignment of cells to lineages (ie. a cell shared between 50 | two lineages will have two weights of \code{0.5}).} 51 | } 52 | \value{ 53 | \code{slingPseudotime}: an \code{n} by \code{L} matrix representing 54 | each cell's pseudotime along each lineage. 55 | 56 | \code{slingCurveWeights}: an \code{n} by \code{L} matrix of cell 57 | weights along each lineage. 58 | 59 | \code{slingAvgPseudotime}: a length \code{n} vector of average cell 60 | pseudotimes, where the average is a weighted average across lineages, 61 | weighted by the assignment weights. 62 | } 63 | \description{ 64 | Extract the matrix of pseudotime values or cells' weights along 65 | each lineage. 66 | } 67 | \examples{ 68 | data("slingshotExample") 69 | rd <- slingshotExample$rd 70 | cl <- slingshotExample$cl 71 | pto <- slingshot(rd, cl, start.clus = '1') 72 | slingPseudotime(pto) 73 | slingCurveWeights(pto) 74 | slingAvgPseudotime(pto) 75 | } 76 | -------------------------------------------------------------------------------- /man/slingReducedDim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/AllHelperFunctions.R 3 | \name{slingReducedDim} 4 | \alias{slingReducedDim} 5 | \alias{slingReducedDim,PseudotimeOrdering-method} 6 | \alias{slingReducedDim,SlingshotDataSet-method} 7 | \alias{slingReducedDim,SingleCellExperiment-method} 8 | \title{Extract dimensionality reduction used by Slingshot} 9 | \usage{ 10 | slingReducedDim(x) 11 | 12 | \S4method{slingReducedDim}{PseudotimeOrdering}(x) 13 | 14 | \S4method{slingReducedDim}{SlingshotDataSet}(x) 15 | 16 | \S4method{slingReducedDim}{SingleCellExperiment}(x) 17 | } 18 | \arguments{ 19 | \item{x}{an object containing \code{\link{slingshot}} output.} 20 | } 21 | \value{ 22 | A matrix of coordinates. 23 | } 24 | \description{ 25 | Extract the dimensionality reduction used by 26 | \code{\link{slingshot}}. 27 | } 28 | \examples{ 29 | data("slingshotExample") 30 | rd <- slingshotExample$rd 31 | cl <- slingshotExample$cl 32 | pto <- slingshot(rd, cl, start.clus = '1') 33 | slingReducedDim(pto) 34 | } 35 | -------------------------------------------------------------------------------- /man/slingshot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllGenerics.R, R/slingshot.R 3 | \name{slingshot} 4 | \alias{slingshot} 5 | \alias{slingshot,matrix,character-method} 6 | \alias{slingshot,matrix,matrix-method} 7 | \alias{slingshot,SlingshotDataSet,ANY-method} 8 | \alias{slingshot,data.frame,ANY-method} 9 | \alias{slingshot,matrix,numeric-method} 10 | \alias{slingshot,matrix,factor-method} 11 | \alias{slingshot,matrix,ANY-method} 12 | \alias{slingshot,ClusterExperiment,ANY-method} 13 | \alias{slingshot,SingleCellExperiment,ANY-method} 14 | \title{Perform trajectory inference with Slingshot} 15 | \usage{ 16 | slingshot(data, clusterLabels, ...) 17 | 18 | \S4method{slingshot}{matrix,character}( 19 | data, 20 | clusterLabels, 21 | reducedDim = NULL, 22 | start.clus = NULL, 23 | end.clus = NULL, 24 | dist.method = "slingshot", 25 | use.median = FALSE, 26 | omega = FALSE, 27 | omega_scale = 1.5, 28 | times = NULL, 29 | shrink = TRUE, 30 | extend = "y", 31 | reweight = TRUE, 32 | reassign = TRUE, 33 | thresh = 0.001, 34 | maxit = 15, 35 | stretch = 2, 36 | approx_points = NULL, 37 | smoother = "smooth.spline", 38 | shrink.method = "cosine", 39 | allow.breaks = TRUE, 40 | ... 41 | ) 42 | 43 | \S4method{slingshot}{matrix,matrix}( 44 | data, 45 | clusterLabels, 46 | reducedDim = NULL, 47 | start.clus = NULL, 48 | end.clus = NULL, 49 | dist.method = "slingshot", 50 | use.median = FALSE, 51 | omega = FALSE, 52 | omega_scale = 1.5, 53 | times = NULL, 54 | shrink = TRUE, 55 | extend = "y", 56 | reweight = TRUE, 57 | reassign = TRUE, 58 | thresh = 0.001, 59 | maxit = 15, 60 | stretch = 2, 61 | approx_points = NULL, 62 | smoother = "smooth.spline", 63 | shrink.method = "cosine", 64 | allow.breaks = TRUE, 65 | ... 66 | ) 67 | 68 | \S4method{slingshot}{SlingshotDataSet,ANY}(data, clusterLabels, ...) 69 | 70 | \S4method{slingshot}{data.frame,ANY}(data, clusterLabels, ...) 71 | 72 | \S4method{slingshot}{matrix,numeric}(data, clusterLabels, ...) 73 | 74 | \S4method{slingshot}{matrix,factor}(data, clusterLabels, ...) 75 | 76 | \S4method{slingshot}{matrix,ANY}(data, clusterLabels, ...) 77 | 78 | \S4method{slingshot}{ClusterExperiment,ANY}( 79 | data, 80 | clusterLabels, 81 | reducedDim = NULL, 82 | start.clus = NULL, 83 | end.clus = NULL, 84 | dist.method = "slingshot", 85 | use.median = FALSE, 86 | omega = FALSE, 87 | omega_scale = 1.5, 88 | times = NULL, 89 | shrink = TRUE, 90 | extend = "y", 91 | reweight = TRUE, 92 | reassign = TRUE, 93 | thresh = 0.001, 94 | maxit = 15, 95 | stretch = 2, 96 | approx_points = NULL, 97 | smoother = "smooth.spline", 98 | shrink.method = "cosine", 99 | allow.breaks = TRUE, 100 | ... 101 | ) 102 | 103 | \S4method{slingshot}{SingleCellExperiment,ANY}( 104 | data, 105 | clusterLabels, 106 | reducedDim = NULL, 107 | start.clus = NULL, 108 | end.clus = NULL, 109 | dist.method = "slingshot", 110 | use.median = FALSE, 111 | omega = FALSE, 112 | omega_scale = 1.5, 113 | times = NULL, 114 | shrink = TRUE, 115 | extend = "y", 116 | reweight = TRUE, 117 | reassign = TRUE, 118 | thresh = 0.001, 119 | maxit = 15, 120 | stretch = 2, 121 | approx_points = NULL, 122 | smoother = "smooth.spline", 123 | shrink.method = "cosine", 124 | allow.breaks = TRUE, 125 | ... 126 | ) 127 | } 128 | \arguments{ 129 | \item{data}{a data object containing the matrix of coordinates to be used for 130 | lineage inference. Supported types include \code{matrix}, 131 | \code{\link{SingleCellExperiment}}, \code{\link{SlingshotDataSet}}, and 132 | \code{\link[TrajectoryUtils]{PseudotimeOrdering}}.} 133 | 134 | \item{clusterLabels}{each cell's cluster assignment. This can be a single 135 | vector of labels, or a \code{#cells} by \code{#clusters} matrix 136 | representing weighted cluster assignment. Either representation may 137 | optionally include a \code{"-1"} group meaning "unclustered."} 138 | 139 | \item{...}{Additional parameters to pass to scatter plot smoothing function, 140 | \code{smoother}.} 141 | 142 | \item{reducedDim}{(optional) the dimensionality reduction to be used. Can be 143 | a matrix or a character identifying which element of 144 | \code{reducedDim(data)} is to be used. If multiple dimensionality 145 | reductions are present and this argument is not provided, the first element 146 | will be used by default.} 147 | 148 | \item{start.clus}{(optional) character, indicates the starting cluster(s) 149 | from which lineages will be drawn.} 150 | 151 | \item{end.clus}{(optional) character, indicates which cluster(s) will be 152 | forced to be leaf nodes in the graph.} 153 | 154 | \item{dist.method}{(optional) character, specifies the method for calculating 155 | distances between clusters. Default is \code{"slingshot"}, see 156 | \code{\link[TrajectoryUtils]{createClusterMST}} for details.} 157 | 158 | \item{use.median}{logical, whether to use the median (instead of mean) when 159 | calculating cluster centroid coordinates.} 160 | 161 | \item{omega}{(optional) numeric, this granularity parameter determines the 162 | distance between every real cluster and the artificial cluster, 163 | \code{.OMEGA}. In practice, this makes \code{omega} the maximum allowable 164 | distance between two connected clusters. By default, \code{omega = Inf}. If 165 | \code{omega = TRUE}, the maximum edge length will be set to the median edge 166 | length of the unsupervised MST times a scaling factor (\code{omega_scale}, 167 | default \code{= 3}). This value is provided as a potentially useful rule of 168 | thumb for datasets with outlying clusters or multiple, distinct 169 | trajectories. See \code{outgroup} in 170 | \code{\link[TrajectoryUtils]{createClusterMST}}.} 171 | 172 | \item{omega_scale}{(optional) numeric, scaling factor to use when \code{omega 173 | = TRUE}. The maximum edge length will be set to the median edge length of 174 | the unsupervised MST times \code{omega_scale} (default \code{= 1.5}). See 175 | \code{outscale} in \code{\link[TrajectoryUtils]{createClusterMST}}.} 176 | 177 | \item{times}{numeric, vector of external times associated with either 178 | clusters or cells. See \code{\link[TrajectoryUtils]{defineMSTPaths}} for 179 | details.} 180 | 181 | \item{shrink}{logical or numeric between 0 and 1, determines whether and how 182 | much to shrink branching lineages toward their average prior to the split 183 | (default \code{= TRUE}).} 184 | 185 | \item{extend}{character, how to handle root and leaf clusters of lineages 186 | when constructing the initial, piece-wise linear curve. Accepted values are 187 | \code{'y'} (default), \code{'n'}, and \code{'pc1'}. See 'Details' for more.} 188 | 189 | \item{reweight}{logical, whether to allow cells shared between lineages to be 190 | reweighted during curve fitting. If \code{TRUE} (default), cells shared 191 | between lineages will be iteratively reweighted based on the quantiles of 192 | their projection distances to each curve. See 'Details' for more.} 193 | 194 | \item{reassign}{logical, whether to reassign cells to lineages at each 195 | iteration. If \code{TRUE} (default), cells will be added to a lineage when 196 | their projection distance to the curve is less than the median distance for 197 | all cells currently assigned to the lineage. Additionally, shared cells 198 | will be removed from a lineage if their projection distance to the curve is 199 | above the 90th percentile and their weight along the curve is less than 200 | \code{0.1}.} 201 | 202 | \item{thresh}{numeric, determines the convergence criterion. Percent change 203 | in the total distance from cells to their projections along curves must be 204 | less than \code{thresh}. Default is \code{0.001}, similar to 205 | \code{\link[princurve]{principal_curve}}.} 206 | 207 | \item{maxit}{numeric, maximum number of iterations (default \code{= 15}), see 208 | \code{\link[princurve]{principal_curve}}.} 209 | 210 | \item{stretch}{numeric factor by which curves can be extrapolated beyond 211 | endpoints. Default is \code{2}, see 212 | \code{\link[princurve]{principal_curve}}.} 213 | 214 | \item{approx_points}{numeric, whether curves should be approximated by a 215 | fixed number of points. If \code{FALSE} (or 0), no approximation will be 216 | performed and curves will contain as many points as the input data. If 217 | numeric, curves will be approximated by this number of points (default 218 | \code{= 150} or \code{#cells}, whichever is smaller). See 'Details' and 219 | \code{\link[princurve]{principal_curve}} for more.} 220 | 221 | \item{smoother}{choice of scatter plot smoother. Same as 222 | \code{\link[princurve]{principal_curve}}, but \code{"lowess"} option is 223 | replaced with \code{"loess"} for additional flexibility.} 224 | 225 | \item{shrink.method}{character denoting how to determine the appropriate 226 | amount of shrinkage for a branching lineage. Accepted values are the same 227 | as for \code{kernel} in \code{\link{density}} (default is \code{"cosine"}), 228 | as well as \code{"tricube"} and \code{"density"}. See 'Details' for more.} 229 | 230 | \item{allow.breaks}{logical, determines whether curves that branch very close 231 | to the origin should be allowed to have different starting points.} 232 | } 233 | \value{ 234 | An object of class \code{\link{PseudotimeOrdering}} containing the 235 | pseudotime estimates and lineage assignment weights in the \code{assays}. 236 | The \code{reducedDim} and \code{clusterLabels} matrices will be stored in 237 | the \code{\link[TrajectoryUtils]{cellData}}. Additionally, the 238 | \code{metadata} slot will contain an \code{\link[igraph]{igraph}} object 239 | named \code{mst}, a list of parameter values named \code{slingParams}, a 240 | list of lineages (ordered sets of clusters) named \code{lineages}, and a 241 | list of \code{\link[princurve]{principal_curve}} objects named 242 | \code{curves}. 243 | } 244 | \description{ 245 | Perform trajectory inference with Slingshot 246 | 247 | Perform trajectory inference by (1) identifying lineage 248 | structure with a cluster-based minimum spanning tree, and (2) constructing 249 | smooth representations of each lineage using simultaneous principal curves. 250 | This function wraps the \code{\link{getLineages}} and 251 | \code{\link{getCurves}} functions and is the primary function of the 252 | \code{slingshot} package. 253 | } 254 | \details{ 255 | Given a reduced-dimensional data matrix \code{n} by \code{p} and a 256 | vector of cluster labels (or matrix of soft cluster assignments, 257 | potentially including a \code{-1} label for "unclustered"), this function 258 | performs trajectory inference using a cluster-based minimum spanning tree 259 | on the clusters and simultaneous principal curves for smooth, branching 260 | paths. 261 | 262 | The graph of this structure is learned by fitting a (possibly 263 | constrained) minimum-spanning tree on the clusters, plus the artificial 264 | cluster, \code{.OMEGA}, which is a fixed distance away from every real 265 | cluster. This effectively limits the maximum branch length in the MST to 266 | the chosen distance, meaning that the output may contain multiple trees. 267 | 268 | Once the graph is known, lineages are identified in 269 | any tree with at least two clusters. For a given tree, if there is an 270 | annotated starting cluster, every possible path out of a starting cluster 271 | and ending in a leaf that isn't another starting cluster will be returned. 272 | If no starting cluster is annotated, one will be chosen by a heuristic 273 | method, but this is not recommended. 274 | 275 | When there is only a single lineage, the curve-fitting algorithm is 276 | nearly identical to that of \code{\link[princurve]{principal_curve}}. When 277 | there are multiple lineages and \code{shrink > 0}, an additional step 278 | is added to the iterative procedure, forcing curves to be similar in the 279 | neighborhood of shared points (ie., before they branch). 280 | 281 | The \code{approx_points} argument, which sets the number of points 282 | to be used for each curve, can have a large effect on computation time. Due 283 | to this consideration, we set the default value to \code{150} whenever the 284 | input dataset contains more than that many cells. This setting should help 285 | with exploratory analysis while having little to no impact on the final 286 | curves. To disable this behavior and construct curves with the maximum 287 | number of points, set \code{approx_points = FALSE}. 288 | 289 | The \code{extend} argument determines how to construct the 290 | piece-wise linear curve used to initiate the recursive algorithm. The 291 | initial curve is always based on the lines between cluster centers and if 292 | \code{extend = 'n'}, this curve will terminate at the center of the 293 | endpoint clusters. Setting \code{extend = 'y'} will allow the first and 294 | last segments to extend beyond the cluster center to the orthogonal 295 | projection of the furthest point. Setting \code{extend = 'pc1'} is similar 296 | to \code{'y'}, but uses the first principal component of the cluster to 297 | determine the direction of the curve beyond the cluster center. These 298 | options typically have limited impact on the final curve, but can 299 | occasionally help with stability issues. 300 | 301 | When \code{shink = TRUE}, we compute a percent shrinkage curve, 302 | \eqn{w_l(t)}, for each lineage, a non-increasing function of pseudotime 303 | that determines how much that lineage should be shrunk toward a shared 304 | average curve. We set \eqn{w_l(0) = 1} (complete shrinkage), so that the 305 | curves will always perfectly overlap the average curve at pseudotime 306 | \code{0}. The weighting curve decreases from \code{1} to \code{0} over the 307 | non-outlying pseudotime values of shared cells (where outliers are defined 308 | by the \code{1.5*IQR} rule). The exact shape of the curve in this region is 309 | controlled by \code{shrink.method}, and can follow the shape of any 310 | standard kernel function's cumulative density curve (or more precisely, 311 | survival curve, since we require a decreasing function). Different choices 312 | of \code{shrink.method} to have no discernable impact on the final curves, 313 | in most cases. 314 | 315 | When \code{reweight = TRUE}, weights for shared cells are based on 316 | the quantiles of their projection distances onto each curve. The 317 | distances are ranked and converted into quantiles between \code{0} and 318 | \code{1}, which are then transformed by \code{1 - q^2}. Each cell's weight 319 | along a given lineage is the ratio of this value to the maximum value for 320 | this cell across all lineages. 321 | } 322 | \examples{ 323 | data("slingshotExample") 324 | rd <- slingshotExample$rd 325 | cl <- slingshotExample$cl 326 | pto <- slingshot(rd, cl, start.clus = '1') 327 | 328 | # plotting 329 | sds <- as.SlingshotDataSet(pto) 330 | plot(rd, col = cl, asp = 1) 331 | lines(sds, type = 'c', lwd = 3) 332 | 333 | } 334 | \references{ 335 | Hastie, T., and Stuetzle, W. (1989). "Principal Curves." 336 | \emph{Journal of the American Statistical Association}, 84:502-516. 337 | 338 | Street, K., et al. (2018). "Slingshot: cell lineage and 339 | pseudotime inference for single-cell transcriptomics." \emph{BMC Genomics}, 340 | 19:477. 341 | } 342 | -------------------------------------------------------------------------------- /man/slingshotExample.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AllHelperFunctions.R 3 | \docType{data} 4 | \name{slingshotExample} 5 | \alias{slingshotExample} 6 | \title{Bifurcating lineages data} 7 | \format{ 8 | \code{rd} is a matrix of coordinates in two dimensions, representing 9 | 140 cells. \code{cl} is a numeric vector of 140 corresponding cluster 10 | labels for each cell. 11 | } 12 | \source{ 13 | Simulated data provided with the \code{slingshot} package. 14 | } 15 | \usage{ 16 | data("slingshotExample") 17 | } 18 | \description{ 19 | This simulated dataset contains a low-dimensional representation 20 | of two bifurcating lineages (\code{rd}) and a vector of cluster labels 21 | generated by k-means with \code{K = 5} (\code{cl}). 22 | } 23 | \examples{ 24 | data("slingshotExample") 25 | rd <- slingshotExample$rd 26 | cl <- slingshotExample$cl 27 | slingshot(rd, cl) 28 | } 29 | \keyword{datasets} 30 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(slingshot) 3 | 4 | test_check("slingshot") 5 | -------------------------------------------------------------------------------- /vignettes/bibFile.bib: -------------------------------------------------------------------------------- 1 | @article{tseng2005, 2 | Author = {George C. Tseng and Wing H. Wong}, 3 | Journal = {Biometrics}, 4 | Number = {1}, 5 | Pages = {10-16}, 6 | Title = {Tight Clustering: A Resampling-Based Approach for Identifying Stable and Tight Patterns in Data}, 7 | Volume = {61}, 8 | Year = {2005}} 9 | 10 | @article{finak2015mast, 11 | title={MAST: a flexible statistical framework for assessing transcriptional changes and characterizing heterogeneity in single-cell RNA sequencing data}, 12 | author={Finak, Greg and McDavid, Andrew and Yajima, Masanao and Deng, Jingyuan and Gersuk, Vivian and Shalek, Alex K and Slichter, Chloe K and Miller, Hannah W and McElrath, M Juliana and Prlic, Martin and others}, 13 | journal={Genome Biology}, 14 | volume={16}, 15 | number={1}, 16 | pages={1--13}, 17 | year={2015} 18 | } 19 | 20 | @article{princurve, 21 | title={Principal Curves}, 22 | author={Trevor Hastie and Werner Stuetzle}, 23 | journal={Journal of the American Statistical Association}, 24 | volume={84}, 25 | number={406}, 26 | pages={502-516}, 27 | year={1989} 28 | } 29 | 30 | @article{trapnell2009, 31 | title={TopHat: discovering splice junctions with RNA-Seq}, 32 | author={Cole Trapnell and Lior Pachter and Steven L. Salzberg}, 33 | journal={Bioinformatics}, 34 | volume={25}, 35 | number={9}, 36 | pages={1105-1111}, 37 | year={2009} 38 | } 39 | 40 | @article{risso2014, 41 | title={Normalization of RNA-seq data using factor analysis of control genes or samples}, 42 | author={Davide Risso and John Ngai and Terence P. Speed and Sandrine Dudoit}, 43 | journal={Nature Biotechnology}, 44 | volume={32}, 45 | pages={896–902}, 46 | year={2014} 47 | } 48 | 49 | @article {slingshot, 50 | author = {Street, Kelly and Risso, Davide and Fletcher, Russell B and Das, Diya and Ngai, John and Yosef, Nir and Purdom, Elizabeth and Dudoit, Sandrine}, 51 | title = {Slingshot: Cell lineage and pseudotime inference for single-cell transcriptomics}, 52 | year = {2017}, 53 | doi = {10.1101/128843}, 54 | publisher = {Cold Spring Harbor Laboratory}, 55 | URL = {https://www.biorxiv.org/content/early/2017/04/19/128843}, 56 | eprint = {https://www.biorxiv.org/content/early/2017/04/19/128843.full.pdf}, 57 | journal = {bioRxiv} 58 | } 59 | 60 | @Article{mclust, 61 | title = {{mclust} 5: clustering, classification and density estimation using {G}aussian finite mixture models}, 62 | author = {Luca Scrucca and Michael Fop and Thomas Brendan Murphy and Adrian E. Raftery}, 63 | journal = {The {R} Journal}, 64 | year = {2016}, 65 | volume = {8}, 66 | number = {1}, 67 | pages = {205--233}, 68 | } 69 | 70 | @Manual{scone, 71 | title = {scone: Single Cell Overview of Normalized Expression data}, 72 | author = {Michael Cole and Davide Risso}, 73 | year = {2018}, 74 | note = {R package version 1.4.0}, 75 | } 76 | 77 | @Manual{SingleCellExperiment, 78 | title = {SingleCellExperiment: S4 Classes for Single Cell Data}, 79 | author = {Aaron Lun and Davide Risso}, 80 | year = {2017}, 81 | note = {R package version 1.2.0}, 82 | } 83 | 84 | @Article{zinbwave, 85 | title = {A general and flexible method for signal extraction from single-cell {RNA}-seq data}, 86 | author = {Davide Risso and Fanny Perraudeau and Svetlana Gribkova and Sandrine Dudoit and Jean-Philippe Vert}, 87 | year = {2018}, 88 | volume = {9}, 89 | pages = {284}, 90 | journal = {Nature Communications}, 91 | url = {https://doi.org/10.1038/s41467-017-02554-5}, 92 | } 93 | 94 | 95 | @Article{mnn, 96 | author = {Laleh Haghverdi and Aaron T. L. Lun and Michael D. Morgan and John C. Marioni}, 97 | title = {{{B}atch effects in single-cell {R}{N}{A}-sequencing data are corrected by matching mutual nearest neighbors}}, 98 | journal = {Nat. Biotechnol.}, 99 | year = {2018}, 100 | volume = {36}, 101 | number = {5}, 102 | pages = {421--427}, 103 | doi = {10.1038/nbt.4091}, 104 | } 105 | 106 | @article{van2020trajectory, 107 | Author = {Van den Berge, Koen and Roux de B{\'e}zieux, Hector and Street, Kelly and Saelens, Wouter and Cannoodt, Robrecht and Saeys, Yvan and Dudoit, Sandrine and Clement, Lieven}, 108 | Da = {2020/03/05}, 109 | Date-Added = {2020-03-09 20:41:31 +0000}, 110 | Date-Modified = {2020-03-09 20:41:31 +0000}, 111 | Doi = {10.1038/s41467-020-14766-3}, 112 | Id = {Van den Berge2020}, 113 | Isbn = {2041-1723}, 114 | Journal = {Nature Communications}, 115 | Number = {1}, 116 | Pages = {1201}, 117 | Title = {Trajectory-based differential expression analysis for single-cell sequencing data}, 118 | Ty = {JOUR}, 119 | Url = {https://doi.org/10.1038/s41467-020-14766-3}, 120 | Volume = {11}, 121 | Year = {2020}, 122 | Bdsk-Url-1 = {https://doi.org/10.1038/s41467-020-14766-3}} 123 | 124 | -------------------------------------------------------------------------------- /vignettes/conditionsVignette.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Differential Topology: Comparing Conditions along a Trajectory" 3 | author: "Kelly Street and Koen Van den Berge" 4 | date: "`r Sys.Date()`" 5 | output: 6 | BiocStyle::html_document: 7 | toc: true 8 | vignette: > 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | 16 | 17 | ```{r setup, include=FALSE} 18 | knitr::opts_chunk$set(echo = TRUE) 19 | library(SingleCellExperiment, quietly = TRUE) 20 | library(slingshot, quietly = TRUE) 21 | library(RColorBrewer) 22 | graphics:::par(pch = 16, las = 1) 23 | set.seed(1) 24 | ``` 25 | 26 | # Deprecation Notice 27 | 28 | This vignette represents some of our very early thinking on these topics, which 29 | has since developed significantly. For more up-to-date recommendations, we 30 | strongly encourage you to check out our workflow 31 | [here](https://kstreet13.github.io/bioc2020trajectories/articles/workshopTrajectories.html), 32 | or either of our presentations from [BioC 33 | 2020](https://www.youtube.com/watch?v=Fbd08bIv4yk) and 34 | [EuroBioc2020](https://www.youtube.com/watch?v=4NwIbsJ9lMI). We also recommend 35 | checking out our upcoming package, `condiments`, which implements these concepts 36 | and more! 37 | 38 | # Problem Statement 39 | 40 | This vignette is designed to help you think about the problem of trajectory inference in the presence of two or more conditions. These conditions may be thought of as an organism-level status with potential effects on the lineage topology, ie. "case vs. control" or "mutant vs. wild-type." While some such conditions may induce global changes in cell composition or cell state, we will assume that at least some cell types along the trajectory of interest remain comparable between conditions. 41 | 42 | # Methods 43 | 44 | While it may seem reasonable to perform trajectory inference separately on cells from our different conditions, this is problematic for two reasons: 1) inferred trajectories will be less stable, as they would use only a subset of the available data, and 2) there would be no straightforward method for combining these results (eg. how would we map a branching trajectory onto a non-branching trajectory?). 45 | 46 | Therefore, we propose the following workflow: first, use all available cells to construct the most stable trajectory possible. Second, use this common trajectory framework to compare distributions of cells from different conditions. We will demonstrate this process on an example dataset. 47 | 48 | ```{r dataSetup} 49 | data('slingshotExample') 50 | rd <- slingshotExample$rd 51 | cl <- slingshotExample$cl 52 | condition <- factor(rep(c('A','B'), length.out = nrow(rd))) 53 | condition[110:140] <- 'A' 54 | ls() 55 | ``` 56 | ```{r, echo=FALSE} 57 | plot(rd, asp = 1, pch = 16, col = brewer.pal(3,'Set1')[condition], las=1) 58 | legend('topleft','(x,y)',legend = c('A','B'), title = 'Condition', pch=16, col = brewer.pal(3,'Set1')[1:2]) 59 | ``` 60 | 61 | This dataset consists of a branching trajectory with two conditions (`A` and `B`). Under condition `A`, we find cells from all possible states along the trajectory, but under condition `B`, one of the branches is blocked and only one terminal state can be reached. 62 | 63 | ## Trajectory Inference 64 | 65 | We will start by performing trajectory inference on the full dataset. In principle, any method could be used here, but we will use `slingshot` (cf. [PopGenGoogling](https://twitter.com/popgengoogling/status/1065022894659592192)). 66 | 67 | ```{r} 68 | pto <- slingshot(rd, cl) 69 | ``` 70 | 71 | ```{r, echo=FALSE} 72 | plot(rd, asp = 1, pch = 16, col = brewer.pal(3,'Set1')[condition], las=1) 73 | lines(SlingshotDataSet(pto), lwd=3) 74 | legend('topleft','(x,y)',legend = c('A','B'), title = 'Condition', pch=16, col = brewer.pal(3,'Set1')[1:2]) 75 | ``` 76 | 77 | Each cell has now been assigned to one or two lineages (with an associated weight indicating the certainty of each assignment) and pseudotime values have been calculated. 78 | 79 | ```{r, fig.height=4, fig.width=8, echo = FALSE} 80 | n <- nrow(rd); L <- ncol(slingPseudotime(pto)) 81 | noise <- runif(n, -.1,.1) 82 | plot(as.numeric(slingPseudotime(pto)), rep(1:L, each=n)+noise,pch=16, col = brewer.pal(9,'Set1')[condition], axes=FALSE, xlab='Pseudotime', ylab='Lineage', las=1) 83 | axis(1); axis(2, at=1:L, las=1) 84 | ``` 85 | 86 | ## Approach 1: Distributional Differences 87 | 88 | ```{r, echo=FALSE} 89 | # tA1 <- slingPseudotime(pto, na=FALSE)[condition=='A',1] 90 | # wA1 <- slingCurveWeights(pto)[condition=='A',1]; wA1 <- wA1/sum(wA1) 91 | # dA1 <- density(tA1, weights = wA1) 92 | # tB1 <- slingPseudotime(pto, na=FALSE)[condition=='B',1] 93 | # wB1 <- slingCurveWeights(pto)[condition=='B',1]; wB1 <- wB1/sum(wB1) 94 | # dB1 <- density(tB1, weights = wB1) 95 | # tA2 <- slingPseudotime(pto, na=FALSE)[condition=='A',2] 96 | # wA2 <- slingCurveWeights(pto)[condition=='A',2]; wA2 <- wA2/sum(wA2) 97 | # dA2 <- density(tA2, weights = wA2) 98 | # tB2 <- slingPseudotime(pto, na=FALSE)[condition=='B',2] 99 | # wB2 <- slingCurveWeights(pto)[condition=='B',2]; wB2 <- wB2/sum(wB2) 100 | # dB2 <- density(tB2, weights = wB2) 101 | # 102 | # plot(range(slingPseudotime(pto),na.rm=TRUE), c(1,2+7*max(c(dA2$y,dB2$y))), col='white', xlab='Pseudotime', ylab='Lineage', axes = FALSE, las=1) 103 | # axis(1); axis(2, at=1:2) 104 | # polygon(c(min(dA1$x),dA1$x,max(dA1$x)), 1+7*c(0,dA1$y,0), col=rgb(1,0,0,.5)) 105 | # polygon(c(min(dB1$x),dB1$x,max(dB1$x)), 1+7*c(0,dB1$y,0), col=rgb(0,0,1,.5)) 106 | # polygon(c(min(dA2$x),dA2$x,max(dA2$x)), 2+7*c(0,dA2$y,0), col=rgb(1,0,0,.5)) 107 | # polygon(c(min(dB2$x),dB2$x,max(dB2$x)), 2+7*c(0,dB2$y,0), col=rgb(0,0,1,.5)) 108 | 109 | layout(matrix(1:2,nrow=1)) 110 | boxplot(slingPseudotime(pto)[,1] ~ condition, col = brewer.pal(3,'Set1')[1:2], main = 'Lineage 1', xlab='Condition', ylab='Pseudotime', las=1, pch = 16) 111 | boxplot(slingPseudotime(pto)[,2] ~ condition, col = brewer.pal(3,'Set1')[1:2], main = 'Lineage 2', xlab='Condition', ylab='Pseudotime', las=1, pch = 16) 112 | layout(1) 113 | ``` 114 | 115 | 116 | Now that we have the trajectory, we are interested in testing for differences between the conditions. Any difference in the distributions of cells along a lineage may be meaningful, as it may indicate an overabundance of a particular cell type(s) in one condition. Thus, for this paradigm, we ultimately recommend a Kolmogorov-Smirnov test for detecting differences in the distributions of cells along a lineage. However, we will begin with an illustrative example of testing for a location shift. 117 | 118 | ### Permutation Test 119 | 120 | A T-test would work for this, but we'll use a slightly more robust permutation test. Specifically, we will look for a difference in the weighted means of the pseudotime values between the two conditions. 121 | 122 | ```{r, eval=FALSE} 123 | # Permutation test 124 | t1 <- slingPseudotime(pto, na=FALSE)[,1] 125 | w1 <- slingCurveWeights(pto)[,1] 126 | d1 <- weighted.mean(t1[condition=='A'], w1[condition=='A']) - 127 | weighted.mean(t1[condition=='B'], w1[condition=='B']) 128 | dist1 <- replicate(1e4, { 129 | condition.i <- sample(condition) 130 | weighted.mean(t1[condition.i=='A'], w1[condition.i=='A']) - 131 | weighted.mean(t1[condition.i=='B'], w1[condition.i=='B']) 132 | }) 133 | ``` 134 | ```{r, echo=FALSE, fig.height=4, fig.width=9} 135 | t1 <- slingPseudotime(pto, na=FALSE)[,1] 136 | w1 <- slingCurveWeights(pto)[,1] 137 | d1 <- weighted.mean(t1[condition=='A'], w1[condition=='A']) - 138 | weighted.mean(t1[condition=='B'], w1[condition=='B']) 139 | dist1 <- replicate(1e4, { 140 | condition.i <- sample(condition) 141 | weighted.mean(t1[condition.i=='A'], w1[condition.i=='A']) - 142 | weighted.mean(t1[condition.i=='B'], w1[condition.i=='B']) 143 | }) 144 | t2 <- slingPseudotime(pto, na=FALSE)[,2] 145 | w2 <- slingCurveWeights(pto)[,2] 146 | d2 <- weighted.mean(t2[condition=='A'], w2[condition=='A']) - 147 | weighted.mean(t2[condition=='B'], w2[condition=='B']) 148 | dist2 <- replicate(1e4, { 149 | condition.i <- sample(condition) 150 | weighted.mean(t2[condition.i=='A'], w2[condition.i=='A']) - 151 | weighted.mean(t2[condition.i=='B'], w2[condition.i=='B']) 152 | }) 153 | 154 | layout(matrix(1:2,nrow = 1)) 155 | hist(dist1, breaks=50, col='grey50', xlim = range(c(d1,dist1)), probability = TRUE, xlab = 'Difference of Weighted Means', main = 'Lineage 1 Permutation Test', las=1) 156 | abline(v=d1, col=2, lwd=2) 157 | legend('topright','(x,y)',legend = c('Null Distn.','Observed'), fill=c('grey50',NA), border=c(1,NA), lty=c(NA,1), lwd=c(NA,2), col=c(NA,2), merge = TRUE, seg.len = .6) 158 | 159 | hist(dist2, breaks=50, col='grey50', xlim = range(c(d2,dist2)), probability = TRUE, xlab = 'Difference of Weighted Means', main = 'Lineage 2 Permutation Test', las=1) 160 | abline(v=d2, col=2, lwd=2) 161 | legend('topright','(x,y)',legend = c('Null Distn.','Observed'), fill=c('grey50',NA), border=c(1,NA), lty=c(NA,1), lwd=c(NA,2), col=c(NA,2), merge = TRUE, seg.len = .6) 162 | layout(1) 163 | ``` 164 | ```{r} 165 | paste0('Lineage 1 p-value: ', mean(abs(dist1) > abs(d1))) 166 | paste0('Lineage 2 p-value: ', mean(abs(dist2) > abs(d2))) 167 | ``` 168 | 169 | As we would expect, we see a significant difference in the second lineage (where condition `B` is impeded), but not in the first. However, because the two conditions have different distributions along the second lineage, the exchangeability assumption is violated and the resulting p-value is not valid. 170 | 171 | ### Kolmogorov-Smirnov Test 172 | 173 | Another, more general approach we could take to test for a difference in distributions would be the Kolmogorov-Smirnov test (or a similar permutation test using that test's statistic). This test would, in theory, allow us to pick up subtler differences between the conditions, such as an overabundance of a cell type in one condition. 174 | 175 | ```{r} 176 | # Kolmogorov-Smirnov test 177 | ks.test(slingPseudotime(pto)[condition=='A',1], slingPseudotime(pto)[condition=='B',1]) 178 | ks.test(slingPseudotime(pto)[condition=='A',2], slingPseudotime(pto)[condition=='B',2]) 179 | ``` 180 | 181 | Again, we see a significant difference in the second lineage, but not in the first. Note that unlike the difference of weighted means we used above, this test largely ignores the weights which assign cells to lineages, using any cell with a lineage weight greater than 0 (those with weights of zero are assigned pseudotime values of `NA`). Theoretically, one could use weights with the Kolmogorov-Smirnov test, as the test statistic is based on the maximum difference between cumulative distribution functions, but we were unable to find an implementation of this in base `R` or the `stats` package. For more stringent cutoffs, the user could specify a minimum lineage weight, say of 0.5. Due to this test's ability to detect a wide variety of differences, we would recommend it over the previous procedure for general purpose use. 182 | 183 | 184 | ## Approach 2: Condition Imbalance 185 | 186 | Below, the problem is somewhat rephrased as compared to the approaches used above. Whereas the permutation and KS tests were testing within-lineage differences in **pseudotime values** between conditions, the approaches suggested below rather test for within-lineage **condition imbalance** along the progression of pseudotime. 187 | 188 | The advantages of this approach would be: 189 | 190 | * more straightforward comparison between lineages. 191 | * identification of pseudotime regions where the distribution of conditions is skewed. 192 | * automatic adjustment for baseline condition probabilities as determined by the inception of the trajectory (for example, if one condition naturally has 80% of cells and the other has 20%, which could then evolve to a different proportion as pseudotime progresses). Note that the approaches above could also handle this, but they do not directly estimate a change in contribution for the conditions (e.g. you won’t be able to say that you went from 75% condition A to 50% condition A, only that something’s happened). 193 | 194 | Its disadvantages, however, are: 195 | 196 | * requiring explicit distributional assumptions. 197 | * possibly more difficult to extend to assessing shifts in other moments than the mean (e.g. variance). 198 | 199 | 200 | ### Logistic Regression 201 | 202 | As a first approach, one might check whether the probability of having a cell from a particular condition changes across pseudotime in any lineage. For two conditions, this may proceed using binomial logistic regression, which might be extended to multinomial logistic regression for multiple conditions. To loosen the restrictive binomial assumption, we allow for a more flexible variance structure by using a quasibinomial model. 203 | 204 | ```{r} 205 | pt <- slingPseudotime(pto, na=FALSE) 206 | cw <- slingCurveWeights(pto) 207 | assignment <- apply(cw, 1, which.max) 208 | ptAs <- c() #assigned pseudotime 209 | for(ii in 1:nrow(pt)) ptAs[ii] <- pt[ii,assignment[ii]] 210 | ``` 211 | ```{r,echo=FALSE,fig.height=3.5} 212 | layout(matrix(1:2,nrow=1)) 213 | noise <- runif(n, -.05,.05) 214 | plot(ptAs[assignment == 1], (as.numeric(condition)+noise)[assignment == 1], 215 | col = brewer.pal(9,'Set1')[condition[assignment == 1]], 216 | xlab="Pseudotime", ylab="Condition", main="Lineage 1", pch=16, axes = FALSE) 217 | axis(1); axis(2, at=seq_along(levels(condition)), labels = levels(condition), las = 1) 218 | 219 | plot(ptAs[assignment == 2], (as.numeric(condition)+noise)[assignment == 2], 220 | col = brewer.pal(9,'Set1')[condition[assignment == 2]], 221 | xlab="Pseudotime", ylab="Condition", main="Lineage 2", pch=16, axes = FALSE) 222 | axis(1); axis(2, at=seq_along(levels(condition)), labels = levels(condition), las = 1) 223 | layout(1) 224 | ``` 225 | 226 | 227 | ```{r} 228 | # model for lineage 1: not significant 229 | cond1 <- factor(condition[assignment == 1]) 230 | t1 <- ptAs[assignment == 1] 231 | m1 <- glm(cond1 ~ t1, family = quasibinomial(link = "logit")) 232 | summary(m1) 233 | ``` 234 | 235 | ```{r} 236 | # model for lineage 2: significant 237 | cond2 <- factor(condition[assignment == 2]) 238 | t2 <- ptAs[assignment == 2] 239 | m2 <- glm(cond2 ~ t2, family = quasibinomial(link = "logit")) 240 | summary(m2) 241 | ``` 242 | 243 | 244 | ### Additive Logistic Regression 245 | 246 | Advantages: 247 | 248 | * By extending the linearity assumption, this approach can detect more subtle patterns, e.g. intermediate regions of a lineage that lack cells from a particular condition. This would be reflected by a bimodal distribution of pseudotimes for that condition. 249 | * Also easily extends to multiple conditions using multinomial additive models. 250 | 251 | ```{r} 252 | ### note that logistic regression is monotone hence only allows for increasing or decreasing proportion of cells for a particular condition. 253 | ### hence, for complex trajectories, you could consider smoothing the pseudotime, i.e. 254 | require(mgcv, quietly = TRUE) 255 | m1s <- mgcv::gam(cond1 ~ s(t1), family="quasibinomial") 256 | summary(m1s) 257 | ``` 258 | 259 | ```{r} 260 | m2s <- mgcv::gam(cond2 ~ s(t2), family="quasibinomial") 261 | summary(m2s) 262 | ``` 263 | 264 | ```{r,echo=FALSE} 265 | layout(matrix(1:2,nrow=1)) 266 | plot(m1s, shade = TRUE, main = "Lineage 1", 267 | xlab="Pseudotime", ylab="Logit Prob(B)", scheme=0) 268 | plot(m2s, shade = TRUE, main = "Lineage 2", 269 | xlab="Pseudotime", ylab="Logit Prob(B)") 270 | layout(1) 271 | ``` 272 | 273 | 274 | ### `tradeSeq`-like 275 | 276 | [With apologies](https://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail), suppose that the probability of a particular condition decreases in both lineages, but moreso in one than the other. This can be obscured with the above approaches, especially if the lineages have different lengths. In that case, it would be informative to compare the lineages directly. We can accomplish this goal with an additive model. 277 | 278 | The code below fits an additive model with smoothing terms for the pseudotimes along each lineage. The smoothers may be directly compared since we require the same penalization and basis functions (by setting `id = 1`). Based on the fitted model, inference would be exactly like we do in `tradeSeq`. 279 | 280 | ```{r} 281 | t1 <- pt[,1] 282 | t2 <- pt[,2] 283 | l1 <- as.numeric(assignment == 1) 284 | l2 <- as.numeric(assignment == 2) 285 | m <- gam(condition ~ s(t1, by=l1, id=1) + s(t2, by=l2, id=1), 286 | family = quasibinomial(link = "logit")) 287 | summary(m) 288 | ### and then we're back to tradeSeq-like inference ... 289 | ``` 290 | ```{r, echo=FALSE} 291 | layout(matrix(1:2,nrow=1)) 292 | plot(m, select=1, shade=TRUE, main='Lineage 1', 293 | xlab="Pseudotime", ylab="Logit Prob(B)") 294 | plot(m, select=2, shade=TRUE, main='Lineage 2', 295 | xlab="Pseudotime", ylab="Logit Prob(B)") 296 | layout(1) 297 | ``` 298 | 299 | 300 | ## Other Approaches 301 | 302 | The frameworks suggested here are only a few of the many possible approaches to a highly complex problem. 303 | 304 | We can envision another potential approach, similar to the `Condition Imbalance` framework, but requiring fewer parametric assumptions, inspired by the batch effect metric of [Büttner, et al. (2019)](https://www.nature.com/articles/s41592-018-0254-1) (see Figure 1). Essentially, we may be able to march along the trajectory and check for condition imbalance in a series of local neighborhoods, defined by the k nearest neighbors of particular cells. 305 | 306 | 307 | # Parting Words 308 | 309 | For both of the above procedures, it is important to note that we are making multiple comparisons (in this case, 2). The p-values we obtain from these tests should be corrected for multiple testing, especially for trajectories with a large number of lineages. 310 | 311 | That said, trajectory inference is often one of the last computational methods in a very long analysis pipeline (generally including gene-level quantification, gene filtering / feature selection, and dimensionality reduction). Hence, we strongly discourage the reader from putting too much faith in any p-value that comes out of this analysis. Such values may be useful suggestions, indicating particular features or cells for follow-up study, but should not be treated as meaningful statistical quantities. 312 | 313 | Finally, we would like to emphasize that these procedures are merely suggestions which have not (yet) been subjected to extensive testing and revision. If you have any ideas, questions, or results that you are willing to share, we would love to hear them! Feel free to email Kelly Street or open an issue on either the [`slingshot` repo](https://github.com/kstreet13/slingshot) or [`tradeSeq` repo](https://github.com/statOmics/tradeSeq) on GitHub. 314 | 315 | # Session Info 316 | 317 | ```{r session} 318 | sessionInfo() 319 | ``` 320 | --------------------------------------------------------------------------------