diff --git a/dev/articles/stability.html b/dev/articles/stability.html
index f1073b9cc..48a769852 100644
--- a/dev/articles/stability.html
+++ b/dev/articles/stability.html
@@ -302,7 +302,7 @@
Incompatible vectors and non-vecto
#> [[1]]
#> function (x, ...)
#> UseMethod("mean")
-#> <bytecode: 0x5610211a7640>
+#> <bytecode: 0x560544655640>
#> <environment: namespace:base>
#>
#> [[2]]
@@ -577,7 +577,7 @@
#> [1] <NA> big big small small
#> Levels: small big
if_else ( x > 2 , Sys.Date ( ) , Sys.Date ( ) + 7 )
-#> [1] NA "2024-05-07" "2024-05-07" "2024-04-30" "2024-04-30"
+#> [1] NA "2024-05-21" "2024-05-21" "2024-05-14" "2024-05-14"
By using vec_size()
and vec_slice()
, this
definition of if_else()
automatically works with
data.frames and matrices:
diff --git a/dev/news/index.html b/dev/news/index.html
index 4c678f411..ae06a4d27 100644
--- a/dev/news/index.html
+++ b/dev/news/index.html
@@ -55,7 +55,9 @@
vctrs (development version)
-
+
+vec_detect_complete(NULL)
now returns logical()
, consistent with vec_detect_missing(NULL)
(#1916 ).
+
vctrs 0.6.5 CRAN release: 2023-12-01
Internal changes requested by CRAN around C level format strings (#1896 ).
diff --git a/dev/pkgdown.yml b/dev/pkgdown.yml
index d0bf5d807..5d21b16e2 100644
--- a/dev/pkgdown.yml
+++ b/dev/pkgdown.yml
@@ -6,7 +6,7 @@ articles:
s3-vector: s3-vector.html
stability: stability.html
type-size: type-size.html
-last_built: 2024-04-30T09:58Z
+last_built: 2024-05-14T15:28Z
urls:
reference: https://vctrs.r-lib.org/reference
article: https://vctrs.r-lib.org/articles
diff --git a/dev/reference/unspecified.html b/dev/reference/unspecified.html
index 0dbf8bf3f..352643462 100644
--- a/dev/reference/unspecified.html
+++ b/dev/reference/unspecified.html
@@ -87,9 +87,9 @@
On this page
diff --git a/dev/search.json b/dev/search.json
index 843c8e5fc..07a802019 100644
--- a/dev/search.json
+++ b/dev/search.json
@@ -1 +1 @@
-[{"path":[]},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"our-pledge","dir":"","previous_headings":"","what":"Our Pledge","title":"Contributor Covenant Code of Conduct","text":"members, contributors, leaders pledge make participation community harassment-free experience everyone, regardless age, body size, visible invisible disability, ethnicity, sex characteristics, gender identity expression, level experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, sexual identity orientation. pledge act interact ways contribute open, welcoming, diverse, inclusive, healthy community.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"our-standards","dir":"","previous_headings":"","what":"Our Standards","title":"Contributor Covenant Code of Conduct","text":"Examples behavior contributes positive environment community include: Demonstrating empathy kindness toward people respectful differing opinions, viewpoints, experiences Giving gracefully accepting constructive feedback Accepting responsibility apologizing affected mistakes, learning experience Focusing best just us individuals, overall community Examples unacceptable behavior include: use sexualized language imagery, sexual attention advances kind Trolling, insulting derogatory comments, personal political attacks Public private harassment Publishing others’ private information, physical email address, without explicit permission conduct reasonably considered inappropriate professional setting","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"enforcement-responsibilities","dir":"","previous_headings":"","what":"Enforcement Responsibilities","title":"Contributor Covenant Code of Conduct","text":"Community leaders responsible clarifying enforcing standards acceptable behavior take appropriate fair corrective action response behavior deem inappropriate, threatening, offensive, harmful. Community leaders right responsibility remove, edit, reject comments, commits, code, wiki edits, issues, contributions aligned Code Conduct, communicate reasons moderation decisions appropriate.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"scope","dir":"","previous_headings":"","what":"Scope","title":"Contributor Covenant Code of Conduct","text":"Code Conduct applies within community spaces, also applies individual officially representing community public spaces. Examples representing community include using official e-mail address, posting via official social media account, acting appointed representative online offline event.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"enforcement","dir":"","previous_headings":"","what":"Enforcement","title":"Contributor Covenant Code of Conduct","text":"Instances abusive, harassing, otherwise unacceptable behavior may reported community leaders responsible enforcement codeofconduct@posit.co. complaints reviewed investigated promptly fairly. community leaders obligated respect privacy security reporter incident.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"enforcement-guidelines","dir":"","previous_headings":"","what":"Enforcement Guidelines","title":"Contributor Covenant Code of Conduct","text":"Community leaders follow Community Impact Guidelines determining consequences action deem violation Code Conduct:","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"id_1-correction","dir":"","previous_headings":"Enforcement Guidelines","what":"1. Correction","title":"Contributor Covenant Code of Conduct","text":"Community Impact: Use inappropriate language behavior deemed unprofessional unwelcome community. Consequence: private, written warning community leaders, providing clarity around nature violation explanation behavior inappropriate. public apology may requested.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"id_2-warning","dir":"","previous_headings":"Enforcement Guidelines","what":"2. Warning","title":"Contributor Covenant Code of Conduct","text":"Community Impact: violation single incident series actions. Consequence: warning consequences continued behavior. interaction people involved, including unsolicited interaction enforcing Code Conduct, specified period time. includes avoiding interactions community spaces well external channels like social media. Violating terms may lead temporary permanent ban.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"id_3-temporary-ban","dir":"","previous_headings":"Enforcement Guidelines","what":"3. Temporary Ban","title":"Contributor Covenant Code of Conduct","text":"Community Impact: serious violation community standards, including sustained inappropriate behavior. Consequence: temporary ban sort interaction public communication community specified period time. public private interaction people involved, including unsolicited interaction enforcing Code Conduct, allowed period. Violating terms may lead permanent ban.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"id_4-permanent-ban","dir":"","previous_headings":"Enforcement Guidelines","what":"4. Permanent Ban","title":"Contributor Covenant Code of Conduct","text":"Community Impact: Demonstrating pattern violation community standards, including sustained inappropriate behavior, harassment individual, aggression toward disparagement classes individuals. Consequence: permanent ban sort public interaction within community.","code":""},{"path":"https://vctrs.r-lib.org/dev/CODE_OF_CONDUCT.html","id":"attribution","dir":"","previous_headings":"","what":"Attribution","title":"Contributor Covenant Code of Conduct","text":"Code Conduct adapted Contributor Covenant, version 2.1, available https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. Community Impact Guidelines inspired [Mozilla’s code conduct enforcement ladder][https://github.com/mozilla/inclusion]. answers common questions code conduct, see FAQ https://www.contributor-covenant.org/faq. Translations available https://www.contributor-covenant.org/translations.","code":""},{"path":"https://vctrs.r-lib.org/dev/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2023 vctrs authors Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"prerequisites","dir":"Articles","previous_headings":"","what":"Prerequisites","title":"Printing vectors nicely in tibbles","text":"illustrate basic ideas ’re going create \"latlon\" class encodes geographic coordinates record. ’ll pretend code lives package called earth. simplicity, values printed degrees minutes . using vctrs_rcrd(), already get infrastructure make class fully compatible data frames free. See vignette(\"s3-vector\", package = \"vctrs\") details record data type.","code":"#' @export latlon <- function(lat, lon) { new_rcrd(list(lat = lat, lon = lon), class = \"earth_latlon\") } #' @export format.earth_latlon <- function(x, ..., formatter = deg_min) { x_valid <- which(!is.na(x)) lat <- field(x, \"lat\")[x_valid] lon <- field(x, \"lon\")[x_valid] ret <- rep(NA_character_, vec_size(x)) ret[x_valid] <- paste0(formatter(lat, \"lat\"), \" \", formatter(lon, \"lon\")) # It's important to keep NA in the vector! ret } deg_min <- function(x, direction) { pm <- if (direction == \"lat\") c(\"N\", \"S\") else c(\"E\", \"W\") sign <- sign(x) x <- abs(x) deg <- trunc(x) x <- x - deg min <- round(x * 60) # Ensure the columns are always the same width so they line up nicely ret <- sprintf(\"%d°%.2d'%s\", deg, min, ifelse(sign >= 0, pm[[1]], pm[[2]])) format(ret, justify = \"right\") } latlon(c(32.71, 2.95), c(-117.17, 1.67)) #> #> [1] 32°43'N 117°10'W 2°57'N 1°40'E"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"using-in-a-tibble","dir":"Articles","previous_headings":"","what":"Using in a tibble","title":"Printing vectors nicely in tibbles","text":"Columns class can used tibble right away ’ve made class using vctrs infrastructure provided format() method: output ok, improve : Using description type abbreviation . Using dash colour highlight important parts value. Providing narrower view horizontal space premium. following sections show enhance rendering.","code":"library(tibble) #> #> Attaching package: 'tibble' #> The following object is masked from 'package:vctrs': #> #> data_frame loc <- latlon( c(28.3411783, 32.7102978, 30.2622356, 37.7859102, 28.5, NA), c(-81.5480348, -117.1704058, -97.7403327, -122.4131357, -81.4, NA) ) data <- tibble(venue = \"rstudio::conf\", year = 2017:2022, loc = loc) data #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::conf 2017 28°20'N 81°33'W #> 2 rstudio::conf 2018 32°43'N 117°10'W #> 3 rstudio::conf 2019 30°16'N 97°44'W #> 4 rstudio::conf 2020 37°47'N 122°25'W #> 5 rstudio::conf 2021 28°30'N 81°24'W #> 6 rstudio::conf 2022 NA"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"fixing-the-data-type","dir":"Articles","previous_headings":"","what":"Fixing the data type","title":"Printing vectors nicely in tibbles","text":"Instead ’d prefer use . can implementing vec_ptype_abbr() method, return string can used column header. classes, strive evocative abbreviation ’s 6 characters.","code":"#' @export vec_ptype_abbr.earth_latlon <- function(x) { \"latlon\" } data #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::conf 2017 28°20'N 81°33'W #> 2 rstudio::conf 2018 32°43'N 117°10'W #> 3 rstudio::conf 2019 30°16'N 97°44'W #> 4 rstudio::conf 2020 37°47'N 122°25'W #> 5 rstudio::conf 2021 28°30'N 81°24'W #> 6 rstudio::conf 2022 NA"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"custom-rendering","dir":"Articles","previous_headings":"","what":"Custom rendering","title":"Printing vectors nicely in tibbles","text":"format() method used default rendering. custom formatting need implement pillar_shaft() method. function always return pillar shaft object, created new_pillar_shaft_simple() similar. new_pillar_shaft_simple() accepts ANSI escape codes colouring, pillar includes built styles like style_subtle(). can use subtle style degree minute separators make data obvious. First define degree formatter makes use style_subtle(): pass format() method: Currently, ANSI escapes rendered vignettes, result doesn’t look different, run code ’ll see improved display. well functions pillar, cli package provides variety tools styling text.","code":"deg_min_color <- function(x, direction) { pm <- if (direction == \"lat\") c(\"N\", \"S\") else c(\"E\", \"W\") sign <- sign(x) x <- abs(x) deg <- trunc(x) x <- x - deg rad <- round(x * 60) ret <- sprintf( \"%d%s%.2d%s%s\", deg, pillar::style_subtle(\"°\"), rad, pillar::style_subtle(\"'\"), pm[ifelse(sign >= 0, 1, 2)] ) format(ret, justify = \"right\") } #' @importFrom pillar pillar_shaft #' @export pillar_shaft.earth_latlon <- function(x, ...) { out <- format(x, formatter = deg_min_color) pillar::new_pillar_shaft_simple(out, align = \"right\") } data #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::conf 2017 28°20'N 81°33'W #> 2 rstudio::conf 2018 32°43'N 117°10'W #> 3 rstudio::conf 2019 30°16'N 97°44'W #> 4 rstudio::conf 2020 37°47'N 122°25'W #> 5 rstudio::conf 2021 28°30'N 81°24'W #> 6 rstudio::conf 2022 NA"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"truncation","dir":"Articles","previous_headings":"","what":"Truncation","title":"Printing vectors nicely in tibbles","text":"Tibbles can automatically compacts columns ’s enough horizontal space display everything: Currently latlon class isn’t ever compacted haven’t specified minimum width constructing shaft. Let’s fix re-print data:","code":"print(data, width = 30) #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstu… 2017 28°20'N 81°33'W #> 2 rstu… 2018 32°43'N 117°10'W #> 3 rstu… 2019 30°16'N 97°44'W #> 4 rstu… 2020 37°47'N 122°25'W #> 5 rstu… 2021 28°30'N 81°24'W #> 6 rstu… 2022 NA #' @importFrom pillar pillar_shaft #' @export pillar_shaft.earth_latlon <- function(x, ...) { out <- format(x) pillar::new_pillar_shaft_simple(out, align = \"right\", min_width = 10) } print(data, width = 30) #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::c… 2017 28°20'N … #> 2 rstudio::c… 2018 32°43'N 1… #> 3 rstudio::c… 2019 30°16'N … #> 4 rstudio::c… 2020 37°47'N 1… #> 5 rstudio::c… 2021 28°30'N … #> 6 rstudio::c… 2022 NA"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"adaptive-rendering","dir":"Articles","previous_headings":"","what":"Adaptive rendering","title":"Printing vectors nicely in tibbles","text":"Truncation may useful character data, lat-lon data ’d nicer show full degrees remove minutes. ’ll first write function : use part sophisticated implementation pillar_shaft() method: Now pillar_shaft() method returns object class \"pillar_shaft_latlon\" created new_pillar_shaft(). object contains necessary information render values, also minimum maximum width values. simplicity, formats pre-rendered, minimum maximum widths computed . (get_max_extent() helper computes maximum display width occupied values character vector.) ’s left implement format() method new \"pillar_shaft_latlon\" class. method called width argument, determines formats choose. formatting choice passed new_ornament() function:","code":"deg <- function(x, direction) { pm <- if (direction == \"lat\") c(\"N\", \"S\") else c(\"E\", \"W\") sign <- sign(x) x <- abs(x) deg <- round(x) ret <- sprintf(\"%d°%s\", deg, pm[ifelse(sign >= 0, 1, 2)]) format(ret, justify = \"right\") } #' @importFrom pillar pillar_shaft #' @export pillar_shaft.earth_latlon <- function(x, ...) { deg <- format(x, formatter = deg) deg_min <- format(x) pillar::new_pillar_shaft( list(deg = deg, deg_min = deg_min), width = pillar::get_max_extent(deg_min), min_width = pillar::get_max_extent(deg), class = \"pillar_shaft_latlon\" ) } #' @export format.pillar_shaft_latlon <- function(x, width, ...) { if (get_max_extent(x$deg_min) <= width) { ornament <- x$deg_min } else { ornament <- x$deg } pillar::new_ornament(ornament, align = \"right\") } data #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::conf 2017 28°20'N 81°33'W #> 2 rstudio::conf 2018 32°43'N 117°10'W #> 3 rstudio::conf 2019 30°16'N 97°44'W #> 4 rstudio::conf 2020 37°47'N 122°25'W #> 5 rstudio::conf 2021 28°30'N 81°24'W #> 6 rstudio::conf 2022 NA print(data, width = 30) #> # A tibble: 6 × 3 #> venue year loc #> #> 1 rstudio::c… 2017 28°N 82°W #> 2 rstudio::c… 2018 33°N 117°W #> 3 rstudio::c… 2019 30°N 98°W #> 4 rstudio::c… 2020 38°N 122°W #> 5 rstudio::c… 2021 28°N 81°W #> 6 rstudio::c… 2022 NA"},{"path":"https://vctrs.r-lib.org/dev/articles/pillar.html","id":"testing","dir":"Articles","previous_headings":"","what":"Testing","title":"Printing vectors nicely in tibbles","text":"want test output code, can compare known state recorded text file. testthat::expect_snapshot() function offers easy way test output-generating functions. takes care details Unicode, ANSI escapes, output width. Furthermore won’t make tests fail CRAN. important output may rely details control, fixed eventually lead package removed CRAN. Use testthat expectation one test files create snapshot test: See https://testthat.r-lib.org/articles/snapshotting.html information.","code":"expect_snapshot(pillar_shaft(data$loc))"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"basics","dir":"Articles","previous_headings":"","what":"Basics","title":"S3 vectors","text":"section ’ll learn create new vctrs class calling new_vctr(). creates object class vctrs_vctr number methods. designed make life easy possible. example: print() str() methods defined terms format() get pleasant, consistent display soon ’ve made format() method. can immediately put new vector class data frame .data.frame.vctrs_vctr() right thing. Subsetting ([, [[, $), length<-, rep() methods automatically preserve attributes use vec_restore(). default vec_restore() works classes attributes data-independent, can easily customised attributes depend data. Default subset-assignment methods ([<-, [[<-, $<-) follow principle new values coerced match existing vector. gives predictable behaviour clear error messages.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"percent-class","dir":"Articles","previous_headings":"Basics","what":"Percent class","title":"S3 vectors","text":"section, ’ll show make percent class, .e., double vector printed percentage. start defining low-level constructor check types /sizes call new_vctr(). percent built double vector length doesn’t attributes. Note prefix name class name package. prevents conflicting definitions packages. packages implement one class (blob), ’s fine use package name without prefix class name. follow user friendly helper. ’ll use vec_cast() allow accept anything coercible double: go , check user-friendly constructor returns zero-length vector called arguments. makes easy use prototype. convenience users, consider implementing is_percent() function:","code":"new_percent <- function(x = double()) { if (!is_double(x)) { abort(\"`x` must be a double vector.\") } new_vctr(x, class = \"vctrs_percent\") } x <- new_percent(c(seq(0, 1, length.out = 4), NA)) x #> #> [1] 0.0000000 0.3333333 0.6666667 1.0000000 NA str(x) #> vctrs_pr [1:5] 0.0000000, 0.3333333, 0.6666667, 1.0000000, NA percent <- function(x = double()) { x <- vec_cast(x, double()) new_percent(x) } new_percent() #> percent() #> is_percent <- function(x) { inherits(x, \"vctrs_percent\") }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"format-method","dir":"Articles","previous_headings":"Basics","what":"format() method","title":"S3 vectors","text":"first method every class almost always format() method. return character vector length x. easiest way rely one R’s low-level formatting functions like formatC(): (Note use vec_data() format() doesn’t get stuck infinite loop, take little care convert NA \"NA\"; leads better printing.) format method also used data frames, tibbles, str(): optimal display, recommend also defining abbreviated type name, 4-5 letters commonly used vectors. used tibbles str(): need control printing tibbles, implement method pillar::pillar_shaft(). See vignette(\"pillar\", package = \"vctrs\") details.","code":"format.vctrs_percent <- function(x, ...) { out <- formatC(signif(vec_data(x) * 100, 3)) out[is.na(x)] <- NA out[!is.na(x)] <- paste0(out[!is.na(x)], \"%\") out } x #> #> [1] 0% 33.3% 66.7% 100% data.frame(x) #> x #> 1 0% #> 2 33.3% #> 3 66.7% #> 4 100% #> 5 vec_ptype_abbr.vctrs_percent <- function(x, ...) { \"prcnt\" } tibble::tibble(x) #> # A tibble: 5 × 1 #> x #> #> 1 0% #> 2 33.3% #> 3 66.7% #> 4 100% #> 5 NA str(x) #> prcnt [1:5] 0%, 33.3%, 66.7%, 100%, "},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"casting-and-coercion","dir":"Articles","previous_headings":"","what":"Casting and coercion","title":"S3 vectors","text":"next set methods likely need related coercion casting. Coercion casting two sides coin: changing prototype existing object. change happens implicitly (e.g c()) call coercion; change happens explicitly (e.g. .integer(x)), call casting. One main goals vctrs put coercion casting robust theoretical footing ’s possible make accurate predictions (e.g.) c(x, y) x y different prototypes. vctrs achieves goal two generics: vec_ptype2(x, y) defines possible set coercions. returns prototype x y can safely coerced prototype; otherwise returns error. set automatic coercions usually quite small many tend make code harder reason silently propagate mistakes. vec_cast(x, ) defines possible sets casts. returns x translated prototype , throws error conversion isn’t possible. set possible casts superset possible coercions ’re requested explicitly.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"double-dispatch","dir":"Articles","previous_headings":"Casting and coercion","what":"Double dispatch","title":"S3 vectors","text":"generics use double dispatch means implementation selected based class two arguments, just one. S3 natively support double dispatch, implement dispatch mechanism. practice, means: end method names two classes, like vec_ptype2.foo.bar(). don’t need implement default methods (never called ). can’t call NextMethod().","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"percent","dir":"Articles","previous_headings":"Casting and coercion","what":"Percent class","title":"S3 vectors","text":"’ll make percent class coercible back forth double vectors. vec_ptype2() provides user friendly error message coercion doesn’t exist makes sure NA handled standard way. NA technically logical vector, want stand missing value type. default simple cases, object class compatible : However works attributes objects . Also default methods bit slower. always good idea provide explicit coercion method case identical classes. ’ll start saying vctrs_percent combined vctrs_percent yields vctrs_percent, indicate returning prototype generated constructor. Next define methods say combining percent double yield double. avoid returning percent errors scale (1 vs. 0.01) obvious raw numbers. double dispatch bit hack, need provide two methods. ’s responsibility ensure member pair returns result: don’t get weird unpredictable behaviour. double dispatch mechanism requires us refer underlying type, double, method name. implemented vec_ptype2.vctrs_percent.numeric(), never called. can check ’ve implemented correctly vec_ptype_show(): vec_ptype2() methods define input richer type vctrs coerce . However, don’t perform conversion. job vec_cast(), implement next. ’ll provide method cast percent percent: converting back forth doubles. convert double percent use percent() helper (constructor; unvalidated user input). convert percent double, strip attributes. Note historical reasons order argument signature opposite vec_ptype2(). class comes first, class x comes second. , double dispatch mechanism requires us refer underlying type, double, method name. Implementing vec_cast.vctrs_percent.numeric() effect. can check works vec_cast(): ’ve implemented vec_ptype2() vec_cast(), get vec_c(), [<-, [[<- implementations free. ’ll also get mostly correct behaviour c(). exception use c() base R class: Unfortunately ’s way fix problem current design c(). , convenience, consider providing as_percent() function makes use casts defined vec_cast.vctrs_percent() methods: Occasionally, useful provide conversions go beyond ’s allowed casting. example, offer parsing method character vectors. case, as_percent() generic, default method cast, additional methods implement flexible conversion:","code":"vec_ptype2(\"bogus\", percent()) #> Error: #> ! Can't combine `\"bogus\"` and `percent()` . vec_ptype2(percent(), NA) #> vec_ptype2(NA, percent()) #> vec_ptype2(percent(), percent()) #> vec_ptype2.vctrs_percent.vctrs_percent <- function(x, y, ...) new_percent() vec_ptype2.vctrs_percent.double <- function(x, y, ...) double() vec_ptype2.double.vctrs_percent <- function(x, y, ...) double() vec_ptype_show(percent(), double(), percent()) #> Prototype: #> 0. ( , ) = #> 1. ( , ) = #> 2. ( , ) = vec_cast.vctrs_percent.vctrs_percent <- function(x, to, ...) x vec_cast.vctrs_percent.double <- function(x, to, ...) percent(x) vec_cast.double.vctrs_percent <- function(x, to, ...) vec_data(x) vec_cast(0.5, percent()) #> #> [1] 50% vec_cast(percent(0.5), double()) #> [1] 0.5 vec_c(percent(0.5), 1) #> [1] 0.5 1.0 vec_c(NA, percent(0.5)) #> #> [1] 50% # but vec_c(TRUE, percent(0.5)) #> Error in `vec_c()`: #> ! Can't combine `..1` and `..2` . x <- percent(c(0.5, 1, 2)) x[1:2] <- 2:1 #> Error in `vec_restore_dispatch()`: #> ! Can't convert to . x[[3]] <- 0.5 x #> #> [1] 50% 100% 50% # Correct c(percent(0.5), 1) #> [1] 0.5 1.0 c(percent(0.5), factor(1)) #> Error in `vec_c()`: #> ! Can't combine `..1` and `..2` >. # Incorrect c(factor(1), percent(0.5)) #> [1] 1.0 0.5 as_percent <- function(x) { vec_cast(x, new_percent()) } as_percent <- function(x, ...) { UseMethod(\"as_percent\") } as_percent.default <- function(x, ...) { vec_cast(x, new_percent()) } as_percent.character <- function(x) { value <- as.numeric(gsub(\" *% *$\", \"\", x)) / 100 new_percent(value) }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"decimal-class","dir":"Articles","previous_headings":"Casting and coercion","what":"Decimal class","title":"S3 vectors","text":"Now ’ve seen basics simple S3 class, ’ll gradually explore complicated scenarios. section creates decimal class prints specified number decimal places. similar percent now class needs attribute: number decimal places display (integer vector length 1). start , defining low-level constructor, user-friendly constructor, format() method, vec_ptype_abbr(). Note additional object attributes simply passed along new_vctr(): Note provide little helper extract digits attribute. makes code little easier read exported. default, vctrs assumes attributes independent data automatically preserved. ’ll see attributes data dependent next section. sake exposition, ’ll assume digits important attribute class included full type: Now consider vec_cast() vec_ptype2(). Casting coercing one decimal another requires little thought values digits attribute might different, need way reconcile . ’ve decided chose maximum two; reasonable options take value left-hand side throw error. Finally, can implement coercion types, like doubles. automatically coercing, choose richer type (.e., decimal). type x greater resolution y, inputs lose precision. generate errors using stop_lossy_cast(). can see action casting doubles integers; doubles can become integers without losing resolution.","code":"new_decimal <- function(x = double(), digits = 2L) { if (!is_double(x)) { abort(\"`x` must be a double vector.\") } if (!is_integer(digits)) { abort(\"`digits` must be an integer vector.\") } vec_check_size(digits, size = 1L) new_vctr(x, digits = digits, class = \"vctrs_decimal\") } decimal <- function(x = double(), digits = 2L) { x <- vec_cast(x, double()) digits <- vec_recycle(vec_cast(digits, integer()), 1L) new_decimal(x, digits = digits) } digits <- function(x) attr(x, \"digits\") format.vctrs_decimal <- function(x, ...) { sprintf(paste0(\"%-0.\", digits(x), \"f\"), x) } vec_ptype_abbr.vctrs_decimal <- function(x, ...) { \"dec\" } x <- decimal(runif(10), 1L) x #> #> [1] 0.1 0.8 0.6 0.2 0.0 0.5 0.5 0.3 0.7 0.8 x[1:2] #> #> [1] 0.1 0.8 x[[1]] #> #> [1] 0.1 vec_ptype_full.vctrs_decimal <- function(x, ...) { paste0(\"decimal<\", digits(x), \">\") } x #> [10]> #> [1] 0.1 0.8 0.6 0.2 0.0 0.5 0.5 0.3 0.7 0.8 vec_ptype2.vctrs_decimal.vctrs_decimal <- function(x, y, ...) { new_decimal(digits = max(digits(x), digits(y))) } vec_cast.vctrs_decimal.vctrs_decimal <- function(x, to, ...) { new_decimal(vec_data(x), digits = digits(to)) } vec_c(decimal(1/100, digits = 3), decimal(2/100, digits = 2)) #> [2]> #> [1] 0.010 0.020 vec_ptype2.vctrs_decimal.double <- function(x, y, ...) x vec_ptype2.double.vctrs_decimal <- function(x, y, ...) y vec_cast.vctrs_decimal.double <- function(x, to, ...) new_decimal(x, digits = digits(to)) vec_cast.double.vctrs_decimal <- function(x, to, ...) vec_data(x) vec_c(decimal(1, digits = 1), pi) #> [2]> #> [1] 1.0 3.1 vec_c(pi, decimal(1, digits = 1)) #> [2]> #> [1] 3.1 1.0 vec_cast(c(1, 2, 10), to = integer()) #> [1] 1 2 10 vec_cast(c(1.5, 2, 10.5), to = integer()) #> Error: #> ! Can't convert from `c(1.5, 2, 10.5)` to due to loss of precision. #> • Locations: 1, 3"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"cached-sum","dir":"Articles","previous_headings":"Casting and coercion","what":"Cached sum class","title":"S3 vectors","text":"next level complexity object data-dependent attributes. explore idea ’ll create vector caches sum values. usual, start low-level user-friendly constructors: class, can use default format() method, instead, ’ll customise obj_print_footer() method. good place display user facing attributes. ’ll also override sum() mean() use attribute. easiest vec_math(), ’ll learn later. mentioned , vctrs assumes attributes independent data. means take advantage default methods, ’ll work, return incorrect result: fix , need provide vec_restore() method. Note method dispatches argument. works vctrs methods dispatch underlying base function first stripping extra attributes vec_data() reapplying vec_restore(). default vec_restore() method copies attributes, appropriate attributes depend data. Note vec_restore.class subtly different vec_cast.class.class(). vec_restore() used restoring attributes lost; vec_cast() used coercions. easier understand concrete example. Imagine factors implemented new_vctr(). vec_restore.factor() restore attributes back integer vector, want allow manually casting integer factor vec_cast().","code":"new_cached_sum <- function(x = double(), sum = 0L) { if (!is_double(x)) { abort(\"`x` must be a double vector.\") } if (!is_double(sum)) { abort(\"`sum` must be a double vector.\") } vec_check_size(sum, size = 1L) new_vctr(x, sum = sum, class = \"vctrs_cached_sum\") } cached_sum <- function(x) { x <- vec_cast(x, double()) new_cached_sum(x, sum(x)) } obj_print_footer.vctrs_cached_sum <- function(x, ...) { cat(\"# Sum: \", format(attr(x, \"sum\"), digits = 3), \"\\n\", sep = \"\") } x <- cached_sum(runif(10)) x #> #> [1] 0.87460066 0.17494063 0.03424133 0.32038573 0.40232824 0.19566983 #> [7] 0.40353812 0.06366146 0.38870131 0.97554784 #> # Sum: 3.83 vec_math.vctrs_cached_sum <- function(.fn, .x, ...) { cat(\"Using cache\\n\") switch(.fn, sum = attr(.x, \"sum\"), mean = attr(.x, \"sum\") / length(.x), vec_math_base(.fn, .x, ...) ) } sum(x) #> Using cache #> [1] 3.833615 x[1:2] #> #> [1] 0.8746007 0.1749406 #> # Sum: 3.83 vec_restore.vctrs_cached_sum <- function(x, to, ..., i = NULL) { new_cached_sum(x, sum(x)) } x[1] #> #> [1] 0.8746007 #> # Sum: 0.875"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"record-style-objects","dir":"Articles","previous_headings":"","what":"Record-style objects","title":"S3 vectors","text":"Record-style objects use list equal-length vectors represent individual components object. best example POSIXlt, underneath hood list 11 fields like year, month, day. Record-style classes override length() subsetting methods conceal implementation detail. vctrs makes easy create new record-style classes using new_rcrd(), wide selection default methods.","code":"x <- as.POSIXlt(ISOdatetime(2020, 1, 1, 0, 0, 1:3)) x #> [1] \"2020-01-01 00:00:01 UTC\" \"2020-01-01 00:00:02 UTC\" #> [3] \"2020-01-01 00:00:03 UTC\" length(x) #> [1] 3 length(unclass(x)) #> [1] 11 x[[1]] # the first date time #> [1] \"2020-01-01 00:00:01 UTC\" unclass(x)[[1]] # the first component, the number of seconds #> [1] 1 2 3"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"rational-class","dir":"Articles","previous_headings":"Record-style objects","what":"Rational class","title":"S3 vectors","text":"fraction, rational number, can represented pair integer vectors representing numerator (number top) denominator (number bottom), length vector must . represent data structure turn new base data type: record (rcrd short). usual start low-level user-friendly constructors. low-level constructor calls new_rcrd(), needs named list equal-length vectors. user friendly constructor casts n d integers recycles length. Behind scenes, x named list two elements. details hidden behaves like vector: access underlying fields need use field() fields(): Notice can’t print() str() new rational vector x yet. Printing causes error: haven’t defined class can printed underlying data. Note want look hood development, can always call vec_data(x). generally best define formatting method early development class. format method defines display class can printed normal way: vctrs uses format() method str(), hiding underlying implementation details user: rational, vec_ptype2() vec_cast() follow pattern percent(). allow coercion integer doubles.","code":"new_rational <- function(n = integer(), d = integer()) { if (!is_integer(n)) { abort(\"`n` must be an integer vector.\") } if (!is_integer(d)) { abort(\"`d` must be an integer vector.\") } new_rcrd(list(n = n, d = d), class = \"vctrs_rational\") } rational <- function(n = integer(), d = integer()) { c(n, d) %<-% vec_cast_common(n, d, .to = integer()) c(n, d) %<-% vec_recycle_common(n, d) new_rational(n, d) } x <- rational(1, 1:10) names(x) #> NULL length(x) #> [1] 10 fields(x) #> [1] \"n\" \"d\" field(x, \"n\") #> [1] 1 1 1 1 1 1 1 1 1 1 x #> #> Error in `format()`: #> ! `format.vctrs_rational()` not implemented. str(x) #> Error in `format()`: #> ! `format.vctrs_rational()` not implemented. vec_data(x) #> n d #> 1 1 1 #> 2 1 2 #> 3 1 3 #> 4 1 4 #> 5 1 5 #> 6 1 6 #> 7 1 7 #> 8 1 8 #> 9 1 9 #> 10 1 10 str(vec_data(x)) #> 'data.frame': 10 obs. of 2 variables: #> $ n: int 1 1 1 1 1 1 1 1 1 1 #> $ d: int 1 2 3 4 5 6 7 8 9 10 format.vctrs_rational <- function(x, ...) { n <- field(x, \"n\") d <- field(x, \"d\") out <- paste0(n, \"/\", d) out[is.na(n) | is.na(d)] <- NA out } vec_ptype_abbr.vctrs_rational <- function(x, ...) \"rtnl\" vec_ptype_full.vctrs_rational <- function(x, ...) \"rational\" x #> #> [1] 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10 str(x) #> rtnl [1:10] 1/1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10 vec_ptype2.vctrs_rational.vctrs_rational <- function(x, y, ...) new_rational() vec_ptype2.vctrs_rational.integer <- function(x, y, ...) new_rational() vec_ptype2.integer.vctrs_rational <- function(x, y, ...) new_rational() vec_cast.vctrs_rational.vctrs_rational <- function(x, to, ...) x vec_cast.double.vctrs_rational <- function(x, to, ...) field(x, \"n\") / field(x, \"d\") vec_cast.vctrs_rational.integer <- function(x, to, ...) rational(x, 1) vec_c(rational(1, 2), 1L, NA) #> #> [1] 1/2 1/1 "},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"decimal2-class","dir":"Articles","previous_headings":"Record-style objects","what":"Decimal2 class","title":"S3 vectors","text":"previous implementation decimal built top doubles. bad idea decimal vectors typically used care precise values (.e., dollars cents bank account), double values suffer floating point problems. better implementation decimal class use pair integers, one value left decimal point, value right (divided scale). following code quick sketch might start creating class:","code":"new_decimal2 <- function(l, r, scale = 2L) { if (!is_integer(l)) { abort(\"`l` must be an integer vector.\") } if (!is_integer(r)) { abort(\"`r` must be an integer vector.\") } if (!is_integer(scale)) { abort(\"`scale` must be an integer vector.\") } vec_check_size(scale, size = 1L) new_rcrd(list(l = l, r = r), scale = scale, class = \"vctrs_decimal2\") } decimal2 <- function(l, r, scale = 2L) { l <- vec_cast(l, integer()) r <- vec_cast(r, integer()) c(l, r) %<-% vec_recycle_common(l, r) scale <- vec_cast(scale, integer()) # should check that r < 10^scale new_decimal2(l = l, r = r, scale = scale) } format.vctrs_decimal2 <- function(x, ...) { val <- field(x, \"l\") + field(x, \"r\") / 10^attr(x, \"scale\") sprintf(paste0(\"%.0\", attr(x, \"scale\"), \"f\"), val) } decimal2(10, c(0, 5, 99)) #> #> [1] 10.00 10.05 10.99"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"equality-and-comparison","dir":"Articles","previous_headings":"","what":"Equality and comparison","title":"S3 vectors","text":"vctrs provides four “proxy” generics. Two let control class determines equality comparison: vec_proxy_equal() returns data vector suitable comparison. underpins ==, !=, unique(), anyDuplicated(), .na(). vec_proxy_compare() specifies compare elements vector. proxy used <, <=, >=, >, min(), max(), median(), quantile(). Two proxy generic used sorting unordered data types accessing raw data exotic storage formats: vec_proxy_order() specifies sort elements vector. used xtfrm(), turn called order() sort() functions. proxy added implement behaviour lists, sortable (order proxy sorts first occurrence) comparable (comparison operators cause error). default implementation classes calls vec_proxy_compare() normally don’t need implement proxy. vec_proxy() returns actual data vector. useful store data field class. time, shouldn’t need implement vec_proxy(). default behavior follows: vec_proxy_equal() calls vec_proxy() vec_proxy_compare() calls vec_proxy_equal() vec_proxy_order() calls vec_proxy_compare() implement proxies preprocessing data needed make elements comparable. case, defining methods get lot behaviour relatively little work. proxy functions always return simple object (either bare vector data frame) possesses properties class. permits efficient implementation vctrs internals allows dispatch happen R, efficient computations can written C.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"rational-class-1","dir":"Articles","previous_headings":"Equality and comparison","what":"Rational class","title":"S3 vectors","text":"Let’s explore ideas rational class started . default, vec_proxy() converts record data frame, default comparison works column column: makes sense default isn’t correct rational(1, 1) represents number rational(2, 2), equal. can fix implementing vec_proxy_equal() method divides n d greatest common divisor: vec_proxy_equal() also used unique(): now need fix comparison operations similarly, since comparison currently happens lexicographically n, d: easiest fix convert fraction floating point number use proxy: also fixes sort(), default implementation vec_proxy_order() calls vec_proxy_compare(). (used approach vec_proxy_equal(), working floating point numbers ’s necessarily true x == y implies d * x == d * y.)","code":"x <- rational(c(1, 2, 1, 2), c(1, 1, 2, 2)) x #> #> [1] 1/1 2/1 1/2 2/2 vec_proxy(x) #> n d #> 1 1 1 #> 2 2 1 #> 3 1 2 #> 4 2 2 x == rational(1, 1) #> [1] TRUE FALSE FALSE FALSE # Thanks to Matthew Lundberg: https://stackoverflow.com/a/21504113/16632 gcd <- function(x, y) { r <- x %% y ifelse(r, gcd(y, r), y) } vec_proxy_equal.vctrs_rational <- function(x, ...) { n <- field(x, \"n\") d <- field(x, \"d\") gcd <- gcd(n, d) data.frame(n = n / gcd, d = d / gcd) } vec_proxy_equal(x) #> n d #> 1 1 1 #> 2 2 1 #> 3 1 2 #> 4 1 1 x == rational(1, 1) #> [1] TRUE FALSE FALSE TRUE unique(x) #> #> [1] 1/1 2/1 1/2 rational(1, 2) < rational(2, 3) #> [1] TRUE rational(2, 4) < rational(2, 3) #> [1] TRUE vec_proxy_compare.vctrs_rational <- function(x, ...) { field(x, \"n\") / field(x, \"d\") } rational(2, 4) < rational(2, 3) #> [1] TRUE sort(x) #> #> [1] 1/2 1/1 2/2 2/1"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"polynomial-class","dir":"Articles","previous_headings":"Equality and comparison","what":"Polynomial class","title":"S3 vectors","text":"related problem occurs build vector top list. following code defines polynomial class represents polynomials (like 1 + 3x - 2x^2) using list integer vectors (like c(1, 3, -2)). Note use new_list_of() constructor. resulting objects inherit vctrs_list_of class, provides tailored methods $, [[, corresponding assignment operators, methods. class implements list interface: fine internal implementation class appropriate behaved like atomic vector rather list.","code":"poly <- function(...) { x <- vec_cast_common(..., .to = integer()) new_poly(x) } new_poly <- function(x) { new_list_of(x, ptype = integer(), class = \"vctrs_poly_list\") } vec_ptype_full.vctrs_poly_list <- function(x, ...) \"polynomial\" vec_ptype_abbr.vctrs_poly_list <- function(x, ...) \"poly\" format.vctrs_poly_list <- function(x, ...) { format_one <- function(x) { if (length(x) == 0) { return(\"\") } if (length(x) == 1) { format(x) } else { suffix <- c(paste0(\"\\u22C5x^\", seq(length(x) - 1, 1)), \"\") out <- paste0(x, suffix) out <- out[x != 0L] paste0(out, collapse = \" + \") } } vapply(x, format_one, character(1)) } obj_print_data.vctrs_poly_list <- function(x, ...) { if (length(x) != 0) { print(format(x), quote = FALSE) } } p <- poly(1, c(1, 0, 0, 0, 2), c(1, 0, 1)) p #> #> [1] 1 1⋅x^4 + 2 1⋅x^2 + 1 class(p) #> [1] \"vctrs_poly_list\" \"vctrs_list_of\" \"vctrs_vctr\" #> [4] \"list\" p[2] #> #> [1] 1⋅x^4 + 2 p[[2]] #> [1] 1 0 0 0 2 obj_is_list(p) #> [1] TRUE"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"make-an-atomic-polynomial-vector","dir":"Articles","previous_headings":"Equality and comparison > Polynomial class","what":"Make an atomic polynomial vector","title":"S3 vectors","text":"atomic vector vector like integer character [[ returns type. Unlike lists, can’t reach inside atomic vector. make polynomial class atomic vector, ’ll wrap internal list_of() class within record vector. Usually records used can store several fields data observation. one, use class anyway inherit atomicity. new format() method delegates one wrote internal list. vector looks just like : Making class atomic means obj_is_list() now returns FALSE. prevents recursive algorithms traverse lists reaching far inside polynomial internals. importantly, prevents users reaching internals [[:","code":"poly <- function(...) { x <- vec_cast_common(..., .to = integer()) x <- new_poly(x) new_rcrd(list(data = x), class = \"vctrs_poly\") } format.vctrs_poly <- function(x, ...) { format(field(x, \"data\")) } p <- poly(1, c(1, 0, 0, 0, 2), c(1, 0, 1)) p #> #> [1] 1 1⋅x^4 + 2 1⋅x^2 + 1 obj_is_list(p) #> [1] FALSE p[[2]] #> #> [1] 1⋅x^4 + 2"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"implementing-equality-and-comparison","dir":"Articles","previous_headings":"Equality and comparison > Polynomial class","what":"Implementing equality and comparison","title":"S3 vectors","text":"Equality works box can tell two integer vectors equal: can’t compare individual elements, data stored list default lists comparable: enable comparison, implement vec_proxy_compare() method: Often, sufficient also implement sort(). However, lists, already default vec_proxy_order() method sorts first occurrence: ensure consistency ordering comparison, forward vec_proxy_order() vec_proxy_compare():","code":"p == poly(c(1, 0, 1)) #> [1] FALSE FALSE TRUE p < p[2] #> Error in `vec_proxy_compare()`: #> ! `vec_proxy_compare.vctrs_poly_list()` not supported. vec_proxy_compare.vctrs_poly <- function(x, ...) { # Get the list inside the record vector x_raw <- vec_data(field(x, \"data\")) # First figure out the maximum length n <- max(vapply(x_raw, length, integer(1))) # Then expand all vectors to this length by filling in with zeros full <- lapply(x_raw, function(x) c(rep(0L, n - length(x)), x)) # Then turn into a data frame as.data.frame(do.call(rbind, full)) } p < p[2] #> [1] TRUE FALSE TRUE sort(p) #> #> [1] 1 1⋅x^2 + 1 1⋅x^4 + 2 sort(p[c(1:3, 1:2)]) #> #> [1] 1 1 1⋅x^2 + 1 1⋅x^4 + 2 1⋅x^4 + 2 vec_proxy_order.vctrs_poly <- function(x, ...) { vec_proxy_compare(x, ...) } sort(p) #> #> [1] 1 1⋅x^2 + 1 1⋅x^4 + 2"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"arithmetic","dir":"Articles","previous_headings":"","what":"Arithmetic","title":"S3 vectors","text":"vctrs also provides two mathematical generics allow define broad swath mathematical behaviour : vec_math(fn, x, ...) specifies behaviour mathematical functions like abs(), sum(), mean(). (Note var() sd() can’t overridden, see ?vec_math() complete list supported vec_math().) vec_arith(op, x, y) specifies behaviour arithmetic operations like +, -, %%. (See ?vec_arith() complete list.) generics define behaviour multiple functions sum.vctrs_vctr(x) calls vec_math.vctrs_vctr(\"sum\", x), x + y calls vec_math.x_class.y_class(\"+\", x, y). ’re accompanied vec_math_base() vec_arith_base() make easy call underlying base R functions. vec_arith() uses double dispatch needs following standard boilerplate: Correctly exporting vec_arith() methods package currently little awkward. See instructions Arithmetic section “Implementing vctrs S3 class package” section .","code":"vec_arith.MYCLASS <- function(op, x, y, ...) { UseMethod(\"vec_arith.MYCLASS\", y) } vec_arith.MYCLASS.default <- function(op, x, y, ...) { stop_incompatible_op(op, x, y) }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"cached-sum-class","dir":"Articles","previous_headings":"Arithmetic","what":"Cached sum class","title":"S3 vectors","text":"showed example vec_math() define sum() mean() methods cached_sum. Now let’s talk exactly works. vec_math() functions similar form. use switch statement handle methods care fall back vec_math_base() don’t care .","code":"vec_math.vctrs_cached_sum <- function(.fn, .x, ...) { switch(.fn, sum = attr(.x, \"sum\"), mean = attr(.x, \"sum\") / length(.x), vec_math_base(.fn, .x, ...) ) }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"meter-class","dir":"Articles","previous_headings":"Arithmetic","what":"Meter class","title":"S3 vectors","text":"explore infix arithmetic operators exposed vec_arith() ’ll create new class represents measurement meters: meter built top double vector, basic mathematic operations work: can’t arithmetic: allow infix functions work, ’ll need provide vec_arith() generic. , let’s think combinations inputs support: makes sense add subtract meters: yields another meter. can divide meter another meter (yielding unitless number), can’t multiply meters (yield area). combination meter number multiplication division number acceptable. Addition subtraction don’t make much sense , strictly speaking, dealing objects different nature. vec_arith() another function uses double dispatch, usual start template. write method two meter objects. use switch statement cover cases care stop_incompatible_op() throw informative error message everything else. Next write pair methods arithmetic meter number. almost identical, meter(10) / 2 makes sense, 2 / meter(10) (neither addition subtraction). support doubles integers operands, dispatch numeric instead double. completeness, also need vec_arith.vctrs_meter.MISSING unary + - operators:","code":"new_meter <- function(x) { stopifnot(is.double(x)) new_vctr(x, class = \"vctrs_meter\") } format.vctrs_meter <- function(x, ...) { paste0(format(vec_data(x)), \" m\") } meter <- function(x) { x <- vec_cast(x, double()) new_meter(x) } x <- meter(1:10) x #> #> [1] 1 m 2 m 3 m 4 m 5 m 6 m 7 m 8 m 9 m 10 m sum(x) #> #> [1] 55 m mean(x) #> #> [1] 5.5 m x + 1 #> Error in `vec_arith()`: #> ! + is not permitted meter(10) + meter(1) #> Error in `vec_arith()`: #> ! + is not permitted meter(10) * 3 #> Error in `vec_arith()`: #> ! * is not permitted vec_arith.vctrs_meter <- function(op, x, y, ...) { UseMethod(\"vec_arith.vctrs_meter\", y) } vec_arith.vctrs_meter.default <- function(op, x, y, ...) { stop_incompatible_op(op, x, y) } vec_arith.vctrs_meter.vctrs_meter <- function(op, x, y, ...) { switch( op, \"+\" = , \"-\" = new_meter(vec_arith_base(op, x, y)), \"/\" = vec_arith_base(op, x, y), stop_incompatible_op(op, x, y) ) } meter(10) + meter(1) #> #> [1] 11 m meter(10) - meter(1) #> #> [1] 9 m meter(10) / meter(1) #> [1] 10 meter(10) * meter(1) #> Error in `vec_arith()`: #> ! * is not permitted vec_arith.vctrs_meter.numeric <- function(op, x, y, ...) { switch( op, \"/\" = , \"*\" = new_meter(vec_arith_base(op, x, y)), stop_incompatible_op(op, x, y) ) } vec_arith.numeric.vctrs_meter <- function(op, x, y, ...) { switch( op, \"*\" = new_meter(vec_arith_base(op, x, y)), stop_incompatible_op(op, x, y) ) } meter(2) * 10 #> #> [1] 20 m meter(2) * as.integer(10) #> #> [1] 20 m 10 * meter(2) #> #> [1] 20 m meter(20) / 10 #> #> [1] 2 m 10 / meter(20) #> Error in `vec_arith()`: #> ! / is not permitted meter(20) + 10 #> Error in `vec_arith()`: #> ! + is not permitted vec_arith.vctrs_meter.MISSING <- function(op, x, y, ...) { switch(op, `-` = x * -1, `+` = x, stop_incompatible_op(op, x, y) ) } -meter(1) #> #> [1] -1 m +meter(1) #> #> [1] 1 m"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"implementing-a-vctrs-s3-class-in-a-package","dir":"Articles","previous_headings":"","what":"Implementing a vctrs S3 class in a package","title":"S3 vectors","text":"Defining S3 methods interactively fine iteration exploration, class lives package, need things: Register S3 methods listing NAMESPACE file. Create documentation around methods, sake user satisfy R CMD check. Let’s assume percent class implemented pizza package file R/percent.R. walk major sections hypothetical file. ’ve seen code , now ’s augmented roxygen2 directives produce correct NAMESPACE entries help topics.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"getting-started","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Getting started","title":"S3 vectors","text":"First, pizza package needs include vctrs Imports section DESCRIPTION (perhaps calling usethis::use_package(\"vctrs\"). vctrs active development, probably makes sense state minimum version. make vctrs functions available within pizza package including directive #' @import vctrs somewhere. Usually, ’s good practice @import entire namespace package, vctrs deliberately designed use case mind. put #' @import vctrs? two natural locations: package-level docs R/pizza-doc.R. can use usethis::use_package_doc() initiate package-level documentation. R/percent.R. makes sense vctrs S3 class modest self-contained part overall package. also must use one locations dump internal documentation ’s needed avoid R CMD check complaints. don’t expect human ever read documentation. ’s dummy documentation look, combined #' @import vctrs directive described . appear R/pizza-doc.R (package-level docs) R/percent.R (class-focused file). Remember call devtools::document() regularly, develop, regenerate NAMESPACE .Rd files. point , code shown expected appear R/percent.R.","code":"Imports: a_package, another_package, ... vctrs (>= x.y.z), ... #' Internal vctrs methods #' #' @import vctrs #' @keywords internal #' @name pizza-vctrs NULL"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"low-level-and-user-friendly-constructors","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Low-level and user-friendly constructors","title":"S3 vectors","text":"Next add constructor: Note name package must included class name (pizza_percent), need included constructor name. need export constructor, unless want people extend class. can also add call setOldClass() compatibility S4: ’ve used function methods package, ’ll also need add methods Imports, (e.g.) usethis::use_package(\"methods\"). “free” dependency methods bundled every R install. Next implement, export, document user-friendly helper: percent(). (note package name appear class, need occur function, can already pizza::percent(); redundant pizza::pizza_percent().)","code":"new_percent <- function(x = double()) { if (!is_double(x)) { abort(\"`x` must be a double vector.\") } new_vctr(x, class = \"pizza_percent\") } # for compatibility with the S4 system methods::setOldClass(c(\"pizza_percent\", \"vctrs_vctr\")) #' `percent` vector #' #' This creates a double vector that represents percentages so when it is #' printed, it is multiplied by 100 and suffixed with `%`. #' #' @param x A numeric vector #' @return An S3 vector of class `pizza_percent`. #' @export #' @examples #' percent(c(0.25, 0.5, 0.75)) percent <- function(x = double()) { x <- vec_cast(x, double()) new_percent(x) }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"other-helpers","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Other helpers","title":"S3 vectors","text":"’s good idea provide function tests object class. , makes sense document user-friendly constructor percent(): ’ll also need update percent() documentation reflect x now means two different things: Next provide key methods make printing work. S3 methods, don’t need documented, need exported. Finally, implement methods vec_ptype2() vec_cast().","code":"#' @export #' @rdname percent is_percent <- function(x) { inherits(x, \"pizza_percent\") } #' @param x #' * For `percent()`: A numeric vector #' * For `is_percent()`: An object to test. #' @export format.pizza_percent <- function(x, ...) { out <- formatC(signif(vec_data(x) * 100, 3)) out[is.na(x)] <- NA out[!is.na(x)] <- paste0(out[!is.na(x)], \"%\") out } #' @export vec_ptype_abbr.pizza_percent <- function(x, ...) { \"prcnt\" } #' @export vec_ptype2.vctrs_percent.vctrs_percent <- function(x, y, ...) new_percent() #' @export vec_ptype2.double.vctrs_percent <- function(x, y, ...) double() #' @export vec_cast.pizza_percent.pizza_percent <- function(x, to, ...) x #' @export vec_cast.pizza_percent.double <- function(x, to, ...) percent(x) #' @export vec_cast.double.pizza_percent <- function(x, to, ...) vec_data(x)"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"arithmetic-1","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Arithmetic","title":"S3 vectors","text":"Writing double dispatch methods vec_arith() currently awkward writing vec_ptype2() vec_cast(). plan improve future. now, can use following instructions. define new type want write vec_arith() methods , ’ll need provide new single dispatch S3 generic following form: Note actually functions S3 method vec_arith() S3 generic called vec_arith.my_type() dispatches y. roxygen2 recognizes S3 generic, register S3 method part explicit @method call. , can define double dispatch methods, still need explicit @method tag ensure registered correct generic: vctrs provides hybrid S3 generics/methods base R types, like vec_arith.integer(). don’t fully import vctrs @import vctrs, need explicitly import generic registering double dispatch methods @importFrom vctrs vec_arith.integer.","code":"#' @export #' @method vec_arith my_type vec_arith.my_type <- function(op, x, y, ...) { UseMethod(\"vec_arith.my_type\", y) } #' @export #' @method vec_arith.my_type my_type vec_arith.my_type.my_type <- function(op, x, y, ...) { # implementation here } #' @export #' @method vec_arith.my_type integer vec_arith.my_type.integer <- function(op, x, y, ...) { # implementation here } #' @export #' @method vec_arith.integer my_type vec_arith.integer.my_type <- function(op, x, y, ...) { # implementation here }"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"testing","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Testing","title":"S3 vectors","text":"’s good practice test new class. Specific recommendations: R/percent.R type file really want 100% test coverage. can use devtools::test_coverage_file() check . Make sure test behaviour zero-length inputs missing values. Use testthat::verify_output() test format method. Customised printing often primary motivation creating S3 class first place, alert unexpected changes printed output. Read verify_output() testthat v2.3.0 blog post; ’s example -called golden test. Check method symmetry; use expect_s3_class(), probably exact = TRUE, ensure vec_c(x, y) vec_c(y, x) return type output important xs ys domain. Use testthat::expect_error() check inputs can’t combined fail error. , generally checking class error, message. Relevant classes include vctrs_error_assert_ptype, vctrs_error_assert_size, vctrs_error_incompatible_type. tests pass run devtools::test(), fail run R CMD check, likely reflect problem S3 method registration. Carefully check roxygen2 comments generated NAMESPACE.","code":"expect_error(vec_c(1, \"a\"), class = \"vctrs_error_incompatible_type\")"},{"path":"https://vctrs.r-lib.org/dev/articles/s3-vector.html","id":"existing-classes","dir":"Articles","previous_headings":"Implementing a vctrs S3 class in a package","what":"Existing classes","title":"S3 vectors","text":"build class, might want consider using, subclassing existing classes. can check awesome-vctrs curated list R vector classes, built vctrs. ’ve built extended class, consider adding list people can use .","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"definitions","dir":"Articles","previous_headings":"","what":"Definitions","title":"Type and size stability","text":"say function type-stable iff: can predict output type knowing input types. order arguments … affect output type. Similarly, function size-stable iff: can predict output size knowing input sizes, single numeric input specifies output size. base R functions size-stable, ’ll also define slightly weaker condition. ’ll call function length-stable iff: can predict output length knowing input lengths, single numeric input specifies output length. (note length-stable particularly robust definition length() returns value things vectors.) ’ll call functions don’t obey principles type-unstable size-unstable respectively. top type- size-stability ’s also desirable single set rules applied consistently. want one set type-coercion size-recycling rules apply everywhere, many sets rules apply different functions. goal principles minimise cognitive overhead. Rather memorise many special cases, able learn one set principles apply .","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"examples","dir":"Articles","previous_headings":"Definitions","what":"Examples","title":"Type and size stability","text":"make ideas concrete, let’s apply base functions: mean() trivially type-stable size-stable always returns double vector length 1 (throws error). Surprisingly, median() type-unstable: , however, size-stable, since always returns vector length 1. sapply() type-unstable can’t predict output type knowing input types: ’s quite size-stable; vec_size(sapply(x, f)) vec_size(x) vectors matrices (output transposed) data frames (iterates columns). vapply() type-stable version sapply() vec_ptype_show(vapply(x, fn, template)) always vec_ptype_show(template). size-unstable reasons sapply(). c() type-unstable c(x, y) doesn’t always output type c(y, x). c() almost always length-stable length(c(x, y)) almost always equals length(x) + length(y). One common source instability dealing non-vectors (see later section “Non-vectors”): paste(x1, x2) length-stable length(paste(x1, x2)) equals max(length(x1), length(x2)). However, doesn’t follow usual arithmetic recycling rules paste(1:2, 1:3) doesn’t generate warning. ifelse() length-stable length(ifelse(cond, true, false)) always length(cond). ifelse() type-unstable output type depends value cond: read.csv(file) type-unstable size-unstable , know return data frame, don’t know columns return many rows . Similarly, df[[]] type-stable result depends value . many important functions can made type-stable size-stable! understanding type- size-stability hand, ’ll use analyse base R functions greater depth propose alternatives better properties.","code":"vec_ptype_show(median(c(1L, 1L))) #> Prototype: double vec_ptype_show(median(c(1L, 1L, 1L))) #> Prototype: integer vec_ptype_show(sapply(1L, function(x) c(x, x))) #> Prototype: integer[,1] vec_ptype_show(sapply(integer(), function(x) c(x, x))) #> Prototype: list vec_ptype_show(c(NA, Sys.Date())) #> Prototype: double vec_ptype_show(c(Sys.Date(), NA)) #> Prototype: date env <- new.env(parent = emptyenv()) length(env) #> [1] 0 length(mean) #> [1] 1 length(c(env, mean)) #> [1] 2 vec_ptype_show(ifelse(NA, 1L, 1L)) #> Prototype: logical vec_ptype_show(ifelse(FALSE, 1L, 1L)) #> Prototype: integer"},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"c-and-vctrsvec_c","dir":"Articles","previous_headings":"","what":"c() and vctrs::vec_c()","title":"Type and size stability","text":"section ’ll compare contrast c() vec_c(). vec_c() type- size-stable possesses following invariants: vec_ptype(vec_c(x, y)) equals vec_ptype_common(x, y). vec_size(vec_c(x, y)) equals vec_size(x) + vec_size(y). c() another undesirable property ’s consistent unlist(); .e., unlist(list(x, y)) always equal c(x, y); .e., base R multiple sets type-coercion rules. won’t consider problem . two goals : fully document quirks c(), hence motivating development alternative. discuss non-obvious consequences type- size-stability .","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"atomic-vectors","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Atomic vectors","title":"Type and size stability","text":"consider atomic vectors, c() type-stable uses hierarchy types: character > complex > double > integer > logical. vec_c() obeys similar rules: automatically coerce character vectors lists:","code":"c(FALSE, 1L, 2.5) #> [1] 0.0 1.0 2.5 vec_c(FALSE, 1L, 2.5) #> [1] 0.0 1.0 2.5 c(FALSE, \"x\") #> [1] \"FALSE\" \"x\" vec_c(FALSE, \"x\") #> Error in `vec_c()`: #> ! Can't combine `..1` and `..2` . c(FALSE, list(1)) #> [[1]] #> [1] FALSE #> #> [[2]] #> [1] 1 vec_c(FALSE, list(1)) #> Error in `vec_c()`: #> ! Can't combine `..1` and `..2` ."},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"incompatible-vectors-and-non-vectors","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Incompatible vectors and non-vectors","title":"Type and size stability","text":"general, base methods throw error: inputs aren’t vectors, c() automatically puts list: numeric versions, depends order inputs. Version first error, otherwise input wrapped list: vec_c() throws error inputs vectors automatically coercible:","code":"c(10.5, factor(\"x\")) #> [1] 10.5 1.0 c(mean, globalenv()) #> [[1]] #> function (x, ...) #> UseMethod(\"mean\") #> #> #> #> [[2]] #> c(getRversion(), \"x\") #> Error: invalid version specification 'x' c(\"x\", getRversion()) #> [[1]] #> [1] \"x\" #> #> [[2]] #> [1] 4 4 0 vec_c(mean, globalenv()) #> Error in `vec_c()`: #> ! `..1` must be a vector, not a function. vec_c(Sys.Date(), factor(\"x\"), \"x\") #> Error in `vec_c()`: #> ! Can't combine `..1` and `..2` >."},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"factors","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Factors","title":"Type and size stability","text":"Combining two factors returns integer vector: (documented c() still undesirable.) vec_c() returns factor taking union levels. behaviour motivated pragmatics: many places base R automatically convert character vectors factors, enforcing stricter behaviour unnecessarily onerous. (backed experience dplyr::bind_rows(), stricter common source user difficulty.)","code":"fa <- factor(\"a\") fb <- factor(\"b\") c(fa, fb) #> [1] a b #> Levels: a b vec_c(fa, fb) #> [1] a b #> Levels: a b vec_c(fb, fa) #> [1] b a #> Levels: b a"},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"date-times","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Date-times","title":"Type and size stability","text":"c() strips time zone associated date-times: behaviour documented ?DateTimeClasses source considerable user pain. vec_c() preserves time zones: time zone output inputs different time zones? One option strict force user manually align time zones. However, onerous (particularly ’s easy way change time zone base R), vctrs chooses use first non-local time zone:","code":"datetime_nz <- as.POSIXct(\"2020-01-01 09:00\", tz = \"Pacific/Auckland\") c(datetime_nz) #> [1] \"2020-01-01 09:00:00 NZDT\" vec_c(datetime_nz) #> [1] \"2020-01-01 09:00:00 NZDT\" datetime_local <- as.POSIXct(\"2020-01-01 09:00\") datetime_houston <- as.POSIXct(\"2020-01-01 09:00\", tz = \"US/Central\") vec_c(datetime_local, datetime_houston, datetime_nz) #> [1] \"2020-01-01 03:00:00 CST\" \"2020-01-01 09:00:00 CST\" #> [3] \"2019-12-31 14:00:00 CST\" vec_c(datetime_houston, datetime_nz) #> [1] \"2020-01-01 09:00:00 CST\" \"2019-12-31 14:00:00 CST\" vec_c(datetime_nz, datetime_houston) #> [1] \"2020-01-01 09:00:00 NZDT\" \"2020-01-02 04:00:00 NZDT\""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"dates-and-date-times","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Dates and date-times","title":"Type and size stability","text":"Combining dates date-times c() gives silently incorrect results: behaviour arises neither c.Date() c.POSIXct() check inputs type. vec_c() uses standard set rules avoid problem. mix dates date-times, vctrs returns date-time converts dates date-times midnight (timezone date-time).","code":"date <- as.Date(\"2020-01-01\") datetime <- as.POSIXct(\"2020-01-01 09:00\") c(date, datetime) #> [1] \"2020-01-01\" \"2020-01-01\" c(datetime, date) #> [1] \"2020-01-01 09:00:00 UTC\" \"2020-01-01 00:00:00 UTC\" vec_c(date, datetime) #> [1] \"2020-01-01 00:00:00 UTC\" \"2020-01-01 09:00:00 UTC\" vec_c(date, datetime_nz) #> [1] \"2020-01-01 00:00:00 NZDT\" \"2020-01-01 09:00:00 NZDT\""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"missing-values","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Missing values","title":"Type and size stability","text":"missing value comes beginning inputs, c() falls back internal behaviour, strips attributes: vec_c() takes different approach treating logical vector consisting NA unspecified() class can converted 1d type:","code":"c(NA, fa) #> [1] NA 1 c(NA, date) #> [1] NA 18262 c(NA, datetime) #> [1] NA 1577869200 vec_c(NA, fa) #> [1] a #> Levels: a vec_c(NA, date) #> [1] NA \"2020-01-01\" vec_c(NA, datetime) #> [1] NA \"2020-01-01 09:00:00 UTC\""},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"data-frames","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Data frames","title":"Type and size stability","text":"almost always length-stable, c() combines data frames column wise (list): vec_c() size-stable, implies row-bind data frames:","code":"df1 <- data.frame(x = 1) df2 <- data.frame(x = 2) str(c(df1, df1)) #> List of 2 #> $ x: num 1 #> $ x: num 1 vec_c(df1, df2) #> x #> 1 1 #> 2 2"},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"matrices-and-arrays","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Matrices and arrays","title":"Type and size stability","text":"reasoning applies matrices: One difference vec_c() “broadcast” vector match dimensions matrix:","code":"m <- matrix(1:4, nrow = 2) c(m, m) #> [1] 1 2 3 4 1 2 3 4 vec_c(m, m) #> [,1] [,2] #> [1,] 1 3 #> [2,] 2 4 #> [3,] 1 3 #> [4,] 2 4 c(m, 1) #> [1] 1 2 3 4 1 vec_c(m, 1) #> [,1] [,2] #> [1,] 1 3 #> [2,] 2 4 #> [3,] 1 1"},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"implementation","dir":"Articles","previous_headings":"c() and vctrs::vec_c()","what":"Implementation","title":"Type and size stability","text":"basic implementation vec_c() reasonably simple. first figure properties output, .e. common type total size, allocate vec_init(), insert input correct place output. (real vec_c() bit complicated order handle inner outer names).","code":"vec_c <- function(...) { args <- compact(list2(...)) ptype <- vec_ptype_common(!!!args) if (is.null(ptype)) return(NULL) ns <- map_int(args, vec_size) out <- vec_init(ptype, sum(ns)) pos <- 1 for (i in seq_along(ns)) { n <- ns[[i]] x <- vec_cast(args[[i]], to = ptype) vec_slice(out, pos:(pos + n - 1)) <- x pos <- pos + n } out }"},{"path":"https://vctrs.r-lib.org/dev/articles/stability.html","id":"ifelse","dir":"Articles","previous_headings":"","what":"ifelse()","title":"Type and size stability","text":"One functions motivate development vctrs ifelse(). surprising property result value “vector length attributes (including dimensions class) test”. , seems reasonable type output controlled type yes arguments. dplyr::if_else() swung far towards strictness: throws error yes type. annoying practice requires typed missing values (NA_character_ etc), checks class (full prototype), ’s easy create invalid output. found much easier understand ifelse() internalised ideas type- size-stability: first argument must logical. vec_ptype(if_else(test, yes, )) equals vec_ptype_common(yes, ). Unlike ifelse() implies if_else() must always evaluate yes order figure correct type. think consistent && (scalar operation, short circuits) & (vectorised, evaluates sides). vec_size(if_else(test, yes, )) equals vec_size_common(test, yes, ). think output size test (.e., behaviour ifelse), think general rule inputs either mutually recycling . leads following implementation: using vec_size() vec_slice(), definition if_else() automatically works data.frames matrices:","code":"if_else <- function(test, yes, no) { if (!is_logical(test)) { abort(\"`test` must be a logical vector.\") } c(yes, no) %<-% vec_cast_common(yes, no) c(test, yes, no) %<-% vec_recycle_common(test, yes, no) out <- vec_init(yes, vec_size(yes)) vec_slice(out, test) <- vec_slice(yes, test) vec_slice(out, !test) <- vec_slice(no, !test) out } x <- c(NA, 1:4) if_else(x > 2, \"small\", \"big\") #> [1] NA \"big\" \"big\" \"small\" \"small\" if_else(x > 2, factor(\"small\"), factor(\"big\")) #> [1] big big small small #> Levels: small big if_else(x > 2, Sys.Date(), Sys.Date() + 7) #> [1] NA \"2024-05-07\" \"2024-05-07\" \"2024-04-30\" \"2024-04-30\" if_else(x > 2, data.frame(x = 1), data.frame(y = 2)) #> x y #> 1 NA NA #> 2 NA 2 #> 3 NA 2 #> 4 1 NA #> 5 1 NA if_else(x > 2, matrix(1:10, ncol = 2), cbind(30, 30)) #> [,1] [,2] #> [1,] NA NA #> [2,] 30 30 #> [3,] 30 30 #> [4,] 4 9 #> [5,] 5 10"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"prototype","dir":"Articles","previous_headings":"","what":"Prototype","title":"Prototypes and sizes","text":"idea prototype capture metadata associated vector without capturing data. Unfortunately, class() object inadequate purpose: class() doesn’t include attributes. Attributes important , example, store levels factor timezone POSIXct. combine two factors two POSIXcts without thinking attributes. class() matrix “matrix” doesn’t include type underlying vector dimensionality. Instead, vctrs takes advantage R’s vectorised nature uses prototype, 0-observation slice vector (basically x[0] subtleties ’ll come back later). miniature version vector contains attributes none data. Conveniently, can create many prototypes using existing base functions (e.g, double() factor(levels = c(\"\", \"b\"))). vctrs provides helpers (e.g. new_date(), new_datetime(), new_duration()) equivalents base R missing.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"base-prototypes","dir":"Articles","previous_headings":"Prototype","what":"Base prototypes","title":"Prototypes and sizes","text":"vec_ptype() creates prototype existing object. However, many base vectors uninformative printing methods 0-length subsets, vctrs also provides vec_ptype_show(), prints prototype friendly way (returns nothing). Using vec_ptype_show() allows us see prototypes base R classes: Atomic vectors attributes just display underlying typeof(): prototype matrices arrays include base type dimensions first: prototype factor includes levels. Levels character vector, can arbitrarily long, prototype just shows hash. hash two factors equal, ’s highly likely levels also equal. vec_ptype_show() prints hash, prototype object contain levels: Base R three key date time classes: dates, date-times (POSIXct), durations (difftime). Date-times timezone, durations unit. Data frames complex prototype: prototype data frame name prototype column: Data frames can columns data frames, making “recursive” type:","code":"vec_ptype_show(FALSE) #> Prototype: logical vec_ptype_show(1L) #> Prototype: integer vec_ptype_show(2.5) #> Prototype: double vec_ptype_show(\"three\") #> Prototype: character vec_ptype_show(list(1, 2, 3)) #> Prototype: list vec_ptype_show(array(logical(), c(2, 3))) #> Prototype: logical[,3] vec_ptype_show(array(integer(), c(2, 3, 4))) #> Prototype: integer[,3,4] vec_ptype_show(array(character(), c(2, 3, 4, 5))) #> Prototype: character[,3,4,5] vec_ptype_show(factor(\"a\")) #> Prototype: factor<4d52a> vec_ptype_show(ordered(\"b\")) #> Prototype: ordered<9b7e3> vec_ptype(factor(\"a\")) #> factor() #> Levels: a vec_ptype_show(Sys.Date()) #> Prototype: date vec_ptype_show(Sys.time()) #> Prototype: datetime vec_ptype_show(as.difftime(10, units = \"mins\")) #> Prototype: duration vec_ptype_show(data.frame(a = FALSE, b = 1L, c = 2.5, d = \"x\")) #> Prototype: data.frame< #> a: logical #> b: integer #> c: double #> d: character #> > df <- data.frame(x = FALSE) df$y <- data.frame(a = 1L, b = 2.5) vec_ptype_show(df) #> Prototype: data.frame< #> x: logical #> y: #> data.frame< #> a: integer #> b: double #> > #> >"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"coercing-to-common-type","dir":"Articles","previous_headings":"Prototype","what":"Coercing to common type","title":"Prototypes and sizes","text":"’s often important combine vectors multiple types. vctrs provides consistent set rules coercion, via vec_ptype_common(). vec_ptype_common() possesses following invariants: class(vec_ptype_common(x, y)) equals class(vec_ptype_common(y, x)). class(vec_ptype_common(x, vec_ptype_common(y, z)) equals class(vec_ptype_common(vec_ptype_common(x, y), z)). vec_ptype_common(x, NULL) == vec_ptype(x). .e., vec_ptype_common() commutative associative (respect class) identity element, NULL; .e., ’s commutative monoid. means underlying implementation quite simple: can find common type number objects progressively finding common type pairs objects. Like vec_ptype(), easiest way explore vec_ptype_common() vec_ptype_show(): given multiple inputs, print common prototype. (words: program vec_ptype_common() play vec_ptype_show().) common type atomic vectors computed similar rules base R, except coerce character automatically: Matrices arrays automatically broadcast higher dimensions: Provided dimensions follow vctrs recycling rules: Factors combine levels order appear. Combining date date-time yields date-time: combining two date times, timezone taken first input: Unless ’s local timezone, case explicit time zone win: common type two data frames common type column occurs data frames: union columns occur one: Note new columns added right-hand side. consistent way factor levels time zones handled.","code":"vec_ptype_show(logical(), integer(), double()) #> Prototype: #> 0. ( , ) = #> 1. ( , ) = #> 2. ( , ) = vec_ptype_show(logical(), character()) #> Error in `vec_ptype_show()`: #> ! Can't combine `out_types[[i - 1]]` and `in_types[[i]]` . vec_ptype_show( array(1, c(0, 1)), array(1, c(0, 2)) ) #> Prototype: #> 0. ( , ) = #> 1. ( , ) = vec_ptype_show( array(1, c(0, 1)), array(1, c(0, 3)), array(1, c(0, 3, 4)), array(1, c(0, 3, 4, 5)) ) #> Prototype: #> 0. ( , ) = #> 1. ( , ) = #> 2. ( , ) = #> 3. ( , ) = vec_ptype_show( array(1, c(0, 2)), array(1, c(0, 3)) ) #> Error: #> ! Can't combine `out_types[[i - 1]]` and `in_types[[i]]` . #> ✖ Incompatible sizes 2 and 3 along axis 2. fa <- factor(\"a\") fb <- factor(\"b\") levels(vec_ptype_common(fa, fb)) #> [1] \"a\" \"b\" levels(vec_ptype_common(fb, fa)) #> [1] \"b\" \"a\" vec_ptype_show(new_date(), new_datetime()) #> Prototype: > #> 0. ( , ) = #> 1. ( , > ) = > vec_ptype_show( new_datetime(tzone = \"US/Central\"), new_datetime(tzone = \"Pacific/Auckland\") ) #> Prototype: > #> 0. ( , > ) = > #> 1. ( > , > ) = > vec_ptype_show( new_datetime(tzone = \"\"), new_datetime(tzone = \"\"), new_datetime(tzone = \"Pacific/Auckland\") ) #> Prototype: > #> 0. ( , > ) = > #> 1. ( > , > ) = > #> 2. ( > , > ) = > vec_ptype_show( data.frame(x = FALSE), data.frame(x = 1L), data.frame(x = 2.5) ) #> Prototype: > #> 0. ( , > ) = > #> 1. ( > , > ) = > #> 2. ( > , > ) = > vec_ptype_show(data.frame(x = 1, y = 1), data.frame(y = 1, z = 1)) #> Prototype: x: double #> y: double #> z: double #> >> #> 0. ┌ , │ x: double │ x: double #> │ y: double │ y: double #> └ >> ┘ >> #> 1. ┌ │ x: double y: double │ x: double #> │ y: double z: double │ y: double #> │ >> >> │ z: double #> └ ┘ >>"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"casting-to-specified-type","dir":"Articles","previous_headings":"Prototype","what":"Casting to specified type","title":"Prototypes and sizes","text":"vec_ptype_common() finds common type set vector. Typically, however, want set vectors coerced common type. ’s job vec_cast_common(): Alternatively, can cast specific prototype using vec_cast(): cast possible general (.e., double -> integer), information lost specific input (e.g. 1.5 -> 1), generate error. can suppress lossy cast errors allow_lossy_cast(): suppress lossy cast errors. Supply prototypes want specific type lossy cast allowed: set casts permissive set coercions. enforced expected classes follow rule keep coercion ecosystem sound.","code":"str(vec_cast_common( FALSE, 1:5, 2.5 )) #> List of 3 #> $ : num 0 #> $ : num [1:5] 1 2 3 4 5 #> $ : num 2.5 str(vec_cast_common( factor(\"x\"), factor(\"y\") )) #> List of 2 #> $ : Factor w/ 2 levels \"x\",\"y\": 1 #> $ : Factor w/ 2 levels \"x\",\"y\": 2 str(vec_cast_common( data.frame(x = 1), data.frame(y = 1:2) )) #> List of 2 #> $ :'data.frame': 1 obs. of 2 variables: #> ..$ x: num 1 #> ..$ y: int NA #> $ :'data.frame': 2 obs. of 2 variables: #> ..$ x: num [1:2] NA NA #> ..$ y: int [1:2] 1 2 # Cast succeeds vec_cast(c(1, 2), integer()) #> [1] 1 2 # Cast fails vec_cast(c(1.5, 2.5), factor(\"a\")) #> Error: #> ! Can't convert `c(1.5, 2.5)` to >. vec_cast(c(1.5, 2), integer()) #> Error: #> ! Can't convert from `c(1.5, 2)` to due to loss of precision. #> • Locations: 1 allow_lossy_cast( vec_cast(c(1.5, 2), integer()) ) #> [1] 1 2 allow_lossy_cast( vec_cast(c(1.5, 2), integer()), x_ptype = double(), to_ptype = integer() ) #> [1] 1 2"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"size","dir":"Articles","previous_headings":"","what":"Size","title":"Prototypes and sizes","text":"vec_size() motivated need invariant describes number “observations” data structure. particularly important data frames, ’s useful function f(data.frame(x)) equals f(x). base function property: length(data.frame(x)) equals 1 length data frame number columns. nrow(data.frame(x)) equal nrow(x) nrow() vector NULL. NROW(data.frame(x)) equals NROW(x) vector x, almost want. NROW() defined terms length(), returns value every object, even types can’t go data frame, e.g. data.frame(mean) errors even though NROW(mean) 1. define vec_size() follows: length 1d vectors. number rows data frames, matrices, arrays. throws error non vectors. Given vec_size(), can give precise definition data frame: data frame list vectors every vector size. desirable property trivially supporting matrix data frame columns.","code":""},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"slicing","dir":"Articles","previous_headings":"Size","what":"Slicing","title":"Prototypes and sizes","text":"vec_slice() vec_size() [ length(); .e., allows select observations regardless dimensionality underlying object. vec_slice(x, ) equivalent : x[] x vector. x[, , drop = FALSE] x data frame. x[, , , drop = FALSE] x 3d array. vec_slice(data.frame(x), ) equals data.frame(vec_slice(x, )) (modulo variable row names). Prototypes generated vec_slice(x, 0L); given prototype, can initialize vector given size (filled NAs) vec_init().","code":"x <- sample(1:10) df <- data.frame(x = x) vec_slice(x, 5:6) #> [1] 4 1 vec_slice(df, 5:6) #> x #> 1 4 #> 2 1"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"common-sizes-recycling-rules","dir":"Articles","previous_headings":"Size","what":"Common sizes: recycling rules","title":"Prototypes and sizes","text":"Closely related definition size recycling rules. recycling rules determine size output two vectors different sizes combined. vctrs, recycling rules encoded vec_size_common(), gives common size set vectors: vctrs obeys stricter set recycling rules base R. Vectors size 1 recycled size. size combinations generate error. strictness prevents common mistakes like dest == c(\"IAH\", \"HOU\")), cost occasionally requiring explicit calls rep(). can apply recycling rules two ways: vector desired size, use vec_recycle(): multiple vectors want recycle size, use vec_recycle_common():","code":"vec_size_common(1:3, 1:3, 1:3) #> [1] 3 vec_size_common(1:10, 1) #> [1] 10 vec_size_common(integer(), 1) #> [1] 0 vec_recycle(1:3, 3) #> [1] 1 2 3 vec_recycle(1, 10) #> [1] 1 1 1 1 1 1 1 1 1 1 vec_recycle_common(1:3, 1:3) #> [[1]] #> [1] 1 2 3 #> #> [[2]] #> [1] 1 2 3 vec_recycle_common(1:10, 1) #> [[1]] #> [1] 1 2 3 4 5 6 7 8 9 10 #> #> [[2]] #> [1] 1 1 1 1 1 1 1 1 1 1"},{"path":"https://vctrs.r-lib.org/dev/articles/type-size.html","id":"appendix-recycling-in-base-r","dir":"Articles","previous_headings":"","what":"Appendix: recycling in base R","title":"Prototypes and sizes","text":"recycling rules base R described R Language Definition implemented single function thus applied consistently. , give brief overview common realisation, well showing exceptions. Generally, base R, pair vectors length, shorter vector recycled length longer: length longer vector integer multiple length shorter, usually get warning: functions recycle silently: data.frame() throws error: R language definition states “arithmetic operation involving zero-length vector zero-length result”. outside arithmetic, rule consistently followed:","code":"rep(1, 6) + 1 #> [1] 2 2 2 2 2 2 rep(1, 6) + 1:2 #> [1] 2 3 2 3 2 3 rep(1, 6) + 1:3 #> [1] 2 3 4 2 3 4 invisible(pmax(1:2, 1:3)) #> Warning in pmax(1:2, 1:3): an argument will be fractionally recycled invisible(1:2 + 1:3) #> Warning in 1:2 + 1:3: longer object length is not a multiple of shorter #> object length invisible(cbind(1:2, 1:3)) #> Warning in cbind(1:2, 1:3): number of rows of result is not a multiple of #> vector length (arg 1) length(atan2(1:3, 1:2)) #> [1] 3 length(paste(1:3, 1:2)) #> [1] 3 length(ifelse(1:3, 1:2, 1:2)) #> [1] 3 data.frame(1:2, 1:3) #> Error in data.frame(1:2, 1:3): arguments imply differing number of rows: 2, 3 # length-0 output 1:2 + integer() #> integer(0) atan2(1:2, integer()) #> numeric(0) pmax(1:2, integer()) #> integer(0) # dropped cbind(1:2, integer()) #> [,1] #> [1,] 1 #> [2,] 2 # recycled to length of first ifelse(rep(TRUE, 4), integer(), character()) #> [1] NA NA NA NA # preserved-ish paste(1:2, integer()) #> [1] \"1 \" \"2 \" # Errors data.frame(1:2, integer()) #> Error in data.frame(1:2, integer()): arguments imply differing number of rows: 2, 0"},{"path":"https://vctrs.r-lib.org/dev/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Hadley Wickham. Author. Lionel Henry. Author. Davis Vaughan. Author, maintainer. data.table team. Copyright holder. Radix sort based data.table's forder() contribution R's order() . Copyright holder, funder.","code":""},{"path":"https://vctrs.r-lib.org/dev/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Wickham H, Henry L, Vaughan D (2024). vctrs: Vector Helpers. R package version 0.6.5.9000, https://github.com/r-lib/vctrs, https://vctrs.r-lib.org/.","code":"@Manual{, title = {vctrs: Vector Helpers}, author = {Hadley Wickham and Lionel Henry and Davis Vaughan}, year = {2024}, note = {R package version 0.6.5.9000, https://github.com/r-lib/vctrs}, url = {https://vctrs.r-lib.org/}, }"},{"path":"https://vctrs.r-lib.org/dev/index.html","id":"vctrs-","dir":"","previous_headings":"","what":"Vector Helpers","title":"Vector Helpers","text":"three main goals vctrs package, described vignette: propose vec_size() vec_ptype() alternatives length() class(); vignette(\"type-size\"). definitions paired framework size-recycling type-coercion. ptype evoke notion prototype, .e. original typical form something. define size- type-stability desirable function properties, use analyse existing base functions, propose better alternatives; vignette(\"stability\"). work particularly motivated thinking ideal properties c(), ifelse(), rbind(). provide new vctr base class makes easy create new S3 vectors; vignette(\"s3-vector\"). vctrs provides methods many base generics terms new vctrs generics, making implementation considerably simpler robust. vctrs developer-focussed package. Understanding extending vctrs requires effort developers, invisible users. ’s hope underlying theory mean users can build accurate mental model without explicitly learning theory. vctrs typically used packages, making easy provide new classes S3 vectors supported throughout tidyverse (beyond). reason, vctrs dependencies.","code":""},{"path":"https://vctrs.r-lib.org/dev/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"Vector Helpers","text":"Install vctrs CRAN : Alternatively, need development version, install :","code":"install.packages(\"vctrs\") # install.packages(\"pak\") pak::pak(\"r-lib/vctrs\")"},{"path":"https://vctrs.r-lib.org/dev/index.html","id":"usage","dir":"","previous_headings":"","what":"Usage","title":"Vector Helpers","text":"","code":"library(vctrs) # Sizes str(vec_size_common(1, 1:10)) #> int 10 str(vec_recycle_common(1, 1:10)) #> List of 2 #> $ : num [1:10] 1 1 1 1 1 1 1 1 1 1 #> $ : int [1:10] 1 2 3 4 5 6 7 8 9 10 # Prototypes str(vec_ptype_common(FALSE, 1L, 2.5)) #> num(0) str(vec_cast_common(FALSE, 1L, 2.5)) #> List of 3 #> $ : num 0 #> $ : num 1 #> $ : num 2.5"},{"path":"https://vctrs.r-lib.org/dev/index.html","id":"motivation","dir":"","previous_headings":"","what":"Motivation","title":"Vector Helpers","text":"original motivation vctrs comes two separate related problems. first problem base::c() rather undesirable behaviour mix different S3 vectors: behaviour arises c() dual purposes: well primary duty combining vectors, secondary duty stripping attributes. example, ?POSIXct suggests use c() want reset timezone. second problem dplyr::bind_rows() extensible others. Currently, handles arbitrary S3 classes using heuristics, often fail, feels like really need think problem order build principled solution. intersects need cleanly support types data frame columns, including lists data frames, data frames, matrices.","code":"# combining factors makes integers c(factor(\"a\"), factor(\"b\")) #> [1] 1 1 # combining dates and date-times gives incorrect values; also, order matters dt <- as.Date(\"2020-01-01\") dttm <- as.POSIXct(dt) c(dt, dttm) #> [1] \"2020-01-01\" \"4321940-06-07\" c(dttm, dt) #> [1] \"2019-12-31 19:00:00 EST\" \"1970-01-01 00:04:22 EST\""},{"path":"https://vctrs.r-lib.org/dev/reference/as-is.html","id":null,"dir":"Reference","previous_headings":"","what":"AsIs S3 class — as-is","title":"AsIs S3 class — as-is","text":"functions help base AsIs class fit vctrs type system providing coercion casting functions.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/as-is.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"AsIs S3 class — as-is","text":"","code":"# S3 method for AsIs vec_ptype2(x, y, ..., x_arg = \"\", y_arg = \"\")"},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":null,"dir":"Reference","previous_headings":"","what":"Construct a data frame — data_frame","title":"Construct a data frame — data_frame","text":"data_frame() constructs data frame. similar base::data.frame(), notable differences make line vctrs principles. Properties section outlines .","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Construct a data frame — data_frame","text":"","code":"data_frame( ..., .size = NULL, .name_repair = c(\"check_unique\", \"unique\", \"universal\", \"minimal\", \"unique_quiet\", \"universal_quiet\"), .error_call = current_env() )"},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Construct a data frame — data_frame","text":"... Vectors become columns data frame. inputs named, names used column names. .size number rows data frame. NULL, computed common size inputs. .name_repair One \"check_unique\", \"unique\", \"universal\", \"minimal\", \"unique_quiet\", \"universal_quiet\". See vec_as_names() meaning options. .error_call execution environment currently running function, e.g. caller_env(). function mentioned error messages source error. See call argument abort() information.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Construct a data frame — data_frame","text":"column names supplied, \"\" used default name columns. applied name repair occurs, default name repair \"check_unique\" error unnamed inputs supplied \"unique\" (\"unique_quiet\") repair empty string column names appropriately. column names matter, use \"minimal\" name repair convenience performance.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":"properties","dir":"Reference","previous_headings":"","what":"Properties","title":"Construct a data frame — data_frame","text":"Inputs recycled common size vec_recycle_common(). exception data frames, inputs modified way. Character vectors never converted factors, lists stored -easy creation list-columns. Unnamed data frame inputs automatically unpacked. Named data frame inputs stored unmodified data frame columns. NULL inputs completely ignored. dots dynamic, allowing splicing lists !!! unquoting.","code":""},{"path":[]},{"path":"https://vctrs.r-lib.org/dev/reference/data_frame.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Construct a data frame — data_frame","text":"","code":"data_frame(x = 1, y = 2) #> x y #> 1 1 2 # Inputs are recycled using tidyverse recycling rules data_frame(x = 1, y = 1:3) #> x y #> 1 1 1 #> 2 1 2 #> 3 1 3 # Strings are never converted to factors class(data_frame(x = \"foo\")$x) #> [1] \"character\" # List columns can be easily created df <- data_frame(x = list(1:2, 2, 3:4), y = 3:1) # However, the base print method is suboptimal for displaying them, # so it is recommended to convert them to tibble if (rlang::is_installed(\"tibble\")) { tibble::as_tibble(df) } #> # A tibble: 3 × 2 #> x y #> #> 1 3 #> 2 2 #> 3 1 # Named data frame inputs create data frame columns df <- data_frame(x = data_frame(y = 1:2, z = \"a\")) # The `x` column itself is another data frame df$x #> y z #> 1 1 a #> 2 2 a # Again, it is recommended to convert these to tibbles for a better # print method if (rlang::is_installed(\"tibble\")) { tibble::as_tibble(df) } #> # A tibble: 2 × 1 #> x$y $z #> #> 1 1 a #> 2 2 a # Unnamed data frame input is automatically unpacked data_frame(x = 1, data_frame(y = 1:2, z = \"a\")) #> x y z #> 1 1 1 a #> 2 1 2 a"},{"path":"https://vctrs.r-lib.org/dev/reference/df_list.html","id":null,"dir":"Reference","previous_headings":"","what":"Collect columns for data frame construction — df_list","title":"Collect columns for data frame construction — df_list","text":"df_list() constructs data structure underlying data frame, named list equal-length vectors. often used combination new_data_frame() safely consistently create helper function data frame subclasses.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/df_list.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Collect columns for data frame construction — df_list","text":"","code":"df_list( ..., .size = NULL, .unpack = TRUE, .name_repair = c(\"check_unique\", \"unique\", \"universal\", \"minimal\", \"unique_quiet\", \"universal_quiet\"), .error_call = current_env() )"},{"path":"https://vctrs.r-lib.org/dev/reference/df_list.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Collect columns for data frame construction — df_list","text":"... Vectors equal-length. inputs named, names used names resulting list. .size common size vectors supplied .... NULL, computed common size inputs. .unpack unnamed data frame inputs unpacked? Defaults TRUE. .name_repair One \"check_unique\", \"unique\", \"universal\", \"minimal\", \"unique_quiet\", \"universal_quiet\". See vec_as_names() meaning options. .error_call execution environment currently running function, e.g. caller_env(). function mentioned error messages source error. See call argument abort() information.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/df_list.html","id":"properties","dir":"Reference","previous_headings":"","what":"Properties","title":"Collect columns for data frame construction — df_list","text":"Inputs recycled common size vec_recycle_common(). exception data frames, inputs modified way. Character vectors never converted factors, lists stored -easy creation list-columns. Unnamed data frame inputs automatically unpacked. Named data frame inputs stored unmodified data frame columns. NULL inputs completely ignored. dots dynamic, allowing splicing lists !!! unquoting.","code":""},{"path":[]},{"path":"https://vctrs.r-lib.org/dev/reference/df_list.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Collect columns for data frame construction — df_list","text":"","code":"# `new_data_frame()` can be used to create custom data frame constructors new_fancy_df <- function(x = list(), n = NULL, ..., class = NULL) { new_data_frame(x, n = n, ..., class = c(class, \"fancy_df\")) } # Combine this constructor with `df_list()` to create a safe, # consistent helper function for your data frame subclass fancy_df <- function(...) { data <- df_list(...) new_fancy_df(data) } df <- fancy_df(x = 1) class(df) #> [1] \"fancy_df\" \"data.frame\""},{"path":"https://vctrs.r-lib.org/dev/reference/df_ptype2.html","id":null,"dir":"Reference","previous_headings":"","what":"Coercion between two data frames — df_ptype2","title":"Coercion between two data frames — df_ptype2","text":"df_ptype2() df_cast() two functions need call vec_ptype2() vec_cast() methods data frame subclasses. See ?howto-faq-coercion-data-frame. main job determine common type two data frames, adding coercing columns needed, throwing incompatible type error columns compatible.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/df_ptype2.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Coercion between two data frames — df_ptype2","text":"","code":"df_ptype2(x, y, ..., x_arg = \"\", y_arg = \"\", call = caller_env()) df_cast(x, to, ..., x_arg = \"\", to_arg = \"\", call = caller_env()) tib_ptype2(x, y, ..., x_arg = \"\", y_arg = \"\", call = caller_env()) tib_cast(x, to, ..., x_arg = \"\", to_arg = \"\", call = caller_env())"},{"path":"https://vctrs.r-lib.org/dev/reference/df_ptype2.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Coercion between two data frames — df_ptype2","text":"x, y, Subclasses data frame. ... call df_ptype2() df_cast() vec_ptype2() vec_cast() method, must forward dots passed method df_ptype2() df_cast(). x_arg, y_arg Argument names x y. used error messages inform user locations incompatible types (see stop_incompatible_type()). call execution environment currently running function, e.g. caller_env(). function mentioned error messages source error. See call argument abort() information. to_arg Argument name used error messages inform user locations incompatible types (see stop_incompatible_type()).","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/df_ptype2.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Coercion between two data frames — df_ptype2","text":"x y compatible, error class vctrs_error_incompatible_type thrown. x y compatible, df_ptype2() returns common type bare data frame. tib_ptype2() returns common type bare tibble.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/faq-compatibility-types.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - How is the compatibility of vector types decided? — faq-compatibility-types","title":"FAQ - How is the compatibility of vector types decided? — faq-compatibility-types","text":"Two vectors compatible can safely: Combine one larger vector. Assign values one vectors vector. Examples compatible types integer double vectors. hand, integer character vectors compatible.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/faq-compatibility-types.html","id":"common-type-of-multiple-vectors","dir":"Reference","previous_headings":"","what":"Common type of multiple vectors","title":"FAQ - How is the compatibility of vector types decided? — faq-compatibility-types","text":"two possible outcomes multiple vectors different types combined larger vector: incompatible type error thrown types compatible: vectors combined vector common type inputs. example, common type integer logical integer: general, common type richer type, words type can represent values. Logical vectors bottom hierarchy numeric types can represent two values (counting missing values). come integer vectors, doubles. vctrs type hierarchy fundamental vectors:","code":"df1 <- data.frame(x = 1:3) df2 <- data.frame(x = \"foo\") dplyr::bind_rows(df1, df2) #> Error in `dplyr::bind_rows()`: #> ! Can't combine `..1$x` and `..2$x` . df1 <- data.frame(x = 1:3) df2 <- data.frame(x = FALSE) dplyr::bind_rows(df1, df2) #> x #> 1 1 #> 2 2 #> 3 3 #> 4 0"},{"path":"https://vctrs.r-lib.org/dev/reference/faq-compatibility-types.html","id":"type-conversion-and-lossy-cast-errors","dir":"Reference","previous_headings":"","what":"Type conversion and lossy cast errors","title":"FAQ - How is the compatibility of vector types decided? — faq-compatibility-types","text":"Type compatibility necessarily mean can convert one type type. ’s one types might support larger set possible values. instance, integer double vectors compatible, double vectors can’t converted integer contain fractional values. vctrs can’t convert vector target type rich source type, throws lossy cast error. Assigning fractional number integer vector typical example lossy cast error:","code":"int_vector <- 1:3 vec_assign(int_vector, 2, 0.001) #> Error in `vec_assign()`: #> ! Can't convert from to due to loss of precision. #> * Locations: 1"},{"path":"https://vctrs.r-lib.org/dev/reference/faq-compatibility-types.html","id":"how-to-make-two-vector-classes-compatible-","dir":"Reference","previous_headings":"","what":"How to make two vector classes compatible?","title":"FAQ - How is the compatibility of vector types decided? — faq-compatibility-types","text":"encounter two vector types think compatible, might need implement coercion methods. Reach author(s) classes ask makes sense classes compatible. developer FAQ items provide guides implementing coercion methods: example implementing coercion methods simple vectors, see ?howto-faq-coercion. example implementing coercion methods data frame subclasses, see ?howto-faq-coercion-data-frame.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/faq-error-incompatible-attributes.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - Error/Warning: Some attributes are incompatible — faq-error-incompatible-attributes","title":"FAQ - Error/Warning: Some attributes are incompatible — faq-error-incompatible-attributes","text":"error occurs vec_ptype2() vec_cast() supplied vectors classes different attributes. case, vctrs know combine inputs. fix error, maintainer class implement self--self coercion methods vec_ptype2() vec_cast().","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/faq-error-incompatible-attributes.html","id":"implementing-coercion-methods","dir":"Reference","previous_headings":"","what":"Implementing coercion methods","title":"FAQ - Error/Warning: Some attributes are incompatible — faq-error-incompatible-attributes","text":"overview generics work roles vctrs, see ?theory-faq-coercion. example implementing coercion methods simple vectors, see ?howto-faq-coercion. example implementing coercion methods data frame subclasses, see ?howto-faq-coercion-data-frame. tutorial implementing vctrs classes scratch, see vignette(\"s3-vector\").","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/faq-error-scalar-type.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - Error: Input must be a vector — faq-error-scalar-type","title":"FAQ - Error: Input must be a vector — faq-error-scalar-type","text":"error occurs function expects vector gets scalar object instead. commonly happens code attempts assign scalar object column data frame:","code":"fn <- function() NULL tibble::tibble(x = fn) #> Error in `tibble::tibble()`: #> ! All columns in a tibble must be vectors. #> x Column `x` is a function. fit <- lm(1:3 ~ 1) tibble::tibble(x = fit) #> Error in `tibble::tibble()`: #> ! All columns in a tibble must be vectors. #> x Column `x` is a `lm` object."},{"path":"https://vctrs.r-lib.org/dev/reference/faq-error-scalar-type.html","id":"vectorness-in-base-r-and-in-the-tidyverse","dir":"Reference","previous_headings":"","what":"Vectorness in base R and in the tidyverse","title":"FAQ - Error: Input must be a vector — faq-error-scalar-type","text":"base R, almost everything vector behaves like vector. tidyverse chosen bit stricter considered vector. main question ask decide vectorness type whether makes sense include object column data frame. main difference S3 lists considered vectors base R tidyverse ’s case default: Defused function calls another (esoteric) example:","code":"fit <- lm(1:3 ~ 1) typeof(fit) #> [1] \"list\" class(fit) #> [1] \"lm\" # S3 lists can be subset like a vector using base R: fit[c(1, 4)] #> $coefficients #> (Intercept) #> 2 #> #> $rank #> [1] 1 # But not in vctrs vctrs::vec_slice(fit, c(1, 4)) #> Error in `vctrs::vec_slice()`: #> ! `x` must be a vector, not a object. call <- quote(foo(bar = TRUE, baz = FALSE)) call #> foo(bar = TRUE, baz = FALSE) # They can be subset like a vector using base R: call[1:2] #> foo(bar = TRUE) lapply(call, function(x) x) #> [[1]] #> foo #> #> $bar #> [1] TRUE #> #> $baz #> [1] FALSE # But not with vctrs: vctrs::vec_slice(call, 1:2) #> Error in `vctrs::vec_slice()`: #> ! `x` must be a vector, not a call."},{"path":"https://vctrs.r-lib.org/dev/reference/faq-error-scalar-type.html","id":"i-get-a-scalar-type-error-but-i-think-this-is-a-bug","dir":"Reference","previous_headings":"","what":"I get a scalar type error but I think this is a bug","title":"FAQ - Error: Input must be a vector — faq-error-scalar-type","text":"’s possible author class needs work declare class vector. Consider reaching author. written developer FAQ page help fix issue.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/fields.html","id":null,"dir":"Reference","previous_headings":"","what":"Tools for accessing the fields of a record. — fields","title":"Tools for accessing the fields of a record. — fields","text":"rcrd behaves like vector, length(), names(), $ can provide access fields underlying list. helpers : fields() equivalent names(); n_fields() equivalent length(); field() equivalent $.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/fields.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Tools for accessing the fields of a record. — fields","text":"","code":"fields(x) n_fields(x) field(x, i) field(x, i) <- value"},{"path":"https://vctrs.r-lib.org/dev/reference/fields.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Tools for accessing the fields of a record. — fields","text":"x rcrd, .e. list equal length vectors unique names.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/fields.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Tools for accessing the fields of a record. — fields","text":"","code":"x <- new_rcrd(list(x = 1:3, y = 3:1, z = letters[1:3])) n_fields(x) #> [1] 3 fields(x) #> [1] \"x\" \"y\" \"z\" field(x, \"y\") #> [1] 3 2 1 field(x, \"y\") <- runif(3) field(x, \"y\") #> [1] 0.08075014 0.83433304 0.60076089"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion-data-frame.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","title":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","text":"guide provides practical recipe implementing vec_ptype2() vec_cast() methods coercions data frame subclasses. Related topics: overview coercion mechanism vctrs, see ?theory-faq-coercion. example implementing coercion methods simple vectors, see ?howto-faq-coercion. Coercion data frames occurs different data frame classes combined way. two main methods combination currently row-binding vec_rbind() col-binding vec_cbind() (turn used number dplyr tidyr functions). functions take multiple data frame inputs automatically coerce common type. vctrs generally strict kind automatic coercions performed combining inputs. case data frames decided bit less strict convenience. Instead throwing incompatible type error, fall back base data frame tibble don’t know combine two data frame subclasses. still good idea specify proper coercion behaviour data frame subclasses soon possible. see two examples guide. first example data frame subclass particular attributes manage. second example, implement coercion methods tibble subclass includes potentially incompatible attributes.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion-data-frame.html","id":"roxygen-workflow","dir":"Reference","previous_headings":"","what":"Roxygen workflow","title":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","text":"implement methods generics, first import generics namespace redocument: Note batches methods add package, need export methods redocument immediately, even development. Otherwise won’t scope run unit tests e.g. testthat. Implementing double dispatch methods similar implementing regular S3 methods. examples using roxygen2 tags register methods, can also register methods manually NAMESPACE file lazily s3_register().","code":"#' @importFrom vctrs vec_ptype2 vec_cast NULL"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion-data-frame.html","id":"parent-methods","dir":"Reference","previous_headings":"","what":"Parent methods","title":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","text":"common type determination performed parent class. vctrs, double dispatch implemented way need call methods parent class manually. vec_ptype2() means need call df_ptype2() (data frame subclasses) tib_ptype2() (tibble subclasses). Similarly, df_cast() tib_cast() workhorses vec_cast() methods subtypes data.frame tbl_df. functions take union columns x y, ensure shared columns type. functions much less strict vec_ptype2() vec_cast() accept subclass data frame input. always return data.frame tbl_df. probably want write similar functions subclass avoid repetition code. may want export well expecting people derive class.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion-data-frame.html","id":"a-data-table-example","dir":"Reference","previous_headings":"","what":"A data.table example","title":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","text":"example actual implementation vctrs coercion methods data.table. simple example don’t keep track attributes class manage incompatibilities. See tibble section complicated example. first create dt_ptype2() dt_cast() helpers. wrap around parent methods df_ptype2() df_cast(), transform common type converted input data table. may want export helpers expect packages derive data frame class. helpers always return data tables. end use conversion generic .data.table(). Depending tools available particular class hand, constructor might appropriate well. start self-self method: data frame data table, consider richer type data table. decision based value coverage data structures, idea data tables richer behaviour. Since data tables richer type, call dt_type2() vec_ptype2() method. always returns data table, matter order arguments: vec_cast() methods follow pattern, note method coercing data frame uses df_cast() rather dt_cast(). Also, please note historical reasons, order classes method name reverse order arguments function signature. first class represents , whereas second class represents x. methods vctrs now able combine data tables data frames:","code":"dt_ptype2 <- function(x, y, ...) { as.data.table(df_ptype2(x, y, ...)) } dt_cast <- function(x, to, ...) { as.data.table(df_cast(x, to, ...)) } #' @export vec_ptype2.data.table.data.table <- function(x, y, ...) { dt_ptype2(x, y, ...) } #' @export vec_ptype2.data.table.data.frame <- function(x, y, ...) { dt_ptype2(x, y, ...) } #' @export vec_ptype2.data.frame.data.table <- function(x, y, ...) { dt_ptype2(x, y, ...) } #' @export vec_cast.data.table.data.table <- function(x, to, ...) { dt_cast(x, to, ...) } #' @export vec_cast.data.table.data.frame <- function(x, to, ...) { # `x` is a data.frame to be converted to a data.table dt_cast(x, to, ...) } #' @export vec_cast.data.frame.data.table <- function(x, to, ...) { # `x` is a data.table to be converted to a data.frame df_cast(x, to, ...) } vec_cbind(data.frame(x = 1:3), data.table(y = \"foo\")) #> x y #> 1: 1 foo #> 2: 2 foo #> 3: 3 foo"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion-data-frame.html","id":"a-tibble-example","dir":"Reference","previous_headings":"","what":"A tibble example","title":"FAQ - How to implement ptype2 and cast methods? (Data frames) — howto-faq-coercion-data-frame","text":"example implement coercion methods tibble subclass carries colour scalar metadata: subclass simple. modify header. Combinations work properly box, instead vctrs falls back bare tibble: Instead falling back data frame, like return combined data frame tibble. subclass metadata normal data frames (colour), supertype tibble data frame, .e. richer type. similar grouped tibble general type tibble data frame. Conceptually, latter pinned single constant group. coercion methods data frames operate two steps: check compatible subclass attributes. case tibble colour , undefined. call parent methods, case tib_ptype2() tib_cast() subclass tibble. eventually calls data frame methods df_ptype2() tib_ptype2() match columns types. process usually wrapped two functions avoid repetition. Consider exporting expect class derived subclasses. first implement helper determine two data frames compatible colours. use df_colour() accessor returns NULL data frame colour undefined. Next implement coercion helpers. colours compatible, call stop_incompatible_cast() stop_incompatible_type(). strict coercion semantics justified class colour data attribute. non essential detail attribute, like timezone datetime, just standardise value left-hand side. simpler cases (like data.table example), methods need take arguments suffixed _arg. need take arguments can pass stop_ functions detect incompatibility. also passed parent methods. Let’s now implement coercion methods, starting self-self methods. can now combine compatible instances class! methods combining class tibbles follow pattern. ptype2 return class cases richer type: cast careful returning tibble casting tibble. Note call vctrs::tib_cast(): point, get correct combinations tibbles: However done yet. coercion hierarchy different class hierarchy, inheritance coercion methods. ’re getting correct behaviour data frames yet haven’t explicitly specified methods class: Let’s finish boiler plate: completes implementation:","code":"# User constructor my_tibble <- function(colour = NULL, ...) { new_my_tibble(tibble::tibble(...), colour = colour) } # Developer constructor new_my_tibble <- function(x, colour = NULL) { stopifnot(is.data.frame(x)) tibble::new_tibble( x, colour = colour, class = \"my_tibble\", nrow = nrow(x) ) } df_colour <- function(x) { if (inherits(x, \"my_tibble\")) { attr(x, \"colour\") } else { NULL } } #'@export print.my_tibble <- function(x, ...) { cat(sprintf(\"<%s: %s>\\n\", class(x)[[1]], df_colour(x))) cli::cat_line(format(x)[-1]) } red <- my_tibble(\"red\", x = 1, y = 1:2) red #> #> x y #> #> 1 1 1 #> 2 1 2 red[2] #> #> y #> #> 1 1 #> 2 2 green <- my_tibble(\"green\", z = TRUE) green #> #> z #> #> 1 TRUE vec_rbind(red, tibble::tibble(x = 10:12)) #> # A tibble: 5 x 2 #> x y #> #> 1 1 1 #> 2 1 2 #> 3 10 NA #> 4 11 NA #> 5 12 NA has_compatible_colours <- function(x, y) { x_colour <- df_colour(x) %||% df_colour(y) y_colour <- df_colour(y) %||% x_colour identical(x_colour, y_colour) } #' @export my_tib_cast <- function(x, to, ..., x_arg = \"\", to_arg = \"\") { out <- tib_cast(x, to, ..., x_arg = x_arg, to_arg = to_arg) if (!has_compatible_colours(x, to)) { stop_incompatible_cast( x, to, x_arg = x_arg, to_arg = to_arg, details = \"Can't combine colours.\" ) } colour <- df_colour(x) %||% df_colour(to) new_my_tibble(out, colour = colour) } #' @export my_tib_ptype2 <- function(x, y, ..., x_arg = \"\", y_arg = \"\") { out <- tib_ptype2(x, y, ..., x_arg = x_arg, y_arg = y_arg) if (!has_compatible_colours(x, y)) { stop_incompatible_type( x, y, x_arg = x_arg, y_arg = y_arg, details = \"Can't combine colours.\" ) } colour <- df_colour(x) %||% df_colour(y) new_my_tibble(out, colour = colour) } #' @export vec_ptype2.my_tibble.my_tibble <- function(x, y, ...) { my_tib_ptype2(x, y, ...) } #' @export vec_cast.my_tibble.my_tibble <- function(x, to, ...) { my_tib_cast(x, to, ...) } vec_rbind(red, red) #> #> x y #> #> 1 1 1 #> 2 1 2 #> 3 1 1 #> 4 1 2 vec_rbind(green, green) #> #> z #> #> 1 TRUE #> 2 TRUE vec_rbind(green, red) #> Error in `my_tib_ptype2()`: #> ! Can't combine `..1` and `..2` . #> Can't combine colours. #' @export vec_ptype2.my_tibble.tbl_df <- function(x, y, ...) { my_tib_ptype2(x, y, ...) } #' @export vec_ptype2.tbl_df.my_tibble <- function(x, y, ...) { my_tib_ptype2(x, y, ...) } #' @export vec_cast.my_tibble.tbl_df <- function(x, to, ...) { my_tib_cast(x, to, ...) } #' @export vec_cast.tbl_df.my_tibble <- function(x, to, ...) { tib_cast(x, to, ...) } vec_rbind(red, tibble::tibble(x = 10:12)) #> #> x y #> #> 1 1 1 #> 2 1 2 #> 3 10 NA #> 4 11 NA #> 5 12 NA vec_rbind(red, data.frame(x = 10:12)) #> # A tibble: 5 x 2 #> x y #> #> 1 1 1 #> 2 1 2 #> 3 10 NA #> 4 11 NA #> 5 12 NA #' @export vec_ptype2.my_tibble.data.frame <- function(x, y, ...) { my_tib_ptype2(x, y, ...) } #' @export vec_ptype2.data.frame.my_tibble <- function(x, y, ...) { my_tib_ptype2(x, y, ...) } #' @export vec_cast.my_tibble.data.frame <- function(x, to, ...) { my_tib_cast(x, to, ...) } #' @export vec_cast.data.frame.my_tibble <- function(x, to, ...) { df_cast(x, to, ...) } vec_rbind(red, data.frame(x = 10:12)) #> #> x y #> #> 1 1 1 #> 2 1 2 #> 3 10 NA #> 4 11 NA #> 5 12 NA"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"guide illustrates implement vec_ptype2() vec_cast() methods existing classes. Related topics: overview generics work roles vctrs, see ?theory-faq-coercion. example implementing coercion methods data frame subclasses, see ?howto-faq-coercion-data-frame. tutorial implementing vctrs classes scratch, see vignette(\"s3-vector\")","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"the-natural-number-class","dir":"Reference","previous_headings":"","what":"The natural number class","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"’ll illustrate implement coercion methods simple class represents natural numbers. scenario existing class already features constructor methods print() subset.","code":"#' @export new_natural <- function(x) { if (is.numeric(x) || is.logical(x)) { stopifnot(is_whole(x)) x <- as.integer(x) } else { stop(\"Can't construct natural from unknown type.\") } structure(x, class = \"my_natural\") } is_whole <- function(x) { all(x %% 1 == 0 | is.na(x)) } #' @export print.my_natural <- function(x, ...) { cat(\"\\n\") x <- unclass(x) NextMethod() } #' @export `[.my_natural` <- function(x, i, ...) { new_natural(NextMethod()) } new_natural(1:3) #> #> [1] 1 2 3 new_natural(c(1, NA)) #> #> [1] 1 NA"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"roxygen-workflow","dir":"Reference","previous_headings":"","what":"Roxygen workflow","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"implement methods generics, first import generics namespace redocument: Note batches methods add package, need export methods redocument immediately, even development. Otherwise won’t scope run unit tests e.g. testthat. Implementing double dispatch methods similar implementing regular S3 methods. examples using roxygen2 tags register methods, can also register methods manually NAMESPACE file lazily s3_register().","code":"#' @importFrom vctrs vec_ptype2 vec_cast NULL"},{"path":[]},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"the-self-self-method","dir":"Reference","previous_headings":"","what":"The self-self method","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"first method implement one signals class compatible : vec_ptype2() implements fallback try compatible simple classes, may seem don’t need implement self-self coercion method. However, must implement explicitly vctrs knows class implementing vctrs methods (instance disable fallbacks base::c()). Also, makes class bit efficient.","code":"#' @export vec_ptype2.my_natural.my_natural <- function(x, y, ...) { x } vec_ptype2(new_natural(1), new_natural(2:3)) #> #> integer(0)"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"the-parent-and-children-methods","dir":"Reference","previous_headings":"","what":"The parent and children methods","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"natural number class conceptually parent child , class compatible logical, integer, double vectors yet: ’ll specify twin methods classes, returning richer class case. natural number integer, latter richer class: longer get common type errors logical integer: done yet. Pairwise coercion methods must implemented connected nodes coercion hierarchy, include double vectors . coercion methods grand-parent types must implemented separately:","code":"vec_ptype2(TRUE, new_natural(2:3)) #> Error: #> ! Can't combine `TRUE` and `new_natural(2:3)` . vec_ptype2(new_natural(1), 2:3) #> Error: #> ! Can't combine `new_natural(1)` and `2:3` . #' @export vec_ptype2.my_natural.logical <- function(x, y, ...) { # The order of the classes in the method name follows the order of # the arguments in the function signature, so `x` is the natural # number and `y` is the logical x } #' @export vec_ptype2.logical.my_natural <- function(x, y, ...) { # In this case `y` is the richer natural number y } #' @export vec_ptype2.my_natural.integer <- function(x, y, ...) { y } #' @export vec_ptype2.integer.my_natural <- function(x, y, ...) { x } vec_ptype2(TRUE, new_natural(2:3)) #> #> integer(0) vec_ptype2(new_natural(1), 2:3) #> integer(0) #' @export vec_ptype2.my_natural.double <- function(x, y, ...) { y } #' @export vec_ptype2.double.my_natural <- function(x, y, ...) { x }"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"incompatible-attributes","dir":"Reference","previous_headings":"","what":"Incompatible attributes","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"time, inputs incompatible different classes vec_ptype2() method implemented. rarely, inputs incompatible attributes. case incompatibility signalled calling stop_incompatible_type(). following example, implement self-self ptype2 method hypothetical subclass stricter combination semantics. method throws error levels two factors compatible. Note methods need take x_arg y_arg parameters pass stop_incompatible_type(). argument tags help create informative error messages common type determination column data frame. part generic signature can usually left used.","code":"#' @export vec_ptype2.my_strict_factor.my_strict_factor <- function(x, y, ..., x_arg = \"\", y_arg = \"\") { if (!setequal(levels(x), levels(y))) { stop_incompatible_type(x, y, x_arg = x_arg, y_arg = y_arg) } x }"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-coercion.html","id":"implementing-vec-cast-","dir":"Reference","previous_headings":"","what":"Implementing vec_cast()","title":"FAQ - How to implement ptype2 and cast methods? — howto-faq-coercion","text":"Corresponding vec_cast() methods must implemented vec_ptype2() methods. general pattern convert argument x type . methods validate values x make sure conform values . Please note historical reasons, order classes method name reverse order arguments function signature. first class represents , whereas second class represents x. self-self method easy case, just returns target input: types need validated. perform input validation new_natural() constructor, ’s good fit vec_cast() implementations. methods, vctrs now able combine logical natural vectors. properly returns richer type two, natural vector: haven’t implemented conversions natural, still doesn’t know combine natural richer integer double types: quick work completes implementation coercion methods vctrs: now get expected combinations.","code":"#' @export vec_cast.my_natural.my_natural <- function(x, to, ...) { x } #' @export vec_cast.my_natural.logical <- function(x, to, ...) { # The order of the classes in the method name is in reverse order # of the arguments in the function signature, so `to` is the natural # number and `x` is the logical new_natural(x) } vec_cast.my_natural.integer <- function(x, to, ...) { new_natural(x) } vec_cast.my_natural.double <- function(x, to, ...) { new_natural(x) } vec_c(TRUE, new_natural(1), FALSE) #> #> [1] 1 1 0 vec_c(new_natural(1), 10L) #> Error in `vec_c()`: #> ! Can't convert `..1` to . vec_c(1.5, new_natural(1)) #> Error in `vec_c()`: #> ! Can't convert `..2` to . #' @export vec_cast.logical.my_natural <- function(x, to, ...) { # In this case `to` is the logical and `x` is the natural number attributes(x) <- NULL as.logical(x) } #' @export vec_cast.integer.my_natural <- function(x, to, ...) { attributes(x) <- NULL as.integer(x) } #' @export vec_cast.double.my_natural <- function(x, to, ...) { attributes(x) <- NULL as.double(x) } vec_c(new_natural(1), 10L) #> [1] 1 10 vec_c(1.5, new_natural(1)) #> [1] 1.5 1.0"},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-fix-scalar-type-error.html","id":null,"dir":"Reference","previous_headings":"","what":"FAQ - Why isn't my class treated as a vector? — howto-faq-fix-scalar-type-error","title":"FAQ - Why isn't my class treated as a vector? — howto-faq-fix-scalar-type-error","text":"tidyverse bit stricter base R regarding kind objects considered vectors (see user FAQ topic). Sometimes vctrs won’t treat class vector .","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-fix-scalar-type-error.html","id":"why-isn-t-my-list-class-considered-a-vector-","dir":"Reference","previous_headings":"","what":"Why isn’t my list class considered a vector?","title":"FAQ - Why isn't my class treated as a vector? — howto-faq-fix-scalar-type-error","text":"default, S3 lists considered vectors vctrs: treated vector, class must either inherit \"list\" explicitly: implement vec_proxy() method returns input explicit inheritance possible troublesome: Note explicit inheritance preferred way makes possible class dispatch list methods S3 generics:","code":"my_list <- structure(list(), class = \"my_class\") vctrs::vec_is(my_list) #> [1] FALSE my_explicit_list <- structure(list(), class = c(\"my_class\", \"list\")) vctrs::vec_is(my_explicit_list) #> [1] TRUE #' @export vec_proxy.my_class <- function(x, ...) x vctrs::vec_is(my_list) #> [1] FALSE my_generic <- function(x) UseMethod(\"my_generic\") my_generic.list <- function(x) \"dispatched!\" my_generic(my_list) #> Error in UseMethod(\"my_generic\"): no applicable method for 'my_generic' applied to an object of class \"my_class\" my_generic(my_explicit_list) #> [1] \"dispatched!\""},{"path":"https://vctrs.r-lib.org/dev/reference/howto-faq-fix-scalar-type-error.html","id":"why-isn-t-my-data-frame-class-considered-a-vector-","dir":"Reference","previous_headings":"","what":"Why isn’t my data frame class considered a vector?","title":"FAQ - Why isn't my class treated as a vector? — howto-faq-fix-scalar-type-error","text":"likely explanation data frame properly constructed. However, get “Input must vector” error data frame subclass, probably means data frame properly constructed. main cause errors data frames whose base class \"data.frame\": problematic many tidyverse functions won’t work properly: generally appropriate declare class superclass another class. generally consider undefined behaviour (UB). fix errors, can simply change construction data frame class \"data.frame\" base class, .e. come last class vector:","code":"my_df <- data.frame(x = 1) class(my_df) <- c(\"data.frame\", \"my_class\") vctrs::obj_check_vector(my_df) #> Error: #> ! `my_df` must be a vector, not a object. dplyr::slice(my_df, 1) #> Error in `vec_slice()`: #> ! `x` must be a vector, not a object. class(my_df) <- c(\"my_class\", \"data.frame\") vctrs::obj_check_vector(my_df) dplyr::slice(my_df, 1) #> x #> 1 1"},{"path":"https://vctrs.r-lib.org/dev/reference/int64.html","id":null,"dir":"Reference","previous_headings":"","what":"64 bit integers — vec_ptype_full.integer64","title":"64 bit integers — vec_ptype_full.integer64","text":"integer64 64 bits integer vector, implemented bit64 package.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/int64.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"64 bit integers — vec_ptype_full.integer64","text":"","code":"# S3 method for integer64 vec_ptype_full(x, ...) # S3 method for integer64 vec_ptype_abbr(x, ...) # S3 method for integer64 vec_ptype2(x, y, ...) # S3 method for integer64 vec_cast(x, to, ...)"},{"path":"https://vctrs.r-lib.org/dev/reference/int64.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"64 bit integers — vec_ptype_full.integer64","text":"functions help integer64 class bit64 vctrs type system providing coercion functions casting functions.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-matches-algorithm.html","id":null,"dir":"Reference","previous_headings":"","what":"Internal FAQ - Implementation of vec_locate_matches() — internal-faq-matches-algorithm","title":"Internal FAQ - Implementation of vec_locate_matches() — internal-faq-matches-algorithm","text":"vec_locate_matches() similar vec_match(), detects matches default, can match conditions equality (like >= <). also various arguments limit adjust exactly kinds matches returned. example:","code":"x <- c(\"a\", \"b\", \"a\", \"c\", \"d\") y <- c(\"d\", \"b\", \"a\", \"d\", \"a\", \"e\") # For each value of `x`, find all matches in `y` # - The \"c\" in `x` doesn't have a match, so it gets an NA location by default # - The \"e\" in `y` isn't matched by anything in `x`, so it is dropped by default vec_locate_matches(x, y) #> needles haystack #> 1 1 3 #> 2 1 5 #> 3 2 2 #> 4 3 3 #> 5 3 5 #> 6 4 NA #> 7 5 1 #> 8 5 4"},{"path":[]},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-matches-algorithm.html","id":"overview-and-","dir":"Reference","previous_headings":"","what":"Overview and ==","title":"Internal FAQ - Implementation of vec_locate_matches() — internal-faq-matches-algorithm","text":"simplest (approximate) way think algorithm df_locate_matches_recurse() uses sorts inputs, starts midpoint needles uses binary search find needle haystack. Since might multiple needle, find location lower upper duplicate needle handle duplicates needle . Similarly, duplicates matching haystack value, find lower upper duplicates match. condition ==, pretty much . needle, record 3 things: location needle, location lower match haystack, match size (.e. loc_upper_match - loc_lower_match + 1). later gets expanded expand_compact_indices() actual output. recording matches single needle, perform procedure LHS RHS needle (remember started midpoint needle). .e. [1, loc_needle-1] [loc_needle+1, size_needles], taking midpoint two ranges, finding respective needle haystack, recording matches, continuing next needle. iteration proceeds run needles. data frame multiple columns, add layer recursion . first column, find locations lower/upper duplicate current needle, find locations lower/upper matches haystack. final column data frame, record matches, otherwise pass information another call df_locate_matches_recurse(), bumping column index using refined lower/upper bounds starting bounds next column. think example useful , step process iterations: real code, rather comparing double values columns directly, replace column pseudo \"joint ranks\" computed -th column needles -th column haystack. approximately like vec_rank(vec_c(needles$x, haystack$x), type = \"dense\"), splitting resulting ranks back corresponding needle/haystack columns. keeps recursion code simpler, worry comparing integers.","code":"# these are sorted already for simplicity needles <- data_frame(x = c(1, 1, 2, 2, 2, 3), y = c(1, 2, 3, 4, 5, 3)) haystack <- data_frame(x = c(1, 1, 2, 2, 3), y = c(2, 3, 4, 4, 1)) needles #> x y #> 1 1 1 #> 2 1 2 #> 3 2 3 #> 4 2 4 #> 5 2 5 #> 6 3 3 haystack #> x y #> 1 1 2 #> 2 1 3 #> 3 2 4 #> 4 2 4 #> 5 3 1 ## Column 1, iteration 1 # start at midpoint in needles # this corresponds to x==2 loc_mid_needles <- 3L # finding all x==2 values in needles gives us: loc_lower_duplicate_needles <- 3L loc_upper_duplicate_needles <- 5L # finding matches in haystack give us: loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 4L # compute LHS/RHS bounds for next needle lhs_loc_lower_bound_needles <- 1L # original lower bound lhs_loc_upper_bound_needles <- 2L # lower_duplicate-1 rhs_loc_lower_bound_needles <- 6L # upper_duplicate+1 rhs_loc_upper_bound_needles <- 6L # original upper bound # We still have a 2nd column to check. So recurse and pass on the current # duplicate and match bounds to start the 2nd column with. ## Column 2, iteration 1 # midpoint of [3, 5] # value y==4 loc_mid_needles <- 4L loc_lower_duplicate_needles <- 4L loc_upper_duplicate_needles <- 4L loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 4L # last column, so record matches # - this was location 4 in needles # - lower match in haystack is at loc 3 # - match size is 2 # Now handle LHS and RHS of needle midpoint lhs_loc_lower_bound_needles <- 3L # original lower bound lhs_loc_upper_bound_needles <- 3L # lower_duplicate-1 rhs_loc_lower_bound_needles <- 5L # upper_duplicate+1 rhs_loc_upper_bound_needles <- 5L # original upper bound ## Column 2, iteration 2 (using LHS bounds) # midpoint of [3,3] # value of y==3 loc_mid_needles <- 3L loc_lower_duplicate_needles <- 3L loc_upper_duplicate_needles <- 3L # no match! no y==3 in haystack for x==2 # lower-match will always end up > upper-match in this case loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 2L # no LHS or RHS needle values to do, so we are done here ## Column 2, iteration 3 (using RHS bounds) # same as above, range of [5,5], value of y==5, which has no match in haystack ## Column 1, iteration 2 (LHS of first x needle) # Now we are done with the x needles from [3,5], so move on to the LHS and RHS # of that. Here we would do the LHS: # midpoint of [1,2] loc_mid_needles <- 1L # ... ## Column 1, iteration 3 (RHS of first x needle) # midpoint of [6,6] loc_mid_needles <- 6L # ..."},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-matches-algorithm.html","id":"non-equi-conditions-and-containers","dir":"Reference","previous_headings":"","what":"Non-equi conditions and containers","title":"Internal FAQ - Implementation of vec_locate_matches() — internal-faq-matches-algorithm","text":"point can talk non-equi conditions like < >=. general idea pretty simple, just builds algorithm. example, start x column needles/haystack : used condition <=, everything : Midpoint needles location 3, value x==2 Find lower/upper duplicates needles, giving locations [3, 5] Find lower/upper exact match haystack, giving locations [3, 4] point, need \"adjust\" haystack match bounds account condition. Since haystack ordered, \"rule\" <= keep lower match location , extend upper match location upper bound, end [3, 5]. know can extend upper match location every haystack value exact match less needle. just record matches continue normally. approach really nice, exactly match needle haystack. compare needle every value haystack, take massive amount time. However, gets slightly complex data frames multiple columns. go back original needles haystack data frames apply condition <= column. another worked example, shows case \"rule\" falls apart second column. read example, see rule work . problem haystack ordered (vec_order()s standards), column ordered independently others. Instead, column ordered within \"group\" created previous columns. Concretely, haystack ordered x column, look haystack$y , ordered (1 end). causes rule fail. fix , need create haystack \"containers\" values within container totally ordered. haystack create 2 containers look like: essentially computing_nesting_container_ids() . can actually see ids helper, compute_nesting_container_info(): idea needle, look haystack container find matches, aggregate matches end. df_locate_matches_with_containers() job iterating containers. Computing totally ordered containers can expensive, luckily happen often normal usage. == conditions, need containers (.e. equi join) 1 non-equi condition conditions , need containers (.e. rolling joins) Otherwise typical case need containers something like date >= lower, date <= upper. Even , computation cost generally scales number columns haystack compute containers (2), really slows around 4 columns , ever seen real life example .","code":"needles$x #> [1] 1 1 2 2 2 3 haystack$x #> [1] 1 1 2 2 3 needles #> x y #> 1 1 1 #> 2 1 2 #> 3 2 3 #> 4 2 4 #> 5 2 5 #> 6 3 3 haystack #> x y #> 1 1 2 #> 2 1 3 #> 3 2 4 #> 4 2 4 #> 5 3 1 # `condition = c(\"<=\", \"<=\")` ## Column 1, iteration 1 # x == 2 loc_mid_needles <- 3L loc_lower_duplicate_needles <- 3L loc_upper_duplicate_needles <- 5L # finding exact matches in haystack give us: loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 4L # because haystack is ordered we know we can expand the upper bound automatically # to include everything past the match. i.e. needle of x==2 must be less than # the haystack value at loc 5, which we can check by seeing that it is x==3. loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 5L ## Column 2, iteration 1 # needles range of [3, 5] # y == 4 loc_mid_needles <- 4L loc_lower_duplicate_needles <- 4L loc_upper_duplicate_needles <- 4L # finding exact matches in haystack give us: loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 4L # lets try using our rule, which tells us we should be able to extend the upper # bound: loc_lower_match_haystack <- 3L loc_upper_match_haystack <- 5L # but the haystack value of y at location 5 is y==1, which is not less than y==4 # in the needles! looks like our rule failed us. haystack #> x y #> 1 1 2 #> 2 1 3 #> 3 2 4 #> 4 2 4 #> 5 3 1 haystack[1:4,] #> # A tibble: 4 × 2 #> x y #> #> 1 1 2 #> 2 1 3 #> 3 2 4 #> 4 2 4 haystack[5,] #> # A tibble: 1 × 2 #> x y #> #> 1 3 1 haystack2 <- haystack # we really pass along the integer ranks, but in this case that is equivalent # to converting our double columns to integers haystack2$x <- as.integer(haystack2$x) haystack2$y <- as.integer(haystack2$y) info <- compute_nesting_container_info(haystack2, condition = c(\"<=\", \"<=\")) # the ids are in the second slot. # container ids break haystack into [1, 4] and [5, 5]. info[[2]] #> [1] 0 0 0 0 1"},{"path":[]},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-ptype2-identity.html","id":"promotion-monoid","dir":"Reference","previous_headings":"","what":"Promotion monoid","title":"Internal FAQ - vec_ptype2(), NULL, and unspecified vectors — internal-faq-ptype2-identity","text":"Promotions (.e. automatic coercions) always transform inputs richer type avoid losing values precision. vec_ptype2() returns richer type two vectors, throws incompatible type error none two vector types include . example, richer type integer double latter double covers larger range values integer. vec_ptype2() monoid vectors, practical terms means well behaved operation reduction. Reduction important operation promotions richer type multiple elements computed. monoid, vec_ptype2() needs identity element, .e. value doesn’t change result reduction. vctrs two identity values, NULL unspecified vectors.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-ptype2-identity.html","id":"the-null-identity","dir":"Reference","previous_headings":"","what":"The NULL identity","title":"Internal FAQ - vec_ptype2(), NULL, and unspecified vectors — internal-faq-ptype2-identity","text":"identity element shouldn’t influence determination common type set vectors, NULL promoted type: common type NULL NULL identity NULL: way result vec_ptype2(NULL, NULL) influence subsequent promotions:","code":"vec_ptype2(NULL, \"\") #> character(0) vec_ptype2(1L, NULL) #> integer(0) vec_ptype2(NULL, NULL) #> NULL vec_ptype2( vec_ptype2(NULL, NULL), \"\" ) #> character(0)"},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-ptype2-identity.html","id":"unspecified-vectors","dir":"Reference","previous_headings":"","what":"Unspecified vectors","title":"Internal FAQ - vec_ptype2(), NULL, and unspecified vectors — internal-faq-ptype2-identity","text":"vctrs coercion system, logical vectors missing values also automatically promoted type vector, just like NULL. call vectors unspecified. special coercion semantics unspecified vectors serve two purposes: makes possible assign vectors NA inside type vectors, even coercible logical: can’t put NULL data frame, need identity element behaves like vector. Logical vectors NA seem natural fit . Unspecified vectors thus promoted type, just like NULL:","code":"x <- letters[1:5] vec_assign(x, 1:2, c(NA, NA)) #> [1] NA NA \"c\" \"d\" \"e\" vec_ptype2(NA, \"\") #> character(0) vec_ptype2(1L, c(NA, NA)) #> integer(0)"},{"path":"https://vctrs.r-lib.org/dev/reference/internal-faq-ptype2-identity.html","id":"finalising-common-types","dir":"Reference","previous_headings":"","what":"Finalising common types","title":"Internal FAQ - vec_ptype2(), NULL, and unspecified vectors — internal-faq-ptype2-identity","text":"vctrs internal vector type class vctrs_unspecified. Users normally don’t see vectors wild, come taking common type unspecified vector another identity value: can’t return NA vec_ptype2() normally returns empty vectors. also can’t return NULL unspecified vectors need recognised logical vectors haven’t promoted end reduction. See output vec_ptype_common() performs reduction finalises type, ready used caller: Note partial types vctrs make use mechanism. finalised vec_ptype_finalise().","code":"vec_ptype2(NA, NA) #> [0] vec_ptype2(NA, NULL) #> [0] vec_ptype2(NULL, NA) #> [0] vec_ptype_finalise(vec_ptype2(NULL, NA)) #> logical(0) vec_ptype_common(NULL, NULL) #> NULL vec_ptype_common(NA, NULL) #> logical(0)"},{"path":"https://vctrs.r-lib.org/dev/reference/list_drop_empty.html","id":null,"dir":"Reference","previous_headings":"","what":"Drop empty elements from a list — list_drop_empty","title":"Drop empty elements from a list — list_drop_empty","text":"list_drop_empty() removes empty elements list. includes NULL elements along empty vectors, like integer(0). equivalent , faster , vec_slice(x, list_sizes(x) != 0L).","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_drop_empty.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Drop empty elements from a list — list_drop_empty","text":"","code":"list_drop_empty(x)"},{"path":"https://vctrs.r-lib.org/dev/reference/list_drop_empty.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Drop empty elements from a list — list_drop_empty","text":"x list.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_drop_empty.html","id":"dependencies","dir":"Reference","previous_headings":"","what":"Dependencies","title":"Drop empty elements from a list — list_drop_empty","text":"vec_slice()","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_drop_empty.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Drop empty elements from a list — list_drop_empty","text":"","code":"x <- list(1, NULL, integer(), 2) list_drop_empty(x) #> [[1]] #> [1] 1 #> #> [[2]] #> [1] 2 #>"},{"path":"https://vctrs.r-lib.org/dev/reference/list_of.html","id":null,"dir":"Reference","previous_headings":"","what":"list_of S3 class for homogenous lists — list_of","title":"list_of S3 class for homogenous lists — list_of","text":"list_of object list element type. Modifying list $, [, [[ preserves constraint coercing input items.","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_of.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"list_of S3 class for homogenous lists — list_of","text":"","code":"list_of(..., .ptype = NULL) as_list_of(x, ...) is_list_of(x) # S3 method for vctrs_list_of vec_ptype2(x, y, ..., x_arg = \"\", y_arg = \"\") # S3 method for vctrs_list_of vec_cast(x, to, ...)"},{"path":"https://vctrs.r-lib.org/dev/reference/list_of.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"list_of S3 class for homogenous lists — list_of","text":"... Vectors coerce. .ptype NULL, default, output type determined computing common type across elements .... Alternatively, can supply .ptype give output known type. getOption(\"vctrs.no_guessing\") TRUE must supply value: convenient way make production code demand fixed types. x as_list_of(), vector coerced list_of. y, Arguments vec_ptype2() vec_cast(). x_arg, y_arg Argument names x y. used error messages inform user locations incompatible types (see stop_incompatible_type()).","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_of.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"list_of S3 class for homogenous lists — list_of","text":"Unlike regular lists, setting list element NULL using [[ remove .","code":""},{"path":"https://vctrs.r-lib.org/dev/reference/list_of.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"list_of S3 class for homogenous lists — list_of","text":"","code":"x <- list_of(1:3, 5:6, 10:15) if (requireNamespace(\"tibble\", quietly = TRUE)) { tibble::tibble(x = x) } #> # A tibble: 3 × 1 #> x #>