├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── align.R ├── containers.R ├── count.R ├── diff.R ├── format.R ├── groups.R ├── helper.R ├── import-standalone-obj-type.R ├── import-standalone-purrr.R ├── import-standalone-types-check.R ├── iv.R ├── ivs-deprecated.R ├── ivs-package.R ├── relation.R ├── set-pairwise.R ├── set.R ├── span.R ├── splits.R ├── utils.R ├── vector.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── ivs.Rproj ├── man ├── allen-relation-count.Rd ├── allen-relation-detect-pairwise.Rd ├── allen-relation-detect.Rd ├── allen-relation-locate.Rd ├── figures │ ├── complement-upper.png │ ├── complement.png │ ├── groups-abutting-keep.png │ ├── groups.png │ ├── intersect.png │ ├── iv.graffle │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-soft-deprecated.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── splits.png ├── is_iv.Rd ├── iv-accessors.Rd ├── iv-containers.Rd ├── iv-genericity.Rd ├── iv-groups.Rd ├── iv-set-pairwise-deprecated.Rd ├── iv-set-pairwise.Rd ├── iv-sets-deprecated.Rd ├── iv-sets.Rd ├── iv-splits.Rd ├── iv.Rd ├── iv_align.Rd ├── iv_diff.Rd ├── iv_format.Rd ├── iv_pairwise_span.Rd ├── iv_span.Rd ├── ivs-package.Rd ├── new_iv.Rd ├── relation-count.Rd ├── relation-detect-pairwise.Rd ├── relation-detect.Rd ├── relation-locate.Rd ├── vector-count.Rd ├── vector-detect-pairwise.Rd ├── vector-detect.Rd └── vector-locate.Rd ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── align.md │ ├── containers.md │ ├── count.md │ ├── diff.md │ ├── format.md │ ├── iv.md │ ├── ivs-deprecated.md │ ├── relation.md │ ├── set-pairwise.md │ ├── span.md │ ├── splits.md │ └── vector.md │ ├── test-align.R │ ├── test-containers.R │ ├── test-count.R │ ├── test-diff.R │ ├── test-format.R │ ├── test-groups.R │ ├── test-iv.R │ ├── test-ivs-deprecated.R │ ├── test-relation.R │ ├── test-set-pairwise.R │ ├── test-set.R │ ├── test-span.R │ ├── test-splits.R │ └── test-vector.R └── vignettes ├── .gitignore ├── examples.Rmd └── ivs.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^ivs\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^codecov\.yml$ 6 | ^\.github$ 7 | ^_pkgdown\.yml$ 8 | ^docs$ 9 | ^pkgdown$ 10 | ^cran-comments\.md$ 11 | ^CRAN-RELEASE$ 12 | ^CRAN-SUBMISSION$ 13 | -------------------------------------------------------------------------------- /.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@posit.co. 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/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 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: macos-latest, r: 'release'} 26 | 27 | - {os: windows-latest, r: 'release'} 28 | # Use 3.6 to trigger usage of RTools35 29 | - {os: windows-latest, r: '3.6'} 30 | # use 4.1 to check with rtools40's older compiler 31 | - {os: windows-latest, r: '4.1'} 32 | 33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 34 | - {os: ubuntu-latest, r: 'release'} 35 | - {os: ubuntu-latest, r: 'oldrel-1'} 36 | - {os: ubuntu-latest, r: 'oldrel-2'} 37 | - {os: ubuntu-latest, r: 'oldrel-3'} 38 | - {os: ubuntu-latest, r: 'oldrel-4'} 39 | 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | R_KEEP_PKG_SOURCE: yes 43 | 44 | steps: 45 | - uses: actions/checkout@v3 46 | 47 | - uses: r-lib/actions/setup-pandoc@v2 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | http-user-agent: ${{ matrix.config.http-user-agent }} 53 | use-public-rspm: true 54 | 55 | - uses: r-lib/actions/setup-r-dependencies@v2 56 | with: 57 | extra-packages: any::rcmdcheck 58 | needs: check 59 | 60 | - uses: r-lib/actions/check-r-package@v2 61 | with: 62 | upload-snapshots: true 63 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.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 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v3 24 | 25 | - uses: r-lib/actions/setup-pandoc@v2 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::pkgdown, local::. 34 | needs: website 35 | 36 | - name: Build site 37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 38 | shell: Rscript {0} 39 | 40 | - name: Deploy to GitHub pages 🚀 41 | if: github.event_name != 'pull_request' 42 | uses: JamesIves/github-pages-deploy-action@v4.4.1 43 | with: 44 | clean: false 45 | branch: gh-pages 46 | folder: docs 47 | -------------------------------------------------------------------------------- /.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 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: | 31 | covr::codecov( 32 | quiet = FALSE, 33 | clean = FALSE, 34 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 35 | ) 36 | shell: Rscript {0} 37 | 38 | - name: Show testthat output 39 | if: always() 40 | run: | 41 | ## -------------------------------------------------------------------- 42 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 43 | shell: bash 44 | 45 | - name: Upload test results 46 | if: failure() 47 | uses: actions/upload-artifact@v3 48 | with: 49 | name: coverage-test-failures 50 | path: ${{ runner.temp }}/package 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | inst/doc 7 | docs 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ivs 2 | Title: Interval Vectors 3 | Version: 0.2.0.9000 4 | Authors@R: c( 5 | person("Davis", "Vaughan", , "davis@posit.co", role = c("aut", "cre")), 6 | person("Posit Software, PBC", role = c("cph", "fnd")) 7 | ) 8 | Description: Provides a library for generic interval manipulations using a 9 | new interval vector class. Capabilities include: locating various 10 | kinds of relationships between two interval vectors, merging overlaps 11 | within a single interval vector, splitting an interval vector on its 12 | overlapping endpoints, and applying set theoretical operations on 13 | interval vectors. Many of the operations in this package were inspired 14 | by James Allen's interval algebra, Allen (1983) 15 | . 16 | License: MIT + file LICENSE 17 | URL: https://github.com/DavisVaughan/ivs, 18 | https://davisvaughan.github.io/ivs/ 19 | BugReports: https://github.com/DavisVaughan/ivs/issues 20 | Depends: 21 | R (>= 3.5.0) 22 | Imports: 23 | glue (>= 1.6.2), 24 | lifecycle (>= 1.0.3), 25 | rlang (>= 1.1.0), 26 | vctrs (>= 0.6.0) 27 | Suggests: 28 | bit64 (>= 4.0.5), 29 | clock (>= 0.6.0), 30 | covr, 31 | dplyr (>= 1.1.0), 32 | knitr, 33 | rmarkdown, 34 | testthat (>= 3.0.0), 35 | tidyr (>= 1.1.4) 36 | VignetteBuilder: 37 | knitr 38 | Config/testthat/edition: 3 39 | Encoding: UTF-8 40 | Roxygen: list(markdown = TRUE) 41 | RoxygenNote: 7.2.3 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: ivs authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 ivs authors 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 | S3method(format,ivs_iv) 4 | S3method(format,ivs_nested_integer_iv) 5 | S3method(iv_format,Date) 6 | S3method(iv_format,POSIXt) 7 | S3method(iv_format,character) 8 | S3method(iv_format,data.frame) 9 | S3method(iv_format,default) 10 | S3method(iv_format,difftime) 11 | S3method(iv_format,double) 12 | S3method(iv_format,factor) 13 | S3method(iv_format,integer) 14 | S3method(iv_format,integer64) 15 | S3method(iv_format,logical) 16 | S3method(iv_proxy,default) 17 | S3method(iv_proxy,ivs_nested_integer_iv) 18 | S3method(iv_restore,default) 19 | S3method(iv_restore,ivs_iv) 20 | S3method(iv_restore,ivs_nested_integer_iv) 21 | S3method(vec_cast,ivs_iv.ivs_iv) 22 | S3method(vec_cast,ivs_nested_integer_iv.ivs_nested_integer_iv) 23 | S3method(vec_ptype,ivs_iv) 24 | S3method(vec_ptype2,ivs_iv.ivs_iv) 25 | S3method(vec_ptype2,ivs_nested_integer_iv.ivs_nested_integer_iv) 26 | S3method(vec_ptype_abbr,ivs_iv) 27 | S3method(vec_ptype_finalise,ivs_iv) 28 | S3method(vec_ptype_full,ivs_iv) 29 | S3method(vec_ptype_full,ivs_nested_integer_iv) 30 | S3method(vec_restore,ivs_iv) 31 | export(is_iv) 32 | export(iv) 33 | export(iv_align) 34 | export(iv_between) 35 | export(iv_complement) 36 | export(iv_containers) 37 | export(iv_count_between) 38 | export(iv_count_follows) 39 | export(iv_count_includes) 40 | export(iv_count_overlaps) 41 | export(iv_count_precedes) 42 | export(iv_count_relates) 43 | export(iv_diff) 44 | export(iv_difference) 45 | export(iv_end) 46 | export(iv_follows) 47 | export(iv_format) 48 | export(iv_groups) 49 | export(iv_identify_container) 50 | export(iv_identify_containers) 51 | export(iv_identify_group) 52 | export(iv_identify_splits) 53 | export(iv_includes) 54 | export(iv_intersect) 55 | export(iv_locate_between) 56 | export(iv_locate_containers) 57 | export(iv_locate_follows) 58 | export(iv_locate_groups) 59 | export(iv_locate_includes) 60 | export(iv_locate_overlaps) 61 | export(iv_locate_precedes) 62 | export(iv_locate_relates) 63 | export(iv_locate_splits) 64 | export(iv_overlaps) 65 | export(iv_pairs) 66 | export(iv_pairwise_between) 67 | export(iv_pairwise_complement) 68 | export(iv_pairwise_difference) 69 | export(iv_pairwise_follows) 70 | export(iv_pairwise_includes) 71 | export(iv_pairwise_intersect) 72 | export(iv_pairwise_overlaps) 73 | export(iv_pairwise_precedes) 74 | export(iv_pairwise_relates) 75 | export(iv_pairwise_set_complement) 76 | export(iv_pairwise_set_difference) 77 | export(iv_pairwise_set_intersect) 78 | export(iv_pairwise_set_symmetric_difference) 79 | export(iv_pairwise_set_union) 80 | export(iv_pairwise_span) 81 | export(iv_pairwise_symmetric_difference) 82 | export(iv_pairwise_union) 83 | export(iv_precedes) 84 | export(iv_proxy) 85 | export(iv_relates) 86 | export(iv_restore) 87 | export(iv_set_complement) 88 | export(iv_set_difference) 89 | export(iv_set_intersect) 90 | export(iv_set_symmetric_difference) 91 | export(iv_set_union) 92 | export(iv_span) 93 | export(iv_splits) 94 | export(iv_start) 95 | export(iv_symmetric_difference) 96 | export(iv_union) 97 | export(new_iv) 98 | import(rlang) 99 | import(vctrs) 100 | importFrom(glue,glue) 101 | importFrom(glue,glue_collapse) 102 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # ivs (development version) 2 | 3 | # ivs 0.2.0 4 | 5 | * The family of "set" functions has been renamed to include a `set_` prefix, 6 | for example, `iv_union()` is now `iv_set_union()`. This has been done to 7 | align with the new family of vctrs set functions, like `vec_set_union()`, and 8 | to reduce the ambiguity between the new `iv_diff()` helper and 9 | `iv_difference()` (now `iv_set_difference()`). The old names are deprecated, 10 | and will be removed in a future release (#35). 11 | 12 | * `iv_complement()` -> `iv_set_complement()` 13 | * `iv_union()` -> `iv_set_union()` 14 | * `iv_intersect()` -> `iv_set_intersect()` 15 | * `iv_difference()` -> `iv_set_difference()` 16 | * `iv_symmetric_difference()` -> `iv_set_symmetric_difference()` 17 | * The same changes have been made for the `iv_pairwise_*()` functions that 18 | share the same suffixes. 19 | 20 | * New family of functions that perform the inverse of `iv_between()`, i.e. 21 | rather than detecting if `needles[i]`, a vector value, falls _between_ any 22 | intervals in `haystack`, an iv, these detect if `needles[i]`, an interval, 23 | _includes_ any value from `haystack`, a vector. These functions are: 24 | `iv_includes()`, `iv_locate_includes()`, `iv_count_includes()`, and 25 | `iv_pairwise_includes()` (#41). 26 | 27 | * New family of functions for identifying interval _containers_, which are 28 | intervals that aren't contained within any other interval. These functions 29 | are: `iv_containers()`, `iv_identify_containers()`, `iv_identify_container()`, 30 | and `iv_locate_containers()` (#20). 31 | 32 | * New `iv_diff()` for generating an iv from an existing vector that is in 33 | strictly increasing order (#17). 34 | 35 | * New `iv_span()` for computing a summary interval that encompasses the entire 36 | range of an existing iv (#49). 37 | 38 | * New Examples vignette that links out to Stack Overflow questions that were 39 | solved with ivs. View it locally with `vignette("examples", package = "ivs")`. 40 | 41 | * `vec_ptype()` and `vec_ptype_finalise()` methods have been added for the iv 42 | class. This should result in slightly better performance when combining many 43 | ivs together (#27). 44 | 45 | * `iv_locate_overlaps()`, `iv_locate_precedes()`, `iv_locate_follows()`, 46 | `iv_locate_between()`, and `iv_locate_includes()` have all gained the 47 | `relationship` argument from the underlying engine, 48 | `vctrs::vec_locate_matches()` (#45). 49 | 50 | * `iv_proxy()` now returns the input unchanged if it doesn't implement an S3 51 | method, rather than erroring. In combination with `is_iv()`, this provides a 52 | way to check if an input implements a proxy method and to implement different 53 | behaviors depending on the result. 54 | 55 | * In `iv()`, incomplete value propagation is now done before the `start < end` 56 | check, which fixes an inconsistent edge case (#36). 57 | 58 | * You can now combine an iv containing unspecified components with any other iv 59 | (#33). 60 | 61 | * The `"iv"` class name has been renamed to the more specific `"ivs_iv"` to 62 | better insulate it from potential collisions with classes from other packages 63 | (#25). 64 | 65 | * Improved on the call reported by errors thrown in ivs (#23). 66 | 67 | * Switched to using `vec_run_sizes()` and `vec_chop(sizes =)` internally, which 68 | improves performance in some cases (#50). 69 | 70 | * Added a `NEWS.md` file to track changes to the package. 71 | -------------------------------------------------------------------------------- /R/align.R: -------------------------------------------------------------------------------- 1 | #' Align after locating relationships 2 | #' 3 | #' @description 4 | #' `iv_align()` will align/join `needles` and `haystack` together using a data 5 | #' frame of `locations`. These `locations` are intended to be the output of one 6 | #' of: [iv_locate_overlaps()], [iv_locate_precedes()], [iv_locate_follows()], 7 | #' [iv_locate_relates()], or [iv_locate_between()]. 8 | #' 9 | #' This is mainly a convenience function that slices both `needles` and 10 | #' `haystack` according to those `locations`, and then stores the result 11 | #' in a new two column data frame. 12 | #' 13 | #' @inheritParams rlang::args_dots_empty 14 | #' 15 | #' @param needles,haystack `[vector]` 16 | #' 17 | #' Two vectors to align. 18 | #' 19 | #' @param locations `[two-column data frame]` 20 | #' 21 | #' The data frame of locations returned from one of [iv_locate_overlaps()], 22 | #' [iv_locate_precedes()], [iv_locate_follows()], [iv_locate_relates()], or 23 | #' [iv_locate_between()]. 24 | #' 25 | #' @return A two column data frame with a `$needles` column containing the 26 | #' sliced version of `needles` and a `$haystack` column containing the sliced 27 | #' version of `haystack`. 28 | #' 29 | #' @export 30 | #' @examples 31 | #' needles <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 32 | #' haystack <- iv_pairs(c(0, 2), c(4, 6)) 33 | #' 34 | #' locations <- iv_locate_overlaps(needles, haystack) 35 | #' iv_align(needles, haystack, locations = locations) 36 | #' 37 | #' locations <- iv_locate_overlaps(needles, haystack, no_match = "drop") 38 | #' iv_align(needles, haystack, locations = locations) 39 | #' 40 | #' needles <- c(1, 15, 4, 11) 41 | #' haystack <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 42 | #' 43 | #' locations <- iv_locate_between(needles, haystack) 44 | #' iv_align(needles, haystack, locations = locations) 45 | iv_align <- function(needles, haystack, ..., locations) { 46 | check_dots_empty0(...) 47 | check_locations(locations) 48 | 49 | needles <- vec_slice(needles, locations[["needles"]]) 50 | haystack <- vec_slice(haystack, locations[["haystack"]]) 51 | 52 | data_frame(needles = needles, haystack = haystack) 53 | } 54 | 55 | check_locations <- function(locations, ..., call = caller_env()) { 56 | check_dots_empty0(...) 57 | 58 | if (!is.data.frame(locations)) { 59 | abort("`locations` must be a data frame.", call = call) 60 | } 61 | if (ncol(locations) != 2L) { 62 | abort("`locations` must be a two column data frame.", call = call) 63 | } 64 | 65 | if (!has_name(locations, "needles")) { 66 | abort("`locations` must have a column named \"needles\".", call = call) 67 | } 68 | if (!has_name(locations, "haystack")) { 69 | abort("`locations` must have a column named \"haystack\".", call = call) 70 | } 71 | 72 | needles <- locations[["needles"]] 73 | haystack <- locations[["haystack"]] 74 | 75 | if (!is_integer(needles)) { 76 | abort("`locations$needles` must be an integer vector.", call = call) 77 | } 78 | if (!is_integer(haystack)) { 79 | abort("`locations$haystack` must be an integer vector.", call = call) 80 | } 81 | 82 | return() 83 | } 84 | -------------------------------------------------------------------------------- /R/containers.R: -------------------------------------------------------------------------------- 1 | #' Containers 2 | #' 3 | #' @description 4 | #' This family of functions revolves around computing interval _containers_. 5 | #' A container is defined as the widest interval that isn't contained by any 6 | #' other interval. 7 | #' 8 | #' - `iv_containers()` returns all of the containers found within `x`. 9 | #' 10 | #' - `iv_identify_containers()` identifies the containers that each interval in 11 | #' `x` falls in. It replaces `x` with a list of the same size where each element 12 | #' of the list contains the containers that the corresponding interval in `x` 13 | #' falls in. This is particularly useful alongside [tidyr::unnest()]. 14 | #' 15 | #' - `iv_identify_container()` is similar in spirit to 16 | #' `iv_identify_containers()`, but is useful when you suspect that each interval 17 | #' in `x` is contained within exactly 1 container. It replaces `x` with an iv of 18 | #' the same size where each interval is the container that the corresponding 19 | #' interval in `x` falls in. If any interval falls in more than one container, 20 | #' an error is thrown. 21 | #' 22 | #' - `iv_locate_containers()` returns a two column data frame with a `key` 23 | #' column containing the result of `iv_containers()` and a `loc` list-column 24 | #' containing integer vectors that map each interval in `x` to the container 25 | #' that it falls in. 26 | #' 27 | #' @param x `[iv]` 28 | #' 29 | #' An interval vector. 30 | #' 31 | #' @return 32 | #' - For `iv_containers()`, an iv with the same type as `x`. 33 | #' 34 | #' - For `iv_identify_containers()`, a list-of containing ivs with the same size 35 | #' as `x`. 36 | #' 37 | #' - For `iv_identify_container()`, an iv with the same type as `x`. 38 | #' 39 | #' - For `iv_locate_containers()`, a two column data frame with a `key` column 40 | #' containing the result of `iv_containers()` and a `loc` list-column containing 41 | #' integer vectors. 42 | #' 43 | #' @name iv-containers 44 | #' 45 | #' @examples 46 | #' library(dplyr, warn.conflicts = FALSE) 47 | #' library(tidyr) 48 | #' 49 | #' x <- iv_pairs( 50 | #' c(4, 6), 51 | #' c(1, 5), 52 | #' c(2, 3), 53 | #' c(NA, NA), 54 | #' c(NA, NA), 55 | #' c(9, 12), 56 | #' c(9, 14) 57 | #' ) 58 | #' x 59 | #' 60 | #' # Containers are intervals which aren't contained in any other interval. 61 | #' # They are always returned in ascending order. 62 | #' # If any missing intervals are present, a single one is retained. 63 | #' iv_containers(x) 64 | #' 65 | #' # `iv_identify_container()` is useful alongside `group_by()` and 66 | #' # `summarize()` if you know that each interval is contained within exactly 67 | #' # 1 container 68 | #' df <- tibble(x = x) 69 | #' df <- mutate(df, container = iv_identify_container(x)) 70 | #' df 71 | #' 72 | #' df %>% 73 | #' group_by(container) %>% 74 | #' summarize(n = n()) 75 | #' 76 | #' # If any interval is contained within multiple containers, 77 | #' # then you can't use `iv_identify_container()` 78 | #' y <- c(x, iv_pairs(c(0, 3), c(8, 13))) 79 | #' y 80 | #' 81 | #' try(iv_identify_container(y)) 82 | #' 83 | #' # Instead, use `iv_identify_containers()` to identify every container 84 | #' # that each interval falls in 85 | #' df <- tibble(y = y, container = iv_identify_containers(y)) 86 | #' df 87 | #' 88 | #' # You can use `tidyr::unchop()` to see the containers that each interval 89 | #' # falls in 90 | #' df %>% 91 | #' mutate(row = row_number(), .before = 1) %>% 92 | #' unchop(container) 93 | #' 94 | #' # A more programmatic interface to `iv_identify_containers()` is 95 | #' # `iv_locate_containers()`, which returns the containers you get from 96 | #' # `iv_containers()` alongside the locations in the input that they contain. 97 | #' iv_locate_containers(y) 98 | NULL 99 | 100 | #' @rdname iv-containers 101 | #' @export 102 | iv_containers <- function(x) { 103 | proxy <- iv_proxy(x) 104 | check_iv(proxy, arg = "x") 105 | 106 | start <- field_start(proxy) 107 | end <- field_end(proxy) 108 | 109 | loc <- vec_interval_locate_containers( 110 | start = start, 111 | end = end 112 | ) 113 | 114 | both <- data_frame(start = start, end = end) 115 | both <- vec_slice(both, loc) 116 | 117 | start <- both$start 118 | end <- both$end 119 | 120 | out <- new_iv(start, end) 121 | out <- iv_restore(out, x) 122 | 123 | out 124 | } 125 | 126 | #' @rdname iv-containers 127 | #' @export 128 | iv_identify_containers <- function(x) { 129 | containers <- iv_containers(x) 130 | 131 | loc <- iv_locate_overlaps( 132 | needles = x, 133 | haystack = containers, 134 | type = "within" 135 | ) 136 | 137 | sizes <- vec_run_sizes(loc$needles) 138 | loc <- vec_chop(loc$haystack, sizes = sizes) 139 | 140 | ptype <- vec_ptype(containers) 141 | ptype <- vec_ptype_finalise(ptype) 142 | 143 | out <- vec_chop(containers, indices = loc) 144 | out <- new_list_of(out, ptype = ptype) 145 | 146 | out 147 | } 148 | 149 | #' @rdname iv-containers 150 | #' @export 151 | iv_identify_container <- function(x) { 152 | containers <- iv_containers(x) 153 | 154 | loc <- with_multiple_containers_errors(iv_locate_overlaps( 155 | needles = x, 156 | haystack = containers, 157 | type = "within", 158 | relationship = "many-to-one" 159 | )) 160 | 161 | out <- vec_slice(containers, loc$haystack) 162 | 163 | out 164 | } 165 | 166 | #' @rdname iv-containers 167 | #' @export 168 | iv_locate_containers <- function(x) { 169 | containers <- iv_containers(x) 170 | 171 | # Each `containers` value is guaranteed to match at least one `x` value, 172 | # since it was generated from them, and vice-versa. This means we don't 173 | # need to re-slice the `containers` after locating overlaps. 174 | loc <- iv_locate_overlaps( 175 | needles = containers, 176 | haystack = x, 177 | type = "contains" 178 | ) 179 | 180 | sizes <- vec_run_sizes(loc$needles) 181 | loc <- vec_chop(loc$haystack, sizes = sizes) 182 | 183 | out <- data_frame(key = containers, loc = loc) 184 | 185 | out 186 | } 187 | 188 | 189 | with_multiple_containers_errors <- function(expr, error_call = caller_env()) { 190 | withCallingHandlers( 191 | expr = expr, 192 | vctrs_error_matches_relationship_many_to_one = function(cnd) { 193 | stop_multiple_containers(cnd$i, error_call = error_call) 194 | } 195 | ) 196 | } 197 | 198 | stop_multiple_containers <- function(i, error_call = caller_env()) { 199 | message <- c( 200 | "Intervals in `x` can't fall within multiple containers.", 201 | i = glue("Location {i} falls within multiple containers."), 202 | i = paste0( 203 | "Use `iv_identify_containers()` to identify all of the containers ", 204 | "that a particular interval is contained by." 205 | ) 206 | ) 207 | 208 | abort(message, call = error_call) 209 | } 210 | -------------------------------------------------------------------------------- /R/count.R: -------------------------------------------------------------------------------- 1 | SIGNAL_MISSING <- 0L 2 | SIGNAL_NO_MATCH <- -1L 3 | 4 | iv_count_locations <- function(locations, missing, no_match) { 5 | # No need to worry about `NA` values in `needles` because `remaining` isn't 6 | # exposed in the count functions (it doesn't make sense to do so) 7 | out <- vec_run_sizes(locations$needles) 8 | 9 | value_missing <- is_scalar_integer(missing) 10 | value_no_match <- is_scalar_integer(no_match) 11 | 12 | if (!value_missing && !value_no_match) { 13 | # Some combination of these that doesn't require special post-processing: 14 | # `missing = "equals" / "error"` 15 | # `no_match = "error"` 16 | return(out) 17 | } 18 | 19 | ones <- out == 1L 20 | any_ones <- any(ones) 21 | 22 | if (!any_ones) { 23 | # No missing or unmatched `needles` are possible 24 | return(out) 25 | } 26 | 27 | ones <- which(ones) 28 | starts <- vec_run_sizes_to_starts(out) 29 | starts <- vec_slice(starts, ones) 30 | haystack <- vec_slice(locations$haystack, starts) 31 | 32 | if (value_missing) { 33 | where_missing <- haystack == SIGNAL_MISSING 34 | 35 | if (any(where_missing)) { 36 | out_missing <- vec_slice(ones, where_missing) 37 | out <- vec_assign(out, out_missing, missing) 38 | } 39 | } 40 | 41 | if (value_no_match) { 42 | where_no_match <- haystack == SIGNAL_NO_MATCH 43 | 44 | if (any(where_no_match)) { 45 | out_no_match <- vec_slice(ones, where_no_match) 46 | out <- vec_assign(out, out_no_match, no_match) 47 | } 48 | } 49 | 50 | out 51 | } 52 | 53 | check_count_missing <- function(missing, ..., call = caller_env()) { 54 | check_dots_empty0(...) 55 | 56 | if (is_string(missing)) { 57 | # `"drop"` doesn't make sense here 58 | missing <- arg_match0( 59 | arg = missing, 60 | values = c("equals", "error"), 61 | arg_nm = "missing", 62 | error_call = call 63 | ) 64 | } else { 65 | missing <- vec_cast(missing, to = integer(), call = call) 66 | vec_check_size(missing, size = 1L, call = call) 67 | } 68 | 69 | missing 70 | } 71 | 72 | translate_count_missing <- function(missing) { 73 | if (is_scalar_integer(missing)) { 74 | SIGNAL_MISSING 75 | } else { 76 | missing 77 | } 78 | } 79 | 80 | check_count_no_match <- function(no_match, ..., call = caller_env()) { 81 | check_dots_empty0(...) 82 | 83 | if (is_string(no_match)) { 84 | # `"drop"` doesn't make sense here 85 | no_match <- arg_match0( 86 | arg = no_match, 87 | values = "error", 88 | arg_nm = "no_match", 89 | error_call = call 90 | ) 91 | } else { 92 | no_match <- vec_cast(no_match, to = integer(), call = call) 93 | vec_check_size(no_match, size = 1L, call = call) 94 | } 95 | 96 | no_match 97 | } 98 | 99 | translate_count_no_match <- function(no_match) { 100 | if (is_scalar_integer(no_match)) { 101 | SIGNAL_NO_MATCH 102 | } else { 103 | no_match 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /R/diff.R: -------------------------------------------------------------------------------- 1 | #' Diff a vector to create an interval vector 2 | #' 3 | #' @description 4 | #' `iv_diff()` is a convenient way to generate an iv from a preexisting vector, 5 | #' as long as that vector is in strictly increasing order. It returns an iv 6 | #' that is 1 element shorter than `x` (unless `x` is already empty). 7 | #' 8 | #' It is particularly useful for creating an iv column from an existing column 9 | #' inside of [dplyr::mutate()], but requires you to explicitly handle padding 10 | #' in that case, see the examples. 11 | #' 12 | #' Missing values are allowed, and will be propagated to each side of the 13 | #' resulting interval after applying the diff. 14 | #' 15 | #' @details 16 | #' `iv_diff()` is inspired by [diff()]. 17 | #' 18 | #' @param x `[vector]` 19 | #' 20 | #' A vector in strictly increasing order. 21 | #' 22 | #' @return 23 | #' An iv using `x` as the inner type, with size equal to 24 | #' `max(0L, vec_size(x) - 1L)`. 25 | #' 26 | #' @export 27 | #' @examples 28 | #' x <- as.Date("2019-01-01") + c(0, 5, 7, 10, 19) 29 | #' x 30 | #' 31 | #' # Notice how the boundaries don't overlap, because the closing `)` aligns 32 | #' # with an opening `[`. 33 | #' iv_diff(x) 34 | #' 35 | #' # Like `iv()`, missing values propagate to both boundaries of the interval. 36 | #' # Before missing value propagation was applied, it looked like this: 37 | #' # [1, NA), [NA, 2), [2, 3) 38 | #' x <- c(1, NA, 2, 3) 39 | #' iv_diff(x) 40 | #' 41 | #' # Values in `x` must be in strictly increasing order to generate a valid 42 | #' # interval vector 43 | #' x <- c(1, 0, 2, 2) 44 | #' try(iv_diff(x)) 45 | #' 46 | #' x <- c(1, NA, 0) 47 | #' try(iv_diff(x)) 48 | #' 49 | #' # --------------------------------------------------------------------------- 50 | #' # Use with `mutate()` 51 | #' 52 | #' library(dplyr) 53 | #' 54 | #' # `iv_diff()` is useful for converting a pre-existing column into an interval 55 | #' # vector, but you'll need to apply padding to ensure that the size of the 56 | #' # diff-ed result is the same as the number of rows in your data frame. There 57 | #' # are two main ways to pad, which are explored below. 58 | #' df <- tibble(x = c(1, 3, 6)) 59 | #' 60 | #' # Pad with a known lower/upper bound 61 | #' df %>% mutate(iv = iv_diff(c(0, x))) 62 | #' df %>% mutate(iv = iv_diff(c(x, Inf))) 63 | #' 64 | #' # Pad with a missing value, which results in a fully missing interval 65 | #' df %>% mutate(iv = iv_diff(c(NA, x))) 66 | #' df %>% mutate(iv = iv_diff(c(x, NA))) 67 | iv_diff <- function(x) { 68 | obj_check_vector(x) 69 | 70 | rank <- vec_rank(x, ties = "dense", incomplete = "na") 71 | is_strictly_increasing <- !is.unsorted(rank, na.rm = TRUE, strictly = TRUE) 72 | 73 | if (!is_strictly_increasing) { 74 | index <- vec_seq_along(rank) 75 | 76 | complete <- vec_detect_complete(rank) 77 | rank <- vec_slice(rank, complete) 78 | index <- vec_slice(index, complete) 79 | 80 | loc <- which(diff(rank) < 1L) 81 | loc <- loc + 1L 82 | loc <- index[loc] 83 | 84 | loc <- err_locs(loc) 85 | 86 | abort(c( 87 | "`x` must be in strictly increasing order.", 88 | i = glue("`x` is not in strictly increasing order at locations: {loc}.") 89 | )) 90 | } 91 | 92 | size <- vec_size(x) 93 | 94 | start <- seq2(1L, size - 1L) 95 | end <- seq2(2L, size) 96 | 97 | start <- vec_slice(x, start) 98 | end <- vec_slice(x, end) 99 | 100 | iv(start, end) 101 | } 102 | -------------------------------------------------------------------------------- /R/format.R: -------------------------------------------------------------------------------- 1 | #' Formatting 2 | #' 3 | #' @description 4 | #' `iv_format()` is an S3 generic intended as a developer tool for making a 5 | #' custom class print nicely when stored in an iv. The default method simply 6 | #' calls [format()], and in many cases this is enough for most classes. 7 | #' However, if your class automatically adds justification or padding when 8 | #' formatting a single vector, you might need to implement an `iv_format()` 9 | #' method to avoid that padding, since it often looks strange when nested 10 | #' in an interval vector. 11 | #' 12 | #' @param x `[vector]` 13 | #' 14 | #' A vector to format. This will be called on the [iv_start()] and [iv_end()] 15 | #' vectors of an iv. 16 | #' 17 | #' @return A character vector, likely generated through a call to `format()`. 18 | #' 19 | #' @export 20 | #' @examples 21 | #' # Numeric values get padding automatically through `format()` 22 | #' x <- c(1, 100) 23 | #' format(x) 24 | #' 25 | #' # This ends up looking strange in an iv, so an `iv_format()` method for 26 | #' # numeric values is implemented which turns off that padding 27 | #' iv_format(x) 28 | iv_format <- function(x) { 29 | UseMethod("iv_format") 30 | } 31 | 32 | #' @export 33 | iv_format.default <- function(x) { 34 | format(x) 35 | } 36 | 37 | #' @export 38 | iv_format.logical <- function(x) { 39 | format(x, trim = TRUE) 40 | } 41 | 42 | #' @export 43 | iv_format.integer <- function(x) { 44 | format(x, trim = TRUE, scientific = FALSE) 45 | } 46 | 47 | #' @export 48 | iv_format.double <- function(x) { 49 | format(x, trim = TRUE, scientific = FALSE) 50 | } 51 | 52 | #' @export 53 | iv_format.character <- function(x) { 54 | format(x, justify = "none", na.encode = TRUE) 55 | } 56 | 57 | #' @export 58 | iv_format.factor <- function(x) { 59 | format(x, justify = "none", na.encode = TRUE) 60 | } 61 | 62 | #' @export 63 | iv_format.data.frame <- function(x) { 64 | # Printing data would be too complicated, 65 | # just print a type summary and the row number 66 | abbr <- vec_ptype_abbr(x, suffix_shape = FALSE) 67 | 68 | out <- vec_paste0(abbr, "[", vec_seq_along(x), ",]") 69 | 70 | missing <- vec_detect_missing(x) 71 | if (any(missing)) { 72 | out[missing] <- "NA" 73 | } 74 | 75 | out 76 | } 77 | 78 | #' @export 79 | iv_format.Date <- function(x) { 80 | format(x, format = "%Y-%m-%d") 81 | } 82 | 83 | #' @export 84 | iv_format.POSIXt <- function(x) { 85 | format(x, format = "%Y-%m-%d %H:%M:%S") 86 | } 87 | 88 | #' @export 89 | iv_format.difftime <- function(x) { 90 | format(x, trim = TRUE, scientific = FALSE) 91 | } 92 | 93 | #' @export 94 | iv_format.integer64 <- function(x) { 95 | # We implement this for convenience, since it won't make it upstream. 96 | # It goes through `format.character()`, so we use that method. 97 | iv_format.character(x) 98 | } 99 | 100 | # ------------------------------------------------------------------------------ 101 | 102 | #' @export 103 | format.ivs_iv <- function(x, ...) { 104 | proxy <- iv_proxy(x) 105 | check_iv(proxy, arg = "x") 106 | 107 | start <- field_start(proxy) 108 | end <- field_end(proxy) 109 | 110 | start <- iv_format(start) 111 | end <- iv_format(end) 112 | 113 | out <- vec_paste0("[", start, ", ", end, ")") 114 | 115 | out 116 | } 117 | -------------------------------------------------------------------------------- /R/groups.R: -------------------------------------------------------------------------------- 1 | #' Group overlapping intervals 2 | #' 3 | #' @description 4 | #' This family of functions revolves around grouping overlapping intervals 5 | #' within a single iv. When multiple overlapping intervals are grouped together 6 | #' they result in a wider interval containing the smallest [iv_start()] and the 7 | #' largest [iv_end()] of the overlaps. 8 | #' 9 | #' - `iv_groups()` merges all overlapping intervals found within `x`. The 10 | #' resulting intervals are known as the "groups" of `x`. 11 | #' 12 | #' - `iv_identify_group()` identifies the group that the current interval of `x` 13 | #' falls in. This is particularly useful alongside [dplyr::group_by()]. 14 | #' 15 | #' - `iv_locate_groups()` returns a two column data frame with a `key` column 16 | #' containing the result of `iv_groups()` and a `loc` list-column containing 17 | #' integer vectors that map each interval in `x` to the group that it falls in. 18 | #' 19 | #' Optionally, you can choose _not_ to group abutting intervals together with 20 | #' `abutting = FALSE`, which can be useful if you'd like to retain those 21 | #' boundaries. 22 | #' 23 | #' ## Minimal interval vectors 24 | #' 25 | #' `iv_groups()` is particularly useful because it can generate a _minimal_ 26 | #' interval vector, which covers the range of an interval vector in the most 27 | #' compact form possible. In particular, a minimal interval vector: 28 | #' 29 | #' - Has no overlapping intervals 30 | #' - Has no abutting intervals 31 | #' - Is ordered on both `start` and `end` 32 | #' 33 | #' A minimal interval vector is allowed to have a single missing interval, 34 | #' which is located at the end of the vector. 35 | #' 36 | #' @section Graphical Representation: 37 | #' 38 | #' Graphically, generating groups looks like: 39 | #' 40 | #' ![](groups.png) 41 | #' 42 | #' With `abutting = FALSE`, intervals that touch aren't grouped: 43 | #' 44 | #' ![](groups-abutting-keep.png) 45 | #' 46 | #' @inheritParams rlang::args_dots_empty 47 | #' 48 | #' @param x `[iv]` 49 | #' 50 | #' An interval vector. 51 | #' 52 | #' @param abutting `[TRUE / FALSE]` 53 | #' 54 | #' Should abutting intervals be grouped together? 55 | #' 56 | #' If `TRUE`, `[a, b)` and `[b, c)` will merge as `[a, c)`. If `FALSE`, they 57 | #' will be kept separate. To be a minimal interval vector, all abutting 58 | #' intervals must be grouped together. 59 | #' 60 | #' @return 61 | #' - For `iv_groups()`, an iv with the same type as `x`. 62 | #' 63 | #' - For `iv_identify_group()`, an iv with the same type and size as `x`. 64 | #' 65 | #' - For `iv_locate_groups()`, a two column data frame with a `key` column 66 | #' containing the result of `iv_groups()` and a `loc` list-column containing 67 | #' integer vectors. 68 | #' 69 | #' @name iv-groups 70 | #' 71 | #' @examples 72 | #' library(dplyr, warn.conflicts = FALSE) 73 | #' 74 | #' x <- iv_pairs( 75 | #' c(1, 5), 76 | #' c(2, 3), 77 | #' c(NA, NA), 78 | #' c(5, 6), 79 | #' c(NA, NA), 80 | #' c(9, 12), 81 | #' c(11, 14) 82 | #' ) 83 | #' x 84 | #' 85 | #' # Grouping removes all redundancy while still covering the full range 86 | #' # of values that were originally represented. If any missing intervals 87 | #' # are present, a single one is retained. 88 | #' iv_groups(x) 89 | #' 90 | #' # Abutting intervals are typically grouped together, but you can choose not 91 | #' # to group them if you want to retain those boundaries 92 | #' iv_groups(x, abutting = FALSE) 93 | #' 94 | #' # `iv_identify_group()` is useful alongside `group_by()` and `summarize()` 95 | #' df <- tibble(x = x) 96 | #' df <- mutate(df, u = iv_identify_group(x)) 97 | #' df 98 | #' 99 | #' df %>% 100 | #' group_by(u) %>% 101 | #' summarize(n = n()) 102 | #' 103 | #' # The real workhorse here is `iv_locate_groups()`, which returns 104 | #' # the groups and information on which observations in `x` fall in which 105 | #' # group 106 | #' iv_locate_groups(x) 107 | NULL 108 | 109 | #' @rdname iv-groups 110 | #' @export 111 | iv_groups <- function(x, ..., abutting = TRUE) { 112 | check_dots_empty0(...) 113 | 114 | proxy <- iv_proxy(x) 115 | check_iv(proxy, arg = "x") 116 | 117 | start <- field_start(proxy) 118 | end <- field_end(proxy) 119 | 120 | groups <- vec_interval_groups( 121 | start = start, 122 | end = end, 123 | abutting = abutting 124 | ) 125 | 126 | start <- groups$start 127 | end <- groups$end 128 | 129 | out <- new_iv(start, end) 130 | out <- iv_restore(out, x) 131 | 132 | out 133 | } 134 | 135 | #' @rdname iv-groups 136 | #' @export 137 | iv_identify_group <- function(x, ..., abutting = TRUE) { 138 | check_dots_empty0(...) 139 | 140 | proxy <- iv_proxy(x) 141 | check_iv(proxy, arg = "x") 142 | 143 | start <- field_start(proxy) 144 | end <- field_end(proxy) 145 | 146 | groups <- vec_interval_locate_groups( 147 | start = start, 148 | end = end, 149 | abutting = abutting 150 | ) 151 | 152 | times <- list_sizes(groups$loc) 153 | 154 | loc <- list_unchop(groups$loc, ptype = integer(), name_spec = zap()) 155 | loc <- vec_order(loc) 156 | 157 | key <- groups$key 158 | key <- vec_rep_each(key, times = times) 159 | key <- vec_slice(key, loc) 160 | 161 | start <- key$start 162 | end <- key$end 163 | 164 | out <- new_iv(start, end) 165 | out <- iv_restore(out, x) 166 | 167 | out 168 | } 169 | 170 | #' @rdname iv-groups 171 | #' @export 172 | iv_locate_groups <- function(x, ..., abutting = TRUE) { 173 | check_dots_empty0(...) 174 | 175 | proxy <- iv_proxy(x) 176 | check_iv(proxy, arg = "x") 177 | 178 | start <- field_start(proxy) 179 | end <- field_end(proxy) 180 | 181 | out <- vec_interval_locate_groups( 182 | start = start, 183 | end = end, 184 | abutting = abutting 185 | ) 186 | 187 | start <- out$key$start 188 | end <- out$key$end 189 | 190 | key <- new_iv(start, end) 191 | key <- iv_restore(key, x) 192 | 193 | out[["key"]] <- key 194 | 195 | out 196 | } 197 | -------------------------------------------------------------------------------- /R/helper.R: -------------------------------------------------------------------------------- 1 | # An internal helper class used for testing, 2 | # but this lives here so we can register the S3 methods 3 | 4 | nested_integer_iv <- function(start, end) { 5 | out <- iv(start, end, ptype = integer()) 6 | new_nested_integer_iv(out) 7 | } 8 | 9 | nested_integer_iv_pairs <- function(...) { 10 | out <- iv_pairs(..., ptype = integer()) 11 | new_nested_integer_iv(out) 12 | } 13 | 14 | new_nested_integer_iv <- function(iv, ..., class = character()) { 15 | if (!is_iv(iv)) { 16 | abort("`iv` must be an .") 17 | } 18 | if (!is_bare_integer(iv_start(iv))) { 19 | abort("`iv_start(iv)` must be a bare integer.") 20 | } 21 | if (!is_bare_integer(iv_end(iv))) { 22 | abort("`iv_end(iv)` must be a bare integer.") 23 | } 24 | 25 | fields <- list(iv = iv) 26 | 27 | new_rcrd(fields, ..., class = c(class, "ivs_nested_integer_iv")) 28 | } 29 | 30 | #' @export 31 | vec_ptype_full.ivs_nested_integer_iv <- function(x, ...) { 32 | "nested_integer_iv" 33 | } 34 | 35 | #' @export 36 | format.ivs_nested_integer_iv <- function(x, ...) { 37 | format(field(x, "iv"), ...) 38 | } 39 | 40 | #' @export 41 | vec_ptype2.ivs_nested_integer_iv.ivs_nested_integer_iv <- function(x, y, ...) { 42 | iv <- new_iv(start = integer(), end = integer()) 43 | new_nested_integer_iv(iv = iv) 44 | } 45 | 46 | #' @export 47 | vec_cast.ivs_nested_integer_iv.ivs_nested_integer_iv <- function(x, to, ...) { 48 | x 49 | } 50 | 51 | #' @export 52 | iv_proxy.ivs_nested_integer_iv <- function(x, ...) { 53 | field(x, "iv") 54 | } 55 | 56 | #' @export 57 | iv_restore.ivs_nested_integer_iv <- function(x, to, ...) { 58 | new_nested_integer_iv(x) 59 | } 60 | -------------------------------------------------------------------------------- /R/ivs-deprecated.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # ivs 0.2.0 3 | 4 | # ivs 0.2.0: Deprecated 5 | 6 | #' Set operations 7 | #' 8 | #' @description 9 | #' `r lifecycle::badge("deprecated")` 10 | #' 11 | #' These functions are deprecated in favor of their `set_` prefixed equivalents. 12 | #' 13 | #' - `iv_complement()` -> [iv_set_complement()] 14 | #' - `iv_union()` -> [iv_set_union()] 15 | #' - `iv_intersect()` -> [iv_set_intersect()] 16 | #' - `iv_difference()` -> [iv_set_difference()] 17 | #' - `iv_symmetric_difference()` -> [iv_set_symmetric_difference()] 18 | #' 19 | #' @inheritParams iv-sets 20 | #' 21 | #' @name iv-sets-deprecated 22 | #' @keywords internal 23 | #' 24 | NULL 25 | 26 | #' @export 27 | #' @rdname iv-sets-deprecated 28 | iv_complement <- function(x, ..., lower = NULL, upper = NULL) { 29 | lifecycle::deprecate_warn( 30 | when = "0.2.0", 31 | what = "iv_complement()", 32 | with = "iv_set_complement()", 33 | always = TRUE 34 | ) 35 | iv_set_complement(x, ..., lower = lower, upper = upper) 36 | } 37 | 38 | #' @export 39 | #' @rdname iv-sets-deprecated 40 | iv_union <- function(x, y) { 41 | lifecycle::deprecate_warn( 42 | when = "0.2.0", 43 | what = "iv_union()", 44 | with = "iv_set_union()", 45 | always = TRUE 46 | ) 47 | iv_set_union(x, y) 48 | } 49 | 50 | #' @export 51 | #' @rdname iv-sets-deprecated 52 | iv_intersect <- function(x, y) { 53 | lifecycle::deprecate_warn( 54 | when = "0.2.0", 55 | what = "iv_intersect()", 56 | with = "iv_set_intersect()", 57 | always = TRUE 58 | ) 59 | iv_set_intersect(x, y) 60 | } 61 | 62 | #' @export 63 | #' @rdname iv-sets-deprecated 64 | iv_difference <- function(x, y) { 65 | lifecycle::deprecate_warn( 66 | when = "0.2.0", 67 | what = "iv_difference()", 68 | with = "iv_set_difference()", 69 | always = TRUE 70 | ) 71 | iv_set_difference(x, y) 72 | } 73 | 74 | #' @export 75 | #' @rdname iv-sets-deprecated 76 | iv_symmetric_difference <- function(x, y) { 77 | lifecycle::deprecate_warn( 78 | when = "0.2.0", 79 | what = "iv_symmetric_difference()", 80 | with = "iv_set_symmetric_difference()", 81 | always = TRUE 82 | ) 83 | iv_set_symmetric_difference(x, y) 84 | } 85 | 86 | #' Pairwise set operations 87 | #' 88 | #' @description 89 | #' `r lifecycle::badge("deprecated")` 90 | #' 91 | #' These functions are deprecated in favor of their `set_` prefixed equivalents. 92 | #' 93 | #' - `iv_pairwise_complement()` -> [iv_pairwise_set_complement()] 94 | #' - `iv_pairwise_union()` -> [iv_pairwise_set_union()] 95 | #' - `iv_pairwise_intersect()` -> [iv_pairwise_set_intersect()] 96 | #' - `iv_pairwise_difference()` -> [iv_pairwise_set_difference()] 97 | #' - `iv_pairwise_symmetric_difference()` -> [iv_pairwise_set_symmetric_difference()] 98 | #' 99 | #' @inheritParams iv-set-pairwise 100 | #' 101 | #' @name iv-set-pairwise-deprecated 102 | #' @keywords internal 103 | #' 104 | NULL 105 | 106 | #' @export 107 | #' @rdname iv-set-pairwise-deprecated 108 | iv_pairwise_complement <- function(x, y) { 109 | lifecycle::deprecate_warn( 110 | when = "0.2.0", 111 | what = "iv_pairwise_complement()", 112 | with = "iv_pairwise_set_complement()", 113 | always = TRUE 114 | ) 115 | iv_pairwise_set_complement(x, y) 116 | } 117 | 118 | #' @export 119 | #' @rdname iv-set-pairwise-deprecated 120 | iv_pairwise_union <- function(x, y) { 121 | lifecycle::deprecate_warn( 122 | when = "0.2.0", 123 | what = "iv_pairwise_union()", 124 | with = "iv_pairwise_set_union()", 125 | always = TRUE 126 | ) 127 | iv_pairwise_set_union(x, y) 128 | } 129 | 130 | #' @export 131 | #' @rdname iv-set-pairwise-deprecated 132 | iv_pairwise_intersect <- function(x, y) { 133 | lifecycle::deprecate_warn( 134 | when = "0.2.0", 135 | what = "iv_pairwise_intersect()", 136 | with = "iv_pairwise_set_intersect()", 137 | always = TRUE 138 | ) 139 | iv_pairwise_set_intersect(x, y) 140 | } 141 | 142 | #' @export 143 | #' @rdname iv-set-pairwise-deprecated 144 | iv_pairwise_difference <- function(x, y) { 145 | lifecycle::deprecate_warn( 146 | when = "0.2.0", 147 | what = "iv_pairwise_difference()", 148 | with = "iv_pairwise_set_difference()", 149 | always = TRUE 150 | ) 151 | iv_pairwise_set_difference(x, y) 152 | } 153 | 154 | #' @export 155 | #' @rdname iv-set-pairwise-deprecated 156 | iv_pairwise_symmetric_difference <- function(x, y) { 157 | lifecycle::deprecate_warn( 158 | when = "0.2.0", 159 | what = "iv_pairwise_symmetric_difference()", 160 | with = "iv_pairwise_set_symmetric_difference()", 161 | always = TRUE 162 | ) 163 | iv_pairwise_set_symmetric_difference(x, y) 164 | } 165 | -------------------------------------------------------------------------------- /R/ivs-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @import rlang 6 | #' @import vctrs 7 | #' @importFrom glue glue 8 | #' @importFrom glue glue_collapse 9 | ## usethis namespace: end 10 | NULL 11 | 12 | # Singletons 13 | the <- new_environment() 14 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | vec_paste0 <- function(...) { 2 | # Use tidyverse recycling rules to avoid size zero recycling bugs 3 | args <- vec_recycle_common(...) 4 | exec(paste0, !!!args) 5 | } 6 | 7 | vec_run_sizes_to_starts <- function(sizes) { 8 | n_sizes <- length(sizes) 9 | 10 | if (n_sizes > 0L) { 11 | cumsum(c(1L, sizes[-n_sizes])) 12 | } else { 13 | integer() 14 | } 15 | } 16 | 17 | err_locs <- function(x) { 18 | if (!is.integer(x)) { 19 | abort("`x` must be an integer vector of locations.", .internal = TRUE) 20 | } 21 | 22 | size <- length(x) 23 | 24 | if (size == 0L) { 25 | abort("`x` must have at least 1 location.", .internal = TRUE) 26 | } else if (size == 1L) { 27 | glue("`{x}`") 28 | } else if (size <= 5L) { 29 | x <- glue_collapse(x, sep = ", ") 30 | glue("`c({x})`") 31 | } else { 32 | x <- x[1:5] 33 | x <- glue_collapse(x, sep = ", ") 34 | glue("`c({x})` and {size - 5L} more") 35 | } 36 | } 37 | 38 | obj_s3_method_exists <- function(x, generic) { 39 | !is_null(obj_s3_method_lookup(x, generic)) 40 | } 41 | 42 | obj_s3_method_lookup <- function(x, generic) { 43 | if (!is.object(x)) { 44 | return(NULL) 45 | } 46 | 47 | classes <- class(x) 48 | 49 | if (!is_character(classes)) { 50 | abort("`class(x)` didn't return a character vector.", .internal = TRUE) 51 | } 52 | 53 | for (class in classes) { 54 | method <- paste0(generic, ".", class) 55 | method <- s3_method_get(method) 56 | 57 | if (!is_null(method)) { 58 | return(method) 59 | } 60 | } 61 | 62 | NULL 63 | } 64 | 65 | s3_method_get <- function(name) { 66 | # Try global env first in case the user registered a method interactively 67 | env <- global_env() 68 | fn <- env_get(env, name, default = NULL) 69 | 70 | if (is_function(fn)) { 71 | return(fn) 72 | } 73 | 74 | # Then try the package S3 methods table 75 | env <- the$env_s3_methods_table 76 | fn <- env_get(env, name, default = NULL) 77 | 78 | if (is_function(fn)) { 79 | return(fn) 80 | } 81 | 82 | # Symbol not bound to the `env`, or it was bound to a non-function 83 | NULL 84 | } 85 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # nocov start 2 | 3 | .onLoad <- function(libname, pkgname) { 4 | env_ns <- ns_env(pkgname) 5 | 6 | the$env_ns <- env_ns 7 | the$env_s3_methods_table <- env_ns[[".__S3MethodsTable__."]] 8 | 9 | env_bind( 10 | .env = env_ns, 11 | vec_interval_groups = import_vctrs("exp_vec_interval_groups"), 12 | vec_interval_locate_groups = import_vctrs("exp_vec_interval_locate_groups"), 13 | vec_interval_complement = import_vctrs("exp_vec_interval_complement"), 14 | vec_interval_locate_containers = import_vctrs("exp_vec_interval_locate_containers") 15 | ) 16 | } 17 | 18 | import_vctrs <- function(name) { 19 | import_from(name, "vctrs") 20 | } 21 | import_from <- function(name, package) { 22 | ns <- getNamespace(package) 23 | 24 | if (!exists(name, mode = "function", envir = ns, inherits = FALSE)) { 25 | abort(sprintf("No such '%s' function: `%s()`.", package, name)) 26 | } 27 | 28 | get(name, mode = "function", envir = ns, inherits = FALSE) 29 | } 30 | 31 | # nocov end 32 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | editor_options: 4 | chunk_output_type: console 5 | --- 6 | 7 | 8 | 9 | ```{r, include = FALSE} 10 | knitr::opts_chunk$set( 11 | collapse = TRUE, 12 | comment = "#>", 13 | fig.path = "man/figures/README-", 14 | out.width = "100%" 15 | ) 16 | ``` 17 | 18 | # ivs 19 | 20 | 21 | 22 | [![Codecov test coverage](https://codecov.io/gh/DavisVaughan/ivs/branch/main/graph/badge.svg)](https://app.codecov.io/gh/DavisVaughan/ivs?branch=main) [![R-CMD-check](https://github.com/DavisVaughan/ivs/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/DavisVaughan/ivs/actions/workflows/R-CMD-check.yaml) 23 | 24 | 25 | 26 | ivs (said, "eye-vees") is a package dedicated to working with intervals in a generic way. 27 | It introduces a new type, the **i**nterval **v**ector, which is generally referred to as an **iv**. 28 | An iv is created from two parallel vectors representing the starts (inclusive) and ends (exclusive) of the intervals, like this: 29 | 30 | ```{r} 31 | library(ivs) 32 | 33 | # Interval vector of integers 34 | iv(1:5, 7:11) 35 | 36 | # Interval vector of dates 37 | starts <- as.Date("2019-01-01") + 0:2 38 | ends <- starts + c(2, 5, 10) 39 | 40 | iv(starts, ends) 41 | ``` 42 | 43 | There are a number of useful things you can do with these, including: 44 | 45 | - Determining how two ivs are related (i.e. does one precede, follow, or overlap the other?) with `iv_locate_overlaps()`. 46 | 47 | - Grouping / Merging overlapping intervals within a single iv with `iv_groups()`. 48 | 49 | - Splitting an iv on its overlapping endpoints with `iv_splits()`. 50 | 51 | - Applying set theoretical operations on two ivs, such as `iv_set_intersect()`. 52 | 53 | Interval vectors are completely generic, meaning that you can create them from any comparable type that is supported by [vctrs](https://vctrs.r-lib.org). 54 | This means that user defined S3 types work automatically, like `hms::hms()`, `bit64::integer64()`, and `clock::year_month_day()`. 55 | 56 | ## Learning 57 | 58 | The best way to learn about ivs is by reading the [Getting Started](https://davisvaughan.github.io/ivs/articles/ivs.html) vignette! 59 | 60 | ## Installation 61 | 62 | Install the released version from [CRAN](https://CRAN.R-project.org) with: 63 | 64 | ``` r 65 | install.packages("ivs") 66 | ``` 67 | 68 | You can install the development version of ivs from [GitHub](https://github.com/) with: 69 | 70 | ``` r 71 | # install.packages("pak") 72 | pak::pak("DavisVaughan/ivs") 73 | ``` 74 | 75 | ## Inspiration 76 | 77 | This package was inspired by many sources! 78 | 79 | - [IRanges](https://github.com/Bioconductor/IRanges) is the closest equivalent, and inspired many of the function names seen here. 80 | It is mainly focused on integer intervals, and always uses closed intervals. 81 | It is also based on S4, and unfortunately that currently means it can't be used as a column in a tibble with the current limitations in vctrs. 82 | 83 | - [intervals](https://github.com/edzer/intervals) is another R package that supports intervals. 84 | It supports integer/numeric intervals and allows for varying the endpoint bounds. 85 | 86 | - [data.table](https://github.com/Rdatatable/data.table) contains a function named `foverlaps()` for detecting overlaps (which was inspired by `IRanges::findOverlaps()`). 87 | 88 | - [Maintaining Knowledge about Temporal Intervals](https://cse.unl.edu/~choueiry/Documents/Allen-CACM1983.pdf) is a paper by James Allen that `iv_locate_relates()` is based on. 89 | It is a great primer on interval algebra. 90 | 91 | - [Why numbering should start at 0](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) is a small white paper that describes why right-open intervals are often the best choice. 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ivs 5 | 6 | 7 | 8 | [![Codecov test 9 | coverage](https://codecov.io/gh/DavisVaughan/ivs/branch/main/graph/badge.svg)](https://app.codecov.io/gh/DavisVaughan/ivs?branch=main) 10 | [![R-CMD-check](https://github.com/DavisVaughan/ivs/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/DavisVaughan/ivs/actions/workflows/R-CMD-check.yaml) 11 | 12 | 13 | 14 | ivs (said, “eye-vees”) is a package dedicated to working with intervals 15 | in a generic way. It introduces a new type, the **i**nterval **v**ector, 16 | which is generally referred to as an **iv**. An iv is created from two 17 | parallel vectors representing the starts (inclusive) and ends 18 | (exclusive) of the intervals, like this: 19 | 20 | ``` r 21 | library(ivs) 22 | 23 | # Interval vector of integers 24 | iv(1:5, 7:11) 25 | #> [5]> 26 | #> [1] [1, 7) [2, 8) [3, 9) [4, 10) [5, 11) 27 | 28 | # Interval vector of dates 29 | starts <- as.Date("2019-01-01") + 0:2 30 | ends <- starts + c(2, 5, 10) 31 | 32 | iv(starts, ends) 33 | #> [3]> 34 | #> [1] [2019-01-01, 2019-01-03) [2019-01-02, 2019-01-07) [2019-01-03, 2019-01-13) 35 | ``` 36 | 37 | There are a number of useful things you can do with these, including: 38 | 39 | - Determining how two ivs are related (i.e. does one precede, follow, or 40 | overlap the other?) with `iv_locate_overlaps()`. 41 | 42 | - Grouping / Merging overlapping intervals within a single iv with 43 | `iv_groups()`. 44 | 45 | - Splitting an iv on its overlapping endpoints with `iv_splits()`. 46 | 47 | - Applying set theoretical operations on two ivs, such as 48 | `iv_set_intersect()`. 49 | 50 | Interval vectors are completely generic, meaning that you can create 51 | them from any comparable type that is supported by 52 | [vctrs](https://vctrs.r-lib.org). This means that user defined S3 types 53 | work automatically, like `hms::hms()`, `bit64::integer64()`, and 54 | `clock::year_month_day()`. 55 | 56 | ## Learning 57 | 58 | The best way to learn about ivs is by reading the [Getting 59 | Started](https://davisvaughan.github.io/ivs/articles/ivs.html) vignette! 60 | 61 | ## Installation 62 | 63 | Install the released version from [CRAN](https://CRAN.R-project.org) 64 | with: 65 | 66 | ``` r 67 | install.packages("ivs") 68 | ``` 69 | 70 | You can install the development version of ivs from 71 | [GitHub](https://github.com/) with: 72 | 73 | ``` r 74 | # install.packages("pak") 75 | pak::pak("DavisVaughan/ivs") 76 | ``` 77 | 78 | ## Inspiration 79 | 80 | This package was inspired by many sources! 81 | 82 | - [IRanges](https://github.com/Bioconductor/IRanges) is the closest 83 | equivalent, and inspired many of the function names seen here. It is 84 | mainly focused on integer intervals, and always uses closed intervals. 85 | It is also based on S4, and unfortunately that currently means it 86 | can’t be used as a column in a tibble with the current limitations in 87 | vctrs. 88 | 89 | - [intervals](https://github.com/edzer/intervals) is another R package 90 | that supports intervals. It supports integer/numeric intervals and 91 | allows for varying the endpoint bounds. 92 | 93 | - [data.table](https://github.com/Rdatatable/data.table) contains a 94 | function named `foverlaps()` for detecting overlaps (which was 95 | inspired by `IRanges::findOverlaps()`). 96 | 97 | - [Maintaining Knowledge about Temporal 98 | Intervals](https://cse.unl.edu/~choueiry/Documents/Allen-CACM1983.pdf) 99 | is a paper by James Allen that `iv_locate_relates()` is based on. It 100 | is a great primer on interval algebra. 101 | 102 | - [Why numbering should start at 103 | 0](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) 104 | is a small white paper that describes why right-open intervals are 105 | often the best choice. 106 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://davisvaughan.github.io/ivs/ 2 | 3 | template: 4 | bootstrap: 5 5 | 6 | development: 7 | mode: auto 8 | 9 | reference: 10 | - title: Interval vectors 11 | contents: 12 | - iv 13 | - iv_diff 14 | - iv_start 15 | 16 | - title: Core operations 17 | contents: 18 | - iv_groups 19 | - iv_splits 20 | - iv_containers 21 | - iv_span 22 | - iv_pairwise_span 23 | 24 | - title: Relationships between two ivs 25 | contents: 26 | - iv_overlaps 27 | - iv_locate_overlaps 28 | - iv_count_overlaps 29 | - iv_pairwise_overlaps 30 | - iv_align 31 | 32 | - title: Relationships between a vector and an iv 33 | contents: 34 | - iv_between 35 | - iv_locate_between 36 | - iv_count_between 37 | - iv_pairwise_between 38 | 39 | - title: Set operations 40 | contents: 41 | - iv_set_complement 42 | - iv_pairwise_set_complement 43 | 44 | - title: Allen's Interval Algebra 45 | contents: 46 | - iv_relates 47 | - iv_locate_relates 48 | - iv_count_relates 49 | - iv_pairwise_relates 50 | 51 | - title: Developer tools 52 | contents: 53 | - new_iv 54 | - is_iv 55 | - iv_proxy 56 | - iv_format 57 | -------------------------------------------------------------------------------- /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 | There are no reverse dependencies and no breaking changes. 2 | -------------------------------------------------------------------------------- /ivs.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/allen-relation-detect-pairwise.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/relation.R 3 | \name{allen-relation-detect-pairwise} 4 | \alias{allen-relation-detect-pairwise} 5 | \alias{iv_pairwise_relates} 6 | \title{Pairwise detect relations from Allen's Interval Algebra} 7 | \usage{ 8 | iv_pairwise_relates(x, y, ..., type) 9 | } 10 | \arguments{ 11 | \item{x, y}{\verb{[iv]} 12 | 13 | A pair of interval vectors. 14 | 15 | These will be recycled against each other and cast to the same type.} 16 | 17 | \item{...}{These dots are for future extensions and must be empty.} 18 | 19 | \item{type}{\verb{[character(1)]} 20 | 21 | The type of relationship to find. See the Allen's Interval Algebra section 22 | for a complete description of each type. One of: 23 | \itemize{ 24 | \item \code{"precedes"} 25 | \item \code{"preceded-by"} 26 | \item \code{"meets"} 27 | \item \code{"met-by"} 28 | \item \code{"overlaps"} 29 | \item \code{"overlapped-by"} 30 | \item \code{"starts"} 31 | \item \code{"started-by"} 32 | \item \code{"during"} 33 | \item \code{"contains"} 34 | \item \code{"finishes"} 35 | \item \code{"finished-by"} 36 | \item \code{"equals"} 37 | }} 38 | } 39 | \value{ 40 | A logical vector the same size as the common size of \code{x} and \code{y}. 41 | } 42 | \description{ 43 | \code{iv_pairwise_relates()} is similar to 44 | \code{\link[=iv_pairwise_overlaps]{iv_pairwise_overlaps()}}, but it detects a specific set of relations 45 | developed by James Allen in the paper: \href{http://cse.unl.edu/~choueiry/Documents/Allen-CACM1983.pdf}{Maintaining Knowledge about Temporal Intervals}. 46 | } 47 | \section{Allen's Interval Algebra}{ 48 | 49 | 50 | The interval algebra developed by James Allen serves as a basis and 51 | inspiration for \code{\link[=iv_locate_overlaps]{iv_locate_overlaps()}}, \code{\link[=iv_locate_precedes]{iv_locate_precedes()}}, and 52 | \code{\link[=iv_locate_follows]{iv_locate_follows()}}. The original algebra is composed of 13 relations 53 | which have the following properties: 54 | \itemize{ 55 | \item Distinct: No pair of intervals can be related by more than one \code{type}. 56 | \item Exhaustive: All pairs of intervals are described by one of the \code{type}s. 57 | \item Qualitative: No numeric intervals are considered. The relationships are 58 | computed by purely qualitative means. 59 | } 60 | 61 | Take the notation that \code{x} and \code{y} represent two intervals. Now assume that 62 | \code{x} can be represented as \verb{[x_s, x_e)}, where \code{x_s} is the start of the 63 | interval and \code{x_e} is the end of it. Additionally, assume that \code{x_s < x_e}. 64 | With this notation, the 13 relations are as follows: 65 | \itemize{ 66 | \item \emph{Precedes}: 67 | 68 | \code{x_e < y_s} 69 | \item \emph{Preceded-by}: 70 | 71 | \code{x_s > y_e} 72 | \item \emph{Meets}: 73 | 74 | \code{x_e == y_s} 75 | \item \emph{Met-by}: 76 | 77 | \code{x_s == y_e} 78 | \item \emph{Overlaps}: 79 | 80 | \code{(x_s < y_s) & (x_e > y_s) & (x_e < y_e)} 81 | \item \emph{Overlapped-by}: 82 | 83 | \code{(x_e > y_e) & (x_s < y_e) & (x_s > y_s)} 84 | \item \emph{Starts}: 85 | 86 | \code{(x_s == y_s) & (x_e < y_e)} 87 | \item \emph{Started-by}: 88 | 89 | \code{(x_s == y_s) & (x_e > y_e)} 90 | \item \emph{Finishes}: 91 | 92 | \code{(x_s > y_s) & (x_e == y_e)} 93 | \item \emph{Finished-by}: 94 | 95 | \code{(x_s < y_s) & (x_e == y_e)} 96 | \item \emph{During}: 97 | 98 | \code{(x_s > y_s) & (x_e < y_e)} 99 | \item \emph{Contains}: 100 | 101 | \code{(x_s < y_s) & (x_e > y_e)} 102 | \item \emph{Equals}: 103 | 104 | \code{(x_s == y_s) & (x_e == y_e)} 105 | } 106 | 107 | Note that when \code{missing = "equals"}, missing intervals will only match 108 | the \code{type = "equals"} relation. This ensures that the distinct property 109 | of the algebra is maintained. 110 | \subsection{Connection to other functions}{ 111 | 112 | Note that some of the above relations are fairly restrictive. For example, 113 | \code{"overlaps"} only detects cases where \code{x} straddles \code{y_s}. It does not 114 | consider the case where \code{x} and \code{y} are equal to be an overlap (as this 115 | is \code{"equals"}) nor does it consider when \code{x} straddles \code{y_e} to be an 116 | overlap (as this is \code{"overlapped-by"}). This makes the relations extremely 117 | useful from a theoretical perspective, because they can be combined without 118 | fear of duplicating relations, but they don't match our typical expectations 119 | for what an "overlap" is. 120 | 121 | \code{\link[=iv_locate_overlaps]{iv_locate_overlaps()}}, \code{\link[=iv_locate_precedes]{iv_locate_precedes()}}, and \code{\link[=iv_locate_follows]{iv_locate_follows()}} use 122 | more intuitive \code{type}s that aren't distinct, but typically match your 123 | expectations better. They can each be expressed in terms of Allen's 124 | relations: 125 | \itemize{ 126 | \item \code{iv_locate_overlaps()}: 127 | \itemize{ 128 | \item \code{"any"}: 129 | 130 | \code{overlaps | overlapped-by | starts | started-by | finishes | finished-by | during | contains | equals} 131 | \item \code{"contains"}: 132 | 133 | \code{contains | started-by | finished-by | equals} 134 | \item \code{"within"}: 135 | 136 | \code{during | starts | finishes | equals} 137 | \item \code{"starts"}: 138 | 139 | \code{starts | started-by | equals} 140 | \item \code{"ends"}: 141 | 142 | \code{finishes | finished-by | equals} 143 | \item \code{"equals"}: 144 | 145 | \code{equals} 146 | } 147 | \item \code{iv_locate_precedes()}: 148 | 149 | \code{precedes | meets} 150 | \item \code{iv_locate_follows()}: 151 | 152 | \code{preceded-by | met-by} 153 | } 154 | } 155 | } 156 | 157 | \examples{ 158 | x <- iv_pairs(c(1, 3), c(3, 5)) 159 | y <- iv_pairs(c(3, 4), c(6, 7)) 160 | 161 | # `"precedes"` is strict, and doesn't let the endpoints match 162 | iv_pairwise_relates(x, y, type = "precedes") 163 | 164 | # Since that is what `"meets"` represents 165 | iv_pairwise_relates(x, y, type = "meets") 166 | 167 | # `"during"` only matches when `x` is completely contained in `y`, and 168 | # doesn't allow any endpoints to match 169 | x <- iv_pairs(c(1, 3), c(4, 5), c(8, 9)) 170 | y <- iv_pairs(c(1, 4), c(3, 8), c(8, 9)) 171 | 172 | iv_pairwise_relates(x, y, type = "during") 173 | } 174 | \seealso{ 175 | \link[=relation-locate]{Locating relationships} 176 | 177 | \link[=allen-relation-locate]{Locating relations from Allen's Interval Algebra} 178 | 179 | \link[=allen-relation-detect]{Detecting relations from Allen's Interval Algebra} 180 | } 181 | -------------------------------------------------------------------------------- /man/figures/complement-upper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/complement-upper.png -------------------------------------------------------------------------------- /man/figures/complement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/complement.png -------------------------------------------------------------------------------- /man/figures/groups-abutting-keep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/groups-abutting-keep.png -------------------------------------------------------------------------------- /man/figures/groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/groups.png -------------------------------------------------------------------------------- /man/figures/intersect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/intersect.png -------------------------------------------------------------------------------- /man/figures/iv.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/iv.graffle -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: archived 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | archived 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: defunct 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | defunct 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: experimental 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | experimental 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: maturing 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | maturing 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: questioning 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | questioning 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: soft-deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | soft-deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/splits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DavisVaughan/ivs/a93a4467a6f15e6831d16908ae97f9adbd209e0e/man/figures/splits.png -------------------------------------------------------------------------------- /man/is_iv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iv.R 3 | \name{is_iv} 4 | \alias{is_iv} 5 | \title{Is \code{x} an iv?} 6 | \usage{ 7 | is_iv(x) 8 | } 9 | \arguments{ 10 | \item{x}{\verb{[object]} 11 | 12 | An object.} 13 | } 14 | \value{ 15 | A single \code{TRUE} or \code{FALSE}. 16 | } 17 | \description{ 18 | \code{is_iv()} tests if \code{x} is an iv object. 19 | } 20 | \examples{ 21 | is_iv(1) 22 | is_iv(new_iv(1, 2)) 23 | } 24 | -------------------------------------------------------------------------------- /man/iv-accessors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iv.R 3 | \name{iv-accessors} 4 | \alias{iv-accessors} 5 | \alias{iv_start} 6 | \alias{iv_end} 7 | \title{Access the start or end of an interval vector} 8 | \usage{ 9 | iv_start(x) 10 | 11 | iv_end(x) 12 | } 13 | \arguments{ 14 | \item{x}{\verb{[iv]} 15 | 16 | An interval vector.} 17 | } 18 | \value{ 19 | The start or end of \code{x}. 20 | } 21 | \description{ 22 | \itemize{ 23 | \item \code{iv_start()} accesses the start of an interval vector. 24 | \item \code{iv_end()} accesses the end of an interval vector. 25 | } 26 | } 27 | \examples{ 28 | x <- new_iv(1, 2) 29 | 30 | iv_start(x) 31 | iv_end(x) 32 | } 33 | -------------------------------------------------------------------------------- /man/iv-containers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/containers.R 3 | \name{iv-containers} 4 | \alias{iv-containers} 5 | \alias{iv_containers} 6 | \alias{iv_identify_containers} 7 | \alias{iv_identify_container} 8 | \alias{iv_locate_containers} 9 | \title{Containers} 10 | \usage{ 11 | iv_containers(x) 12 | 13 | iv_identify_containers(x) 14 | 15 | iv_identify_container(x) 16 | 17 | iv_locate_containers(x) 18 | } 19 | \arguments{ 20 | \item{x}{\verb{[iv]} 21 | 22 | An interval vector.} 23 | } 24 | \value{ 25 | \itemize{ 26 | \item For \code{iv_containers()}, an iv with the same type as \code{x}. 27 | \item For \code{iv_identify_containers()}, a list-of containing ivs with the same size 28 | as \code{x}. 29 | \item For \code{iv_identify_container()}, an iv with the same type as \code{x}. 30 | \item For \code{iv_locate_containers()}, a two column data frame with a \code{key} column 31 | containing the result of \code{iv_containers()} and a \code{loc} list-column containing 32 | integer vectors. 33 | } 34 | } 35 | \description{ 36 | This family of functions revolves around computing interval \emph{containers}. 37 | A container is defined as the widest interval that isn't contained by any 38 | other interval. 39 | \itemize{ 40 | \item \code{iv_containers()} returns all of the containers found within \code{x}. 41 | \item \code{iv_identify_containers()} identifies the containers that each interval in 42 | \code{x} falls in. It replaces \code{x} with a list of the same size where each element 43 | of the list contains the containers that the corresponding interval in \code{x} 44 | falls in. This is particularly useful alongside \code{\link[tidyr:unnest]{tidyr::unnest()}}. 45 | \item \code{iv_identify_container()} is similar in spirit to 46 | \code{iv_identify_containers()}, but is useful when you suspect that each interval 47 | in \code{x} is contained within exactly 1 container. It replaces \code{x} with an iv of 48 | the same size where each interval is the container that the corresponding 49 | interval in \code{x} falls in. If any interval falls in more than one container, 50 | an error is thrown. 51 | \item \code{iv_locate_containers()} returns a two column data frame with a \code{key} 52 | column containing the result of \code{iv_containers()} and a \code{loc} list-column 53 | containing integer vectors that map each interval in \code{x} to the container 54 | that it falls in. 55 | } 56 | } 57 | \examples{ 58 | library(dplyr, warn.conflicts = FALSE) 59 | library(tidyr) 60 | 61 | x <- iv_pairs( 62 | c(4, 6), 63 | c(1, 5), 64 | c(2, 3), 65 | c(NA, NA), 66 | c(NA, NA), 67 | c(9, 12), 68 | c(9, 14) 69 | ) 70 | x 71 | 72 | # Containers are intervals which aren't contained in any other interval. 73 | # They are always returned in ascending order. 74 | # If any missing intervals are present, a single one is retained. 75 | iv_containers(x) 76 | 77 | # `iv_identify_container()` is useful alongside `group_by()` and 78 | # `summarize()` if you know that each interval is contained within exactly 79 | # 1 container 80 | df <- tibble(x = x) 81 | df <- mutate(df, container = iv_identify_container(x)) 82 | df 83 | 84 | df \%>\% 85 | group_by(container) \%>\% 86 | summarize(n = n()) 87 | 88 | # If any interval is contained within multiple containers, 89 | # then you can't use `iv_identify_container()` 90 | y <- c(x, iv_pairs(c(0, 3), c(8, 13))) 91 | y 92 | 93 | try(iv_identify_container(y)) 94 | 95 | # Instead, use `iv_identify_containers()` to identify every container 96 | # that each interval falls in 97 | df <- tibble(y = y, container = iv_identify_containers(y)) 98 | df 99 | 100 | # You can use `tidyr::unchop()` to see the containers that each interval 101 | # falls in 102 | df \%>\% 103 | mutate(row = row_number(), .before = 1) \%>\% 104 | unchop(container) 105 | 106 | # A more programmatic interface to `iv_identify_containers()` is 107 | # `iv_locate_containers()`, which returns the containers you get from 108 | # `iv_containers()` alongside the locations in the input that they contain. 109 | iv_locate_containers(y) 110 | } 111 | -------------------------------------------------------------------------------- /man/iv-genericity.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iv.R 3 | \name{iv-genericity} 4 | \alias{iv-genericity} 5 | \alias{iv_proxy} 6 | \alias{iv_restore} 7 | \title{Proxy and restore} 8 | \usage{ 9 | iv_proxy(x, ...) 10 | 11 | iv_restore(x, to, ...) 12 | } 13 | \arguments{ 14 | \item{x}{\verb{[vector]} 15 | 16 | A vector.} 17 | 18 | \item{...}{These dots are for future extensions and must be empty.} 19 | 20 | \item{to}{\verb{[vector]} 21 | 22 | The original vector to restore to.} 23 | } 24 | \value{ 25 | \itemize{ 26 | \item \code{iv_proxy()} should return an iv object for further manipulation. 27 | \item \code{iv_restore()} should return an object of type \code{to}, if possible. In 28 | some cases, it may be required to fall back to returning an iv object. 29 | } 30 | } 31 | \description{ 32 | \itemize{ 33 | \item \code{iv_proxy()} is an S3 generic which allows you to write S3 methods for 34 | iv extension types to ensure that they are treated like iv objects. The 35 | input will be your iv extension object, \code{x}, and the return value should 36 | be an iv object. 37 | \item \code{iv_restore()} is an S3 generic that dispatches off \code{to} that allows you 38 | to restore a proxied iv extension type back to its original type. The 39 | inputs will be a bare iv object, \code{x}, and your original iv extension 40 | object, \code{to}, and the return value should correspond to \code{x} restored to 41 | the type of \code{to}, if possible. 42 | } 43 | 44 | You typically \emph{don't} need to create an \code{iv_proxy()} method if your class 45 | directly extends iv through the \code{class} argument of \code{\link[=new_iv]{new_iv()}}. You only 46 | need to implement this if your class has a different structure than a 47 | typical iv object. In particular, if \code{vctrs::field(x, "start")} and 48 | \code{vctrs::field(x, "end")} don't return the \code{start} and \code{end} of the interval 49 | vector respectively, then you probably need an \code{iv_proxy()} method. 50 | 51 | You typically \emph{do} need an \code{iv_restore()} method for custom iv extensions. 52 | If your class is simple, then you can generally just call your constructor, 53 | like \code{new_my_iv()}, to restore the class and any additional attributes that 54 | might be required. If your class doesn't use \code{\link[=new_iv]{new_iv()}}, then an 55 | \code{iv_restore()} method is mandatory, as this is one of the ways that ivs 56 | detects that your class is compatible with ivs. 57 | 58 | This system allows you to use any \verb{iv_*()} function on your iv extension 59 | object without having to define S3 methods for all of them. 60 | 61 | Note that the default method for \code{iv_proxy()} returns its input unchanged, 62 | even if it isn't an iv. Each \verb{iv_*()} function does separate checking to 63 | ensure that the proxy is a valid iv, or implements an alternate behavior if 64 | no proxy method is implemented. In contrast, \code{iv_restore()} will error if a 65 | method for \code{to} isn't registered. 66 | } 67 | \examples{ 68 | if (FALSE) { 69 | # Registering S3 methods outside of a package doesn't always work quite 70 | # right (like on the pkgdown site), so this code should only be run by a 71 | # user reading the manual. If that is you, fear not! It should run just fine 72 | # in your console. 73 | 74 | library(vctrs) 75 | 76 | new_nested_iv <- function(iv) { 77 | fields <- list(iv = iv) 78 | new_rcrd(fields, class = "nested_iv") 79 | } 80 | 81 | format.nested_iv <- function(x, ...) { 82 | format(field(x, "iv")) 83 | } 84 | 85 | iv_proxy.nested_iv <- function(x, ...) { 86 | field(x, "iv") 87 | } 88 | 89 | iv_restore.nested_iv <- function(x, to, ...) { 90 | new_nested_iv(x) 91 | } 92 | 93 | iv <- new_iv(c(1, 5), c(2, 7)) 94 | 95 | x <- new_nested_iv(iv) 96 | x 97 | 98 | # Proxies, then accesses the `start` field 99 | iv_start(x) 100 | 101 | # Proxies, computes the complement to generate an iv, 102 | # then restores to the original type 103 | iv_set_complement(x) 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /man/iv-groups.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/groups.R 3 | \name{iv-groups} 4 | \alias{iv-groups} 5 | \alias{iv_groups} 6 | \alias{iv_identify_group} 7 | \alias{iv_locate_groups} 8 | \title{Group overlapping intervals} 9 | \usage{ 10 | iv_groups(x, ..., abutting = TRUE) 11 | 12 | iv_identify_group(x, ..., abutting = TRUE) 13 | 14 | iv_locate_groups(x, ..., abutting = TRUE) 15 | } 16 | \arguments{ 17 | \item{x}{\verb{[iv]} 18 | 19 | An interval vector.} 20 | 21 | \item{...}{These dots are for future extensions and must be empty.} 22 | 23 | \item{abutting}{\verb{[TRUE / FALSE]} 24 | 25 | Should abutting intervals be grouped together? 26 | 27 | If \code{TRUE}, \verb{[a, b)} and \verb{[b, c)} will merge as \verb{[a, c)}. If \code{FALSE}, they 28 | will be kept separate. To be a minimal interval vector, all abutting 29 | intervals must be grouped together.} 30 | } 31 | \value{ 32 | \itemize{ 33 | \item For \code{iv_groups()}, an iv with the same type as \code{x}. 34 | \item For \code{iv_identify_group()}, an iv with the same type and size as \code{x}. 35 | \item For \code{iv_locate_groups()}, a two column data frame with a \code{key} column 36 | containing the result of \code{iv_groups()} and a \code{loc} list-column containing 37 | integer vectors. 38 | } 39 | } 40 | \description{ 41 | This family of functions revolves around grouping overlapping intervals 42 | within a single iv. When multiple overlapping intervals are grouped together 43 | they result in a wider interval containing the smallest \code{\link[=iv_start]{iv_start()}} and the 44 | largest \code{\link[=iv_end]{iv_end()}} of the overlaps. 45 | \itemize{ 46 | \item \code{iv_groups()} merges all overlapping intervals found within \code{x}. The 47 | resulting intervals are known as the "groups" of \code{x}. 48 | \item \code{iv_identify_group()} identifies the group that the current interval of \code{x} 49 | falls in. This is particularly useful alongside \code{\link[dplyr:group_by]{dplyr::group_by()}}. 50 | \item \code{iv_locate_groups()} returns a two column data frame with a \code{key} column 51 | containing the result of \code{iv_groups()} and a \code{loc} list-column containing 52 | integer vectors that map each interval in \code{x} to the group that it falls in. 53 | } 54 | 55 | Optionally, you can choose \emph{not} to group abutting intervals together with 56 | \code{abutting = FALSE}, which can be useful if you'd like to retain those 57 | boundaries. 58 | \subsection{Minimal interval vectors}{ 59 | 60 | \code{iv_groups()} is particularly useful because it can generate a \emph{minimal} 61 | interval vector, which covers the range of an interval vector in the most 62 | compact form possible. In particular, a minimal interval vector: 63 | \itemize{ 64 | \item Has no overlapping intervals 65 | \item Has no abutting intervals 66 | \item Is ordered on both \code{start} and \code{end} 67 | } 68 | 69 | A minimal interval vector is allowed to have a single missing interval, 70 | which is located at the end of the vector. 71 | } 72 | } 73 | \section{Graphical Representation}{ 74 | 75 | 76 | Graphically, generating groups looks like: 77 | 78 | \figure{groups.png} 79 | 80 | With \code{abutting = FALSE}, intervals that touch aren't grouped: 81 | 82 | \figure{groups-abutting-keep.png} 83 | } 84 | 85 | \examples{ 86 | library(dplyr, warn.conflicts = FALSE) 87 | 88 | x <- iv_pairs( 89 | c(1, 5), 90 | c(2, 3), 91 | c(NA, NA), 92 | c(5, 6), 93 | c(NA, NA), 94 | c(9, 12), 95 | c(11, 14) 96 | ) 97 | x 98 | 99 | # Grouping removes all redundancy while still covering the full range 100 | # of values that were originally represented. If any missing intervals 101 | # are present, a single one is retained. 102 | iv_groups(x) 103 | 104 | # Abutting intervals are typically grouped together, but you can choose not 105 | # to group them if you want to retain those boundaries 106 | iv_groups(x, abutting = FALSE) 107 | 108 | # `iv_identify_group()` is useful alongside `group_by()` and `summarize()` 109 | df <- tibble(x = x) 110 | df <- mutate(df, u = iv_identify_group(x)) 111 | df 112 | 113 | df \%>\% 114 | group_by(u) \%>\% 115 | summarize(n = n()) 116 | 117 | # The real workhorse here is `iv_locate_groups()`, which returns 118 | # the groups and information on which observations in `x` fall in which 119 | # group 120 | iv_locate_groups(x) 121 | } 122 | -------------------------------------------------------------------------------- /man/iv-set-pairwise-deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ivs-deprecated.R 3 | \name{iv-set-pairwise-deprecated} 4 | \alias{iv-set-pairwise-deprecated} 5 | \alias{iv_pairwise_complement} 6 | \alias{iv_pairwise_union} 7 | \alias{iv_pairwise_intersect} 8 | \alias{iv_pairwise_difference} 9 | \alias{iv_pairwise_symmetric_difference} 10 | \title{Pairwise set operations} 11 | \usage{ 12 | iv_pairwise_complement(x, y) 13 | 14 | iv_pairwise_union(x, y) 15 | 16 | iv_pairwise_intersect(x, y) 17 | 18 | iv_pairwise_difference(x, y) 19 | 20 | iv_pairwise_symmetric_difference(x, y) 21 | } 22 | \arguments{ 23 | \item{x, y}{\verb{[iv]} 24 | 25 | A pair of interval vectors. 26 | 27 | These will be cast to the same type, and recycled against each other.} 28 | } 29 | \description{ 30 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 31 | 32 | These functions are deprecated in favor of their \code{set_} prefixed equivalents. 33 | \itemize{ 34 | \item \code{iv_pairwise_complement()} -> \code{\link[=iv_pairwise_set_complement]{iv_pairwise_set_complement()}} 35 | \item \code{iv_pairwise_union()} -> \code{\link[=iv_pairwise_set_union]{iv_pairwise_set_union()}} 36 | \item \code{iv_pairwise_intersect()} -> \code{\link[=iv_pairwise_set_intersect]{iv_pairwise_set_intersect()}} 37 | \item \code{iv_pairwise_difference()} -> \code{\link[=iv_pairwise_set_difference]{iv_pairwise_set_difference()}} 38 | \item \code{iv_pairwise_symmetric_difference()} -> \code{\link[=iv_pairwise_set_symmetric_difference]{iv_pairwise_set_symmetric_difference()}} 39 | } 40 | } 41 | \keyword{internal} 42 | -------------------------------------------------------------------------------- /man/iv-set-pairwise.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/set-pairwise.R 3 | \name{iv-set-pairwise} 4 | \alias{iv-set-pairwise} 5 | \alias{iv_pairwise_set_complement} 6 | \alias{iv_pairwise_set_union} 7 | \alias{iv_pairwise_set_intersect} 8 | \alias{iv_pairwise_set_difference} 9 | \alias{iv_pairwise_set_symmetric_difference} 10 | \title{Pairwise set operations} 11 | \usage{ 12 | iv_pairwise_set_complement(x, y) 13 | 14 | iv_pairwise_set_union(x, y) 15 | 16 | iv_pairwise_set_intersect(x, y) 17 | 18 | iv_pairwise_set_difference(x, y) 19 | 20 | iv_pairwise_set_symmetric_difference(x, y) 21 | } 22 | \arguments{ 23 | \item{x, y}{\verb{[iv]} 24 | 25 | A pair of interval vectors. 26 | 27 | These will be cast to the same type, and recycled against each other.} 28 | } 29 | \value{ 30 | An iv the same size and type as \code{x} and \code{y}. 31 | } 32 | \description{ 33 | This family of functions performs \emph{pairwise} set operations on two ivs. 34 | Pairwise refers to the fact that the i-th interval of \code{x} is going to be 35 | compared against the i-th interval of \code{y}. This is in contrast to their 36 | counterparts, like \code{\link[=iv_set_union]{iv_set_union()}}, which treat the entire vector of \code{x} 37 | as a single set to be compared against all of \code{y}. 38 | 39 | The descriptions of these operations are the same as their non-pairwise 40 | counterparts, but the ones here also have a number of restrictions due to 41 | the fact that each must return an output that is the same size as its inputs: 42 | \itemize{ 43 | \item For \code{iv_pairwise_set_complement()}, \code{x[i]} and \code{y[i]} can't overlap or 44 | abut, as this would generate an empty complement. 45 | \item For \code{iv_pairwise_set_union()}, \code{x[i]} and \code{y[i]} can't be separated by a 46 | gap. Use \code{\link[=iv_pairwise_span]{iv_pairwise_span()}} if you want to force gaps to be filled 47 | anyways. 48 | \item For \code{iv_pairwise_set_intersect()}, \code{x[i]} and \code{y[i]} must overlap, 49 | otherwise an empty interval would be generated. 50 | \item For \code{iv_pairwise_set_difference()}, \code{x[i]} can't be completely contained 51 | within \code{y[i]}, as that would generate an empty interval. Additionally, 52 | \code{y[i]} can't be completely contained within \code{x[i]}, as that would result 53 | in two distinct intervals for a single observation. 54 | \item For \code{iv_pairwise_set_symmetric_difference()}, \code{x[i]} and \code{y[i]} must share 55 | exactly one endpoint, otherwise an empty interval or two distinct intervals 56 | would be generated. 57 | } 58 | } 59 | \examples{ 60 | x <- iv_pairs(c(1, 3), c(6, 8)) 61 | y <- iv_pairs(c(5, 7), c(2, 3)) 62 | 63 | iv_pairwise_set_complement(x, y) 64 | 65 | z <- iv_pairs(c(2, 5), c(4, 7)) 66 | 67 | iv_pairwise_set_union(x, z) 68 | 69 | # Can't take the union when there are gaps 70 | try(iv_pairwise_set_union(x, y)) 71 | 72 | # But you can force a union across gaps with `iv_pairwise_span()` 73 | iv_pairwise_span(x, y) 74 | 75 | iv_pairwise_set_intersect(x, z) 76 | 77 | # Can't take an intersection of non-overlapping intervals 78 | try(iv_pairwise_set_intersect(x, y)) 79 | 80 | iv_pairwise_set_difference(x, z) 81 | 82 | # The pairwise symmetric difference function is fairly strict, 83 | # and is only well defined when exactly one of the interval endpoints match 84 | w <- iv_pairs(c(1, 6), c(7, 8)) 85 | iv_pairwise_set_symmetric_difference(x, w) 86 | } 87 | \seealso{ 88 | The non-pairwise versions of these functions, such as 89 | \code{\link[=iv_set_union]{iv_set_union()}}. 90 | } 91 | -------------------------------------------------------------------------------- /man/iv-sets-deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ivs-deprecated.R 3 | \name{iv-sets-deprecated} 4 | \alias{iv-sets-deprecated} 5 | \alias{iv_complement} 6 | \alias{iv_union} 7 | \alias{iv_intersect} 8 | \alias{iv_difference} 9 | \alias{iv_symmetric_difference} 10 | \title{Set operations} 11 | \usage{ 12 | iv_complement(x, ..., lower = NULL, upper = NULL) 13 | 14 | iv_union(x, y) 15 | 16 | iv_intersect(x, y) 17 | 18 | iv_difference(x, y) 19 | 20 | iv_symmetric_difference(x, y) 21 | } 22 | \arguments{ 23 | \item{x}{\verb{[iv]} 24 | 25 | An interval vector.} 26 | 27 | \item{...}{These dots are for future extensions and must be empty.} 28 | 29 | \item{lower, upper}{\verb{[vector(1) / NULL]} 30 | 31 | Bounds for the universe over which to compute the complement. These should 32 | have the same type as the element type of the interval vector. It is 33 | often useful to expand the universe to, say, \code{-Inf} to \code{Inf}.} 34 | 35 | \item{y}{\verb{[iv]} 36 | 37 | An interval vector.} 38 | } 39 | \description{ 40 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 41 | 42 | These functions are deprecated in favor of their \code{set_} prefixed equivalents. 43 | \itemize{ 44 | \item \code{iv_complement()} -> \code{\link[=iv_set_complement]{iv_set_complement()}} 45 | \item \code{iv_union()} -> \code{\link[=iv_set_union]{iv_set_union()}} 46 | \item \code{iv_intersect()} -> \code{\link[=iv_set_intersect]{iv_set_intersect()}} 47 | \item \code{iv_difference()} -> \code{\link[=iv_set_difference]{iv_set_difference()}} 48 | \item \code{iv_symmetric_difference()} -> \code{\link[=iv_set_symmetric_difference]{iv_set_symmetric_difference()}} 49 | } 50 | } 51 | \keyword{internal} 52 | -------------------------------------------------------------------------------- /man/iv-sets.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/set.R 3 | \name{iv-sets} 4 | \alias{iv-sets} 5 | \alias{iv_set_complement} 6 | \alias{iv_set_union} 7 | \alias{iv_set_intersect} 8 | \alias{iv_set_difference} 9 | \alias{iv_set_symmetric_difference} 10 | \title{Set operations} 11 | \usage{ 12 | iv_set_complement(x, ..., lower = NULL, upper = NULL) 13 | 14 | iv_set_union(x, y) 15 | 16 | iv_set_intersect(x, y) 17 | 18 | iv_set_difference(x, y) 19 | 20 | iv_set_symmetric_difference(x, y) 21 | } 22 | \arguments{ 23 | \item{x}{\verb{[iv]} 24 | 25 | An interval vector.} 26 | 27 | \item{...}{These dots are for future extensions and must be empty.} 28 | 29 | \item{lower, upper}{\verb{[vector(1) / NULL]} 30 | 31 | Bounds for the universe over which to compute the complement. These should 32 | have the same type as the element type of the interval vector. It is 33 | often useful to expand the universe to, say, \code{-Inf} to \code{Inf}.} 34 | 35 | \item{y}{\verb{[iv]} 36 | 37 | An interval vector.} 38 | } 39 | \value{ 40 | \itemize{ 41 | \item For \code{iv_set_complement()}, a vector of the same type as \code{x} containing the 42 | complement. 43 | \item For all other set operations, a vector of the same type as the common type 44 | of \code{x} and \code{y} containing the result. 45 | } 46 | } 47 | \description{ 48 | This family of functions treats ivs as sets. They always compute 49 | the \link[=iv_groups]{minimal} iv of each input and return a minimal iv. 50 | \itemize{ 51 | \item \code{iv_set_complement()} takes the complement of the intervals in an iv. By 52 | default, the minimum and maximum of the inputs define the bounds to take 53 | the complement over, but this can be adjusted with \code{lower} and \code{upper}. 54 | Missing intervals are always dropped in the complement. 55 | \item \code{iv_set_union()} answers the question, "Which intervals are in \code{x} or \code{y}?" 56 | It is equivalent to combining the two vectors together and then calling 57 | \code{iv_groups()}. 58 | \item \code{iv_set_intersect()} answers the question, "Which intervals are in \code{x} and 59 | \code{y}?" 60 | \item \code{iv_set_difference()} answers the question, "Which intervals are in \code{x} but 61 | not \code{y}?" Note that this is an asymmetrical difference. 62 | \item \code{iv_set_symmetric_difference()} answers the question, "Which intervals are 63 | in \code{x} or \code{y} but not both?" 64 | } 65 | } 66 | \section{Graphical Representation}{ 67 | 68 | 69 | Graphically, generating the complement looks like: 70 | 71 | \figure{complement.png} 72 | 73 | If you were to set \code{upper = 20} with these intervals, then you'd get one more 74 | interval in the complement. 75 | 76 | \figure{complement-upper.png} 77 | 78 | Generating the intersection between two ivs looks like: 79 | 80 | \figure{intersect.png} 81 | } 82 | 83 | \examples{ 84 | x <- iv_pairs( 85 | c(10, 12), 86 | c(0, 5), 87 | c(NA, NA), 88 | c(3, 6), 89 | c(-5, -2), 90 | c(NA, NA) 91 | ) 92 | x 93 | 94 | y <- iv_pairs( 95 | c(2, 7), 96 | c(NA, NA), 97 | c(-3, -1), 98 | c(14, 15) 99 | ) 100 | y 101 | 102 | # Complement contains any values from `[-5, 12)` that aren't represented 103 | # in these intervals. Missing intervals are dropped. 104 | iv_set_complement(x) 105 | 106 | # Expand out the "universe" of possible values 107 | iv_set_complement(x, lower = -Inf) 108 | iv_set_complement(x, lower = -Inf, upper = Inf) 109 | 110 | # Which intervals are in x or y? 111 | iv_set_union(x, y) 112 | 113 | # Which intervals are in x and y? 114 | iv_set_intersect(x, y) 115 | 116 | # Which intervals are in x but not y? 117 | iv_set_difference(x, y) 118 | 119 | # Which intervals are in y but not x? 120 | iv_set_difference(y, x) 121 | 122 | # Missing intervals in x are kept if there aren't missing intervals in y 123 | iv_set_difference(x, iv(1, 2)) 124 | 125 | # Which intervals are in x or y but not both? 126 | iv_set_symmetric_difference(x, y) 127 | 128 | # Missing intervals will be kept if they only appear on one side 129 | iv_set_symmetric_difference(x, iv(1, 2)) 130 | iv_set_symmetric_difference(iv(1, 2), x) 131 | } 132 | \seealso{ 133 | The \emph{pairwise} versions of these functions, such as 134 | \code{\link[=iv_pairwise_set_union]{iv_pairwise_set_union()}}. 135 | } 136 | -------------------------------------------------------------------------------- /man/iv-splits.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/splits.R 3 | \name{iv-splits} 4 | \alias{iv-splits} 5 | \alias{iv_splits} 6 | \alias{iv_identify_splits} 7 | \alias{iv_locate_splits} 8 | \title{Splits} 9 | \usage{ 10 | iv_splits(x, ..., on = NULL) 11 | 12 | iv_identify_splits(x, ..., on = NULL) 13 | 14 | iv_locate_splits(x, ..., on = NULL) 15 | } 16 | \arguments{ 17 | \item{x}{\verb{[iv]} 18 | 19 | An interval vector.} 20 | 21 | \item{...}{These dots are for future extensions and must be empty.} 22 | 23 | \item{on}{\verb{[vector / NULL]} 24 | 25 | An optional vector of additional values to split on. 26 | 27 | This should have the same type as \code{iv_start(x)}.} 28 | } 29 | \value{ 30 | \itemize{ 31 | \item For \code{iv_splits()}, an iv with the same type as \code{x}. 32 | \item For \code{iv_identify_splits()}, a list-of containing ivs with the same size as 33 | \code{x}. 34 | \item For \code{iv_locate_splits()}, a two column data frame with a \code{key} column 35 | of the same type as \code{x} and \code{loc} list-column containing integer vectors. 36 | } 37 | } 38 | \description{ 39 | This family of functions revolves around splitting an iv on its endpoints, 40 | which results in a new iv that is entirely disjoint (i.e. non-overlapping). 41 | The intervals in the resulting iv are known as "splits". 42 | \itemize{ 43 | \item \code{iv_splits()} computes the disjoint splits for \code{x}. 44 | \item \code{iv_identify_splits()} identifies the splits that correspond to each 45 | interval in \code{x}. It replaces \code{x} with a list of the same size where each 46 | element of the list contains the splits that the corresponding interval in 47 | \code{x} overlaps. This is particularly useful alongside \code{\link[tidyr:unnest]{tidyr::unnest()}}. 48 | \item \code{iv_locate_splits()} returns a two column data frame with a \code{key} column 49 | containing the result of \code{iv_splits()} and a \code{loc} list-column containing 50 | integer vectors that map each interval in \code{x} to the splits that it overlaps. 51 | } 52 | } 53 | \section{Graphical Representation}{ 54 | 55 | 56 | Graphically, generating splits looks like: 57 | 58 | \figure{splits.png} 59 | } 60 | 61 | \examples{ 62 | library(tidyr) 63 | library(dplyr) 64 | 65 | # Guests to a party and their arrival/departure times 66 | guests <- tibble( 67 | arrive = as.POSIXct( 68 | c("2008-05-20 19:30:00", "2008-05-20 20:10:00", "2008-05-20 22:15:00"), 69 | tz = "UTC" 70 | ), 71 | depart = as.POSIXct( 72 | c("2008-05-20 23:00:00", "2008-05-21 00:00:00", "2008-05-21 00:30:00"), 73 | tz = "UTC" 74 | ), 75 | name = list( 76 | c("Mary", "Harry"), 77 | c("Diana", "Susan"), 78 | "Peter" 79 | ) 80 | ) 81 | 82 | guests <- unnest(guests, name) \%>\% 83 | mutate(iv = iv(arrive, depart), .keep = "unused") 84 | 85 | guests 86 | 87 | # You can determine the disjoint intervals at which people 88 | # arrived/departed with `iv_splits()` 89 | iv_splits(guests$iv) 90 | 91 | # Say you'd like to determine who was at the party at any given time 92 | # throughout the night 93 | guests <- mutate(guests, splits = iv_identify_splits(iv)) 94 | guests 95 | 96 | # Unnest the splits to generate disjoint intervals for each guest 97 | guests <- guests \%>\% 98 | unnest(splits) \%>\% 99 | select(name, splits) 100 | 101 | guests 102 | 103 | # Tabulate who was there at any given time 104 | guests \%>\% 105 | summarise(n = n(), who = list(name), .by = splits) 106 | 107 | # --------------------------------------------------------------------------- 108 | 109 | x <- iv_pairs(c(1, 5), c(4, 9), c(12, 15)) 110 | x 111 | 112 | # You can provide additional singular values to split on with `on` 113 | iv_splits(x, on = c(2, 13)) 114 | } 115 | -------------------------------------------------------------------------------- /man/iv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iv.R 3 | \name{iv} 4 | \alias{iv} 5 | \alias{iv_pairs} 6 | \title{Create an interval vector} 7 | \usage{ 8 | iv(start, end, ..., ptype = NULL, size = NULL) 9 | 10 | iv_pairs(..., ptype = NULL) 11 | } 12 | \arguments{ 13 | \item{start, end}{\verb{[vector]} 14 | 15 | A pair of vectors to represent the bounds of the intervals. 16 | 17 | To be a valid interval vector, \code{start} must be strictly less than \code{end}. 18 | 19 | If either \code{start} or \code{end} are incomplete / missing, then both bounds will 20 | be coerced to missing values. 21 | 22 | \code{start} and \code{end} are recycled against each other and are cast to the same 23 | type.} 24 | 25 | \item{...}{For \code{iv_pairs()}: 26 | 27 | \verb{[vector pairs]} 28 | 29 | Vectors of size 2 representing intervals to include in the result. 30 | 31 | All inputs will be cast to the same type. 32 | 33 | For \code{iv()}: 34 | 35 | These dots are for future extensions and must be empty.} 36 | 37 | \item{ptype}{\verb{[vector(0) / NULL]} 38 | 39 | A prototype to force for the inner type of the resulting iv. If \code{NULL}, 40 | this defaults to the common type of the inputs.} 41 | 42 | \item{size}{\verb{[integer(1) / NULL]} 43 | 44 | A size to force for the resulting iv. If \code{NULL}, this defaults to the 45 | common size of the inputs.} 46 | } 47 | \value{ 48 | An iv. 49 | } 50 | \description{ 51 | \itemize{ 52 | \item \code{iv()} creates an interval vector from \code{start} and \code{end} vectors. This 53 | is how you will typically create interval vectors, and is often used with 54 | columns in a data frame. 55 | \item \code{iv_pairs()} creates an interval vector from \emph{pairs}. This is often useful 56 | for interactive testing, as it provides a more intuitive interface for 57 | creating small interval vectors. It should generally not be used on a large 58 | scale because it can be slow. 59 | } 60 | \subsection{Intervals}{ 61 | 62 | Interval vectors are \emph{right-open}, i.e. \verb{[start, end)}. This means that 63 | \code{start < end} is a requirement to generate an interval vector. In particular, 64 | empty intervals with \code{start == end} are not allowed. 65 | 66 | Right-open intervals tend to be the most practically useful. For example, 67 | \verb{[2019-01-01 00:00:00, 2019-01-02 00:00:00)} nicely encapsulates all times on 68 | \code{2019-01-01}. With closed intervals, you'd have to attempt to specify this as 69 | \verb{2019-01-01 23:59:59}, which is inconvenient and inaccurate, as it doesn't 70 | capture fractional seconds. 71 | 72 | Right-open intervals also have the extremely nice technical property that 73 | they create a closed algebra. Concretely, the complement of a vector of 74 | right-open intervals and the union, intersection, or difference of two 75 | vectors of right-open intervals will always result in another vector of 76 | right-open intervals. 77 | } 78 | 79 | \subsection{Missing intervals}{ 80 | 81 | When creating interval vectors with \code{iv()}, if either bound is 82 | \link[vctrs:vec_detect_complete]{incomplete}, then both bounds are set to 83 | their missing value. 84 | } 85 | } 86 | \examples{ 87 | library(dplyr, warn.conflicts = FALSE) 88 | 89 | set.seed(123) 90 | 91 | x <- tibble( 92 | start = as.Date("2019-01-01") + 1:5, 93 | end = start + sample(1:10, length(start), replace = TRUE) 94 | ) 95 | 96 | # Typically you'll use `iv()` with columns of a data frame 97 | mutate(x, iv = iv(start, end), .keep = "unused") 98 | 99 | # `iv_pairs()` is useful for generating interval vectors interactively 100 | iv_pairs(c(1, 5), c(2, 3), c(6, 10)) 101 | } 102 | -------------------------------------------------------------------------------- /man/iv_align.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/align.R 3 | \name{iv_align} 4 | \alias{iv_align} 5 | \title{Align after locating relationships} 6 | \usage{ 7 | iv_align(needles, haystack, ..., locations) 8 | } 9 | \arguments{ 10 | \item{needles, haystack}{\verb{[vector]} 11 | 12 | Two vectors to align.} 13 | 14 | \item{...}{These dots are for future extensions and must be empty.} 15 | 16 | \item{locations}{\verb{[two-column data frame]} 17 | 18 | The data frame of locations returned from one of \code{\link[=iv_locate_overlaps]{iv_locate_overlaps()}}, 19 | \code{\link[=iv_locate_precedes]{iv_locate_precedes()}}, \code{\link[=iv_locate_follows]{iv_locate_follows()}}, \code{\link[=iv_locate_relates]{iv_locate_relates()}}, or 20 | \code{\link[=iv_locate_between]{iv_locate_between()}}.} 21 | } 22 | \value{ 23 | A two column data frame with a \verb{$needles} column containing the 24 | sliced version of \code{needles} and a \verb{$haystack} column containing the sliced 25 | version of \code{haystack}. 26 | } 27 | \description{ 28 | \code{iv_align()} will align/join \code{needles} and \code{haystack} together using a data 29 | frame of \code{locations}. These \code{locations} are intended to be the output of one 30 | of: \code{\link[=iv_locate_overlaps]{iv_locate_overlaps()}}, \code{\link[=iv_locate_precedes]{iv_locate_precedes()}}, \code{\link[=iv_locate_follows]{iv_locate_follows()}}, 31 | \code{\link[=iv_locate_relates]{iv_locate_relates()}}, or \code{\link[=iv_locate_between]{iv_locate_between()}}. 32 | 33 | This is mainly a convenience function that slices both \code{needles} and 34 | \code{haystack} according to those \code{locations}, and then stores the result 35 | in a new two column data frame. 36 | } 37 | \examples{ 38 | needles <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 39 | haystack <- iv_pairs(c(0, 2), c(4, 6)) 40 | 41 | locations <- iv_locate_overlaps(needles, haystack) 42 | iv_align(needles, haystack, locations = locations) 43 | 44 | locations <- iv_locate_overlaps(needles, haystack, no_match = "drop") 45 | iv_align(needles, haystack, locations = locations) 46 | 47 | needles <- c(1, 15, 4, 11) 48 | haystack <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 49 | 50 | locations <- iv_locate_between(needles, haystack) 51 | iv_align(needles, haystack, locations = locations) 52 | } 53 | -------------------------------------------------------------------------------- /man/iv_diff.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diff.R 3 | \name{iv_diff} 4 | \alias{iv_diff} 5 | \title{Diff a vector to create an interval vector} 6 | \usage{ 7 | iv_diff(x) 8 | } 9 | \arguments{ 10 | \item{x}{\verb{[vector]} 11 | 12 | A vector in strictly increasing order.} 13 | } 14 | \value{ 15 | An iv using \code{x} as the inner type, with size equal to 16 | \code{max(0L, vec_size(x) - 1L)}. 17 | } 18 | \description{ 19 | \code{iv_diff()} is a convenient way to generate an iv from a preexisting vector, 20 | as long as that vector is in strictly increasing order. It returns an iv 21 | that is 1 element shorter than \code{x} (unless \code{x} is already empty). 22 | 23 | It is particularly useful for creating an iv column from an existing column 24 | inside of \code{\link[dplyr:mutate]{dplyr::mutate()}}, but requires you to explicitly handle padding 25 | in that case, see the examples. 26 | 27 | Missing values are allowed, and will be propagated to each side of the 28 | resulting interval after applying the diff. 29 | } 30 | \details{ 31 | \code{iv_diff()} is inspired by \code{\link[=diff]{diff()}}. 32 | } 33 | \examples{ 34 | x <- as.Date("2019-01-01") + c(0, 5, 7, 10, 19) 35 | x 36 | 37 | # Notice how the boundaries don't overlap, because the closing `)` aligns 38 | # with an opening `[`. 39 | iv_diff(x) 40 | 41 | # Like `iv()`, missing values propagate to both boundaries of the interval. 42 | # Before missing value propagation was applied, it looked like this: 43 | # [1, NA), [NA, 2), [2, 3) 44 | x <- c(1, NA, 2, 3) 45 | iv_diff(x) 46 | 47 | # Values in `x` must be in strictly increasing order to generate a valid 48 | # interval vector 49 | x <- c(1, 0, 2, 2) 50 | try(iv_diff(x)) 51 | 52 | x <- c(1, NA, 0) 53 | try(iv_diff(x)) 54 | 55 | # --------------------------------------------------------------------------- 56 | # Use with `mutate()` 57 | 58 | library(dplyr) 59 | 60 | # `iv_diff()` is useful for converting a pre-existing column into an interval 61 | # vector, but you'll need to apply padding to ensure that the size of the 62 | # diff-ed result is the same as the number of rows in your data frame. There 63 | # are two main ways to pad, which are explored below. 64 | df <- tibble(x = c(1, 3, 6)) 65 | 66 | # Pad with a known lower/upper bound 67 | df \%>\% mutate(iv = iv_diff(c(0, x))) 68 | df \%>\% mutate(iv = iv_diff(c(x, Inf))) 69 | 70 | # Pad with a missing value, which results in a fully missing interval 71 | df \%>\% mutate(iv = iv_diff(c(NA, x))) 72 | df \%>\% mutate(iv = iv_diff(c(x, NA))) 73 | } 74 | -------------------------------------------------------------------------------- /man/iv_format.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/format.R 3 | \name{iv_format} 4 | \alias{iv_format} 5 | \title{Formatting} 6 | \usage{ 7 | iv_format(x) 8 | } 9 | \arguments{ 10 | \item{x}{\verb{[vector]} 11 | 12 | A vector to format. This will be called on the \code{\link[=iv_start]{iv_start()}} and \code{\link[=iv_end]{iv_end()}} 13 | vectors of an iv.} 14 | } 15 | \value{ 16 | A character vector, likely generated through a call to \code{format()}. 17 | } 18 | \description{ 19 | \code{iv_format()} is an S3 generic intended as a developer tool for making a 20 | custom class print nicely when stored in an iv. The default method simply 21 | calls \code{\link[=format]{format()}}, and in many cases this is enough for most classes. 22 | However, if your class automatically adds justification or padding when 23 | formatting a single vector, you might need to implement an \code{iv_format()} 24 | method to avoid that padding, since it often looks strange when nested 25 | in an interval vector. 26 | } 27 | \examples{ 28 | # Numeric values get padding automatically through `format()` 29 | x <- c(1, 100) 30 | format(x) 31 | 32 | # This ends up looking strange in an iv, so an `iv_format()` method for 33 | # numeric values is implemented which turns off that padding 34 | iv_format(x) 35 | } 36 | -------------------------------------------------------------------------------- /man/iv_pairwise_span.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/span.R 3 | \name{iv_pairwise_span} 4 | \alias{iv_pairwise_span} 5 | \title{Pairwise span} 6 | \usage{ 7 | iv_pairwise_span(x, y) 8 | } 9 | \arguments{ 10 | \item{x, y}{\verb{[iv]} 11 | 12 | A pair of interval vectors. 13 | 14 | These will be cast to the same type, and recycled against each other.} 15 | } 16 | \value{ 17 | An iv the same size and type as \code{x} and \code{y}. 18 | } 19 | \description{ 20 | \code{iv_pairwise_span()} computes the \emph{pairwise} "span" between the i-th interval 21 | of \code{x} and the i-th interval of \code{y}. The pairwise span of two intervals is 22 | a new interval containing the minimum start and maximum end of the original 23 | intervals. It is similar to \code{\link[=iv_pairwise_set_union]{iv_pairwise_set_union()}}, except it fills across 24 | gaps. 25 | } 26 | \examples{ 27 | x <- iv_pairs(c(1, 3), c(6, 8)) 28 | y <- iv_pairs(c(5, 7), c(2, 3)) 29 | 30 | # Can't take the set union when there are gaps 31 | try(iv_pairwise_set_union(x, y)) 32 | 33 | # But you can compute the span of the intervals 34 | iv_pairwise_span(x, y) 35 | } 36 | -------------------------------------------------------------------------------- /man/iv_span.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/span.R 3 | \name{iv_span} 4 | \alias{iv_span} 5 | \title{Span} 6 | \usage{ 7 | iv_span(x, ..., missing = "propagate", empty = "missing") 8 | } 9 | \arguments{ 10 | \item{x}{\verb{[iv]} 11 | 12 | An interval vector.} 13 | 14 | \item{...}{These dots are for future extensions and must be empty.} 15 | 16 | \item{missing}{\verb{["propagate" / "drop" / "error" / iv(1)]} 17 | 18 | Handling of missing intervals in \code{x}. 19 | \itemize{ 20 | \item \code{"propagate"} forces \code{iv_span()} to return a missing interval if any 21 | missing intervals are detected in \code{x}. 22 | \item \code{"drop"} drops missing intervals before computing the span. If this 23 | results in an empty vector, then \code{empty} will be applied. 24 | \item \code{"error"} throws an error if any missing intervals are detected. 25 | \item If an iv of size 1 is supplied, then this is returned if any missing 26 | intervals are detected. It is cast to the type of \code{x} before returning. 27 | }} 28 | 29 | \item{empty}{\verb{["missing" / "error" / iv(1)]} 30 | 31 | Handling of empty \code{x} vectors. 32 | \itemize{ 33 | \item \code{"missing"} forces \code{iv_span()} to return a missing interval if \code{x} is 34 | empty. 35 | \item \code{"error"} throws an error if \code{x} is empty. 36 | \item If an iv of size 1 is supplied, then this is returned if \code{x} is empty. It 37 | is cast to the type of \code{x} before returning. 38 | }} 39 | } 40 | \description{ 41 | \code{iv_span()} computes the span of an iv. The span is a single interval which 42 | encompasses the entire range of the iv. It is similar to \code{\link[=iv_groups]{iv_groups()}}, if 43 | groups were also merged across gaps. 44 | 45 | \code{iv_span()} is a \emph{summary} function, like \code{\link[=min]{min()}} and \code{\link[=max]{max()}}, so it always 46 | returns a size 1 iv, even for empty ivs. The \code{empty} argument can be used to 47 | control what is returned in the empty case. 48 | } 49 | \details{ 50 | \code{iv_span()} is currently limited by the fact that it calls \code{\link[=min]{min()}} and 51 | \code{\link[=max]{max()}} internally, which doesn't work for all vector types that ivs 52 | supports (mainly data frames). In the future, we hope to be able to leverage 53 | \code{vctrs::vec_min()} and \code{vctrs::vec_max()}, which don't exist yet. 54 | } 55 | \examples{ 56 | x <- iv_pairs(c(1, 5), c(2, 6), c(9, 10)) 57 | 58 | # The span covers the full range of values seen in `x` 59 | iv_span(x) 60 | 61 | # Compare against `iv_groups()`, which merges overlaps but doesn't merge 62 | # across gaps 63 | iv_groups(x) 64 | 65 | x <- iv_pairs(c(1, 3), c(NA, NA), c(5, 6), c(NA, NA)) 66 | 67 | # Because `iv_span()` is a summary function, if any missing intervals are 68 | # present then it returns a missing interval by default 69 | iv_span(x) 70 | 71 | # Further control this with `missing` 72 | iv_span(x, missing = "drop") 73 | try(iv_span(x, missing = "error")) 74 | iv_span(x, missing = iv(-1, 0)) 75 | 76 | x <- iv(double(), double()) 77 | 78 | # If `x` is empty, then by default a missing interval is returned 79 | iv_span(x) 80 | 81 | # Control this with `empty` 82 | try(iv_span(x, empty = "error")) 83 | iv_span(x, empty = iv(-Inf, Inf)) 84 | 85 | # `empty` kicks in if `missing = "drop"` is used and all elements were 86 | # missing 87 | x <- iv(c(NA, NA), c(NA, NA), ptype = double()) 88 | iv_span(x, missing = "drop", empty = iv(-Inf, Inf)) 89 | } 90 | -------------------------------------------------------------------------------- /man/ivs-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ivs-package.R 3 | \docType{package} 4 | \name{ivs-package} 5 | \alias{ivs} 6 | \alias{ivs-package} 7 | \title{ivs: Interval Vectors} 8 | \description{ 9 | Provides a library for generic interval manipulations using a new interval vector class. Capabilities include: locating various kinds of relationships between two interval vectors, merging overlaps within a single interval vector, splitting an interval vector on its overlapping endpoints, and applying set theoretical operations on interval vectors. Many of the operations in this package were inspired by James Allen's interval algebra, Allen (1983) \doi{10.1145/182.358434}. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/DavisVaughan/ivs} 15 | \item \url{https://davisvaughan.github.io/ivs/} 16 | \item Report bugs at \url{https://github.com/DavisVaughan/ivs/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Davis Vaughan \email{davis@posit.co} 22 | 23 | Other contributors: 24 | \itemize{ 25 | \item Posit Software, PBC [copyright holder, funder] 26 | } 27 | 28 | } 29 | \keyword{internal} 30 | -------------------------------------------------------------------------------- /man/new_iv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iv.R 3 | \name{new_iv} 4 | \alias{new_iv} 5 | \title{Construct a new iv} 6 | \usage{ 7 | new_iv(start, end, ..., class = character()) 8 | } 9 | \arguments{ 10 | \item{start, end}{\verb{[vector]} 11 | 12 | A pair of vectors to represent the bounds of the intervals. 13 | 14 | To be a valid interval vector, \code{start} must be strictly less than \code{end}, 15 | or both \code{start} and \code{end} must be a missing value.} 16 | 17 | \item{...}{\verb{[name-value pairs]} 18 | 19 | Additional named attributes to attach to the result.} 20 | 21 | \item{class}{\verb{[character]} 22 | 23 | The name of the subclass to create.} 24 | } 25 | \value{ 26 | A new iv object. 27 | } 28 | \description{ 29 | \code{new_iv()} is a developer focused function for creating a new interval 30 | vector. It does minimal checks on the inputs, for performance. 31 | } 32 | \examples{ 33 | new_iv(1, 2) 34 | } 35 | -------------------------------------------------------------------------------- /man/relation-count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/relation.R 3 | \name{relation-count} 4 | \alias{relation-count} 5 | \alias{iv_count_overlaps} 6 | \alias{iv_count_precedes} 7 | \alias{iv_count_follows} 8 | \title{Count relationships between two ivs} 9 | \usage{ 10 | iv_count_overlaps( 11 | needles, 12 | haystack, 13 | ..., 14 | type = "any", 15 | missing = "equals", 16 | no_match = 0L 17 | ) 18 | 19 | iv_count_precedes( 20 | needles, 21 | haystack, 22 | ..., 23 | closest = FALSE, 24 | missing = "equals", 25 | no_match = 0L 26 | ) 27 | 28 | iv_count_follows( 29 | needles, 30 | haystack, 31 | ..., 32 | closest = FALSE, 33 | missing = "equals", 34 | no_match = 0L 35 | ) 36 | } 37 | \arguments{ 38 | \item{needles, haystack}{\verb{[iv]} 39 | 40 | Interval vectors used for relation matching. 41 | \itemize{ 42 | \item Each element of \code{needles} represents the interval to search for. 43 | \item \code{haystack} represents the intervals to search in. 44 | } 45 | 46 | Prior to comparison, \code{needles} and \code{haystack} are coerced to the same type.} 47 | 48 | \item{...}{These dots are for future extensions and must be empty.} 49 | 50 | \item{type}{\verb{[character(1)]} 51 | 52 | The type of relationship to find. One of: 53 | \itemize{ 54 | \item \code{"any"}: Finds any overlap whatsoever between an interval in \code{needles} 55 | and an interval in \code{haystack}. 56 | \item \code{"within"}: Finds when an interval in \code{needles} is completely within 57 | (or equal to) an interval in \code{haystack}. 58 | \item \code{"contains"}: Finds when an interval in \code{needles} completely contains 59 | (or equals) an interval in \code{haystack}. 60 | \item \code{"equals"}: Finds when an interval in \code{needles} is exactly equal to 61 | an interval in \code{haystack}. 62 | \item \code{"starts"}: Finds when the start of an interval in \code{needles} matches the 63 | start of an interval in \code{haystack}. 64 | \item \code{"ends"}: Finds when the end of an interval in \code{needles} matches the end 65 | of an interval in \code{haystack}. 66 | }} 67 | 68 | \item{missing}{\verb{[integer(1) / "equals" / "error"]} 69 | 70 | Handling of missing intervals in \code{needles}. 71 | \itemize{ 72 | \item \code{"equals"} considers missing intervals in \code{needles} as exactly equal 73 | to missing intervals in \code{haystack} when determining if there is a 74 | matching relationship between them. 75 | \item \code{"error"} throws an error if any intervals in \code{needles} are missing. 76 | \item If a single integer value is provided, this represents the count returned 77 | for a missing interval in \code{needles}. Use \code{0L} to force missing intervals 78 | to never match. 79 | }} 80 | 81 | \item{no_match}{\verb{[integer(1) / "error"]} 82 | 83 | Handling of \code{needles} without a match. 84 | \itemize{ 85 | \item \code{"error"} throws an error if any needles have zero matches. 86 | \item If a single integer is provided, this represents the count returned for 87 | a needle with zero matches. The default value gives unmatched needles 88 | a count of \code{0L}. 89 | }} 90 | 91 | \item{closest}{\verb{[TRUE / FALSE]} 92 | 93 | Should only the closest relationship be returned? 94 | 95 | If \code{TRUE}, will only return the closest interval(s) in \code{haystack} that 96 | the current value of \code{needles} either precedes or follows. Note that 97 | multiple intervals can still be returned if there are ties, which can 98 | be resolved using \code{multiple}.} 99 | } 100 | \value{ 101 | An integer vector the same size as \code{needles}. 102 | } 103 | \description{ 104 | This family of functions counts different types of relationships between 105 | two ivs. It works similar to \code{\link[base:match]{base::match()}}, where \code{needles[i]} checks for 106 | a relationship in all of \code{haystack}. 107 | \itemize{ 108 | \item \code{iv_count_overlaps()} counts instances of a specific \code{type} of overlap 109 | between the two ivs. 110 | \item \code{iv_count_precedes()} counts instances when \code{needles[i]} precedes (i.e. 111 | comes before) any interval in \code{haystack}. 112 | \item \code{iv_count_follows()} counts instances when \code{needles[i]} follows (i.e. 113 | comes after) any interval in \code{haystack}. 114 | } 115 | 116 | These functions return an integer vector the same size as \code{needles} 117 | containing a count of the times a particular relationship between the \code{i}-th 118 | interval of \code{needles} and any interval of \code{haystack} occurred. 119 | } 120 | \examples{ 121 | library(vctrs) 122 | 123 | x <- iv_pairs( 124 | as.Date(c("2019-01-05", "2019-01-10")), 125 | as.Date(c("2019-01-07", "2019-01-15")), 126 | as.Date(c("2019-01-20", "2019-01-31")) 127 | ) 128 | 129 | y <- iv_pairs( 130 | as.Date(c("2019-01-01", "2019-01-03")), 131 | as.Date(c("2019-01-04", "2019-01-08")), 132 | as.Date(c("2019-01-07", "2019-01-09")), 133 | as.Date(c("2019-01-10", "2019-01-20")), 134 | as.Date(c("2019-01-15", "2019-01-20")) 135 | ) 136 | 137 | x 138 | y 139 | 140 | # Count the number of times `x` overlaps `y` at all 141 | iv_count_overlaps(x, y) 142 | 143 | # Count the number of times `y` is within an interval in `x` 144 | iv_count_overlaps(y, x, type = "within") 145 | 146 | # Count the number of times `x` precedes `y` 147 | iv_count_precedes(x, y) 148 | 149 | # --------------------------------------------------------------------------- 150 | 151 | a <- iv(c(1, NA), c(2, NA)) 152 | b <- iv(c(NA, NA), c(NA, NA)) 153 | 154 | # Missing intervals are seen as exactly equal by default, so they are 155 | # considered to overlap 156 | iv_count_overlaps(a, b) 157 | 158 | # If you'd like missing intervals to be treated as unmatched, set 159 | # `missing = 0L` 160 | iv_count_overlaps(a, b, missing = 0L) 161 | 162 | # If you'd like to propagate missing intervals, set `missing = NA` 163 | iv_count_overlaps(a, b, missing = NA) 164 | } 165 | \seealso{ 166 | \link[=relation-locate]{Locating relationships} 167 | } 168 | -------------------------------------------------------------------------------- /man/relation-detect-pairwise.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/relation.R 3 | \name{relation-detect-pairwise} 4 | \alias{relation-detect-pairwise} 5 | \alias{iv_pairwise_overlaps} 6 | \alias{iv_pairwise_precedes} 7 | \alias{iv_pairwise_follows} 8 | \title{Pairwise detect a relationship between two ivs} 9 | \usage{ 10 | iv_pairwise_overlaps(x, y, ..., type = "any") 11 | 12 | iv_pairwise_precedes(x, y) 13 | 14 | iv_pairwise_follows(x, y) 15 | } 16 | \arguments{ 17 | \item{x, y}{\verb{[iv]} 18 | 19 | A pair of interval vectors. 20 | 21 | These will be recycled against each other and cast to the same type.} 22 | 23 | \item{...}{These dots are for future extensions and must be empty.} 24 | 25 | \item{type}{\verb{[character(1)]} 26 | 27 | The type of relationship to find. One of: 28 | \itemize{ 29 | \item \code{"any"}: Finds any overlap whatsoever between an interval in \code{needles} 30 | and an interval in \code{haystack}. 31 | \item \code{"within"}: Finds when an interval in \code{needles} is completely within 32 | (or equal to) an interval in \code{haystack}. 33 | \item \code{"contains"}: Finds when an interval in \code{needles} completely contains 34 | (or equals) an interval in \code{haystack}. 35 | \item \code{"equals"}: Finds when an interval in \code{needles} is exactly equal to 36 | an interval in \code{haystack}. 37 | \item \code{"starts"}: Finds when the start of an interval in \code{needles} matches the 38 | start of an interval in \code{haystack}. 39 | \item \code{"ends"}: Finds when the end of an interval in \code{needles} matches the end 40 | of an interval in \code{haystack}. 41 | }} 42 | } 43 | \value{ 44 | A logical vector the same size as the common size of \code{x} and \code{y}. 45 | } 46 | \description{ 47 | This family of functions detects different types of relationships between 48 | two ivs \emph{pairwise}, where pairwise means that the i-th interval of 49 | \code{x} is compared against the i-th interval of \code{y}. This is in contrast to 50 | \code{\link[=iv_overlaps]{iv_overlaps()}}, which works more like \link[base:match]{base::\%in\%}. 51 | \itemize{ 52 | \item \code{iv_pairwise_overlaps()} detects a specific \code{type} of overlap 53 | between the i-th interval of \code{x} and the i-th interval of \code{y}. 54 | \item \code{iv_pairwise_precedes()} detects if the i-th interval of \code{x} 55 | precedes (i.e. comes before) the i-th interval of \code{y}. 56 | \item \code{iv_pairwise_follows()} detects if the i-th interval of \code{x} 57 | follows (i.e. comes after) the i-th interval of \code{y}. 58 | } 59 | 60 | These functions return a logical vector the same size as the common size of 61 | \code{x} and \code{y}. 62 | } 63 | \examples{ 64 | library(vctrs) 65 | 66 | x <- iv_pairs( 67 | as.Date(c("2019-01-05", "2019-01-10")), 68 | as.Date(c("2019-01-07", "2019-01-15")), 69 | as.Date(c("2019-01-20", "2019-01-31")) 70 | ) 71 | 72 | y <- iv_pairs( 73 | as.Date(c("2019-01-01", "2019-01-03")), 74 | as.Date(c("2019-01-07", "2019-01-09")), 75 | as.Date(c("2019-01-18", "2019-01-21")) 76 | ) 77 | 78 | x 79 | y 80 | 81 | # Does the i-th interval of `x` overlap the i-th interval of `y`? 82 | iv_pairwise_overlaps(x, y) 83 | 84 | # Does the i-th interval of `x` contain the i-th interval of `y`? 85 | iv_pairwise_overlaps(x, y, type = "contains") 86 | 87 | # Does the i-th interval of `x` follow the i-th interval of `y`? 88 | iv_pairwise_follows(x, y) 89 | 90 | a <- iv_pairs(c(1, 2), c(NA, NA), c(NA, NA)) 91 | b <- iv_pairs(c(NA, NA), c(3, 4), c(NA, NA)) 92 | 93 | # Missing intervals always propagate 94 | iv_pairwise_overlaps(a, b) 95 | } 96 | \seealso{ 97 | \link[=relation-locate]{Locating relationships} 98 | 99 | \link[=relation-detect]{Detecting relationships} 100 | 101 | \link[=allen-relation-locate]{Locating relations from Allen's Interval Algebra} 102 | } 103 | -------------------------------------------------------------------------------- /man/relation-detect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/relation.R 3 | \name{relation-detect} 4 | \alias{relation-detect} 5 | \alias{iv_overlaps} 6 | \alias{iv_precedes} 7 | \alias{iv_follows} 8 | \title{Detect a relationship between two ivs} 9 | \usage{ 10 | iv_overlaps(needles, haystack, ..., type = "any", missing = "equals") 11 | 12 | iv_precedes(needles, haystack, ..., missing = "equals") 13 | 14 | iv_follows(needles, haystack, ..., missing = "equals") 15 | } 16 | \arguments{ 17 | \item{needles, haystack}{\verb{[iv]} 18 | 19 | Interval vectors used for relation matching. 20 | \itemize{ 21 | \item Each element of \code{needles} represents the interval to search for. 22 | \item \code{haystack} represents the intervals to search in. 23 | } 24 | 25 | Prior to comparison, \code{needles} and \code{haystack} are coerced to the same type.} 26 | 27 | \item{...}{These dots are for future extensions and must be empty.} 28 | 29 | \item{type}{\verb{[character(1)]} 30 | 31 | The type of relationship to find. One of: 32 | \itemize{ 33 | \item \code{"any"}: Finds any overlap whatsoever between an interval in \code{needles} 34 | and an interval in \code{haystack}. 35 | \item \code{"within"}: Finds when an interval in \code{needles} is completely within 36 | (or equal to) an interval in \code{haystack}. 37 | \item \code{"contains"}: Finds when an interval in \code{needles} completely contains 38 | (or equals) an interval in \code{haystack}. 39 | \item \code{"equals"}: Finds when an interval in \code{needles} is exactly equal to 40 | an interval in \code{haystack}. 41 | \item \code{"starts"}: Finds when the start of an interval in \code{needles} matches the 42 | start of an interval in \code{haystack}. 43 | \item \code{"ends"}: Finds when the end of an interval in \code{needles} matches the end 44 | of an interval in \code{haystack}. 45 | }} 46 | 47 | \item{missing}{\verb{[logical(1) / "equals" / "error"]} 48 | 49 | Handling of missing intervals in \code{needles}. 50 | \itemize{ 51 | \item \code{"equals"} considers missing intervals in \code{needles} as exactly equal 52 | to missing intervals in \code{haystack} when determining if there is a 53 | matching relationship between them. Matched missing intervals in 54 | \code{needles} result in a \code{TRUE} value in the result, and unmatched missing 55 | intervals result in a \code{FALSE} value. 56 | \item \code{"error"} throws an error if any intervals in \code{needles} are missing. 57 | \item If a single logical value is provided, this represents the value returned 58 | in the result for intervals in \code{needles} that are missing. You can force 59 | missing intervals to be unmatched by setting this to \code{FALSE}, and you 60 | can force them to be propagated by setting this to \code{NA}. 61 | }} 62 | } 63 | \value{ 64 | A logical vector the same size as \code{needles}. 65 | } 66 | \description{ 67 | This family of functions detects different types of relationships between 68 | two ivs. It works similar to \link[base:match]{base::\%in\%}, where \code{needles[i]} checks for 69 | a relationship in all of \code{haystack}. 70 | \itemize{ 71 | \item \code{iv_overlaps()} detects a specific \code{type} of overlap between the two ivs. 72 | \item \code{iv_precedes()} detects if \code{needles[i]} precedes (i.e. comes before) any 73 | interval in \code{haystack}. 74 | \item \code{iv_follows()} detects if \code{needles[i]} follows (i.e. comes after) any 75 | interval in \code{haystack}. 76 | } 77 | 78 | These functions return a logical vector the same size as \code{needles} containing 79 | \code{TRUE} if the interval in \code{needles} has a matching relationship in 80 | \code{haystack} and \code{FALSE} otherwise. 81 | } 82 | \examples{ 83 | library(vctrs) 84 | 85 | x <- iv_pairs( 86 | as.Date(c("2019-01-05", "2019-01-10")), 87 | as.Date(c("2019-01-07", "2019-01-15")), 88 | as.Date(c("2019-01-20", "2019-01-31")) 89 | ) 90 | 91 | y <- iv_pairs( 92 | as.Date(c("2019-01-01", "2019-01-03")), 93 | as.Date(c("2019-01-04", "2019-01-08")), 94 | as.Date(c("2019-01-07", "2019-01-09")), 95 | as.Date(c("2019-01-10", "2019-01-20")), 96 | as.Date(c("2019-01-15", "2019-01-20")) 97 | ) 98 | 99 | x 100 | y 101 | 102 | # Does each interval of `x` overlap `y` at all? 103 | iv_overlaps(x, y) 104 | 105 | # Which intervals of `y` are within an interval in `x`? 106 | iv_overlaps(y, x, type = "within") 107 | 108 | # --------------------------------------------------------------------------- 109 | 110 | a <- iv(c(1, NA), c(2, NA)) 111 | b <- iv(c(NA, NA), c(NA, NA)) 112 | 113 | # Missing intervals are seen as exactly equal by default, so they are 114 | # considered to overlap 115 | iv_overlaps(a, b) 116 | 117 | # If you'd like missing intervals to be treated as unmatched, set 118 | # `missing = FALSE` 119 | iv_overlaps(a, b, missing = FALSE) 120 | 121 | # If you'd like to propagate missing intervals, set `missing = NA` 122 | iv_overlaps(a, b, missing = NA) 123 | } 124 | \seealso{ 125 | \link[=relation-locate]{Locating relationships} 126 | 127 | \link[=relation-detect-pairwise]{Detecting relationships pairwise} 128 | 129 | \link[=allen-relation-locate]{Locating relations from Allen's Interval Algebra} 130 | } 131 | -------------------------------------------------------------------------------- /man/vector-count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vector.R 3 | \name{vector-count} 4 | \alias{vector-count} 5 | \alias{iv_count_between} 6 | \alias{iv_count_includes} 7 | \title{Count relationships between a vector and an iv} 8 | \usage{ 9 | iv_count_between(needles, haystack, ..., missing = "equals", no_match = 0L) 10 | 11 | iv_count_includes(needles, haystack, ..., missing = "equals", no_match = 0L) 12 | } 13 | \arguments{ 14 | \item{needles, haystack}{\verb{[vector, iv]} 15 | 16 | For \verb{iv_*_between()}, \code{needles} should be a vector and \code{haystack} should be 17 | an iv. 18 | 19 | For \verb{iv_*_includes()}, \code{needles} should be an iv and \code{haystack} should be 20 | a vector. 21 | \itemize{ 22 | \item Each element of \code{needles} represents the value / interval to match. 23 | \item \code{haystack} represents the values / intervals to match against. 24 | }} 25 | 26 | \item{...}{These dots are for future extensions and must be empty.} 27 | 28 | \item{missing}{\verb{[integer(1) / "equals" / "error"]} 29 | 30 | Handling of missing values in \code{needles}. 31 | \itemize{ 32 | \item \code{"equals"} considers missing values in \code{needles} as exactly equal 33 | to missing values in \code{haystack} when determining if there is a 34 | matching relationship between them. 35 | \item \code{"error"} throws an error if any values in \code{needles} are missing. 36 | \item If a single integer value is provided, this represents the count returned 37 | for a missing value in \code{needles}. Use \code{0L} to force missing values 38 | to never match. 39 | }} 40 | 41 | \item{no_match}{\verb{[integer(1) / "error"]} 42 | 43 | Handling of \code{needles} without a match. 44 | \itemize{ 45 | \item \code{"error"} throws an error if any needles have zero matches. 46 | \item If a single integer is provided, this represents the count returned for 47 | a needle with zero matches. The default value gives unmatched needles 48 | a count of \code{0L}. 49 | }} 50 | } 51 | \value{ 52 | An integer vector the same size as \code{needles}. 53 | } 54 | \description{ 55 | This family of functions counts different types of relationships between a 56 | vector and an iv. It works similar to \code{\link[base:match]{base::match()}}, where \code{needles[i]} 57 | checks for a match in all of \code{haystack}. 58 | \itemize{ 59 | \item \code{iv_count_between()} counts instances of when \code{needles}, a vector, falls 60 | between the bounds of \code{haystack}, an iv. 61 | \item \code{iv_count_includes()} counts instances of when \code{needles}, an iv, includes 62 | the values of \code{haystack}, a vector. 63 | } 64 | 65 | These functions return an integer vector the same size as \code{needles} 66 | containing a count of the times where the \code{i}-th value of \code{needles} contained 67 | a match in \code{haystack}. 68 | } 69 | \examples{ 70 | x <- as.Date(c("2019-01-05", "2019-01-10", "2019-01-07", "2019-01-20")) 71 | 72 | y <- iv_pairs( 73 | as.Date(c("2019-01-01", "2019-01-03")), 74 | as.Date(c("2019-01-04", "2019-01-08")), 75 | as.Date(c("2019-01-07", "2019-01-09")), 76 | as.Date(c("2019-01-10", "2019-01-20")), 77 | as.Date(c("2019-01-15", "2019-01-20")) 78 | ) 79 | 80 | x 81 | y 82 | 83 | # Count the number of times `x` is between the intervals in `y` 84 | iv_count_between(x, y) 85 | 86 | # Count the number of times `y` includes a value from `x` 87 | iv_count_includes(y, x) 88 | 89 | # --------------------------------------------------------------------------- 90 | 91 | a <- c(1, NA) 92 | b <- iv(c(NA, NA), c(NA, NA)) 93 | 94 | # By default, missing values in `needles` are treated as being exactly 95 | # equal to missing values in `haystack`, so the missing value in `a` is 96 | # considered between the missing interval in `b`. 97 | iv_count_between(a, b) 98 | iv_count_includes(b, a) 99 | 100 | # If you'd like to propagate missing values, set `missing = NA` 101 | iv_count_between(a, b, missing = NA) 102 | iv_count_includes(b, a, missing = NA) 103 | 104 | # If you'd like missing values to be treated as unmatched, set 105 | # `missing = 0L` 106 | iv_count_between(a, b, missing = 0L) 107 | iv_count_includes(b, a, missing = 0L) 108 | } 109 | \seealso{ 110 | \link[=vector-locate]{Locating relationships between a vector and an iv} 111 | } 112 | -------------------------------------------------------------------------------- /man/vector-detect-pairwise.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vector.R 3 | \name{vector-detect-pairwise} 4 | \alias{vector-detect-pairwise} 5 | \alias{iv_pairwise_between} 6 | \alias{iv_pairwise_includes} 7 | \title{Pairwise detect relationships between a vector and an iv} 8 | \usage{ 9 | iv_pairwise_between(x, y) 10 | 11 | iv_pairwise_includes(x, y) 12 | } 13 | \arguments{ 14 | \item{x, y}{\verb{[vector, iv]} 15 | 16 | For \code{iv_pairwise_between()}, \code{x} must be a vector and \code{y} must be an iv. 17 | 18 | For \code{iv_pairwise_includes()}, \code{x} must be an iv and \code{y} must be a vector. 19 | 20 | \code{x} and \code{y} will be recycled against each other.} 21 | } 22 | \value{ 23 | A logical vector the same size as the common size of \code{x} and \code{y}. 24 | } 25 | \description{ 26 | This family of functions detects different types of relationships between a 27 | vector and an iv \emph{pairwise}. where pairwise means that the i-th value of \code{x} 28 | is compared against the i-th value of \code{y}. This is in contrast to 29 | \code{\link[=iv_between]{iv_between()}}, which works more like \link[base:match]{base::\%in\%}. 30 | \itemize{ 31 | \item \code{iv_pairwise_between()} detects if the i-th value of \code{x}, a vector, falls 32 | between the bounds of the i-th value of \code{y}, an iv. 33 | \item \code{iv_pairwise_includes()} detects if the i-th value of \code{x}, an iv, includes 34 | the i-th value of \code{y}, a vector. 35 | } 36 | 37 | These functions return a logical vector the same size as the common size of 38 | \code{x} and \code{y}. 39 | } 40 | \examples{ 41 | x <- as.Date(c("2019-01-01", "2019-01-08", "2019-01-21")) 42 | 43 | y <- iv_pairs( 44 | as.Date(c("2019-01-01", "2019-01-03")), 45 | as.Date(c("2019-01-07", "2019-01-09")), 46 | as.Date(c("2019-01-18", "2019-01-21")) 47 | ) 48 | 49 | x 50 | y 51 | 52 | # Does the i-th value of `x` fall between the i-th interval of `y`? 53 | iv_pairwise_between(x, y) 54 | 55 | # Does the i-th interval of `y` include the i-th value of `x`? 56 | iv_pairwise_includes(y, x) 57 | 58 | a <- c(1, NA, NA) 59 | b <- iv_pairs(c(NA, NA), c(3, 4), c(NA, NA)) 60 | 61 | # Missing intervals always propagate 62 | iv_pairwise_between(a, b) 63 | iv_pairwise_includes(b, a) 64 | } 65 | \seealso{ 66 | \link[=relation-locate]{Locating relationships} 67 | 68 | \link[=vector-locate]{Locating relationships between a vector and an iv} 69 | 70 | \link[=vector-detect]{Detecting relationships between a vector and an iv} 71 | } 72 | -------------------------------------------------------------------------------- /man/vector-detect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vector.R 3 | \name{vector-detect} 4 | \alias{vector-detect} 5 | \alias{iv_between} 6 | \alias{iv_includes} 7 | \title{Detect relationships between a vector and an iv} 8 | \usage{ 9 | iv_between(needles, haystack, ..., missing = "equals") 10 | 11 | iv_includes(needles, haystack, ..., missing = "equals") 12 | } 13 | \arguments{ 14 | \item{needles, haystack}{\verb{[vector, iv]} 15 | 16 | For \verb{iv_*_between()}, \code{needles} should be a vector and \code{haystack} should be 17 | an iv. 18 | 19 | For \verb{iv_*_includes()}, \code{needles} should be an iv and \code{haystack} should be 20 | a vector. 21 | \itemize{ 22 | \item Each element of \code{needles} represents the value / interval to match. 23 | \item \code{haystack} represents the values / intervals to match against. 24 | }} 25 | 26 | \item{...}{These dots are for future extensions and must be empty.} 27 | 28 | \item{missing}{\verb{[logical(1) / "equals" / "error"]} 29 | 30 | Handling of missing values in \code{needles}. 31 | \itemize{ 32 | \item \code{"equals"} considers missing values in \code{needles} as exactly equal 33 | to missing values in \code{haystack} when determining if there is a 34 | matching relationship between them. Matched missing values in 35 | \code{needles} result in a \code{TRUE} value in the result, and unmatched missing 36 | values result in a \code{FALSE} value. 37 | \item \code{"error"} throws an error if any values in \code{needles} are missing. 38 | \item If a single logical value is provided, this represents the value returned 39 | in the result for values in \code{needles} that are missing. You can force 40 | missing values to be unmatched by setting this to \code{FALSE}, and you 41 | can force them to be propagated by setting this to \code{NA}. 42 | }} 43 | } 44 | \value{ 45 | A logical vector the same size as \code{needles}. 46 | } 47 | \description{ 48 | This family of functions detects different types of relationships between a 49 | vector and an iv. It works similar to \link[base:match]{base::\%in\%}, where \code{needles[i]} 50 | checks for a match in all of \code{haystack}. 51 | \itemize{ 52 | \item \code{iv_between()} detects when \code{needles}, a vector, falls between the 53 | bounds in \code{haystack}, an iv. 54 | \item \code{iv_includes()} detects when \code{needles}, an iv, includes the values 55 | of \code{haystack}, a vector. 56 | } 57 | 58 | This function returns a logical vector the same size as \code{needles} containing 59 | \code{TRUE} if the value in \code{needles} matches any value in \code{haystack} and \code{FALSE} 60 | otherwise. 61 | } 62 | \examples{ 63 | x <- as.Date(c("2019-01-05", "2019-01-10", "2019-01-07", "2019-01-20")) 64 | 65 | y <- iv_pairs( 66 | as.Date(c("2019-01-01", "2019-01-03")), 67 | as.Date(c("2019-01-04", "2019-01-08")), 68 | as.Date(c("2019-01-07", "2019-01-09")), 69 | as.Date(c("2019-01-10", "2019-01-20")), 70 | as.Date(c("2019-01-15", "2019-01-20")) 71 | ) 72 | 73 | x 74 | y 75 | 76 | # Detect if the i-th location in `x` is between any intervals in `y` 77 | iv_between(x, y) 78 | 79 | # Detect if the i-th location in `y` includes any value in `x` 80 | iv_includes(y, x) 81 | 82 | # --------------------------------------------------------------------------- 83 | 84 | a <- c(1, NA) 85 | b <- iv(c(NA, NA), c(NA, NA)) 86 | 87 | # By default, missing values in `needles` are treated as being exactly 88 | # equal to missing values in `haystack`, so the missing value in `a` is 89 | # considered between the missing interval in `b`. 90 | iv_between(a, b) 91 | iv_includes(b, a) 92 | 93 | # If you'd like to propagate missing values, set `missing = NA` 94 | iv_between(a, b, missing = NA) 95 | iv_includes(b, a, missing = NA) 96 | 97 | # If you'd like missing values to be treated as unmatched, set 98 | # `missing = FALSE` 99 | iv_between(a, b, missing = FALSE) 100 | iv_includes(b, a, missing = FALSE) 101 | } 102 | \seealso{ 103 | \link[=relation-locate]{Locating relationships} 104 | 105 | \link[=vector-locate]{Locating relationships between a vector and an iv} 106 | 107 | \link[=vector-detect-pairwise]{Pairwise detect relationships between a vector and an iv} 108 | } 109 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(ivs) 3 | 4 | test_check("ivs") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/align.md: -------------------------------------------------------------------------------- 1 | # `locations` is validated 2 | 3 | Code 4 | (expect_error(iv_align(1, 2, locations = 1))) 5 | Output 6 | 7 | Error in `iv_align()`: 8 | ! `locations` must be a data frame. 9 | Code 10 | (expect_error(iv_align(1, 2, locations = data_frame()))) 11 | Output 12 | 13 | Error in `iv_align()`: 14 | ! `locations` must be a two column data frame. 15 | Code 16 | (expect_error(iv_align(1, 2, locations = data_frame(x = 1, haystack = 2)))) 17 | Output 18 | 19 | Error in `iv_align()`: 20 | ! `locations` must have a column named "needles". 21 | Code 22 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1, x = 2)))) 23 | Output 24 | 25 | Error in `iv_align()`: 26 | ! `locations` must have a column named "haystack". 27 | Code 28 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1, haystack = 2L))) 29 | ) 30 | Output 31 | 32 | Error in `iv_align()`: 33 | ! `locations$needles` must be an integer vector. 34 | Code 35 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1L, haystack = 2))) 36 | ) 37 | Output 38 | 39 | Error in `iv_align()`: 40 | ! `locations$haystack` must be an integer vector. 41 | 42 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/containers.md: -------------------------------------------------------------------------------- 1 | # errors if an interval falls in multiple containers 2 | 3 | Code 4 | iv_identify_container(x) 5 | Condition 6 | Error in `iv_identify_container()`: 7 | ! Intervals in `x` can't fall within multiple containers. 8 | i Location 3 falls within multiple containers. 9 | i Use `iv_identify_containers()` to identify all of the containers that a particular interval is contained by. 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/count.md: -------------------------------------------------------------------------------- 1 | # drop is not allowed 2 | 3 | Code 4 | check_count_missing("drop") 5 | Condition 6 | Error: 7 | ! `missing` must be one of "equals" or "error", not "drop". 8 | 9 | --- 10 | 11 | Code 12 | check_count_no_match("drop") 13 | Condition 14 | Error: 15 | ! `no_match` must be one of "error", not "drop". 16 | 17 | # `missing` is checked 18 | 19 | Code 20 | check_count_missing(1.5) 21 | Condition 22 | Error: 23 | ! Can't convert from `missing` to due to loss of precision. 24 | * Locations: 1 25 | 26 | --- 27 | 28 | Code 29 | check_count_missing(c(1, 2)) 30 | Condition 31 | Error: 32 | ! `missing` must have size 1, not size 2. 33 | 34 | # `no_match` is checked 35 | 36 | Code 37 | check_count_no_match(1.5) 38 | Condition 39 | Error: 40 | ! Can't convert from `no_match` to due to loss of precision. 41 | * Locations: 1 42 | 43 | --- 44 | 45 | Code 46 | check_count_no_match(c(1, 2)) 47 | Condition 48 | Error: 49 | ! `no_match` must have size 1, not size 2. 50 | 51 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/diff.md: -------------------------------------------------------------------------------- 1 | # checks that `x` is a vector 2 | 3 | Code 4 | iv_diff(x) 5 | Condition 6 | Error in `iv_diff()`: 7 | ! `x` must be a vector, not an environment. 8 | 9 | # detects strictly increasing violations 10 | 11 | Code 12 | iv_diff(x) 13 | Condition 14 | Error in `iv_diff()`: 15 | ! `x` must be in strictly increasing order. 16 | i `x` is not in strictly increasing order at locations: `c(2, 4)`. 17 | 18 | # can detect strictly increasing violations in the presence of missings 19 | 20 | Code 21 | iv_diff(x) 22 | Condition 23 | Error in `iv_diff()`: 24 | ! `x` must be in strictly increasing order. 25 | i `x` is not in strictly increasing order at locations: `3`. 26 | 27 | --- 28 | 29 | Code 30 | iv_diff(x) 31 | Condition 32 | Error in `iv_diff()`: 33 | ! `x` must be in strictly increasing order. 34 | i `x` is not in strictly increasing order at locations: `c(5, 6)`. 35 | 36 | --- 37 | 38 | Code 39 | iv_diff(x) 40 | Condition 41 | Error in `iv_diff()`: 42 | ! `x` must be in strictly increasing order. 43 | i `x` is not in strictly increasing order at locations: `3`. 44 | 45 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/format.md: -------------------------------------------------------------------------------- 1 | # logical formatting 2 | 3 | Code 4 | iv(c(FALSE, NA), TRUE) 5 | Output 6 | [2]> 7 | [1] [FALSE, TRUE) [NA, NA) 8 | 9 | # integer formatting 10 | 11 | Code 12 | iv(c(1L, 100L), 200L) 13 | Output 14 | [2]> 15 | [1] [1, 200) [100, 200) 16 | Code 17 | iv(100000000L, 1000000000L) 18 | Output 19 | [1]> 20 | [1] [100000000, 1000000000) 21 | Code 22 | iv_pairs(c(1L, 5L), c(NA, NA)) 23 | Output 24 | [2]> 25 | [1] [1, 5) [NA, NA) 26 | 27 | # double formatting 28 | 29 | Code 30 | iv(c(1.5, 1000.5), 2000.5) 31 | Output 32 | [2]> 33 | [1] [1.5, 2000.5) [1000.5, 2000.5) 34 | Code 35 | iv(1e+08, 1e+09) 36 | Output 37 | [1]> 38 | [1] [100000000, 1000000000) 39 | Code 40 | iv_pairs(c(1, 5), c(NA, NA)) 41 | Output 42 | [2]> 43 | [1] [1, 5) [NA, NA) 44 | Code 45 | iv(100.555, 200) 46 | Output 47 | [1]> 48 | [1] [100.555, 200) 49 | Code 50 | iv(100000.5555, 2e+05) 51 | Output 52 | [1]> 53 | [1] [100000.6, 200000) 54 | Code 55 | with_options(digits = 12, print(iv(100000.5555, 2e+05))) 56 | Output 57 | [1]> 58 | [1] [100000.5555, 200000) 59 | 60 | # character formatting 61 | 62 | Code 63 | iv(c("x", "xxxxxxx"), "zzzzzzz") 64 | Output 65 | [2]> 66 | [1] [x, zzzzzzz) [xxxxxxx, zzzzzzz) 67 | Code 68 | iv_pairs(c("x", "z"), c(NA, NA)) 69 | Output 70 | [2]> 71 | [1] [x, z) [NA, NA) 72 | 73 | # factor formatting 74 | 75 | Code 76 | start <- factor(c("x", "xxxxxxx"), levels = c("x", "xxxxxxx", "zzzzzzz")) 77 | end <- factor("zzzzzzz", levels = c("x", "xxxxxxx", "zzzzzzz")) 78 | iv(start, end) 79 | Output 80 | >[2]> 81 | [1] [x, zzzzzzz) [xxxxxxx, zzzzzzz) 82 | Code 83 | iv_pairs(factor(c("x", "z")), factor(c(NA, NA))) 84 | Output 85 | >[2]> 86 | [1] [x, z) [NA, NA) 87 | 88 | # data frame formatting 89 | 90 | Code 91 | iv(vctrs::data_frame(x = 1:5), vctrs::data_frame(x = 2:6)) 92 | Output 93 | >[5]> 94 | [1] [df[1,], df[1,]) [df[2,], df[2,]) [df[3,], df[3,]) [df[4,], df[4,]) 95 | [5] [df[5,], df[5,]) 96 | Code 97 | iv(vctrs::data_frame(x = 1:5), vctrs::data_frame(x = c(2, 3, NA, 5, NA))) 98 | Output 99 | >[5]> 100 | [1] [df[1,], df[1,]) [df[2,], df[2,]) [NA, NA) [df[4,], df[4,]) 101 | [5] [NA, NA) 102 | Code 103 | start <- vctrs::data_frame(x = 1, y = "x", z = 4L) 104 | end <- vctrs::data_frame(x = 2, y = "z", z = 5L) 105 | iv(start, end) 106 | Output 107 | >[1]> 112 | [1] [df[1,], df[1,]) 113 | 114 | # Date formatting 115 | 116 | Code 117 | iv(as.Date(c("2019-01-01", "2019-01-05")), as.Date("2019-01-10")) 118 | Output 119 | [2]> 120 | [1] [2019-01-01, 2019-01-10) [2019-01-05, 2019-01-10) 121 | Code 122 | iv_pairs(as.Date(c("2019-01-01", "2019-01-05")), as.Date(c(NA, NA))) 123 | Output 124 | [2]> 125 | [1] [2019-01-01, 2019-01-05) [NA, NA) 126 | 127 | # POSIXt formatting 128 | 129 | Code 130 | iv(as.POSIXct(c("2019-01-01", "2019-01-05"), tz = "UTC"), as.POSIXct( 131 | "2019-01-10", tz = "UTC")) 132 | Output 133 | >[2]> 134 | [1] [2019-01-01 00:00:00, 2019-01-10 00:00:00) 135 | [2] [2019-01-05 00:00:00, 2019-01-10 00:00:00) 136 | Code 137 | iv_pairs(as.POSIXct(c("2019-01-01", "2019-01-05"), tz = "UTC"), as.POSIXct(c(NA, 138 | NA), tz = "UTC")) 139 | Output 140 | >[2]> 141 | [1] [2019-01-01 00:00:00, 2019-01-05 00:00:00) 142 | [2] [NA, NA) 143 | 144 | # difftime formatting 145 | 146 | Code 147 | iv(as.difftime(c(1, 100), units = "secs"), as.difftime(200, units = "secs")) 148 | Output 149 | >[2]> 150 | [1] [1 secs, 200 secs) [100 secs, 200 secs) 151 | Code 152 | iv(as.difftime(1e+08, units = "secs"), as.difftime(1e+09, units = "secs")) 153 | Output 154 | >[1]> 155 | [1] [100000000 secs, 1000000000 secs) 156 | Code 157 | iv(as.difftime(NA_real_, units = "secs"), as.difftime(NA_real_, units = "secs")) 158 | Output 159 | >[1]> 160 | [1] [NA secs, NA secs) 161 | 162 | # integer64 formatting 163 | 164 | Code 165 | start <- bit64::as.integer64(c(1, 100)) 166 | end <- bit64::as.integer64(200) 167 | iv(start, end) 168 | Output 169 | [2]> 170 | [1] [1, 200) [100, 200) 171 | Code 172 | start <- bit64::as.integer64(100000000L) 173 | end <- bit64::as.integer64(1000000000L) 174 | iv(start, end) 175 | Output 176 | [1]> 177 | [1] [100000000, 1000000000) 178 | Code 179 | start <- bit64::as.integer64(NA) 180 | end <- bit64::as.integer64(NA) 181 | iv(start, end) 182 | Output 183 | [1]> 184 | [1] [NA, NA) 185 | 186 | # iv formatting 187 | 188 | Code 189 | iv(iv(1:2, 5:6), iv(100, 200)) 190 | Output 191 | >[2]> 192 | [1] [[1, 5), [100, 200)) [[2, 6), [100, 200)) 193 | Code 194 | iv(iv(c(1, NA), c(5, NA)), iv(100, 200)) 195 | Output 196 | >[2]> 197 | [1] [[1, 5), [100, 200)) [[NA, NA), [NA, NA)) 198 | 199 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/iv.md: -------------------------------------------------------------------------------- 1 | # `start` must be less than `end` 2 | 3 | Code 4 | iv(2, 2) 5 | Condition 6 | Error in `iv()`: 7 | ! `start` must be less than `end`. 8 | i `start` is not less than `end` at locations: `1`. 9 | 10 | --- 11 | 12 | Code 13 | iv(3, 2) 14 | Condition 15 | Error in `iv()`: 16 | ! `start` must be less than `end`. 17 | i `start` is not less than `end` at locations: `1`. 18 | 19 | --- 20 | 21 | Code 22 | iv(x, y) 23 | Condition 24 | Error in `iv()`: 25 | ! `start` must be less than `end`. 26 | i `start` is not less than `end` at locations: `c(1, 2, 3, 4, 5)` and 15 more. 27 | 28 | # inputs must be type compatible 29 | 30 | Code 31 | iv("x", 1) 32 | Condition 33 | Error in `iv()`: 34 | ! Can't combine `start` and `end` . 35 | 36 | # inputs must be size compatible 37 | 38 | Code 39 | iv(1:2, 1:3) 40 | Condition 41 | Error in `iv()`: 42 | ! Can't recycle `start` (size 2) to match `end` (size 3). 43 | 44 | # inputs must be vectors 45 | 46 | Code 47 | iv(NULL, 2) 48 | Condition 49 | Error in `iv()`: 50 | ! `start` must be a vector. 51 | 52 | --- 53 | 54 | Code 55 | iv(2, NULL) 56 | Condition 57 | Error in `iv()`: 58 | ! `end` must be a vector. 59 | 60 | # inputs must be in pairs 61 | 62 | Code 63 | iv_pairs(c(1, 2), 3) 64 | Condition 65 | Error in `iv_pairs()`: 66 | ! All inputs must be in pairs of size 2. 67 | i Input 2 is size 1. 68 | 69 | # must have at least one input 70 | 71 | Code 72 | iv_pairs() 73 | Condition 74 | Error in `iv_pairs()`: 75 | ! Must supply at least one input. 76 | 77 | # pairs must be type compatible 78 | 79 | Code 80 | iv_pairs(c("a", "b"), c(1, 2)) 81 | Condition 82 | Error in `iv_pairs()`: 83 | ! Can't combine `..1` and `..2` . 84 | 85 | # can check if an object is an iv and error if not 86 | 87 | Code 88 | check_iv(1) 89 | Condition 90 | Error: 91 | ! `1` must be an , not the number 1. 92 | 93 | --- 94 | 95 | Code 96 | my_check() 97 | Condition 98 | Error in `my_check()`: 99 | ! `x` must be an , not absent. 100 | 101 | --- 102 | 103 | Code 104 | my_check(1) 105 | Condition 106 | Error in `my_check()`: 107 | ! `x` must be an , not the number 1. 108 | 109 | # ptype2 errors as needed 110 | 111 | Code 112 | vec_ptype2(iv("x", "y"), iv(1L, 2L)) 113 | Condition 114 | Error: 115 | ! Can't combine `iv("x", "y")` and `iv(1L, 2L)` . 116 | 117 | # cast errors as needed 118 | 119 | Code 120 | vec_cast(iv("x", "y"), iv(1L, 2L)) 121 | Condition 122 | Error: 123 | ! Can't convert `iv("x", "y")` to . 124 | 125 | # abbreviation is passed through to inner type 126 | 127 | Code 128 | vec_ptype_abbr(iv(1, 2)) 129 | Output 130 | [1] "iv" 131 | 132 | --- 133 | 134 | Code 135 | vec_ptype_abbr(iv(data_frame(x = 1), data_frame(x = 2))) 136 | Output 137 | [1] "iv" 138 | 139 | # full ptype is passed through to inner type 140 | 141 | Code 142 | vec_ptype_full(iv(1, 2)) 143 | Output 144 | [1] "iv" 145 | 146 | --- 147 | 148 | Code 149 | vec_ptype_full(iv(data_frame(x = 1, y = 2), data_frame(x = 2, y = 3))) 150 | Output 151 | [1] "iv>" 152 | 153 | # default restore error works 154 | 155 | Code 156 | iv_restore(1, 2) 157 | Condition 158 | Error in `iv_restore()`: 159 | ! Object `to`, with type , is not an and does not implement an `iv_restore()` method. 160 | 161 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ivs-deprecated.md: -------------------------------------------------------------------------------- 1 | # `iv_complement()` works but throws a deprecation warning 2 | 3 | Code 4 | out <- iv_complement(x) 5 | Condition 6 | Warning: 7 | `iv_complement()` was deprecated in ivs 0.2.0. 8 | i Please use `iv_set_complement()` instead. 9 | 10 | # `iv_union()` works but throws a deprecation warning 11 | 12 | Code 13 | out <- iv_union(x, y) 14 | Condition 15 | Warning: 16 | `iv_union()` was deprecated in ivs 0.2.0. 17 | i Please use `iv_set_union()` instead. 18 | 19 | # `iv_intersect()` works but throws a deprecation warning 20 | 21 | Code 22 | out <- iv_intersect(x, y) 23 | Condition 24 | Warning: 25 | `iv_intersect()` was deprecated in ivs 0.2.0. 26 | i Please use `iv_set_intersect()` instead. 27 | 28 | # `iv_difference()` works but throws a deprecation warning 29 | 30 | Code 31 | out <- iv_difference(x, y) 32 | Condition 33 | Warning: 34 | `iv_difference()` was deprecated in ivs 0.2.0. 35 | i Please use `iv_set_difference()` instead. 36 | 37 | # `iv_symmetric_difference()` works but throws a deprecation warning 38 | 39 | Code 40 | out <- iv_symmetric_difference(x, y) 41 | Condition 42 | Warning: 43 | `iv_symmetric_difference()` was deprecated in ivs 0.2.0. 44 | i Please use `iv_set_symmetric_difference()` instead. 45 | 46 | # `iv_pairwise_complement()` works but throws a deprecation warning 47 | 48 | Code 49 | out <- iv_pairwise_complement(x, y) 50 | Condition 51 | Warning: 52 | `iv_pairwise_complement()` was deprecated in ivs 0.2.0. 53 | i Please use `iv_pairwise_set_complement()` instead. 54 | 55 | # `iv_pairwise_union()` works but throws a deprecation warning 56 | 57 | Code 58 | out <- iv_pairwise_union(x, y) 59 | Condition 60 | Warning: 61 | `iv_pairwise_union()` was deprecated in ivs 0.2.0. 62 | i Please use `iv_pairwise_set_union()` instead. 63 | 64 | # `iv_pairwise_intersect()` works but throws a deprecation warning 65 | 66 | Code 67 | out <- iv_pairwise_intersect(x, y) 68 | Condition 69 | Warning: 70 | `iv_pairwise_intersect()` was deprecated in ivs 0.2.0. 71 | i Please use `iv_pairwise_set_intersect()` instead. 72 | 73 | # `iv_pairwise_difference()` works but throws a deprecation warning 74 | 75 | Code 76 | out <- iv_pairwise_difference(x, y) 77 | Condition 78 | Warning: 79 | `iv_pairwise_difference()` was deprecated in ivs 0.2.0. 80 | i Please use `iv_pairwise_set_difference()` instead. 81 | 82 | # `iv_pairwise_symmetric_difference()` works but throws a deprecation warning 83 | 84 | Code 85 | out <- iv_pairwise_symmetric_difference(x, y) 86 | Condition 87 | Warning: 88 | `iv_pairwise_symmetric_difference()` was deprecated in ivs 0.2.0. 89 | i Please use `iv_pairwise_set_symmetric_difference()` instead. 90 | 91 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/span.md: -------------------------------------------------------------------------------- 1 | # missing + `missing = error` errors 2 | 3 | Code 4 | iv_span(x, missing = "error") 5 | Condition 6 | Error in `iv_span()`: 7 | ! `x` can't contain missing values. 8 | 9 | # missing + `missing = drop` + `empty = error` errors 10 | 11 | Code 12 | iv_span(x, missing = "drop", empty = "error") 13 | Condition 14 | Error in `iv_span()`: 15 | ! `x` can't be empty. 16 | 17 | # empty + `empty = error` errors 18 | 19 | Code 20 | iv_span(x, empty = "error") 21 | Condition 22 | Error in `iv_span()`: 23 | ! `x` can't be empty. 24 | 25 | # span casts pre-proxied `empty` to pre-proxied type of `x` 26 | 27 | Code 28 | iv_span(x, empty = empty) 29 | Condition 30 | Error in `iv_span()`: 31 | ! Can't convert `empty` > to match type of `x` . 32 | 33 | --- 34 | 35 | Code 36 | iv_span(x, empty = empty) 37 | Condition 38 | Error in `iv_span()`: 39 | ! Can't convert `empty` to match type of `x` >. 40 | 41 | # span casts pre-proxied `missing` to pre-proxied type of `x` 42 | 43 | Code 44 | iv_span(x, missing = missing) 45 | Condition 46 | Error in `iv_span()`: 47 | ! Can't convert `missing` > to match type of `x` . 48 | 49 | --- 50 | 51 | Code 52 | iv_span(x, missing = missing) 53 | Condition 54 | Error in `iv_span()`: 55 | ! Can't convert `missing` to match type of `x` >. 56 | 57 | # errors on non-empty dots 58 | 59 | Code 60 | iv_span(x, 2) 61 | Condition 62 | Error in `iv_span()`: 63 | ! `...` must be empty. 64 | x Problematic argument: 65 | * ..1 = 2 66 | i Did you forget to name an argument? 67 | 68 | # validates `x` is an iv 69 | 70 | Code 71 | iv_span(1) 72 | Condition 73 | Error in `iv_span()`: 74 | ! `x` must be an , not the number 1. 75 | 76 | # validates `empty` 77 | 78 | Code 79 | iv_span(x, empty = "x") 80 | Condition 81 | Error in `iv_span()`: 82 | ! `empty` must be one of "missing" or "error", not "x". 83 | 84 | --- 85 | 86 | Code 87 | iv_span(x, empty = 1) 88 | Condition 89 | Error in `iv_span()`: 90 | ! `empty` must be a string or an iv, not the number 1. 91 | 92 | --- 93 | 94 | Code 95 | iv_span(x, empty = iv(1.5, 2.5)) 96 | Condition 97 | Error in `iv_span()`: 98 | ! Can't convert from `empty` to `x` due to loss of precision. 99 | * Locations: 1 100 | 101 | --- 102 | 103 | Code 104 | iv_span(x, empty = iv(1:2, 2:3)) 105 | Condition 106 | Error in `iv_span()`: 107 | ! `empty` must have size 1, not size 2. 108 | 109 | # validates `missing` 110 | 111 | Code 112 | iv_span(x, missing = "x") 113 | Condition 114 | Error in `iv_span()`: 115 | ! `missing` must be one of "propagate", "drop", or "error", not "x". 116 | 117 | --- 118 | 119 | Code 120 | iv_span(x, missing = 1) 121 | Condition 122 | Error in `iv_span()`: 123 | ! `missing` must be a string or an iv, not the number 1. 124 | 125 | --- 126 | 127 | Code 128 | iv_span(x, missing = iv(1.5, 2.5)) 129 | Condition 130 | Error in `iv_span()`: 131 | ! Can't convert from `missing` to `x` due to loss of precision. 132 | * Locations: 1 133 | 134 | --- 135 | 136 | Code 137 | iv_span(x, missing = iv(1:2, 2:3)) 138 | Condition 139 | Error in `iv_span()`: 140 | ! `missing` must have size 1, not size 2. 141 | 142 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/splits.md: -------------------------------------------------------------------------------- 1 | # casts `on` to type of `x` bounds 2 | 3 | Code 4 | (expect_error(iv_splits(x, on = "x"))) 5 | Output 6 | 7 | Error in `iv_splits()`: 8 | ! Can't convert `on` to match type of `iv_start(x)` . 9 | 10 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vector.md: -------------------------------------------------------------------------------- 1 | # between takes the common type 2 | 3 | Code 4 | (expect_error(iv_locate_between(1, iv("a", "b")))) 5 | Output 6 | 7 | Error in `iv_locate_between()`: 8 | ! Can't combine `needles` and `iv_start(haystack)` . 9 | 10 | # between can error on missing needles 11 | 12 | Code 13 | (expect_error(iv_locate_between(NA, iv(1, 2), missing = "error"))) 14 | Output 15 | 16 | Error in `iv_locate_between()`: 17 | ! `needles` can't contain missing values. 18 | x Location 1 contains missing values. 19 | 20 | --- 21 | 22 | Code 23 | (expect_error(iv_count_between(NA, iv(1, 2), missing = "error"))) 24 | Output 25 | 26 | Error in `iv_count_between()`: 27 | ! `needles` can't contain missing values. 28 | x Location 1 contains missing values. 29 | 30 | # between can error on invalid relationships 31 | 32 | Code 33 | (expect_error(iv_locate_between(x, y, relationship = "many-to-one"))) 34 | Output 35 | 36 | Error in `iv_locate_between()`: 37 | ! Each value of `needles` can match at most 1 value from `haystack`. 38 | x Location 1 of `needles` matches multiple values. 39 | 40 | # includes takes the common type 41 | 42 | Code 43 | (expect_error(iv_locate_includes(iv("a", "b"), 1))) 44 | Output 45 | 46 | Error in `iv_locate_includes()`: 47 | ! Can't combine `haystack` and `iv_start(needles)` . 48 | 49 | # includes can error on missing needles 50 | 51 | Code 52 | (expect_error(iv_locate_includes(iv(NA, NA), 1, missing = "error"))) 53 | Output 54 | 55 | Error in `iv_locate_includes()`: 56 | ! `needles` can't contain missing values. 57 | x Location 1 contains missing values. 58 | 59 | --- 60 | 61 | Code 62 | (expect_error(iv_count_includes(iv(NA, NA), 2, missing = "error"))) 63 | Output 64 | 65 | Error in `iv_count_includes()`: 66 | ! `needles` can't contain missing values. 67 | x Location 1 contains missing values. 68 | 69 | # includes can error on invalid relationships 70 | 71 | Code 72 | (expect_error(iv_locate_includes(x, y, relationship = "one-to-many"))) 73 | Output 74 | 75 | Error in `iv_locate_includes()`: 76 | ! Each value of `haystack` can match at most 1 value from `needles`. 77 | x Location 1 of `haystack` matches multiple values. 78 | 79 | # between can error on unmatched needles 80 | 81 | Code 82 | (expect_error(iv_count_between(3, iv(1, 2), no_match = "error"))) 83 | Output 84 | 85 | Error in `iv_count_between()`: 86 | ! Each value of `needles` must have a match in `haystack`. 87 | x Location 1 of `needles` does not have a match. 88 | 89 | # includes can error on unmatched needles 90 | 91 | Code 92 | (expect_error(iv_count_includes(iv(1, 2), 3, no_match = "error"))) 93 | Output 94 | 95 | Error in `iv_count_includes()`: 96 | ! Each value of `needles` must have a match in `haystack`. 97 | x Location 1 of `needles` does not have a match. 98 | 99 | # detect between takes the common type 100 | 101 | Code 102 | (expect_error(iv_between(1, iv("a", "b")))) 103 | Output 104 | 105 | Error in `iv_between()`: 106 | ! Can't combine `needles` and `iv_start(haystack)` . 107 | 108 | # detect between can error on missing needles 109 | 110 | Code 111 | (expect_error(iv_between(NA, iv(1, 2), missing = "error"))) 112 | Output 113 | 114 | Error in `iv_between()`: 115 | ! `needles` can't contain missing values. 116 | x Location 1 contains missing values. 117 | 118 | # detect includes takes the common type 119 | 120 | Code 121 | (expect_error(iv_includes(iv("a", "b"), 1))) 122 | Output 123 | 124 | Error in `iv_includes()`: 125 | ! Can't combine `haystack` and `iv_start(needles)` . 126 | 127 | # detect includes can error on missing needles 128 | 129 | Code 130 | (expect_error(iv_includes(iv(NA, NA), 2, missing = "error"))) 131 | Output 132 | 133 | Error in `iv_includes()`: 134 | ! `needles` can't contain missing values. 135 | x Location 1 contains missing values. 136 | 137 | # detect pairwise between takes the common type 138 | 139 | Code 140 | (expect_error(iv_pairwise_between(1, iv("a", "b")))) 141 | Output 142 | 143 | Error in `iv_pairwise_between()`: 144 | ! Can't combine `x` and `iv_start(y)` . 145 | 146 | # detect pairwise between throws recycling errors 147 | 148 | Code 149 | (expect_error(iv_pairwise_between(1:3, iv(1:2, 2:3)))) 150 | Output 151 | 152 | Error in `iv_pairwise_between()`: 153 | ! Can't recycle `x` (size 3) to match `iv_start(y)` (size 2). 154 | 155 | # detect pairwise includes takes the common type 156 | 157 | Code 158 | (expect_error(iv_pairwise_includes(iv("a", "b"), 1))) 159 | Output 160 | 161 | Error in `iv_pairwise_includes()`: 162 | ! Can't combine `y` and `iv_start(x)` . 163 | 164 | # detect pairwise includes throws recycling errors 165 | 166 | Code 167 | (expect_error(iv_pairwise_includes(iv(1:2, 2:3), 1:3))) 168 | Output 169 | 170 | Error in `iv_pairwise_includes()`: 171 | ! Can't recycle `y` (size 3) to match `iv_start(x)` (size 2). 172 | 173 | -------------------------------------------------------------------------------- /tests/testthat/test-align.R: -------------------------------------------------------------------------------- 1 | test_that("correctly aligns ivs", { 2 | needles <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 3 | haystack <- iv_pairs(c(0, 2), c(4, 6)) 4 | 5 | locations <- iv_locate_overlaps(needles, haystack) 6 | 7 | expect <- data_frame( 8 | needles = vec_slice(needles, locations$needles), 9 | haystack = vec_slice(haystack, locations$haystack) 10 | ) 11 | 12 | expect_identical( 13 | iv_align(needles, haystack, locations = locations), 14 | expect 15 | ) 16 | }) 17 | 18 | test_that("correctly aligns with between", { 19 | needles <- c(1, 15, 4, 11) 20 | haystack <- iv_pairs(c(1, 5), c(3, 7), c(10, 12)) 21 | 22 | locations <- iv_locate_between(needles, haystack) 23 | 24 | expect <- data_frame( 25 | needles = vec_slice(needles, locations$needles), 26 | haystack = vec_slice(haystack, locations$haystack) 27 | ) 28 | 29 | expect_identical( 30 | iv_align(needles, haystack, locations = locations), 31 | expect 32 | ) 33 | }) 34 | 35 | test_that("`locations` is validated", { 36 | expect_snapshot({ 37 | (expect_error(iv_align(1, 2, locations = 1))) 38 | (expect_error(iv_align(1, 2, locations = data_frame()))) 39 | (expect_error(iv_align(1, 2, locations = data_frame(x = 1, haystack = 2)))) 40 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1, x = 2)))) 41 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1, haystack = 2L)))) 42 | (expect_error(iv_align(1, 2, locations = data_frame(needles = 1L, haystack = 2)))) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /tests/testthat/test-containers.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # iv_containers() 3 | 4 | # Most tests handled by vctrs 5 | 6 | test_that("can compute containers", { 7 | x <- iv_pairs(c(1, 5), c(3, 5), c(4, 6)) 8 | expect_identical(iv_containers(x), iv_pairs(c(1, 5), c(4, 6))) 9 | }) 10 | 11 | test_that("works with empty iv", { 12 | x <- iv(integer(), integer()) 13 | expect_identical(iv_containers(x), x) 14 | }) 15 | 16 | test_that("duplicate intervals only return a single container", { 17 | x <- iv_pairs(c(1, 2), c(1, 2)) 18 | expect_identical(iv_containers(x), iv(1, 2)) 19 | }) 20 | 21 | test_that("containers are in sorted order", { 22 | x <- iv_pairs(c(4, 6), c(1, 5), c(0, 5), c(5, 7)) 23 | 24 | expect_identical( 25 | iv_containers(x), 26 | iv_pairs(c(0, 5), c(4, 6), c(5, 7)) 27 | ) 28 | }) 29 | 30 | test_that("retains missing at the end", { 31 | x <- iv_pairs(c(1, 3), c(NA, NA), c(NA, NA), c(0, 4)) 32 | expect_identical(iv_containers(x), iv_pairs(c(0, 4), c(NA, NA))) 33 | }) 34 | 35 | test_that("containers is generic", { 36 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 5), c(NA, NA)) 37 | 38 | expect_identical( 39 | iv_containers(x), 40 | nested_integer_iv_pairs(c(1, 5), c(NA, NA)) 41 | ) 42 | }) 43 | 44 | # ------------------------------------------------------------------------------ 45 | # iv_identify_containers() 46 | 47 | test_that("can identify containers", { 48 | x <- iv_pairs(c(2, 8), c(1, 6), c(2, 3)) 49 | 50 | expect_identical( 51 | iv_identify_containers(x), 52 | list_of(x[1], x[2], x[c(2, 1)]) 53 | ) 54 | }) 55 | 56 | test_that("missing intervals are identified", { 57 | x <- iv_pairs(c(NA, NA), c(1, 2), c(NA, NA)) 58 | 59 | expect_identical( 60 | iv_identify_containers(x), 61 | list_of(x[1], x[2], x[1]) 62 | ) 63 | }) 64 | 65 | test_that("identify containers works with single missing interval", { 66 | x <- iv(NA, NA) 67 | out <- iv_identify_containers(x) 68 | 69 | expect_identical(out, list_of(x)) 70 | expect_identical(attr(out, "ptype"), iv(logical(), logical())) 71 | }) 72 | 73 | test_that("identify containers is generic", { 74 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 5), c(NA, NA), c(2, 6)) 75 | 76 | expect_identical( 77 | iv_identify_containers(x), 78 | list_of(x[1], x[c(1, 4)], x[3], x[4]) 79 | ) 80 | }) 81 | 82 | # ------------------------------------------------------------------------------ 83 | # iv_identify_container() 84 | 85 | test_that("can identify container", { 86 | x <- iv_pairs(c(2, 8), c(1, 6), c(2, 7)) 87 | 88 | expect_identical( 89 | iv_identify_container(x), 90 | iv_pairs(c(2, 8), c(1, 6), c(2, 8)) 91 | ) 92 | }) 93 | 94 | test_that("errors if an interval falls in multiple containers", { 95 | x <- iv_pairs(c(2, 8), c(1, 6), c(2, 6)) 96 | 97 | expect_snapshot(error = TRUE, { 98 | iv_identify_container(x) 99 | }) 100 | }) 101 | 102 | test_that("identify container is generic", { 103 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 5), c(NA, NA), c(1, 6)) 104 | 105 | expect_identical( 106 | iv_identify_container(x), 107 | nested_integer_iv_pairs(c(1, 6), c(1, 6), c(NA, NA), c(1, 6)) 108 | ) 109 | }) 110 | 111 | # ------------------------------------------------------------------------------ 112 | # iv_locate_containers() 113 | 114 | test_that("can locate containers", { 115 | x <- iv_pairs(c(2, 8), c(1, 6), c(2, 6)) 116 | 117 | expect_identical( 118 | iv_locate_containers(x), 119 | data_frame( 120 | key = iv_pairs(c(1, 6), c(2, 8)), 121 | loc = list(c(2L, 3L), c(1L, 3L)) 122 | ) 123 | ) 124 | }) 125 | 126 | test_that("locate containers is generic", { 127 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 5), c(NA, NA), c(2, 6)) 128 | 129 | expect_identical( 130 | iv_locate_containers(x), 131 | data_frame( 132 | key = nested_integer_iv_pairs(c(1, 5), c(2, 6), c(NA, NA)), 133 | loc = list(c(1L, 2L), c(2L, 4L), 3L) 134 | ) 135 | ) 136 | }) 137 | -------------------------------------------------------------------------------- /tests/testthat/test-count.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # iv_count_locations() 3 | 4 | test_that("counts are returned in appearance order", { 5 | locations <- data_frame( 6 | needles = c(1L, 1L, 2L, 2L, 2L, 3L), 7 | haystack = c(2L, 2L, 2L, 2L, 2L, 2L) 8 | ) 9 | 10 | expect_identical( 11 | iv_count_locations(locations, missing = "error", no_match = "error"), 12 | c(2L, 3L, 1L) 13 | ) 14 | }) 15 | 16 | test_that("missings can be differentiated from no_match", { 17 | locations <- data_frame( 18 | needles = c(1L, 2L, 3L), 19 | haystack = c(2L, SIGNAL_MISSING, SIGNAL_NO_MATCH) 20 | ) 21 | 22 | expect_identical( 23 | iv_count_locations(locations, missing = 0L, no_match = NA_integer_), 24 | c(1L, 0L, NA) 25 | ) 26 | }) 27 | 28 | # ------------------------------------------------------------------------------ 29 | # check_count_missing() 30 | 31 | test_that("equals and error pass through", { 32 | expect_identical(check_count_missing("equals"), "equals") 33 | expect_identical(check_count_missing("error"), "error") 34 | }) 35 | 36 | test_that("drop is not allowed", { 37 | expect_snapshot(error = TRUE, check_count_missing("drop")) 38 | }) 39 | 40 | test_that("integerish is allowed", { 41 | expect_identical(check_count_missing(1), 1L) 42 | }) 43 | 44 | test_that("`missing` is checked", { 45 | expect_snapshot(error = TRUE, check_count_missing(1.5)) 46 | expect_snapshot(error = TRUE, check_count_missing(c(1, 2))) 47 | }) 48 | 49 | # ------------------------------------------------------------------------------ 50 | # translate_count_missing() 51 | 52 | test_that("string is left alone", { 53 | expect_identical(translate_count_missing("foo"), "foo") 54 | }) 55 | 56 | test_that("integer is converted to SIGNAL_MISSING", { 57 | expect_identical(translate_count_missing(1L), SIGNAL_MISSING) 58 | }) 59 | 60 | # ------------------------------------------------------------------------------ 61 | # check_count_no_match() 62 | 63 | test_that("error passes through", { 64 | expect_identical(check_count_no_match("error"), "error") 65 | }) 66 | 67 | test_that("drop is not allowed", { 68 | expect_snapshot(error = TRUE, check_count_no_match("drop")) 69 | }) 70 | 71 | test_that("integerish is allowed", { 72 | expect_identical(check_count_no_match(1), 1L) 73 | }) 74 | 75 | test_that("`no_match` is checked", { 76 | expect_snapshot(error = TRUE, check_count_no_match(1.5)) 77 | expect_snapshot(error = TRUE, check_count_no_match(c(1, 2))) 78 | }) 79 | 80 | # ------------------------------------------------------------------------------ 81 | # translate_count_no_match() 82 | 83 | test_that("string is left alone", { 84 | expect_identical(translate_count_no_match("foo"), "foo") 85 | }) 86 | 87 | test_that("integer is converted to SIGNAL_NO_MATCH", { 88 | expect_identical(translate_count_no_match(1L), SIGNAL_NO_MATCH) 89 | }) 90 | -------------------------------------------------------------------------------- /tests/testthat/test-diff.R: -------------------------------------------------------------------------------- 1 | test_that("can generate diffs", { 2 | x <- c(5, 6, 10, 20) 3 | expect_identical(iv_diff(x), iv_pairs(c(5, 6), c(6, 10), c(10, 20))) 4 | }) 5 | 6 | test_that("can propagate missings", { 7 | x <- c(NA, 1, 2) 8 | expect_identical(iv_diff(x), iv_pairs(c(NA, NA), c(1, 2))) 9 | 10 | x <- c(1, 2, NA) 11 | expect_identical(iv_diff(x), iv_pairs(c(1, 2), c(NA, NA))) 12 | 13 | x <- c(1, 2, NA, 3, 4) 14 | expect_identical(iv_diff(x), iv_pairs(c(1, 2), c(NA, NA), c(NA, NA), c(3, 4))) 15 | 16 | x <- c(NA, NA) 17 | expect_identical(iv_diff(x), iv_pairs(c(NA, NA))) 18 | }) 19 | 20 | test_that("checks that `x` is a vector", { 21 | x <- env() 22 | 23 | expect_snapshot(error = TRUE, { 24 | iv_diff(x) 25 | }) 26 | }) 27 | 28 | test_that("detects strictly increasing violations", { 29 | x <- c(1, 0, 2, 1, 2) 30 | 31 | expect_snapshot(error = TRUE, { 32 | iv_diff(x) 33 | }) 34 | }) 35 | 36 | test_that("can detect strictly increasing violations in the presence of missings", { 37 | x <- c(1, NA, 0) 38 | 39 | expect_snapshot(error = TRUE, { 40 | iv_diff(x) 41 | }) 42 | 43 | x <- c(NA, 0, 2, NA, 0, -1, NA) 44 | 45 | expect_snapshot(error = TRUE, { 46 | iv_diff(x) 47 | }) 48 | 49 | x <- data_frame(a = c(1, NA, 0, 2), b = c(2, NA, 3, 1)) 50 | 51 | expect_snapshot(error = TRUE, { 52 | iv_diff(x) 53 | }) 54 | }) 55 | 56 | test_that("incomplete values are promoted to fully missing", { 57 | x <- data_frame(a = c(1, NA, 4, 5), b = c(2, 3, 7, 8)) 58 | 59 | expect_identical( 60 | iv_diff(x), 61 | iv_pairs(c(NA, NA), c(NA, NA), vec_c(x[3,], x[4,])) 62 | ) 63 | }) 64 | 65 | test_that("incomplete rows become fully missing even if they are technically comparable (#36)", { 66 | # Row 1 is technically "greater than" row 2 if `vec_compare()` is used, but we 67 | # want incomplete rows to always be considered missing. 68 | x <- data_frame(a = c(1, 0, 3, 3), b = c(2, NA, 4, 5)) 69 | 70 | expect_identical( 71 | iv_diff(x), 72 | iv_pairs(c(NA, NA), c(NA, NA), vec_c(x[3,], x[4,])) 73 | ) 74 | }) 75 | 76 | test_that("works with size 1 input", { 77 | expect_identical(iv_diff(1), iv(double(), double())) 78 | 79 | x <- data_frame(a = 1, b = 2) 80 | expect_identical(iv_diff(x), iv(x[0,], x[0,])) 81 | 82 | x <- data_frame(a = 1, b = NA) 83 | expect_identical(iv_diff(x), iv(x[0,], x[0,])) 84 | }) 85 | 86 | test_that("works with single `NA`", { 87 | expect_identical(iv_diff(NA), iv(logical(), logical())) 88 | }) 89 | 90 | test_that("works with size 0 input", { 91 | expect_identical(iv_diff(double()), iv(double(), double())) 92 | 93 | x <- data_frame(a = double(), b = logical()) 94 | expect_identical(iv_diff(x), iv(x, x)) 95 | }) 96 | -------------------------------------------------------------------------------- /tests/testthat/test-format.R: -------------------------------------------------------------------------------- 1 | test_that("logical formatting", { 2 | expect_snapshot({ 3 | # With trimming (have to use missing to show trim) 4 | iv(c(FALSE, NA), TRUE) 5 | }) 6 | }) 7 | 8 | test_that("integer formatting", { 9 | expect_snapshot({ 10 | # With trimming 11 | iv(c(1L, 100L), 200L) 12 | 13 | # With no scientific format 14 | iv(1e8L, 1e9L) 15 | 16 | # Missings 17 | iv_pairs(c(1L, 5L), c(NA, NA)) 18 | }) 19 | }) 20 | 21 | test_that("double formatting", { 22 | expect_snapshot({ 23 | # With trimming 24 | iv(c(1.5, 1000.5), 2000.5) 25 | 26 | # With no scientific format 27 | iv(1e8, 1e9) 28 | 29 | # Missings 30 | iv_pairs(c(1, 5), c(NA, NA)) 31 | 32 | # With various decimal values 33 | iv(100.555, 200) 34 | 35 | # Same amount of rounding as `digits` 36 | iv(100000.5555, 200000) 37 | with_options(digits = 12, print(iv(100000.5555, 200000))) 38 | }) 39 | }) 40 | 41 | test_that("character formatting", { 42 | expect_snapshot({ 43 | # With no justification 44 | iv(c("x", "xxxxxxx"), "zzzzzzz") 45 | 46 | # Missings 47 | iv_pairs(c("x", "z"), c(NA, NA)) 48 | }) 49 | }) 50 | 51 | test_that("factor formatting", { 52 | expect_snapshot({ 53 | # With no justification 54 | start <- factor(c("x", "xxxxxxx"), levels = c("x", "xxxxxxx", "zzzzzzz")) 55 | end <- factor("zzzzzzz", levels = c("x", "xxxxxxx", "zzzzzzz")) 56 | iv(start, end) 57 | 58 | # Missings 59 | iv_pairs(factor(c("x", "z")), factor(c(NA, NA))) 60 | }) 61 | }) 62 | 63 | test_that("data frame formatting", { 64 | expect_snapshot({ 65 | iv(vctrs::data_frame(x = 1:5), vctrs::data_frame(x = 2:6)) 66 | 67 | # Missings 68 | iv(vctrs::data_frame(x = 1:5), vctrs::data_frame(x = c(2, 3, NA, 5, NA))) 69 | 70 | # Complex frame 71 | start <- vctrs::data_frame( 72 | x = 1, 73 | y = "x", 74 | z = 4L 75 | ) 76 | end <- vctrs::data_frame( 77 | x = 2, 78 | y = "z", 79 | z = 5L 80 | ) 81 | iv(start, end) 82 | }) 83 | }) 84 | 85 | test_that("Date formatting", { 86 | expect_snapshot({ 87 | # year-month-day format 88 | iv(as.Date(c("2019-01-01", "2019-01-05")), as.Date("2019-01-10")) 89 | 90 | # Missings 91 | iv_pairs(as.Date(c("2019-01-01", "2019-01-05")), as.Date(c(NA, NA))) 92 | }) 93 | }) 94 | 95 | test_that("POSIXt formatting", { 96 | expect_snapshot({ 97 | # year-month-day-hour-minute-second format 98 | iv( 99 | as.POSIXct(c("2019-01-01", "2019-01-05"), tz = "UTC"), 100 | as.POSIXct("2019-01-10", tz = "UTC") 101 | ) 102 | 103 | # Missings 104 | iv_pairs( 105 | as.POSIXct(c("2019-01-01", "2019-01-05"), tz = "UTC"), 106 | as.POSIXct(c(NA, NA), tz = "UTC") 107 | ) 108 | }) 109 | }) 110 | 111 | test_that("difftime formatting", { 112 | expect_snapshot({ 113 | # With trimming 114 | iv(as.difftime(c(1, 100), units = "secs"), as.difftime(200, units = "secs")) 115 | 116 | # With no scientific format 117 | iv(as.difftime(1e8, units = "secs"), as.difftime(1e9, units = "secs")) 118 | 119 | # Missings 120 | iv(as.difftime(NA_real_, units = "secs"), as.difftime(NA_real_, units = "secs")) 121 | }) 122 | }) 123 | 124 | test_that("integer64 formatting", { 125 | skip_if_not_installed("bit64") 126 | 127 | expect_snapshot({ 128 | # With trimming 129 | start <- bit64::as.integer64(c(1, 100)) 130 | end <- bit64::as.integer64(200) 131 | iv(start, end) 132 | 133 | # With no scientific format 134 | start <- bit64::as.integer64(1e8L) 135 | end <- bit64::as.integer64(1e9L) 136 | iv(start, end) 137 | 138 | # Missings 139 | start <- bit64::as.integer64(NA) 140 | end <- bit64::as.integer64(NA) 141 | iv(start, end) 142 | }) 143 | }) 144 | 145 | test_that("iv formatting", { 146 | expect_snapshot({ 147 | # Recursively avoid trimming 148 | iv(iv(1:2, 5:6), iv(100, 200)) 149 | 150 | # Missings 151 | iv(iv(c(1, NA), c(5, NA)), iv(100, 200)) 152 | }) 153 | }) 154 | -------------------------------------------------------------------------------- /tests/testthat/test-groups.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # iv_groups() 3 | 4 | # Most tests handled by vctrs 5 | 6 | test_that("can compute groups", { 7 | x <- iv_pairs(c(1, 3), c(3, 5)) 8 | expect_identical(iv_groups(x), iv(1, 5)) 9 | }) 10 | 11 | test_that("can choose not to group abutting", { 12 | x <- iv_pairs(c(1, 3), c(3, 5)) 13 | expect_identical(iv_groups(x, abutting = FALSE), x) 14 | }) 15 | 16 | test_that("can retain missing", { 17 | x <- iv_pairs(c(1, 3), c(NA, NA)) 18 | expect_identical(iv_groups(x), x) 19 | }) 20 | 21 | test_that("groups is generic", { 22 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 7)) 23 | 24 | expect_identical( 25 | iv_groups(x), 26 | nested_integer_iv_pairs(c(1, 7)) 27 | ) 28 | }) 29 | 30 | # ------------------------------------------------------------------------------ 31 | # iv_identify_group() 32 | 33 | test_that("computes the order on the group locations to map back to original locations", { 34 | x <- iv_pairs( 35 | c(1, 5), 36 | c(11, 14), 37 | c(5, 6), 38 | c(9, 12) 39 | ) 40 | 41 | expect <- iv_pairs( 42 | c(1, 6), 43 | c(9, 14), 44 | c(1, 6), 45 | c(9, 14) 46 | ) 47 | 48 | expect_identical(iv_identify_group(x), expect) 49 | }) 50 | 51 | test_that("identify group is generic", { 52 | x <- nested_integer_iv_pairs(c(1, 5), c(3, 7)) 53 | 54 | expect_identical( 55 | iv_identify_group(x), 56 | nested_integer_iv_pairs(c(1, 7), c(1, 7)) 57 | ) 58 | }) 59 | 60 | # ------------------------------------------------------------------------------ 61 | # iv_locate_groups() 62 | 63 | # Most tests handled by vctrs 64 | 65 | test_that("locates the groups", { 66 | x <- iv_pairs(c(1, 3), c(3, 5)) 67 | out <- iv_locate_groups(x) 68 | expect_identical(out$key, iv(1, 5)) 69 | expect_identical(out$loc, list(1:2)) 70 | }) 71 | 72 | test_that("can choose not to group abutting intervals", { 73 | x <- iv_pairs(c(1, 3), c(3, 5)) 74 | out <- iv_locate_groups(x, abutting = FALSE) 75 | expect_identical(out$key, x) 76 | expect_identical(out$loc, list(1L, 2L)) 77 | }) 78 | 79 | test_that("can retain missing", { 80 | x <- iv_pairs(c(1, 3), c(NA, NA)) 81 | out <- iv_locate_groups(x) 82 | expect_identical(out$key, x) 83 | expect_identical(out$loc, list(1L, 2L)) 84 | }) 85 | -------------------------------------------------------------------------------- /tests/testthat/test-ivs-deprecated.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # iv_complement() 3 | 4 | test_that("`iv_complement()` works but throws a deprecation warning", { 5 | x <- iv(1, 2) 6 | 7 | expect_snapshot({ 8 | out <- iv_complement(x) 9 | }) 10 | expect_identical( 11 | out, 12 | iv_set_complement(x) 13 | ) 14 | }) 15 | 16 | # ------------------------------------------------------------------------------ 17 | # iv_union() 18 | 19 | test_that("`iv_union()` works but throws a deprecation warning", { 20 | x <- iv(1, 3) 21 | y <- iv(2, 4) 22 | 23 | expect_snapshot({ 24 | out <- iv_union(x, y) 25 | }) 26 | expect_identical( 27 | out, 28 | iv_set_union(x, y) 29 | ) 30 | }) 31 | 32 | # ------------------------------------------------------------------------------ 33 | # iv_intersect() 34 | 35 | test_that("`iv_intersect()` works but throws a deprecation warning", { 36 | x <- iv(1, 3) 37 | y <- iv(2, 4) 38 | 39 | expect_snapshot({ 40 | out <- iv_intersect(x, y) 41 | }) 42 | expect_identical( 43 | out, 44 | iv_set_intersect(x, y) 45 | ) 46 | }) 47 | 48 | # ------------------------------------------------------------------------------ 49 | # iv_difference() 50 | 51 | test_that("`iv_difference()` works but throws a deprecation warning", { 52 | x <- iv(1, 3) 53 | y <- iv(2, 4) 54 | 55 | expect_snapshot({ 56 | out <- iv_difference(x, y) 57 | }) 58 | expect_identical( 59 | out, 60 | iv_set_difference(x, y) 61 | ) 62 | }) 63 | 64 | # ------------------------------------------------------------------------------ 65 | # iv_symmetric_difference() 66 | 67 | test_that("`iv_symmetric_difference()` works but throws a deprecation warning", { 68 | x <- iv(1, 3) 69 | y <- iv(2, 4) 70 | 71 | expect_snapshot({ 72 | out <- iv_symmetric_difference(x, y) 73 | }) 74 | expect_identical( 75 | out, 76 | iv_set_symmetric_difference(x, y) 77 | ) 78 | }) 79 | 80 | # ------------------------------------------------------------------------------ 81 | # iv_pairwise_complement() 82 | 83 | test_that("`iv_pairwise_complement()` works but throws a deprecation warning", { 84 | x <- iv(1, 3) 85 | y <- iv(5, 6) 86 | 87 | expect_snapshot({ 88 | out <- iv_pairwise_complement(x, y) 89 | }) 90 | expect_identical( 91 | out, 92 | iv_pairwise_set_complement(x, y) 93 | ) 94 | }) 95 | 96 | # ------------------------------------------------------------------------------ 97 | # iv_pairwise_union() 98 | 99 | test_that("`iv_pairwise_union()` works but throws a deprecation warning", { 100 | x <- iv(1, 3) 101 | y <- iv(2, 6) 102 | 103 | expect_snapshot({ 104 | out <- iv_pairwise_union(x, y) 105 | }) 106 | expect_identical( 107 | out, 108 | iv_pairwise_set_union(x, y) 109 | ) 110 | }) 111 | 112 | # ------------------------------------------------------------------------------ 113 | # iv_pairwise_intersect() 114 | 115 | test_that("`iv_pairwise_intersect()` works but throws a deprecation warning", { 116 | x <- iv(1, 3) 117 | y <- iv(2, 6) 118 | 119 | expect_snapshot({ 120 | out <- iv_pairwise_intersect(x, y) 121 | }) 122 | expect_identical( 123 | out, 124 | iv_pairwise_set_intersect(x, y) 125 | ) 126 | }) 127 | 128 | # ------------------------------------------------------------------------------ 129 | # iv_pairwise_difference() 130 | 131 | test_that("`iv_pairwise_difference()` works but throws a deprecation warning", { 132 | x <- iv(1, 3) 133 | y <- iv(2, 6) 134 | 135 | expect_snapshot({ 136 | out <- iv_pairwise_difference(x, y) 137 | }) 138 | expect_identical( 139 | out, 140 | iv_pairwise_set_difference(x, y) 141 | ) 142 | }) 143 | 144 | # ------------------------------------------------------------------------------ 145 | # iv_pairwise_symmetric_difference() 146 | 147 | test_that("`iv_pairwise_symmetric_difference()` works but throws a deprecation warning", { 148 | x <- iv(1, 3) 149 | y <- iv(1, 2) 150 | 151 | expect_snapshot({ 152 | out <- iv_pairwise_symmetric_difference(x, y) 153 | }) 154 | expect_identical( 155 | out, 156 | iv_pairwise_set_symmetric_difference(x, y) 157 | ) 158 | }) 159 | -------------------------------------------------------------------------------- /tests/testthat/test-span.R: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # iv_span() 3 | 4 | test_that("takes the span over the whole range", { 5 | x <- iv_pairs(c(1, 3), c(7, 10), c(-6, -2), c(9, 12)) 6 | 7 | expect_identical(iv_span(x), iv(-6, 12)) 8 | }) 9 | 10 | test_that("missing + `missing = propagate` returns missing value", { 11 | x <- iv_pairs(c(1, 3), c(NA, NA), c(-5, -1)) 12 | expect_identical(iv_span(x), iv(NA_real_, NA_real_)) 13 | }) 14 | 15 | test_that("missing + `missing = error` errors", { 16 | x <- iv_pairs(c(1, 3), c(NA, NA), c(-5, -1)) 17 | 18 | expect_snapshot(error = TRUE, { 19 | iv_span(x, missing = "error") 20 | }) 21 | }) 22 | 23 | test_that("missing + `missing = ` returns `missing` value", { 24 | x <- iv_pairs(c(1, 3), c(NA, NA), c(-5, -1)) 25 | missing <- iv(-Inf, Inf) 26 | expect_identical(iv_span(x, missing = missing), missing) 27 | }) 28 | 29 | test_that("missing + `missing = drop` filters out missings", { 30 | x <- iv_pairs(c(1, 3), c(NA, NA), c(-5, -1)) 31 | expect_identical(iv_span(x, missing = "drop"), iv(-5, 3)) 32 | }) 33 | 34 | test_that("missing + `missing = drop` + `empty = missing` returns missing value", { 35 | x <- iv_pairs(c(NA, NA), c(NA, NA)) 36 | expect_identical(iv_span(x, missing = "drop"), iv(NA, NA)) 37 | }) 38 | 39 | test_that("missing + `missing = drop` + `empty = error` errors", { 40 | x <- iv_pairs(c(NA, NA), c(NA, NA), ptype = double()) 41 | 42 | expect_snapshot(error = TRUE, { 43 | iv_span(x, missing = "drop", empty = "error") 44 | }) 45 | }) 46 | 47 | test_that("missing + `missing = drop` + `empty = ` returns `empty` value", { 48 | x <- iv_pairs(c(NA, NA), c(NA, NA), ptype = double()) 49 | empty <- iv(-Inf, Inf) 50 | expect_identical(iv_span(x, missing = "drop", empty = empty), empty) 51 | }) 52 | 53 | test_that("empty + `empty = missing` returns missing value", { 54 | x <- iv(integer(), integer()) 55 | expect_identical(iv_span(x), iv(NA_integer_, NA_integer_)) 56 | }) 57 | 58 | test_that("empty + `empty = error` errors", { 59 | x <- iv(integer(), integer()) 60 | 61 | expect_snapshot(error = TRUE, { 62 | iv_span(x, empty = "error") 63 | }) 64 | }) 65 | 66 | test_that("empty + `empty = ` returns `empty` value", { 67 | x <- iv(integer(), integer()) 68 | empty <- iv(0L, 1L) 69 | expect_identical(iv_span(x, empty = empty), empty) 70 | }) 71 | 72 | test_that("span is generic over the container", { 73 | x <- nested_integer_iv_pairs(c(-5, 0), c(2, 4)) 74 | expect_identical(iv_span(x), nested_integer_iv(-5, 4)) 75 | 76 | x <- nested_integer_iv_pairs(c(-5, 0), c(2, 4), c(NA, NA)) 77 | expect_identical(iv_span(x), nested_integer_iv(NA, NA)) 78 | expect_identical(iv_span(x, missing = "drop"), nested_integer_iv(-5, 4)) 79 | }) 80 | 81 | test_that("span casts pre-proxied `empty` to pre-proxied type of `x`", { 82 | x <- nested_integer_iv(integer(), integer()) 83 | empty <- nested_integer_iv(0, 1) 84 | expect_identical(iv_span(x, empty = empty), empty) 85 | 86 | x <- nested_integer_iv(integer(), integer()) 87 | empty <- iv(0, 1) 88 | expect_snapshot(error = TRUE, { 89 | iv_span(x, empty = empty) 90 | }) 91 | 92 | x <- iv(integer(), integer()) 93 | empty <- nested_integer_iv(0, 1) 94 | expect_snapshot(error = TRUE, { 95 | iv_span(x, empty = empty) 96 | }) 97 | }) 98 | 99 | test_that("span casts pre-proxied `missing` to pre-proxied type of `x`", { 100 | x <- nested_integer_iv(NA, NA) 101 | missing <- nested_integer_iv(0, 1) 102 | expect_identical(iv_span(x, missing = missing), missing) 103 | 104 | x <- nested_integer_iv(NA, NA) 105 | missing <- iv(0L, 1L) 106 | expect_snapshot(error = TRUE, { 107 | iv_span(x, missing = missing) 108 | }) 109 | 110 | x <- iv(NA, NA, ptype = integer()) 111 | missing <- nested_integer_iv(0, 1) 112 | expect_snapshot(error = TRUE, { 113 | iv_span(x, missing = missing) 114 | }) 115 | }) 116 | 117 | test_that("errors on non-empty dots", { 118 | x <- iv(1, 2) 119 | 120 | expect_snapshot(error = TRUE, { 121 | iv_span(x, 2) 122 | }) 123 | }) 124 | 125 | test_that("validates `x` is an iv", { 126 | expect_snapshot(error = TRUE, { 127 | iv_span(1) 128 | }) 129 | }) 130 | 131 | test_that("validates `empty`", { 132 | x <- iv(integer(), integer()) 133 | 134 | expect_snapshot(error = TRUE, { 135 | iv_span(x, empty = "x") 136 | }) 137 | expect_snapshot(error = TRUE, { 138 | iv_span(x, empty = 1) 139 | }) 140 | expect_snapshot(error = TRUE, { 141 | iv_span(x, empty = iv(1.5, 2.5)) 142 | }) 143 | expect_snapshot(error = TRUE, { 144 | iv_span(x, empty = iv(1:2, 2:3)) 145 | }) 146 | }) 147 | 148 | test_that("validates `missing`", { 149 | x <- iv(integer(), integer()) 150 | 151 | expect_snapshot(error = TRUE, { 152 | iv_span(x, missing = "x") 153 | }) 154 | expect_snapshot(error = TRUE, { 155 | iv_span(x, missing = 1) 156 | }) 157 | expect_snapshot(error = TRUE, { 158 | iv_span(x, missing = iv(1.5, 2.5)) 159 | }) 160 | expect_snapshot(error = TRUE, { 161 | iv_span(x, missing = iv(1:2, 2:3)) 162 | }) 163 | }) 164 | 165 | # ------------------------------------------------------------------------------ 166 | # iv_pairwise_span() 167 | 168 | test_that("can take the pairwise span", { 169 | x <- iv(1, 3) 170 | y <- iv(2, 4) 171 | 172 | expect_identical( 173 | iv_pairwise_span(x, y), 174 | iv(1, 4) 175 | ) 176 | 177 | y <- iv(3, 4) 178 | 179 | expect_identical( 180 | iv_pairwise_span(x, y), 181 | iv(1, 4) 182 | ) 183 | }) 184 | 185 | test_that("merges across gaps", { 186 | x <- iv(1, 3) 187 | y <- iv(4, 5) 188 | 189 | expect_identical(iv_pairwise_span(x, y), iv(1, 5)) 190 | expect_identical(iv_pairwise_span(y, x), iv(1, 5)) 191 | }) 192 | 193 | test_that("pairwise span propagates NAs", { 194 | x <- iv(c(0, NA), c(2, NA)) 195 | y <- iv(1, 4) 196 | 197 | expect_identical( 198 | iv_pairwise_span(x, y), 199 | iv(c(0, NA), c(4, NA)) 200 | ) 201 | expect_identical( 202 | iv_pairwise_span(y, x), 203 | iv(c(0, NA), c(4, NA)) 204 | ) 205 | }) 206 | 207 | test_that("pairwise span is generic over container", { 208 | x <- nested_integer_iv(1, 2) 209 | y <- nested_integer_iv(2, 3) 210 | expect_identical(iv_pairwise_span(x, y), nested_integer_iv(1, 3)) 211 | }) 212 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/examples.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Stack Overflow Examples" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Stack Overflow Examples} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | This vignette collects links to a number of Stack Overflow questions that use ivs to answer them. They are roughly grouped by the function used to solve the question. 18 | 19 | #### iv_groups() 20 | 21 | - [Flatten / Merge overlapping time periods by group](https://stackoverflow.com/questions/28938147/how-to-flatten-merge-overlapping-time-periods/71754454#71754454) 22 | 23 | - [Merge millisecond date-time periods](https://stackoverflow.com/questions/60322604/merge-overlapping-time-periods-with-milliseconds-in-r/71754233#71754233) 24 | 25 | - [Merge overlapping date-time intervals by group](https://stackoverflow.com/questions/53213418/collapse-and-merge-overlapping-time-intervals/71753991#71753991) 26 | 27 | - [Group hospital patient stay dates](https://stackoverflow.com/questions/72188780/grouping-dates-in-r-to-create-patient-episodes/72189705#72189705) 28 | 29 | - [Merge overlaps within groups](https://stackoverflow.com/questions/56190415/group-by-if-overlap/73636229#73636229) 30 | 31 | - [Detect self-overlaps within groups](https://stackoverflow.com/questions/34951356/r-find-overlap-among-time-periods/73639374#73639374) 32 | 33 | #### iv_identify_group() 34 | 35 | - [Identify overlapping date ranges](https://stackoverflow.com/questions/57208770/identify-overlapping-date-ranges-by-id-r/71753203#71753203) 36 | 37 | - [Determine if a date interval overlaps other date intervals](https://stackoverflow.com/questions/58283935/r-determine-if-each-date-interval-overlaps-with-all-other-date-intervals-in-a-d/71753341#71753341) 38 | 39 | - [Identify active maintanence types](https://stackoverflow.com/questions/72062736/generate-new-variable-based-on-start-and-stop-date-in-dplyr/72063499#72063499) 40 | 41 | #### iv_identify_splits() 42 | 43 | - [Find overlaps and create new rows for each overlap](https://stackoverflow.com/questions/46151452/find-overlapping-dates-for-each-id-and-create-a-new-row-for-the-overlap/71753687#71753687) 44 | 45 | #### iv_count_between() 46 | 47 | - [Aggregate rolling enrollment by month](https://stackoverflow.com/questions/71621389/use-dplyr-to-aggregate-counts-by-month-from-start-stop-ranged-variables/71752959#71752959) 48 | 49 | - [Another aggregate rolling enrollment by month](https://stackoverflow.com/questions/73561876/finding-how-many-records-are-open-in-a-month-in-a-dataset-containing-single-rec/73575090#73575090) 50 | 51 | #### iv_count_overlaps() 52 | 53 | - [Detect self-overlaps](https://stackoverflow.com/questions/40129485/overlap-ranges-in-single-dataframe/73636659#73636659) 54 | 55 | #### iv_count_includes() 56 | 57 | - [Track rolling monthly disease prevalence](https://stackoverflow.com/questions/75221096/how-to-aggregate-data-by-month-using-study-start-and-end-dates-in-r-to-calculate/75222987#75222987) 58 | 59 | #### iv_set_complement() 60 | 61 | - [Find ranges of dates where an apartment is empty](https://stackoverflow.com/questions/72536566/adding-rows-for-missing-intervals-between-existing-intervals-in-r/72547866#72547866) 62 | 63 | #### iv_locate_overlaps() 64 | 65 | - [Locate self overlaps and compute a proportion within those overlaps](https://stackoverflow.com/questions/72987129/count-instances-of-value-within-overlapping-dates/73039209#73039209) 66 | 67 | #### iv_overlaps() 68 | 69 | - [Detecting if there are any overlaps between two sets of ranges](https://stackoverflow.com/questions/26905601/comparing-and-finding-overlap-range-in-r/73639244#73639244) 70 | --------------------------------------------------------------------------------