├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── workflows │ ├── R-CMD-check.yaml │ ├── julia-style.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── CRAN-SUBMISSION ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── aaa.R ├── calculate_pvalue.R ├── clusterpermute.R ├── clusters_methods.R ├── compute_timewise_statistics.R ├── extract_clusters.R ├── interop-utils-unexported.R ├── interop-utils.R ├── jlmer.R ├── jlmer_spec.R ├── jlmerclusterperm-package.R ├── julia_rng.R ├── permute.R ├── permute_timewise_statistics.R ├── srr-stats-standards.R ├── threshold_search.R ├── tidy.R ├── utils.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── codemeta.json ├── cran-comments.md ├── format ├── Project.toml └── run.jl ├── inst ├── WORDLIST ├── julia │ ├── JlmerClusterPerm │ │ ├── Project.toml │ │ └── src │ │ │ ├── 01-utils.jl │ │ │ ├── 02-jlmer.jl │ │ │ ├── 03-compute_timewise_statistics.jl │ │ │ ├── 04-extract_clusters.jl │ │ │ ├── 05-permute.jl │ │ │ ├── 06-permute_timewise_statistics.jl │ │ │ └── JlmerClusterPerm.jl │ ├── Project.toml │ └── load-pkgs.jl └── scripts │ └── generate_logo.R ├── jlmerclusterperm.Rproj ├── man ├── calculate_clusters_pvalues.Rd ├── cluster_permutation_tidiers.Rd ├── clusterpermute.Rd ├── compute_timewise_statistics.Rd ├── extract_empirical_clusters.Rd ├── extract_null_cluster_dists.Rd ├── figures │ ├── README- │ │ ├── CPA-io-dark.svg │ │ ├── CPA-io.svg │ │ ├── calculate_clusters_pvalues-dark.svg │ │ ├── calculate_clusters_pvalues.svg │ │ ├── empirical_clusters-dark.svg │ │ ├── empirical_clusters.svg │ │ ├── null_statistics-dark.svg │ │ ├── null_statistics.svg │ │ ├── reCPA-io-dark.svg │ │ ├── reCPA-io.svg │ │ ├── setup-io-dark.svg │ │ ├── setup-io.svg │ │ ├── spec-io-dark.svg │ │ ├── spec-io.svg │ │ ├── walk_threshold_steps-dark.svg │ │ └── walk_threshold_steps.svg │ ├── README-chickweight-1.png │ ├── README-empirical_statistics-1.png │ ├── clusterpermute_animation.gif │ ├── clusterpermute_slice.png │ ├── deCarvalho_fig7.jpg │ ├── desktop.ini │ ├── jlmerclusterperm_fn_design.png │ ├── jlmerclusterperm_logo_plot.png │ ├── logo.png │ ├── logo.svg │ └── logo_highres.png ├── jlmer.Rd ├── jlmerclusterperm-package.Rd ├── jlmerclusterperm_setup.Rd ├── julia_model_tidiers.Rd ├── julia_progress.Rd ├── julia_rng.Rd ├── julia_setup_ok.Rd ├── make_jlmer_spec.Rd ├── permute_by_predictor.Rd ├── permute_timewise_statistics.Rd ├── reexports.Rd ├── to_jlmer.Rd └── walk_threshold_steps.Rd ├── pkgdown ├── extra.css └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── tests ├── README.md ├── testthat.R └── testthat │ ├── helper-skip.R │ ├── test-aaa-a-logging.R │ ├── test-aaa-b-setup.R │ ├── test-clusterpermute.R │ ├── test-jlmer.R │ ├── test-jlmer_spec.R │ ├── test-julia_rng.R │ ├── test-permute.R │ ├── test-progress.R │ ├── test-singularity.R │ ├── test-threshold_search.R │ └── test-timewise_statistics.R └── vignettes ├── .gitignore ├── articles ├── Garrison-et-al-2020.Rmd ├── Geller-et-al-2020.Rmd ├── Ito-et-al-2018.Rmd ├── asynchronous-cpa.Rmd ├── deCarvalho-et-al-2021.Rmd ├── eyetrackingR-comparison.Rmd ├── julia-interface.Rmd ├── paper_files │ └── deCarvalho_fig7.jpg ├── reproducibility.Rmd └── tidying-output.Rmd └── jlmerclusterperm.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^jlmerclusterperm\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^_pkgdown\.yml$ 6 | ^docs$ 7 | ^pkgdown$ 8 | ^\.github$ 9 | ^vignettes/articles$ 10 | ^inst/WORDLIST$ 11 | ^inst/scripts$ 12 | ^codecov\.yml$ 13 | ^CITATION\.cff$ 14 | ^codemeta\.json$ 15 | ^cran-comments\.md$ 16 | ^CRAN-SUBMISSION$ 17 | ^doc$ 18 | ^Meta$ 19 | ^inst/julia/Manifest.toml$ 20 | ^inst/julia/JlmerClusterPerm/Manifest.toml$ 21 | ^format/ 22 | ^tests\README\.md$ 23 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@rstudio.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | . 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | . Translations are available at . 125 | 126 | [homepage]: https://www.contributor-covenant.org 127 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to jlmerclusterperm 2 | 3 | This outlines how to propose a change to jlmerclusterperm. 4 | 5 | ## Fixing typos 6 | 7 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 8 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 9 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 10 | 11 | ## Bigger changes 12 | 13 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 14 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 15 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 16 | 17 | ### Pull request process 18 | 19 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("yjunechoe/jlmerclusterperm", fork = TRUE)`. 20 | 21 | * Install all development dependencies with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 22 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 23 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 24 | 25 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 26 | The title of your PR should briefly describe the change. 27 | The body of your PR should contain `Fixes #issue-number`. 28 | 29 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 30 | 31 | ### Code style 32 | 33 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 34 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. 35 | 36 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 37 | 38 | * We use [testthat](https://cran.r-project.org/package=testthat) for unit tests. 39 | Contributions with test cases included are easier to accept. 40 | 41 | ## Code of Conduct 42 | 43 | Please note that the jlmerclusterperm project is released with a 44 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 45 | project you agree to abide by its terms. 46 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | workflow_dispatch: 9 | 10 | name: R-CMD-check 11 | 12 | jobs: 13 | R-CMD-check: 14 | runs-on: ubuntu-latest 15 | env: 16 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 17 | R_KEEP_PKG_SOURCE: yes 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - uses: r-lib/actions/setup-r-dependencies@v2 26 | with: 27 | extra-packages: any::rcmdcheck 28 | needs: check 29 | 30 | - uses: r-lib/actions/check-r-package@v2 31 | -------------------------------------------------------------------------------- /.github/workflows/julia-style.yaml: -------------------------------------------------------------------------------- 1 | name: Julia-Style 2 | concurrency: 3 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 4 | cancel-in-progress: true 5 | on: 6 | push: 7 | branches: 8 | - 'main' 9 | tags: '*' 10 | pull_request: 11 | types: [opened, synchronize, reopened, ready_for_review] 12 | jobs: 13 | format-check: 14 | name: Style Enforcement (Julia ${{ matrix.julia-version }} - ${{ github.event_name }}) 15 | # Run on push's or non-draft PRs 16 | if: (github.event_name == 'push') || (github.event.pull_request.draft == false) 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | julia-version: [1.8] 21 | steps: 22 | - uses: julia-actions/setup-julia@latest 23 | with: 24 | version: ${{ matrix.julia-version }} 25 | - uses: actions/checkout@v1 26 | - name: Instantiate `format` environment and format 27 | run: | 28 | julia --project=format -e 'using Pkg; Pkg.instantiate(); Pkg.resolve()' 29 | julia --project=format 'format/run.jl' 30 | - uses: reviewdog/action-suggester@v1 31 | if: github.event_name == 'pull_request' 32 | with: 33 | tool_name: JuliaFormatter 34 | fail_on_error: true 35 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | jobs: 10 | document: 11 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 12 | name: document 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: r-lib/actions/pr-fetch@v2 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: any::roxygen2 30 | needs: pr-document 31 | 32 | - name: Document 33 | run: roxygen2::roxygenise() 34 | shell: Rscript {0} 35 | 36 | - name: commit 37 | run: | 38 | git config --local user.name "$GITHUB_ACTOR" 39 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 40 | git add man/\* NAMESPACE 41 | git commit -m 'Document' 42 | 43 | - uses: r-lib/actions/pr-push@v2 44 | with: 45 | repo-token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | style: 48 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 49 | name: style 50 | runs-on: ubuntu-latest 51 | env: 52 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 53 | steps: 54 | - uses: actions/checkout@v3 55 | 56 | - uses: r-lib/actions/pr-fetch@v2 57 | with: 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | - uses: r-lib/actions/setup-r@v2 61 | 62 | - name: Install dependencies 63 | run: install.packages("styler") 64 | shell: Rscript {0} 65 | 66 | - name: Style 67 | run: styler::style_pkg() 68 | shell: Rscript {0} 69 | 70 | - name: commit 71 | run: | 72 | git config --local user.name "$GITHUB_ACTOR" 73 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 74 | git add \*.R 75 | git commit -m 'Style' 76 | 77 | - uses: r-lib/actions/pr-push@v2 78 | with: 79 | repo-token: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | covr::codecov( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 37 | ) 38 | shell: Rscript {0} 39 | 40 | - name: Show testthat output 41 | if: always() 42 | run: | 43 | ## -------------------------------------------------------------------- 44 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 45 | shell: bash 46 | 47 | - name: Upload test results 48 | if: failure() 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: coverage-test-failures 52 | path: ${{ runner.temp }}/package 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | inst/julia/Manifest.toml 7 | inst/julia/JlmerClusterPerm/Manifest.toml 8 | format/Manifest.toml$ 9 | ^CRAN-SUBMISSION$ 10 | inst/doc 11 | /doc/ 12 | /docs/ 13 | /Meta/ 14 | *pptx 15 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 1.1.4 2 | Date: 2024-06-30 05:06:10 UTC 3 | SHA: 0c742e6bc9a4b090b2f8ba043e9921ee3a34eae9 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: jlmerclusterperm 2 | Title: Cluster-Based Permutation Analysis for Densely Sampled Time Data 3 | Version: 1.1.4 4 | Authors@R: 5 | person("June", "Choe", , "jchoe001@gmail.com", role = c("aut", "cre", "cph"), 6 | comment = c(ORCID = "0000-0002-0701-921X")) 7 | Description: An implementation of fast cluster-based permutation analysis 8 | (CPA) for densely-sampled time data developed in Maris & Oostenveld, 9 | 2007 . Supports (generalized, 10 | mixed-effects) regression models for the calculation of timewise 11 | statistics. Provides both a wholesale and a piecemeal interface to the 12 | CPA procedure with an emphasis on interpretability and diagnostics. 13 | Integrates 'Julia' libraries 'MixedModels.jl' and 'GLM.jl' for 14 | performance improvements, with additional functionalities for 15 | interfacing with 'Julia' from 'R' powered by the 'JuliaConnectoR' 16 | package. 17 | License: MIT + file LICENSE 18 | URL: https://github.com/yjunechoe/jlmerclusterperm, 19 | https://yjunechoe.github.io/jlmerclusterperm/ 20 | BugReports: https://github.com/yjunechoe/jlmerclusterperm/issues 21 | Depends: 22 | R (>= 3.5) 23 | Imports: 24 | backports (>= 1.1.7), 25 | cli, 26 | generics, 27 | JuliaConnectoR, 28 | JuliaFormulae, 29 | lme4, 30 | stats, 31 | tools, 32 | utils 33 | Suggests: 34 | broom, 35 | broom.mixed, 36 | covr, 37 | dplyr, 38 | eyetrackingR, 39 | forcats, 40 | future, 41 | ggplot2, 42 | knitr, 43 | MASS, 44 | patchwork, 45 | readr, 46 | rmarkdown, 47 | scales, 48 | testthat (>= 3.0.0), 49 | tibble 50 | VignetteBuilder: 51 | knitr 52 | Config/testthat/edition: 3 53 | Encoding: UTF-8 54 | Roxygen: list(markdown = TRUE, roclets = c ("namespace", "rd", 55 | "srr::srr_stats_roclet")) 56 | RoxygenNote: 7.3.2 57 | SystemRequirements: Julia (>= 1.8) 58 | Collate: 59 | 'jlmerclusterperm-package.R' 60 | 'aaa.R' 61 | 'utils.R' 62 | 'interop-utils.R' 63 | 'interop-utils-unexported.R' 64 | 'julia_rng.R' 65 | 'jlmer_spec.R' 66 | 'jlmer.R' 67 | 'compute_timewise_statistics.R' 68 | 'permute.R' 69 | 'permute_timewise_statistics.R' 70 | 'clusters_methods.R' 71 | 'extract_clusters.R' 72 | 'calculate_pvalue.R' 73 | 'clusterpermute.R' 74 | 'threshold_search.R' 75 | 'tidy.R' 76 | 'zzz.R' 77 | 'srr-stats-standards.R' 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: June Choe 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 June Choe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | 4 | if (getRversion() >= "4.0.0") { 5 | importFrom(tools, R_user_dir) 6 | } else { 7 | importFrom(backports, R_user_dir) 8 | } 9 | S3method(format,empirical_clusters) 10 | S3method(format,jlmer_mod) 11 | S3method(format,jlmer_spec) 12 | S3method(format,null_cluster_dists) 13 | S3method(glance,jlmer_mod) 14 | S3method(print,CPA_out) 15 | S3method(print,empirical_clusters) 16 | S3method(print,jlmer_mod) 17 | S3method(print,jlmer_spec) 18 | S3method(print,null_cluster_dists) 19 | S3method(print,timewise_statistics) 20 | S3method(tidy,empirical_clusters) 21 | S3method(tidy,jlmer_mod) 22 | S3method(tidy,null_cluster_dists) 23 | S3method(tidy,timewise_statistics) 24 | export(calculate_clusters_pvalues) 25 | export(clusterpermute) 26 | export(clusters_are_comparable) 27 | export(compute_timewise_statistics) 28 | export(extract_empirical_clusters) 29 | export(extract_null_cluster_dists) 30 | export(get_rng_seed) 31 | export(get_rng_state) 32 | export(glance) 33 | export(jlmer) 34 | export(jlmerclusterperm_setup) 35 | export(julia_progress) 36 | export(julia_setup_ok) 37 | export(make_jlmer_spec) 38 | export(permute_by_predictor) 39 | export(permute_timewise_statistics) 40 | export(reset_rng_state) 41 | export(set_rng_seed) 42 | export(set_rng_state) 43 | export(stopJulia) 44 | export(tidy) 45 | export(to_jlmer) 46 | export(walk_threshold_steps) 47 | importFrom(JuliaConnectoR,stopJulia) 48 | importFrom(generics,glance) 49 | importFrom(generics,tidy) 50 | importFrom(stats,setNames) 51 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # jlmerclusterperm (development version) 2 | 3 | * Better detection of Julia executable 4 | 5 | # jlmerclusterperm 1.1.4 6 | 7 | - Imports `{JuliaFormulae}` package for parsing R formula into Julia formula 8 | 9 | # jlmerclusterperm 1.1.3 10 | 11 | No user-visible changes 12 | 13 | Fixes for CRAN: 14 | 15 | - Stricter specification of package versions in `Project.toml` to avoid version conflicts. 16 | 17 | # jlmerclusterperm 1.1.2 18 | 19 | - `jlmerclusterperm()` exposes a `cache_dir` argument for manually specifying the cache directory. This was added largely for the convenience of testing. The default value of `cache_dir = NULL` preserves old behavior. 20 | 21 | Fixes for CRAN: 22 | 23 | - Write cache to `tempdir()` for the purposes of running examples and tests. 24 | 25 | # jlmerclusterperm 1.1.1 26 | 27 | - More informative warnings when constructing a spec object 28 | 29 | - Fixed minor bugs when interfacing with Julia 30 | 31 | - Fixed bug with reporting convergence warnings when only one is encountered 32 | 33 | # jlmerclusterperm 1.1.0 34 | 35 | Minor breaking change: 36 | 37 | - Slightly loosened the default convergence criterion for `permute_timewise_statistics()` 38 | 39 | Fixes for CRAN: 40 | 41 | - Specified importing `R_user_dir()` from `{backports}` which was missing previously 42 | 43 | # jlmerclusterperm 1.0.6 44 | 45 | - Fixed bug where `add1` argument to `walk_threshold_steps()` was not being passed down properly 46 | 47 | - R < 4.0 falls back to using `{backports}` for `R_user_dir()` 48 | 49 | Fixes for CRAN: 50 | 51 | - Downgrade DataFrames.jl dependency to 1.3 to avoid a mysterious pre-compilation failure on CRAN windows checks 52 | 53 | # jlmerclusterperm 1.0.5 54 | 55 | No user-visible changes 56 | 57 | Fixes for CRAN: 58 | 59 | - Ensure minimum version requirements for Tables.jl and DataAPI.jl are met 60 | 61 | # jlmerclusterperm 1.0.4 62 | 63 | No user-visible changes 64 | 65 | - Restructured internal Julia code into a module (`JlmerClusterPerm.jl`) 66 | 67 | - Upon activating project, Manifest.toml is cached to `tools::R_user_dir()` to speed up pre-compilation. 68 | 69 | # jlmerclusterperm 1.0.3 70 | 71 | Fixes for CRAN: 72 | 73 | - Use string for package version comparison 74 | 75 | # jlmerclusterperm 1.0.2 76 | 77 | ### Bug fixes 78 | 79 | - `jlmerclusterperm_setup()` now works for rc and alpha builds of Julia 80 | 81 | Fixes for CRAN: 82 | 83 | - The exclusion of Manifest.toml is now specified in .Rbuildignore - only Project.toml is bundled as intended. 84 | 85 | # jlmerclusterperm 1.0.1 86 | 87 | `jlmerclusterperm_setup()` now exits early if Julia version requirement (>=1.8) is not met. 88 | 89 | Fixes for CRAN: 90 | 91 | - Check Julia version requirement before proceeding to examples and tests 92 | 93 | # jlmerclusterperm 1.0.0 94 | 95 | ### Breaking changes 96 | 97 | - The `threshold_steps` argument of `walk_threshold_steps()` is renamed to `steps`. 98 | 99 | ### New features 100 | 101 | - New functions to interface with Julia RNG seed: `get_rng_seed()` and `set_rng_seed()` 102 | 103 | ### Other improvements 104 | 105 | - `jlmerclusterperm_setup()` now echos `Pkg.instantiate()` to print precompilation information upon the first setup call 106 | 107 | # jlmerclusterperm 0.2.0 108 | 109 | Added vignettes. Significant usability improvements 110 | 111 | # jlmerclusterperm 0.1.0 112 | 113 | Initial release 114 | -------------------------------------------------------------------------------- /R/calculate_pvalue.R: -------------------------------------------------------------------------------- 1 | #' Calculate bootstrapped p-values of cluster-mass statistics 2 | #' 3 | #' @param empirical_clusters A `empirical_clusters` object 4 | #' @param null_cluster_dists A `null_cluster_dists` object 5 | #' @param add1 Whether to add 1 to the numerator and denominator when calculating the p-value. 6 | #' Use `TRUE` to effectively count the observed statistic as part of the permuted 7 | #' null distribution (recommended with larger `nsim` prior to publishing results). 8 | #' 9 | #' @seealso [extract_empirical_clusters()], [extract_null_cluster_dists()] 10 | #' 11 | #' @examplesIf julia_setup_ok() 12 | #' \donttest{ 13 | #' \dontshow{ 14 | #' options("jlmerclusterperm.nthreads" = 2) 15 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 16 | #' julia_progress(show = FALSE) 17 | #' } 18 | #' 19 | #' library(dplyr, warn.conflicts = FALSE) 20 | #' 21 | #' # Specification object 22 | #' spec <- make_jlmer_spec( 23 | #' weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 24 | #' subject = "Chick", time = "Time" 25 | #' ) 26 | #' spec 27 | #' 28 | #' # Make empirical clusters 29 | #' empirical_statistics <- compute_timewise_statistics(spec) 30 | #' empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold = 2) 31 | #' empirical_clusters 32 | #' 33 | #' # Make null cluster-mass distribution 34 | #' reset_rng_state() 35 | #' null_statistics <- permute_timewise_statistics(spec, nsim = 100) 36 | #' null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold = 2) 37 | #' 38 | #' # Significance test the empirical cluster(s) from each predictor against the simulated null 39 | #' calculate_clusters_pvalues(empirical_clusters, null_cluster_dists) 40 | #' 41 | #' # Set `add1 = TRUE` to normalize by adding 1 to numerator and denominator 42 | #' calculate_clusters_pvalues(empirical_clusters, null_cluster_dists, add1 = TRUE) 43 | #' 44 | #' # This sequence of procedures is equivalent to `clusterpermute()` 45 | #' reset_rng_state() 46 | #' clusterpermute(spec, threshold = 2, nsim = 100, progress = FALSE) 47 | #' 48 | #' # The empirical clusters and the null cluster-mass distribution must be comparable 49 | #' empirical_clusters2 <- extract_empirical_clusters(empirical_statistics, threshold = 3) 50 | #' # For example, below code errors because thresholds are different (2 vs. 3) 51 | #' try( calculate_clusters_pvalues(empirical_clusters2, null_cluster_dists) ) 52 | #' 53 | #' # Check for compatibility with `clusters_are_comparable()` 54 | #' clusters_are_comparable(empirical_clusters, null_cluster_dists) 55 | #' clusters_are_comparable(empirical_clusters2, null_cluster_dists) 56 | #' 57 | #' \dontshow{ 58 | #' JuliaConnectoR::stopJulia() 59 | #' } 60 | #' } 61 | #' 62 | #' @export 63 | #' @return An `empirical_clusters` object augmented with p-values. 64 | calculate_clusters_pvalues <- function(empirical_clusters, null_cluster_dists, add1 = FALSE) { 65 | clusters_are_comparable(empirical_clusters, null_cluster_dists, error = TRUE) 66 | empirical <- lapply(empirical_clusters, `[[`, "statistic") 67 | empirical <- Filter(function(x) !near_zero(x[1]), empirical) 68 | null <- lapply(null_cluster_dists, `[[`, "statistic") 69 | predictors_to_test <- setNames(nm = names(empirical)[names(empirical) %in% names(null)]) 70 | pvalues <- lapply(predictors_to_test, function(x) { 71 | vapply(empirical[[x]], function(cluster_statistic) { 72 | mean(c(abs(null[[x]]) >= abs(cluster_statistic), if (add1) TRUE)) 73 | }, numeric(1)) 74 | }) 75 | augmented <- empirical_clusters 76 | attr(augmented, "pvalues") <- pvalues 77 | augmented 78 | } 79 | 80 | #' @param error Whether to throw an error if incompatible 81 | #' @rdname calculate_clusters_pvalues 82 | #' @export 83 | clusters_are_comparable <- function(empirical_clusters, null_cluster_dists, error = FALSE) { 84 | if (!inherits(empirical_clusters, "empirical_clusters") || !inherits(null_cluster_dists, "null_cluster_dists")) { 85 | cli::cli_abort("Can only compare object of class {.cls empirical_clusters} against object of class {.cls null_cluster_dists}") 86 | } 87 | empirical_attrs <- attributes(empirical_clusters) 88 | null_attrs <- attributes(null_cluster_dists) 89 | if (!identical(empirical_attrs[c("statistic", "threshold")], null_attrs[c("statistic", "threshold")])) { 90 | cluster_properties <- sapply(list(empirical_attrs, null_attrs), `[`, c("statistic", "threshold")) 91 | mismatch_info <- apply(cluster_properties, 1L, function(x) { 92 | if (x[[1]] != x[[2]]) { 93 | x <- unlist(x, use.names = FALSE) 94 | if (is.numeric(x)) x <- paste0("{", x, "}") 95 | paste0("empirical uses {.val ", x[1], "} but null uses {.val ", x[2], "}.") 96 | } 97 | }) 98 | mismatch_info <- Filter(Negate(is.null), mismatch_info) 99 | mismatch_info <- setNames(paste0("{.strong ", names(mismatch_info), "}: ", mismatch_info), rep("x", length(mismatch_info))) 100 | if (error) { 101 | cli::cli_abort(c( 102 | "Cluster-mass statistics between empirical and null are not comparable.", 103 | "i" = "{.arg empirical_clusters} and {.arg null_cluster_dists} must share the same {.code statistic} and {.code threshold}.", 104 | mismatch_info 105 | )) 106 | } 107 | return(FALSE) 108 | } else { 109 | return(TRUE) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /R/clusterpermute.R: -------------------------------------------------------------------------------- 1 | #' Conduct a cluster-based permutation test 2 | #' 3 | #' @inheritParams permute_timewise_statistics 4 | #' @inheritParams extract_empirical_clusters 5 | #' @inheritParams calculate_clusters_pvalues 6 | #' @param progress Defaults to `TRUE`, which prints progress on each step of the cluster permutation test. 7 | #' 8 | #' @seealso [compute_timewise_statistics()], [permute_timewise_statistics()], 9 | #' [extract_empirical_clusters()], [extract_null_cluster_dists()], 10 | #' [calculate_clusters_pvalues()] 11 | #' 12 | #' @examplesIf julia_setup_ok() 13 | #' \donttest{ 14 | #' \dontshow{ 15 | #' options("jlmerclusterperm.nthreads" = 2) 16 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 17 | #' julia_progress(show = FALSE) 18 | #' } 19 | #' 20 | #' library(dplyr, warn.conflicts = FALSE) 21 | #' 22 | #' # Specification object 23 | #' spec <- make_jlmer_spec( 24 | #' weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 25 | #' subject = "Chick", time = "Time" 26 | #' ) 27 | #' spec 28 | #' 29 | #' # Should minimally provide `threshold` and `nsim`, in addition to the spec object 30 | #' reset_rng_state() 31 | #' CPA <- clusterpermute(spec, threshold = 2, nsim = 100, progress = FALSE) 32 | #' CPA 33 | #' 34 | #' # CPA is a list of `` and `` objects 35 | #' sapply(CPA, class) 36 | #' 37 | #' # You can extract the individual components for further inspection 38 | #' CPA$null_cluster_dists 39 | #' CPA$empirical_clusters 40 | #' 41 | #' \dontshow{ 42 | #' JuliaConnectoR::stopJulia() 43 | #' } 44 | #' } 45 | #' 46 | #' @export 47 | #' @return A list of `null_cluster_dists` and `empirical_clusters` with p-values 48 | clusterpermute <- function(jlmer_spec, 49 | family = c("gaussian", "binomial"), 50 | statistic = c("t", "chisq"), 51 | threshold, 52 | nsim = 100L, 53 | predictors = NULL, 54 | binned = FALSE, 55 | top_n = Inf, 56 | add1 = TRUE, 57 | ..., 58 | progress = TRUE) { 59 | family <- match.arg(family) 60 | statistic <- match.arg(statistic) 61 | jlmer_spec$.backdoor$prepped_for_jlmer <- prep_for_jlmer(jlmer_spec, family, ...) 62 | jlmer_spec$.backdoor$augmented_term_groups <- augment_term_groups(jlmer_spec, statistic) 63 | if (progress) cli::cli_progress_step("Detecting empirical clusters and calculating cluster-mass statistics.") 64 | old_opts <- julia_progress(show = FALSE) 65 | empirical_statistics <- suppressMessages(compute_timewise_statistics(jlmer_spec, family, statistic, ...)) 66 | julia_progress(show = old_opts$show) 67 | empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold, binned, top_n) 68 | if (progress) cli::cli_progress_step("Sampling cluster-mass statistics from a bootstrapped null distribution.") 69 | null_statistics <- permute_timewise_statistics(jlmer_spec, family, statistic, nsim, predictors, ...) 70 | null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold, binned) 71 | if (progress) cli::cli_progress_step("Calculating the probability of the observed cluster-mass statistics.") 72 | empirical_clusters_p <- calculate_clusters_pvalues(empirical_clusters, null_cluster_dists, add1) 73 | structure(list(null_cluster_dists = null_cluster_dists, empirical_clusters = empirical_clusters_p), class = "CPA_out") 74 | } 75 | 76 | #' @export 77 | print.CPA_out <- function(x, ...) { 78 | cat( 79 | "$null_cluster_dists", 80 | format(x$null_cluster_dists, ...), 81 | "\n$empirical_clusters", 82 | format(x$empirical_clusters, ...), 83 | sep = "\n" 84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /R/clusters_methods.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | print.empirical_clusters <- function(x, ...) { 3 | cat(format(x, ...), sep = "\n") 4 | } 5 | 6 | #' @export 7 | format.empirical_clusters <- function(x, ...) { 8 | pvalues <- attr(x, "pvalues") 9 | missing_clusters <- attr(x, "missing_clusters") 10 | term_groups <- attr(x, "term_groups") 11 | predictor_dfs <- attr(term_groups, "dfs") 12 | time <- attr(x, "time") 13 | statistic <- attr(x, "statistic") 14 | threshold <- attr(x, "threshold") 15 | # binned <- attr(x, "binned") 16 | zero_clusters <- x[missing_clusters] 17 | valid_clusters <- x[!missing_clusters] 18 | if (!is.null(pvalues)) valid_clusters <- valid_clusters[names(pvalues)] 19 | cli::cli_format_method( 20 | { 21 | cli::cli_rule(left = paste("{.strong Empirical clusters}", format_threshold(statistic, threshold)), right = "{.cls empirical_clusters}") 22 | for (i in seq_along(valid_clusters)) { 23 | predictor <- names(valid_clusters)[[i]] 24 | cli::cli_text("{.el {predictor}}", if (statistic == "chisq") " ({.emph df = {predictor_dfs[[predictor]]}}){?*}") 25 | cluster_df <- valid_clusters[[i]] 26 | clusters <- split(cluster_df, seq_len(nrow(cluster_df))) 27 | names(clusters) <- paste0("[", time[cluster_df$cluster_start], ", ", time[cluster_df$cluster_end], "]") 28 | clusters <- lapply(clusters, function(cluster) sprintf("%0.3f", cluster$statistic)) 29 | if (clusters[[1]] != "0.00") { 30 | if (!is.null(pvalues)) { 31 | clusters[] <- lapply(seq_along(clusters), function(cluster_ind) { 32 | pval <- pvalues[[i]][[cluster_ind]] 33 | paste(clusters[[cluster_ind]], sprintf(paste0("{.", ifelse(pval < 0.05, "emph", "lemph"), " (p=%0.4f)}"), round(pval, 4))) 34 | }) 35 | } 36 | cli::cli_ul() 37 | cli::cli_dl(clusters) 38 | cli::cli_end() 39 | } 40 | } 41 | cli::cli_rule() 42 | if (length(zero_clusters) > 0) { 43 | cli::cli_alert_warning("No clusters found for {.el {names(zero_clusters)}}") 44 | } 45 | if (statistic == "chisq" && any(predictor_dfs > 1)) { 46 | cli::cli_alert_info(c("* The {.val chisq} statistic for multi-level factors are unsigned. Use {.arg statistic = {.val t}} for a more interpretable result.")) 47 | } 48 | }, 49 | theme = .jlmerclusterperm$cli_theme 50 | ) 51 | } 52 | 53 | #' @export 54 | print.null_cluster_dists <- function(x, levels = 0.95, ...) { 55 | cat(format(x, levels, ...), sep = "\n") 56 | } 57 | 58 | #' @export 59 | format.null_cluster_dists <- function(x, levels = 0.95, ...) { 60 | term_groups <- attr(x, "term_groups") 61 | predictor_dfs <- attr(term_groups, "dfs") 62 | statistic <- attr(x, "statistic") 63 | threshold <- attr(x, "threshold") 64 | # binned <- attr(x, "binned") 65 | cluster_stats <- lapply(x, extract_null_cluster_stats, levels) 66 | cli::cli_format_method( 67 | { 68 | cli::cli_rule(left = paste("{.strong Null cluster-mass distribution}", format_threshold(statistic, threshold)), right = "{.cls null_cluster_dists}") 69 | for (i in seq_along(cluster_stats)) { 70 | predictor <- names(x)[[i]] 71 | cli::cli_text("{.el {predictor}} (n = {cluster_stats[[i]]$n}", if (statistic == "chisq") ", {.emph df = {predictor_dfs[[predictor]]}}{?*}", ")") 72 | cli::cli_ul() 73 | cli::cli_dl(cluster_stats[[i]][1:2]) 74 | cli::cli_end() 75 | } 76 | cli::cli_rule() 77 | if (statistic == "chisq" && any(predictor_dfs > 1)) { 78 | cli::cli_alert_info(c("* The {.val chisq} statistic for multi-level factors are unsigned. Use {.arg statistic = {.val t}} for a more interpretable result.")) 79 | } 80 | }, 81 | theme = .jlmerclusterperm$cli_theme 82 | ) 83 | } 84 | 85 | extract_null_cluster_stats <- function(x, levels) { 86 | statistics <- x$statistic 87 | mean_se <- do.call(sprintf, c("%0.3f (%0.2f)", lapply(list(mean, stats::sd), function(f) f(statistics)))) 88 | cis <- paste(vapply(levels, function(prob) { 89 | percent <- paste0(prob * 100, "%") 90 | bounds <- ((1 - prob) / 2) + c(0, prob) 91 | interval <- stats::quantile(statistics, bounds) 92 | paste(percent, sprintf("[%0.3f, %0.3f]", interval[1], interval[2])) 93 | }, character(1)), collapse = ", ") 94 | list("Mean (SD)" = mean_se, "Coverage intervals" = cis, n = nrow(x)) 95 | } 96 | 97 | format_threshold <- function(statistic, threshold) { 98 | if (statistic == "t") { 99 | "(t > {.val {threshold}})" 100 | } else if (statistic == "chisq") { 101 | "(chisq p < {.val {threshold}})" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /R/compute_timewise_statistics.R: -------------------------------------------------------------------------------- 1 | #' Fit Julia regression models to each time point of a time series data 2 | #' 3 | #' @inheritParams jlmer 4 | #' @param statistic Test statistic for calculating cluster mass. 5 | #' Can be one of `"t"` (default) from the regression model output or 6 | #' `"chisq"` from a likelihood ratio test (takes about twice as long to calculate). 7 | #' @param ... Optional arguments passed to Julia for model fitting. 8 | #' Defaults to `fast = TRUE` (when `family = "binomial"`) and `progress = FALSE`. 9 | #' 10 | #' @seealso [jlmer()], [make_jlmer_spec()] 11 | #' 12 | #' @srrstats {RE3.0} Issues singularity messages and excludes runs with convergence failures in permutation testing (and informs of this) 13 | #' @srrstats {RE3.1} Convergence failures can be retrieved from function outputs, but users are encouraged to watch out for warnings and messages. 14 | #' These can be suppressed via the `suppress*()` functions. 15 | #' 16 | #' @examplesIf julia_setup_ok() 17 | #' \donttest{ 18 | #' \dontshow{ 19 | #' options("jlmerclusterperm.nthreads" = 2) 20 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 21 | #' julia_progress(show = FALSE) 22 | #' } 23 | #' 24 | #' library(dplyr, warn.conflicts = FALSE) 25 | #' 26 | #' # Specification object 27 | #' spec <- make_jlmer_spec( 28 | #' weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 29 | #' subject = "Chick", time = "Time" 30 | #' ) 31 | #' spec 32 | #' 33 | #' # Predictor x Time matrix of t-statistics from regression output 34 | #' empirical_statistics <- compute_timewise_statistics(spec) 35 | #' round(empirical_statistics, 2) 36 | #' 37 | #' # Collect as dataframe with `tidy()` 38 | #' empirical_statistics_df <- tidy(empirical_statistics) 39 | #' empirical_statistics_df 40 | #' 41 | #' # Timewise statistics are from regression models fitted to each time point 42 | #' # - Note the identical statistics at `Time == 0` 43 | #' empirical_statistics_df %>% 44 | #' filter(time == 0) 45 | #' to_jlmer(weight ~ 1 + Diet, filter(ChickWeight, Time == 0)) %>% 46 | #' tidy() %>% 47 | #' select(term, statistic) 48 | #' 49 | #' \dontshow{ 50 | #' JuliaConnectoR::stopJulia() 51 | #' } 52 | #' } 53 | #' 54 | #' @return A predictor-by-time matrix of cluster statistics, of class `timewise_statistics`. 55 | #' @export 56 | compute_timewise_statistics <- function(jlmer_spec, family = c("gaussian", "binomial"), statistic = c("t", "chisq"), ...) { 57 | check_arg_class(jlmer_spec, "jlmer_spec") 58 | family <- match.arg(family) 59 | statistic <- match.arg(statistic) 60 | is_mem <- jlmer_spec$meta$is_mem 61 | term_groups <- augment_term_groups(jlmer_spec, statistic) 62 | args <- prep_for_jlmer(jlmer_spec, family = family, ...) 63 | 64 | opts <- list(...) 65 | opts <- utils::modifyList(list(progress = FALSE), opts) 66 | if (family == "binomial") { 67 | opts <- utils::modifyList(list(fast = TRUE), opts) 68 | } 69 | 70 | 71 | out <- JuliaConnectoR::juliaGet(do.call( 72 | .jlmerclusterperm$jl$compute_timewise_statistics, 73 | c(args, term_groups$jl, statistic, is_mem, .jlmerclusterperm$get_jl_opts(), opts) 74 | )) 75 | 76 | alert_diagnostics(jlmer_spec, out) 77 | 78 | if (statistic == "t") { 79 | dimnames(out$t_matrix) <- out[c("Predictor", "Time")] 80 | out$t_matrix <- out$t_matrix[out$Predictor != "1", , drop = FALSE] 81 | } else { 82 | predictors <- names(term_groups$r) 83 | dimnames(out$t_matrix) <- c(list(Predictor = predictors[predictors != "1"]), out["Time"]) 84 | } 85 | 86 | structure(out$t_matrix, 87 | class = "timewise_statistics", 88 | statistic = statistic, term_groups = term_groups$r 89 | ) 90 | } 91 | 92 | alert_diagnostics <- function(jlmer_spec, out) { 93 | if (any(out$convergence_failures)) { 94 | cli::cli_alert_danger(c( 95 | "{.val {sum(out$convergence_failures)}} convergence failure{?s} at the following timepoint{?s}: ", 96 | "{.val {out$Time[out$convergence_failures]}}." 97 | )) 98 | } 99 | if (jlmer_spec$meta$is_mem) { 100 | singular_fits <- out$singular_fits 101 | if (any(singular_fits)) { 102 | cli::cli_alert_info("{.val {sum(singular_fits)}} singular fit{?s} ({round(mean(singular_fits) * 100, 2)}%).") 103 | } 104 | re_n_terms <- sapply(lme4::findbars(jlmer_spec$formula$jl), function(x) { 105 | setNames(length(x[[2]]), deparse1(x[[3]])) 106 | }) 107 | if (mean(singular_fits) > .2 && any(re_n_terms > 1)) { 108 | cli::cli_alert_info("Average number of components estimated to capture 95% of RE variance:") 109 | rePCs <- rowMeans(out$rePCA_95_matrix) 110 | cli::cli_ul() 111 | lapply(seq_along(out$Grouping), function(i) { 112 | if (re_n_terms[out$Grouping[i]] > 1) cli::cli_li("{out$Grouping[i]}: {sprintf('%.01f', rePCs[i])}") 113 | }) 114 | cli::cli_end() 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /R/interop-utils-unexported.R: -------------------------------------------------------------------------------- 1 | strip_JLTYPE <- function(x) { 2 | attr(x, "JLTYPE") <- NULL 3 | x 4 | } 5 | 6 | df_from_DF <- function(DF) { 7 | df_str <- JuliaConnectoR::juliaGet(DF) 8 | as.data.frame(df_str$columns, col.names = unlist(df_str$colindex$names, use.names = FALSE)) 9 | } 10 | 11 | prep_for_jlmer <- function(jlmer_spec, family, ...) { 12 | if (!is.null(jlmer_spec$.backdoor$prepped_for_jlmer)) { 13 | return(jlmer_spec$.backdoor$prepped_for_jlmer) 14 | } 15 | 16 | julia_formula <- jlmer_spec$formula$jl 17 | data <- jlmer_spec$data 18 | time <- jlmer_spec$meta$time 19 | 20 | opts <- list(...) 21 | if (is.null(opts) || any(names(opts) == "")) { 22 | cli::cli_abort("All optional arguments to fit in {.arg ...} must be named.") 23 | } 24 | 25 | jlmer_fm <- JuliaConnectoR::juliaEval(paste0("@formula(", deparse1(julia_formula), ")")) 26 | jlmer_df <- JuliaConnectoR::juliaCall("DataFrame", data) 27 | 28 | jmler_family <- JuliaConnectoR::juliaCall(switch(family, 29 | gaussian = "Normal", 30 | binomial = "Bernoulli" 31 | )) 32 | 33 | jlmer_groupings <- if (!is.null(lme4::findbars(julia_formula))) { 34 | grouping_vars <- lapply(lme4::findbars(julia_formula), `[[`, 3) 35 | jlmer_groupings <- JuliaConnectoR::juliaLet("Dict(x .=> [Grouping()])", x = grouping_vars) 36 | } else { 37 | NULL 38 | } 39 | 40 | list(jlmer_fm, jlmer_df, time, jmler_family, jlmer_groupings) 41 | } 42 | -------------------------------------------------------------------------------- /R/interop-utils.R: -------------------------------------------------------------------------------- 1 | #' @importFrom JuliaConnectoR stopJulia 2 | #' @export 3 | JuliaConnectoR::stopJulia 4 | 5 | #' Set/get options for Julia progress bar 6 | #' 7 | #' @param show Whether to show the progress bar. You may also pass in a list of `"show"` and `"width"`. 8 | #' @param width Width of the progress bar. If `"auto"`, adjusts the progress bar width to fit the console. 9 | #' 10 | #' @examplesIf julia_setup_ok() 11 | #' \donttest{ 12 | #' \dontshow{ 13 | #' options("jlmerclusterperm.nthreads" = 2) 14 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 15 | #' } 16 | #' 17 | #' # Show current progress options 18 | #' julia_progress() 19 | #' 20 | #' # Set options and save previous options 21 | #' old_progress_opts <- julia_progress(show = FALSE, width = 100) 22 | #' julia_progress() 23 | #' 24 | #' # Restoring progress settings by passing a list of old options 25 | #' old_progress_opts 26 | #' julia_progress(old_progress_opts) 27 | #' identical(julia_progress(), old_progress_opts) 28 | #' 29 | #' # Alternatively, reset to default settings using this syntax: 30 | #' julia_progress(show = TRUE, width = "auto") 31 | #' 32 | #' \dontshow{ 33 | #' JuliaConnectoR::stopJulia() 34 | #' } 35 | #' } 36 | #' 37 | #' @return Previous values for `show` and `width` 38 | #' @export 39 | julia_progress <- function(show, width) { 40 | show_missing <- missing(show) 41 | both_missing <- show_missing && missing(width) 42 | opts_is_list <- !show_missing && is.list(show) && identical(names(show), c("show", "width")) 43 | old_opts <- JuliaConnectoR::juliaGet(JuliaConnectoR::juliaEval("(show = pg[:io] != devnull, width = pg[:width])")) 44 | if (!show_missing) { 45 | if (opts_is_list) { 46 | width <- show$width 47 | show <- show$show 48 | } 49 | JuliaConnectoR::juliaEval(paste0("pg[:io] = ", if (show) "stderr" else "devnull")) 50 | } 51 | if (!missing(width)) { 52 | if (width == "auto") { 53 | width <- max(1L, cli::console_width() - 44L) 54 | } 55 | JuliaConnectoR::juliaEval(paste0("pg[:width] = ", width)) 56 | } 57 | out <- strip_JLTYPE(old_opts) 58 | if (both_missing) { 59 | out 60 | } else { 61 | invisible(out) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /R/jlmer.R: -------------------------------------------------------------------------------- 1 | #' Fit a Julia regression model using lme4 syntax 2 | #' 3 | #' @param jlmer_spec_opts List of options passed to `make_jlmer_spec()` 4 | #' @inheritParams jlmer 5 | #' @inheritParams make_jlmer_spec 6 | #' 7 | #' @seealso [jlmer()], [make_jlmer_spec()] 8 | #' 9 | #' @srrstats {G2.16} Undefined values are caught during model fitting in Julia. 10 | #' @srrstats {RE1.0} Uses R formula interface 11 | #' @srrstats {RE4.0} `jlmer()` and `to_jlmer()` return pointers to Julia model objects. 12 | #' 13 | #' @examplesIf julia_setup_ok() 14 | #' \donttest{ 15 | #' \dontshow{ 16 | #' options("jlmerclusterperm.nthreads" = 2) 17 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 18 | #' julia_progress(show = FALSE) 19 | #' } 20 | #' 21 | #' # Fitting a regression model with R formula syntax 22 | #' to_jlmer(weight ~ 1 + Diet, ChickWeight) 23 | #' 24 | #' # `lm()` equivalent 25 | #' summary(lm(weight ~ 1 + Diet, ChickWeight))$coef 26 | #' 27 | #' # Fitting a mixed model with {lme4} syntax 28 | #' to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight) 29 | #' 30 | #' # Passing MixedModels.jl fit options to the `...` 31 | #' to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight, REML = TRUE) 32 | #' 33 | #' \dontshow{ 34 | #' JuliaConnectoR::stopJulia() 35 | #' } 36 | #' } 37 | #' 38 | #' @return A `jlmer_mod` object. 39 | #' @export 40 | to_jlmer <- function(formula, data, family = c("gaussian", "binomial"), jlmer_spec_opts = list(), ..., progress = FALSE) { 41 | jlmer_spec <- do.call(make_jlmer_spec, utils::modifyList(jlmer_spec_opts, list(formula = formula, data = data))) 42 | jlmer(jlmer_spec, family, ...) 43 | } 44 | 45 | #' Fit a Julia regression model using jlmer specifications 46 | #' 47 | #' @param jlmer_spec Data prepped for jlmer from `make_jlmer_spec()` 48 | #' @param family A GLM family. Currently supports "gaussian" and "binomial". 49 | #' @param ... Optional arguments passed to Julia for model fitting. 50 | #' @param progress If `TRUE`, prints the timing of iterations. 51 | #' 52 | #' @seealso [make_jlmer_spec()] 53 | #' 54 | #' @srrstats {RE3.3} Convergence thresholds can be explicitly set by passing the appropriate argument to the `...` of functions that call GLM/MixedModels 55 | #' 56 | #' @examplesIf julia_setup_ok() 57 | #' \donttest{ 58 | #' \dontshow{ 59 | #' options("jlmerclusterperm.nthreads" = 2) 60 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 61 | #' julia_progress(show = FALSE) 62 | #' } 63 | #' 64 | #' # Fitting a regression model with a specification object 65 | #' spec <- make_jlmer_spec(weight ~ 1 + Diet, ChickWeight) 66 | #' jlmer(spec) 67 | #' 68 | #' # `lm()` equivalent 69 | #' summary(lm(weight ~ 1 + Diet, ChickWeight))$coef 70 | #' 71 | #' \dontshow{ 72 | #' JuliaConnectoR::stopJulia() 73 | #' } 74 | #' } 75 | #' 76 | #' @return A `jlmer_mod` object. 77 | #' @export 78 | jlmer <- function(jlmer_spec, family = c("gaussian", "binomial"), ..., progress = FALSE) { 79 | check_arg_class(jlmer_spec, "jlmer_spec") 80 | family <- match.arg(family) 81 | args <- prep_for_jlmer(jlmer_spec, family = family, ...)[-3] 82 | 83 | mod <- do.call(.jlmerclusterperm$jl$jlmer, c(args, jlmer_spec$meta$is_mem, progress = progress, ...)) 84 | structure(mod, class = c("jlmer_mod", class(mod))) 85 | } 86 | 87 | #' @srrstats {RE4.3} Confidence intervals printed in `print.jlmer_mod()` for LMs, not implemented for LMEMs. 88 | #' @srrstats {RE4.4} Formula printed via `print.jlmer_mod()` or evaluating Julia code on model object via JuliaConnectoR 89 | #' @srrstats {RE4.6} The variance-covariance matrix of the model parameters via `print.jlmer_mod()` or evaluating Julia code on model object via JuliaConnectoR 90 | #' @srrstats {RE4.8} Response variable and metadata printed via `print.jlmer_mod()` or evaluating Julia code on model object via JuliaConnectoR 91 | #' @srrstats {RE4.13} Predictor variables and metadata printed via `print.jlmer_mod()` or evaluating Julia code on model object via JuliaConnectoR 92 | #' @srrstats {RE4.17} Print method defined for all custom S3 objects. 93 | #' @export 94 | print.jlmer_mod <- function(x, ...) { 95 | cat(format(x, ...)) 96 | } 97 | 98 | #' @export 99 | format.jlmer_mod <- function(x, ...) { 100 | cat("\n", sep = "") 101 | if (JuliaConnectoR::juliaLet("x isa MixedModel", x = x)) { 102 | re <- gsub("\n\n$", "\n", showobj_reformat(JuliaConnectoR::juliaCall("VarCorr", x))) 103 | fe <- showobj_reformat(JuliaConnectoR::juliaCall("coeftable", x)) 104 | out <- c(re, fe) 105 | } else { 106 | out <- showobj_reformat(JuliaConnectoR::juliaCall("coeftable", x)) 107 | } 108 | out 109 | } 110 | 111 | showobj_reformat <- function(x) { 112 | paste0(trimws(utils::capture.output(print(x))[-1], whitespace = "[\n]"), collapse = "\n") 113 | } 114 | -------------------------------------------------------------------------------- /R/jlmerclusterperm-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## jlmerclusterperm namespace: start 5 | #' @importFrom stats setNames 6 | #' @rawNamespace 7 | #' if (getRversion() >= "4.0.0") { 8 | #' importFrom(tools, R_user_dir) 9 | #' } else { 10 | #' importFrom(backports, R_user_dir) 11 | #' } 12 | ## jlmerclusterperm namespace: end 13 | NULL 14 | 15 | # nocov start 16 | ### Usethis issue bullets 17 | release_bullets <- function() { 18 | c( 19 | "Repeat checks with Julia v1.8.0 (`juliaup default 1.8.0`)" 20 | ) 21 | } 22 | # nocov end 23 | -------------------------------------------------------------------------------- /R/julia_rng.R: -------------------------------------------------------------------------------- 1 | #' Interface to the Julia RNG 2 | #' 3 | #' @name julia_rng 4 | #' @examplesIf julia_setup_ok() 5 | #' \donttest{ 6 | #' \dontshow{ 7 | #' options("jlmerclusterperm.nthreads" = 2) 8 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 9 | #' julia_progress(show = FALSE) 10 | #' } 11 | #' 12 | #' # RNG initializes to seed=1 counter=0 13 | #' get_rng_seed() 14 | #' get_rng_state() 15 | #' 16 | #' # setter/getter for RNG counter 17 | #' set_rng_state(123) 18 | #' get_rng_state() 19 | #' 20 | #' # setter/getter for RNG seed 21 | #' set_rng_seed(2) 22 | #' get_rng_seed() 23 | #' 24 | #' # restore to initial setting (seed=1, counter=0) 25 | #' set_rng_seed(1) 26 | #' set_rng_state(0) 27 | #' 28 | #' \dontshow{ 29 | #' JuliaConnectoR::stopJulia() 30 | #' } 31 | #' } 32 | #' 33 | #' @return The current seed or counter 34 | NULL 35 | 36 | #' @param i Counter number 37 | #' 38 | #' @rdname julia_rng 39 | #' @export 40 | set_rng_state <- function(i) { 41 | JuliaConnectoR::juliaLet("set_counter!(rng, Int(i))", i = i) 42 | invisible(i) 43 | } 44 | 45 | #' @rdname julia_rng 46 | #' @export 47 | reset_rng_state <- function() { 48 | JuliaConnectoR::juliaEval("set_counter!(rng, 0)") 49 | invisible(0) 50 | } 51 | 52 | #' @rdname julia_rng 53 | #' @export 54 | get_rng_state <- function() { 55 | as.double(JuliaConnectoR::juliaEval("Int(rng.ctr1)")) 56 | } 57 | 58 | #' @rdname julia_rng 59 | #' @param seed Seed 60 | #' @export 61 | set_rng_seed <- function(seed) { 62 | seed_missing <- missing(seed) 63 | if (seed_missing) { 64 | seed <- as.double(JuliaConnectoR::juliaEval("Int(Random123.gen_seed(UInt32, 1)[1])")) 65 | } 66 | JuliaConnectoR::juliaEval(paste0("Random123.seed!(rng, (Int(", seed, "), 20))")) 67 | .jlmerclusterperm$opts$seed <- seed 68 | if (seed_missing) { 69 | cli::cli_alert_info("Using randomly generated seed") 70 | seed 71 | } else { 72 | invisible(seed) 73 | } 74 | } 75 | 76 | #' @rdname julia_rng 77 | #' @export 78 | get_rng_seed <- function() { 79 | .jlmerclusterperm$opts$seed 80 | } 81 | -------------------------------------------------------------------------------- /R/permute.R: -------------------------------------------------------------------------------- 1 | #' Permute data while respecting grouping structure(s) of observations 2 | #' 3 | #' @inheritParams jlmer 4 | #' @param predictors A vector of terms from the model. If multiple, the must form the levels of one predictor. 5 | #' @param predictor_type Whether the predictor is `"between_participant"` or `"within_participant"`. Defaults to `"guess"`. 6 | #' @param n Number of permuted samples of the data to generate. Defaults to `1L`. 7 | #' 8 | #' @examplesIf julia_setup_ok() 9 | #' \donttest{ 10 | #' \dontshow{ 11 | #' options("jlmerclusterperm.nthreads" = 2) 12 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 13 | #' julia_progress(show = FALSE) 14 | #' } 15 | #' 16 | #' # Example data setup 17 | #' chickweights_df <- ChickWeight 18 | #' chickweights_df <- chickweights_df[chickweights_df$Time <= 20, ] 19 | #' chickweights_df$DietInt <- as.integer(chickweights_df$Diet) 20 | #' head(chickweights_df) 21 | #' 22 | #' # Example 1: Spec object using the continuous `DietInt` predictor 23 | #' chickweights_spec1 <- make_jlmer_spec( 24 | #' formula = weight ~ 1 + DietInt, 25 | #' data = chickweights_df, 26 | #' subject = "Chick", time = "Time" 27 | #' ) 28 | #' chickweights_spec1 29 | #' 30 | #' # Shuffling `DietInt` values guesses `predictor_type = "between_participant"` 31 | #' reset_rng_state() 32 | #' spec1_perm1 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt") 33 | #' # This calls the same shuffling algorithm for CPA in Julia, so counter is incremented 34 | #' get_rng_state() 35 | #' 36 | #' # Shuffling under shared RNG state reproduces the same permutation of the data 37 | #' reset_rng_state() 38 | #' spec1_perm2 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt") 39 | #' identical(spec1_perm1, spec1_perm2) 40 | #' 41 | #' # Example 2: Spec object using the multilevel `Diet` predictor 42 | #' chickweights_spec2 <- make_jlmer_spec( 43 | #' formula = weight ~ 1 + Diet, 44 | #' data = chickweights_df, 45 | #' subject = "Chick", time = "Time" 46 | #' ) 47 | #' chickweights_spec2 48 | #' 49 | #' # Levels of a category are automatically shuffled together 50 | #' reset_rng_state() 51 | #' spec2_perm1 <- permute_by_predictor(chickweights_spec2, predictors = "Diet2") 52 | #' reset_rng_state() 53 | #' spec2_perm2 <- permute_by_predictor(chickweights_spec2, predictors = c("Diet2", "Diet3", "Diet4")) 54 | #' identical(spec2_perm1, spec2_perm2) 55 | #' 56 | #' \dontshow{ 57 | #' JuliaConnectoR::stopJulia() 58 | #' } 59 | #' } 60 | #' 61 | #' @return A long dataframe of permuted re-samples with `.id` column representing replication IDs. 62 | #' @export 63 | permute_by_predictor <- function(jlmer_spec, predictors, predictor_type = c("guess", "between_participant", "within_participant"), n = 1L) { 64 | df <- jlmer_spec$data 65 | df_jl <- JuliaConnectoR::juliaCall("DataFrame", as.data.frame(df)) 66 | subject <- jlmer_spec$meta$subject 67 | trial <- jlmer_spec$meta$trial 68 | predictor_type <- match.arg(predictor_type) 69 | if (predictor_type == "guess") { 70 | predictor_type <- .jlmerclusterperm$jl$guess_shuffle_as(df_jl, predictors, subject, trial) 71 | cli::cli_alert_info("Guessed {.arg predictor_type} to be {.val {predictor_type}}") 72 | } 73 | predictor_group <- Filter(function(x) any(predictors %in% x), jlmer_spec$meta$term_groups) 74 | if (length(predictor_group) > 1) { 75 | cli::cli_abort("You can only shuffle levels of one factor at a time.") 76 | } else { 77 | predictor_group <- predictor_group[[1]] 78 | if (!all(predictor_group %in% predictors)) { 79 | cli::cli_alert_info("Shuffling all levels of the factor together ({.val {predictor_group}})") 80 | } 81 | predictors <- predictor_group 82 | } 83 | shuffled <- df_from_DF(.jlmerclusterperm$jl$permute_by_predictor( 84 | df_jl, predictor_type, predictors, subject, trial, as.integer(n), .jlmerclusterperm$get_jl_opts()[[1]] 85 | )) 86 | class(shuffled) <- class(df) 87 | shuffled 88 | } 89 | -------------------------------------------------------------------------------- /R/srr-stats-standards.R: -------------------------------------------------------------------------------- 1 | #' srr_stats 2 | #' 3 | #' All of the following standards initially have `@srrstats` tags. 4 | #' These may be moved at any time to any other locations in your code. 5 | #' Once addressed, please modify the tag from `@srrstats` to `@srrstats`, 6 | #' or `@srrstatsNA`, ensuring that references to every one of the following 7 | #' standards remain somewhere within your code. 8 | #' (These comments may be deleted at any time.) 9 | #' 10 | #' @srrstatsVerbose TRUE 11 | #' 12 | #' @srrstats {G1.4a} 13 | #' @srrstats {G1.6} Some performance comparisons are available in the case study vignettes upon following the links to original tutorials they replicate. 14 | #' @srrstats {G2.0} Function arguments passed to Julia are appropriately handled in Julia. Outputs from Julia to R are checked on type and length. 15 | #' @srrstats {G2.0a} 16 | #' @srrstats {G2.1} Function arguments passed to Julia are appropriately handled in Julia. Type conversion is applied where R and Julia disagree. 17 | #' @srrstats {G2.1a} 18 | #' @srrstats {G2.2} Package checks length of parameters where appropriate. 19 | #' @srrstats {G2.3} Functions use `match.arg()` where appropriate. Character parameters are documented as lower-case and functions do not implement implicit `tolower()` conversion. 20 | #' @srrstats {G2.3a} 21 | #' @srrstats {G2.3b} 22 | #' @srrstats {G2.4} The occasional type conversions happen in transporting data between R and Julia and these are handled appropriately. 23 | #' @srrstats {G2.4a} 24 | #' @srrstats {G2.4b} 25 | #' @srrstats {G2.4c} 26 | #' @srrstats {G2.4d} 27 | #' @srrstats {G2.4e} 28 | #' @srrstats {G2.6} Values are appropriately pre-processed regardless of class structures where appropriate 29 | #' @srrstats {G2.14a} 30 | #' @srrstats {G2.14b} 31 | #' @srrstats {G2.14c} 32 | #' @srrstats {G2.15} Functions check for missinginess in output where appropriate. 33 | #' @srrstats {G5.2a} 34 | #' @srrstats {G5.2b} 35 | #' @srrstats {G5.4a} 36 | #' @srrstats {G5.4b} 37 | #' @srrstats {G5.4c} 38 | #' @srrstats {G5.8a} 39 | #' @srrstats {G5.8b} 40 | #' @srrstats {G5.8c} 41 | #' @srrstats {G5.8d} 42 | #' @srrstats {G5.9a} 43 | #' @srrstats {G5.9b} 44 | #' @srrstats {G5.10} Tests ran on codecov. 45 | #' @srrstats {RE1.2} See G2.5 46 | #' @srrstats {RE1.3a} 47 | #' @srrstats {RE2.1} See G2.14 48 | #' @srrstats {RE4.7} Convergence statistics can be retrieved from evaluating Julia code on model object via JuliaConnectoR 49 | #' @noRd 50 | NULL 51 | 52 | #' NA_standards 53 | #' 54 | #' Any non-applicable standards can have their tags changed from `@srrstats` 55 | #' to `@srrstatsNA`, and placed together in this block, along with explanations 56 | #' for why each of these standards have been deemed not applicable. 57 | #' (These comments may also be deleted at any time.) 58 | #' 59 | #' @srrstatsNA {G1.5} There are no associated publications with the package. 60 | #' @srrstatsNA {G3.1} Package does not rely on covariance calculations 61 | #' @srrstatsNA {G3.1a} 62 | #' @srrstatsNA {G4.0} Package does not enable outputs to be written to local files 63 | #' @srrstatsNA {G5.1} Package does not create any data sets to export. 64 | #' @srrstatsNA {G5.6} No parameter recovery tests, but handled in GLM.jl and MixedModels.jl 65 | #' @srrstatsNA {G5.6a} 66 | #' @srrstatsNA {G5.6b} 67 | #' @srrstatsNA {G5.7} No Algorithm performance tests on regression itself, but handled in GLM.jl and MixedModels.jl 68 | #' @srrstatsNA {G5.11} Tests only require built-in datasets 69 | #' @srrstatsNA {G5.11a} 70 | #' @srrstatsNA {G5.12} All tests are trivial checks on quality and correctness. 71 | #' @srrstatsNA {RE1.4} No data transformations applied beyond numerical coding of categorical variables via `model.matrix()` 72 | #' @srrstatsNA {RE2.0} No data transformations applied beyond numerical coding of categorical variables via `model.matrix()` 73 | #' @srrstatsNA {RE2.2} Missing data in response variable not allowed. 74 | #' @srrstatsNA {RE2.3} Centering data not within scope of the package. 75 | #' @srrstatsNA {RE2.4} Colinearity checks not within scope of the package. 76 | #' @srrstatsNA {RE2.4a} 77 | #' @srrstatsNA {RE2.4b} 78 | #' @srrstatsNA {RE3.2} Handled in GLM.jl and MixedModels.jl 79 | #' @srrstatsNA {RE4.1} Generating a model object without actually fitting values not within the scope of the package. 80 | #' @srrstatsNA {RE4.9} Modelled values of response variables not within scope of the package 81 | #' @srrstatsNA {RE4.12} Transformation functions not within the scope of the package. 82 | #' @srrstatsNA {RE4.14} Extrapolation or forecast *errors* capability not within the scope of the package. 83 | #' @srrstatsNA {RE4.15} Forecase-related functionalities not within the scope of the package. 84 | #' @srrstatsNA {RE4.16} No feature for modelling distinct responses for different categorical groups. 85 | #' @srrstatsNA {RE4.18} No `summary()` method implemented for Julia model objects but see `tidy.jlmer_mod()` and `glance.jlmer_mod()` 86 | #' @srrstatsNA {RE5.0} Scaling relationships on speed not checked, but handled in GLM.jl and MixedModels.jl 87 | #' @srrstatsNA {RE6.0} No default `plot()` methods but users may interface with Julia-specific visualization tools via JuliaConnectoR. 88 | #' @srrstatsNA {RE6.1} See RE6.0 89 | #' @srrstatsNA {RE6.2} See RE6.0 90 | #' @srrstatsNA {RE6.3} See RE4.15 91 | #' @srrstatsNA {RE7.0} Tests with noiseless, exact relationships between predictor (independent) data handled in GLM.jl and MixedModels.jl 92 | #' @srrstatsNA {RE7.0a} 93 | #' @srrstatsNA {RE7.1} Tests with noiseless, exact relationships between predictor (independent) and response (dependent) data handled in GLM.jl and MixedModels.jl 94 | #' @srrstatsNA {RE7.1a} 95 | #' @srrstatsNA {RE7.2} Output objects are designed to strip meta-data attributes input data for R-Julia interoperability 96 | #' @srrstatsNA {RE7.4} See RE4.15 97 | #' @noRd 98 | NULL 99 | -------------------------------------------------------------------------------- /R/threshold_search.R: -------------------------------------------------------------------------------- 1 | #' Test the probability of cluster-mass statistics over a range of threshold values 2 | #' 3 | #' @param steps A vector of threshold values to test 4 | #' @inheritParams extract_empirical_clusters 5 | #' @inheritParams extract_null_cluster_dists 6 | #' @inheritParams calculate_clusters_pvalues 7 | #' @param progress Whether to display a progress bar 8 | #' 9 | #' @examplesIf julia_setup_ok() 10 | #' \donttest{ 11 | #' \dontshow{ 12 | #' options("jlmerclusterperm.nthreads" = 2) 13 | #' jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 14 | #' julia_progress(show = FALSE) 15 | #' } 16 | #' 17 | #' # Specification object 18 | #' spec <- make_jlmer_spec( 19 | #' weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 20 | #' subject = "Chick", time = "Time" 21 | #' ) 22 | #' spec 23 | #' 24 | #' # Compute timewise statistics for the observed and permuted data 25 | #' empirical_statistics <- compute_timewise_statistics(spec) 26 | #' null_statistics <- permute_timewise_statistics(spec, nsim = 100) 27 | #' 28 | #' # Test cluster mass/probability under different threshold values 29 | #' walk_threshold_steps(empirical_statistics, null_statistics, steps = 1:3, 30 | #' progress = FALSE) 31 | #' 32 | #' \dontshow{ 33 | #' JuliaConnectoR::stopJulia() 34 | #' } 35 | #' } 36 | #' 37 | #' @return A data frame of predictor clusters-mass statistics by threshold. 38 | #' @export 39 | walk_threshold_steps <- function(empirical_statistics, null_statistics, steps, 40 | top_n = Inf, binned = FALSE, add1 = TRUE, progress = TRUE) { 41 | test_threshold <- function(threshold) { 42 | empirical <- extract_empirical_clusters(empirical_statistics, threshold = threshold, binned = binned, top_n = top_n) 43 | if (all(attr(empirical, "missing_clusters"))) { 44 | return(NULL) 45 | } 46 | null <- extract_null_cluster_dists(null_statistics, threshold = threshold, binned = binned) 47 | out <- tidy(calculate_clusters_pvalues(empirical, null, add1 = add1)) 48 | out[!is.na(out$pvalue), ] 49 | } 50 | if (progress) { 51 | i_vec <- cli::cli_progress_along(steps) 52 | } else { 53 | i_vec <- seq_along(steps) 54 | } 55 | res <- lapply(i_vec, function(i) { 56 | out <- test_threshold(steps[i]) 57 | if (!is.null(out)) out$threshold <- steps[i] 58 | out 59 | }) 60 | 61 | res_df <- do.call(rbind.data.frame, res) 62 | res_df[, c("threshold", "predictor", "id", "start", "end", "length", "sum_statistic", "pvalue")] 63 | } 64 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | `%|0|%` <- function(lhs, rhs) if (is.null(lhs) || identical(lhs, "") || length(lhs) == 0 || is.na(lhs)) rhs else lhs 2 | 3 | #' @srrstats {G3.0} Equality comparison between floats use tolerance (e.g., the internal function `near_zero()`) 4 | near_zero <- function(x) { 5 | abs(x) < .Machine$double.eps^0.5 6 | } 7 | 8 | maybe_as_tibble <- function(x) { 9 | if ("tibble" %in% loadedNamespaces()) { 10 | rownames(x) <- NULL 11 | class(x) <- c("tbl_df", "tbl", class(x)) 12 | } 13 | x 14 | } 15 | 16 | backtrans_interaction <- function(x) { 17 | gsub("__", ":", x, fixed = TRUE) 18 | } 19 | 20 | replace_as_na <- function(x, y) { 21 | x[x == y] <- NA 22 | x 23 | } 24 | 25 | zero_pad <- function(x, y) { 26 | if (missing(y)) y <- max(x) 27 | sprintf(paste0("%0", floor(log10(y)) + 1, "d"), x) 28 | } 29 | 30 | check_arg_class <- function(x, x_class, x_arg = x_class) { 31 | if (!inherits(x, x_class)) { 32 | cli::cli_abort("{.arg {x_arg}} must be a {.cls {x_class}} object, not a {.cls {class(x)}}") 33 | } 34 | invisible(TRUE) 35 | } 36 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname) { 2 | backports::import(pkgname, "R_user_dir") 3 | } 4 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://yjunechoe.github.io/jlmerclusterperm/ 2 | 3 | home: 4 | title: jlmerclusterperm 5 | description: Julia implementation of bootstrapped cluster-based permutation analysis for time series data 6 | 7 | authors: 8 | June Choe: 9 | href: https://yjunechoe.github.io 10 | 11 | template: 12 | bootstrap: 5 13 | bootswatch: flatly 14 | bslib: 15 | base_font: {google: "Atkinson Hyperlegible"} 16 | 17 | reference: 18 | 19 | - title: Julia setup 20 | desc: > 21 | Start a Julia session set up for jlmerclusterperm 22 | contents: 23 | - julia_setup_ok 24 | - jlmerclusterperm_setup 25 | 26 | - title: Interface to Julia regression modelling 27 | desc: > 28 | Making a jlmer_spec object and fitting models in Julia 29 | contents: 30 | - make_jlmer_spec 31 | - jlmer 32 | - to_jlmer 33 | 34 | - title: Cluster-based permutation analysis 35 | - subtitle: Empirical clusters 36 | desc: > 37 | Detect empirical clusters from the observed data using timewise regression models 38 | contents: 39 | - compute_timewise_statistics 40 | - extract_empirical_clusters 41 | - subtitle: Null distribution 42 | desc: > 43 | Construct a null distribution of cluster-mass statistics via bootstrapped permutation 44 | contents: 45 | - permute_by_predictor 46 | - permute_timewise_statistics 47 | - extract_null_cluster_dists 48 | - subtitle: Significance test 49 | desc: > 50 | Test the probability of the observed cluster-mass statistic vs. null distribution 51 | contents: 52 | - calculate_clusters_pvalues 53 | - walk_threshold_steps 54 | - subtitle: All in one go 55 | contents: 56 | - clusterpermute 57 | 58 | - title: Tidiers 59 | desc: > 60 | Methods for collecting jlmerclusterperm objects as tidy data 61 | contents: 62 | - cluster_permutation_tidiers 63 | - julia_model_tidiers 64 | 65 | - title: Interop utilities 66 | desc: > 67 | Set and get Julia options from R 68 | contents: 69 | - julia_rng 70 | - julia_progress 71 | 72 | articles: 73 | - title: Topics 74 | navbar: Topics 75 | contents: 76 | - articles/tidying-output 77 | - articles/julia-interface 78 | - articles/reproducibility 79 | - articles/asynchronous-cpa 80 | - articles/eyetrackingR-comparison 81 | - title: Case Studies 82 | navbar: Case Studies 83 | contents: 84 | - articles/Garrison-et-al-2020 85 | - articles/Geller-et-al-2020 86 | - articles/deCarvalho-et-al-2021 87 | - articles/Ito-et-al-2018 88 | 89 | news: 90 | - add_cran_date: TRUE 91 | 92 | figures: 93 | dev: ragg::agg_png 94 | dpi: 96 95 | dev.args: [] 96 | out.width: "100%" 97 | fig.ext: png 98 | fig.height: ~ 99 | fig.retina: 2 100 | bg: "white" 101 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | Fixes for CRAN: 2 | 3 | * Ensure nothing is written out to cache while running tests and examples 4 | 5 | ## R CMD check results 6 | 7 | 0 errors | 0 warnings | 0 notes 8 | -------------------------------------------------------------------------------- /format/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" 3 | -------------------------------------------------------------------------------- /format/run.jl: -------------------------------------------------------------------------------- 1 | using JuliaFormatter 2 | 3 | function main() 4 | perfect = true 5 | for d in ["inst/julia"] 6 | @info "...linting $d ..." 7 | dir_perfect = format( 8 | d; 9 | style=BlueStyle(), 10 | join_lines_based_on_source=true, 11 | remove_extra_newlines=true 12 | ) 13 | perfect = perfect && dir_perfect 14 | end 15 | if perfect 16 | @info "Linting complete - no files altered" 17 | else 18 | @info "Linting complete - files altered" 19 | run(`git status`) 20 | end 21 | return nothing 22 | end 23 | 24 | main() 25 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | JuliaConnectoR 2 | Lifecycle 3 | MixedModels 4 | ORCID 5 | WIP 6 | blazingly 7 | ing 8 | jl 9 | jlmer 10 | juliaup 11 | lme 12 | timewise 13 | walkthrough 14 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/Project.toml: -------------------------------------------------------------------------------- 1 | name = "JlmerClusterPerm" 2 | uuid = "2a59065a-9564-4903-82dd-0e42ce19d0e1" 3 | authors = ["June Choe "] 4 | version = "0.1.1" 5 | 6 | [deps] 7 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 8 | Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" 9 | GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a" 10 | MixedModels = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316" 11 | ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" 12 | Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 13 | Random123 = "74087812-796a-5b5d-8853-05524746bad3" 14 | StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" 15 | StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d" 16 | Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" 17 | 18 | [compat] 19 | DataFrames = "1.3" 20 | GLM = "1.8" 21 | MixedModels = "4" 22 | ProgressMeter = "1.7" 23 | Random123 = "1.6" 24 | StatsBase = "0.33" 25 | StatsModels = "0.7" 26 | Suppressor = "0.2" 27 | julia = "1.8" 28 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/01-utils.jl: -------------------------------------------------------------------------------- 1 | function t_value(mod::RegressionModel) 2 | return coef(mod) ./ stderror(mod) 3 | end 4 | 5 | function chisq_value(lrt::StatsModels.LRTestResult) 6 | return abs(2 * (lrt.loglikelihood[2] - lrt.loglikelihood[1])) 7 | end 8 | 9 | function reduce_formula( 10 | to_remove::Vector{Symbol}, 11 | enriched_formula::FormulaTerm, 12 | is_mem::Bool 13 | ) 14 | rhs = enriched_formula.rhs 15 | if is_mem 16 | is_fe = [map(x -> x isa MatrixTerm, rhs)...] 17 | fe_terms = rhs[findfirst(is_fe)].terms 18 | fe_to_keep = findall(!in(to_remove), Symbol.(fe_terms)) 19 | fe_keep = MixedModels.collect_matrix_terms(fe_terms[fe_to_keep]) 20 | new_rhs = (fe_keep, rhs[.!is_fe]...) 21 | else 22 | fe_terms = rhs.terms 23 | fe_to_keep = findall(!in(to_remove), Symbol.(fe_terms)) 24 | new_rhs = StatsModels.collect_matrix_terms(fe_terms[fe_to_keep]) 25 | end 26 | return FormulaTerm(enriched_formula.lhs, new_rhs) 27 | end 28 | 29 | # backports DF.jl 30 | function insertcolval(df::DataFrame, key::Symbol, val::Any) 31 | _df = copy(df) 32 | _df[!, key] .= val 33 | return _df 34 | end 35 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/02-jlmer.jl: -------------------------------------------------------------------------------- 1 | """ 2 | jlmer(formula::FormulaTerm, data::DataFrame, family::Distribution, 3 | contrasts::Union{Nothing,Dict}, is_mem::Bool; opts...) 4 | 5 | Fit a (mixed-effects) regression model using GLM.jl or MixedModels.jl 6 | 7 | `opts...` are passed to fit() for mixed models (`is_mem = true`) 8 | 9 | !!! note 10 | Called from R function `jlmerclusterperm::jlmer()` and 11 | `jlmerclusterperm::to_jlmer()` 12 | """ 13 | function jlmer( 14 | formula::FormulaTerm, 15 | data::DataFrame, 16 | family::Distribution, 17 | contrasts::Union{Nothing,Dict}, 18 | is_mem::Bool; 19 | opts..., 20 | ) 21 | if is_mem 22 | fit(MixedModel, formula, data, family; contrasts=contrasts, opts...) 23 | else 24 | glm(formula, data, family) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/04-extract_clusters.jl: -------------------------------------------------------------------------------- 1 | """ 2 | extract_clusters(t_matrix::Matrix{<:AbstractFloat}, binned::Bool, n::Integer) 3 | 4 | Extract clusters from a predictor-by-time matrix of test statistics. 5 | 6 | !!! note 7 | Called from R function `jlmerclusterperm::extract_empirical_clusters()` 8 | and `jlmerclusterperm::extract_null_cluster_dists()` 9 | """ 10 | function extract_clusters(t_matrix::Matrix{<:AbstractFloat}, binned::Bool, n::Integer) 11 | out = Vector{DataFrame}(undef, size(t_matrix, 1)) 12 | for i in 1:length(out) 13 | out[i] = _extract_clusters(t_matrix[i, :], binned, n, i) 14 | end 15 | return vcat(out...) 16 | end 17 | 18 | function _extract_clusters( 19 | t_vec::Vector{<:AbstractFloat}, 20 | binned::Bool, 21 | n::Integer, 22 | id::Integer 23 | ) 24 | runs = rle(sign.(t_vec)) 25 | run_inds = vcat(0, cumsum(runs[2])) 26 | clusters = (:).(run_inds[1:(end - 1)] .+ 1, run_inds[2:end]) 27 | run_groups = getindex.(Ref(t_vec), clusters) 28 | sum_t = (sum.(x -> isinf(x) ? 0 : x, run_groups)) 29 | cluster_ranges = extrema.(clusters) 30 | clusters_df = DataFrame(cluster_ranges) 31 | rename!(clusters_df, :1 => :cluster_start, :2 => :cluster_end) 32 | clusters_df.statistic = sum_t 33 | filter!(:statistic => !≈(0), clusters_df) 34 | clusters_df.abs_stat = abs.(clusters_df.statistic) 35 | if !binned 36 | filter!([:cluster_end, :cluster_start] => !=, clusters_df) 37 | end 38 | clusters_df.cluster_id = 1:nrow(clusters_df) 39 | sort!(clusters_df, :abs_stat; rev=true) 40 | select!(clusters_df, [:cluster_id, :cluster_start, :cluster_end, :statistic]) 41 | if nrow(clusters_df) == 0 42 | out = DataFrame(; 43 | cluster_start=0, 44 | cluster_end=0, 45 | statistic=0, 46 | cluster_id=0, 47 | id=id, 48 | ) 49 | else 50 | out = first(clusters_df, n) 51 | out.id .= id 52 | end 53 | return out 54 | end 55 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/05-permute.jl: -------------------------------------------------------------------------------- 1 | """ 2 | permute_by_predictor(df::DataFrame, shuffle_type::String, 3 | predictor_cols::Union{String,Vector{String}}, 4 | participant_col::String, 5 | trial_col::Union{Nothing,Integer,String}, 6 | n::Integer, global_opts::NamedTuple) 7 | 8 | Permute data for CPA, respecting the grouping structure(s) of observations. 9 | 10 | !!! note 11 | Called from R function `jlmerclusterperm::permute_by_predictor()` 12 | """ 13 | function permute_by_predictor( 14 | df::DataFrame, 15 | shuffle_type::String, 16 | predictor_cols::Union{String,Vector{String}}, 17 | participant_col::String, 18 | trial_col::Union{Nothing,Integer,String}, 19 | n::Integer, 20 | global_opts::NamedTuple, 21 | ) 22 | _df = copy(df) 23 | out = insertcolval( 24 | shuffle_as!( 25 | _df, 26 | shuffle_type, 27 | predictor_cols, 28 | participant_col, 29 | trial_col, 30 | global_opts.rng 31 | ), 32 | :id, 1, 33 | ) 34 | if (n > 1) 35 | for i in 2:n 36 | append!( 37 | out, 38 | insertcolval( 39 | shuffle_as!( 40 | _df, 41 | shuffle_type, 42 | predictor_cols, 43 | participant_col, 44 | trial_col, 45 | global_opts.rng, 46 | ), 47 | :id, i, 48 | ), 49 | ) 50 | end 51 | end 52 | return select!(out, :id, Not(:id)) 53 | end 54 | 55 | function shuffle_as!( 56 | df::DataFrame, 57 | shuffle_type::String, 58 | predictor_cols::Union{String,Vector{String}}, 59 | participant_col::String, 60 | trial_col::Union{Nothing,Integer,String}, 61 | rng::AbstractRNG, 62 | ) 63 | if shuffle_type == "between_participant" 64 | subj_pred_pair = unique(df[!, vcat(participant_col, predictor_cols)]) 65 | shuffle!(rng, subj_pred_pair[!, participant_col]) 66 | select!(df, Not(predictor_cols)) 67 | leftjoin!(df, subj_pred_pair; on=participant_col) 68 | elseif shuffle_type == "within_participant" 69 | trial_pred_pair = unique(df[!, vcat(participant_col, trial_col, predictor_cols)]) 70 | combine( 71 | groupby(trial_pred_pair, participant_col), 72 | sdf -> shuffle!(rng, sdf[!, trial_col]), 73 | ) 74 | select!(df, Not(predictor_cols)) 75 | leftjoin!(df, trial_pred_pair; on=[participant_col, trial_col]) 76 | end 77 | end 78 | 79 | function guess_shuffle_as( 80 | df::DataFrame, 81 | predictor_cols::Union{String,Vector{String}}, 82 | participant_col::String, 83 | trial_col::Union{Nothing,Integer,String}, 84 | ) 85 | subj_pred_pair = unique(df[!, vcat(participant_col, predictor_cols)]) 86 | unique_combinations = length(unique(df[!, participant_col])) == nrow(subj_pred_pair) 87 | if unique_combinations 88 | "between_participant" 89 | else 90 | if (ismissing(trial_col) || isnothing(trial_col)) 91 | throw("""Guessed "within_participant" but no column for `trial` supplied.""") 92 | else 93 | "within_participant" 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/06-permute_timewise_statistics.jl: -------------------------------------------------------------------------------- 1 | """ 2 | compute_timewise_statistics(formula::FormulaTerm, data::DataFrame, time::String, 3 | family::Distribution, contrasts::Union{Nothing,Dict}, 4 | term_groups::Tuple, statistic::String, is_mem::Bool, 5 | global_opts::NamedTuple; opts...) 6 | 7 | Generate permutation of the data and compute timewise test statistics 8 | from regression models fitted to each time point for each permuted sample. 9 | 10 | `opts...` are passed to fit() for mixed models (`is_mem = true`) 11 | 12 | !!! note 13 | Called from R function `jlmerclusterperm::permute_timewise_statistics()` 14 | """ 15 | function permute_timewise_statistics( 16 | formula::FormulaTerm, 17 | data::DataFrame, 18 | time::String, 19 | family::Distribution, 20 | contrasts::Union{Nothing,Dict}, 21 | nsim::Integer, 22 | participant_col::String, 23 | trial_col::Union{Missing,String}, 24 | term_groups::Tuple, 25 | predictors_subset::Union{Nothing,Dict}, 26 | statistic::String, 27 | is_mem::Bool, 28 | global_opts::NamedTuple; 29 | opts..., 30 | ) 31 | response_var = formula.lhs.sym 32 | times = sort(unique(data[!, time])) 33 | n_times = length(times) 34 | 35 | predictors_exclude = ["(Intercept)"] 36 | if isnothing(predictors_subset) 37 | term_groups_est = filter(grp -> !all(in(predictors_exclude), grp.p), term_groups) 38 | else 39 | term_groups_est = filter( 40 | grp -> any(in(predictors_subset), vcat(grp.p, grp.P)), term_groups 41 | ) 42 | end 43 | 44 | nsims = nsim * length(term_groups_est) 45 | pg = Progress( 46 | nsims; 47 | output=global_opts.pg[:io], 48 | barlen=global_opts.pg[:width], 49 | showspeed=true 50 | ) 51 | 52 | if is_mem 53 | fm_schema = MixedModels.schema(formula, data, contrasts) 54 | form = MixedModels.apply_schema(formula, fm_schema, MixedModel) 55 | re_term = [isa(x, MixedModels.AbstractReTerm) for x in form.rhs] 56 | fixed = String.(Symbol.(form.rhs[.!re_term][1].terms)) 57 | grouping_vars = [String(Symbol(x.rhs)) for x in form.rhs[re_term]] 58 | else 59 | fm_schema = StatsModels.schema(formula, data) 60 | form = StatsModels.apply_schema(formula, fm_schema) 61 | fixed = String.(Symbol.(form.rhs.terms)) 62 | end 63 | 64 | n_fixed = length(fixed) 65 | res = zeros(nsim, n_times, n_fixed) 66 | 67 | for term_groups in term_groups_est 68 | predictors = term_groups.p 69 | permute_data = copy(data) 70 | shuffle_type = guess_shuffle_as( 71 | permute_data, 72 | predictors, 73 | participant_col, 74 | trial_col == "" ? nothing : 3 75 | ) 76 | 77 | if statistic == "chisq" 78 | reduced_formula = reduce_formula(Symbol.(predictors), form, is_mem) 79 | test_opts = (reduced_formula=(fm=reduced_formula, i=term_groups.i),) 80 | elseif statistic == "t" 81 | test_opts = nothing 82 | end 83 | 84 | for i in 1:nsim 85 | shuffle_as!( 86 | permute_data, 87 | shuffle_type, 88 | predictors, 89 | participant_col, 90 | trial_col, 91 | global_opts.rng, 92 | ) 93 | if is_mem 94 | timewise_stats = timewise_lme( 95 | formula, 96 | permute_data, 97 | time, 98 | family, 99 | contrasts, 100 | statistic, 101 | test_opts, 102 | response_var, 103 | fixed, 104 | grouping_vars, 105 | times, 106 | n_times, 107 | false, 108 | global_opts; 109 | opts..., 110 | ) 111 | zs = timewise_stats.t_matrix 112 | else 113 | timewise_stats = timewise_lm( 114 | formula, 115 | permute_data, 116 | time, 117 | family, 118 | statistic, 119 | test_opts, 120 | response_var, 121 | fixed, 122 | times, 123 | n_times, 124 | ) 125 | zs = timewise_stats.t_matrix 126 | end 127 | for term_ind in term_groups.i 128 | res[i, :, term_ind] = zs[term_ind, :] 129 | end 130 | next!(pg) 131 | end 132 | end 133 | 134 | predictors = vcat(map(terms -> terms.p, term_groups_est)...) 135 | res = res[:, :, vcat(map(terms -> terms.i, term_groups_est)...)] 136 | 137 | return (z_array=res, predictors=predictors) 138 | end 139 | -------------------------------------------------------------------------------- /inst/julia/JlmerClusterPerm/src/JlmerClusterPerm.jl: -------------------------------------------------------------------------------- 1 | module JlmerClusterPerm 2 | 3 | using Suppressor 4 | using Random 5 | using Random123 6 | using StatsBase 7 | using StatsModels 8 | using Distributions 9 | using GLM 10 | using MixedModels 11 | using DataFrames 12 | using ProgressMeter 13 | 14 | export jlmer 15 | export compute_timewise_statistics 16 | export extract_clusters 17 | export permute_by_predictor 18 | export permute_timewise_statistics 19 | 20 | include("01-utils.jl") 21 | include("02-jlmer.jl") 22 | include("03-compute_timewise_statistics.jl") 23 | include("04-extract_clusters.jl") 24 | include("05-permute.jl") 25 | include("06-permute_timewise_statistics.jl") 26 | 27 | end # module JlmerClusterPerm 28 | -------------------------------------------------------------------------------- /inst/julia/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 3 | Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" 4 | GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a" 5 | JlmerClusterPerm = "2a59065a-9564-4903-82dd-0e42ce19d0e1" 6 | MixedModels = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316" 7 | ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" 8 | Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 9 | Random123 = "74087812-796a-5b5d-8853-05524746bad3" 10 | StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" 11 | StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d" 12 | Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" 13 | 14 | # add DataFrames@1.5, GLM@1.8, MixedModels@4, ProgressMeter@1.7, 15 | # Random123@1.6, StatsBase@0.33, StatsModels@0.7, Suppressor@0.2 16 | # add Distributions, Random 17 | # dev JlmerClusterPerm 18 | 19 | [compat] 20 | DataFrames = "~1.3.0" 21 | GLM = "~1.8" 22 | JlmerClusterPerm = "=0.1.1" 23 | MixedModels = "4" 24 | ProgressMeter = "~1.7" 25 | Random123 = "~1.6" 26 | StatsBase = "~0.33" 27 | StatsModels = "~0.7" 28 | Suppressor = "~0.2" 29 | julia = "1.8" 30 | -------------------------------------------------------------------------------- /inst/julia/load-pkgs.jl: -------------------------------------------------------------------------------- 1 | using Suppressor 2 | using Random 3 | using Random123 4 | using StatsBase 5 | using StatsModels 6 | using Distributions 7 | using GLM 8 | using MixedModels 9 | using ProgressMeter 10 | using DataFrames 11 | -------------------------------------------------------------------------------- /inst/scripts/generate_logo.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | 3 | df <- data.frame(x = (1:25 * 4) / 100) 4 | df$y1 <- plogis(df$x * 3 - 1) 5 | df$y2 <- plogis(df$x * 6 - 1.5) 6 | df 7 | 8 | pal <- list(blue = "#4063D8", green = "#389826", purple = "#9558B2") 9 | 10 | p <- ggplot(df, aes(x)) + 11 | geom_ribbon( 12 | aes(ymin = y1, ymax = y2), 13 | fill = alpha("black", .5), 14 | data = ~ .x[10:18, ] 15 | ) + 16 | geom_line( 17 | aes(y = y1), 18 | linewidth = 2, 19 | color = pal$green 20 | ) + 21 | geom_line( 22 | aes(y = y2), 23 | linewidth = 2, 24 | color = pal$purple 25 | ) + 26 | geom_point( 27 | aes(y = y1), 28 | fill = pal$green, 29 | size = 3, 30 | shape = 21 31 | ) + 32 | geom_point( 33 | aes(y = y2), 34 | fill = pal$purple, 35 | size = 3, 36 | shape = 21 37 | ) + 38 | geom_segment( 39 | aes(x = x[10], xend = x[18], y = -Inf, yend = -Inf), 40 | linewidth = 10, 41 | color = pal$blue 42 | ) + 43 | scale_x_continuous(n.breaks = nrow(df)) + 44 | labs(x = "jlmerclusterperm", y = NULL) + 45 | theme_classic() + 46 | theme( 47 | axis.text = element_blank(), 48 | axis.ticks.y = element_blank(), 49 | axis.line.y = element_blank(), 50 | axis.title.x = element_text( 51 | family = "JuliaMono", 52 | face = "bold", 53 | size = 32, 54 | vjust = -3 55 | ), 56 | plot.background = element_rect(color = NA, fill = NA), 57 | panel.background = element_rect(fill = NA), 58 | plot.margin = margin(0, 1, 1, 1, "cm") 59 | ) 60 | 61 | ggsave("man/figures/jlmerclusterperm_logo_plot.png", p, width = 4.6, height = 3) 62 | -------------------------------------------------------------------------------- /jlmerclusterperm.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /man/calculate_clusters_pvalues.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/calculate_pvalue.R 3 | \name{calculate_clusters_pvalues} 4 | \alias{calculate_clusters_pvalues} 5 | \alias{clusters_are_comparable} 6 | \title{Calculate bootstrapped p-values of cluster-mass statistics} 7 | \usage{ 8 | calculate_clusters_pvalues( 9 | empirical_clusters, 10 | null_cluster_dists, 11 | add1 = FALSE 12 | ) 13 | 14 | clusters_are_comparable(empirical_clusters, null_cluster_dists, error = FALSE) 15 | } 16 | \arguments{ 17 | \item{empirical_clusters}{A \code{empirical_clusters} object} 18 | 19 | \item{null_cluster_dists}{A \code{null_cluster_dists} object} 20 | 21 | \item{add1}{Whether to add 1 to the numerator and denominator when calculating the p-value. 22 | Use \code{TRUE} to effectively count the observed statistic as part of the permuted 23 | null distribution (recommended with larger \code{nsim} prior to publishing results).} 24 | 25 | \item{error}{Whether to throw an error if incompatible} 26 | } 27 | \value{ 28 | An \code{empirical_clusters} object augmented with p-values. 29 | } 30 | \description{ 31 | Calculate bootstrapped p-values of cluster-mass statistics 32 | } 33 | \examples{ 34 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 35 | \donttest{ 36 | \dontshow{ 37 | options("jlmerclusterperm.nthreads" = 2) 38 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 39 | julia_progress(show = FALSE) 40 | } 41 | 42 | library(dplyr, warn.conflicts = FALSE) 43 | 44 | # Specification object 45 | spec <- make_jlmer_spec( 46 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 47 | subject = "Chick", time = "Time" 48 | ) 49 | spec 50 | 51 | # Make empirical clusters 52 | empirical_statistics <- compute_timewise_statistics(spec) 53 | empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold = 2) 54 | empirical_clusters 55 | 56 | # Make null cluster-mass distribution 57 | reset_rng_state() 58 | null_statistics <- permute_timewise_statistics(spec, nsim = 100) 59 | null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold = 2) 60 | 61 | # Significance test the empirical cluster(s) from each predictor against the simulated null 62 | calculate_clusters_pvalues(empirical_clusters, null_cluster_dists) 63 | 64 | # Set `add1 = TRUE` to normalize by adding 1 to numerator and denominator 65 | calculate_clusters_pvalues(empirical_clusters, null_cluster_dists, add1 = TRUE) 66 | 67 | # This sequence of procedures is equivalent to `clusterpermute()` 68 | reset_rng_state() 69 | clusterpermute(spec, threshold = 2, nsim = 100, progress = FALSE) 70 | 71 | # The empirical clusters and the null cluster-mass distribution must be comparable 72 | empirical_clusters2 <- extract_empirical_clusters(empirical_statistics, threshold = 3) 73 | # For example, below code errors because thresholds are different (2 vs. 3) 74 | try( calculate_clusters_pvalues(empirical_clusters2, null_cluster_dists) ) 75 | 76 | # Check for compatibility with `clusters_are_comparable()` 77 | clusters_are_comparable(empirical_clusters, null_cluster_dists) 78 | clusters_are_comparable(empirical_clusters2, null_cluster_dists) 79 | 80 | \dontshow{ 81 | JuliaConnectoR::stopJulia() 82 | } 83 | } 84 | \dontshow{\}) # examplesIf} 85 | } 86 | \seealso{ 87 | \code{\link[=extract_empirical_clusters]{extract_empirical_clusters()}}, \code{\link[=extract_null_cluster_dists]{extract_null_cluster_dists()}} 88 | } 89 | -------------------------------------------------------------------------------- /man/cluster_permutation_tidiers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tidy.R 3 | \name{cluster_permutation_tidiers} 4 | \alias{cluster_permutation_tidiers} 5 | \alias{tidy.timewise_statistics} 6 | \alias{tidy.empirical_clusters} 7 | \alias{tidy.null_cluster_dists} 8 | \title{Tidiers for cluster permutation test objects} 9 | \usage{ 10 | \method{tidy}{timewise_statistics}(x, ...) 11 | 12 | \method{tidy}{empirical_clusters}(x, ...) 13 | 14 | \method{tidy}{null_cluster_dists}(x, ...) 15 | } 16 | \arguments{ 17 | \item{x}{An object of class \verb{}, \verb{}, or \verb{}} 18 | 19 | \item{...}{Unused} 20 | } 21 | \value{ 22 | A data frame 23 | } 24 | \description{ 25 | Tidiers for cluster permutation test objects 26 | } 27 | \examples{ 28 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 29 | \donttest{ 30 | \dontshow{ 31 | options("jlmerclusterperm.nthreads" = 2) 32 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 33 | julia_progress(show = FALSE) 34 | } 35 | 36 | library(dplyr, warn.conflicts = FALSE) 37 | 38 | # Specification object 39 | spec <- make_jlmer_spec( 40 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 41 | subject = "Chick", time = "Time" 42 | ) 43 | spec 44 | 45 | # Method for `` 46 | empirical_statistics <- compute_timewise_statistics(spec) 47 | class(empirical_statistics) 48 | tidy(empirical_statistics) 49 | 50 | reset_rng_state() 51 | null_statistics <- permute_timewise_statistics(spec, nsim = 100) 52 | class(null_statistics) 53 | tidy(null_statistics) 54 | 55 | # Method for `` 56 | empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold = 2) 57 | class(empirical_clusters) 58 | tidy(empirical_clusters) 59 | 60 | # Method for `` 61 | null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold = 2) 62 | class(null_cluster_dists) 63 | tidy(null_cluster_dists) 64 | 65 | \dontshow{ 66 | JuliaConnectoR::stopJulia() 67 | } 68 | } 69 | \dontshow{\}) # examplesIf} 70 | } 71 | -------------------------------------------------------------------------------- /man/clusterpermute.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clusterpermute.R 3 | \name{clusterpermute} 4 | \alias{clusterpermute} 5 | \title{Conduct a cluster-based permutation test} 6 | \usage{ 7 | clusterpermute( 8 | jlmer_spec, 9 | family = c("gaussian", "binomial"), 10 | statistic = c("t", "chisq"), 11 | threshold, 12 | nsim = 100L, 13 | predictors = NULL, 14 | binned = FALSE, 15 | top_n = Inf, 16 | add1 = TRUE, 17 | ..., 18 | progress = TRUE 19 | ) 20 | } 21 | \arguments{ 22 | \item{jlmer_spec}{Data prepped for jlmer from \code{make_jlmer_spec()}} 23 | 24 | \item{family}{A GLM family. Currently supports "gaussian" and "binomial".} 25 | 26 | \item{statistic}{Test statistic for calculating cluster mass. 27 | Can be one of \code{"t"} (default) from the regression model output or 28 | \code{"chisq"} from a likelihood ratio test (takes about twice as long to calculate).} 29 | 30 | \item{threshold}{The threshold value that the statistic must pass to contribute to cluster mass. 31 | Interpretation differs on the choice of statistic (more below): 32 | \itemize{ 33 | \item If \code{statistic = "t"}, the threshold for t-value (beta/std.err) from the regression model. 34 | \item If \code{statistic = "chisq"}, the threshold for the p-value of chi-squared statistics from likelihood ratio tests. 35 | }} 36 | 37 | \item{nsim}{Number of simulations description} 38 | 39 | \item{predictors}{(Optional) a subset of predictors to test. Defaults to \code{NULL} which tests all predictors.} 40 | 41 | \item{binned}{Whether the data has been aggregated/collapsed into time bins. Defaults to \code{FALSE}, 42 | which requires a cluster to span at least two time points. If \code{TRUE}, allows length-1 clusters to exist.} 43 | 44 | \item{top_n}{How many clusters to return, in the order of the size of the cluster-mass statistic. 45 | Defaults to \code{Inf} which return all detected clusters.} 46 | 47 | \item{add1}{Whether to add 1 to the numerator and denominator when calculating the p-value. 48 | Use \code{TRUE} to effectively count the observed statistic as part of the permuted 49 | null distribution (recommended with larger \code{nsim} prior to publishing results).} 50 | 51 | \item{...}{Optional arguments passed to Julia for model fitting. 52 | Defaults to \code{fast = TRUE} (when \code{family = "binomial"}) and \code{progress = FALSE}.} 53 | 54 | \item{progress}{Defaults to \code{TRUE}, which prints progress on each step of the cluster permutation test.} 55 | } 56 | \value{ 57 | A list of \code{null_cluster_dists} and \code{empirical_clusters} with p-values 58 | } 59 | \description{ 60 | Conduct a cluster-based permutation test 61 | } 62 | \examples{ 63 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 64 | \donttest{ 65 | \dontshow{ 66 | options("jlmerclusterperm.nthreads" = 2) 67 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 68 | julia_progress(show = FALSE) 69 | } 70 | 71 | library(dplyr, warn.conflicts = FALSE) 72 | 73 | # Specification object 74 | spec <- make_jlmer_spec( 75 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 76 | subject = "Chick", time = "Time" 77 | ) 78 | spec 79 | 80 | # Should minimally provide `threshold` and `nsim`, in addition to the spec object 81 | reset_rng_state() 82 | CPA <- clusterpermute(spec, threshold = 2, nsim = 100, progress = FALSE) 83 | CPA 84 | 85 | # CPA is a list of `` and `` objects 86 | sapply(CPA, class) 87 | 88 | # You can extract the individual components for further inspection 89 | CPA$null_cluster_dists 90 | CPA$empirical_clusters 91 | 92 | \dontshow{ 93 | JuliaConnectoR::stopJulia() 94 | } 95 | } 96 | \dontshow{\}) # examplesIf} 97 | } 98 | \seealso{ 99 | \code{\link[=compute_timewise_statistics]{compute_timewise_statistics()}}, \code{\link[=permute_timewise_statistics]{permute_timewise_statistics()}}, 100 | \code{\link[=extract_empirical_clusters]{extract_empirical_clusters()}}, \code{\link[=extract_null_cluster_dists]{extract_null_cluster_dists()}}, 101 | \code{\link[=calculate_clusters_pvalues]{calculate_clusters_pvalues()}} 102 | } 103 | -------------------------------------------------------------------------------- /man/compute_timewise_statistics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/compute_timewise_statistics.R 3 | \name{compute_timewise_statistics} 4 | \alias{compute_timewise_statistics} 5 | \title{Fit Julia regression models to each time point of a time series data} 6 | \usage{ 7 | compute_timewise_statistics( 8 | jlmer_spec, 9 | family = c("gaussian", "binomial"), 10 | statistic = c("t", "chisq"), 11 | ... 12 | ) 13 | } 14 | \arguments{ 15 | \item{jlmer_spec}{Data prepped for jlmer from \code{make_jlmer_spec()}} 16 | 17 | \item{family}{A GLM family. Currently supports "gaussian" and "binomial".} 18 | 19 | \item{statistic}{Test statistic for calculating cluster mass. 20 | Can be one of \code{"t"} (default) from the regression model output or 21 | \code{"chisq"} from a likelihood ratio test (takes about twice as long to calculate).} 22 | 23 | \item{...}{Optional arguments passed to Julia for model fitting. 24 | Defaults to \code{fast = TRUE} (when \code{family = "binomial"}) and \code{progress = FALSE}.} 25 | } 26 | \value{ 27 | A predictor-by-time matrix of cluster statistics, of class \code{timewise_statistics}. 28 | } 29 | \description{ 30 | Fit Julia regression models to each time point of a time series data 31 | } 32 | \examples{ 33 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 34 | \donttest{ 35 | \dontshow{ 36 | options("jlmerclusterperm.nthreads" = 2) 37 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 38 | julia_progress(show = FALSE) 39 | } 40 | 41 | library(dplyr, warn.conflicts = FALSE) 42 | 43 | # Specification object 44 | spec <- make_jlmer_spec( 45 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 46 | subject = "Chick", time = "Time" 47 | ) 48 | spec 49 | 50 | # Predictor x Time matrix of t-statistics from regression output 51 | empirical_statistics <- compute_timewise_statistics(spec) 52 | round(empirical_statistics, 2) 53 | 54 | # Collect as dataframe with `tidy()` 55 | empirical_statistics_df <- tidy(empirical_statistics) 56 | empirical_statistics_df 57 | 58 | # Timewise statistics are from regression models fitted to each time point 59 | # - Note the identical statistics at `Time == 0` 60 | empirical_statistics_df \%>\% 61 | filter(time == 0) 62 | to_jlmer(weight ~ 1 + Diet, filter(ChickWeight, Time == 0)) \%>\% 63 | tidy() \%>\% 64 | select(term, statistic) 65 | 66 | \dontshow{ 67 | JuliaConnectoR::stopJulia() 68 | } 69 | } 70 | \dontshow{\}) # examplesIf} 71 | } 72 | \seealso{ 73 | \code{\link[=jlmer]{jlmer()}}, \code{\link[=make_jlmer_spec]{make_jlmer_spec()}} 74 | } 75 | -------------------------------------------------------------------------------- /man/extract_empirical_clusters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract_clusters.R 3 | \name{extract_empirical_clusters} 4 | \alias{extract_empirical_clusters} 5 | \title{Detect largest clusters from a time sequence of predictor statistics} 6 | \usage{ 7 | extract_empirical_clusters( 8 | empirical_statistics, 9 | threshold, 10 | binned = FALSE, 11 | top_n = Inf 12 | ) 13 | } 14 | \arguments{ 15 | \item{empirical_statistics}{A predictor-by-time matrix of empirical timewise statistics.} 16 | 17 | \item{threshold}{The threshold value that the statistic must pass to contribute to cluster mass. 18 | Interpretation differs on the choice of statistic (more below): 19 | \itemize{ 20 | \item If \code{statistic = "t"}, the threshold for t-value (beta/std.err) from the regression model. 21 | \item If \code{statistic = "chisq"}, the threshold for the p-value of chi-squared statistics from likelihood ratio tests. 22 | }} 23 | 24 | \item{binned}{Whether the data has been aggregated/collapsed into time bins. Defaults to \code{FALSE}, 25 | which requires a cluster to span at least two time points. If \code{TRUE}, allows length-1 clusters to exist.} 26 | 27 | \item{top_n}{How many clusters to return, in the order of the size of the cluster-mass statistic. 28 | Defaults to \code{Inf} which return all detected clusters.} 29 | } 30 | \value{ 31 | An \code{empirical_clusters} object. 32 | } 33 | \description{ 34 | Detect largest clusters from a time sequence of predictor statistics 35 | } 36 | \examples{ 37 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 38 | \donttest{ 39 | \dontshow{ 40 | options("jlmerclusterperm.nthreads" = 2) 41 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 42 | julia_progress(show = FALSE) 43 | } 44 | 45 | library(dplyr, warn.conflicts = FALSE) 46 | 47 | # Specification object 48 | spec <- make_jlmer_spec( 49 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 50 | subject = "Chick", time = "Time" 51 | ) 52 | spec 53 | 54 | # Empirical clusters are derived from the timewise statistics 55 | empirical_statistics <- compute_timewise_statistics(spec) 56 | empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold = 2) 57 | empirical_clusters 58 | 59 | # Collect as dataframe with `tidy()` 60 | empirical_clusters_df <- tidy(empirical_clusters) 61 | empirical_clusters_df 62 | 63 | # Changing the `threshold` value identifies different clusters 64 | extract_empirical_clusters(empirical_statistics, threshold = 1) 65 | 66 | # A predictor can have zero or multiple clusters associated with it 67 | extract_empirical_clusters(empirical_statistics, threshold = 3) 68 | 69 | \dontshow{ 70 | JuliaConnectoR::stopJulia() 71 | } 72 | } 73 | \dontshow{\}) # examplesIf} 74 | } 75 | \seealso{ 76 | \code{\link[=compute_timewise_statistics]{compute_timewise_statistics()}} 77 | } 78 | -------------------------------------------------------------------------------- /man/extract_null_cluster_dists.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract_clusters.R 3 | \name{extract_null_cluster_dists} 4 | \alias{extract_null_cluster_dists} 5 | \title{Construct a null distribution of cluster-mass statistics} 6 | \usage{ 7 | extract_null_cluster_dists(null_statistics, threshold, binned = FALSE) 8 | } 9 | \arguments{ 10 | \item{null_statistics}{A simulation-by-time-by-predictor 3D array of null (permuted) timewise statistics.} 11 | 12 | \item{threshold}{The threshold value that the statistic must pass to contribute to cluster mass. 13 | Interpretation differs on the choice of statistic (more below): 14 | \itemize{ 15 | \item If \code{statistic = "t"}, the threshold for t-value (beta/std.err) from the regression model. 16 | \item If \code{statistic = "chisq"}, the threshold for the p-value of chi-squared statistics from likelihood ratio tests. 17 | }} 18 | 19 | \item{binned}{Whether the data has been aggregated/collapsed into time bins. Defaults to \code{FALSE}, 20 | which requires a cluster to span at least two time points. If \code{TRUE}, allows length-1 clusters to exist.} 21 | } 22 | \value{ 23 | A \code{null_cluster_dists} object. 24 | } 25 | \description{ 26 | Construct a null distribution of cluster-mass statistics 27 | } 28 | \examples{ 29 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 30 | \donttest{ 31 | \dontshow{ 32 | options("jlmerclusterperm.nthreads" = 2) 33 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 34 | julia_progress(show = FALSE) 35 | } 36 | 37 | library(dplyr, warn.conflicts = FALSE) 38 | 39 | # Specification object 40 | spec <- make_jlmer_spec( 41 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 42 | subject = "Chick", time = "Time" 43 | ) 44 | spec 45 | 46 | # Null cluster-mass distributions are derived from the permuted timewise statistics 47 | reset_rng_state() 48 | null_statistics <- permute_timewise_statistics(spec, nsim = 100) 49 | null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold = 2) 50 | null_cluster_dists 51 | 52 | # Collect as dataframe with `tidy()` 53 | # - Each simulation contributes one (largest) cluster-mass statistic to the null 54 | # - When no clusters are found, the `sum_statistic` value is zero 55 | null_cluster_dists_df <- tidy(null_cluster_dists) 56 | null_cluster_dists_df 57 | 58 | # Changing the `threshold` value changes the shape of the null 59 | extract_null_cluster_dists(null_statistics, threshold = 1) 60 | extract_null_cluster_dists(null_statistics, threshold = 3) 61 | 62 | \dontshow{ 63 | JuliaConnectoR::stopJulia() 64 | } 65 | } 66 | \dontshow{\}) # examplesIf} 67 | } 68 | \seealso{ 69 | \code{\link[=permute_timewise_statistics]{permute_timewise_statistics()}} 70 | } 71 | -------------------------------------------------------------------------------- /man/figures/README-/calculate_clusters_pvalues-dark.svg: -------------------------------------------------------------------------------- 1 | ──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.121(p=0.0495)Diet3[3,12]:35.769(p=0.0099)Diet4[2,8]:32.442(p=0.0099)──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/calculate_clusters_pvalues.svg: -------------------------------------------------------------------------------- 1 | ──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.121(p=0.0495)Diet3[3,12]:35.769(p=0.0099)Diet4[2,8]:32.442(p=0.0099)──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/empirical_clusters-dark.svg: -------------------------------------------------------------------------------- 1 | ──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.121Diet3[3,12]:35.769Diet4[2,8]:32.442──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/empirical_clusters.svg: -------------------------------------------------------------------------------- 1 | ──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.121Diet3[3,12]:35.769Diet4[2,8]:32.442──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/null_statistics-dark.svg: -------------------------------------------------------------------------------- 1 | ──Nullcluster-massdistribution(t>2.5)────────────<null_cluster_dists>──Diet2(n=100)Mean(SD):-0.039(1.89)Coverageintervals:95%[-2.862,0.000]Diet3(n=100)Mean(SD):-0.129(2.02)Coverageintervals:95%[0.000,0.000]Diet4(n=100)Mean(SD):0.296(3.21)Coverageintervals:95%[0.000,5.797]──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/null_statistics.svg: -------------------------------------------------------------------------------- 1 | ──Nullcluster-massdistribution(t>2.5)────────────<null_cluster_dists>──Diet2(n=100)Mean(SD):-0.039(1.89)Coverageintervals:95%[-2.862,0.000]Diet3(n=100)Mean(SD):-0.129(2.02)Coverageintervals:95%[0.000,0.000]Diet4(n=100)Mean(SD):0.296(3.21)Coverageintervals:95%[0.000,5.797]──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/reCPA-io-dark.svg: -------------------------------------------------------------------------------- 1 | Detectingempiricalclustersandcalculatingcluster-massstatistics.[7.1s]Progress:100%|████████████████████████████████████|Time:0:00:00(9.66ms/it)Samplingcluster-massstatisticsfromabootstrappednulldistribution.[1.3sCalculatingtheprobabilityoftheobservedcluster-massstatistics.[27ms]──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.387(p=0.0594)Diet3[2,12]:39.919(p=0.0099)Diet4[2,8]:33.853(p=0.0099)──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/reCPA-io.svg: -------------------------------------------------------------------------------- 1 | Detectingempiricalclustersandcalculatingcluster-massstatistics.[7.1s]Progress:100%|████████████████████████████████████|Time:0:00:00(9.66ms/it)Samplingcluster-massstatisticsfromabootstrappednulldistribution.[1.3sCalculatingtheprobabilityoftheobservedcluster-massstatistics.[27ms]──Empiricalclusters(t>2.5)────────────────────────<empirical_clusters>──Diet2[3,4]:6.387(p=0.0594)Diet3[2,12]:39.919(p=0.0099)Diet4[2,8]:33.853(p=0.0099)──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/setup-io-dark.svg: -------------------------------------------------------------------------------- 1 | StartingJuliawith7threads[4.5s]Activatingpackageenvironment[6.8s]Runningpackagesetupscripts(11/11)[10.2s] -------------------------------------------------------------------------------- /man/figures/README-/setup-io.svg: -------------------------------------------------------------------------------- 1 | StartingJuliawith7threads[4.5s]Activatingpackageenvironment[6.8s]Runningpackagesetupscripts(11/11)[10.2s] -------------------------------------------------------------------------------- /man/figures/README-/spec-io-dark.svg: -------------------------------------------------------------------------------- 1 | ──jlmerspecification─────────────────────────────────────────<jlmer_spec>──Formula:weight~1+Diet2+Diet3+Diet4Predictors:Diet:Diet2,Diet3,Diet4Groupings:Subject:ChickTrial:Time:TimeData:weightDiet2Diet3Diet4ChickTime142000112510001235900013[reached'max'/getOption("max.print")--omitted575rows]──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/spec-io.svg: -------------------------------------------------------------------------------- 1 | ──jlmerspecification─────────────────────────────────────────<jlmer_spec>──Formula:weight~1+Diet2+Diet3+Diet4Predictors:Diet:Diet2,Diet3,Diet4Groupings:Subject:ChickTrial:Time:TimeData:weightDiet2Diet3Diet4ChickTime142000112510001235900013[reached'max'/getOption("max.print")--omitted575rows]──────────────────────────────────────────────────────────────────────────────── -------------------------------------------------------------------------------- /man/figures/README-/walk_threshold_steps-dark.svg: -------------------------------------------------------------------------------- 1 | thresholdpredictoridstartendlengthsum_statisticpvalue12.0Diet213538.4963760.0792079222.0Diet312121138.2160350.0099009932.0Diet412121141.6514680.0099009942.5Diet213426.1211410.0495049552.5Diet313121035.7689570.0099009962.5Diet4128732.4423520.00990099313.0Diet3135312.7192310.00990099213.0Diet32912414.0376220.00990099413.0Diet4127629.6594020.00990099 -------------------------------------------------------------------------------- /man/figures/README-/walk_threshold_steps.svg: -------------------------------------------------------------------------------- 1 | thresholdpredictoridstartendlengthsum_statisticpvalue12.0Diet213538.4963760.0792079222.0Diet312121138.2160350.0099009932.0Diet412121141.6514680.0099009942.5Diet213426.1211410.0495049552.5Diet313121035.7689570.0099009962.5Diet4128732.4423520.00990099313.0Diet3135312.7192310.00990099213.0Diet32912414.0376220.00990099413.0Diet4127629.6594020.00990099 -------------------------------------------------------------------------------- /man/figures/README-chickweight-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/README-chickweight-1.png -------------------------------------------------------------------------------- /man/figures/README-empirical_statistics-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/README-empirical_statistics-1.png -------------------------------------------------------------------------------- /man/figures/clusterpermute_animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/clusterpermute_animation.gif -------------------------------------------------------------------------------- /man/figures/clusterpermute_slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/clusterpermute_slice.png -------------------------------------------------------------------------------- /man/figures/deCarvalho_fig7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/deCarvalho_fig7.jpg -------------------------------------------------------------------------------- /man/figures/desktop.ini: -------------------------------------------------------------------------------- 1 | [LocalizedFileNames] 2 | clusterperm_anim.gif=@clusterperm_anim,0 3 | clusterperm_slice.png=@clusterperm_slice,0 4 | -------------------------------------------------------------------------------- /man/figures/jlmerclusterperm_fn_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/jlmerclusterperm_fn_design.png -------------------------------------------------------------------------------- /man/figures/jlmerclusterperm_logo_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/jlmerclusterperm_logo_plot.png -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/logo.png -------------------------------------------------------------------------------- /man/figures/logo_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/man/figures/logo_highres.png -------------------------------------------------------------------------------- /man/jlmer.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/jlmer.R 3 | \name{jlmer} 4 | \alias{jlmer} 5 | \title{Fit a Julia regression model using jlmer specifications} 6 | \usage{ 7 | jlmer(jlmer_spec, family = c("gaussian", "binomial"), ..., progress = FALSE) 8 | } 9 | \arguments{ 10 | \item{jlmer_spec}{Data prepped for jlmer from \code{make_jlmer_spec()}} 11 | 12 | \item{family}{A GLM family. Currently supports "gaussian" and "binomial".} 13 | 14 | \item{...}{Optional arguments passed to Julia for model fitting.} 15 | 16 | \item{progress}{If \code{TRUE}, prints the timing of iterations.} 17 | } 18 | \value{ 19 | A \code{jlmer_mod} object. 20 | } 21 | \description{ 22 | Fit a Julia regression model using jlmer specifications 23 | } 24 | \examples{ 25 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 26 | \donttest{ 27 | \dontshow{ 28 | options("jlmerclusterperm.nthreads" = 2) 29 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 30 | julia_progress(show = FALSE) 31 | } 32 | 33 | # Fitting a regression model with a specification object 34 | spec <- make_jlmer_spec(weight ~ 1 + Diet, ChickWeight) 35 | jlmer(spec) 36 | 37 | # `lm()` equivalent 38 | summary(lm(weight ~ 1 + Diet, ChickWeight))$coef 39 | 40 | \dontshow{ 41 | JuliaConnectoR::stopJulia() 42 | } 43 | } 44 | \dontshow{\}) # examplesIf} 45 | } 46 | \seealso{ 47 | \code{\link[=make_jlmer_spec]{make_jlmer_spec()}} 48 | } 49 | -------------------------------------------------------------------------------- /man/jlmerclusterperm-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/jlmerclusterperm-package.R 3 | \docType{package} 4 | \name{jlmerclusterperm-package} 5 | \alias{jlmerclusterperm} 6 | \alias{jlmerclusterperm-package} 7 | \title{jlmerclusterperm: Cluster-Based Permutation Analysis for Densely Sampled Time Data} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | An implementation of fast cluster-based permutation analysis (CPA) for densely-sampled time data developed in Maris & Oostenveld, 2007 \doi{10.1016/j.jneumeth.2007.03.024}. Supports (generalized, mixed-effects) regression models for the calculation of timewise statistics. Provides both a wholesale and a piecemeal interface to the CPA procedure with an emphasis on interpretability and diagnostics. Integrates 'Julia' libraries 'MixedModels.jl' and 'GLM.jl' for performance improvements, with additional functionalities for interfacing with 'Julia' from 'R' powered by the 'JuliaConnectoR' package. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://github.com/yjunechoe/jlmerclusterperm} 17 | \item \url{https://yjunechoe.github.io/jlmerclusterperm/} 18 | \item Report bugs at \url{https://github.com/yjunechoe/jlmerclusterperm/issues} 19 | } 20 | 21 | } 22 | \author{ 23 | \strong{Maintainer}: June Choe \email{jchoe001@gmail.com} (\href{https://orcid.org/0000-0002-0701-921X}{ORCID}) [copyright holder] 24 | 25 | } 26 | \keyword{internal} 27 | -------------------------------------------------------------------------------- /man/jlmerclusterperm_setup.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa.R 3 | \name{jlmerclusterperm_setup} 4 | \alias{jlmerclusterperm_setup} 5 | \title{Initial setup for the jlmerclusterperm package} 6 | \usage{ 7 | jlmerclusterperm_setup(..., cache_dir = NULL, restart = TRUE, verbose = TRUE) 8 | } 9 | \arguments{ 10 | \item{...}{Ignored.} 11 | 12 | \item{cache_dir}{The location to write out package cache files (namely, Manifest.toml). 13 | If \code{NULL} (default), attempts to write to the package's cache directory discovered via 14 | \code{R_user_dir()} and falls back to \code{tempdir()}.} 15 | 16 | \item{restart}{Whether to set up a fresh Julia session, given that one is already running. 17 | If \code{FALSE} and \code{jlmerclusterperm_setup()} has already been called, nothing happens.} 18 | 19 | \item{verbose}{Whether to print progress and messages from Julia in the console} 20 | } 21 | \value{ 22 | TRUE 23 | } 24 | \description{ 25 | Initial setup for the jlmerclusterperm package 26 | } 27 | \examples{ 28 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 29 | \donttest{ 30 | options("jlmerclusterperm.nthreads" = 2) 31 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 32 | 33 | \dontshow{ 34 | JuliaConnectoR::stopJulia() 35 | } 36 | } 37 | \dontshow{\}) # examplesIf} 38 | } 39 | -------------------------------------------------------------------------------- /man/julia_model_tidiers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tidy.R 3 | \name{julia_model_tidiers} 4 | \alias{julia_model_tidiers} 5 | \alias{tidy.jlmer_mod} 6 | \alias{glance.jlmer_mod} 7 | \title{Tidier methods for Julia regression models} 8 | \usage{ 9 | \method{tidy}{jlmer_mod}(x, effects = c("var_model", "ran_pars", "fixed"), ...) 10 | 11 | \method{glance}{jlmer_mod}(x, ...) 12 | } 13 | \arguments{ 14 | \item{x}{An object of class \code{jlmer_mod}} 15 | 16 | \item{effects}{One of "var_model", "ran_pars", or "fixed"} 17 | 18 | \item{...}{Unused} 19 | } 20 | \value{ 21 | A data frame 22 | } 23 | \description{ 24 | Tidier methods for Julia regression models 25 | } 26 | \examples{ 27 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 28 | \donttest{ 29 | \dontshow{ 30 | options("jlmerclusterperm.nthreads" = 2) 31 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 32 | julia_progress(show = FALSE) 33 | } 34 | 35 | # Fixed-effects only model 36 | mod1 <- to_jlmer(weight ~ 1 + Diet, ChickWeight) 37 | tidy(mod1) 38 | glance(mod1) 39 | 40 | # Mixed model 41 | mod2 <- to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight) 42 | tidy(mod2) 43 | glance(mod2) 44 | 45 | # Select which of fixed/random effects to return 46 | tidy(mod2, effects = "fixed") 47 | tidy(mod2, effects = "ran_pars") 48 | 49 | \dontshow{ 50 | JuliaConnectoR::stopJulia() 51 | } 52 | } 53 | \dontshow{\}) # examplesIf} 54 | } 55 | -------------------------------------------------------------------------------- /man/julia_progress.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interop-utils.R 3 | \name{julia_progress} 4 | \alias{julia_progress} 5 | \title{Set/get options for Julia progress bar} 6 | \usage{ 7 | julia_progress(show, width) 8 | } 9 | \arguments{ 10 | \item{show}{Whether to show the progress bar. You may also pass in a list of \code{"show"} and \code{"width"}.} 11 | 12 | \item{width}{Width of the progress bar. If \code{"auto"}, adjusts the progress bar width to fit the console.} 13 | } 14 | \value{ 15 | Previous values for \code{show} and \code{width} 16 | } 17 | \description{ 18 | Set/get options for Julia progress bar 19 | } 20 | \examples{ 21 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 22 | \donttest{ 23 | \dontshow{ 24 | options("jlmerclusterperm.nthreads" = 2) 25 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 26 | } 27 | 28 | # Show current progress options 29 | julia_progress() 30 | 31 | # Set options and save previous options 32 | old_progress_opts <- julia_progress(show = FALSE, width = 100) 33 | julia_progress() 34 | 35 | # Restoring progress settings by passing a list of old options 36 | old_progress_opts 37 | julia_progress(old_progress_opts) 38 | identical(julia_progress(), old_progress_opts) 39 | 40 | # Alternatively, reset to default settings using this syntax: 41 | julia_progress(show = TRUE, width = "auto") 42 | 43 | \dontshow{ 44 | JuliaConnectoR::stopJulia() 45 | } 46 | } 47 | \dontshow{\}) # examplesIf} 48 | } 49 | -------------------------------------------------------------------------------- /man/julia_rng.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/julia_rng.R 3 | \name{julia_rng} 4 | \alias{julia_rng} 5 | \alias{set_rng_state} 6 | \alias{reset_rng_state} 7 | \alias{get_rng_state} 8 | \alias{set_rng_seed} 9 | \alias{get_rng_seed} 10 | \title{Interface to the Julia RNG} 11 | \usage{ 12 | set_rng_state(i) 13 | 14 | reset_rng_state() 15 | 16 | get_rng_state() 17 | 18 | set_rng_seed(seed) 19 | 20 | get_rng_seed() 21 | } 22 | \arguments{ 23 | \item{i}{Counter number} 24 | 25 | \item{seed}{Seed} 26 | } 27 | \value{ 28 | The current seed or counter 29 | } 30 | \description{ 31 | Interface to the Julia RNG 32 | } 33 | \examples{ 34 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 35 | \donttest{ 36 | \dontshow{ 37 | options("jlmerclusterperm.nthreads" = 2) 38 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 39 | julia_progress(show = FALSE) 40 | } 41 | 42 | # RNG initializes to seed=1 counter=0 43 | get_rng_seed() 44 | get_rng_state() 45 | 46 | # setter/getter for RNG counter 47 | set_rng_state(123) 48 | get_rng_state() 49 | 50 | # setter/getter for RNG seed 51 | set_rng_seed(2) 52 | get_rng_seed() 53 | 54 | # restore to initial setting (seed=1, counter=0) 55 | set_rng_seed(1) 56 | set_rng_state(0) 57 | 58 | \dontshow{ 59 | JuliaConnectoR::stopJulia() 60 | } 61 | } 62 | \dontshow{\}) # examplesIf} 63 | } 64 | -------------------------------------------------------------------------------- /man/julia_setup_ok.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa.R 3 | \name{julia_setup_ok} 4 | \alias{julia_setup_ok} 5 | \title{Check Julia requirements for jlmerclusterperm} 6 | \usage{ 7 | julia_setup_ok() 8 | } 9 | \value{ 10 | Boolean 11 | } 12 | \description{ 13 | Check Julia requirements for jlmerclusterperm 14 | } 15 | \examples{ 16 | julia_setup_ok() 17 | } 18 | -------------------------------------------------------------------------------- /man/make_jlmer_spec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/jlmer_spec.R 3 | \name{make_jlmer_spec} 4 | \alias{make_jlmer_spec} 5 | \title{Create a specifications object for fitting regression models in Julia} 6 | \usage{ 7 | make_jlmer_spec( 8 | formula, 9 | data, 10 | subject = NULL, 11 | trial = NULL, 12 | time = NULL, 13 | drop_terms = NULL, 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{formula}{Model formula in R syntax} 19 | 20 | \item{data}{A data frame} 21 | 22 | \item{subject}{Column for subjects in the data.} 23 | 24 | \item{trial}{Column for trials in the data. Must uniquely identify a time series within subject 25 | (for example, the column for items in a counterbalanced design where each subject sees exactly one item).} 26 | 27 | \item{time}{Column for time in the data.} 28 | 29 | \item{drop_terms}{(Optional) any terms to drop from the reconstructed model formula} 30 | 31 | \item{...}{Unused, for extensibility.} 32 | } 33 | \value{ 34 | An object of class \code{jlmer_spec}. 35 | } 36 | \description{ 37 | Create a specifications object for fitting regression models in Julia 38 | } 39 | \examples{ 40 | # Bare specification object (minimal spec for fitting a global model) 41 | spec <- make_jlmer_spec(weight ~ 1 + Diet, ChickWeight) 42 | spec 43 | 44 | # Constraints on specification for CPA: 45 | # 1) The combination of `subject`, `trial`, and `time` must uniquely identify rows in the data 46 | # 2) `time` must have constant sampling rate (i.e., evenly spaced values) 47 | spec_wrong <- make_jlmer_spec( 48 | weight ~ 1 + Diet, ChickWeight, 49 | time = "Time" 50 | ) 51 | unique(ChickWeight$Time) 52 | 53 | # Corrected specification for the above 54 | spec_correct <- make_jlmer_spec( 55 | weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 56 | subject = "Chick", time = "Time" 57 | ) 58 | spec_correct 59 | 60 | } 61 | -------------------------------------------------------------------------------- /man/permute_by_predictor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/permute.R 3 | \name{permute_by_predictor} 4 | \alias{permute_by_predictor} 5 | \title{Permute data while respecting grouping structure(s) of observations} 6 | \usage{ 7 | permute_by_predictor( 8 | jlmer_spec, 9 | predictors, 10 | predictor_type = c("guess", "between_participant", "within_participant"), 11 | n = 1L 12 | ) 13 | } 14 | \arguments{ 15 | \item{jlmer_spec}{Data prepped for jlmer from \code{make_jlmer_spec()}} 16 | 17 | \item{predictors}{A vector of terms from the model. If multiple, the must form the levels of one predictor.} 18 | 19 | \item{predictor_type}{Whether the predictor is \code{"between_participant"} or \code{"within_participant"}. Defaults to \code{"guess"}.} 20 | 21 | \item{n}{Number of permuted samples of the data to generate. Defaults to \code{1L}.} 22 | } 23 | \value{ 24 | A long dataframe of permuted re-samples with \code{.id} column representing replication IDs. 25 | } 26 | \description{ 27 | Permute data while respecting grouping structure(s) of observations 28 | } 29 | \examples{ 30 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 31 | \donttest{ 32 | \dontshow{ 33 | options("jlmerclusterperm.nthreads" = 2) 34 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 35 | julia_progress(show = FALSE) 36 | } 37 | 38 | # Example data setup 39 | chickweights_df <- ChickWeight 40 | chickweights_df <- chickweights_df[chickweights_df$Time <= 20, ] 41 | chickweights_df$DietInt <- as.integer(chickweights_df$Diet) 42 | head(chickweights_df) 43 | 44 | # Example 1: Spec object using the continuous `DietInt` predictor 45 | chickweights_spec1 <- make_jlmer_spec( 46 | formula = weight ~ 1 + DietInt, 47 | data = chickweights_df, 48 | subject = "Chick", time = "Time" 49 | ) 50 | chickweights_spec1 51 | 52 | # Shuffling `DietInt` values guesses `predictor_type = "between_participant"` 53 | reset_rng_state() 54 | spec1_perm1 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt") 55 | # This calls the same shuffling algorithm for CPA in Julia, so counter is incremented 56 | get_rng_state() 57 | 58 | # Shuffling under shared RNG state reproduces the same permutation of the data 59 | reset_rng_state() 60 | spec1_perm2 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt") 61 | identical(spec1_perm1, spec1_perm2) 62 | 63 | # Example 2: Spec object using the multilevel `Diet` predictor 64 | chickweights_spec2 <- make_jlmer_spec( 65 | formula = weight ~ 1 + Diet, 66 | data = chickweights_df, 67 | subject = "Chick", time = "Time" 68 | ) 69 | chickweights_spec2 70 | 71 | # Levels of a category are automatically shuffled together 72 | reset_rng_state() 73 | spec2_perm1 <- permute_by_predictor(chickweights_spec2, predictors = "Diet2") 74 | reset_rng_state() 75 | spec2_perm2 <- permute_by_predictor(chickweights_spec2, predictors = c("Diet2", "Diet3", "Diet4")) 76 | identical(spec2_perm1, spec2_perm2) 77 | 78 | \dontshow{ 79 | JuliaConnectoR::stopJulia() 80 | } 81 | } 82 | \dontshow{\}) # examplesIf} 83 | } 84 | -------------------------------------------------------------------------------- /man/permute_timewise_statistics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/permute_timewise_statistics.R 3 | \name{permute_timewise_statistics} 4 | \alias{permute_timewise_statistics} 5 | \title{Simulate cluster-mass statistics via bootstrapped permutations} 6 | \usage{ 7 | permute_timewise_statistics( 8 | jlmer_spec, 9 | family = c("gaussian", "binomial"), 10 | statistic = c("t", "chisq"), 11 | nsim = 100L, 12 | predictors = NULL, 13 | ... 14 | ) 15 | } 16 | \arguments{ 17 | \item{jlmer_spec}{Data prepped for jlmer from \code{make_jlmer_spec()}} 18 | 19 | \item{family}{A GLM family. Currently supports "gaussian" and "binomial".} 20 | 21 | \item{statistic}{Test statistic for calculating cluster mass. 22 | Can be one of \code{"t"} (default) from the regression model output or 23 | \code{"chisq"} from a likelihood ratio test (takes about twice as long to calculate).} 24 | 25 | \item{nsim}{Number of simulations description} 26 | 27 | \item{predictors}{(Optional) a subset of predictors to test. Defaults to \code{NULL} which tests all predictors.} 28 | 29 | \item{...}{Optional arguments passed to Julia for model fitting. 30 | Defaults to \code{fast = TRUE} (when \code{family = "binomial"}) and \code{progress = FALSE}.} 31 | } 32 | \value{ 33 | A simulation-by-time-by-predictor 3D array of cluster statistics, of class \code{timewise_statistics}. 34 | } 35 | \description{ 36 | Simulate cluster-mass statistics via bootstrapped permutations 37 | } 38 | \examples{ 39 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 40 | \donttest{ 41 | \dontshow{ 42 | options("jlmerclusterperm.nthreads" = 2) 43 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 44 | julia_progress(show = FALSE) 45 | } 46 | 47 | library(dplyr, warn.conflicts = FALSE) 48 | 49 | # Specification object 50 | spec <- make_jlmer_spec( 51 | weight ~ 1 + Diet, filter(ChickWeight, Time <= 20), 52 | subject = "Chick", time = "Time" 53 | ) 54 | spec 55 | 56 | # Simulation x Time x Predictor array of t-statistics from regression output 57 | reset_rng_state() 58 | null_statistics <- permute_timewise_statistics(spec, nsim = 3) 59 | round(null_statistics, 2) 60 | 61 | # Collect as dataframe with `tidy()` 62 | permuted_timewise_stats_df <- tidy(null_statistics) 63 | permuted_timewise_stats_df 64 | 65 | # Permutations ran under the same RNG state are identical 66 | reset_rng_state() 67 | null_statistics2 <- permute_timewise_statistics(spec, nsim = 3) 68 | identical(null_statistics, null_statistics2) 69 | 70 | get_rng_state() 71 | null_statistics3 <- permute_timewise_statistics(spec, nsim = 3) 72 | identical(null_statistics, null_statistics3) 73 | 74 | \dontshow{ 75 | JuliaConnectoR::stopJulia() 76 | } 77 | } 78 | \dontshow{\}) # examplesIf} 79 | } 80 | \seealso{ 81 | \code{\link[=make_jlmer_spec]{make_jlmer_spec()}} 82 | } 83 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interop-utils.R, R/tidy.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{stopJulia} 7 | \alias{tidy} 8 | \alias{glance} 9 | \title{Objects exported from other packages} 10 | \keyword{internal} 11 | \description{ 12 | These objects are imported from other packages. Follow the links 13 | below to see their documentation. 14 | 15 | \describe{ 16 | \item{generics}{\code{\link[generics]{glance}}, \code{\link[generics]{tidy}}} 17 | 18 | \item{JuliaConnectoR}{\code{\link[JuliaConnectoR]{stopJulia}}} 19 | }} 20 | 21 | -------------------------------------------------------------------------------- /man/to_jlmer.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/jlmer.R 3 | \name{to_jlmer} 4 | \alias{to_jlmer} 5 | \title{Fit a Julia regression model using lme4 syntax} 6 | \usage{ 7 | to_jlmer( 8 | formula, 9 | data, 10 | family = c("gaussian", "binomial"), 11 | jlmer_spec_opts = list(), 12 | ..., 13 | progress = FALSE 14 | ) 15 | } 16 | \arguments{ 17 | \item{formula}{Model formula in R syntax} 18 | 19 | \item{data}{A data frame} 20 | 21 | \item{family}{A GLM family. Currently supports "gaussian" and "binomial".} 22 | 23 | \item{jlmer_spec_opts}{List of options passed to \code{make_jlmer_spec()}} 24 | 25 | \item{...}{Optional arguments passed to Julia for model fitting.} 26 | 27 | \item{progress}{If \code{TRUE}, prints the timing of iterations.} 28 | } 29 | \value{ 30 | A \code{jlmer_mod} object. 31 | } 32 | \description{ 33 | Fit a Julia regression model using lme4 syntax 34 | } 35 | \examples{ 36 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 37 | \donttest{ 38 | \dontshow{ 39 | options("jlmerclusterperm.nthreads" = 2) 40 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 41 | julia_progress(show = FALSE) 42 | } 43 | 44 | # Fitting a regression model with R formula syntax 45 | to_jlmer(weight ~ 1 + Diet, ChickWeight) 46 | 47 | # `lm()` equivalent 48 | summary(lm(weight ~ 1 + Diet, ChickWeight))$coef 49 | 50 | # Fitting a mixed model with {lme4} syntax 51 | to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight) 52 | 53 | # Passing MixedModels.jl fit options to the `...` 54 | to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight, REML = TRUE) 55 | 56 | \dontshow{ 57 | JuliaConnectoR::stopJulia() 58 | } 59 | } 60 | \dontshow{\}) # examplesIf} 61 | } 62 | \seealso{ 63 | \code{\link[=jlmer]{jlmer()}}, \code{\link[=make_jlmer_spec]{make_jlmer_spec()}} 64 | } 65 | -------------------------------------------------------------------------------- /man/walk_threshold_steps.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/threshold_search.R 3 | \name{walk_threshold_steps} 4 | \alias{walk_threshold_steps} 5 | \title{Test the probability of cluster-mass statistics over a range of threshold values} 6 | \usage{ 7 | walk_threshold_steps( 8 | empirical_statistics, 9 | null_statistics, 10 | steps, 11 | top_n = Inf, 12 | binned = FALSE, 13 | add1 = TRUE, 14 | progress = TRUE 15 | ) 16 | } 17 | \arguments{ 18 | \item{empirical_statistics}{A predictor-by-time matrix of empirical timewise statistics.} 19 | 20 | \item{null_statistics}{A simulation-by-time-by-predictor 3D array of null (permuted) timewise statistics.} 21 | 22 | \item{steps}{A vector of threshold values to test} 23 | 24 | \item{top_n}{How many clusters to return, in the order of the size of the cluster-mass statistic. 25 | Defaults to \code{Inf} which return all detected clusters.} 26 | 27 | \item{binned}{Whether the data has been aggregated/collapsed into time bins. Defaults to \code{FALSE}, 28 | which requires a cluster to span at least two time points. If \code{TRUE}, allows length-1 clusters to exist.} 29 | 30 | \item{add1}{Whether to add 1 to the numerator and denominator when calculating the p-value. 31 | Use \code{TRUE} to effectively count the observed statistic as part of the permuted 32 | null distribution (recommended with larger \code{nsim} prior to publishing results).} 33 | 34 | \item{progress}{Whether to display a progress bar} 35 | } 36 | \value{ 37 | A data frame of predictor clusters-mass statistics by threshold. 38 | } 39 | \description{ 40 | Test the probability of cluster-mass statistics over a range of threshold values 41 | } 42 | \examples{ 43 | \dontshow{if (julia_setup_ok()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 44 | \donttest{ 45 | \dontshow{ 46 | options("jlmerclusterperm.nthreads" = 2) 47 | jlmerclusterperm_setup(cache_dir = tempdir(), verbose = FALSE) 48 | julia_progress(show = FALSE) 49 | } 50 | 51 | # Specification object 52 | spec <- make_jlmer_spec( 53 | weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 54 | subject = "Chick", time = "Time" 55 | ) 56 | spec 57 | 58 | # Compute timewise statistics for the observed and permuted data 59 | empirical_statistics <- compute_timewise_statistics(spec) 60 | null_statistics <- permute_timewise_statistics(spec, nsim = 100) 61 | 62 | # Test cluster mass/probability under different threshold values 63 | walk_threshold_steps(empirical_statistics, null_statistics, steps = 1:3, 64 | progress = FALSE) 65 | 66 | \dontshow{ 67 | JuliaConnectoR::stopJulia() 68 | } 69 | } 70 | \dontshow{\}) # examplesIf} 71 | } 72 | -------------------------------------------------------------------------------- /pkgdown/extra.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | padding-top: 0.7rem; 3 | padding-bottom: 0.7rem; 4 | } 5 | 6 | .template-home .page-header { 7 | min-height: auto; 8 | } 9 | 10 | .row > main, .row > aside { 11 | margin-top: 75px; 12 | } 13 | 14 | h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { 15 | margin-top: 0.5rem; 16 | margin-bottom: 1rem; 17 | } 18 | 19 | .section > h2 { 20 | border-bottom: 3px solid #cdcdcd; 21 | padding-bottom: 5px; 22 | margin-top: 1.5rem; 23 | margin-bottom: 1.5rem; 24 | } 25 | 26 | .section > h3 { 27 | margin-top: 2rem; 28 | margin-bottom: 1rem; 29 | border-left: 5px solid #4385c7; 30 | padding-left: 10px; 31 | } 32 | 33 | #toc > .nav .nav-link .active { 34 | background-color: #d1e7f5; 35 | } 36 | 37 | .nav-text.text-muted { 38 | color: white !important; 39 | } 40 | 41 | .navbar-brand { 42 | font-size: 1.5rem; 43 | } 44 | 45 | a { 46 | color: #009680; 47 | } 48 | 49 | pre { 50 | padding: 0.5rem 0.5rem; 51 | } 52 | 53 | details { 54 | background-color: #008bff0f; 55 | outline: 2px solid #38566F; 56 | padding: 1rem 1rem 0.5rem 1rem; 57 | } 58 | 59 | .caption { 60 | text-align: center; 61 | color: grey; 62 | font-size: .9em; 63 | } 64 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/tests.html 7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files 8 | 9 | library(testthat) 10 | library(jlmerclusterperm) 11 | 12 | test_check("jlmerclusterperm") 13 | -------------------------------------------------------------------------------- /tests/testthat/helper-skip.R: -------------------------------------------------------------------------------- 1 | no_julia <- !JuliaConnectoR::juliaSetupOk() 2 | skip_conditionally <- function() { 3 | # skip_on_cran() 4 | if (no_julia) { 5 | testthat::skip("No Julia installation detected.") 6 | } 7 | if (!julia_version_compatible()) { 8 | testthat::skip("Julia version >=1.8 required.") 9 | } 10 | invisible() 11 | } 12 | -------------------------------------------------------------------------------- /tests/testthat/test-aaa-a-logging.R: -------------------------------------------------------------------------------- 1 | skip_conditionally() 2 | 3 | JuliaConnectoR::stopJulia() 4 | julia_cmd <- asNamespace("JuliaConnectoR")$getJuliaExecutablePath() 5 | system2(julia_cmd, '-e "using InteractiveUtils; println(versioninfo());"') 6 | 7 | jlmerclusterperm:::start_with_threads(, max_threads = 2, verbose = TRUE) 8 | jlmerclusterperm:::set_projenv(cache_dir = tempdir(), verbose = TRUE) 9 | cat(JuliaConnectoR::juliaCall("Pkg.status")) 10 | cat(readLines(file.path(jlmerclusterperm:::.jlmerclusterperm$opts$projdir, "Manifest.toml")), sep = "\n") 11 | -------------------------------------------------------------------------------- /tests/testthat/test-aaa-b-setup.R: -------------------------------------------------------------------------------- 1 | skip_conditionally() 2 | 3 | #' @srrstats {G5.2} Appropriate message/warning/error tests are in `/tests/testthat` 4 | 5 | test_that("Setup with seed works (use 2 for testing)", { 6 | options("jlmerclusterperm.nthreads" = 2) 7 | expect_true(jlmerclusterperm_setup(cache_dir = tempdir(), restart = TRUE)) 8 | expect_true(file.exists(file.path(tempdir(), "julia", "Manifest.toml"))) 9 | expect_equal(JuliaConnectoR::juliaEval("Threads.nthreads()"), 2) 10 | }) 11 | 12 | test_that("RNG initializes to seed=1 counter=0", { 13 | expect_equal(get_rng_seed(), 1) 14 | expect_equal(get_rng_state(), 0) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/testthat/test-clusterpermute.R: -------------------------------------------------------------------------------- 1 | skip_conditionally() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | #' @srrstats {G5.0} Uses the built-in `ChickWeight` dataset for tests 6 | spec <- make_jlmer_spec( 7 | weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 8 | subject = "Chick", time = "Time" 9 | ) 10 | 11 | #' @srrstats {G5.5} Uses shared Julia RNG state to test correctness 12 | #' @srrstats {G5.9} Tests for stochastic nature of the CPA under different RNG states 13 | test_that("CPAs under the same RNG state are identical", { 14 | reset_rng_state() 15 | a <- clusterpermute(spec, threshold = 2, progress = FALSE) 16 | reset_rng_state() 17 | b <- clusterpermute(spec, threshold = 2, progress = FALSE) 18 | expect_equal(a, b) 19 | }) 20 | 21 | # piecemeal vs. wholesale 22 | reset_rng_state() 23 | wholesale <- clusterpermute(spec, threshold = 2, progress = FALSE) 24 | reset_rng_state() 25 | empirical_statistics <- compute_timewise_statistics(spec) 26 | empirical_clusters <- extract_empirical_clusters(empirical_statistics, threshold = 2) 27 | null_statistics <- permute_timewise_statistics(spec) 28 | null_cluster_dists <- extract_null_cluster_dists(null_statistics, threshold = 2) 29 | empirical_clusters_tested <- calculate_clusters_pvalues(empirical_clusters, null_cluster_dists, add1 = TRUE) 30 | reset_rng_state() 31 | 32 | test_that("Piecemeal and wholesale CPAs are identical", { 33 | expect_equal(wholesale$null_cluster_dists, null_cluster_dists) 34 | expect_equal(tidy(wholesale$null_cluster_dists), tidy(null_cluster_dists)) 35 | expect_equal(wholesale$empirical_clusters, empirical_clusters_tested) 36 | expect_equal(tidy(wholesale$empirical_clusters), tidy(empirical_clusters_tested)) 37 | }) 38 | 39 | test_that("Errors on incompatible clusters", { 40 | expect_error(calculate_clusters_pvalues(extract_empirical_clusters(empirical_statistics, threshold = 3), null_cluster_dists)) 41 | expect_error(calculate_clusters_pvalues(extract_empirical_clusters(compute_timewise_statistics(spec, statistic = "chisq"), threshold = .05), null_cluster_dists)) 42 | expect_error(calculate_clusters_pvalues(empirical_clusters, extract_null_cluster_dists(null_statistics, threshold = 3))) 43 | }) 44 | 45 | test_that("Errors on no predictors", { 46 | spec_intercept <- make_jlmer_spec( 47 | weight ~ 1, subset(ChickWeight, Time <= 20), 48 | subject = "Chick", time = "Time" 49 | ) 50 | expect_error(clusterpermute(spec_intercept, threshold = 1.5, nsim = 1), "No predictors to permute") 51 | expect_error(permute_timewise_statistics(spec_intercept, threshold = 1.5, nsim = 1), "No predictors to permute") 52 | }) 53 | 54 | # Coverage 55 | lapply(list(empirical_statistics, empirical_clusters, null_statistics, null_cluster_dists, empirical_clusters_tested), tidy) 56 | lapply(list(spec, empirical_statistics, empirical_clusters, null_statistics, null_cluster_dists, empirical_clusters_tested), print) 57 | print(wholesale) 58 | -------------------------------------------------------------------------------- /tests/testthat/test-jlmer.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | #' @srrstats {G5.3} Tests for correctness but no explicit test for NA 4 | #' @srrstats {G5.4} Tests compare to R `lm()` output. 5 | 6 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 7 | 8 | spec_lm <- make_jlmer_spec(weight ~ 1 + Diet, ChickWeight) 9 | jlm1 <- to_jlmer(weight ~ 1 + Diet, ChickWeight) 10 | jlm2 <- jlmer(spec_lm) 11 | 12 | #' @srrstats {RE7.3} Tests for `print()`, `tidy()`, and `glance()` 13 | test_that("direct and indirect fits identical", { 14 | expect_equal(tidy(jlm1), tidy(jlm2)) 15 | expect_equal(glance(jlm1), glance(jlm2)) 16 | }) 17 | 18 | test_that("returns julia object", { 19 | expect_s3_class(jlm1, "jlmer_mod") 20 | expect_s3_class(jlm2, "jlmer_mod") 21 | }) 22 | 23 | spec_lme <- make_jlmer_spec(weight ~ 1 + Diet + (1 | Chick), ChickWeight) 24 | jlme1 <- to_jlmer(weight ~ 1 + Diet + (1 | Chick), ChickWeight) 25 | jlme2 <- jlmer(spec_lme) 26 | 27 | test_that("direct and indirect fits identical - mixed", { 28 | expect_equal(tidy(jlme1), tidy(jlme2)) 29 | expect_equal(glance(jlme1), glance(jlme2)) 30 | }) 31 | 32 | print(jlm1) 33 | print(jlme1) 34 | -------------------------------------------------------------------------------- /tests/testthat/test-jlmer_spec.R: -------------------------------------------------------------------------------- 1 | test_that("Minimal specification without grouping structures allowed", { 2 | expect_equal(names(make_jlmer_spec(weight ~ 1 + Diet, ChickWeight)), c("formula", "data", "meta")) 3 | expect_equal(names(make_jlmer_spec(weight ~ 1 + Diet + (1 | Chick), ChickWeight)), c("formula", "data", "meta")) 4 | }) 5 | 6 | test_that("Inform misspecifications in grouping structure", { 7 | expect_message(make_jlmer_spec(weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), time = "Time")) 8 | expect_message(make_jlmer_spec(weight ~ 1 + Diet, ChickWeight, subject = "Chick", time = "Time")) 9 | }) 10 | 11 | test_that("Warn uneven time sampling rate", { 12 | expect_message(make_jlmer_spec(weight ~ 1 + Diet, ChickWeight, subject = "Chick", time = "Time"), "Sampling rate") 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/test-julia_rng.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | test_that("RNG counter setter/getter", { 6 | expect_equal(set_rng_state(123), 123) 7 | expect_equal(get_rng_state(), 123) 8 | }) 9 | 10 | test_that("RNG seed setter/getter", { 11 | expect_equal(set_rng_seed(2), 2) 12 | expect_equal(get_rng_seed(), 2) 13 | }) 14 | 15 | test_that("RNG restore", { 16 | set_rng_seed(1) 17 | expect_equal(reset_rng_state(), 0) 18 | expect_equal(get_rng_seed(), 1) 19 | expect_equal(get_rng_state(), 0) 20 | }) 21 | 22 | test_that("RNG generate random seed", { 23 | expect_message(rand_seed <- set_rng_seed(), "Using randomly generated seed") 24 | expect_equal(rand_seed, get_rng_seed()) 25 | }) 26 | 27 | set_rng_seed(1) 28 | reset_rng_state() 29 | -------------------------------------------------------------------------------- /tests/testthat/test-permute.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | # Example 1 6 | chickweights_df <- ChickWeight 7 | chickweights_df <- chickweights_df[chickweights_df$Time <= 20, ] 8 | chickweights_df$DietInt <- as.integer(chickweights_df$Diet) 9 | chickweights_spec1 <- make_jlmer_spec( 10 | formula = weight ~ 1 + DietInt, 11 | data = chickweights_df, 12 | subject = "Chick", time = "Time" 13 | ) 14 | chickweights_spec1 15 | 16 | test_that("preserves participant structure", { 17 | p_str_original <- table(unique(chickweights_spec1$data[, c("Chick", "DietInt")])$DietInt) 18 | permuted <- permute_by_predictor(chickweights_spec1, predictors = "DietInt", predictor_type = "between_participant") 19 | p_str_permuted <- table(unique(permuted[, c("Chick", "DietInt")])$DietInt) 20 | expect_equal(p_str_original, p_str_permuted) 21 | }) 22 | 23 | test_that("preserves temporal structure", { 24 | t_str_original <- unname(sort(tapply(chickweights_spec1$data$weight, chickweights_spec1$data$Chick, toString))) 25 | permuted <- permute_by_predictor(chickweights_spec1, predictors = "DietInt", predictor_type = "between_participant") 26 | t_str_permuted <- unname(sort(tapply(permuted$weight, permuted$Chick, toString))) 27 | expect_equal(t_str_original, t_str_permuted) 28 | }) 29 | 30 | test_that("guesses type", { 31 | reset_rng_state() 32 | expect_message(spec1_perm1 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt"), "between_participant") 33 | }) 34 | 35 | test_that("increments counter", { 36 | expect_true(get_rng_state() > 0) 37 | }) 38 | 39 | test_that("shuffling reproducibility", { 40 | reset_rng_state() 41 | spec1_perm1 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt", predictor_type = "between_participant") 42 | reset_rng_state() 43 | spec1_perm2 <- permute_by_predictor(chickweights_spec1, predictors = "DietInt", predictor_type = "between_participant") 44 | expect_equal(spec1_perm1, spec1_perm2) 45 | }) 46 | 47 | test_that("levels of a category shuffled together", { 48 | chickweights_spec2 <- make_jlmer_spec( 49 | formula = weight ~ 1 + Diet, 50 | data = chickweights_df, 51 | subject = "Chick", time = "Time" 52 | ) 53 | reset_rng_state() 54 | expect_message(spec2_perm1 <- permute_by_predictor(chickweights_spec2, predictors = "Diet2", predictor_type = "between_participant"), "Diet3") 55 | reset_rng_state() 56 | spec2_perm2 <- permute_by_predictor(chickweights_spec2, predictors = c("Diet2", "Diet3", "Diet4"), predictor_type = "between_participant") 57 | expect_equal(spec2_perm1, spec2_perm2) 58 | }) 59 | -------------------------------------------------------------------------------- /tests/testthat/test-progress.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | test_that("No side effects when called empty", { 6 | expect_equal(julia_progress(), julia_progress()) 7 | }) 8 | 9 | default_opts <- julia_progress() 10 | 11 | test_that("Side effect by show, width, or both", { 12 | julia_progress(show = FALSE) 13 | expect_equal(julia_progress()$show, FALSE) 14 | julia_progress(width = 20) 15 | expect_equal(julia_progress()$width, 20) 16 | julia_progress(default_opts) 17 | expect_equal(julia_progress(), default_opts) 18 | }) 19 | 20 | test_that("Restore startup option", { 21 | julia_progress(show = TRUE, width = "auto") 22 | expect_equal(default_opts, julia_progress()) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/testthat/test-singularity.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | singularity_spec <- make_jlmer_spec( 6 | formula = weight ~ 1 + Diet + (1 + Diet | Chick), 7 | data = subset(transform(ChickWeight, Diet = as.integer(Chick)), Chick %in% 1:5 & Time <= 20), 8 | subject = "Chick", time = "Time" 9 | ) 10 | 11 | test_that("Informs on singularity", { 12 | expect_message(expect_message(expect_message( 13 | compute_timewise_statistics(singularity_spec), 14 | "singular fits" 15 | ), "number of components"), "Chick") 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/test-threshold_search.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | reset_rng_state() 6 | 7 | spec <- make_jlmer_spec( 8 | weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 9 | subject = "Chick", time = "Time" 10 | ) 11 | empirical_statistics <- compute_timewise_statistics(spec) 12 | null_statistics <- permute_timewise_statistics(spec, nsim = 100) 13 | 14 | test_that("tests all steps", { 15 | out <- walk_threshold_steps(empirical_statistics, null_statistics, steps = 1:3) 16 | expect_equal(sort(unique(out$threshold)), 1:3) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/testthat/test-timewise_statistics.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran() 2 | 3 | jlmerclusterperm_setup(cache_dir = tempdir(), restart = FALSE, verbose = FALSE) 4 | 5 | spec <- make_jlmer_spec( 6 | weight ~ 1 + Diet, subset(ChickWeight, Time <= 20), 7 | subject = "Chick", time = "Time" 8 | ) 9 | 10 | empirical_ts <- compute_timewise_statistics(spec, statistic = "t") 11 | empirical_chisqs <- compute_timewise_statistics(spec, statistic = "chisq") 12 | 13 | test_that("chisq bound by 0-1", { 14 | expect_error(extract_empirical_clusters(empirical_chisqs, threshold = 2)) 15 | }) 16 | 17 | test_that("dims of empirical stats", { 18 | expect_equal(dim(empirical_ts), c(3, 11)) 19 | expect_equal(dim(empirical_chisqs), c(1, 11)) 20 | }) 21 | 22 | test_that("tidy dims of empirical stats", { 23 | expect_equal(dim(tidy(empirical_ts)), c(33, 3)) 24 | expect_equal(dim(tidy(empirical_chisqs)), c(11, 3)) 25 | }) 26 | 27 | null_ts <- permute_timewise_statistics(spec, statistic = "t") 28 | null_chisqs <- permute_timewise_statistics(spec, statistic = "chisq") 29 | 30 | test_that("dims of null stats", { 31 | expect_equal(dim(null_ts), c(100, rev(dim(empirical_ts)))) 32 | expect_equal(dim(null_chisqs), c(100, rev(dim(empirical_chisqs)))) 33 | }) 34 | 35 | test_that("tidy dims of null stats", { 36 | expect_equal(dim(tidy(null_ts)), c(nrow(tidy(empirical_ts)) * 100, 4)) 37 | expect_equal(dim(tidy(null_chisqs)), c(nrow(tidy(empirical_chisqs)) * 100, 4)) 38 | }) 39 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/articles/julia-interface.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Julia interface" 3 | description: | 4 | Interacting with Julia regression models from R using {JuliaConnectoR} 5 | --- 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | eval = FALSE, 11 | comment = "#>" 12 | ) 13 | ``` 14 | 15 | This vignette showcases ways of interacting with pointers to `{jlmerclusterperm}` Julia objects from R using [JuliaConnectoR](https://github.com/stefan-m-lenz/JuliaConnectoR). 16 | 17 | See more tutorials and vignettes on the [Articles](https://yjunechoe.github.io/jlmerclusterperm/articles) page. 18 | 19 | ### Setup 20 | 21 | ```{r setup} 22 | library(jlmerclusterperm) 23 | jlmerclusterperm_setup(verbose = FALSE) 24 | julia_progress(show = FALSE) 25 | ``` 26 | 27 | ## Interacting with Julia objects 28 | 29 | All `jlmerclusterperm` functions collect Julia objects as R objects, except `jlmer()` and `to_jlmer()` which return GLM.jl or MixedModels.jl fitted model objects. 30 | 31 | ```{r to-jlmer} 32 | jmod <- to_jlmer(Reaction ~ Days + (Days | Subject), lme4::sleepstudy) 33 | jmod 34 | #> 35 | #> Variance components: 36 | #> Column Variance Std.Dev. Corr. 37 | #> Subject (Intercept) 565.51067 23.78047 38 | #> Days 32.68212 5.71683 +0.08 39 | #> Residual 654.94145 25.59182 40 | #> ────────────────────────────────────────────────── 41 | #> Coef. Std. Error z Pr(>|z|) 42 | #> ────────────────────────────────────────────────── 43 | #> (Intercept) 251.405 6.63226 37.91 <1e-99 44 | #> Days 10.4673 1.50224 6.97 <1e-11 45 | #> ────────────────────────────────────────────────── 46 | ``` 47 | 48 | You can use functions from [`JuliaConnectoR`](https://github.com/stefan-m-lenz/JuliaConnectoR) to interact with these (pointers to) Julia objects: 49 | 50 | ```{r julia-interop} 51 | library(JuliaConnectoR) 52 | juliaLet("x.rePCA", x = jmod) 53 | #> 54 | #> (Subject = [0.5406660682947617, 1.0],) 55 | juliaCall("issingular", jmod) 56 | #> [1] FALSE 57 | ``` 58 | 59 | You can also call functions from other Julia packages that you already have installed. For example, [Effects.jl](https://github.com/beacon-biosignals/Effects.jl) for its [`marginaleffects`](https://github.com/vincentarelbundock/marginaleffects)/[`emmeans`](https://github.com/rvlenth/emmeans)-like features: 60 | 61 | ```{r effects-jl} 62 | # juliaEval('using Pkg; Pkg.activate(); Pkg.add("Effects")') 63 | juliaEval("using Effects") 64 | juliaLet("effects(x.namedelements, y)", x = list(Days = 2:5), y = jmod) 65 | #> 66 | #> 4×5 DataFrame 67 | #> Row │ Days Reaction err lower upper 68 | #> │ Int64 Float64 Float64 Float64 Float64 69 | #> ─────┼──────────────────────────────────────────── 70 | #> 1 │ 2 272.34 7.09427 265.245 279.434 71 | #> 2 │ 3 282.807 7.70544 275.102 290.512 72 | #> 3 │ 4 293.274 8.55557 284.719 301.83 73 | #> 4 │ 5 303.742 9.58128 294.16 313.323 74 | ``` 75 | 76 | Note that `jlmer()` and `to_lmer()` are just conveniences for sanity checking the steps of a cluster-based permutation test, and thus should not be used as a general interface to fitting regression models in Julia for more serious analyses. 77 | 78 | ## Julia global options 79 | 80 | All functions involved in the cluster-based permutation analysis print progress bars that update in real time in the console. The function `julia_progress()` controls whether to `show` the progress bar and the `width` of the printed progress bar. 81 | 82 | ```{r julia_progress} 83 | # Set Julia progress options and save old state 84 | old_progress_opts <- julia_progress(show = FALSE, width = 30) 85 | old_progress_opts 86 | #> $show 87 | #> [1] TRUE 88 | #> 89 | #> $width 90 | #> [1] 50 91 | julia_progress() 92 | #> $show 93 | #> [1] FALSE 94 | #> 95 | #> $width 96 | #> [1] 30 97 | 98 | # Restored old state 99 | julia_progress(old_progress_opts) 100 | identical(old_progress_opts, julia_progress()) 101 | #> [1] TRUE 102 | 103 | # The syntax to reset progress options 104 | julia_progress(show = TRUE, width = "auto") 105 | julia_progress() 106 | ``` 107 | 108 | ## The Julia environment 109 | 110 | The following packages are loaded into the Julia environment via `jlmerclusterperm_setup()`: 111 | 112 | ```{r proj-toml, eval = TRUE} 113 | cat(readLines(system.file("julia/Project.toml", package = "jlmerclusterperm")), sep = "\n") 114 | ``` 115 | 116 | ## Optional configurations 117 | 118 | For speed-ups in model fitting, install [MKL.jl](https://github.com/JuliaLinearAlgebra/MKL.jl) (Intel core) or [AppleAccelerate.jl](https://github.com/JuliaLinearAlgebra/AppleAccelerate.jl) (Mac) and load it on start by modifying the `startup.jl` file. 119 | 120 | For example, on my Windows machine the startup file is located at: 121 | 122 | ```{r startup-file, eval = FALSE} 123 | JuliaConnectoR::juliaCall("Base._local_julia_startup_file") 124 | #> "C:/Users/jchoe/.julia/config/startup.jl" 125 | ``` 126 | 127 | And there I load MKL.jl: 128 | 129 |
130 | if contains(first(Sys.cpu_info()).model, "Intel")
131 |   using MKL
132 | end
133 | 
134 | -------------------------------------------------------------------------------- /vignettes/articles/paper_files/deCarvalho_fig7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yjunechoe/jlmerclusterperm/a16a95cde59358ce09e1ae0474ca68a9a8a5845d/vignettes/articles/paper_files/deCarvalho_fig7.jpg --------------------------------------------------------------------------------