vctrs/ 0000755 0001762 0000144 00000000000 14532470452 011421 5 ustar ligges users vctrs/NAMESPACE 0000644 0001762 0000144 00000047247 14465453271 012663 0 ustar ligges users # Generated by roxygen2: do not edit by hand
S3method("!",vctrs_vctr)
S3method("!=",vctrs_vctr)
S3method("$",vctrs_list_of)
S3method("$",vctrs_rcrd)
S3method("$",vctrs_sclr)
S3method("$",vctrs_vctr)
S3method("$<-",vctrs_list_of)
S3method("$<-",vctrs_rcrd)
S3method("$<-",vctrs_sclr)
S3method("$<-",vctrs_vctr)
S3method("%%",vctrs_vctr)
S3method("%/%",vctrs_vctr)
S3method("&",vctrs_vctr)
S3method("*",vctrs_vctr)
S3method("+",vctrs_vctr)
S3method("-",vctrs_vctr)
S3method("/",vctrs_vctr)
S3method("<",vctrs_vctr)
S3method("<=",vctrs_vctr)
S3method("==",vctrs_vctr)
S3method(">",vctrs_vctr)
S3method(">=",vctrs_vctr)
S3method("[",vctrs_rcrd)
S3method("[",vctrs_sclr)
S3method("[",vctrs_unspecified)
S3method("[",vctrs_vctr)
S3method("[<-",vctrs_list_of)
S3method("[<-",vctrs_rcrd)
S3method("[<-",vctrs_sclr)
S3method("[<-",vctrs_vctr)
S3method("[[",vctrs_list_of)
S3method("[[",vctrs_rcrd)
S3method("[[",vctrs_sclr)
S3method("[[",vctrs_vctr)
S3method("[[<-",vctrs_list_of)
S3method("[[<-",vctrs_rcrd)
S3method("[[<-",vctrs_sclr)
S3method("[[<-",vctrs_vctr)
S3method("^",vctrs_vctr)
S3method("dim<-",vctrs_sclr)
S3method("dim<-",vctrs_vctr)
S3method("dimnames<-",vctrs_sclr)
S3method("dimnames<-",vctrs_vctr)
S3method("is.na<-",vctrs_sclr)
S3method("is.na<-",vctrs_vctr)
S3method("length<-",vctrs_rcrd)
S3method("length<-",vctrs_vctr)
S3method("levels<-",vctrs_sclr)
S3method("levels<-",vctrs_vctr)
S3method("names<-",vctrs_rcrd)
S3method("names<-",vctrs_sclr)
S3method("names<-",vctrs_vctr)
S3method("|",vctrs_vctr)
S3method(Complex,vctrs_sclr)
S3method(Math,vctrs_sclr)
S3method(Math,vctrs_vctr)
S3method(Ops,vctrs_sclr)
S3method(Summary,vctrs_sclr)
S3method(Summary,vctrs_vctr)
S3method(anyDuplicated,vctrs_sclr)
S3method(anyDuplicated,vctrs_vctr)
S3method(anyNA,vctrs_vctr)
S3method(as.Date,vctrs_sclr)
S3method(as.Date,vctrs_vctr)
S3method(as.POSIXct,vctrs_sclr)
S3method(as.POSIXct,vctrs_vctr)
S3method(as.POSIXlt,vctrs_vctr)
S3method(as.character,vctrs_list_of)
S3method(as.character,vctrs_sclr)
S3method(as.character,vctrs_vctr)
S3method(as.data.frame,vctrs_sclr)
S3method(as.data.frame,vctrs_vctr)
S3method(as.double,vctrs_sclr)
S3method(as.double,vctrs_vctr)
S3method(as.integer,vctrs_sclr)
S3method(as.integer,vctrs_vctr)
S3method(as.list,vctrs_list_of)
S3method(as.list,vctrs_sclr)
S3method(as.list,vctrs_vctr)
S3method(as.logical,vctrs_sclr)
S3method(as.logical,vctrs_vctr)
S3method(as_list_of,list)
S3method(as_list_of,vctrs_list_of)
S3method(c,vctrs_sclr)
S3method(c,vctrs_vctr)
S3method(can_fall_back,"vctrs:::common_class_fallback")
S3method(can_fall_back,data.frame)
S3method(can_fall_back,default)
S3method(can_fall_back,ts)
S3method(can_fall_back,vctrs_vctr)
S3method(cnd_body,vctrs_error_cast_lossy)
S3method(cnd_body,vctrs_error_incompatible_size)
S3method(cnd_body,vctrs_error_matches_incomplete)
S3method(cnd_body,vctrs_error_matches_multiple)
S3method(cnd_body,vctrs_error_matches_nothing)
S3method(cnd_body,vctrs_error_matches_relationship_many_to_one)
S3method(cnd_body,vctrs_error_matches_relationship_one_to_many)
S3method(cnd_body,vctrs_error_matches_relationship_one_to_one)
S3method(cnd_body,vctrs_error_matches_remaining)
S3method(cnd_body,vctrs_error_names_cannot_be_dot_dot)
S3method(cnd_body,vctrs_error_names_cannot_be_empty)
S3method(cnd_body,vctrs_error_names_must_be_unique)
S3method(cnd_body,vctrs_error_subscript_oob)
S3method(cnd_body,vctrs_error_subscript_type)
S3method(cnd_header,vctrs_error_cast_lossy)
S3method(cnd_header,vctrs_error_incompatible_size)
S3method(cnd_header,vctrs_error_matches_incomplete)
S3method(cnd_header,vctrs_error_matches_multiple)
S3method(cnd_header,vctrs_error_matches_nothing)
S3method(cnd_header,vctrs_error_matches_relationship_many_to_one)
S3method(cnd_header,vctrs_error_matches_relationship_one_to_many)
S3method(cnd_header,vctrs_error_matches_relationship_one_to_one)
S3method(cnd_header,vctrs_error_matches_remaining)
S3method(cnd_header,vctrs_error_names_cannot_be_dot_dot)
S3method(cnd_header,vctrs_error_names_cannot_be_empty)
S3method(cnd_header,vctrs_error_names_must_be_unique)
S3method(cnd_header,vctrs_error_subscript_oob)
S3method(cnd_header,vctrs_error_subscript_size)
S3method(cnd_header,vctrs_error_subscript_type)
S3method(diff,vctrs_vctr)
S3method(duplicated,vctrs_sclr)
S3method(duplicated,vctrs_vctr)
S3method(format,vctrs_bytes)
S3method(format,vctrs_group_rle)
S3method(format,vctrs_list_of)
S3method(format,vctrs_rcrd)
S3method(format,vctrs_vctr)
S3method(is.finite,vctrs_vctr)
S3method(is.infinite,vctrs_vctr)
S3method(is.na,vctrs_vctr)
S3method(is.nan,vctrs_vctr)
S3method(length,vctrs_rcrd)
S3method(levels,vctrs_sclr)
S3method(levels,vctrs_vctr)
S3method(max,vctrs_vctr)
S3method(mean,vctrs_vctr)
S3method(median,vctrs_vctr)
S3method(min,vctrs_vctr)
S3method(na.exclude,vctrs_vctr)
S3method(na.fail,vctrs_vctr)
S3method(na.omit,vctrs_vctr)
S3method(names,vctrs_rcrd)
S3method(obj_print_data,default)
S3method(obj_print_data,vctrs_list_of)
S3method(obj_print_data,vctrs_partial)
S3method(obj_print_footer,default)
S3method(obj_print_header,default)
S3method(obj_print_header,vctrs_group_rle)
S3method(obj_print_header,vctrs_partial)
S3method(obj_str_data,default)
S3method(obj_str_data,vctrs_rcrd)
S3method(obj_str_footer,default)
S3method(obj_str_header,default)
S3method(print,vctrs_bytes)
S3method(print,vctrs_sclr)
S3method(print,vctrs_unspecified)
S3method(print,vctrs_vctr)
S3method(quantile,vctrs_vctr)
S3method(range,vctrs_vctr)
S3method(rep,vctrs_rcrd)
S3method(rep,vctrs_vctr)
S3method(str,vctrs_vctr)
S3method(summary,vctrs_sclr)
S3method(summary,vctrs_vctr)
S3method(t,vctrs_sclr)
S3method(t,vctrs_vctr)
S3method(unique,vctrs_sclr)
S3method(unique,vctrs_vctr)
S3method(vec_arith,Date)
S3method(vec_arith,POSIXct)
S3method(vec_arith,POSIXlt)
S3method(vec_arith,default)
S3method(vec_arith,difftime)
S3method(vec_arith,factor)
S3method(vec_arith,logical)
S3method(vec_arith,numeric)
S3method(vec_arith.Date,Date)
S3method(vec_arith.Date,POSIXct)
S3method(vec_arith.Date,POSIXlt)
S3method(vec_arith.Date,default)
S3method(vec_arith.Date,difftime)
S3method(vec_arith.Date,numeric)
S3method(vec_arith.POSIXct,Date)
S3method(vec_arith.POSIXct,POSIXct)
S3method(vec_arith.POSIXct,POSIXlt)
S3method(vec_arith.POSIXct,default)
S3method(vec_arith.POSIXct,difftime)
S3method(vec_arith.POSIXct,numeric)
S3method(vec_arith.POSIXlt,Date)
S3method(vec_arith.POSIXlt,POSIXct)
S3method(vec_arith.POSIXlt,POSIXlt)
S3method(vec_arith.POSIXlt,default)
S3method(vec_arith.POSIXlt,difftime)
S3method(vec_arith.POSIXlt,numeric)
S3method(vec_arith.difftime,Date)
S3method(vec_arith.difftime,MISSING)
S3method(vec_arith.difftime,POSIXct)
S3method(vec_arith.difftime,POSIXlt)
S3method(vec_arith.difftime,default)
S3method(vec_arith.difftime,difftime)
S3method(vec_arith.difftime,numeric)
S3method(vec_arith.logical,default)
S3method(vec_arith.logical,logical)
S3method(vec_arith.logical,numeric)
S3method(vec_arith.numeric,Date)
S3method(vec_arith.numeric,POSIXct)
S3method(vec_arith.numeric,POSIXlt)
S3method(vec_arith.numeric,default)
S3method(vec_arith.numeric,difftime)
S3method(vec_arith.numeric,logical)
S3method(vec_arith.numeric,numeric)
S3method(vec_cast,Date)
S3method(vec_cast,POSIXct)
S3method(vec_cast,POSIXlt)
S3method(vec_cast,character)
S3method(vec_cast,character.factor)
S3method(vec_cast,character.ordered)
S3method(vec_cast,complex)
S3method(vec_cast,data.frame)
S3method(vec_cast,data.frame.data.table)
S3method(vec_cast,data.table.data.frame)
S3method(vec_cast,data.table.data.table)
S3method(vec_cast,difftime)
S3method(vec_cast,double)
S3method(vec_cast,double.exclude)
S3method(vec_cast,double.omit)
S3method(vec_cast,exclude.double)
S3method(vec_cast,exclude.exclude)
S3method(vec_cast,exclude.integer)
S3method(vec_cast,factor)
S3method(vec_cast,factor.character)
S3method(vec_cast,factor.factor)
S3method(vec_cast,integer)
S3method(vec_cast,integer.exclude)
S3method(vec_cast,integer.omit)
S3method(vec_cast,integer64)
S3method(vec_cast,list)
S3method(vec_cast,list.vctrs_list_of)
S3method(vec_cast,logical)
S3method(vec_cast,omit.double)
S3method(vec_cast,omit.integer)
S3method(vec_cast,omit.omit)
S3method(vec_cast,ordered)
S3method(vec_cast,ordered.character)
S3method(vec_cast,ordered.ordered)
S3method(vec_cast,raw)
S3method(vec_cast,table.table)
S3method(vec_cast,vctrs_list_of)
S3method(vec_cast,vctrs_list_of.list)
S3method(vec_cast,vctrs_rcrd)
S3method(vec_cast,vctrs_rcrd.vctrs_rcrd)
S3method(vec_cast,vctrs_vctr)
S3method(vec_cast.Date,Date)
S3method(vec_cast.Date,POSIXct)
S3method(vec_cast.Date,POSIXlt)
S3method(vec_cast.POSIXct,Date)
S3method(vec_cast.POSIXct,POSIXct)
S3method(vec_cast.POSIXct,POSIXlt)
S3method(vec_cast.POSIXlt,Date)
S3method(vec_cast.POSIXlt,POSIXct)
S3method(vec_cast.POSIXlt,POSIXlt)
S3method(vec_cast.character,character)
S3method(vec_cast.complex,complex)
S3method(vec_cast.complex,double)
S3method(vec_cast.complex,integer)
S3method(vec_cast.complex,logical)
S3method(vec_cast.data.frame,data.frame)
S3method(vec_cast.difftime,difftime)
S3method(vec_cast.double,double)
S3method(vec_cast.double,integer)
S3method(vec_cast.double,integer64)
S3method(vec_cast.double,logical)
S3method(vec_cast.integer,double)
S3method(vec_cast.integer,integer)
S3method(vec_cast.integer,integer64)
S3method(vec_cast.integer,logical)
S3method(vec_cast.integer64,double)
S3method(vec_cast.integer64,integer)
S3method(vec_cast.integer64,integer64)
S3method(vec_cast.integer64,logical)
S3method(vec_cast.list,list)
S3method(vec_cast.logical,double)
S3method(vec_cast.logical,integer)
S3method(vec_cast.logical,integer64)
S3method(vec_cast.logical,logical)
S3method(vec_cast.raw,raw)
S3method(vec_cast.vctrs_list_of,vctrs_list_of)
S3method(vec_cbind_frame_ptype,default)
S3method(vec_cbind_frame_ptype,sf)
S3method(vec_math,Date)
S3method(vec_math,POSIXct)
S3method(vec_math,POSIXlt)
S3method(vec_math,default)
S3method(vec_math,factor)
S3method(vec_math,vctrs_rcrd)
S3method(vec_proxy,"vctrs:::common_class_fallback")
S3method(vec_proxy,AsIs)
S3method(vec_proxy,Date)
S3method(vec_proxy,POSIXct)
S3method(vec_proxy,POSIXlt)
S3method(vec_proxy,default)
S3method(vec_proxy,exclude)
S3method(vec_proxy,factor)
S3method(vec_proxy,numeric_version)
S3method(vec_proxy,omit)
S3method(vec_proxy,ordered)
S3method(vec_proxy,vctrs_list_of)
S3method(vec_proxy,vctrs_rcrd)
S3method(vec_proxy,vctrs_vctr)
S3method(vec_proxy_compare,AsIs)
S3method(vec_proxy_compare,POSIXlt)
S3method(vec_proxy_compare,array)
S3method(vec_proxy_compare,default)
S3method(vec_proxy_compare,list)
S3method(vec_proxy_compare,raw)
S3method(vec_proxy_equal,AsIs)
S3method(vec_proxy_equal,POSIXlt)
S3method(vec_proxy_equal,array)
S3method(vec_proxy_equal,default)
S3method(vec_proxy_equal,integer64)
S3method(vec_proxy_equal,numeric_version)
S3method(vec_proxy_order,AsIs)
S3method(vec_proxy_order,array)
S3method(vec_proxy_order,default)
S3method(vec_proxy_order,list)
S3method(vec_proxy_order,raw)
S3method(vec_ptype2,AsIs)
S3method(vec_ptype2,Date)
S3method(vec_ptype2,POSIXct)
S3method(vec_ptype2,POSIXlt)
S3method(vec_ptype2,character)
S3method(vec_ptype2,character.factor)
S3method(vec_ptype2,character.ordered)
S3method(vec_ptype2,complex)
S3method(vec_ptype2,data.frame)
S3method(vec_ptype2,data.frame.data.table)
S3method(vec_ptype2,data.table.data.frame)
S3method(vec_ptype2,data.table.data.table)
S3method(vec_ptype2,difftime)
S3method(vec_ptype2,double)
S3method(vec_ptype2,double.exclude)
S3method(vec_ptype2,double.omit)
S3method(vec_ptype2,exclude.double)
S3method(vec_ptype2,exclude.exclude)
S3method(vec_ptype2,exclude.integer)
S3method(vec_ptype2,factor)
S3method(vec_ptype2,factor.character)
S3method(vec_ptype2,factor.factor)
S3method(vec_ptype2,factor.ordered)
S3method(vec_ptype2,integer)
S3method(vec_ptype2,integer.exclude)
S3method(vec_ptype2,integer.omit)
S3method(vec_ptype2,integer64)
S3method(vec_ptype2,list)
S3method(vec_ptype2,list.vctrs_list_of)
S3method(vec_ptype2,logical)
S3method(vec_ptype2,omit.double)
S3method(vec_ptype2,omit.integer)
S3method(vec_ptype2,omit.omit)
S3method(vec_ptype2,ordered)
S3method(vec_ptype2,ordered.character)
S3method(vec_ptype2,ordered.factor)
S3method(vec_ptype2,ordered.ordered)
S3method(vec_ptype2,raw)
S3method(vec_ptype2,table.table)
S3method(vec_ptype2,vctrs_list_of)
S3method(vec_ptype2,vctrs_list_of.list)
S3method(vec_ptype2,vctrs_partial_factor)
S3method(vec_ptype2,vctrs_partial_frame)
S3method(vec_ptype2.AsIs,AsIs)
S3method(vec_ptype2.Date,Date)
S3method(vec_ptype2.Date,POSIXct)
S3method(vec_ptype2.Date,POSIXlt)
S3method(vec_ptype2.POSIXct,Date)
S3method(vec_ptype2.POSIXct,POSIXct)
S3method(vec_ptype2.POSIXct,POSIXlt)
S3method(vec_ptype2.POSIXlt,Date)
S3method(vec_ptype2.POSIXlt,POSIXct)
S3method(vec_ptype2.POSIXlt,POSIXlt)
S3method(vec_ptype2.character,character)
S3method(vec_ptype2.complex,complex)
S3method(vec_ptype2.complex,double)
S3method(vec_ptype2.complex,integer)
S3method(vec_ptype2.data.frame,data.frame)
S3method(vec_ptype2.data.frame,vctrs_partial_frame)
S3method(vec_ptype2.difftime,difftime)
S3method(vec_ptype2.double,complex)
S3method(vec_ptype2.double,double)
S3method(vec_ptype2.double,integer)
S3method(vec_ptype2.double,logical)
S3method(vec_ptype2.factor,vctrs_partial_factor)
S3method(vec_ptype2.integer,complex)
S3method(vec_ptype2.integer,double)
S3method(vec_ptype2.integer,integer)
S3method(vec_ptype2.integer,integer64)
S3method(vec_ptype2.integer,logical)
S3method(vec_ptype2.integer64,integer)
S3method(vec_ptype2.integer64,integer64)
S3method(vec_ptype2.integer64,logical)
S3method(vec_ptype2.list,list)
S3method(vec_ptype2.logical,double)
S3method(vec_ptype2.logical,integer)
S3method(vec_ptype2.logical,integer64)
S3method(vec_ptype2.logical,logical)
S3method(vec_ptype2.raw,raw)
S3method(vec_ptype2.vctrs_list_of,vctrs_list_of)
S3method(vec_ptype2.vctrs_partial_factor,factor)
S3method(vec_ptype2.vctrs_partial_factor,vctrs_partial_factor)
S3method(vec_ptype2.vctrs_partial_frame,data.frame)
S3method(vec_ptype2.vctrs_partial_frame,vctrs_partial_frame)
S3method(vec_ptype_abbr,"NULL")
S3method(vec_ptype_abbr,AsIs)
S3method(vec_ptype_abbr,Date)
S3method(vec_ptype_abbr,POSIXct)
S3method(vec_ptype_abbr,POSIXlt)
S3method(vec_ptype_abbr,data.frame)
S3method(vec_ptype_abbr,data.table)
S3method(vec_ptype_abbr,default)
S3method(vec_ptype_abbr,difftime)
S3method(vec_ptype_abbr,factor)
S3method(vec_ptype_abbr,integer64)
S3method(vec_ptype_abbr,ordered)
S3method(vec_ptype_abbr,table)
S3method(vec_ptype_abbr,vctrs_list_of)
S3method(vec_ptype_abbr,vctrs_partial_factor)
S3method(vec_ptype_abbr,vctrs_partial_frame)
S3method(vec_ptype_abbr,vctrs_unspecified)
S3method(vec_ptype_finalise,default)
S3method(vec_ptype_finalise,vctrs_partial)
S3method(vec_ptype_finalise,vctrs_partial_factor)
S3method(vec_ptype_finalise,vctrs_partial_frame)
S3method(vec_ptype_full,"NULL")
S3method(vec_ptype_full,AsIs)
S3method(vec_ptype_full,Date)
S3method(vec_ptype_full,POSIXct)
S3method(vec_ptype_full,POSIXlt)
S3method(vec_ptype_full,default)
S3method(vec_ptype_full,difftime)
S3method(vec_ptype_full,factor)
S3method(vec_ptype_full,integer64)
S3method(vec_ptype_full,ordered)
S3method(vec_ptype_full,table)
S3method(vec_ptype_full,vctrs_list_of)
S3method(vec_ptype_full,vctrs_partial_factor)
S3method(vec_ptype_full,vctrs_partial_frame)
S3method(vec_restore,AsIs)
S3method(vec_restore,Date)
S3method(vec_restore,POSIXct)
S3method(vec_restore,POSIXlt)
S3method(vec_restore,data.frame)
S3method(vec_restore,default)
S3method(vec_restore,exclude)
S3method(vec_restore,factor)
S3method(vec_restore,omit)
S3method(vec_restore,ordered)
S3method(vec_restore,table)
S3method(vec_restore,vctrs_rcrd)
S3method(vec_restore,vctrs_vctr)
S3method(xtfrm,vctrs_sclr)
S3method(xtfrm,vctrs_vctr)
export("%0%")
export("field<-")
export("vec_slice<-")
export(MISSING)
export(allow_lossy_cast)
export(as_list_of)
export(data_frame)
export(df_cast)
export(df_list)
export(df_ptype2)
export(field)
export(fields)
export(is_list_of)
export(is_partial)
export(list_all_size)
export(list_all_vectors)
export(list_check_all_size)
export(list_check_all_vectors)
export(list_drop_empty)
export(list_of)
export(list_sizes)
export(list_unchop)
export(maybe_lossy_cast)
export(n_fields)
export(new_data_frame)
export(new_date)
export(new_datetime)
export(new_duration)
export(new_factor)
export(new_list_of)
export(new_ordered)
export(new_partial)
export(new_rcrd)
export(new_vctr)
export(num_as_location)
export(num_as_location2)
export(obj_check_list)
export(obj_check_vector)
export(obj_is_list)
export(obj_is_vector)
export(obj_print)
export(obj_print_data)
export(obj_print_footer)
export(obj_print_header)
export(obj_str)
export(obj_str_data)
export(obj_str_footer)
export(obj_str_header)
export(partial_factor)
export(partial_frame)
export(s3_register)
export(stop_incompatible_cast)
export(stop_incompatible_op)
export(stop_incompatible_size)
export(stop_incompatible_type)
export(tib_cast)
export(tib_ptype2)
export(unspecified)
export(vec_any_missing)
export(vec_arith)
export(vec_arith.Date)
export(vec_arith.POSIXct)
export(vec_arith.POSIXlt)
export(vec_arith.difftime)
export(vec_arith.logical)
export(vec_arith.numeric)
export(vec_arith_base)
export(vec_as_index)
export(vec_as_location)
export(vec_as_location2)
export(vec_as_names)
export(vec_as_names_legacy)
export(vec_as_subscript)
export(vec_as_subscript2)
export(vec_assert)
export(vec_assign)
export(vec_c)
export(vec_cast)
export(vec_cast.Date)
export(vec_cast.POSIXct)
export(vec_cast.POSIXlt)
export(vec_cast.character)
export(vec_cast.complex)
export(vec_cast.data.frame)
export(vec_cast.difftime)
export(vec_cast.double)
export(vec_cast.factor)
export(vec_cast.integer)
export(vec_cast.integer64)
export(vec_cast.list)
export(vec_cast.logical)
export(vec_cast.ordered)
export(vec_cast.raw)
export(vec_cast.vctrs_list_of)
export(vec_cast_common)
export(vec_cbind)
export(vec_cbind_frame_ptype)
export(vec_check_list)
export(vec_check_size)
export(vec_chop)
export(vec_compare)
export(vec_count)
export(vec_data)
export(vec_default_cast)
export(vec_default_ptype2)
export(vec_detect_complete)
export(vec_detect_missing)
export(vec_duplicate_any)
export(vec_duplicate_detect)
export(vec_duplicate_id)
export(vec_empty)
export(vec_equal)
export(vec_equal_na)
export(vec_expand_grid)
export(vec_fill_missing)
export(vec_group_id)
export(vec_group_loc)
export(vec_group_rle)
export(vec_identify_runs)
export(vec_in)
export(vec_init)
export(vec_init_along)
export(vec_interleave)
export(vec_is)
export(vec_is_empty)
export(vec_is_list)
export(vec_locate_matches)
export(vec_locate_sorted_groups)
export(vec_match)
export(vec_math)
export(vec_math_base)
export(vec_names)
export(vec_names2)
export(vec_order)
export(vec_proxy)
export(vec_proxy_compare)
export(vec_proxy_equal)
export(vec_proxy_order)
export(vec_ptype)
export(vec_ptype2)
export(vec_ptype2.AsIs)
export(vec_ptype2.Date)
export(vec_ptype2.POSIXct)
export(vec_ptype2.POSIXlt)
export(vec_ptype2.character)
export(vec_ptype2.complex)
export(vec_ptype2.data.frame)
export(vec_ptype2.difftime)
export(vec_ptype2.double)
export(vec_ptype2.factor)
export(vec_ptype2.integer)
export(vec_ptype2.integer64)
export(vec_ptype2.list)
export(vec_ptype2.logical)
export(vec_ptype2.ordered)
export(vec_ptype2.raw)
export(vec_ptype2.vctrs_list_of)
export(vec_ptype_abbr)
export(vec_ptype_common)
export(vec_ptype_finalise)
export(vec_ptype_full)
export(vec_ptype_show)
export(vec_rank)
export(vec_rbind)
export(vec_recycle)
export(vec_recycle_common)
export(vec_rep)
export(vec_rep_each)
export(vec_repeat)
export(vec_restore)
export(vec_run_sizes)
export(vec_seq_along)
export(vec_set_difference)
export(vec_set_intersect)
export(vec_set_names)
export(vec_set_symmetric_difference)
export(vec_set_union)
export(vec_size)
export(vec_size_common)
export(vec_slice)
export(vec_sort)
export(vec_split)
export(vec_type)
export(vec_type2)
export(vec_type_common)
export(vec_unchop)
export(vec_unique)
export(vec_unique_count)
export(vec_unique_loc)
export(vec_unrep)
import(rlang)
importFrom(stats,median)
importFrom(stats,na.exclude)
importFrom(stats,na.fail)
importFrom(stats,na.omit)
importFrom(stats,quantile)
useDynLib(vctrs, .registration = TRUE)
vctrs/LICENSE.note 0000644 0001762 0000144 00000041367 14276722575 013420 0 ustar ligges users The implementation of vec_order() is based on data.table’s forder() and their
earlier contribution to R’s order(). This warrants placing specific files in
the vctrs package under the MPL-2.0 license used by data.table. Files named with
the pattern of `src/order-*.c` and `src/order-*.h` are additionally under the
MPL-2.0 license.
MPL-2.0 License ----------------------------------------------------------------
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
vctrs/LICENSE 0000644 0001762 0000144 00000000053 14401442234 012414 0 ustar ligges users YEAR: 2023
COPYRIGHT HOLDER: vctrs authors
vctrs/README.md 0000644 0001762 0000144 00000007441 14532374113 012703 0 ustar ligges users
# vctrs
[](https://app.codecov.io/gh/r-lib/vctrs?branch=main)

[](https://github.com/r-lib/vctrs/actions/workflows/R-CMD-check.yaml)
There are three main goals to the vctrs package, each described in a
vignette:
- To propose `vec_size()` and `vec_ptype()` as alternatives to
`length()` and `class()`; `vignette("type-size")`. These definitions
are paired with a framework for size-recycling and type-coercion.
`ptype` should evoke the notion of a prototype, i.e. the original or
typical form of something.
- To define size- and type-stability as desirable function properties,
use them to analyse existing base functions, and to propose better
alternatives; `vignette("stability")`. This work has been
particularly motivated by thinking about the ideal properties of
`c()`, `ifelse()`, and `rbind()`.
- To provide a new `vctr` base class that makes it easy to create new
S3 vectors; `vignette("s3-vector")`. vctrs provides methods for many
base generics in terms of a few new vctrs generics, making
implementation considerably simpler and more robust.
vctrs is a developer-focussed package. Understanding and extending vctrs
requires some effort from developers, but should be invisible to most
users. It’s our hope that having an underlying theory will mean that
users can build up an accurate mental model without explicitly learning
the theory. vctrs will typically be used by other packages, making it
easy for them to provide new classes of S3 vectors that are supported
throughout the tidyverse (and beyond). For that reason, vctrs has few
dependencies.
## Installation
Install vctrs from CRAN with:
``` r
install.packages("vctrs")
```
Alternatively, if you need the development version, install it with:
``` r
# install.packages("pak")
pak::pak("r-lib/vctrs")
```
## Usage
``` r
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
```
## Motivation
The original motivation for vctrs comes from two separate but related
problems. The first problem is that `base::c()` has rather undesirable
behaviour when you mix different S3 vectors:
``` r
# 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"
```
This behaviour arises because `c()` has dual purposes: as well as its
primary duty of combining vectors, it has a secondary duty of stripping
attributes. For example, `?POSIXct` suggests that you should use `c()`
if you want to reset the timezone.
The second problem is that `dplyr::bind_rows()` is not extensible by
others. Currently, it handles arbitrary S3 classes using heuristics, but
these often fail, and it feels like we really need to think through the
problem in order to build a principled solution. This intersects with
the need to cleanly support more types of data frame columns, including
lists of data frames, data frames, and matrices.
vctrs/man/ 0000755 0001762 0000144 00000000000 14532404540 012167 5 ustar ligges users vctrs/man/vctrs-data-frame.Rd 0000644 0001762 0000144 00000000757 14276722575 015647 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-data-frame.R
\name{vctrs-data-frame}
\alias{vctrs-data-frame}
\alias{vec_ptype2.data.frame}
\alias{vec_cast.data.frame}
\title{vctrs methods for data frames}
\usage{
\method{vec_ptype2}{data.frame}(x, y, ...)
\method{vec_cast}{data.frame}(x, to, ...)
}
\description{
These functions help the base data.frame class fit into the vctrs type system
by providing coercion and casting functions.
}
\keyword{internal}
vctrs/man/theory-faq-coercion.Rd 0000644 0001762 0000144 00000027131 14511320527 016337 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-developer.R
\name{theory-faq-coercion}
\alias{theory-faq-coercion}
\title{FAQ - How does coercion work in vctrs?}
\description{
This is an overview of the usage of \code{vec_ptype2()} and \code{vec_cast()} and
their role in the vctrs coercion mechanism. Related topics:
\itemize{
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
\item For a tutorial about implementing vctrs classes from scratch, see
\code{vignette("s3-vector")}.
}
\subsection{Combination mechanism in vctrs}{
The coercion system in vctrs is designed to make combination of multiple
inputs consistent and extensible. Combinations occur in many places,
such as row-binding, joins, subset-assignment, or grouped summary
functions that use the split-apply-combine strategy. For example:
\if{html}{\out{
}}\preformatted{vec_c(TRUE, 1)
#> [1] 1 1
vec_c("a", 1)
#> Error in `vec_c()`:
#> ! Can't combine `..1` and `..2` .
vec_rbind(
data.frame(x = TRUE),
data.frame(x = 1, y = 2)
)
#> x y
#> 1 1 NA
#> 2 1 2
vec_rbind(
data.frame(x = "a"),
data.frame(x = 1, y = 2)
)
#> Error in `vec_rbind()`:
#> ! Can't combine `..1$x` and `..2$x` .
}\if{html}{\out{
}}
One major goal of vctrs is to provide a central place for implementing
the coercion methods that make generic combinations possible. The two
relevant generics are \code{vec_ptype2()} and \code{vec_cast()}. They both take
two arguments and perform \strong{double dispatch}, meaning that a method is
selected based on the classes of both inputs.
The general mechanism for combining multiple inputs is:
\enumerate{
\item Find the common type of a set of inputs by reducing (as in
\code{base::Reduce()} or \code{purrr::reduce()}) the \code{vec_ptype2()} binary
function over the set.
\item Convert all inputs to the common type with \code{vec_cast()}.
\item Initialise the output vector as an instance of this common type with
\code{vec_init()}.
\item Fill the output vector with the elements of the inputs using
\code{vec_assign()}.
}
The last two steps may require \code{vec_proxy()} and \code{vec_restore()}
implementations, unless the attributes of your class are constant and do
not depend on the contents of the vector. We focus here on the first two
steps, which require \code{vec_ptype2()} and \code{vec_cast()} implementations.
}
\subsection{\code{vec_ptype2()}}{
Methods for \code{vec_ptype2()} are passed two \emph{prototypes}, i.e. two inputs
emptied of their elements. They implement two behaviours:
\itemize{
\item If the types of their inputs are compatible, indicate which of them is
the richer type by returning it. If the types are of equal resolution,
return any of the two.
\item Throw an error with \code{stop_incompatible_type()} when it can be
determined from the attributes that the types of the inputs are not
compatible.
}
\subsection{Type compatibility}{
A type is \strong{compatible} with another type if the values it represents
are a subset or a superset of the values of the other type. The notion
of “value” is to be interpreted at a high level, in particular it is not
the same as the memory representation. For example, factors are
represented in memory with integers but their values are more related to
character vectors than to round numbers:
\if{html}{\out{}}\preformatted{# Two factors are compatible
vec_ptype2(factor("a"), factor("b"))
#> factor()
#> Levels: a b
# Factors are compatible with a character
vec_ptype2(factor("a"), "b")
#> character(0)
# But they are incompatible with integers
vec_ptype2(factor("a"), 1L)
#> Error:
#> ! Can't combine `factor("a")` > and `1L` .
}\if{html}{\out{
}}
}
\subsection{Richness of type}{
Richness of type is not a very precise notion. It can be about richer
data (for instance a \code{double} vector covers more values than an integer
vector), richer behaviour (a \code{data.table} has richer behaviour than a
\code{data.frame}), or both. If you have trouble determining which one of the
two types is richer, it probably means they shouldn’t be automatically
coercible.
Let’s look again at what happens when we combine a factor and a
character:
\if{html}{\out{}}\preformatted{vec_ptype2(factor("a"), "b")
#> character(0)
}\if{html}{\out{
}}
The ptype2 method for \verb{} and \verb{>} returns
\verb{} because the former is a richer type. The factor can only
contain \code{"a"} strings, whereas the character can contain any strings. In
this sense, factors are a \emph{subset} of character.
Note that another valid behaviour would be to throw an incompatible type
error. This is what a strict factor implementation would do. We have
decided to be laxer in vctrs because it is easy to inadvertently create
factors instead of character vectors, especially with older versions of
R where \code{stringsAsFactors} is still true by default.
}
\subsection{Consistency and symmetry on permutation}{
Each ptype2 method should strive to have exactly the same behaviour when
the inputs are permuted. This is not always possible, for example factor
levels are aggregated in order:
\if{html}{\out{}}\preformatted{vec_ptype2(factor(c("a", "c")), factor("b"))
#> factor()
#> Levels: a c b
vec_ptype2(factor("b"), factor(c("a", "c")))
#> factor()
#> Levels: b a c
}\if{html}{\out{
}}
In any case, permuting the input should not return a fundamentally
different type or introduce an incompatible type error.
}
\subsection{Coercion hierarchy}{
The classes that you can coerce together form a coercion (or subtyping)
hierarchy. Below is a schema of the hierarchy for the base types like
integer and factor. In this diagram the directions of the arrows express
which type is richer. They flow from the bottom (more constrained types)
to the top (richer types).
\figure{coerce.png}
A coercion hierarchy is distinct from the structural hierarchy implied
by memory types and classes. For instance, in a structural hierarchy,
factors are built on top of integers. But in the coercion hierarchy they
are more related to character vectors. Similarly, subclasses are not
necessarily coercible with their superclasses because the coercion and
structural hierarchies are separate.
}
\subsection{Implementing a coercion hierarchy}{
As a class implementor, you have two options. The simplest is to create
an entirely separate hierarchy. The date and date-time classes are an
example of an S3-based hierarchy that is completely separate.
Alternatively, you can integrate your class in an existing hierarchy,
typically by adding parent nodes on top of the hierarchy (your class is
richer), by adding children node at the root of the hierarchy (your
class is more constrained), or by inserting a node in the tree.
These coercion hierarchies are \emph{implicit}, in the sense that they are
implied by the \code{vec_ptype2()} implementations. There is no structured
way to create or modify a hierarchy, instead you need to implement the
appropriate coercion methods for all the types in your hierarchy, and
diligently return the richer type in each case. The \code{vec_ptype2()}
implementations are not transitive nor inherited, so all pairwise
methods between classes lying on a given path must be implemented
manually. This is something we might make easier in the future.
}
}
\subsection{\code{vec_cast()}}{
The second generic, \code{vec_cast()}, is the one that looks at the data and
actually performs the conversion. Because it has access to more
information than \code{vec_ptype2()}, it may be stricter and cause an error
in more cases. \code{vec_cast()} has three possible behaviours:
\itemize{
\item Determine that the prototypes of the two inputs are not compatible.
This must be decided in exactly the same way as for \code{vec_ptype2()}.
Call \code{stop_incompatible_cast()} if you can determine from the
attributes that the types are not compatible.
\item Detect incompatible values. Usually this is because the target type is
too restricted for the values supported by the input type. For
example, a fractional number can’t be converted to an integer. The
method should throw an error in that case.
\item Return the input vector converted to the target type if all values are
compatible. Whereas \code{vec_ptype2()} must return the same type when the
inputs are permuted, \code{vec_cast()} is \emph{directional}. It always returns
the type of the right-hand side, or dies trying.
}
}
\subsection{Double dispatch}{
The dispatch mechanism for \code{vec_ptype2()} and \code{vec_cast()} looks like S3
but is actually a custom mechanism. Compared to S3, it has the following
differences:
\itemize{
\item It dispatches on the classes of the first two inputs.
\item There is no inheritance of ptype2 and cast methods. This is because
the S3 class hierarchy is not necessarily the same as the coercion
hierarchy.
\item \code{NextMethod()} does not work. Parent methods must be called explicitly
if necessary.
\item The default method is hard-coded.
}
}
\subsection{Data frames}{
The determination of the common type of data frames with \code{vec_ptype2()}
happens in three steps:
\enumerate{
\item Match the columns of the two input data frames. If some columns
don’t exist, they are created and filled with adequately typed \code{NA}
values.
\item Find the common type for each column by calling \code{vec_ptype2()} on
each pair of matched columns.
\item Find the common data frame type. For example the common type of a
grouped tibble and a tibble is a grouped tibble because the latter
is the richer type. The common type of a data table and a data frame
is a data table.
}
\code{vec_cast()} operates similarly. If a data frame is cast to a target
type that has fewer columns, this is an error.
If you are implementing coercion methods for data frames, you will need
to explicitly call the parent methods that perform the common type
determination or the type conversion described above. These are exported
as \code{\link[=df_ptype2]{df_ptype2()}} and \code{\link[=df_cast]{df_cast()}}.
\subsection{Data frame fallbacks}{
Being too strict with data frame combinations would cause too much pain
because there are many data frame subclasses in the wild that don’t
implement vctrs methods. We have decided to implement a special fallback
behaviour for foreign data frames. Incompatible data frames fall back to
a base data frame:
\if{html}{\out{}}\preformatted{df1 <- data.frame(x = 1)
df2 <- structure(df1, class = c("foreign_df", "data.frame"))
vec_rbind(df1, df2)
#> x
#> 1 1
#> 2 1
}\if{html}{\out{
}}
When a tibble is involved, we fall back to tibble:
\if{html}{\out{}}\preformatted{df3 <- tibble::as_tibble(df1)
vec_rbind(df1, df3)
#> # A tibble: 2 x 1
#> x
#>
#> 1 1
#> 2 1
}\if{html}{\out{
}}
These fallbacks are not ideal but they make sense because all data
frames share a common data structure. This is not generally the case for
vectors. For example factors and characters have different
representations, and it is not possible to find a fallback time
mechanically.
However this fallback has a big downside: implementing vctrs methods for
your data frame subclass is a breaking behaviour change. The proper
coercion behaviour for your data frame class should be specified as soon
as possible to limit the consequences of changing the behaviour of your
class in R scripts.
}
}
}
vctrs/man/vec_duplicate.Rd 0000644 0001762 0000144 00000004131 14276722575 015304 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/dictionary.R
\name{vec_duplicate}
\alias{vec_duplicate}
\alias{vec_duplicate_any}
\alias{vec_duplicate_detect}
\alias{vec_duplicate_id}
\title{Find duplicated values}
\usage{
vec_duplicate_any(x)
vec_duplicate_detect(x)
vec_duplicate_id(x)
}
\arguments{
\item{x}{A vector (including a data frame).}
}
\value{
\itemize{
\item \code{vec_duplicate_any()}: a logical vector of length 1.
\item \code{vec_duplicate_detect()}: a logical vector the same length as \code{x}.
\item \code{vec_duplicate_id()}: an integer vector the same length as \code{x}.
}
}
\description{
\itemize{
\item \code{vec_duplicate_any()}: detects the presence of duplicated values,
similar to \code{\link[=anyDuplicated]{anyDuplicated()}}.
\item \code{vec_duplicate_detect()}: returns a logical vector describing if each
element of the vector is duplicated elsewhere. Unlike \code{\link[=duplicated]{duplicated()}}, it
reports all duplicated values, not just the second and subsequent
repetitions.
\item \code{vec_duplicate_id()}: returns an integer vector giving the location of
the first occurrence of the value.
}
}
\section{Missing values}{
In most cases, missing values are not considered to be equal, i.e.
\code{NA == NA} is not \code{TRUE}. This behaviour would be unappealing here,
so these functions consider all \code{NAs} to be equal. (Similarly,
all \code{NaN} are also considered to be equal.)
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
}
}
\examples{
vec_duplicate_any(1:10)
vec_duplicate_any(c(1, 1:10))
x <- c(10, 10, 20, 30, 30, 40)
vec_duplicate_detect(x)
# Note that `duplicated()` doesn't consider the first instance to
# be a duplicate
duplicated(x)
# Identify elements of a vector by the location of the first element that
# they're equal to:
vec_duplicate_id(x)
# Location of the unique values:
vec_unique_loc(x)
# Equivalent to `duplicated()`:
vec_duplicate_id(x) == seq_along(x)
}
\seealso{
\code{\link[=vec_unique]{vec_unique()}} for functions that work with the dual of duplicated
values: unique values.
}
vctrs/man/faq-error-incompatible-attributes.Rd 0000644 0001762 0000144 00000002337 14276722575 021231 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq.R
\name{faq-error-incompatible-attributes}
\alias{faq-error-incompatible-attributes}
\title{FAQ - Error/Warning: Some attributes are incompatible}
\description{
This error occurs when \code{\link[=vec_ptype2]{vec_ptype2()}} or \code{\link[=vec_cast]{vec_cast()}} are supplied
vectors of the same classes with different attributes. In this
case, vctrs doesn't know how to combine the inputs.
To fix this error, the maintainer of the class should implement
self-to-self coercion methods for \code{\link[=vec_ptype2]{vec_ptype2()}} and \code{\link[=vec_cast]{vec_cast()}}.
}
\section{Implementing coercion methods}{
\itemize{
\item For an overview of how these generics work and their roles in vctrs,
see \code{\link[=theory-faq-coercion]{?theory-faq-coercion}}.
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
\item For a tutorial about implementing vctrs classes from scratch, see
\code{vignette("s3-vector")}.
}
}
vctrs/man/internal-faq-matches-algorithm.Rd 0000644 0001762 0000144 00000027512 14315060307 020452 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/match.R
\name{internal-faq-matches-algorithm}
\alias{internal-faq-matches-algorithm}
\title{Internal FAQ - Implementation of \code{vec_locate_matches()}}
\description{
\code{vec_locate_matches()} is similar to \code{vec_match()}, but detects \emph{all} matches by default, and can match on conditions other than equality (like \code{>=} and \code{<}). There are also various other arguments to limit or adjust exactly which kinds of matches are returned. Here is an example:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
}
\section{Algorithm description}{
\subsection{Overview and \code{==}}{
The simplest (approximate) way to think about the algorithm that \code{df_locate_matches_recurse()} uses is that it sorts both inputs, and then starts at the midpoint in \code{needles} and uses a binary search to find each needle in \code{haystack}. Since there might be multiple of the same needle, we find the location of the lower and upper duplicate of that needle to handle all duplicates of that needle at once. Similarly, if there are duplicates of a matching \code{haystack} value, we find the lower and upper duplicates of the match.
If the condition is \code{==}, that is pretty much all we have to do. For each needle, we then record 3 things: the location of the needle, the location of the lower match in the haystack, and the match size (i.e. \code{loc_upper_match - loc_lower_match + 1}). This later gets expanded in \code{expand_compact_indices()} into the actual output.
After recording the matches for a single needle, we perform the same procedure on the LHS and RHS of that needle (remember we started on the midpoint needle). i.e. from \verb{[1, loc_needle-1]} and \verb{[loc_needle+1, size_needles]}, again taking the midpoint of those two ranges, finding their respective needle in the haystack, recording matches, and continuing on to the next needle. This iteration proceeds until we run out of needles.
When we have a data frame with multiple columns, we add a layer of recursion to this. For the first column, we find the locations of the lower/upper duplicate of the current needle, and we find the locations of the lower/upper matches in the haystack. If we are on the final column in the data frame, we record the matches, otherwise we pass this information on to another call to \code{df_locate_matches_recurse()}, bumping the column index and using these refined lower/upper bounds as the starting bounds for the next column.
I think an example would be useful here, so below I step through this process for a few iterations:
\if{html}{\out{}}\preformatted{# 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
# ...
}\if{html}{\out{
}}
In the real code, rather than comparing the double values of the columns directly, we replace each column with pseudo "joint ranks" computed between the i-th column of \code{needles} and the i-th column of \code{haystack}. It is approximately like doing \code{vec_rank(vec_c(needles$x, haystack$x), type = "dense")}, then splitting the resulting ranks back up into their corresponding needle/haystack columns. This keeps the recursion code simpler, because we only have to worry about comparing integers.
}
\subsection{Non-equi conditions and containers}{
At this point we can talk about non-equi conditions like \code{<} or \code{>=}. The general idea is pretty simple, and just builds on the above algorithm. For example, start with the \code{x} column from needles/haystack above:
\if{html}{\out{}}\preformatted{needles$x
#> [1] 1 1 2 2 2 3
haystack$x
#> [1] 1 1 2 2 3
}\if{html}{\out{
}}
If we used a condition of \code{<=}, then we'd do everything the same as before:
\itemize{
\item Midpoint in needles is location 3, value \code{x==2}
\item Find lower/upper duplicates in needles, giving locations \verb{[3, 5]}
\item Find lower/upper \emph{exact} match in haystack, giving locations \verb{[3, 4]}
}
At this point, we need to "adjust" the \code{haystack} match bounds to account for the condition. Since \code{haystack} is ordered, our "rule" for \code{<=} is to keep the lower match location the same, but extend the upper match location to the upper bound, so we end up with \verb{[3, 5]}. We know we can extend the upper match location because every haystack value after the exact match should be less than the needle. Then we just record the matches and continue on normally.
This approach is really nice, because we only have to exactly match the \code{needle} in \code{haystack}. We don't have to compare each needle against every value in \code{haystack}, which would take a massive amount of time.
However, it gets slightly more complex with data frames with multiple columns. Let's go back to our original \code{needles} and \code{haystack} data frames and apply the condition \code{<=} to each column. Here is another worked example, which shows a case where our "rule" falls apart on the second column.
\if{html}{\out{}}\preformatted{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.
}\if{html}{\out{
}}
If you read through the above example, you'll see that the rule didn't work here. The problem is that while \code{haystack} is ordered (by \code{vec_order()}s standards), each column isn't ordered \emph{independently} of the others. Instead, each column is ordered within the "group" created by previous columns. Concretely, \code{haystack} here has an ordered \code{x} column, but if you look at \code{haystack$y} by itself, it isn't ordered (because of that 1 at the end). That is what causes the rule to fail.
\if{html}{\out{}}\preformatted{haystack
#> x y
#> 1 1 2
#> 2 1 3
#> 3 2 4
#> 4 2 4
#> 5 3 1
}\if{html}{\out{
}}
To fix this, we need to create haystack "containers" where the values within each container are all \emph{totally} ordered. For \code{haystack} that would create 2 containers and look like:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
This is essentially what \code{computing_nesting_container_ids()} does. You can actually see these ids with the helper, \code{compute_nesting_container_info()}:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
So the idea is that for each needle, we look in each haystack container and find all the matches, then we aggregate all of the matches once at the end. \code{df_locate_matches_with_containers()} has the job of iterating over the containers.
Computing totally ordered containers can be expensive, but luckily it doesn't happen very often in normal usage.
\itemize{
\item If there are all \code{==} conditions, we don't need containers (i.e. any equi join)
\item If there is only 1 non-equi condition and no conditions after it, we don't need containers (i.e. most rolling joins)
\item Otherwise the typical case where we need containers is if we have something like \verb{date >= lower, date <= upper}. Even so, the computation cost generally scales with the number of columns in \code{haystack} you compute containers with (here 2), and it only really slows down around 4 columns or so, which I haven't ever seen a real life example of.
}
}
}
vctrs/man/vec_seq_along.Rd 0000644 0001762 0000144 00000001355 13505165544 015276 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/size.R
\name{vec_seq_along}
\alias{vec_seq_along}
\alias{vec_init_along}
\title{Useful sequences}
\usage{
vec_seq_along(x)
vec_init_along(x, y = x)
}
\arguments{
\item{x, y}{Vectors}
}
\value{
\itemize{
\item \code{vec_seq_along()} an integer vector with the same size as \code{x}.
\item \code{vec_init_along()} a vector with the same type as \code{x} and the same size
as \code{y}.
}
}
\description{
\code{vec_seq_along()} is equivalent to \code{\link[=seq_along]{seq_along()}} but uses size, not length.
\code{vec_init_along()} creates a vector of missing values with size matching
an existing object.
}
\examples{
vec_seq_along(mtcars)
vec_init_along(head(mtcars))
}
vctrs/man/vec-set.Rd 0000644 0001762 0000144 00000011411 14362266120 014023 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/set.R
\name{vec-set}
\alias{vec-set}
\alias{vec_set_intersect}
\alias{vec_set_difference}
\alias{vec_set_union}
\alias{vec_set_symmetric_difference}
\title{Set operations}
\usage{
vec_set_intersect(
x,
y,
...,
ptype = NULL,
x_arg = "x",
y_arg = "y",
error_call = current_env()
)
vec_set_difference(
x,
y,
...,
ptype = NULL,
x_arg = "x",
y_arg = "y",
error_call = current_env()
)
vec_set_union(
x,
y,
...,
ptype = NULL,
x_arg = "x",
y_arg = "y",
error_call = current_env()
)
vec_set_symmetric_difference(
x,
y,
...,
ptype = NULL,
x_arg = "x",
y_arg = "y",
error_call = current_env()
)
}
\arguments{
\item{x, y}{A pair of vectors.}
\item{...}{These dots are for future extensions and must be empty.}
\item{ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type between \code{x} and \code{y}. If supplied, both \code{x} and
\code{y} will be cast to this type.}
\item{x_arg, y_arg}{Argument names for \code{x} and \code{y}. These are used in error
messages.}
\item{error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\value{
A vector of the common type of \code{x} and \code{y} (or \code{ptype}, if supplied)
containing the result of the corresponding set function.
}
\description{
\itemize{
\item \code{vec_set_intersect()} returns all values in both \code{x} and \code{y}.
\item \code{vec_set_difference()} returns all values in \code{x} but not \code{y}. Note
that this is an asymmetric set difference, meaning it is not commutative.
\item \code{vec_set_union()} returns all values in either \code{x} or \code{y}.
\item \code{vec_set_symmetric_difference()} returns all values in either \code{x} or \code{y}
but not both. This is a commutative difference.
}
Because these are \emph{set} operations, these functions only return unique values
from \code{x} and \code{y}, returned in the order they first appeared in the original
input. Names of \code{x} and \code{y} are retained on the result, but names are always
taken from \code{x} if the value appears in both inputs.
These functions work similarly to \code{\link[=intersect]{intersect()}}, \code{\link[=setdiff]{setdiff()}}, and \code{\link[=union]{union()}},
but don't strip attributes and can be used with data frames.
}
\details{
Missing values are treated as equal to other missing values. For doubles and
complexes, \code{NaN} are equal to other \code{NaN}, but not to \code{NA}.
}
\section{Dependencies}{
\subsection{\code{vec_set_intersect()}}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
\item \code{\link[=vec_slice]{vec_slice()}}
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
}
}
\subsection{\code{vec_set_difference()}}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
\item \code{\link[=vec_slice]{vec_slice()}}
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
}
}
\subsection{\code{vec_set_union()}}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
\item \code{\link[=vec_slice]{vec_slice()}}
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
\item \code{\link[=vec_c]{vec_c()}}
}
}
\subsection{\code{vec_set_symmetric_difference()}}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
\item \code{\link[=vec_slice]{vec_slice()}}
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
\item \code{\link[=vec_c]{vec_c()}}
}
}
}
\examples{
x <- c(1, 2, 1, 4, 3)
y <- c(2, 5, 5, 1)
# All unique values in both `x` and `y`.
# Duplicates in `x` and `y` are always removed.
vec_set_intersect(x, y)
# All unique values in `x` but not `y`
vec_set_difference(x, y)
# All unique values in either `x` or `y`
vec_set_union(x, y)
# All unique values in either `x` or `y` but not both
vec_set_symmetric_difference(x, y)
# These functions can also be used with data frames
x <- data_frame(
a = c(2, 3, 2, 2),
b = c("j", "k", "j", "l")
)
y <- data_frame(
a = c(1, 2, 2, 2, 3),
b = c("j", "l", "j", "l", "j")
)
vec_set_intersect(x, y)
vec_set_difference(x, y)
vec_set_union(x, y)
vec_set_symmetric_difference(x, y)
# Vector names don't affect set membership, but if you'd like to force
# them to, you can transform the vector into a two column data frame
x <- c(a = 1, b = 2, c = 2, d = 3)
y <- c(c = 2, b = 1, a = 3, d = 3)
vec_set_intersect(x, y)
x <- data_frame(name = names(x), value = unname(x))
y <- data_frame(name = names(y), value = unname(y))
vec_set_intersect(x, y)
}
vctrs/man/vec_rank.Rd 0000644 0001762 0000144 00000012363 14315060307 014251 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/rank.R
\name{vec_rank}
\alias{vec_rank}
\title{Compute ranks}
\usage{
vec_rank(
x,
...,
ties = c("min", "max", "sequential", "dense"),
incomplete = c("rank", "na"),
direction = "asc",
na_value = "largest",
nan_distinct = FALSE,
chr_proxy_collate = NULL
)
}
\arguments{
\item{x}{A vector}
\item{...}{These dots are for future extensions and must be empty.}
\item{ties}{Ranking of duplicate values.
\itemize{
\item \code{"min"}: Use the current rank for all duplicates. The next non-duplicate
value will have a rank incremented by the number of duplicates present.
\item \code{"max"}: Use the current rank \code{+ n_duplicates - 1} for all duplicates.
The next non-duplicate value will have a rank incremented by the number of
duplicates present.
\item \code{"sequential"}: Use an increasing sequence of ranks starting at the
current rank, applied to duplicates in order of appearance.
\item \code{"dense"}: Use the current rank for all duplicates. The next
non-duplicate value will have a rank incremented by \code{1}, effectively
removing any gaps in the ranking.
}}
\item{incomplete}{Ranking of missing and \link[=vec_detect_complete]{incomplete}
observations.
\itemize{
\item \code{"rank"}: Rank incomplete observations normally. Missing values within
incomplete observations will be affected by \code{na_value} and \code{nan_distinct}.
\item \code{"na"}: Don't rank incomplete observations at all. Instead, they are
given a rank of \code{NA}. In this case, \code{na_value} and \code{nan_distinct} have
no effect.
}}
\item{direction}{Direction to sort in.
\itemize{
\item A single \code{"asc"} or \code{"desc"} for ascending or descending order
respectively.
\item For data frames, a length \code{1} or \code{ncol(x)} character vector containing
only \code{"asc"} or \code{"desc"}, specifying the direction for each column.
}}
\item{na_value}{Ordering of missing values.
\itemize{
\item A single \code{"largest"} or \code{"smallest"} for ordering missing values as the
largest or smallest values respectively.
\item For data frames, a length \code{1} or \code{ncol(x)} character vector containing
only \code{"largest"} or \code{"smallest"}, specifying how missing values should
be ordered within each column.
}}
\item{nan_distinct}{A single logical specifying whether or not \code{NaN} should
be considered distinct from \code{NA} for double and complex vectors. If \code{TRUE},
\code{NaN} will always be ordered between \code{NA} and non-missing numbers.}
\item{chr_proxy_collate}{A function generating an alternate representation
of character vectors to use for collation, often used for locale-aware
ordering.
\itemize{
\item If \code{NULL}, no transformation is done.
\item Otherwise, this must be a function of one argument. If the input contains
a character vector, it will be passed to this function after it has been
translated to UTF-8. This function should return a character vector with
the same length as the input. The result should sort as expected in the
C-locale, regardless of encoding.
}
For data frames, \code{chr_proxy_collate} will be applied to all character
columns.
Common transformation functions include: \code{tolower()} for case-insensitive
ordering and \code{stringi::stri_sort_key()} for locale-aware ordering.}
}
\description{
\code{vec_rank()} computes the sample ranks of a vector. For data frames, ranks
are computed along the rows, using all columns after the first to break
ties.
}
\details{
Unlike \code{\link[base:rank]{base::rank()}}, when \code{incomplete = "rank"} all missing values are
given the same rank, rather than an increasing sequence of ranks. When
\code{nan_distinct = FALSE}, \code{NaN} values are given the same rank as \code{NA},
otherwise they are given a rank that differentiates them from \code{NA}.
Like \code{\link[=vec_order_radix]{vec_order_radix()}}, ordering is done in the C-locale. This can affect
the ranks of character vectors, especially regarding how uppercase and
lowercase letters are ranked. See the documentation of \code{\link[=vec_order_radix]{vec_order_radix()}}
for more information.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_order_radix]{vec_order_radix()}}
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\examples{
x <- c(5L, 6L, 3L, 3L, 5L, 3L)
vec_rank(x, ties = "min")
vec_rank(x, ties = "max")
# Sequential ranks use an increasing sequence for duplicates
vec_rank(x, ties = "sequential")
# Dense ranks remove gaps between distinct values,
# even if there are duplicates
vec_rank(x, ties = "dense")
y <- c(NA, x, NA, NaN)
# Incomplete values match other incomplete values by default, and their
# overall position can be adjusted with `na_value`
vec_rank(y, na_value = "largest")
vec_rank(y, na_value = "smallest")
# NaN can be ranked separately from NA if required
vec_rank(y, nan_distinct = TRUE)
# Rank in descending order. Since missing values are the largest value,
# they are given a rank of `1` when ranking in descending order.
vec_rank(y, direction = "desc", na_value = "largest")
# Give incomplete values a rank of `NA` by setting `incomplete = "na"`
vec_rank(y, incomplete = "na")
# Can also rank data frames, using columns after the first to break ties
z <- c(2L, 3L, 4L, 4L, 5L, 2L)
df <- data_frame(x = x, z = z)
df
vec_rank(df)
}
vctrs/man/maybe_lossy_cast.Rd 0000644 0001762 0000144 00000005743 14315060307 016025 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/conditions.R
\name{maybe_lossy_cast}
\alias{maybe_lossy_cast}
\title{Lossy cast error}
\usage{
maybe_lossy_cast(
result,
x,
to,
lossy = NULL,
locations = NULL,
...,
loss_type = c("precision", "generality"),
x_arg,
to_arg,
call = caller_env(),
details = NULL,
message = NULL,
class = NULL,
.deprecation = FALSE
)
}
\arguments{
\item{result}{The result of a potentially lossy cast.}
\item{x}{Vectors to cast.}
\item{to}{Type to cast to.}
\item{lossy}{A logical vector indicating which elements of \code{result}
were lossy.
Can also be a single \code{TRUE}, but note that \code{locations} picks up
locations from this vector by default. In this case, supply your
own location vector, possibly empty.}
\item{locations}{An optional integer vector giving the
locations where \code{x} lost information.}
\item{..., class}{Only use these fields when creating a subclass.}
\item{loss_type}{The kind of lossy cast to be mentioned in error
messages. Can be loss of precision (for instance from double to
integer) or loss of generality (from character to factor).}
\item{x_arg}{Argument name for \code{x}, used in error messages to
inform the user about the locations of incompatible types
(see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{to_arg}{Argument name \code{to} used in error messages to
inform the user about the locations of incompatible types
(see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{details}{Any additional human readable details.}
\item{message}{An overriding message for the error. \code{details} and
\code{message} are mutually exclusive, supplying both is an error.}
\item{.deprecation}{If \code{TRUE}, the error is downgraded to a
deprecation warning. This is useful for transitioning your class
to a stricter conversion scheme. The warning advises your users
to wrap their code with \code{allow_lossy_cast()}.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
By default, lossy casts are an error. Use \code{allow_lossy_cast()} to
silence these errors and continue with the partial results. In this
case the lost values are typically set to \code{NA} or to a lower value
resolution, depending on the type of cast.
Lossy cast errors are thrown by \code{maybe_lossy_cast()}. Unlike
functions prefixed with \code{stop_}, \code{maybe_lossy_cast()} usually
returns a result. If a lossy cast is detected, it throws an error,
unless it's been wrapped in \code{allow_lossy_cast()}. In that case, it
returns the result silently.
}
\keyword{internal}
vctrs/man/vec-rep.Rd 0000644 0001762 0000144 00000006573 14511524374 014037 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/rep.R
\name{vec-rep}
\alias{vec-rep}
\alias{vec_rep}
\alias{vec_rep_each}
\alias{vec_unrep}
\title{Repeat a vector}
\usage{
vec_rep(
x,
times,
...,
error_call = current_env(),
x_arg = "x",
times_arg = "times"
)
vec_rep_each(
x,
times,
...,
error_call = current_env(),
x_arg = "x",
times_arg = "times"
)
vec_unrep(x)
}
\arguments{
\item{x}{A vector.}
\item{times}{For \code{vec_rep()}, a single integer for the number of times to repeat
the entire vector.
For \code{vec_rep_each()}, an integer vector of the number of times to repeat
each element of \code{x}. \code{times} will be \link[=theory-faq-recycling]{recycled} to
the size of \code{x}.}
\item{...}{These dots are for future extensions and must be empty.}
\item{error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{x_arg, times_arg}{Argument names for errors.}
}
\value{
For \code{vec_rep()}, a vector the same type as \code{x} with size
\code{vec_size(x) * times}.
For \code{vec_rep_each()}, a vector the same type as \code{x} with size
\code{sum(vec_recycle(times, vec_size(x)))}.
For \code{vec_unrep()}, a data frame with two columns, \code{key} and \code{times}. \code{key}
is a vector with the same type as \code{x}, and \code{times} is an integer vector.
}
\description{
\itemize{
\item \code{vec_rep()} repeats an entire vector a set number of \code{times}.
\item \code{vec_rep_each()} repeats each element of a vector a set number of \code{times}.
\item \code{vec_unrep()} compresses a vector with repeated values. The repeated values
are returned as a \code{key} alongside the number of \code{times} each key is
repeated.
}
}
\details{
Using \code{vec_unrep()} and \code{vec_rep_each()} together is similar to using
\code{\link[base:rle]{base::rle()}} and \code{\link[base:rle]{base::inverse.rle()}}. The following invariant shows
the relationship between the two functions:
\if{html}{\out{}}\preformatted{compressed <- vec_unrep(x)
identical(x, vec_rep_each(compressed$key, compressed$times))
}\if{html}{\out{
}}
There are two main differences between \code{vec_unrep()} and \code{\link[base:rle]{base::rle()}}:
\itemize{
\item \code{vec_unrep()} treats adjacent missing values as equivalent, while \code{rle()}
treats them as different values.
\item \code{vec_unrep()} works along the size of \code{x}, while \code{rle()} works along its
length. This means that \code{vec_unrep()} works on data frames by compressing
repeated rows.
}
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\examples{
# Repeat the entire vector
vec_rep(1:2, 3)
# Repeat within each vector
vec_rep_each(1:2, 3)
x <- vec_rep_each(1:2, c(3, 4))
x
# After using `vec_rep_each()`, you can recover the original vector
# with `vec_unrep()`
vec_unrep(x)
df <- data.frame(x = 1:2, y = 3:4)
# `rep()` repeats columns of data frames, and returns lists
rep(df, each = 2)
# `vec_rep()` and `vec_rep_each()` repeat rows, and return data frames
vec_rep(df, 2)
vec_rep_each(df, 2)
# `rle()` treats adjacent missing values as different
y <- c(1, NA, NA, 2)
rle(y)
# `vec_unrep()` treats them as equivalent
vec_unrep(y)
}
vctrs/man/vec_ptype2.Rd 0000644 0001762 0000144 00000005654 14315060307 014546 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-bare.R, R/type2.R
\name{vec_ptype2.logical}
\alias{vec_ptype2.logical}
\alias{vec_ptype2.integer}
\alias{vec_ptype2.double}
\alias{vec_ptype2.complex}
\alias{vec_ptype2.character}
\alias{vec_ptype2.raw}
\alias{vec_ptype2.list}
\alias{vec_ptype2}
\title{Find the common type for a pair of vectors}
\usage{
\method{vec_ptype2}{logical}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{integer}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{double}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{complex}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{character}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{raw}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_ptype2}{list}(x, y, ..., x_arg = "", y_arg = "")
vec_ptype2(
x,
y,
...,
x_arg = caller_arg(x),
y_arg = caller_arg(y),
call = caller_env()
)
}
\arguments{
\item{x, y}{Vector types.}
\item{...}{These dots are for future extensions and must be empty.}
\item{x_arg, y_arg}{Argument names for \code{x} and \code{y}. These are used
in error messages to inform the user about the locations of
incompatible types (see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\description{
\code{vec_ptype2()} defines the coercion hierarchy for a set of related
vector types. Along with \code{\link[=vec_cast]{vec_cast()}}, this generic forms the
foundation of type coercions in vctrs.
\code{vec_ptype2()} is relevant when you are implementing vctrs methods
for your class, but it should not usually be called directly. If
you need to find the common type of a set of inputs, call
\code{\link[=vec_ptype_common]{vec_ptype_common()}} instead. This function supports multiple
inputs and \link[=vec_ptype_finalise]{finalises} the common type.
}
\section{Implementing coercion methods}{
\itemize{
\item For an overview of how these generics work and their roles in vctrs,
see \code{\link[=theory-faq-coercion]{?theory-faq-coercion}}.
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
\item For a tutorial about implementing vctrs classes from scratch, see
\code{vignette("s3-vector")}.
}
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_ptype]{vec_ptype()}} is applied to \code{x} and \code{y}
}
}
\seealso{
\code{\link[=stop_incompatible_type]{stop_incompatible_type()}} when you determine from the
attributes that an input can't be cast to the target type.
}
vctrs/man/vec_interleave.Rd 0000644 0001762 0000144 00000005372 14511524374 015465 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/slice-interleave.R
\name{vec_interleave}
\alias{vec_interleave}
\title{Interleave many vectors into one vector}
\usage{
vec_interleave(
...,
.ptype = NULL,
.name_spec = NULL,
.name_repair = c("minimal", "unique", "check_unique", "universal", "unique_quiet",
"universal_quiet")
)
}
\arguments{
\item{...}{Vectors to interleave. These will be
\link[=theory-faq-recycling]{recycled} to a common size.}
\item{.ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{...}.
Alternatively, you can supply \code{.ptype} to give the output known type.
If \code{getOption("vctrs.no_guessing")} is \code{TRUE} you must supply this value:
this is a convenient way to make production code demand fixed types.}
\item{.name_spec}{A name specification for combining
inner and outer names. This is relevant for inputs passed with a
name, when these inputs are themselves named, like \code{outer = c(inner = 1)}, or when they have length greater than 1: \code{outer = 1:2}. By default, these cases trigger an error. You can resolve
the error by providing a specification that describes how to
combine the names or the indices of the inner vector with the
name of the input. This specification can be:
\itemize{
\item A function of two arguments. The outer name is passed as a
string to the first argument, and the inner names or positions
are passed as second argument.
\item An anonymous function as a purrr-style formula.
\item A glue specification of the form \code{"{outer}_{inner}"}.
\item An \code{\link[rlang:zap]{rlang::zap()}} object, in which case both outer and inner
names are ignored and the result is unnamed.
}
See the \link[=name_spec]{name specification topic}.}
\item{.name_repair}{How to repair names, see \code{repair} options in
\code{\link[=vec_as_names]{vec_as_names()}}.}
}
\description{
\code{vec_interleave()} combines multiple vectors together, much like \code{\link[=vec_c]{vec_c()}},
but does so in such a way that the elements of each vector are interleaved
together.
It is a more efficient equivalent to the following usage of \code{vec_c()}:
\if{html}{\out{}}\preformatted{vec_interleave(x, y) == vec_c(x[1], y[1], x[2], y[2], ..., x[n], y[n])
}\if{html}{\out{
}}
}
\section{Dependencies}{
\subsection{vctrs dependencies}{
\itemize{
\item \code{\link[=list_unchop]{list_unchop()}}
}
}
}
\examples{
# The most common case is to interleave two vectors
vec_interleave(1:3, 4:6)
# But you aren't restricted to just two
vec_interleave(1:3, 4:6, 7:9, 10:12)
# You can also interleave data frames
x <- data_frame(x = 1:2, y = c("a", "b"))
y <- data_frame(x = 3:4, y = c("c", "d"))
vec_interleave(x, y)
}
vctrs/man/new_rcrd.Rd 0000644 0001762 0000144 00000001702 14276722575 014301 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-rcrd.R
\name{new_rcrd}
\alias{new_rcrd}
\alias{ses}
\alias{rcrd}
\title{rcrd (record) S3 class}
\usage{
new_rcrd(fields, ..., class = character())
}
\arguments{
\item{fields}{A list or a data frame. Lists must be rectangular
(same sizes), and contain uniquely named vectors (at least
one). \code{fields} is validated with \code{\link[=df_list]{df_list()}} to ensure uniquely
named vectors.}
\item{...}{Additional attributes}
\item{class}{Name of subclass.}
}
\description{
The rcrd class extends \link{vctr}. A rcrd is composed of 1 or more \link{field}s,
which must be vectors of the same length. Is designed specifically for
classes that can naturally be decomposed into multiple vectors of the same
length, like \link{POSIXlt}, but where the organisation should be considered
an implementation detail invisible to the user (unlike a \link{data.frame}).
}
\keyword{internal}
vctrs/man/new_partial.Rd 0000644 0001762 0000144 00000002204 14276722575 015001 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/partial.R
\name{new_partial}
\alias{new_partial}
\alias{is_partial}
\alias{vec_ptype_finalise}
\title{Partial type}
\usage{
new_partial(..., class = character())
is_partial(x)
vec_ptype_finalise(x, ...)
}
\arguments{
\item{...}{Attributes of the partial type}
\item{class}{Name of subclass.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
Use \code{new_partial()} when constructing a new partial type subclass;
and use \code{is_partial()} to test if a type is partial. All subclasses
need to provide a \code{vec_ptype_finalise()} method.
}
\details{
As the name suggests, a partial type \emph{partially} specifies a type, and
it must be combined with data to yield a full type. A useful example
of a partial type is \code{\link[=partial_frame]{partial_frame()}}, which makes it possible to
specify the type of just a few columns in a data frame. Use this constructor
if you're making your own partial type.
}
\keyword{internal}
vctrs/man/vector-checks.Rd 0000644 0001762 0000144 00000010131 14401377400 015211 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/assert.R
\name{vector-checks}
\alias{vector-checks}
\alias{obj_is_vector}
\alias{obj_check_vector}
\alias{vec_check_size}
\title{Vector checks}
\usage{
obj_is_vector(x)
obj_check_vector(x, ..., arg = caller_arg(x), call = caller_env())
vec_check_size(x, size, ..., arg = caller_arg(x), call = caller_env())
}
\arguments{
\item{x}{For \verb{obj_*()} functions, an object. For \verb{vec_*()} functions, a
vector.}
\item{...}{These dots are for future extensions and must be empty.}
\item{arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{size}{The size to check for.}
}
\value{
\itemize{
\item \code{obj_is_vector()} returns a single \code{TRUE} or \code{FALSE}.
\item \code{obj_check_vector()} returns \code{NULL} invisibly, or errors.
\item \code{vec_check_size()} returns \code{NULL} invisibly, or errors.
}
}
\description{
\itemize{
\item \code{obj_is_vector()} tests if \code{x} is considered a vector in the vctrs sense.
See \emph{Vectors and scalars} below for the exact details.
\item \code{obj_check_vector()} uses \code{obj_is_vector()} and throws a standardized and
informative error if it returns \code{FALSE}.
\item \code{vec_check_size()} tests if \code{x} has size \code{size}, and throws an informative
error if it doesn't.
}
}
\section{Vectors and scalars}{
Informally, a vector is a collection that makes sense to use as column in a
data frame. The following rules define whether or not \code{x} is considered a
vector.
If no \code{\link[=vec_proxy]{vec_proxy()}} method has been registered, \code{x} is a vector if:
\itemize{
\item The \link[=typeof]{base type} of the object is atomic: \code{"logical"}, \code{"integer"},
\code{"double"}, \code{"complex"}, \code{"character"}, or \code{"raw"}.
\item \code{x} is a list, as defined by \code{\link[=obj_is_list]{obj_is_list()}}.
\item \code{x} is a \link{data.frame}.
}
If a \code{vec_proxy()} method has been registered, \code{x} is a vector if:
\itemize{
\item The proxy satisfies one of the above conditions.
\item The base type of the proxy is \code{"list"}, regardless of its class. S3 lists
are thus treated as scalars unless they implement a \code{vec_proxy()} method.
}
Otherwise an object is treated as scalar and cannot be used as a vector.
In particular:
\itemize{
\item \code{NULL} is not a vector.
\item S3 lists like \code{lm} objects are treated as scalars by default.
\item Objects of type \link{expression} are not treated as vectors.
}
}
\section{Technical limitations}{
\itemize{
\item Support for S4 vectors is currently limited to objects that inherit from an
atomic type.
\item Subclasses of \link{data.frame} that \emph{append} their class to the back of the
\code{"class"} attribute are not treated as vectors. If you inherit from an S3
class, always prepend your class to the front of the \code{"class"} attribute
for correct dispatch. This matches our general principle of allowing
subclasses but not mixins.
}
}
\examples{
obj_is_vector(1)
# Data frames are vectors
obj_is_vector(data_frame())
# Bare lists are vectors
obj_is_vector(list())
# S3 lists are vectors if they explicitly inherit from `"list"`
x <- structure(list(), class = c("my_list", "list"))
obj_is_list(x)
obj_is_vector(x)
# But if they don't explicitly inherit from `"list"`, they aren't
# automatically considered to be vectors. Instead, vctrs considers this
# to be a scalar object, like a linear model returned from `lm()`.
y <- structure(list(), class = "my_list")
obj_is_list(y)
obj_is_vector(y)
# `obj_check_vector()` throws an informative error if the input
# isn't a vector
try(obj_check_vector(y))
# `vec_check_size()` throws an informative error if the size of the
# input doesn't match `size`
vec_check_size(1:5, size = 5)
try(vec_check_size(1:5, size = 4))
}
vctrs/man/faq/ 0000755 0001762 0000144 00000000000 14532470452 012743 5 ustar ligges users vctrs/man/faq/internal/ 0000755 0001762 0000144 00000000000 14315060307 014550 5 ustar ligges users vctrs/man/faq/internal/matches-algorithm.Rmd 0000644 0001762 0000144 00000024565 14315060307 020640 0 ustar ligges users ---
output: html_document
editor_options:
chunk_output_type: console
---
```{r, child = "../setup.Rmd", include = FALSE}
```
`vec_locate_matches()` is similar to `vec_match()`, but detects _all_ matches by default, and can match on conditions other than equality (like `>=` and `<`). There are also various other arguments to limit or adjust exactly which kinds of matches are returned. Here is an example:
```{r}
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)
```
# Algorithm description
## Overview and `==`
The simplest (approximate) way to think about the algorithm that `df_locate_matches_recurse()` uses is that it sorts both inputs, and then starts at the midpoint in `needles` and uses a binary search to find each needle in `haystack`. Since there might be multiple of the same needle, we find the location of the lower and upper duplicate of that needle to handle all duplicates of that needle at once. Similarly, if there are duplicates of a matching `haystack` value, we find the lower and upper duplicates of the match.
If the condition is `==`, that is pretty much all we have to do. For each needle, we then record 3 things: the location of the needle, the location of the lower match in the haystack, and the match size (i.e. `loc_upper_match - loc_lower_match + 1`). This later gets expanded in `expand_compact_indices()` into the actual output.
After recording the matches for a single needle, we perform the same procedure on the LHS and RHS of that needle (remember we started on the midpoint needle). i.e. from `[1, loc_needle-1]` and `[loc_needle+1, size_needles]`, again taking the midpoint of those two ranges, finding their respective needle in the haystack, recording matches, and continuing on to the next needle. This iteration proceeds until we run out of needles.
When we have a data frame with multiple columns, we add a layer of recursion to this. For the first column, we find the locations of the lower/upper duplicate of the current needle, and we find the locations of the lower/upper matches in the haystack. If we are on the final column in the data frame, we record the matches, otherwise we pass this information on to another call to `df_locate_matches_recurse()`, bumping the column index and using these refined lower/upper bounds as the starting bounds for the next column.
I think an example would be useful here, so below I step through this process for a few iterations:
```{r}
# 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
haystack
## 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
# ...
```
In the real code, rather than comparing the double values of the columns directly, we replace each column with pseudo "joint ranks" computed between the i-th column of `needles` and the i-th column of `haystack`. It is approximately like doing `vec_rank(vec_c(needles$x, haystack$x), type = "dense")`, then splitting the resulting ranks back up into their corresponding needle/haystack columns. This keeps the recursion code simpler, because we only have to worry about comparing integers.
## Non-equi conditions and containers
At this point we can talk about non-equi conditions like `<` or `>=`. The general idea is pretty simple, and just builds on the above algorithm. For example, start with the `x` column from needles/haystack above:
```{r}
needles$x
haystack$x
```
If we used a condition of `<=`, then we'd do everything the same as before:
- Midpoint in needles is location 3, value `x==2`
- Find lower/upper duplicates in needles, giving locations `[3, 5]`
- Find lower/upper _exact_ match in haystack, giving locations `[3, 4]`
At this point, we need to "adjust" the `haystack` match bounds to account for the condition. Since `haystack` is ordered, our "rule" for `<=` is to keep the lower match location the same, but extend the upper match location to the upper bound, so we end up with `[3, 5]`. We know we can extend the upper match location because every haystack value after the exact match should be less than the needle. Then we just record the matches and continue on normally.
This approach is really nice, because we only have to exactly match the `needle` in `haystack`. We don't have to compare each needle against every value in `haystack`, which would take a massive amount of time.
However, it gets slightly more complex with data frames with multiple columns. Let's go back to our original `needles` and `haystack` data frames and apply the condition `<=` to each column. Here is another worked example, which shows a case where our "rule" falls apart on the second column.
```{r}
needles
haystack
# `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.
```
If you read through the above example, you'll see that the rule didn't work here. The problem is that while `haystack` is ordered (by `vec_order()`s standards), each column isn't ordered _independently_ of the others. Instead, each column is ordered within the "group" created by previous columns. Concretely, `haystack` here has an ordered `x` column, but if you look at `haystack$y` by itself, it isn't ordered (because of that 1 at the end). That is what causes the rule to fail.
```{r}
haystack
```
To fix this, we need to create haystack "containers" where the values within each container are all _totally_ ordered. For `haystack` that would create 2 containers and look like:
``` r
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
```
This is essentially what `computing_nesting_container_ids()` does. You can actually see these ids with the helper, `compute_nesting_container_info()`:
```{r}
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]]
```
So the idea is that for each needle, we look in each haystack container and find all the matches, then we aggregate all of the matches once at the end. `df_locate_matches_with_containers()` has the job of iterating over the containers.
Computing totally ordered containers can be expensive, but luckily it doesn't happen very often in normal usage.
- If there are all `==` conditions, we don't need containers (i.e. any equi join)
- If there is only 1 non-equi condition and no conditions after it, we don't need containers (i.e. most rolling joins)
- Otherwise the typical case where we need containers is if we have something like `date >= lower, date <= upper`. Even so, the computation cost generally scales with the number of columns in `haystack` you compute containers with (here 2), and it only really slows down around 4 columns or so, which I haven't ever seen a real life example of.
vctrs/man/faq/internal/ptype2-identity.Rmd 0000644 0001762 0000144 00000006123 14276722575 020312 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
## Promotion monoid
Promotions (i.e. automatic coercions) should always transform inputs to their richer type to avoid losing values of precision. `vec_ptype2()` returns the _richer_ type of two vectors, or throws an incompatible type error if none of the two vector types include the other. For example, the richer type of integer and double is the latter because double covers a larger range of values than integer.
`vec_ptype2()` is a [monoid](https://en.wikipedia.org/wiki/Monoid) over vectors, which in practical terms means that it is a well behaved operation for [reduction](https://purrr.tidyverse.org/reference/reduce.html). Reduction is an important operation for promotions because that is how the richer type of multiple elements is computed. As a monoid, `vec_ptype2()` needs an identity element, i.e. a value that doesn't change the result of the reduction. vctrs has two identity values, `NULL` and __unspecified__ vectors.
## The `NULL` identity
As an identity element that shouldn't influence the determination of the common type of a set of vectors, `NULL` is promoted to any type:
```{r}
vec_ptype2(NULL, "")
vec_ptype2(1L, NULL)
```
The common type of `NULL` and `NULL` is the identity `NULL`:
```{r}
vec_ptype2(NULL, NULL)
```
This way the result of `vec_ptype2(NULL, NULL)` does not influence subsequent promotions:
```{r}
vec_ptype2(
vec_ptype2(NULL, NULL),
""
)
```
## Unspecified vectors
In the vctrs coercion system, logical vectors of missing values are also automatically promoted to the type of any other vector, just like `NULL`. We call these vectors unspecified. The special coercion semantics of unspecified vectors serve two purposes:
1. It makes it possible to assign vectors of `NA` inside any type of vectors, even when they are not coercible with logical:
```{r}
x <- letters[1:5]
vec_assign(x, 1:2, c(NA, NA))
```
2. We can't put `NULL` in a data frame, so we need an identity element that behaves more like a vector. Logical vectors of `NA` seem a natural fit for this.
Unspecified vectors are thus promoted to any other type, just like `NULL`:
```{r}
vec_ptype2(NA, "")
vec_ptype2(1L, c(NA, NA))
```
## Finalising common types
vctrs has an internal vector type of class `vctrs_unspecified`. Users normally don't see such vectors in the wild, but they do come up when taking the common type of an unspecified vector with another identity value:
```{r}
vec_ptype2(NA, NA)
vec_ptype2(NA, NULL)
vec_ptype2(NULL, NA)
```
We can't return `NA` here because `vec_ptype2()` normally returns empty vectors. We also can't return `NULL` because unspecified vectors need to be recognised as logical vectors if they haven't been promoted at the end of the reduction.
```{r}
vec_ptype_finalise(vec_ptype2(NULL, NA))
```
See the output of `vec_ptype_common()` which performs the reduction and finalises the type, ready to be used by the caller:
```{r}
vec_ptype_common(NULL, NULL)
vec_ptype_common(NA, NULL)
```
Note that __partial__ types in vctrs make use of the same mechanism. They are finalised with `vec_ptype_finalise()`.
vctrs/man/faq/developer/ 0000755 0001762 0000144 00000000000 14511524374 014730 5 ustar ligges users vctrs/man/faq/developer/reference-compatibility.Rmd 0000644 0001762 0000144 00000005762 14276722575 022226 0 ustar ligges users
vctrs provides a framework for working with vector classes in a generic way. However, it implements several compatibility fallbacks to base R methods. In this reference you will find how vctrs tries to be compatible with your vector class, and what base methods you need to implement for compatibility.
If you're starting from scratch, we think you'll find it easier to start using [new_vctr()] as documented in `vignette("s3-vector")`. This guide is aimed for developers with existing vector classes.
## Aggregate operations with fallbacks
All vctrs operations are based on four primitive generics described in the next section. However there are many higher level operations. The most important ones implement fallbacks to base generics for maximum compatibility with existing classes.
- [vec_slice()] falls back to the base `[` generic if no [vec_proxy()] method is implemented. This way foreign classes that do not implement [vec_restore()] can restore attributes based on the new subsetted contents.
- [vec_c()] and [vec_rbind()] now fall back to [base::c()] if the inputs have a common parent class with a `c()` method (only if they have no self-to-self `vec_ptype2()` method).
vctrs works hard to make your `c()` method success in various situations (with `NULL` and `NA` inputs, even as first input which would normally prevent dispatch to your method). The main downside compared to using vctrs primitives is that you can't combine vectors of different classes since there is no extensible mechanism of coercion in `c()`, and it is less efficient in some cases.
## The vctrs primitives
Most functions in vctrs are aggregate operations: they call other vctrs functions which themselves call other vctrs functions. The dependencies of a vctrs functions are listed in the Dependencies section of its documentation page. Take a look at [vec_count()] for an example.
These dependencies form a tree whose leaves are the four vctrs primitives. Here is the diagram for `vec_count()`:
\\figure{vec-count-deps.png}
### The coercion generics
The coercion mechanism in vctrs is based on two generics:
- [vec_ptype2()]
- [vec_cast()]
See the [theory overview][theory-faq-coercion].
Two objects with the same class and the same attributes are always considered compatible by ptype2 and cast. If the attributes or classes differ, they throw an incompatible type error.
Coercion errors are the main source of incompatibility with vctrs. See the [howto guide][howto-faq-coercion] if you need to implement methods for these generics.
### The proxy and restoration generics
- [vec_proxy()]
- [vec_restore()]
These generics are essential for vctrs but mostly optional. `vec_proxy()` defaults to an [identity][identity] function and you normally don't need to implement it. The proxy a vector must be one of the atomic vector types, a list, or a data frame. By default, S3 lists that do not inherit from `"list"` do not have an identity proxy. In that case, you need to explicitly implement `vec_proxy()` or make your class inherit from list.
vctrs/man/faq/developer/snippet-roxy-workflow.Rmd 0000644 0001762 0000144 00000001224 14276722575 021737 0 ustar ligges users
To implement methods for generics, first import the generics in your namespace and redocument:
```{r, eval = FALSE}
#' @importFrom vctrs vec_ptype2 vec_cast
NULL
```
Note that for each batches of methods that you add to your package, you need to export the methods and redocument immediately, even during development. Otherwise they won't be in scope when you run unit tests e.g. with testthat.
Implementing double dispatch methods is very similar to implementing regular S3 methods. In these examples we are using roxygen2 tags to register the methods, but you can also register the methods manually in your NAMESPACE file or lazily with `s3_register()`.
vctrs/man/faq/developer/howto-faq-fix-scalar-type-error.Rmd 0000644 0001762 0000144 00000004451 14376223321 023437 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
```{r, include = FALSE}
stopifnot(rlang::is_installed("dplyr"))
```
The tidyverse is a bit stricter than base R regarding what kind of objects are considered as vectors (see the [user FAQ][faq-error-scalar-type] about this topic). Sometimes vctrs won't treat your class as a vector when it should.
## Why isn't my list class considered a vector?
By default, S3 lists are not considered to be vectors by vctrs:
```{r}
my_list <- structure(list(), class = "my_class")
vctrs::vec_is(my_list)
```
To be treated as a vector, the class must either inherit from `"list"` explicitly:
```{r}
my_explicit_list <- structure(list(), class = c("my_class", "list"))
vctrs::vec_is(my_explicit_list)
```
Or it should implement a `vec_proxy()` method that returns its input if explicit inheritance is not possible or troublesome:
```{r}
#' @export
vec_proxy.my_class <- function(x, ...) x
vctrs::vec_is(my_list)
```
Note that explicit inheritance is the preferred way because this makes it possible for your class to dispatch on `list` methods of S3 generics:
```{r, error = TRUE}
my_generic <- function(x) UseMethod("my_generic")
my_generic.list <- function(x) "dispatched!"
my_generic(my_list)
my_generic(my_explicit_list)
```
## Why isn't my data frame class considered a vector?
The most likely explanation is that the data frame has not been
properly constructed.
However, if you get an "Input must be a vector" error with a data frame subclass, it probably means that the data frame has not been properly constructed. The main cause of these errors are data frames whose _base class_ is not `"data.frame"`:
```{r, error = TRUE}
my_df <- data.frame(x = 1)
class(my_df) <- c("data.frame", "my_class")
vctrs::obj_check_vector(my_df)
```
This is problematic as many tidyverse functions won't work properly:
```{r, error = TRUE}
dplyr::slice(my_df, 1)
```
It is generally not appropriate to declare your class to be a superclass of another class. We generally consider this undefined behaviour (UB). To fix these errors, you can simply change the construction of your data frame class so that `"data.frame"` is a base class, i.e. it should come last in the class vector:
```{r}
class(my_df) <- c("my_class", "data.frame")
vctrs::obj_check_vector(my_df)
dplyr::slice(my_df, 1)
```
vctrs/man/faq/developer/howto-coercion.Rmd 0000644 0001762 0000144 00000021060 14511320530 020316 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
```{r, include = FALSE}
old_warn_on_fallback <- options(`vctrs:::warn_on_fallback` = FALSE)
knitr_defer(options(old_warn_on_fallback))
```
This guide illustrates how to implement `vec_ptype2()` and `vec_cast()` methods for existing classes. Related topics:
- For an overview of how these generics work and their roles in vctrs, see [`?theory-faq-coercion`][theory-faq-coercion].
- For an example of implementing coercion methods for data frame subclasses, see [`?howto-faq-coercion-data-frame`][howto-faq-coercion-data-frame].
- For a tutorial about implementing vctrs classes from scratch, see `vignette("s3-vector")`
## The natural number class
We'll illustrate how to implement coercion methods with a simple class that represents natural numbers. In this scenario we have an existing class that already features a constructor and methods for `print()` and subset.
```{r}
#' @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())
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("base::print", "my_natural")
knitr_local_registration("base::[", "my_natural")
```
```{r}
new_natural(1:3)
new_natural(c(1, NA))
```
## Roxygen workflow
```{r, child = "snippet-roxy-workflow.Rmd"}
```
## Implementing `vec_ptype2()`
### The self-self method
The first method to implement is the one that signals that your class is compatible with itself:
```{r}
#' @export
vec_ptype2.my_natural.my_natural <- function(x, y, ...) {
x
}
vec_ptype2(new_natural(1), new_natural(2:3))
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_ptype2", "my_natural.my_natural")
```
`vec_ptype2()` implements a fallback to try and be compatible with simple classes, so it may seem that you don't need to implement the self-self coercion method. However, you must implement it explicitly because this is how vctrs knows that a class that is implementing vctrs methods (for instance this disable fallbacks to `base::c()`). Also, it makes your class a bit more efficient.
### The parent and children methods
Our natural number class is conceptually a parent of `` and a child of ``, but the class is not compatible with logical, integer, or double vectors yet:
```{r, error = TRUE}
vec_ptype2(TRUE, new_natural(2:3))
vec_ptype2(new_natural(1), 2:3)
```
We'll specify the twin methods for each of these classes, returning the richer class in each case.
```{r}
#' @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
}
```
Between a natural number and an integer, the latter is the richer class:
```{r}
#' @export
vec_ptype2.my_natural.integer <- function(x, y, ...) {
y
}
#' @export
vec_ptype2.integer.my_natural <- function(x, y, ...) {
x
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_ptype2", "my_natural.logical")
knitr_local_registration("vctrs::vec_ptype2", "my_natural.integer")
knitr_local_registration("vctrs::vec_ptype2", "integer.my_natural")
knitr_local_registration("vctrs::vec_ptype2", "logical.my_natural")
```
We no longer get common type errors for logical and integer:
```{r}
vec_ptype2(TRUE, new_natural(2:3))
vec_ptype2(new_natural(1), 2:3)
```
We are not done yet. Pairwise coercion methods must be implemented for all the connected nodes in the coercion hierarchy, which include double vectors further up. The coercion methods for grand-parent types must be implemented separately:
```{r}
#' @export
vec_ptype2.my_natural.double <- function(x, y, ...) {
y
}
#' @export
vec_ptype2.double.my_natural <- function(x, y, ...) {
x
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_ptype2", "my_natural.double")
knitr_local_registration("vctrs::vec_ptype2", "double.my_natural")
```
### Incompatible attributes
Most of the time, inputs are incompatible because they have different classes for which no `vec_ptype2()` method is implemented. More rarely, inputs could be incompatible because of their attributes. In that case incompatibility is signalled by calling `stop_incompatible_type()`.
In the following example, we implement a self-self ptype2 method for a hypothetical subclass of `` that has stricter combination semantics. The method throws an error when the levels of the two factors are not compatible.
```{r, eval = FALSE}
#' @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
}
```
Note how the methods need to take `x_arg` and `y_arg` parameters and pass them on to `stop_incompatible_type()`. These argument tags help create more informative error messages when the common type determination is for a column of a data frame. They are part of the generic signature but can usually be left out if not used.
## Implementing `vec_cast()`
Corresponding `vec_cast()` methods must be implemented for all `vec_ptype2()` methods. The general pattern is to convert the argument `x` to the type of `to`. The methods should validate the values in `x` and make sure they conform to the values of `to`.
Please note that for historical reasons, the order of the classes in the method name is in reverse order of the arguments in the function signature. The first class represents `to`, whereas the second class represents `x`.
The self-self method is easy in this case, it just returns the target input:
```{r}
#' @export
vec_cast.my_natural.my_natural <- function(x, to, ...) {
x
}
```
The other types need to be validated. We perform input validation in the `new_natural()` constructor, so that's a good fit for our `vec_cast()` implementations.
```{r}
#' @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)
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_cast", "my_natural.my_natural")
knitr_local_registration("vctrs::vec_cast", "my_natural.logical")
knitr_local_registration("vctrs::vec_cast", "my_natural.integer")
knitr_local_registration("vctrs::vec_cast", "my_natural.double")
```
With these methods, vctrs is now able to combine logical and natural vectors. It properly returns the richer type of the two, a natural vector:
```{r}
vec_c(TRUE, new_natural(1), FALSE)
```
Because we haven't implemented conversions _from_ natural, it still doesn't know how to combine natural with the richer integer and double types:
```{r, error = TRUE}
vec_c(new_natural(1), 10L)
vec_c(1.5, new_natural(1))
```
This is quick work which completes the implementation of coercion methods for vctrs:
```{r}
#' @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)
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_cast", "logical.my_natural")
knitr_local_registration("vctrs::vec_cast", "integer.my_natural")
knitr_local_registration("vctrs::vec_cast", "double.my_natural")
```
And we now get the expected combinations.
```{r}
vec_c(new_natural(1), 10L)
vec_c(1.5, new_natural(1))
```
vctrs/man/faq/developer/theory-recycling.Rmd 0000644 0001762 0000144 00000003217 14511524374 020666 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
Recycling describes the concept of repeating elements of one vector to match the size of another. There are two rules that underlie the "tidyverse" recycling rules:
- Vectors of size 1 will be recycled to the size of any other vector
- Otherwise, all vectors must have the same size
# Examples
```{r, warning = FALSE, message = FALSE, include = FALSE}
library(tibble)
```
Vectors of size 1 are recycled to the size of any other vector:
```{r}
tibble(x = 1:3, y = 1L)
```
This includes vectors of size 0:
```{r}
tibble(x = integer(), y = 1L)
```
If vectors aren't size 1, they must all be the same size. Otherwise, an error is thrown:
```{r, error = TRUE}
tibble(x = 1:3, y = 4:7)
```
# vctrs backend
Packages in r-lib and the tidyverse generally use [vec_size_common()] and [vec_recycle_common()] as the backends for handling recycling rules.
- `vec_size_common()` returns the common size of multiple vectors, after applying the recycling rules
- `vec_recycle_common()` goes one step further, and actually recycles the vectors to their common size
```{r, error = TRUE}
vec_size_common(1:3, "x")
vec_recycle_common(1:3, "x")
vec_size_common(1:3, c("x", "y"))
```
# Base R recycling rules
The recycling rules described here are stricter than the ones generally used by base R, which are:
- If any vector is length 0, the output will be length 0
- Otherwise, the output will be length `max(length_x, length_y)`, and a warning will be thrown if the length of the longer vector is not an integer multiple of the length of the shorter vector.
We explore the base R rules in detail in `vignette("type-size")`.
vctrs/man/faq/developer/theory-coercion.Rmd 0000644 0001762 0000144 00000024000 14315060307 020472 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
This is an overview of the usage of `vec_ptype2()` and `vec_cast()` and their role in the vctrs coercion mechanism. Related topics:
- For an example of implementing coercion methods for simple vectors, see [`?howto-faq-coercion`][howto-faq-coercion].
- For an example of implementing coercion methods for data frame subclasses, see [`?howto-faq-coercion-data-frame`][howto-faq-coercion-data-frame].
- For a tutorial about implementing vctrs classes from scratch, see `vignette("s3-vector")`.
## Combination mechanism in vctrs
The coercion system in vctrs is designed to make combination of multiple inputs consistent and extensible. Combinations occur in many places, such as row-binding, joins, subset-assignment, or grouped summary functions that use the split-apply-combine strategy. For example:
```{r, error = TRUE}
vec_c(TRUE, 1)
vec_c("a", 1)
vec_rbind(
data.frame(x = TRUE),
data.frame(x = 1, y = 2)
)
vec_rbind(
data.frame(x = "a"),
data.frame(x = 1, y = 2)
)
```
One major goal of vctrs is to provide a central place for implementing the coercion methods that make generic combinations possible. The two relevant generics are `vec_ptype2()` and `vec_cast()`. They both take two arguments and perform __double dispatch__, meaning that a method is selected based on the classes of both inputs.
The general mechanism for combining multiple inputs is:
1. Find the common type of a set of inputs by reducing (as in `base::Reduce()` or `purrr::reduce()`) the `vec_ptype2()` binary function over the set.
2. Convert all inputs to the common type with `vec_cast()`.
3. Initialise the output vector as an instance of this common type with `vec_init()`.
4. Fill the output vector with the elements of the inputs using `vec_assign()`.
The last two steps may require `vec_proxy()` and `vec_restore()` implementations, unless the attributes of your class are constant and do not depend on the contents of the vector. We focus here on the first two steps, which require `vec_ptype2()` and `vec_cast()` implementations.
## `vec_ptype2()`
Methods for `vec_ptype2()` are passed two _prototypes_, i.e. two inputs emptied of their elements. They implement two behaviours:
* If the types of their inputs are compatible, indicate which of them is the richer type by returning it. If the types are of equal resolution, return any of the two.
* Throw an error with `stop_incompatible_type()` when it can be determined from the attributes that the types of the inputs are not compatible.
### Type compatibility
A type is __compatible__ with another type if the values it represents are a subset or a superset of the values of the other type. The notion of "value" is to be interpreted at a high level, in particular it is not the same as the memory representation. For example, factors are represented in memory with integers but their values are more related to character vectors than to round numbers:
```{r, error = TRUE}
# Two factors are compatible
vec_ptype2(factor("a"), factor("b"))
# Factors are compatible with a character
vec_ptype2(factor("a"), "b")
# But they are incompatible with integers
vec_ptype2(factor("a"), 1L)
```
### Richness of type
Richness of type is not a very precise notion. It can be about richer data (for instance a `double` vector covers more values than an integer vector), richer behaviour (a `data.table` has richer behaviour than a `data.frame`), or both. If you have trouble determining which one of the two types is richer, it probably means they shouldn't be automatically coercible.
Let's look again at what happens when we combine a factor and a character:
```{r}
vec_ptype2(factor("a"), "b")
```
The ptype2 method for `` and `>` returns `` because the former is a richer type. The factor can only contain `"a"` strings, whereas the character can contain any strings. In this sense, factors are a _subset_ of character.
Note that another valid behaviour would be to throw an incompatible type error. This is what a strict factor implementation would do. We have decided to be laxer in vctrs because it is easy to inadvertently create factors instead of character vectors, especially with older versions of R where `stringsAsFactors` is still true by default.
### Consistency and symmetry on permutation
Each ptype2 method should strive to have exactly the same behaviour when the inputs are permuted. This is not always possible, for example factor levels are aggregated in order:
```{r}
vec_ptype2(factor(c("a", "c")), factor("b"))
vec_ptype2(factor("b"), factor(c("a", "c")))
```
In any case, permuting the input should not return a fundamentally different type or introduce an incompatible type error.
### Coercion hierarchy
The classes that you can coerce together form a coercion (or subtyping) hierarchy. Below is a schema of the hierarchy for the base types like integer and factor. In this diagram the directions of the arrows express which type is richer. They flow from the bottom (more constrained types) to the top (richer types).
\\figure{coerce.png}
A coercion hierarchy is distinct from the structural hierarchy implied by memory types and classes. For instance, in a structural hierarchy, factors are built on top of integers. But in the coercion hierarchy they are more related to character vectors. Similarly, subclasses are not necessarily coercible with their superclasses because the coercion and structural hierarchies are separate.
### Implementing a coercion hierarchy
As a class implementor, you have two options. The simplest is to create an entirely separate hierarchy. The date and date-time classes are an example of an S3-based hierarchy that is completely separate. Alternatively, you can integrate your class in an existing hierarchy, typically by adding parent nodes on top of the hierarchy (your class is richer), by adding children node at the root of the hierarchy (your class is more constrained), or by inserting a node in the tree.
These coercion hierarchies are _implicit_, in the sense that they are implied by the `vec_ptype2()` implementations. There is no structured way to create or modify a hierarchy, instead you need to implement the appropriate coercion methods for all the types in your hierarchy, and diligently return the richer type in each case. The `vec_ptype2()` implementations are not transitive nor inherited, so all pairwise methods between classes lying on a given path must be implemented manually. This is something we might make easier in the future.
## `vec_cast()`
The second generic, `vec_cast()`, is the one that looks at the data and actually performs the conversion. Because it has access to more information than `vec_ptype2()`, it may be stricter and cause an error in more cases. `vec_cast()` has three possible behaviours:
- Determine that the prototypes of the two inputs are not compatible. This must be decided in exactly the same way as for `vec_ptype2()`. Call `stop_incompatible_cast()` if you can determine from the attributes that the types are not compatible.
- Detect incompatible values. Usually this is because the target type is too restricted for the values supported by the input type. For example, a fractional number can't be converted to an integer. The method should throw an error in that case.
- Return the input vector converted to the target type if all values are compatible. Whereas `vec_ptype2()` must return the same type when the inputs are permuted, `vec_cast()` is _directional_. It always returns the type of the right-hand side, or dies trying.
## Double dispatch
The dispatch mechanism for `vec_ptype2()` and `vec_cast()` looks like S3 but is actually a custom mechanism. Compared to S3, it has the following differences:
* It dispatches on the classes of the first two inputs.
* There is no inheritance of ptype2 and cast methods. This is because the S3 class hierarchy is not necessarily the same as the coercion hierarchy.
* `NextMethod()` does not work. Parent methods must be called explicitly if necessary.
* The default method is hard-coded.
## Data frames
The determination of the common type of data frames with `vec_ptype2()` happens in three steps:
1. Match the columns of the two input data frames. If some columns don't exist, they are created and filled with adequately typed `NA` values.
2. Find the common type for each column by calling `vec_ptype2()` on each pair of matched columns.
3. Find the common data frame type. For example the common type of a grouped tibble and a tibble is a grouped tibble because the latter is the richer type. The common type of a data table and a data frame is a data table.
`vec_cast()` operates similarly. If a data frame is cast to a target type that has fewer columns, this is an error.
If you are implementing coercion methods for data frames, you will need to explicitly call the parent methods that perform the common type determination or the type conversion described above. These are exported as [df_ptype2()] and [df_cast()].
### Data frame fallbacks
Being too strict with data frame combinations would cause too much pain because there are many data frame subclasses in the wild that don't implement vctrs methods. We have decided to implement a special fallback behaviour for foreign data frames. Incompatible data frames fall back to a base data frame:
```{r}
df1 <- data.frame(x = 1)
df2 <- structure(df1, class = c("foreign_df", "data.frame"))
vec_rbind(df1, df2)
```
When a tibble is involved, we fall back to tibble:
```{r}
df3 <- tibble::as_tibble(df1)
vec_rbind(df1, df3)
```
These fallbacks are not ideal but they make sense because all data frames share a common data structure. This is not generally the case for vectors. For example factors and characters have different representations, and it is not possible to find a fallback time mechanically.
However this fallback has a big downside: implementing vctrs methods for your data frame subclass is a breaking behaviour change. The proper coercion behaviour for your data frame class should be specified as soon as possible to limit the consequences of changing the behaviour of your class in R scripts.
vctrs/man/faq/developer/howto-coercion-data-frame.Rmd 0000644 0001762 0000144 00000030517 14276722575 022353 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
```{r, include = FALSE}
old_warn_on_fallback <- options(`vctrs:::warn_on_fallback` = FALSE)
knitr_defer(options(old_warn_on_fallback))
```
This guide provides a practical recipe for implementing `vec_ptype2()` and `vec_cast()` methods for coercions of data frame subclasses. Related topics:
- For an overview of the coercion mechanism in vctrs, see [`?theory-faq-coercion`][theory-faq-coercion].
- For an example of implementing coercion methods for simple vectors, see [`?howto-faq-coercion`][howto-faq-coercion].
Coercion of data frames occurs when different data frame classes are combined in some way. The two main methods of combination are currently row-binding with [vec_rbind()] and col-binding with [vec_cbind()] (which are in turn used by a number of dplyr and tidyr functions). These functions take multiple data frame inputs and automatically coerce them to their common type.
vctrs is generally strict about the kind of automatic coercions that are performed when combining inputs. In the case of data frames we have decided to be a bit less strict for convenience. Instead of throwing an incompatible type error, we fall back to a base data frame or a tibble if we don't know how to combine two data frame subclasses. It is still a good idea to specify the proper coercion behaviour for your data frame subclasses as soon as possible.
We will see two examples in this guide. The first example is about a data frame subclass that has no particular attributes to manage. In the second example, we implement coercion methods for a tibble subclass that includes potentially incompatible attributes.
## Roxygen workflow
```{r, child = "snippet-roxy-workflow.Rmd"}
```
## Parent methods
Most of the common type determination should be performed by the parent class. In vctrs, double dispatch is implemented in such a way that you need to call the methods for the parent class manually. For `vec_ptype2()` this means you need to call `df_ptype2()` (for data frame subclasses) or `tib_ptype2()` (for tibble subclasses). Similarly, `df_cast()` and `tib_cast()` are the workhorses for `vec_cast()` methods of subtypes of `data.frame` and `tbl_df`. These functions take the union of the columns in `x` and `y`, and ensure shared columns have the same type.
These functions are much less strict than `vec_ptype2()` and `vec_cast()` as they accept any subclass of data frame as input. They always return a `data.frame` or a `tbl_df`. You will probably want to write similar functions for your subclass to avoid repetition in your code. You may want to export them as well if you are expecting other people to derive from your class.
## A `data.table` example
```{r, include = FALSE}
delayedAssign("as.data.table", {
if (is_installed("data.table")) {
env_get(ns_env("data.table"), "as.data.table")
} else {
function(...) abort("`data.table` must be installed.")
}
})
delayedAssign("data.table", {
if (is_installed("data.table")) {
env_get(ns_env("data.table"), "data.table")
} else {
function(...) abort("`data.table` must be installed.")
}
})
```
This example is the actual implementation of vctrs coercion methods for `data.table`. This is a simple example because we don't have to keep track of attributes for this class or manage incompatibilities. See the tibble section for a more complicated example.
We first create the `dt_ptype2()` and `dt_cast()` helpers. They wrap around the parent methods `df_ptype2()` and `df_cast()`, and transform the common type or converted input to a data table. You may want to export these helpers if you expect other packages to derive from your data frame class.
These helpers should always return data tables. To this end we use the conversion generic `as.data.table()`. Depending on the tools available for the particular class at hand, a constructor might be appropriate as well.
```{r}
dt_ptype2 <- function(x, y, ...) {
as.data.table(df_ptype2(x, y, ...))
}
dt_cast <- function(x, to, ...) {
as.data.table(df_cast(x, to, ...))
}
```
We start with the self-self method:
```{r}
#' @export
vec_ptype2.data.table.data.table <- function(x, y, ...) {
dt_ptype2(x, y, ...)
}
```
Between a data frame and a data table, we consider the richer type to be data table. This decision is not based on the value coverage of each data structures, but on the idea that data tables have richer behaviour. Since data tables are the richer type, we call `dt_type2()` from the `vec_ptype2()` method. It always returns a data table, no matter the order of arguments:
```{r}
#' @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, ...)
}
```
The `vec_cast()` methods follow the same pattern, but note how the method for coercing to data frame uses `df_cast()` rather than `dt_cast()`.
Also, please note that for historical reasons, the order of the classes in the method name is in reverse order of the arguments in the function signature. The first class represents `to`, whereas the second class represents `x`.
```{r}
#' @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, ...)
}
```
With these methods vctrs is now able to combine data tables with data frames:
```{r}
vec_cbind(data.frame(x = 1:3), data.table(y = "foo"))
```
## A tibble example
In this example we implement coercion methods for a tibble subclass that carries a colour as a scalar metadata:
```{r}
# 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])
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("base::print", "my_tibble")
```
This subclass is very simple. All it does is modify the header.
```{r}
red <- my_tibble("red", x = 1, y = 1:2)
red
red[2]
green <- my_tibble("green", z = TRUE)
green
```
Combinations do not work properly out of the box, instead vctrs falls back to a bare tibble:
```{r}
vec_rbind(red, tibble::tibble(x = 10:12))
```
Instead of falling back to a data frame, we would like to return a `` when combined with a data frame or a tibble. Because this subclass has more metadata than normal data frames (it has a colour), it is a _supertype_ of tibble and data frame, i.e. it is the richer type. This is similar to how a grouped tibble is a more general type than a tibble or a data frame. Conceptually, the latter are pinned to a single constant group.
The coercion methods for data frames operate in two steps:
- They check for compatible subclass attributes. In our case the tibble colour has to be the same, or be undefined.
- They call their parent methods, in this case [tib_ptype2()] and [tib_cast()] because we have a subclass of tibble. This eventually calls the data frame methods [df_ptype2()] and [tib_ptype2()] which match the columns and their types.
This process should usually be wrapped in two functions to avoid repetition. Consider exporting these if you expect your class to be derived by other subclasses.
We first implement a helper to determine if two data frames have compatible colours. We use the `df_colour()` accessor which returns `NULL` when the data frame colour is undefined.
```{r}
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)
}
```
Next we implement the coercion helpers. If the colours are not compatible, we call `stop_incompatible_cast()` or `stop_incompatible_type()`. These strict coercion semantics are justified because in this class colour is a _data_ attribute. If it were a non essential _detail_ attribute, like the timezone in a datetime, we would just standardise it to the value of the left-hand side.
In simpler cases (like the data.table example), these methods do not need to take the arguments suffixed in `_arg`. Here we do need to take these arguments so we can pass them to the `stop_` functions when we detect an incompatibility. They also should be passed to the parent methods.
```{r}
#' @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)
}
```
Let's now implement the coercion methods, starting with the self-self methods.
```{r}
#' @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, ...)
}
```
```{r, include = FALSE}
knitr_local_registration("vctrs::vec_ptype2", "my_tibble.my_tibble")
knitr_local_registration("vctrs::vec_cast", "my_tibble.my_tibble")
```
We can now combine compatible instances of our class!
```{r, error = TRUE}
vec_rbind(red, red)
vec_rbind(green, green)
vec_rbind(green, red)
```
The methods for combining our class with tibbles follow the same pattern. For ptype2 we return our class in both cases because it is the richer type:
```{r}
#' @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, ...)
}
```
For cast are careful about returning a tibble when casting to a tibble. Note the call to `vctrs::tib_cast()`:
```{r}
#' @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, ...)
}
```
```{r, include = FALSE}
knitr_local_registration("vctrs::vec_ptype2", "my_tibble.tbl_df")
knitr_local_registration("vctrs::vec_ptype2", "tbl_df.my_tibble")
knitr_local_registration("vctrs::vec_cast", "tbl_df.my_tibble")
knitr_local_registration("vctrs::vec_cast", "my_tibble.tbl_df")
```
From this point, we get correct combinations with tibbles:
```{r}
vec_rbind(red, tibble::tibble(x = 10:12))
```
However we are not done yet. Because the coercion hierarchy is different from the class hierarchy, there is no inheritance of coercion methods. We're not getting correct behaviour for data frames yet because we haven't explicitly specified the methods for this class:
```{r}
vec_rbind(red, data.frame(x = 10:12))
```
Let's finish up the boiler plate:
```{r}
#' @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, ...)
}
```
```{r, include = FALSE}
# Necessary because includeRmd() evaluated in a child of global
knitr_local_registration("vctrs::vec_ptype2", "my_tibble.data.frame")
knitr_local_registration("vctrs::vec_ptype2", "data.frame.my_tibble")
knitr_local_registration("vctrs::vec_cast", "my_tibble.data.frame")
knitr_local_registration("vctrs::vec_cast", "data.frame.my_tibble")
```
This completes the implementation:
```{r}
vec_rbind(red, data.frame(x = 10:12))
```
vctrs/man/faq/developer/links-coercion.Rmd 0000644 0001762 0000144 00000001007 14276722575 020324 0 ustar ligges users
# Implementing coercion methods
- For an overview of how these generics work and their roles in vctrs, see [`?theory-faq-coercion`][theory-faq-coercion].
- For an example of implementing coercion methods for simple vectors, see [`?howto-faq-coercion`][howto-faq-coercion].
- For an example of implementing coercion methods for data frame subclasses, see [`?howto-faq-coercion-data-frame`][howto-faq-coercion-data-frame].
- For a tutorial about implementing vctrs classes from scratch, see `vignette("s3-vector")`.
vctrs/man/faq/user/ 0000755 0001762 0000144 00000000000 14315060307 013712 5 ustar ligges users vctrs/man/faq/user/faq-error-scalar-type.Rmd 0000644 0001762 0000144 00000003053 14315060307 020477 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
This error occurs when a function expects a vector and gets a scalar object instead. This commonly happens when some code attempts to assign a scalar object as column in a data frame:
```{r, error = TRUE}
fn <- function() NULL
tibble::tibble(x = fn)
fit <- lm(1:3 ~ 1)
tibble::tibble(x = fit)
```
# Vectorness in base R and in the tidyverse
In base R, almost everything is a vector or behaves like a vector. In the tidyverse we have chosen to be a bit stricter about what is considered a vector. The main question we ask ourselves to decide on the vectorness of a type is whether it makes sense to include that object as a column in a data frame.
The main difference is that S3 lists are considered vectors by base R but in the tidyverse that's not the case by default:
```{r, error = TRUE}
fit <- lm(1:3 ~ 1)
typeof(fit)
class(fit)
# S3 lists can be subset like a vector using base R:
fit[c(1, 4)]
# But not in vctrs
vctrs::vec_slice(fit, c(1, 4))
```
Defused function calls are another (more esoteric) example:
```{r, error = TRUE}
call <- quote(foo(bar = TRUE, baz = FALSE))
call
# They can be subset like a vector using base R:
call[1:2]
lapply(call, function(x) x)
# But not with vctrs:
vctrs::vec_slice(call, 1:2)
```
# I get a scalar type error but I think this is a bug
It's possible the author of the class needs to do some work to declare their class a vector. Consider reaching out to the author. We have written a [developer FAQ page][howto-faq-fix-scalar-type-error] to help them fix the issue.
vctrs/man/faq/user/faq-compatibility-types.Rmd 0000644 0001762 0000144 00000005176 14276722575 021171 0 ustar ligges users
```{r, child = "../setup.Rmd", include = FALSE}
```
```{r, include = FALSE}
stopifnot(rlang::is_installed("dplyr"))
```
Two vectors are __compatible__ when you can safely:
- Combine them into one larger vector.
- Assign values from one of the vectors into the other vector.
Examples of compatible types are integer and double vectors. On the other hand, integer and character vectors are not compatible.
# Common type of multiple vectors
There are two possible outcomes when multiple vectors of different types are combined into a larger vector:
- An incompatible type error is thrown because some of the types are not compatible:
```{r, error = TRUE}
df1 <- data.frame(x = 1:3)
df2 <- data.frame(x = "foo")
dplyr::bind_rows(df1, df2)
```
- The vectors are combined into a vector that has the common type of all inputs. In this example, the common type of integer and logical is integer:
```{r}
df1 <- data.frame(x = 1:3)
df2 <- data.frame(x = FALSE)
dplyr::bind_rows(df1, df2)
```
In general, the common type is the _richer_ type, in other words the type that can represent the most values. Logical vectors are at the bottom of the hierarchy of numeric types because they can only represent two values (not counting missing values). Then come integer vectors, and then doubles. Here is the vctrs type hierarchy for the fundamental vectors:
\\figure{coerce.png}
# Type conversion and lossy cast errors
Type compatibility does not necessarily mean that you can __convert__ one type to the other type. That's because one of the types might support a larger set of possible values. For instance, integer and double vectors are compatible, but double vectors can't be converted to integer if they contain fractional values.
When vctrs can't convert a vector because the target type is not as rich as the source type, it throws a lossy cast error. Assigning a fractional number to an integer vector is a typical example of a lossy cast error:
```{r, error = TRUE}
int_vector <- 1:3
vec_assign(int_vector, 2, 0.001)
```
# How to make two vector classes compatible?
If you encounter two vector types that you think should be compatible, they might need to implement coercion methods. Reach out to the author(s) of the classes and ask them if it makes sense for their classes to be compatible.
These developer FAQ items provide guides for implementing coercion methods:
- For an example of implementing coercion methods for simple vectors, see [`?howto-faq-coercion`][howto-faq-coercion].
- For an example of implementing coercion methods for data frame subclasses, see [`?howto-faq-coercion-data-frame`][howto-faq-coercion-data-frame].
vctrs/man/faq/setup.Rmd 0000644 0001762 0000144 00000000261 14315060307 014537 0 ustar ligges users
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
options(
cli.unicode = FALSE,
rlang_call_format_srcrefs = FALSE
)
library(vctrs)
```
vctrs/man/vec_is_list.Rd 0000644 0001762 0000144 00000002440 14401377400 014760 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_is_list}
\alias{vec_is_list}
\alias{vec_check_list}
\title{List checks}
\usage{
vec_is_list(x)
vec_check_list(x, ..., arg = caller_arg(x), call = caller_env())
}
\arguments{
\item{x}{For \verb{vec_*()} functions, an object. For \verb{list_*()} functions, a
list.}
\item{...}{These dots are for future extensions and must be empty.}
\item{arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
These functions have been deprecated as of vctrs 0.6.0.
\itemize{
\item \code{vec_is_list()} has been renamed to \code{\link[=obj_is_list]{obj_is_list()}}.
\item \code{vec_check_list()} has been renamed to \code{\link[=obj_check_list]{obj_check_list()}}.
}
}
\keyword{internal}
vctrs/man/vec_unchop.Rd 0000644 0001762 0000144 00000006241 14402367170 014615 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_unchop}
\alias{vec_unchop}
\title{Chopping}
\usage{
vec_unchop(
x,
indices = NULL,
ptype = NULL,
name_spec = NULL,
name_repair = c("minimal", "unique", "check_unique", "universal")
)
}
\arguments{
\item{x}{A vector}
\item{indices}{For \code{vec_chop()}, a list of positive integer vectors to
slice \code{x} with, or \code{NULL}. Can't be used if \code{sizes} is already specified.
If both \code{indices} and \code{sizes} are \code{NULL}, \code{x} is split into its individual
elements, equivalent to using an \code{indices} of \code{as.list(vec_seq_along(x))}.
For \code{list_unchop()}, a list of positive integer vectors specifying the
locations to place elements of \code{x} in. Each element of \code{x} is recycled to
the size of the corresponding index vector. The size of \code{indices} must
match the size of \code{x}. If \code{NULL}, \code{x} is combined in the order it is
provided in, which is equivalent to using \code{\link[=vec_c]{vec_c()}}.}
\item{ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{x}. Alternatively, you
can supply \code{ptype} to give the output a known type.}
\item{name_spec}{A name specification for combining
inner and outer names. This is relevant for inputs passed with a
name, when these inputs are themselves named, like \code{outer = c(inner = 1)}, or when they have length greater than 1: \code{outer = 1:2}. By default, these cases trigger an error. You can resolve
the error by providing a specification that describes how to
combine the names or the indices of the inner vector with the
name of the input. This specification can be:
\itemize{
\item A function of two arguments. The outer name is passed as a
string to the first argument, and the inner names or positions
are passed as second argument.
\item An anonymous function as a purrr-style formula.
\item A glue specification of the form \code{"{outer}_{inner}"}.
\item An \code{\link[rlang:zap]{rlang::zap()}} object, in which case both outer and inner
names are ignored and the result is unnamed.
}
See the \link[=name_spec]{name specification topic}.}
\item{name_repair}{How to repair names, see \code{repair} options in
\code{\link[=vec_as_names]{vec_as_names()}}.}
}
\value{
\itemize{
\item \code{vec_chop()}: A list where each element has the same type as \code{x}. The size
of the list is equal to \code{vec_size(indices)}, \code{vec_size(sizes)}, or
\code{vec_size(x)} depending on whether or not \code{indices} or \code{sizes} is provided.
\item \code{list_unchop()}: A vector of type \code{vec_ptype_common(!!!x)}, or \code{ptype}, if
specified. The size is computed as \code{vec_size_common(!!!indices)} unless
the indices are \code{NULL}, in which case the size is \code{vec_size_common(!!!x)}.
}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
\code{vec_unchop()} has been renamed to \code{\link[=list_unchop]{list_unchop()}} and is deprecated as of
vctrs 0.5.0.
}
\keyword{internal}
vctrs/man/vctrs-conditions.Rd 0000644 0001762 0000144 00000005644 14512002263 015771 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/conditions.R
\name{vctrs-conditions}
\alias{vctrs-conditions}
\alias{stop_incompatible_type}
\alias{stop_incompatible_cast}
\alias{stop_incompatible_op}
\alias{stop_incompatible_size}
\alias{allow_lossy_cast}
\title{Custom conditions for vctrs package}
\usage{
stop_incompatible_type(
x,
y,
...,
x_arg,
y_arg,
action = c("combine", "convert"),
details = NULL,
message = NULL,
class = NULL,
call = caller_env()
)
stop_incompatible_cast(
x,
to,
...,
x_arg,
to_arg,
details = NULL,
message = NULL,
class = NULL,
call = caller_env()
)
stop_incompatible_op(
op,
x,
y,
details = NULL,
...,
message = NULL,
class = NULL,
call = caller_env()
)
stop_incompatible_size(
x,
y,
x_size,
y_size,
...,
x_arg,
y_arg,
details = NULL,
message = NULL,
class = NULL,
call = caller_env()
)
allow_lossy_cast(expr, x_ptype = NULL, to_ptype = NULL)
}
\arguments{
\item{x, y, to}{Vectors}
\item{..., class}{Only use these fields when creating a subclass.}
\item{x_arg, y_arg, to_arg}{Argument names for \code{x}, \code{y}, and \code{to}. Used in
error messages to inform the user about the locations of incompatible
types.}
\item{action}{An option to customize the incompatible type message depending
on the context. Errors thrown from \code{\link[=vec_ptype2]{vec_ptype2()}} use \code{"combine"} and
those thrown from \code{\link[=vec_cast]{vec_cast()}} use \code{"convert"}.}
\item{details}{Any additional human readable details.}
\item{message}{An overriding message for the error. \code{details} and
\code{message} are mutually exclusive, supplying both is an error.}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{x_ptype, to_ptype}{Suppress only the casting errors where \code{x}
or \code{to} match these \link[=vec_ptype]{prototypes}.}
}
\value{
\verb{stop_incompatible_*()} unconditionally raise an error of class
\code{"vctrs_error_incompatible_*"} and \code{"vctrs_error_incompatible"}.
}
\description{
These functions are called for their side effect of raising
errors and warnings.
These conditions have custom classes and structures to make
testing easier.
}
\examples{
# Most of the time, `maybe_lossy_cast()` returns its input normally:
maybe_lossy_cast(
c("foo", "bar"),
NA,
"",
lossy = c(FALSE, FALSE),
x_arg = "",
to_arg = ""
)
# If `lossy` has any `TRUE`, an error is thrown:
try(maybe_lossy_cast(
c("foo", "bar"),
NA,
"",
lossy = c(FALSE, TRUE),
x_arg = "",
to_arg = ""
))
# Unless lossy casts are allowed:
allow_lossy_cast(
maybe_lossy_cast(
c("foo", "bar"),
NA,
"",
lossy = c(FALSE, TRUE),
x_arg = "",
to_arg = ""
)
)
}
\keyword{internal}
vctrs/man/vec_c.Rd 0000644 0001762 0000144 00000007736 14362266120 013553 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/c.R
\name{vec_c}
\alias{vec_c}
\title{Combine many vectors into one vector}
\usage{
vec_c(
...,
.ptype = NULL,
.name_spec = NULL,
.name_repair = c("minimal", "unique", "check_unique", "universal", "unique_quiet",
"universal_quiet"),
.error_arg = "",
.error_call = current_env()
)
}
\arguments{
\item{...}{Vectors to coerce.}
\item{.ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{...}.
Alternatively, you can supply \code{.ptype} to give the output known type.
If \code{getOption("vctrs.no_guessing")} is \code{TRUE} you must supply this value:
this is a convenient way to make production code demand fixed types.}
\item{.name_spec}{A name specification for combining
inner and outer names. This is relevant for inputs passed with a
name, when these inputs are themselves named, like \code{outer = c(inner = 1)}, or when they have length greater than 1: \code{outer = 1:2}. By default, these cases trigger an error. You can resolve
the error by providing a specification that describes how to
combine the names or the indices of the inner vector with the
name of the input. This specification can be:
\itemize{
\item A function of two arguments. The outer name is passed as a
string to the first argument, and the inner names or positions
are passed as second argument.
\item An anonymous function as a purrr-style formula.
\item A glue specification of the form \code{"{outer}_{inner}"}.
\item An \code{\link[rlang:zap]{rlang::zap()}} object, in which case both outer and inner
names are ignored and the result is unnamed.
}
See the \link[=name_spec]{name specification topic}.}
\item{.name_repair}{How to repair names, see \code{repair} options in
\code{\link[=vec_as_names]{vec_as_names()}}.}
\item{.error_arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
\item{.error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\value{
A vector with class given by \code{.ptype}, and length equal to the
sum of the \code{vec_size()} of the contents of \code{...}.
The vector will have names if the individual components have names
(inner names) or if the arguments are named (outer names). If both
inner and outer names are present, an error is thrown unless a
\code{.name_spec} is provided.
}
\description{
Combine all arguments into a new vector of common type.
}
\section{Invariants}{
\itemize{
\item \code{vec_size(vec_c(x, y)) == vec_size(x) + vec_size(y)}
\item \code{vec_ptype(vec_c(x, y)) == vec_ptype_common(x, y)}.
}
}
\section{Dependencies}{
\subsection{vctrs dependencies}{
\itemize{
\item \code{\link[=vec_cast_common]{vec_cast_common()}} with fallback
\item \code{\link[=vec_proxy]{vec_proxy()}}
\item \code{\link[=vec_restore]{vec_restore()}}
}
}
\subsection{base dependencies}{
\itemize{
\item \code{\link[base:c]{base::c()}}
}
If inputs inherit from a common class hierarchy, \code{vec_c()} falls
back to \code{base::c()} if there exists a \code{c()} method implemented for
this class hierarchy.
}
}
\examples{
vec_c(FALSE, 1L, 1.5)
# Date/times --------------------------
c(Sys.Date(), Sys.time())
c(Sys.time(), Sys.Date())
vec_c(Sys.Date(), Sys.time())
vec_c(Sys.time(), Sys.Date())
# Factors -----------------------------
c(factor("a"), factor("b"))
vec_c(factor("a"), factor("b"))
# By default, named inputs must be length 1:
vec_c(name = 1)
try(vec_c(name = 1:3))
# Pass a name specification to work around this:
vec_c(name = 1:3, .name_spec = "{outer}_{inner}")
# See `?name_spec` for more examples of name specifications.
}
\seealso{
\code{\link[=vec_cbind]{vec_cbind()}}/\code{\link[=vec_rbind]{vec_rbind()}} for combining data frames by rows
or columns.
}
vctrs/man/vec_empty.Rd 0000644 0001762 0000144 00000000753 14276722575 014476 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_empty}
\alias{vec_empty}
\title{Is a vector empty}
\usage{
vec_empty(x)
}
\arguments{
\item{x}{An object.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#defunct}{\figure{lifecycle-defunct.svg}{options: alt='[Defunct]'}}}{\strong{[Defunct]}}
This function is defunct, please use \code{\link[=vec_is_empty]{vec_is_empty()}}.
}
\keyword{internal}
vctrs/man/internal-faq-ptype2-identity.Rd 0000644 0001762 0000144 00000010363 14315060307 020110 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-internal.R
\name{internal-faq-ptype2-identity}
\alias{internal-faq-ptype2-identity}
\title{Internal FAQ - \code{vec_ptype2()}, \code{NULL}, and unspecified vectors}
\description{
\subsection{Promotion monoid}{
Promotions (i.e. automatic coercions) should always transform inputs to
their richer type to avoid losing values of precision. \code{vec_ptype2()}
returns the \emph{richer} type of two vectors, or throws an incompatible type
error if none of the two vector types include the other. For example,
the richer type of integer and double is the latter because double
covers a larger range of values than integer.
\code{vec_ptype2()} is a \href{https://en.wikipedia.org/wiki/Monoid}{monoid} over
vectors, which in practical terms means that it is a well behaved
operation for
\href{https://purrr.tidyverse.org/reference/reduce.html}{reduction}.
Reduction is an important operation for promotions because that is how
the richer type of multiple elements is computed. As a monoid,
\code{vec_ptype2()} needs an identity element, i.e. a value that doesn’t
change the result of the reduction. vctrs has two identity values,
\code{NULL} and \strong{unspecified} vectors.
}
\subsection{The \code{NULL} identity}{
As an identity element that shouldn’t influence the determination of the
common type of a set of vectors, \code{NULL} is promoted to any type:
\if{html}{\out{}}\preformatted{vec_ptype2(NULL, "")
#> character(0)
vec_ptype2(1L, NULL)
#> integer(0)
}\if{html}{\out{
}}
The common type of \code{NULL} and \code{NULL} is the identity \code{NULL}:
\if{html}{\out{}}\preformatted{vec_ptype2(NULL, NULL)
#> NULL
}\if{html}{\out{
}}
This way the result of \code{vec_ptype2(NULL, NULL)} does not influence
subsequent promotions:
\if{html}{\out{}}\preformatted{vec_ptype2(
vec_ptype2(NULL, NULL),
""
)
#> character(0)
}\if{html}{\out{
}}
}
\subsection{Unspecified vectors}{
In the vctrs coercion system, logical vectors of missing values are also
automatically promoted to the type of any other vector, just like
\code{NULL}. We call these vectors unspecified. The special coercion
semantics of unspecified vectors serve two purposes:
\enumerate{
\item It makes it possible to assign vectors of \code{NA} inside any type of
vectors, even when they are not coercible with logical:
\if{html}{\out{}}\preformatted{x <- letters[1:5]
vec_assign(x, 1:2, c(NA, NA))
#> [1] NA NA "c" "d" "e"
}\if{html}{\out{
}}
\item We can’t put \code{NULL} in a data frame, so we need an identity element
that behaves more like a vector. Logical vectors of \code{NA} seem a
natural fit for this.
}
Unspecified vectors are thus promoted to any other type, just like
\code{NULL}:
\if{html}{\out{}}\preformatted{vec_ptype2(NA, "")
#> character(0)
vec_ptype2(1L, c(NA, NA))
#> integer(0)
}\if{html}{\out{
}}
}
\subsection{Finalising common types}{
vctrs has an internal vector type of class \code{vctrs_unspecified}. Users
normally don’t see such vectors in the wild, but they do come up when
taking the common type of an unspecified vector with another identity
value:
\if{html}{\out{}}\preformatted{vec_ptype2(NA, NA)
#> [0]
vec_ptype2(NA, NULL)
#> [0]
vec_ptype2(NULL, NA)
#> [0]
}\if{html}{\out{
}}
We can’t return \code{NA} here because \code{vec_ptype2()} normally returns empty
vectors. We also can’t return \code{NULL} because unspecified vectors need to
be recognised as logical vectors if they haven’t been promoted at the
end of the reduction.
\if{html}{\out{}}\preformatted{vec_ptype_finalise(vec_ptype2(NULL, NA))
#> logical(0)
}\if{html}{\out{
}}
See the output of \code{vec_ptype_common()} which performs the reduction and
finalises the type, ready to be used by the caller:
\if{html}{\out{}}\preformatted{vec_ptype_common(NULL, NULL)
#> NULL
vec_ptype_common(NA, NULL)
#> logical(0)
}\if{html}{\out{
}}
Note that \strong{partial} types in vctrs make use of the same mechanism.
They are finalised with \code{vec_ptype_finalise()}.
}
}
vctrs/man/unspecified.Rd 0000644 0001762 0000144 00000001206 14276722575 014773 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-unspecified.R
\name{unspecified}
\alias{unspecified}
\title{A 1d vector of unspecified type}
\usage{
unspecified(n = 0)
}
\arguments{
\item{n}{Length of vector}
}
\description{
This is a \link[=new_partial]{partial type} used to represent logical vectors
that only contain \code{NA}. These require special handling because we want to
allow \code{NA} to specify missingness without requiring a type.
}
\examples{
vec_ptype_show()
vec_ptype_show(NA)
vec_c(NA, factor("x"))
vec_c(NA, Sys.Date())
vec_c(NA, Sys.time())
vec_c(NA, list(1:3, 4:5))
}
\keyword{internal}
vctrs/man/reference-faq-compatibility.Rd 0000644 0001762 0000144 00000007236 14315612253 020041 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-developer.R
\name{reference-faq-compatibility}
\alias{reference-faq-compatibility}
\title{FAQ - Is my class compatible with vctrs?}
\description{
vctrs provides a framework for working with vector classes in a generic
way. However, it implements several compatibility fallbacks to base R
methods. In this reference you will find how vctrs tries to be
compatible with your vector class, and what base methods you need to
implement for compatibility.
If you’re starting from scratch, we think you’ll find it easier to start
using \code{\link[=new_vctr]{new_vctr()}} as documented in
\code{vignette("s3-vector")}. This guide is aimed for developers with
existing vector classes.
\subsection{Aggregate operations with fallbacks}{
All vctrs operations are based on four primitive generics described in
the next section. However there are many higher level operations. The
most important ones implement fallbacks to base generics for maximum
compatibility with existing classes.
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}} falls back to the base \code{[} generic if no
\code{\link[=vec_proxy]{vec_proxy()}} method is implemented. This way foreign
classes that do not implement \code{\link[=vec_restore]{vec_restore()}} can
restore attributes based on the new subsetted contents.
\item \code{\link[=vec_c]{vec_c()}} and \code{\link[=vec_rbind]{vec_rbind()}} now fall back to
\code{\link[base:c]{base::c()}} if the inputs have a common parent class with
a \code{c()} method (only if they have no self-to-self \code{vec_ptype2()}
method).
vctrs works hard to make your \code{c()} method success in various
situations (with \code{NULL} and \code{NA} inputs, even as first input which
would normally prevent dispatch to your method). The main downside
compared to using vctrs primitives is that you can’t combine vectors
of different classes since there is no extensible mechanism of
coercion in \code{c()}, and it is less efficient in some cases.
}
}
\subsection{The vctrs primitives}{
Most functions in vctrs are aggregate operations: they call other vctrs
functions which themselves call other vctrs functions. The dependencies
of a vctrs functions are listed in the Dependencies section of its
documentation page. Take a look at \code{\link[=vec_count]{vec_count()}} for an
example.
These dependencies form a tree whose leaves are the four vctrs
primitives. Here is the diagram for \code{vec_count()}:
\figure{vec-count-deps.png}
\subsection{The coercion generics}{
The coercion mechanism in vctrs is based on two generics:
\itemize{
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
}
See the \link[=theory-faq-coercion]{theory overview}.
Two objects with the same class and the same attributes are always
considered compatible by ptype2 and cast. If the attributes or classes
differ, they throw an incompatible type error.
Coercion errors are the main source of incompatibility with vctrs. See
the \link[=howto-faq-coercion]{howto guide} if you need to implement methods
for these generics.
}
\subsection{The proxy and restoration generics}{
\itemize{
\item \code{\link[=vec_proxy]{vec_proxy()}}
\item \code{\link[=vec_restore]{vec_restore()}}
}
These generics are essential for vctrs but mostly optional.
\code{vec_proxy()} defaults to an \link{identity} function and you
normally don’t need to implement it. The proxy a vector must be one of
the atomic vector types, a list, or a data frame. By default, S3 lists
that do not inherit from \code{"list"} do not have an identity proxy. In that
case, you need to explicitly implement \code{vec_proxy()} or make your class
inherit from list.
}
}
}
vctrs/man/vec_init.Rd 0000644 0001762 0000144 00000000667 14276722575 014307 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/slice.R
\name{vec_init}
\alias{vec_init}
\title{Initialize a vector}
\usage{
vec_init(x, n = 1L)
}
\arguments{
\item{x}{Template of vector to initialize.}
\item{n}{Desired size of result.}
}
\description{
Initialize a vector
}
\section{Dependencies}{
\itemize{
\item vec_slice()
}
}
\examples{
vec_init(1:10, 3)
vec_init(Sys.Date(), 5)
vec_init(mtcars, 2)
}
vctrs/man/vec_proxy_compare.Rd 0000644 0001762 0000144 00000006106 14315060307 016203 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/compare.R
\name{vec_proxy_compare}
\alias{vec_proxy_compare}
\alias{vec_proxy_order}
\title{Comparison and order proxy}
\usage{
vec_proxy_compare(x, ...)
vec_proxy_order(x, ...)
}
\arguments{
\item{x}{A vector x.}
\item{...}{These dots are for future extensions and must be empty.}
}
\value{
A 1d atomic vector or a data frame.
}
\description{
\code{vec_proxy_compare()} and \code{vec_proxy_order()} return proxy objects, i.e.
an atomic vector or data frame of atomic vectors.
For \code{\link[=vctr]{vctrs_vctr}} objects:
\itemize{
\item \code{vec_proxy_compare()} determines the behavior of \code{<}, \code{>}, \code{>=}
and \code{<=} (via \code{\link[=vec_compare]{vec_compare()}}); and \code{\link[=min]{min()}}, \code{\link[=max]{max()}}, \code{\link[=median]{median()}}, and
\code{\link[=quantile]{quantile()}}.
\item \code{vec_proxy_order()} determines the behavior of \code{order()} and \code{sort()}
(via \code{xtfrm()}).
}
}
\details{
The default method of \code{vec_proxy_compare()} assumes that all classes built
on top of atomic vectors or records are comparable. Internally the default
calls \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}. If your class is not comparable, you will need
to provide a \code{vec_proxy_compare()} method that throws an error.
The behavior of \code{vec_proxy_order()} is identical to \code{vec_proxy_compare()},
with the exception of lists. Lists are not comparable, as comparing
elements of different types is undefined. However, to allow ordering of
data frames containing list-columns, the ordering proxy of a list is
generated as an integer vector that can be used to order list elements
by first appearance.
If a class implements a \code{vec_proxy_compare()} method, it usually doesn't need
to provide a \code{vec_proxy_order()} method, because the latter is implemented
by forwarding to \code{vec_proxy_compare()} by default. Classes inheriting from
list are an exception: due to the default \code{vec_proxy_order()} implementation,
\code{vec_proxy_compare()} and \code{vec_proxy_order()} should be provided for such
classes (with identical implementations) to avoid mismatches between
comparison and sorting.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}} called by default in \code{vec_proxy_compare()}
\item \code{\link[=vec_proxy_compare]{vec_proxy_compare()}} called by default in \code{vec_proxy_order()}
}
}
\section{Data frames}{
If the proxy for \code{x} is a data frame, the proxy function is automatically
recursively applied on all columns as well. After applying the proxy
recursively, if there are any data frame columns present in the proxy, then
they are unpacked. Finally, if the resulting data frame only has a single
column, then it is unwrapped and a vector is returned as the proxy.
}
\examples{
# Lists are not comparable
x <- list(1:2, 1, 1:2, 3)
try(vec_compare(x, x))
# But lists are orderable by first appearance to allow for
# ordering data frames with list-cols
df <- new_data_frame(list(x = x))
vec_sort(df)
}
\keyword{internal}
vctrs/man/new_date.Rd 0000644 0001762 0000144 00000004402 14276722575 014264 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-date-time.R
\name{new_date}
\alias{new_date}
\alias{new_datetime}
\alias{new_duration}
\alias{vec_ptype2.Date}
\alias{vec_ptype2.POSIXct}
\alias{vec_ptype2.POSIXlt}
\alias{vec_ptype2.difftime}
\alias{vec_cast.Date}
\alias{vec_cast.POSIXct}
\alias{vec_cast.POSIXlt}
\alias{vec_cast.difftime}
\alias{vec_arith.Date}
\alias{vec_arith.POSIXct}
\alias{vec_arith.POSIXlt}
\alias{vec_arith.difftime}
\title{Date, date-time, and duration S3 classes}
\usage{
new_date(x = double())
new_datetime(x = double(), tzone = "")
new_duration(x = double(), units = c("secs", "mins", "hours", "days", "weeks"))
\method{vec_ptype2}{Date}(x, y, ...)
\method{vec_ptype2}{POSIXct}(x, y, ...)
\method{vec_ptype2}{POSIXlt}(x, y, ...)
\method{vec_ptype2}{difftime}(x, y, ...)
\method{vec_cast}{Date}(x, to, ...)
\method{vec_cast}{POSIXct}(x, to, ...)
\method{vec_cast}{POSIXlt}(x, to, ...)
\method{vec_cast}{difftime}(x, to, ...)
\method{vec_arith}{Date}(op, x, y, ...)
\method{vec_arith}{POSIXct}(op, x, y, ...)
\method{vec_arith}{POSIXlt}(op, x, y, ...)
\method{vec_arith}{difftime}(op, x, y, ...)
}
\arguments{
\item{x}{A double vector representing the number of days since UNIX
epoch for \code{new_date()}, number of seconds since UNIX epoch for
\code{new_datetime()}, and number of \code{units} for \code{new_duration()}.}
\item{tzone}{Time zone. A character vector of length 1. Either \code{""} for
the local time zone, or a value from \code{\link[=OlsonNames]{OlsonNames()}}}
\item{units}{Units of duration.}
}
\description{
\itemize{
\item A \code{date} (\link{Date}) is a double vector. Its value represent the number
of days since the Unix "epoch", 1970-01-01. It has no attributes.
\item A \code{datetime} (\link{POSIXct} is a double vector. Its value represents the
number of seconds since the Unix "Epoch", 1970-01-01. It has a single
attribute: the timezone (\code{tzone}))
\item A \code{duration} (\link{difftime})
}
}
\details{
These function help the base \code{Date}, \code{POSIXct}, and \code{difftime} classes fit
into the vctrs type system by providing constructors, coercion functions,
and casting functions.
}
\examples{
new_date(0)
new_datetime(0, tzone = "UTC")
new_duration(1, "hours")
}
\keyword{internal}
vctrs/man/fields.Rd 0000644 0001762 0000144 00000001542 13566016500 013727 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/fields.R
\name{fields}
\alias{fields}
\alias{n_fields}
\alias{field}
\alias{field<-}
\title{Tools for accessing the fields of a record.}
\usage{
fields(x)
n_fields(x)
field(x, i)
field(x, i) <- value
}
\arguments{
\item{x}{A \link{rcrd}, i.e. a list of equal length vectors with unique names.}
}
\description{
A \link{rcrd} behaves like a vector, so \code{length()}, \code{names()}, and \code{$} can
not provide access to the fields of the underlying list. These helpers do:
\code{fields()} is equivalent to \code{names()}; \code{n_fields()} is equivalent to
\code{length()}; \code{field()} is equivalent to \code{$}.
}
\examples{
x <- new_rcrd(list(x = 1:3, y = 3:1, z = letters[1:3]))
n_fields(x)
fields(x)
field(x, "y")
field(x, "y") <- runif(3)
field(x, "y")
}
\keyword{internal}
vctrs/man/theory-faq-recycling.Rd 0000644 0001762 0000144 00000005217 14511524374 016524 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-developer.R
\name{theory-faq-recycling}
\alias{theory-faq-recycling}
\alias{vector_recycling_rules}
\title{FAQ - How does recycling work in vctrs and the tidyverse?}
\description{
Recycling describes the concept of repeating elements of one vector to
match the size of another. There are two rules that underlie the
“tidyverse” recycling rules:
\itemize{
\item Vectors of size 1 will be recycled to the size of any other vector
\item Otherwise, all vectors must have the same size
}
}
\section{Examples}{
Vectors of size 1 are recycled to the size of any other vector:
\if{html}{\out{}}\preformatted{tibble(x = 1:3, y = 1L)
#> # A tibble: 3 x 2
#> x y
#>
#> 1 1 1
#> 2 2 1
#> 3 3 1
}\if{html}{\out{
}}
This includes vectors of size 0:
\if{html}{\out{}}\preformatted{tibble(x = integer(), y = 1L)
#> # A tibble: 0 x 2
#> # i 2 variables: x , y
}\if{html}{\out{
}}
If vectors aren’t size 1, they must all be the same size. Otherwise, an
error is thrown:
\if{html}{\out{}}\preformatted{tibble(x = 1:3, y = 4:7)
#> Error in `tibble()`:
#> ! Tibble columns must have compatible sizes.
#> * Size 3: Existing data.
#> * Size 4: Column `y`.
#> i Only values of size one are recycled.
}\if{html}{\out{
}}
}
\section{vctrs backend}{
Packages in r-lib and the tidyverse generally use
\code{\link[=vec_size_common]{vec_size_common()}} and
\code{\link[=vec_recycle_common]{vec_recycle_common()}} as the backends for
handling recycling rules.
\itemize{
\item \code{vec_size_common()} returns the common size of multiple vectors, after
applying the recycling rules
\item \code{vec_recycle_common()} goes one step further, and actually recycles
the vectors to their common size
}
\if{html}{\out{}}\preformatted{vec_size_common(1:3, "x")
#> [1] 3
vec_recycle_common(1:3, "x")
#> [[1]]
#> [1] 1 2 3
#>
#> [[2]]
#> [1] "x" "x" "x"
vec_size_common(1:3, c("x", "y"))
#> Error:
#> ! Can't recycle `..1` (size 3) to match `..2` (size 2).
}\if{html}{\out{
}}
}
\section{Base R recycling rules}{
The recycling rules described here are stricter than the ones generally
used by base R, which are:
\itemize{
\item If any vector is length 0, the output will be length 0
\item Otherwise, the output will be length \code{max(length_x, length_y)}, and a
warning will be thrown if the length of the longer vector is not an
integer multiple of the length of the shorter vector.
}
We explore the base R rules in detail in \code{vignette("type-size")}.
}
vctrs/man/vctrs-package.Rd 0000644 0001762 0000144 00000002313 14405105665 015214 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-package.R
\docType{package}
\name{vctrs-package}
\alias{vctrs}
\alias{vctrs-package}
\title{vctrs: Vector Helpers}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#maturing}{\figure{lifecycle-maturing.svg}{options: alt='[Maturing]'}}}{\strong{[Maturing]}}
Defines new notions of prototype and size that are
used to provide tools for consistent and well-founded type-coercion
and size-recycling, and are in turn connected to ideas of type- and
size-stability useful for analysing function interfaces.
}
\seealso{
Useful links:
\itemize{
\item \url{https://vctrs.r-lib.org/}
\item \url{https://github.com/r-lib/vctrs}
\item Report bugs at \url{https://github.com/r-lib/vctrs/issues}
}
}
\author{
\strong{Maintainer}: Davis Vaughan \email{davis@posit.co}
Authors:
\itemize{
\item Hadley Wickham \email{hadley@posit.co}
\item Lionel Henry \email{lionel@posit.co}
}
Other contributors:
\itemize{
\item data.table team (Radix sort based on data.table's forder() and their contribution to R's order()) [copyright holder]
\item Posit Software, PBC [copyright holder, funder]
}
}
\keyword{internal}
vctrs/man/vec_equal_na.Rd 0000644 0001762 0000144 00000001146 14315060307 015100 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_equal_na}
\alias{vec_equal_na}
\title{Missing values}
\usage{
vec_equal_na(x)
}
\arguments{
\item{x}{A vector}
}
\value{
A logical vector the same size as \code{x}.
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
\code{vec_equal_na()} has been renamed to \code{\link[=vec_detect_missing]{vec_detect_missing()}} and is deprecated
as of vctrs 0.5.0.
}
\keyword{internal}
vctrs/man/vec_equal.Rd 0000644 0001762 0000144 00000002202 14315060307 014414 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/equal.R
\name{vec_equal}
\alias{vec_equal}
\title{Equality}
\usage{
vec_equal(x, y, na_equal = FALSE, .ptype = NULL)
}
\arguments{
\item{x, y}{Vectors with compatible types and lengths.}
\item{na_equal}{Should \code{NA} values be considered equal?}
\item{.ptype}{Override to optionally specify common type}
}
\value{
A logical vector the same size as the common size of \code{x} and \code{y}.
Will only contain \code{NA}s if \code{na_equal} is \code{FALSE}.
}
\description{
\code{vec_equal()} tests if two vectors are equal.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_cast_common]{vec_cast_common()}} with fallback
\item \code{\link[=vec_recycle_common]{vec_recycle_common()}}
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
}
}
\examples{
vec_equal(c(TRUE, FALSE, NA), FALSE)
vec_equal(c(TRUE, FALSE, NA), FALSE, na_equal = TRUE)
vec_equal(5, 1:10)
vec_equal("d", letters[1:10])
df <- data.frame(x = c(1, 1, 2, 1), y = c(1, 2, 1, NA))
vec_equal(df, data.frame(x = 1, y = 2))
}
\seealso{
\code{\link[=vec_detect_missing]{vec_detect_missing()}}
}
vctrs/man/vec_group.Rd 0000644 0001762 0000144 00000004704 14276722575 014474 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/group.R
\name{vec_group}
\alias{vec_group}
\alias{vec_group_id}
\alias{vec_group_loc}
\alias{vec_group_rle}
\title{Identify groups}
\usage{
vec_group_id(x)
vec_group_loc(x)
vec_group_rle(x)
}
\arguments{
\item{x}{A vector}
}
\value{
\itemize{
\item \code{vec_group_id()}: An integer vector with the same size as \code{x}.
\item \code{vec_group_loc()}: A two column data frame with size equal to
\code{vec_size(vec_unique(x))}.
\itemize{
\item A \code{key} column of type \code{vec_ptype(x)}
\item A \code{loc} column of type list, with elements of type integer.
}
\item \code{vec_group_rle()}: A \code{vctrs_group_rle} rcrd object with two integer
vector fields: \code{group} and \code{length}.
}
Note that when using \code{vec_group_loc()} for complex types, the default
\code{data.frame} print method will be suboptimal, and you will want to coerce
into a tibble to better understand the output.
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
\itemize{
\item \code{vec_group_id()} returns an identifier for the group that each element of
\code{x} falls in, constructed in the order that they appear. The number of
groups is also returned as an attribute, \code{n}.
\item \code{vec_group_loc()} returns a data frame containing a \code{key} column with the
unique groups, and a \code{loc} column with the locations of each group in \code{x}.
\item \code{vec_group_rle()} locates groups in \code{x} and returns them run length
encoded in the order that they appear. The return value is a rcrd object
with fields for the \code{group} identifiers and the run \code{length} of the
corresponding group. The number of groups is also returned as an
attribute, \code{n}.
}
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
}
}
\examples{
purrr <- c("p", "u", "r", "r", "r")
vec_group_id(purrr)
vec_group_rle(purrr)
groups <- mtcars[c("vs", "am")]
vec_group_id(groups)
group_rle <- vec_group_rle(groups)
group_rle
# Access fields with `field()`
field(group_rle, "group")
field(group_rle, "length")
# `vec_group_id()` is equivalent to
vec_match(groups, vec_unique(groups))
vec_group_loc(mtcars$vs)
vec_group_loc(mtcars[c("vs", "am")])
if (require("tibble")) {
as_tibble(vec_group_loc(mtcars[c("vs", "am")]))
}
}
\keyword{internal}
vctrs/man/howto-faq-coercion-data-frame.Rd 0000644 0001762 0000144 00000035046 14420027341 020165 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-developer.R
\name{howto-faq-coercion-data-frame}
\alias{howto-faq-coercion-data-frame}
\title{FAQ - How to implement ptype2 and cast methods? (Data frames)}
\description{
This guide provides a practical recipe for implementing \code{vec_ptype2()}
and \code{vec_cast()} methods for coercions of data frame subclasses. Related
topics:
\itemize{
\item For an overview of the coercion mechanism in vctrs, see
\code{\link[=theory-faq-coercion]{?theory-faq-coercion}}.
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
}
Coercion of data frames occurs when different data frame classes are
combined in some way. The two main methods of combination are currently
row-binding with \code{\link[=vec_rbind]{vec_rbind()}} and col-binding with
\code{\link[=vec_cbind]{vec_cbind()}} (which are in turn used by a number of
dplyr and tidyr functions). These functions take multiple data frame
inputs and automatically coerce them to their common type.
vctrs is generally strict about the kind of automatic coercions that are
performed when combining inputs. In the case of data frames we have
decided to be a bit less strict for convenience. Instead of throwing an
incompatible type error, we fall back to a base data frame or a tibble
if we don’t know how to combine two data frame subclasses. It is still a
good idea to specify the proper coercion behaviour for your data frame
subclasses as soon as possible.
We will see two examples in this guide. The first example is about a
data frame subclass that has no particular attributes to manage. In the
second example, we implement coercion methods for a tibble subclass that
includes potentially incompatible attributes.
\subsection{Roxygen workflow}{
To implement methods for generics, first import the generics in your
namespace and redocument:
\if{html}{\out{}}\preformatted{#' @importFrom vctrs vec_ptype2 vec_cast
NULL
}\if{html}{\out{
}}
Note that for each batches of methods that you add to your package, you
need to export the methods and redocument immediately, even during
development. Otherwise they won’t be in scope when you run unit tests
e.g. with testthat.
Implementing double dispatch methods is very similar to implementing
regular S3 methods. In these examples we are using roxygen2 tags to
register the methods, but you can also register the methods manually in
your NAMESPACE file or lazily with \code{s3_register()}.
}
\subsection{Parent methods}{
Most of the common type determination should be performed by the parent
class. In vctrs, double dispatch is implemented in such a way that you
need to call the methods for the parent class manually. For
\code{vec_ptype2()} this means you need to call \code{df_ptype2()} (for data frame
subclasses) or \code{tib_ptype2()} (for tibble subclasses). Similarly,
\code{df_cast()} and \code{tib_cast()} are the workhorses for \code{vec_cast()} methods
of subtypes of \code{data.frame} and \code{tbl_df}. These functions take the union
of the columns in \code{x} and \code{y}, and ensure shared columns have the same
type.
These functions are much less strict than \code{vec_ptype2()} and
\code{vec_cast()} as they accept any subclass of data frame as input. They
always return a \code{data.frame} or a \code{tbl_df}. You will probably want to
write similar functions for your subclass to avoid repetition in your
code. You may want to export them as well if you are expecting other
people to derive from your class.
}
\subsection{A \code{data.table} example}{
This example is the actual implementation of vctrs coercion methods for
\code{data.table}. This is a simple example because we don’t have to keep
track of attributes for this class or manage incompatibilities. See the
tibble section for a more complicated example.
We first create the \code{dt_ptype2()} and \code{dt_cast()} helpers. They wrap
around the parent methods \code{df_ptype2()} and \code{df_cast()}, and transform
the common type or converted input to a data table. You may want to
export these helpers if you expect other packages to derive from your
data frame class.
These helpers should always return data tables. To this end we use the
conversion generic \code{as.data.table()}. Depending on the tools available
for the particular class at hand, a constructor might be appropriate as
well.
\if{html}{\out{}}\preformatted{dt_ptype2 <- function(x, y, ...) \{
as.data.table(df_ptype2(x, y, ...))
\}
dt_cast <- function(x, to, ...) \{
as.data.table(df_cast(x, to, ...))
\}
}\if{html}{\out{
}}
We start with the self-self method:
\if{html}{\out{}}\preformatted{#' @export
vec_ptype2.data.table.data.table <- function(x, y, ...) \{
dt_ptype2(x, y, ...)
\}
}\if{html}{\out{
}}
Between a data frame and a data table, we consider the richer type to be
data table. This decision is not based on the value coverage of each
data structures, but on the idea that data tables have richer behaviour.
Since data tables are the richer type, we call \code{dt_type2()} from the
\code{vec_ptype2()} method. It always returns a data table, no matter the
order of arguments:
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
The \code{vec_cast()} methods follow the same pattern, but note how the
method for coercing to data frame uses \code{df_cast()} rather than
\code{dt_cast()}.
Also, please note that for historical reasons, the order of the classes
in the method name is in reverse order of the arguments in the function
signature. The first class represents \code{to}, whereas the second class
represents \code{x}.
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
With these methods vctrs is now able to combine data tables with data
frames:
\if{html}{\out{}}\preformatted{vec_cbind(data.frame(x = 1:3), data.table(y = "foo"))
#> x y
#> 1: 1 foo
#> 2: 2 foo
#> 3: 3 foo
}\if{html}{\out{
}}
}
\subsection{A tibble example}{
In this example we implement coercion methods for a tibble subclass that
carries a colour as a scalar metadata:
\if{html}{\out{}}\preformatted{# 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])
\}
}\if{html}{\out{
}}
This subclass is very simple. All it does is modify the header.
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
Combinations do not work properly out of the box, instead vctrs falls
back to a bare tibble:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
Instead of falling back to a data frame, we would like to return a
\verb{} when combined with a data frame or a tibble. Because this
subclass has more metadata than normal data frames (it has a colour), it
is a \emph{supertype} of tibble and data frame, i.e. it is the richer type.
This is similar to how a grouped tibble is a more general type than a
tibble or a data frame. Conceptually, the latter are pinned to a single
constant group.
The coercion methods for data frames operate in two steps:
\itemize{
\item They check for compatible subclass attributes. In our case the tibble
colour has to be the same, or be undefined.
\item They call their parent methods, in this case
\code{\link[=tib_ptype2]{tib_ptype2()}} and \code{\link[=tib_cast]{tib_cast()}} because
we have a subclass of tibble. This eventually calls the data frame
methods \code{\link[=df_ptype2]{df_ptype2()}} and
\code{\link[=tib_ptype2]{tib_ptype2()}} which match the columns and their
types.
}
This process should usually be wrapped in two functions to avoid
repetition. Consider exporting these if you expect your class to be
derived by other subclasses.
We first implement a helper to determine if two data frames have
compatible colours. We use the \code{df_colour()} accessor which returns
\code{NULL} when the data frame colour is undefined.
\if{html}{\out{}}\preformatted{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)
\}
}\if{html}{\out{
}}
Next we implement the coercion helpers. If the colours are not
compatible, we call \code{stop_incompatible_cast()} or
\code{stop_incompatible_type()}. These strict coercion semantics are
justified because in this class colour is a \emph{data} attribute. If it were
a non essential \emph{detail} attribute, like the timezone in a datetime, we
would just standardise it to the value of the left-hand side.
In simpler cases (like the data.table example), these methods do not
need to take the arguments suffixed in \verb{_arg}. Here we do need to take
these arguments so we can pass them to the \code{stop_} functions when we
detect an incompatibility. They also should be passed to the parent
methods.
\if{html}{\out{}}\preformatted{#' @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)
\}
}\if{html}{\out{
}}
Let’s now implement the coercion methods, starting with the self-self
methods.
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
We can now combine compatible instances of our class!
\if{html}{\out{}}\preformatted{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.
}\if{html}{\out{
}}
The methods for combining our class with tibbles follow the same
pattern. For ptype2 we return our class in both cases because it is the
richer type:
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
For cast are careful about returning a tibble when casting to a tibble.
Note the call to \code{vctrs::tib_cast()}:
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
From this point, we get correct combinations with tibbles:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
However we are not done yet. Because the coercion hierarchy is different
from the class hierarchy, there is no inheritance of coercion methods.
We’re not getting correct behaviour for data frames yet because we
haven’t explicitly specified the methods for this class:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
Let’s finish up the boiler plate:
\if{html}{\out{}}\preformatted{#' @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, ...)
\}
}\if{html}{\out{
}}
This completes the implementation:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
}
}
vctrs/man/vec_arith.Rd 0000644 0001762 0000144 00000003735 13566016500 014433 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/arith.R
\name{vec_arith}
\alias{vec_arith}
\alias{vec_arith.default}
\alias{vec_arith.logical}
\alias{vec_arith.numeric}
\alias{vec_arith_base}
\alias{MISSING}
\title{Arithmetic operations}
\usage{
vec_arith(op, x, y, ...)
\method{vec_arith}{default}(op, x, y, ...)
\method{vec_arith}{logical}(op, x, y, ...)
\method{vec_arith}{numeric}(op, x, y, ...)
vec_arith_base(op, x, y)
MISSING()
}
\arguments{
\item{op}{An arithmetic operator as a string}
\item{x, y}{A pair of vectors. For \code{!}, unary \code{+} and unary \code{-}, \code{y} will be
a sentinel object of class \code{MISSING}, as created by \code{MISSING()}.}
\item{...}{These dots are for future extensions and must be empty.}
}
\description{
This generic provides a common double dispatch mechanism for all infix
operators (\code{+}, \code{-}, \code{/}, \code{*}, \code{^}, \code{\%\%}, \code{\%/\%}, \code{!}, \code{&}, \code{|}). It is used
to power the default arithmetic and boolean operators for \link{vctr}s objects,
overcoming the limitations of the base \link{Ops} generic.
}
\details{
\code{vec_arith_base()} is provided as a convenience for writing methods. It
recycles \code{x} and \code{y} to common length then calls the base operator with the
underlying \code{\link[=vec_data]{vec_data()}}.
\code{vec_arith()} is also used in \code{diff.vctrs_vctr()} method via \code{-}.
}
\examples{
d <- as.Date("2018-01-01")
dt <- as.POSIXct("2018-01-02 12:00")
t <- as.difftime(12, unit = "hours")
vec_arith("-", dt, 1)
vec_arith("-", dt, t)
vec_arith("-", dt, d)
vec_arith("+", dt, 86400)
vec_arith("+", dt, t)
vec_arith("+", t, t)
vec_arith("/", t, t)
vec_arith("/", t, 2)
vec_arith("*", t, 2)
}
\seealso{
\code{\link[=stop_incompatible_op]{stop_incompatible_op()}} for signalling that an arithmetic
operation is not permitted/supported.
See \code{\link[=vec_math]{vec_math()}} for the equivalent for the unary mathematical
functions.
}
\keyword{internal}
vctrs/man/howto-faq-coercion.Rd 0000644 0001762 0000144 00000022715 14511323761 016173 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-developer.R
\name{howto-faq-coercion}
\alias{howto-faq-coercion}
\title{FAQ - How to implement ptype2 and cast methods?}
\description{
This guide illustrates how to implement \code{vec_ptype2()} and \code{vec_cast()}
methods for existing classes. Related topics:
\itemize{
\item For an overview of how these generics work and their roles in vctrs,
see \code{\link[=theory-faq-coercion]{?theory-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
\item For a tutorial about implementing vctrs classes from scratch, see
\code{vignette("s3-vector")}
}
\subsection{The natural number class}{
We’ll illustrate how to implement coercion methods with a simple class
that represents natural numbers. In this scenario we have an existing
class that already features a constructor and methods for \code{print()} and
subset.
\if{html}{\out{}}\preformatted{#' @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())
\}
}\if{html}{\out{
}}
\if{html}{\out{}}\preformatted{new_natural(1:3)
#>
#> [1] 1 2 3
new_natural(c(1, NA))
#>
#> [1] 1 NA
}\if{html}{\out{
}}
}
\subsection{Roxygen workflow}{
To implement methods for generics, first import the generics in your
namespace and redocument:
\if{html}{\out{}}\preformatted{#' @importFrom vctrs vec_ptype2 vec_cast
NULL
}\if{html}{\out{
}}
Note that for each batches of methods that you add to your package, you
need to export the methods and redocument immediately, even during
development. Otherwise they won’t be in scope when you run unit tests
e.g. with testthat.
Implementing double dispatch methods is very similar to implementing
regular S3 methods. In these examples we are using roxygen2 tags to
register the methods, but you can also register the methods manually in
your NAMESPACE file or lazily with \code{s3_register()}.
}
\subsection{Implementing \code{vec_ptype2()}}{
\subsection{The self-self method}{
The first method to implement is the one that signals that your class is
compatible with itself:
\if{html}{\out{}}\preformatted{#' @export
vec_ptype2.my_natural.my_natural <- function(x, y, ...) \{
x
\}
vec_ptype2(new_natural(1), new_natural(2:3))
#>
#> integer(0)
}\if{html}{\out{
}}
\code{vec_ptype2()} implements a fallback to try and be compatible with
simple classes, so it may seem that you don’t need to implement the
self-self coercion method. However, you must implement it explicitly
because this is how vctrs knows that a class that is implementing vctrs
methods (for instance this disable fallbacks to \code{base::c()}). Also, it
makes your class a bit more efficient.
}
\subsection{The parent and children methods}{
Our natural number class is conceptually a parent of \verb{} and a
child of \verb{}, but the class is not compatible with logical,
integer, or double vectors yet:
\if{html}{\out{}}\preformatted{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` .
}\if{html}{\out{
}}
We’ll specify the twin methods for each of these classes, returning the
richer class in each case.
\if{html}{\out{}}\preformatted{#' @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
\}
}\if{html}{\out{
}}
Between a natural number and an integer, the latter is the richer class:
\if{html}{\out{}}\preformatted{#' @export
vec_ptype2.my_natural.integer <- function(x, y, ...) \{
y
\}
#' @export
vec_ptype2.integer.my_natural <- function(x, y, ...) \{
x
\}
}\if{html}{\out{
}}
We no longer get common type errors for logical and integer:
\if{html}{\out{}}\preformatted{vec_ptype2(TRUE, new_natural(2:3))
#>
#> integer(0)
vec_ptype2(new_natural(1), 2:3)
#> integer(0)
}\if{html}{\out{
}}
We are not done yet. Pairwise coercion methods must be implemented for
all the connected nodes in the coercion hierarchy, which include double
vectors further up. The coercion methods for grand-parent types must be
implemented separately:
\if{html}{\out{}}\preformatted{#' @export
vec_ptype2.my_natural.double <- function(x, y, ...) \{
y
\}
#' @export
vec_ptype2.double.my_natural <- function(x, y, ...) \{
x
\}
}\if{html}{\out{
}}
}
\subsection{Incompatible attributes}{
Most of the time, inputs are incompatible because they have different
classes for which no \code{vec_ptype2()} method is implemented. More rarely,
inputs could be incompatible because of their attributes. In that case
incompatibility is signalled by calling \code{stop_incompatible_type()}.
In the following example, we implement a self-self ptype2 method for a
hypothetical subclass of \verb{} that has stricter combination
semantics. The method throws an error when the levels of the two factors
are not compatible.
\if{html}{\out{}}\preformatted{#' @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
\}
}\if{html}{\out{
}}
Note how the methods need to take \code{x_arg} and \code{y_arg} parameters and
pass them on to \code{stop_incompatible_type()}. These argument tags help
create more informative error messages when the common type
determination is for a column of a data frame. They are part of the
generic signature but can usually be left out if not used.
}
}
\subsection{Implementing \code{vec_cast()}}{
Corresponding \code{vec_cast()} methods must be implemented for all
\code{vec_ptype2()} methods. The general pattern is to convert the argument
\code{x} to the type of \code{to}. The methods should validate the values in \code{x}
and make sure they conform to the values of \code{to}.
Please note that for historical reasons, the order of the classes in the
method name is in reverse order of the arguments in the function
signature. The first class represents \code{to}, whereas the second class
represents \code{x}.
The self-self method is easy in this case, it just returns the target
input:
\if{html}{\out{}}\preformatted{#' @export
vec_cast.my_natural.my_natural <- function(x, to, ...) \{
x
\}
}\if{html}{\out{
}}
The other types need to be validated. We perform input validation in the
\code{new_natural()} constructor, so that’s a good fit for our \code{vec_cast()}
implementations.
\if{html}{\out{}}\preformatted{#' @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)
\}
}\if{html}{\out{
}}
With these methods, vctrs is now able to combine logical and natural
vectors. It properly returns the richer type of the two, a natural
vector:
\if{html}{\out{}}\preformatted{vec_c(TRUE, new_natural(1), FALSE)
#>
#> [1] 1 1 0
}\if{html}{\out{
}}
Because we haven’t implemented conversions \emph{from} natural, it still
doesn’t know how to combine natural with the richer integer and double
types:
\if{html}{\out{}}\preformatted{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 .
}\if{html}{\out{
}}
This is quick work which completes the implementation of coercion
methods for vctrs:
\if{html}{\out{}}\preformatted{#' @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)
\}
}\if{html}{\out{
}}
And we now get the expected combinations.
\if{html}{\out{}}\preformatted{vec_c(new_natural(1), 10L)
#> [1] 1 10
vec_c(1.5, new_natural(1))
#> [1] 1.5 1.0
}\if{html}{\out{
}}
}
}
vctrs/man/missing.Rd 0000644 0001762 0000144 00000003620 14315060307 014126 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/missing.R
\name{missing}
\alias{missing}
\alias{vec_detect_missing}
\alias{vec_any_missing}
\title{Missing values}
\usage{
vec_detect_missing(x)
vec_any_missing(x)
}
\arguments{
\item{x}{A vector}
}
\value{
\itemize{
\item \code{vec_detect_missing()} returns a logical vector the same size as \code{x}.
\item \code{vec_any_missing()} returns a single \code{TRUE} or \code{FALSE}.
}
}
\description{
\itemize{
\item \code{vec_detect_missing()} returns a logical vector the same size as \code{x}. For
each element of \code{x}, it returns \code{TRUE} if the element is missing, and \code{FALSE}
otherwise.
\item \code{vec_any_missing()} returns a single \code{TRUE} or \code{FALSE} depending on whether
or not \code{x} has \emph{any} missing values.
}
\subsection{Differences with \code{\link[=is.na]{is.na()}}}{
Data frame rows are only considered missing if every element in the row is
missing. Similarly, \link[=new_rcrd]{record vector} elements are only considered
missing if every field in the record is missing. Put another way, rows with
\emph{any} missing values are considered \link[=vec_detect_complete]{incomplete}, but
only rows with \emph{all} missing values are considered missing.
List elements are only considered missing if they are \code{NULL}.
}
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy_equal]{vec_proxy_equal()}}
}
}
\examples{
x <- c(1, 2, NA, 4, NA)
vec_detect_missing(x)
vec_any_missing(x)
# Data frames are iterated over rowwise, and only report a row as missing
# if every element of that row is missing. If a row is only partially
# missing, it is said to be incomplete, but not missing.
y <- c("a", "b", NA, "d", "e")
df <- data_frame(x = x, y = y)
df$missing <- vec_detect_missing(df)
df$incomplete <- !vec_detect_complete(df)
df
}
\seealso{
\code{\link[=vec_detect_complete]{vec_detect_complete()}}
}
vctrs/man/as-is.Rd 0000644 0001762 0000144 00000000602 14276722575 013510 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-asis.R
\name{as-is}
\alias{as-is}
\alias{vec_ptype2.AsIs}
\title{AsIs S3 class}
\usage{
\method{vec_ptype2}{AsIs}(x, y, ..., x_arg = "", y_arg = "")
}
\description{
These functions help the base AsIs class fit into the vctrs type system
by providing coercion and casting functions.
}
\keyword{internal}
vctrs/man/runs.Rd 0000644 0001762 0000144 00000003467 14363556517 013475 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/runs.R
\name{runs}
\alias{runs}
\alias{vec_identify_runs}
\alias{vec_run_sizes}
\title{Runs}
\usage{
vec_identify_runs(x)
vec_run_sizes(x)
}
\arguments{
\item{x}{A vector.}
}
\value{
\itemize{
\item For \code{vec_identify_runs()}, an integer vector with the same size as \code{x}. A
scalar integer attribute, \code{n}, is attached.
\item For \code{vec_run_sizes()}, an integer vector with size equal to the number of
runs in \code{x}.
}
}
\description{
\itemize{
\item \code{vec_identify_runs()} returns a vector of identifiers for the elements of
\code{x} that indicate which run of repeated values they fall in. The number of
runs is also returned as an attribute, \code{n}.
\item \code{vec_run_sizes()} returns an integer vector corresponding to the size of
each run. This is identical to the \code{times} column from \code{vec_unrep()}, but
is faster if you don't need the run keys.
\item \code{\link[=vec_unrep]{vec_unrep()}} is a generalized \code{\link[base:rle]{base::rle()}}. It is documented alongside
the "repeat" functions of \code{\link[=vec_rep]{vec_rep()}} and \code{\link[=vec_rep_each]{vec_rep_each()}}; look there for
more information.
}
}
\details{
Unlike \code{\link[base:rle]{base::rle()}}, adjacent missing values are considered identical when
constructing runs. For example, \code{vec_identify_runs(c(NA, NA))} will return
\code{c(1, 1)}, not \code{c(1, 2)}.
}
\examples{
x <- c("a", "z", "z", "c", "a", "a")
vec_identify_runs(x)
vec_run_sizes(x)
vec_unrep(x)
y <- c(1, 1, 1, 2, 2, 3)
# With multiple columns, the runs are constructed rowwise
df <- data_frame(
x = x,
y = y
)
vec_identify_runs(df)
vec_run_sizes(df)
vec_unrep(df)
}
\seealso{
\code{\link[=vec_unrep]{vec_unrep()}} for a generalized \code{\link[base:rle]{base::rle()}}.
}
vctrs/man/vec_cbind_frame_ptype.Rd 0000644 0001762 0000144 00000001344 14276722575 017007 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/bind.R
\name{vec_cbind_frame_ptype}
\alias{vec_cbind_frame_ptype}
\title{Frame prototype}
\usage{
vec_cbind_frame_ptype(x, ...)
}
\arguments{
\item{x}{A data frame.}
\item{...}{These dots are for future extensions and must be empty.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
This is an experimental generic that returns zero-columns variants
of a data frame. It is needed for \code{\link[=vec_cbind]{vec_cbind()}}, to work around the
lack of colwise primitives in vctrs. Expect changes.
}
\keyword{internal}
vctrs/man/vec_chop.Rd 0000644 0001762 0000144 00000015745 14402367170 014263 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/slice-chop.R
\name{vec_chop}
\alias{vec_chop}
\alias{list_unchop}
\title{Chopping}
\usage{
vec_chop(x, ..., indices = NULL, sizes = NULL)
list_unchop(
x,
...,
indices = NULL,
ptype = NULL,
name_spec = NULL,
name_repair = c("minimal", "unique", "check_unique", "universal", "unique_quiet",
"universal_quiet"),
error_arg = "x",
error_call = current_env()
)
}
\arguments{
\item{x}{A vector}
\item{...}{These dots are for future extensions and must be empty.}
\item{indices}{For \code{vec_chop()}, a list of positive integer vectors to
slice \code{x} with, or \code{NULL}. Can't be used if \code{sizes} is already specified.
If both \code{indices} and \code{sizes} are \code{NULL}, \code{x} is split into its individual
elements, equivalent to using an \code{indices} of \code{as.list(vec_seq_along(x))}.
For \code{list_unchop()}, a list of positive integer vectors specifying the
locations to place elements of \code{x} in. Each element of \code{x} is recycled to
the size of the corresponding index vector. The size of \code{indices} must
match the size of \code{x}. If \code{NULL}, \code{x} is combined in the order it is
provided in, which is equivalent to using \code{\link[=vec_c]{vec_c()}}.}
\item{sizes}{An integer vector of non-negative sizes representing sequential
indices to slice \code{x} with, or \code{NULL}. Can't be used if \code{indices} is already
specified.
For example, \code{sizes = c(2, 4)} is equivalent to \code{indices = list(1:2, 3:6)},
but is typically faster.
\code{sum(sizes)} must be equal to \code{vec_size(x)}, i.e. \code{sizes} must completely
partition \code{x}, but an individual size is allowed to be \code{0}.}
\item{ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{x}. Alternatively, you
can supply \code{ptype} to give the output a known type.}
\item{name_spec}{A name specification for combining
inner and outer names. This is relevant for inputs passed with a
name, when these inputs are themselves named, like \code{outer = c(inner = 1)}, or when they have length greater than 1: \code{outer = 1:2}. By default, these cases trigger an error. You can resolve
the error by providing a specification that describes how to
combine the names or the indices of the inner vector with the
name of the input. This specification can be:
\itemize{
\item A function of two arguments. The outer name is passed as a
string to the first argument, and the inner names or positions
are passed as second argument.
\item An anonymous function as a purrr-style formula.
\item A glue specification of the form \code{"{outer}_{inner}"}.
\item An \code{\link[rlang:zap]{rlang::zap()}} object, in which case both outer and inner
names are ignored and the result is unnamed.
}
See the \link[=name_spec]{name specification topic}.}
\item{name_repair}{How to repair names, see \code{repair} options in
\code{\link[=vec_as_names]{vec_as_names()}}.}
\item{error_arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
\item{error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\value{
\itemize{
\item \code{vec_chop()}: A list where each element has the same type as \code{x}. The size
of the list is equal to \code{vec_size(indices)}, \code{vec_size(sizes)}, or
\code{vec_size(x)} depending on whether or not \code{indices} or \code{sizes} is provided.
\item \code{list_unchop()}: A vector of type \code{vec_ptype_common(!!!x)}, or \code{ptype}, if
specified. The size is computed as \code{vec_size_common(!!!indices)} unless
the indices are \code{NULL}, in which case the size is \code{vec_size_common(!!!x)}.
}
}
\description{
\itemize{
\item \code{vec_chop()} provides an efficient method to repeatedly slice a vector. It
captures the pattern of \code{map(indices, vec_slice, x = x)}. When no indices
are supplied, it is generally equivalent to \code{\link[=as.list]{as.list()}}.
\item \code{list_unchop()} combines a list of vectors into a single vector, placing
elements in the output according to the locations specified by \code{indices}.
It is similar to \code{\link[=vec_c]{vec_c()}}, but gives greater control over how the elements
are combined. When no indices are supplied, it is identical to \code{vec_c()},
but typically a little faster.
}
If \code{indices} selects every value in \code{x} exactly once, in any order, then
\code{list_unchop()} is the inverse of \code{vec_chop()} and the following invariant
holds:
\if{html}{\out{}}\preformatted{list_unchop(vec_chop(x, indices = indices), indices = indices) == x
}\if{html}{\out{
}}
}
\section{Dependencies of \code{vec_chop()}}{
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\section{Dependencies of \code{list_unchop()}}{
\itemize{
\item \code{\link[=vec_c]{vec_c()}}
}
}
\examples{
vec_chop(1:5)
# These two are equivalent
vec_chop(1:5, indices = list(1:2, 3:5))
vec_chop(1:5, sizes = c(2, 3))
# Can also be used on data frames
vec_chop(mtcars, indices = list(1:3, 4:6))
# If `indices` selects every value in `x` exactly once,
# in any order, then `list_unchop()` inverts `vec_chop()`
x <- c("a", "b", "c", "d")
indices <- list(2, c(3, 1), 4)
vec_chop(x, indices = indices)
list_unchop(vec_chop(x, indices = indices), indices = indices)
# When unchopping, size 1 elements of `x` are recycled
# to the size of the corresponding index
list_unchop(list(1, 2:3), indices = list(c(1, 3, 5), c(2, 4)))
# Names are retained, and outer names can be combined with inner
# names through the use of a `name_spec`
lst <- list(x = c(a = 1, b = 2), y = 1)
list_unchop(lst, indices = list(c(3, 2), c(1, 4)), name_spec = "{outer}_{inner}")
# An alternative implementation of `ave()` can be constructed using
# `vec_chop()` and `list_unchop()` in combination with `vec_group_loc()`
ave2 <- function(.x, .by, .f, ...) {
indices <- vec_group_loc(.by)$loc
chopped <- vec_chop(.x, indices = indices)
out <- lapply(chopped, .f, ...)
list_unchop(out, indices = indices)
}
breaks <- warpbreaks$breaks
wool <- warpbreaks$wool
ave2(breaks, wool, mean)
identical(
ave2(breaks, wool, mean),
ave(breaks, wool, FUN = mean)
)
# If you know your input is sorted and you'd like to split on the groups,
# `vec_run_sizes()` can be efficiently combined with `sizes`
df <- data_frame(
g = c(2, 5, 5, 6, 6, 6, 6, 8, 9, 9),
x = 1:10
)
vec_chop(df, sizes = vec_run_sizes(df$g))
# If you have a list of homogeneous vectors, sometimes it can be useful to
# unchop, apply a function to the flattened vector, and then rechop according
# to the original indices. This can be done efficiently with `list_sizes()`.
x <- list(c(1, 2, 1), c(3, 1), 5, double())
x_flat <- list_unchop(x)
x_flat <- x_flat + max(x_flat)
vec_chop(x_flat, sizes = list_sizes(x))
}
vctrs/man/op-empty-default.Rd 0000644 0001762 0000144 00000001011 14276722575 015663 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/size.R
\name{\%0\%}
\alias{\%0\%}
\title{Default value for empty vectors}
\usage{
x \%0\% y
}
\arguments{
\item{x}{A vector}
\item{y}{Value to use if \code{x} is empty. To preserve type-stability, should
be the same type as \code{x}.}
}
\description{
Use this inline operator when you need to provide a default value for
empty (as defined by \code{\link[=vec_is_empty]{vec_is_empty()}}) vectors.
}
\examples{
1:10 \%0\% 5
integer() \%0\% 5
}
vctrs/man/name_spec.Rd 0000644 0001762 0000144 00000005431 14315060307 014411 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/names.R
\name{name_spec}
\alias{name_spec}
\title{Name specifications}
\arguments{
\item{name_spec, .name_spec}{A name specification for combining
inner and outer names. This is relevant for inputs passed with a
name, when these inputs are themselves named, like \code{outer = c(inner = 1)}, or when they have length greater than 1: \code{outer = 1:2}. By default, these cases trigger an error. You can resolve
the error by providing a specification that describes how to
combine the names or the indices of the inner vector with the
name of the input. This specification can be:
\itemize{
\item A function of two arguments. The outer name is passed as a
string to the first argument, and the inner names or positions
are passed as second argument.
\item An anonymous function as a purrr-style formula.
\item A glue specification of the form \code{"{outer}_{inner}"}.
\item An \code{\link[rlang:zap]{rlang::zap()}} object, in which case both outer and inner
names are ignored and the result is unnamed.
}
See the \link[=name_spec]{name specification topic}.}
}
\description{
A name specification describes how to combine an inner and outer
names. This sort of name combination arises when concatenating
vectors or flattening lists. There are two possible cases:
\itemize{
\item Named vector:
\if{html}{\out{}}\preformatted{vec_c(outer = c(inner1 = 1, inner2 = 2))
}\if{html}{\out{
}}
\item Unnamed vector:
\if{html}{\out{}}\preformatted{vec_c(outer = 1:2)
}\if{html}{\out{
}}
}
In r-lib and tidyverse packages, these cases are errors by default,
because there's no behaviour that works well for every case.
Instead, you can provide a name specification that describes how to
combine the inner and outer names of inputs. Name specifications
can refer to:
\itemize{
\item \code{outer}: The external name recycled to the size of the input
vector.
\item \code{inner}: Either the names of the input vector, or a sequence of
integer from 1 to the size of the vector if it is unnamed.
}
}
\examples{
# By default, named inputs must be length 1:
vec_c(name = 1) # ok
try(vec_c(name = 1:3)) # bad
# They also can't have internal names, even if scalar:
try(vec_c(name = c(internal = 1))) # bad
# Pass a name specification to work around this. A specification
# can be a glue string referring to `outer` and `inner`:
vec_c(name = 1:3, other = 4:5, .name_spec = "{outer}")
vec_c(name = 1:3, other = 4:5, .name_spec = "{outer}_{inner}")
# They can also be functions:
my_spec <- function(outer, inner) paste(outer, inner, sep = "_")
vec_c(name = 1:3, other = 4:5, .name_spec = my_spec)
# Or purrr-style formulas for anonymous functions:
vec_c(name = 1:3, other = 4:5, .name_spec = ~ paste0(.x, .y))
}
vctrs/man/df_list.Rd 0000644 0001762 0000144 00000005400 14511524374 014106 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-data-frame.R
\name{df_list}
\alias{df_list}
\title{Collect columns for data frame construction}
\usage{
df_list(
...,
.size = NULL,
.unpack = TRUE,
.name_repair = c("check_unique", "unique", "universal", "minimal", "unique_quiet",
"universal_quiet"),
.error_call = current_env()
)
}
\arguments{
\item{...}{Vectors of equal-length. When inputs are named, those names
are used for names of the resulting list.}
\item{.size}{The common size of vectors supplied in \code{...}. If \code{NULL}, this
will be computed as the common size of the inputs.}
\item{.unpack}{Should unnamed data frame inputs be unpacked? Defaults to
\code{TRUE}.}
\item{.name_repair}{One of \code{"check_unique"}, \code{"unique"}, \code{"universal"},
\code{"minimal"}, \code{"unique_quiet"}, or \code{"universal_quiet"}. See \code{\link[=vec_as_names]{vec_as_names()}}
for the meaning of these options.}
\item{.error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\description{
\code{df_list()} constructs the data structure underlying a data
frame, a named list of equal-length vectors. It is often used in
combination with \code{\link[=new_data_frame]{new_data_frame()}} to safely and consistently create
a helper function for data frame subclasses.
}
\section{Properties}{
\itemize{
\item Inputs are \link[=theory-faq-recycling]{recycled} to a common size with
\code{\link[=vec_recycle_common]{vec_recycle_common()}}.
\item With the exception of data frames, inputs are not modified in any way.
Character vectors are never converted to factors, and lists are stored
as-is for easy creation of list-columns.
\item Unnamed data frame inputs are automatically unpacked. Named data frame
inputs are stored unmodified as data frame columns.
\item \code{NULL} inputs are completely ignored.
\item The dots are dynamic, allowing for splicing of lists with \verb{!!!} and
unquoting.
}
}
\examples{
# `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)
}
\seealso{
\code{\link[=new_data_frame]{new_data_frame()}} for constructing data frame subclasses from a validated
input. \code{\link[=data_frame]{data_frame()}} for a fast data frame creation helper.
}
vctrs/man/new_data_frame.Rd 0000644 0001762 0000144 00000002663 14511320527 015420 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-data-frame.R
\name{new_data_frame}
\alias{new_data_frame}
\title{Assemble attributes for data frame construction}
\usage{
new_data_frame(x = list(), n = NULL, ..., class = NULL)
}
\arguments{
\item{x}{A named list of equal-length vectors. The lengths are not
checked; it is responsibility of the caller to make sure they are
equal.}
\item{n}{Number of rows. If \code{NULL}, will be computed from the length of
the first element of \code{x}.}
\item{..., class}{Additional arguments for creating subclasses.
The following attributes have special behavior:
\itemize{
\item \code{"names"} is preferred if provided, overriding existing names in \code{x}.
\item \code{"row.names"} is preferred if provided, overriding both \code{n} and the size
implied by \code{x}.
}}
}
\description{
\code{new_data_frame()} constructs a new data frame from an existing list. It is
meant to be performant, and does not check the inputs for correctness in any
way. It is only safe to use after a call to \code{\link[=df_list]{df_list()}}, which collects and
validates the columns used to construct the data frame.
}
\examples{
new_data_frame(list(x = 1:10, y = 10:1))
}
\seealso{
\code{\link[=df_list]{df_list()}} for a way to safely construct a data frame's underlying
data structure from individual columns. This can be used to create a
named list for further use by \code{new_data_frame()}.
}
vctrs/man/data_frame.Rd 0000644 0001762 0000144 00000007161 14511524374 014553 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-data-frame.R
\name{data_frame}
\alias{data_frame}
\title{Construct a data frame}
\usage{
data_frame(
...,
.size = NULL,
.name_repair = c("check_unique", "unique", "universal", "minimal", "unique_quiet",
"universal_quiet"),
.error_call = current_env()
)
}
\arguments{
\item{...}{Vectors to become columns in the data frame. When inputs are
named, those names are used for column names.}
\item{.size}{The number of rows in the data frame. If \code{NULL}, this will
be computed as the common size of the inputs.}
\item{.name_repair}{One of \code{"check_unique"}, \code{"unique"}, \code{"universal"},
\code{"minimal"}, \code{"unique_quiet"}, or \code{"universal_quiet"}. See \code{\link[=vec_as_names]{vec_as_names()}}
for the meaning of these options.}
\item{.error_call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
}
\description{
\code{data_frame()} constructs a data frame. It is similar to
\code{\link[base:data.frame]{base::data.frame()}}, but there are a few notable differences that make it
more in line with vctrs principles. The Properties section outlines these.
}
\details{
If no column names are supplied, \code{""} will be used as a default name for all
columns. This is applied before name repair occurs, so the default name
repair of \code{"check_unique"} will error if any unnamed inputs are supplied and
\code{"unique"} (or \code{"unique_quiet"}) will repair the empty string column names
appropriately. If the column names don't matter, use a \code{"minimal"} name
repair for convenience and performance.
}
\section{Properties}{
\itemize{
\item Inputs are \link[=theory-faq-recycling]{recycled} to a common size with
\code{\link[=vec_recycle_common]{vec_recycle_common()}}.
\item With the exception of data frames, inputs are not modified in any way.
Character vectors are never converted to factors, and lists are stored
as-is for easy creation of list-columns.
\item Unnamed data frame inputs are automatically unpacked. Named data frame
inputs are stored unmodified as data frame columns.
\item \code{NULL} inputs are completely ignored.
\item The dots are dynamic, allowing for splicing of lists with \verb{!!!} and
unquoting.
}
}
\examples{
data_frame(x = 1, y = 2)
# Inputs are recycled using tidyverse recycling rules
data_frame(x = 1, y = 1:3)
# Strings are never converted to factors
class(data_frame(x = "foo")$x)
# 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)
}
# 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
# Again, it is recommended to convert these to tibbles for a better
# print method
if (rlang::is_installed("tibble")) {
tibble::as_tibble(df)
}
# Unnamed data frame input is automatically unpacked
data_frame(x = 1, data_frame(y = 1:2, z = "a"))
}
\seealso{
\code{\link[=df_list]{df_list()}} for safely creating a data frame's underlying data structure from
individual columns. \code{\link[=new_data_frame]{new_data_frame()}} for constructing the actual data
frame from that underlying data structure. Together, these can be useful
for developers when creating new data frame subclasses supporting
standard evaluation.
}
vctrs/man/vec_order.Rd 0000644 0001762 0000144 00000003736 14315060307 014435 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/order.R
\name{vec_order}
\alias{vec_order}
\alias{vec_sort}
\title{Order and sort vectors}
\usage{
vec_order(
x,
...,
direction = c("asc", "desc"),
na_value = c("largest", "smallest")
)
vec_sort(
x,
...,
direction = c("asc", "desc"),
na_value = c("largest", "smallest")
)
}
\arguments{
\item{x}{A vector}
\item{...}{These dots are for future extensions and must be empty.}
\item{direction}{Direction to sort in. Defaults to \code{asc}ending.}
\item{na_value}{Should \code{NA}s be treated as the largest or smallest values?}
}
\value{
\itemize{
\item \code{vec_order()} an integer vector the same size as \code{x}.
\item \code{vec_sort()} a vector with the same size and type as \code{x}.
}
}
\description{
Order and sort vectors
}
\section{Differences with \code{order()}}{
Unlike the \code{na.last} argument of \code{order()} which decides the
positions of missing values irrespective of the \code{decreasing}
argument, the \code{na_value} argument of \code{vec_order()} interacts with
\code{direction}. If missing values are considered the largest value,
they will appear last in ascending order, and first in descending
order.
}
\section{Dependencies of \code{vec_order()}}{
\itemize{
\item \code{\link[=vec_proxy_order]{vec_proxy_order()}}
}
}
\section{Dependencies of \code{vec_sort()}}{
\itemize{
\item \code{\link[=vec_proxy_order]{vec_proxy_order()}}
\item \code{\link[=vec_order]{vec_order()}}
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\examples{
x <- round(c(runif(9), NA), 3)
vec_order(x)
vec_sort(x)
vec_sort(x, direction = "desc")
# Can also handle data frames
df <- data.frame(g = sample(2, 10, replace = TRUE), x = x)
vec_order(df)
vec_sort(df)
vec_sort(df, direction = "desc")
# Missing values interpreted as largest values are last when
# in increasing order:
vec_order(c(1, NA), na_value = "largest", direction = "asc")
vec_order(c(1, NA), na_value = "largest", direction = "desc")
}
vctrs/man/vec_fill_missing.Rd 0000644 0001762 0000144 00000002756 14276722575 016024 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/fill.R
\name{vec_fill_missing}
\alias{vec_fill_missing}
\title{Fill in missing values with the previous or following value}
\usage{
vec_fill_missing(
x,
direction = c("down", "up", "downup", "updown"),
max_fill = NULL
)
}
\arguments{
\item{x}{A vector}
\item{direction}{Direction in which to fill missing values. Must be either
\code{"down"}, \code{"up"}, \code{"downup"}, or \code{"updown"}.}
\item{max_fill}{A single positive integer specifying the maximum number of
sequential missing values that will be filled. If \code{NULL}, there is
no limit.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
\code{vec_fill_missing()} fills gaps of missing values with the previous or
following non-missing value.
}
\examples{
x <- c(NA, NA, 1, NA, NA, NA, 3, NA, NA)
# Filling down replaces missing values with the previous non-missing value
vec_fill_missing(x, direction = "down")
# To also fill leading missing values, use `"downup"`
vec_fill_missing(x, direction = "downup")
# Limit the number of sequential missing values to fill with `max_fill`
vec_fill_missing(x, max_fill = 1)
# Data frames are filled rowwise. Rows are only considered missing
# if all elements of that row are missing.
y <- c(1, NA, 2, NA, NA, 3, 4, NA, 5)
df <- data_frame(x = x, y = y)
df
vec_fill_missing(df)
}
vctrs/man/vec_repeat.Rd 0000644 0001762 0000144 00000001517 14276722575 014617 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_repeat}
\alias{vec_repeat}
\title{Expand the length of a vector}
\usage{
vec_repeat(x, each = 1L, times = 1L)
}
\arguments{
\item{x}{A vector.}
\item{each}{Number of times to repeat each element of \code{x}.}
\item{times}{Number of times to repeat the whole vector of \code{x}.}
}
\value{
A vector the same type as \code{x} with size \code{vec_size(x) * times * each}.
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
\code{vec_repeat()} has been replaced with \code{\link[=vec_rep]{vec_rep()}} and \code{\link[=vec_rep_each]{vec_rep_each()}} and is
deprecated as of vctrs 0.3.0.
}
\keyword{internal}
vctrs/man/vec_proxy_equal.Rd 0000644 0001762 0000144 00000003135 14315060307 015663 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/equal.R
\name{vec_proxy_equal}
\alias{vec_proxy_equal}
\title{Equality proxy}
\usage{
vec_proxy_equal(x, ...)
}
\arguments{
\item{x}{A vector x.}
\item{...}{These dots are for future extensions and must be empty.}
}
\value{
A 1d atomic vector or a data frame.
}
\description{
Returns a proxy object (i.e. an atomic vector or data frame of atomic
vectors). For \link{vctr}s, this determines the behaviour of \code{==} and
\code{!=} (via \code{\link[=vec_equal]{vec_equal()}}); \code{\link[=unique]{unique()}}, \code{\link[=duplicated]{duplicated()}} (via
\code{\link[=vec_unique]{vec_unique()}} and \code{\link[=vec_duplicate_detect]{vec_duplicate_detect()}}); \code{\link[=is.na]{is.na()}} and \code{\link[=anyNA]{anyNA()}}
(via \code{\link[=vec_detect_missing]{vec_detect_missing()}}).
}
\details{
The default method calls \code{\link[=vec_proxy]{vec_proxy()}}, as the default underlying
vector data should be equal-able in most cases. If your class is
not equal-able, provide a \code{vec_proxy_equal()} method that throws an
error.
}
\section{Data frames}{
If the proxy for \code{x} is a data frame, the proxy function is automatically
recursively applied on all columns as well. After applying the proxy
recursively, if there are any data frame columns present in the proxy, then
they are unpacked. Finally, if the resulting data frame only has a single
column, then it is unwrapped and a vector is returned as the proxy.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy]{vec_proxy()}} called by default
}
}
\keyword{internal}
vctrs/man/obj_print.Rd 0000644 0001762 0000144 00000001665 14202760666 014464 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/print-str.R
\name{obj_print}
\alias{obj_print}
\alias{obj_print_header}
\alias{obj_print_data}
\alias{obj_print_footer}
\alias{obj_str}
\alias{obj_str_header}
\alias{obj_str_data}
\alias{obj_str_footer}
\title{\code{print()} and \code{str()} generics.}
\usage{
obj_print(x, ...)
obj_print_header(x, ...)
obj_print_data(x, ...)
obj_print_footer(x, ...)
obj_str(x, ...)
obj_str_header(x, ...)
obj_str_data(x, ...)
obj_str_footer(x, ...)
}
\arguments{
\item{x}{A vector}
\item{...}{Additional arguments passed on to methods. See \code{\link[=print]{print()}} and
\code{\link[=str]{str()}} for commonly used options}
}
\description{
These are constructed to be more easily extensible since you can override
the \verb{_header()}, \verb{_data()} or \verb{_footer()} components individually. The
default methods are built on top of \code{format()}.
}
\keyword{internal}
vctrs/man/vec_ptype.Rd 0000644 0001762 0000144 00000011435 14315060307 014456 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type.R
\name{vec_ptype}
\alias{vec_ptype}
\alias{vec_ptype_common}
\alias{vec_ptype_show}
\title{Find the prototype of a set of vectors}
\usage{
vec_ptype(x, ..., x_arg = "", call = caller_env())
vec_ptype_common(..., .ptype = NULL, .arg = "", .call = caller_env())
vec_ptype_show(...)
}
\arguments{
\item{x}{A vector}
\item{...}{For \code{vec_ptype()}, these dots are for future extensions and must
be empty.
For \code{vec_ptype_common()} and \code{vec_ptype_show()}, vector inputs.}
\item{x_arg}{Argument name for \code{x}. This is used in error messages to inform
the user about the locations of incompatible types.}
\item{call, .call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{.ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{...}.
Alternatively, you can supply \code{.ptype} to give the output known type.
If \code{getOption("vctrs.no_guessing")} is \code{TRUE} you must supply this value:
this is a convenient way to make production code demand fixed types.}
\item{.arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
}
\value{
\code{vec_ptype()} and \code{vec_ptype_common()} return a prototype
(a size-0 vector)
}
\description{
\code{vec_ptype()} returns the unfinalised prototype of a single vector.
\code{vec_ptype_common()} finds the common type of multiple vectors.
\code{vec_ptype_show()} nicely prints the common type of any number of
inputs, and is designed for interactive exploration.
}
\section{\code{vec_ptype()}}{
\code{vec_ptype()} returns \link[=vec_size]{size} 0 vectors potentially
containing attributes but no data. Generally, this is just
\code{vec_slice(x, 0L)}, but some inputs require special
handling.
\itemize{
\item While you can't slice \code{NULL}, the prototype of \code{NULL} is
itself. This is because we treat \code{NULL} as an identity value in
the \code{vec_ptype2()} monoid.
\item The prototype of logical vectors that only contain missing values
is the special \link{unspecified} type, which can be coerced to any
other 1d type. This allows bare \code{NA}s to represent missing values
for any 1d vector type.
}
See \link{internal-faq-ptype2-identity} for more information about
identity values.
\code{vec_ptype()} is a \emph{performance} generic. It is not necessary to implement it
because the default method will work for any vctrs type. However the default
method builds around other vctrs primitives like \code{vec_slice()} which incurs
performance costs. If your class has a static prototype, you might consider
implementing a custom \code{vec_ptype()} method that returns a constant. This will
improve the performance of your class in many cases (\link[=vec_ptype2]{common type} imputation in particular).
Because it may contain unspecified vectors, the prototype returned
by \code{vec_ptype()} is said to be \strong{unfinalised}. Call
\code{\link[=vec_ptype_finalise]{vec_ptype_finalise()}} to finalise it. Commonly you will need the
finalised prototype as returned by \code{vec_slice(x, 0L)}.
}
\section{\code{vec_ptype_common()}}{
\code{vec_ptype_common()} first finds the prototype of each input, then
successively calls \code{\link[=vec_ptype2]{vec_ptype2()}} to find a common type. It returns
a \link[=vec_ptype_finalise]{finalised} prototype.
}
\section{Dependencies of \code{vec_ptype()}}{
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}} for returning an empty slice
}
}
\section{Dependencies of \code{vec_ptype_common()}}{
\itemize{
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_ptype_finalise]{vec_ptype_finalise()}}
}
}
\examples{
# Unknown types ------------------------------------------
vec_ptype_show()
vec_ptype_show(NA)
vec_ptype_show(NULL)
# Vectors ------------------------------------------------
vec_ptype_show(1:10)
vec_ptype_show(letters)
vec_ptype_show(TRUE)
vec_ptype_show(Sys.Date())
vec_ptype_show(Sys.time())
vec_ptype_show(factor("a"))
vec_ptype_show(ordered("a"))
# Matrices -----------------------------------------------
# The prototype of a matrix includes the number of columns
vec_ptype_show(array(1, dim = c(1, 2)))
vec_ptype_show(array("x", dim = c(1, 2)))
# Data frames --------------------------------------------
# The prototype of a data frame includes the prototype of
# every column
vec_ptype_show(iris)
# The prototype of multiple data frames includes the prototype
# of every column that in any data frame
vec_ptype_show(
data.frame(x = TRUE),
data.frame(y = 2),
data.frame(z = "a")
)
}
vctrs/man/faq-compatibility-types.Rd 0000644 0001762 0000144 00000006364 14400165664 017254 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq.R
\name{faq-compatibility-types}
\alias{faq-compatibility-types}
\title{FAQ - How is the compatibility of vector types decided?}
\description{
Two vectors are \strong{compatible} when you can safely:
\itemize{
\item Combine them into one larger vector.
\item Assign values from one of the vectors into the other vector.
}
Examples of compatible types are integer and double vectors. On the
other hand, integer and character vectors are not compatible.
}
\section{Common type of multiple vectors}{
There are two possible outcomes when multiple vectors of different types
are combined into a larger vector:
\itemize{
\item An incompatible type error is thrown because some of the types are not
compatible:
\if{html}{\out{}}\preformatted{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` .
}\if{html}{\out{
}}
\item The vectors are combined into a vector that has the common type of all
inputs. In this example, the common type of integer and logical is
integer:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
}
In general, the common type is the \emph{richer} type, in other words the
type that can represent the most values. Logical vectors are at the
bottom of the hierarchy of numeric types because they can only represent
two values (not counting missing values). Then come integer vectors, and
then doubles. Here is the vctrs type hierarchy for the fundamental
vectors:
\figure{coerce.png}
}
\section{Type conversion and lossy cast errors}{
Type compatibility does not necessarily mean that you can \strong{convert}
one type to the other type. That’s because one of the types might
support a larger set of possible values. For instance, integer and
double vectors are compatible, but double vectors can’t be converted to
integer if they contain fractional values.
When vctrs can’t convert a vector because the target type is not as rich
as the source type, it throws a lossy cast error. Assigning a fractional
number to an integer vector is a typical example of a lossy cast error:
\if{html}{\out{}}\preformatted{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
}\if{html}{\out{
}}
}
\section{How to make two vector classes compatible?}{
If you encounter two vector types that you think should be compatible,
they might need to implement coercion methods. Reach out to the
author(s) of the classes and ask them if it makes sense for their
classes to be compatible.
These developer FAQ items provide guides for implementing coercion
methods:
\itemize{
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
}
}
vctrs/man/vec_as_names_legacy.Rd 0000644 0001762 0000144 00000002114 13505165544 016432 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/names.R
\name{vec_as_names_legacy}
\alias{vec_as_names_legacy}
\title{Repair names with legacy method}
\usage{
vec_as_names_legacy(names, prefix = "V", sep = "")
}
\arguments{
\item{names}{A character vector.}
\item{prefix, sep}{Prefix and separator for repaired names.}
}
\description{
This standardises names with the legacy approach that was used in
tidyverse packages (such as tibble, tidyr, and readxl) before
\code{\link[=vec_as_names]{vec_as_names()}} was implemented. This tool is meant to help
transitioning to the new name repairing standard and will be
deprecated and removed from the package some time in the future.
}
\examples{
if (rlang::is_installed("tibble")) {
library(tibble)
# Names repair is turned off by default in tibble:
try(tibble(a = 1, a = 2))
# You can turn it on by supplying a repair method:
tibble(a = 1, a = 2, .name_repair = "universal")
# If you prefer the legacy method, use `vec_as_names_legacy()`:
tibble(a = 1, a = 2, .name_repair = vec_as_names_legacy)
}
}
\keyword{internal}
vctrs/man/vec_data.Rd 0000644 0001762 0000144 00000002312 14276722575 014242 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/proxy.R
\name{vec_data}
\alias{vec_data}
\title{Extract underlying data}
\usage{
vec_data(x)
}
\arguments{
\item{x}{A vector or object implementing \code{vec_proxy()}.}
}
\value{
The data underlying \code{x}, free from any attributes except the names.
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
Extract the data underlying an S3 vector object, i.e. the underlying
(named) atomic vector, data frame, or list.
}
\section{Difference with \code{vec_proxy()}}{
\itemize{
\item \code{vec_data()} returns unstructured data. The only attributes
preserved are names, dims, and dimnames.
Currently, due to the underlying memory architecture of R, this
creates a full copy of the data for atomic vectors.
\item \code{vec_proxy()} may return structured data. This generic is the
main customisation point for accessing memory values in vctrs,
along with \code{\link[=vec_restore]{vec_restore()}}.
Methods must return a vector type. Records and data frames will
be processed rowwise.
}
}
\keyword{internal}
vctrs/man/vec_math.Rd 0000644 0001762 0000144 00000004033 14276722575 014264 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/numeric.R
\name{vec_math}
\alias{vec_math}
\alias{vec_math_base}
\title{Mathematical operations}
\usage{
vec_math(.fn, .x, ...)
vec_math_base(.fn, .x, ...)
}
\arguments{
\item{.fn}{A mathematical function from the base package, as a string.}
\item{.x}{A vector.}
\item{...}{Additional arguments passed to \code{.fn}.}
}
\description{
This generic provides a common dispatch mechanism for all regular unary
mathematical functions. It is used as a common wrapper around many of the
Summary group generics, the Math group generics, and a handful of other
mathematical functions like \code{mean()} (but not \code{var()} or \code{sd()}).
}
\details{
\code{vec_math_base()} is provided as a convenience for writing methods. It
calls the base \code{.fn} on the underlying \code{\link[=vec_data]{vec_data()}}.
}
\section{Included functions}{
\itemize{
\item From the \link{Summary} group generic:
\code{prod()}, \code{sum()}, \code{any()}, \code{all()}.
\item From the \link{Math} group generic:
\code{abs()}, \code{sign()}, \code{sqrt()}, \code{ceiling()}, \code{floor()}, \code{trunc()}, \code{cummax()},
\code{cummin()}, \code{cumprod()}, \code{cumsum()}, \code{log()}, \code{log10()}, \code{log2()},
\code{log1p()}, \code{acos()}, \code{acosh()}, \code{asin()}, \code{asinh()}, \code{atan()}, \code{atanh()},
\code{exp()}, \code{expm1()}, \code{cos()}, \code{cosh()}, \code{cospi()}, \code{sin()}, \code{sinh()},
\code{sinpi()}, \code{tan()}, \code{tanh()}, \code{tanpi()}, \code{gamma()}, \code{lgamma()},
\code{digamma()}, \code{trigamma()}.
\item Additional generics: \code{mean()}, \code{is.nan()}, \code{is.finite()}, \code{is.infinite()}.
}
Note that \code{median()} is currently not implemented, and \code{sd()} and
\code{var()} are currently not generic and so do not support custom
classes.
}
\examples{
x <- new_vctr(c(1, 2.5, 10))
x
abs(x)
sum(x)
cumsum(x)
}
\seealso{
\code{\link[=vec_arith]{vec_arith()}} for the equivalent for the arithmetic infix operators.
}
\keyword{internal}
vctrs/man/list_of.Rd 0000644 0001762 0000144 00000003274 14362266120 014124 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-list-of.R
\name{list_of}
\alias{list_of}
\alias{as_list_of}
\alias{is_list_of}
\alias{vec_ptype2.vctrs_list_of}
\alias{vec_cast.vctrs_list_of}
\title{\code{list_of} S3 class for homogenous lists}
\usage{
list_of(..., .ptype = NULL)
as_list_of(x, ...)
is_list_of(x)
\method{vec_ptype2}{vctrs_list_of}(x, y, ..., x_arg = "", y_arg = "")
\method{vec_cast}{vctrs_list_of}(x, to, ...)
}
\arguments{
\item{...}{Vectors to coerce.}
\item{.ptype}{If \code{NULL}, the default, the output type is determined by
computing the common type across all elements of \code{...}.
Alternatively, you can supply \code{.ptype} to give the output known type.
If \code{getOption("vctrs.no_guessing")} is \code{TRUE} you must supply this value:
this is a convenient way to make production code demand fixed types.}
\item{x}{For \code{as_list_of()}, a vector to be coerced to list_of.}
\item{y, to}{Arguments to \code{vec_ptype2()} and \code{vec_cast()}.}
\item{x_arg, y_arg}{Argument names for \code{x} and \code{y}. These are used
in error messages to inform the user about the locations of
incompatible types (see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
}
\description{
A \code{list_of} object is a list where each element has the same type.
Modifying the list with \code{$}, \code{[}, and \code{[[} preserves the constraint
by coercing all input items.
}
\details{
Unlike regular lists, setting a list element to \code{NULL} using \code{[[}
does not remove it.
}
\examples{
x <- list_of(1:3, 5:6, 10:15)
if (requireNamespace("tibble", quietly = TRUE)) {
tibble::tibble(x = x)
}
vec_c(list_of(1, 2), list_of(FALSE, TRUE))
}
vctrs/man/df_ptype2.Rd 0000644 0001762 0000144 00000004267 14315060307 014361 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-data-frame.R, R/type-tibble.R
\name{df_ptype2}
\alias{df_ptype2}
\alias{df_cast}
\alias{tib_ptype2}
\alias{tib_cast}
\title{Coercion between two data frames}
\usage{
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())
}
\arguments{
\item{x, y, to}{Subclasses of data frame.}
\item{...}{If you call \code{df_ptype2()} or \code{df_cast()} from a
\code{vec_ptype2()} or \code{vec_cast()} method, you must forward the dots
passed to your method on to \code{df_ptype2()} or \code{df_cast()}.}
\item{x_arg, y_arg}{Argument names for \code{x} and \code{y}. These are used
in error messages to inform the user about the locations of
incompatible types (see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{to_arg}{Argument name \code{to} used in error messages to
inform the user about the locations of incompatible types
(see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
}
\value{
\itemize{
\item When \code{x} and \code{y} are not compatible, an error of class
\code{vctrs_error_incompatible_type} is thrown.
\item When \code{x} and \code{y} are compatible, \code{df_ptype2()} returns the common
type as a bare data frame. \code{tib_ptype2()} returns the common type
as a bare tibble.
}
}
\description{
\code{df_ptype2()} and \code{df_cast()} are the two functions you need to
call from \code{vec_ptype2()} and \code{vec_cast()} methods for data frame
subclasses. See \link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}.
Their main job is to determine the common type of two data frames,
adding and coercing columns as needed, or throwing an incompatible
type error when the columns are not compatible.
}
vctrs/man/faq-error-scalar-type.Rd 0000644 0001762 0000144 00000004722 14404336324 016605 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq.R
\name{faq-error-scalar-type}
\alias{faq-error-scalar-type}
\title{FAQ - Error: Input must be a vector}
\description{
This error occurs when a function expects a vector and gets a scalar
object instead. This commonly happens when some code attempts to assign
a scalar object as column in a data frame:
\if{html}{\out{}}\preformatted{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.
}\if{html}{\out{
}}
}
\section{Vectorness in base R and in the tidyverse}{
In base R, almost everything is a vector or behaves like a vector. In
the tidyverse we have chosen to be a bit stricter about what is
considered a vector. The main question we ask ourselves to decide on the
vectorness of a type is whether it makes sense to include that object as
a column in a data frame.
The main difference is that S3 lists are considered vectors by base R
but in the tidyverse that’s not the case by default:
\if{html}{\out{}}\preformatted{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.
}\if{html}{\out{
}}
Defused function calls are another (more esoteric) example:
\if{html}{\out{}}\preformatted{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.
}\if{html}{\out{
}}
}
\section{I get a scalar type error but I think this is a bug}{
It’s possible the author of the class needs to do some work to declare
their class a vector. Consider reaching out to the author. We have
written a \link[=howto-faq-fix-scalar-type-error]{developer FAQ page} to
help them fix the issue.
}
vctrs/man/vec_ptype_full.Rd 0000644 0001762 0000144 00000002574 14276722575 015526 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/ptype-abbr-full.R
\name{vec_ptype_full}
\alias{vec_ptype_full}
\alias{vec_ptype_abbr}
\title{Vector type as a string}
\usage{
vec_ptype_full(x, ...)
vec_ptype_abbr(x, ..., prefix_named = FALSE, suffix_shape = TRUE)
}
\arguments{
\item{x}{A vector.}
\item{...}{These dots are for future extensions and must be empty.}
\item{prefix_named}{If \code{TRUE}, add a prefix for named vectors.}
\item{suffix_shape}{If \code{TRUE} (the default), append the shape of
the vector.}
}
\value{
A string.
}
\description{
\code{vec_ptype_full()} displays the full type of the vector. \code{vec_ptype_abbr()}
provides an abbreviated summary suitable for use in a column heading.
}
\section{S3 dispatch}{
The default method for \code{vec_ptype_full()} uses the first element of the
class vector. Override this method if your class has parameters that should
be prominently displayed.
The default method for \code{vec_ptype_abbr()} \code{\link[=abbreviate]{abbreviate()}}s \code{vec_ptype_full()}
to 8 characters. You should almost always override, aiming for 4-6
characters where possible.
These arguments are handled by the generic and not passed to methods:
\itemize{
\item \code{prefix_named}
\item \code{suffix_shape}
}
}
\examples{
cat(vec_ptype_full(1:10))
cat(vec_ptype_full(iris))
cat(vec_ptype_abbr(1:10))
}
\keyword{internal}
vctrs/man/vec_type.Rd 0000644 0001762 0000144 00000001502 14276722575 014312 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/vctrs-deprecated.R
\name{vec_type}
\alias{vec_type}
\alias{vec_type_common}
\alias{vec_type2}
\title{Deprecated type functions}
\usage{
vec_type(x)
vec_type_common(..., .ptype = NULL)
vec_type2(x, y, ...)
}
\arguments{
\item{x, y, ..., .ptype}{Arguments for deprecated functions.}
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
These functions have been renamed:
\itemize{
\item \code{vec_type()} => \code{\link[=vec_ptype]{vec_ptype()}}
\item \code{vec_type2()} => \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{vec_type_common()} => \code{\link[=vec_ptype_common]{vec_ptype_common()}}
}
}
\keyword{internal}
vctrs/man/new_list_of.Rd 0000644 0001762 0000144 00000000757 13465327536 015014 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/type-list-of.R
\name{new_list_of}
\alias{new_list_of}
\title{Create list_of subclass}
\usage{
new_list_of(x = list(), ptype = logical(), ..., class = character())
}
\arguments{
\item{x}{A list}
\item{ptype}{The prototype which every element of \code{x} belongs to}
\item{...}{Additional attributes used by subclass}
\item{class}{Optional subclass name}
}
\description{
Create list_of subclass
}
\keyword{internal}
vctrs/man/vec_cast.Rd 0000644 0001762 0000144 00000010502 14315060307 014241 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cast.R, R/type-bare.R
\name{vec_cast}
\alias{vec_cast}
\alias{vec_cast_common}
\alias{vec_cast.logical}
\alias{vec_cast.integer}
\alias{vec_cast.double}
\alias{vec_cast.complex}
\alias{vec_cast.raw}
\alias{vec_cast.character}
\alias{vec_cast.list}
\title{Cast a vector to a specified type}
\usage{
vec_cast(x, to, ..., x_arg = caller_arg(x), to_arg = "", call = caller_env())
vec_cast_common(..., .to = NULL, .arg = "", .call = caller_env())
\method{vec_cast}{logical}(x, to, ...)
\method{vec_cast}{integer}(x, to, ...)
\method{vec_cast}{double}(x, to, ...)
\method{vec_cast}{complex}(x, to, ...)
\method{vec_cast}{raw}(x, to, ...)
\method{vec_cast}{character}(x, to, ...)
\method{vec_cast}{list}(x, to, ...)
}
\arguments{
\item{x}{Vectors to cast.}
\item{to, .to}{Type to cast to. If \code{NULL}, \code{x} will be returned as is.}
\item{...}{For \code{vec_cast_common()}, vectors to cast. For
\code{vec_cast()}, \code{vec_cast_default()}, and \code{vec_restore()}, these
dots are only for future extensions and should be empty.}
\item{x_arg}{Argument name for \code{x}, used in error messages to
inform the user about the locations of incompatible types
(see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{to_arg}{Argument name \code{to} used in error messages to
inform the user about the locations of incompatible types
(see \code{\link[=stop_incompatible_type]{stop_incompatible_type()}}).}
\item{call, .call}{The execution environment of a currently
running function, e.g. \code{caller_env()}. The function will be
mentioned in error messages as the source of the error. See the
\code{call} argument of \code{\link[rlang:abort]{abort()}} for more information.}
\item{.arg}{An argument name as a string. This argument
will be mentioned in error messages as the input that is at the
origin of a problem.}
}
\value{
A vector the same length as \code{x} with the same type as \code{to},
or an error if the cast is not possible. An error is generated if
information is lost when casting between compatible types (i.e. when
there is no 1-to-1 mapping for a specific value).
}
\description{
\code{vec_cast()} provides directional conversions from one type of
vector to another. Along with \code{\link[=vec_ptype2]{vec_ptype2()}}, this generic forms
the foundation of type coercions in vctrs.
}
\section{Implementing coercion methods}{
\itemize{
\item For an overview of how these generics work and their roles in vctrs,
see \code{\link[=theory-faq-coercion]{?theory-faq-coercion}}.
\item For an example of implementing coercion methods for simple vectors,
see \code{\link[=howto-faq-coercion]{?howto-faq-coercion}}.
\item For an example of implementing coercion methods for data frame
subclasses, see
\code{\link[=howto-faq-coercion-data-frame]{?howto-faq-coercion-data-frame}}.
\item For a tutorial about implementing vctrs classes from scratch, see
\code{vignette("s3-vector")}.
}
}
\section{Dependencies of \code{vec_cast_common()}}{
\subsection{vctrs dependencies}{
\itemize{
\item \code{\link[=vec_ptype2]{vec_ptype2()}}
\item \code{\link[=vec_cast]{vec_cast()}}
}
}
\subsection{base dependencies}{
Some functions enable a base-class fallback for
\code{vec_cast_common()}. In that case the inputs are deemed compatible
when they have the same \link[base:typeof]{base type} and inherit from
the same base class.
}
}
\examples{
# x is a double, but no information is lost
vec_cast(1, integer())
# When information is lost the cast fails
try(vec_cast(c(1, 1.5), integer()))
try(vec_cast(c(1, 2), logical()))
# You can suppress this error and get the partial results
allow_lossy_cast(vec_cast(c(1, 1.5), integer()))
allow_lossy_cast(vec_cast(c(1, 2), logical()))
# By default this suppress all lossy cast errors without
# distinction, but you can be specific about what cast is allowed
# by supplying prototypes
allow_lossy_cast(vec_cast(c(1, 1.5), integer()), to_ptype = integer())
try(allow_lossy_cast(vec_cast(c(1, 2), logical()), to_ptype = integer()))
# No sensible coercion is possible so an error is generated
try(vec_cast(1.5, factor("a")))
# Cast to common type
vec_cast_common(factor("a"), factor(c("a", "b")))
}
\seealso{
Call \code{\link[=stop_incompatible_cast]{stop_incompatible_cast()}} when you determine from the
attributes that an input can't be cast to the target type.
}
vctrs/man/list_drop_empty.Rd 0000644 0001762 0000144 00000001163 14315060307 015672 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/empty.R
\name{list_drop_empty}
\alias{list_drop_empty}
\title{Drop empty elements from a list}
\usage{
list_drop_empty(x)
}
\arguments{
\item{x}{A list.}
}
\description{
\code{list_drop_empty()} removes empty elements from a list. This includes \code{NULL}
elements along with empty vectors, like \code{integer(0)}. This is equivalent to,
but faster than, \code{vec_slice(x, list_sizes(x) != 0L)}.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\examples{
x <- list(1, NULL, integer(), 2)
list_drop_empty(x)
}
vctrs/man/figures/ 0000755 0001762 0000144 00000000000 14532404540 013633 5 ustar ligges users vctrs/man/figures/sizes.graffle 0000644 0001762 0000144 00000006530 14211412552 016320 0 ustar ligges users ]ks
]kFoN8& )" c3B'.[B:=}Q0{c?%h?'_ޞީ
ZPa]Neu-X.u{(TFܸ: pߕ岤`TIu藻B/X]N@I#Tͺ:foNT ݿYN:xOޜ+9s=g:)rIA/2䴼):TmtB1;mqYpELPYiժ,'EMu.2 |#05_8Բ\ur7VNqŷk)sawJ+s6}" vwN;^s^WMQy{L[ܮ+'[㙺i"7A۞9[hʑRL"E,G AÁT,cXpxWV,i"|*W
?U#sjo<-UZgnٍ%?0_nGW5i>XZkpy|ȿ^YZOJ}JZ3&[;ip 4DWNDYdQ
S;@uPǔ1OT#оzʅM>