vctrs/ 0000755 0001762 0000144 00000000000 14042554003 011410 5 ustar ligges users vctrs/NAMESPACE 0000644 0001762 0000144 00000043565 14042545721 012653 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_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(cnd_body,vctrs_error_cast_lossy)
S3method(cnd_body,vctrs_error_incompatible_size)
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_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(conditionMessage,vctrs_error_cast_lossy)
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(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,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_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,POSIXlt)
S3method(vec_proxy_compare,array)
S3method(vec_proxy_compare,data.frame)
S3method(vec_proxy_compare,default)
S3method(vec_proxy_compare,integer64)
S3method(vec_proxy_compare,list)
S3method(vec_proxy_compare,raw)
S3method(vec_proxy_compare,vctrs_rcrd)
S3method(vec_proxy_equal,POSIXlt)
S3method(vec_proxy_equal,array)
S3method(vec_proxy_equal,data.frame)
S3method(vec_proxy_equal,default)
S3method(vec_proxy_order,array)
S3method(vec_proxy_order,data.frame)
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,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_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,POSIXt)
S3method(vec_ptype_abbr,data.frame)
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,data.frame)
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_of)
export(list_sizes)
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_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(validate_list_of)
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_chop)
export(vec_compare)
export(vec_count)
export(vec_data)
export(vec_default_cast)
export(vec_default_ptype2)
export(vec_detect_complete)
export(vec_duplicate_any)
export(vec_duplicate_detect)
export(vec_duplicate_id)
export(vec_empty)
export(vec_equal)
export(vec_equal_na)
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_is)
export(vec_is_empty)
export(vec_is_list)
export(vec_list_cast)
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_rbind)
export(vec_recycle)
export(vec_recycle_common)
export(vec_rep)
export(vec_rep_each)
export(vec_repeat)
export(vec_restore)
export(vec_seq_along)
export(vec_set_names)
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,quantile)
useDynLib(vctrs, .registration = TRUE)
vctrs/LICENSE.note 0000644 0001762 0000144 00000041367 13753021253 013400 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 13753021253 012417 0 ustar ligges users YEAR: 2020
COPYRIGHT HOLDER: vctrs authors
vctrs/README.md 0000644 0001762 0000144 00000007370 14030312432 012671 0 ustar ligges users
# vctrs
[](https://codecov.io/github/r-lib/vctrs?branch=master)
 [](https://github.com/r-lib/vctrs/actions)
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("devtools")
devtools::install_github("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 14042546502 012170 5 ustar ligges users vctrs/man/vctrs-data-frame.Rd 0000644 0001762 0000144 00000000757 13712271424 015631 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 00000025753 14027045462 016353 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: 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: 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(0)
#> 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 > and .
}\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(0)
#> Levels: a c b
vec_ptype2(factor("b"), factor(c("a", "c")))
#> factor(0)
#> 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}{
Coercible classes form a coercion (or subtyping) hierarchy. Here is a
simplified diagram of the hierarchy for base types. 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}
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 13663716767 015311 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 13655017473 021223 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/vec_seq_along.Rd 0000644 0001762 0000144 00000001355 13622451540 015270 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_list_cast.Rd 0000644 0001762 0000144 00000001421 13622451540 015277 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cast-list.R
\name{vec_list_cast}
\alias{vec_list_cast}
\title{Cast a list to vector of specific type}
\usage{
vec_list_cast(x, to, ..., x_arg = "", to_arg = "")
}
\arguments{
\item{x}{A list}
\item{to}{Type to coerce to}
\item{...}{These dots are for future extensions and must be empty.}
}
\description{
This is a function for developers to use when extending vctrs. It casts
a list to a more specific vectoring type, keeping the length constant.
It does this by discarding (with a warning), any elements after the 1.
It is called from \code{vec_cast.XYZ.list()} methods to preserve symmetry with
\code{vec_cast.list.XYZ()}.
}
\details{
See \code{vignette("s3-vector")} for details.
}
\keyword{internal}
vctrs/man/maybe_lossy_cast.Rd 0000644 0001762 0000144 00000005236 14027045462 016027 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,
details = NULL,
message = NULL,
class = NULL,
.deprecation = FALSE
)
}
\arguments{
\item{result}{The result of a potentially lossy cast.}
\item{x}{Vectors}
\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{...}{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 names for \code{x}, \code{y}, and \code{to}. Used in
error messages to inform the user about the locations of incompatible
types.}
\item{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{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{class}{Only use these fields when creating a subclass.}
\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 00000005360 13753021253 014023 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)
vec_rep_each(x, 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 recycled to the size of \code{x}.}
}
\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:\preformatted{compressed <- vec_unrep(x)
identical(x, vec_rep_each(compressed$key, compressed$times))
}
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 00000005152 13663716767 014566 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 = "", y_arg = "")
}
\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()}}).}
}
\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/new_rcrd.Rd 0000644 0001762 0000144 00000001702 13723213047 014262 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 14027045462 014764 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/faq/ 0000755 0001762 0000144 00000000000 14042554003 012732 5 ustar ligges users vctrs/man/faq/internal/ 0000755 0001762 0000144 00000000000 14024442541 014551 5 ustar ligges users vctrs/man/faq/internal/ptype2-identity.Rmd 0000644 0001762 0000144 00000006123 13653027721 020277 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 14024441431 014717 5 ustar ligges users vctrs/man/faq/developer/reference-compatibility.Rmd 0000644 0001762 0000144 00000005762 13671672047 022222 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 13653027721 021724 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 00000004435 13712307232 023436 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::vec_assert(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::vec_assert(my_df)
dplyr::slice(my_df, 1)
```
vctrs/man/faq/developer/howto-coercion.Rmd 0000644 0001762 0000144 00000021047 13655017473 020344 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 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-coercion.Rmd 0000644 0001762 0000144 00000023050 13653764444 020517 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
Coercible classes form a coercion (or subtyping) hierarchy. Here is a simplified diagram of the hierarchy for base types. 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}
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 13655017473 022345 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 13653764444 020323 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 14042546502 013715 5 ustar ligges users vctrs/man/faq/user/faq-error-scalar-type.Rmd 0000644 0001762 0000144 00000003043 13712305520 020475 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[1:3]
# But not in vctrs
vctrs::vec_slice(fit, 1:3)
```
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 13712266564 021164 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 00000000210 13723213047 014534 0 ustar ligges users
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
options(cli.unicode = FALSE)
library(vctrs)
```
vctrs/man/vec_is_list.Rd 0000644 0001762 0000144 00000001207 13717173502 014766 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/assert.R
\name{vec_is_list}
\alias{vec_is_list}
\title{Is the object a list?}
\usage{
vec_is_list(x)
}
\arguments{
\item{x}{An object.}
}
\description{
\code{vec_is_list()} tests if \code{x} is considered a list in the vctrs sense. It
returns \code{TRUE} if:
\itemize{
\item \code{x} is a bare list with no class.
\item \code{x} is a list explicitly inheriting from \code{"list"}.
}
}
\details{
Notably, data frames and S3 record style classes like POSIXlt are not
considered lists.
}
\examples{
vec_is_list(list())
vec_is_list(list_of(1))
vec_is_list(data.frame())
}
vctrs/man/vctrs-conditions.Rd 0000644 0001762 0000144 00000005207 13654721330 015775 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
)
stop_incompatible_cast(
x,
to,
...,
x_arg,
to_arg,
details = NULL,
message = NULL,
class = NULL
)
stop_incompatible_op(
op,
x,
y,
details = NULL,
...,
message = NULL,
class = NULL
)
stop_incompatible_size(
x,
y,
x_size,
y_size,
...,
x_arg,
y_arg,
details = NULL,
message = NULL,
class = NULL
)
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{x_ptype, to_ptype}{Suppress only the casting errors where \code{x}
or \code{to} match these \link[=vec_ptype]{prototypes}.}
\item{subclass}{Use if you want to further customize the class.}
}
\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"),
NULL,
"",
lossy = c(FALSE, FALSE),
x_arg = "",
to_arg = ""
)
# If `lossy` has any `TRUE`, an error is thrown:
try(maybe_lossy_cast(
c("foo", "bar"),
NULL,
"",
lossy = c(FALSE, TRUE),
x_arg = "",
to_arg = ""
))
# Unless lossy casts are allowed:
allow_lossy_cast(
maybe_lossy_cast(
c("foo", "bar"),
NULL,
"",
lossy = c(FALSE, TRUE),
x_arg = "",
to_arg = ""
)
)
}
\keyword{internal}
vctrs/man/vec_c.Rd 0000644 0001762 0000144 00000006740 13671672047 013560 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")
)
}
\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()}}.}
}
\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 14027045462 014461 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 00000010213 14022644673 020113 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 13650511520 014750 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 14022644673 020047 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 13663716767 014314 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 00000004305 13705026323 016204 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.
}
\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()}
}
}
\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 13712211241 014235 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 13564465066 013744 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/vctrs-package.Rd 0000644 0001762 0000144 00000002310 14027045462 015207 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{
\if{html}{\figure{logo.png}{options: align='right'}}
\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 Report bugs at \url{https://github.com/r-lib/vctrs/issues}
}
}
\author{
\strong{Maintainer}: Lionel Henry \email{lionel@rstudio.com}
Authors:
\itemize{
\item Hadley Wickham \email{hadley@rstudio.com}
\item Davis Vaughan \email{davis@rstudio.com}
}
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 RStudio [copyright holder]
}
}
\keyword{internal}
vctrs/man/vec_equal.Rd 0000644 0001762 0000144 00000003015 13663716767 014446 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}
\alias{vec_equal_na}
\title{Test if two vectors are equal}
\usage{
vec_equal(x, y, na_equal = FALSE, .ptype = NULL)
vec_equal_na(x)
}
\arguments{
\item{x}{Vectors with compatible types and lengths.}
\item{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. Will only contain \code{NA}s if \code{na_equal} is \code{FALSE}.
}
\description{
\code{vec_equal_na()} tests a special case: equality with \code{NA}. It is similar to
\link{is.na} but:
\itemize{
\item Considers the missing element of a list to be \code{NULL}.
\item Considered data frames and records to be missing if every component
is missing.
This preserves the invariant that \code{vec_equal_na(x)} is equal to
\code{vec_equal(x, vec_init(x), na_equal = TRUE)}.
}
}
\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_na(c(TRUE, FALSE, NA))
vec_equal(5, 1:10)
vec_equal("d", letters[1:10])
df <- data.frame(x = c(1, 1, 2, 1, NA), y = c(1, 2, 1, NA, NA))
vec_equal(df, data.frame(x = 1, y = 2))
vec_equal_na(df)
}
vctrs/man/vec_group.Rd 0000644 0001762 0000144 00000004704 14027045462 014457 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 00000034425 14022644660 020174 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: 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 13622451540 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 00000022211 13653027721 016165 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 and .
vec_ptype2(new_natural(1), 2:3)
#> Error: Can't combine and .
}\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 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: Can't convert to .
vec_c(1.5, new_natural(1))
#> Error: Can't convert 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/as-is.Rd 0000644 0001762 0000144 00000000602 13650511520 013465 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/vec_cbind_frame_ptype.Rd 0000644 0001762 0000144 00000001344 14027045462 016772 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 00000011462 13671672047 014264 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{vec_unchop}
\title{Chopping}
\usage{
vec_chop(x, indices = NULL)
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}. If \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{vec_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 of size \code{vec_size(indices)} or, if \code{indices == NULL},
\code{vec_size(x)}.
\item \code{vec_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{vec_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()}.
}
If \code{indices} selects every value in \code{x} exactly once, in any order, then
\code{vec_unchop()} is the inverse of \code{vec_chop()} and the following invariant
holds:\preformatted{vec_unchop(vec_chop(x, indices), indices) == x
}
}
\section{Dependencies of \code{vec_chop()}}{
\itemize{
\item \code{\link[=vec_slice]{vec_slice()}}
}
}
\section{Dependencies of \code{vec_unchop()}}{
\itemize{
\item \code{\link[=vec_c]{vec_c()}}
}
}
\examples{
vec_chop(1:5)
vec_chop(1:5, list(1, 1:2))
vec_chop(mtcars, list(1:3, 4:6))
# If `indices` selects every value in `x` exactly once,
# in any order, then `vec_unchop()` inverts `vec_chop()`
x <- c("a", "b", "c", "d")
indices <- list(2, c(3, 1), 4)
vec_chop(x, indices)
vec_unchop(vec_chop(x, indices), indices)
# When unchopping, size 1 elements of `x` are recycled
# to the size of the corresponding index
vec_unchop(list(1, 2:3), 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)
vec_unchop(lst, list(c(3, 2), c(1, 4)), name_spec = "{outer}_{inner}")
# An alternative implementation of `ave()` can be constructed using
# `vec_chop()` and `vec_unchop()` in combination with `vec_group_loc()`
ave2 <- function(.x, .by, .f, ...) {
indices <- vec_group_loc(.by)$loc
chopped <- vec_chop(.x, indices)
out <- lapply(chopped, .f, ...)
vec_unchop(out, indices)
}
breaks <- warpbreaks$breaks
wool <- warpbreaks$wool
ave2(breaks, wool, mean)
identical(
ave2(breaks, wool, mean),
ave(breaks, wool, FUN = mean)
)
}
vctrs/man/op-empty-default.Rd 0000644 0001762 0000144 00000001011 13650511520 015640 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 00000005225 13671672047 014430 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:\preformatted{vec_c(outer = c(inner1 = 1, inner2 = 2))
}
\item Unnamed vector:\preformatted{vec_c(outer = 1:2)
}
}
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 00000004357 13712271424 014115 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,
.name_repair = c("check_unique", "unique", "universal", "minimal")
)
}
\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{.name_repair}{One of \code{"check_unique"}, \code{"unique"}, \code{"universal"} or
\code{"minimal"}. See \code{\link[=vec_as_names]{vec_as_names()}} for the meaning of these options.}
}
\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 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 spliced. 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 00000002727 13712271424 015424 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 \code{"names"} and \code{"row.names"} attributes override input in \code{x} and \code{n},
respectively:
\itemize{
\item \code{"names"} is used if provided, overriding existing names in \code{x}
\item \code{"row.names"} is used if provided, if \code{n} is provided it must be
consistent.
}}
}
\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 00000006251 13712271424 014547 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")
)
}
\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"} or
\code{"minimal"}. See \code{\link[=vec_as_names]{vec_as_names()}} for the meaning of these options.}
}
\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 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"} 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 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 spliced. 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 spliced
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 00000002400 14042540502 014415 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{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{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, "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, "desc")
}
vctrs/man/vec_fill_missing.Rd 0000644 0001762 0000144 00000002756 14027045462 016007 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 14027045462 014602 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 00000002474 13663716767 015717 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_equal_na]{vec_equal_na()}}).
}
\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.
If the proxy for \code{x} is a data frame, \code{vec_proxy_equal()} is
recursively applied on all columns as well.
}
\section{Dependencies}{
\itemize{
\item \code{\link[=vec_proxy]{vec_proxy()}} called by default
}
}
\keyword{internal}
vctrs/man/obj_print.Rd 0000644 0001762 0000144 00000001665 13637142417 014463 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 00000010510 14027045462 014454 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 = "")
vec_ptype_common(..., .ptype = NULL)
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{.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.}
}
\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 00000006231 14022644653 017245 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: 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: 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 13622451540 016424 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 14027045462 014225 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 13723213047 014245 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 00000003701 13721736206 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{validate_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, ...)
validate_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}{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{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 00000004116 13663716767 014401 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 = "")
df_cast(x, to, ..., x_arg = "", to_arg = "")
tib_ptype2(x, y, ..., x_arg = "", y_arg = "")
tib_cast(x, to, ..., x_arg = "", to_arg = "")
}
\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}{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{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{to_arg}{Argument names for \code{x} and \code{to}. 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()}}).}
}
\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 00000004756 14027045674 016622 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: All columns in a tibble must be vectors.
#> x Column `x` is a function.
fit <- lm(1:3 ~ 1)
tibble::tibble(x = fit)
#> Error: 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[1:3]
#> $coefficients
#> (Intercept)
#> 2
#>
#> $residuals
#> 1 2 3
#> -1.000000e+00 -3.885781e-16 1.000000e+00
#>
#> $effects
#> (Intercept)
#> -3.4641016 0.3660254 1.3660254
# But not in vctrs
vctrs::vec_slice(fit, 1:3)
#> Error: Input 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: Input 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 14027045462 015511 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 14027045462 014275 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 13622451540 015000 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 00000007300 13663775021 014256 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 = "", to_arg = "")
vec_cast_common(..., .to = NULL)
\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, to_arg}{Argument names for \code{x} and \code{to}. 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()}}).}
}
\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/figures/ 0000755 0001762 0000144 00000000000 14042546502 013634 5 ustar ligges users vctrs/man/figures/sizes.graffle 0000644 0001762 0000144 00000006530 13622451540 016325 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>