Auto-Coercion Rules for Mutable Objects
Source:R/aaa08_squarebrackets_coercion.R
aaa08_squarebrackets_coercion.Rd
This help page describes the auto-coercion rules of the mutable classes,
as they are handled by the 'squarebrackets' package.
This useful information for users who wish to intend to employ
Pass-by-Reference semantics
as provided by 'squarebrackets'.
mutable_atomic
Mutable atomic objects are automatically coerced to fit the modified subset values,
when modifying through copy, just like regular atomic classes.
For example, replacing one or multiple values in an integer vector
(type int
)
with a decimal number
(type dbl
)
will coerce the entire vector to type dbl
.
Replacing or transforming subsets of mutable atomic objects by reference
does not support coercion.
Thus, for example, the following code,
x <- mutable_atomic(1:16)
sb_set(x, i = 1:6, rp = 8.5)
#> coercing replacement to integer
print(x)
#> [1] 8 8 8 8 8 8 7 8 9 10 11 12 13 14 15 16
#> mutable_atomic
#> typeof: integer
gives c(rep(8, 6) 7:16)
instead of c(rep(8.5, 6), 7:16)
,
because x
is of type integer
, so rp
is interpreted as type integer
also.
data.table, when replacing/transforming whole columns
A data.table is actually a list made mutable,
where each column is itself a list.
As such, replacing/transforming whole columns,
without specifying rows (not even 1:nrow(x)
),
allows completely changing the type of the column.
data.table, when partially replacing/transforming columns
If rows are specified in the sb2_set method,
and thus not whole columns but parts of columns are replaced or transformed,
no auto-coercion takes place.
I.e.: replacing/transforming a value in an integer (int
) column to become 1.5
,
will not coerce the column to the decimal type (dbl
);
instead, the replacement value 1.5
is coerced to integer 1
.
The sb2_mod method, however,
allows for coercion just like regular data.frame objects.
Views of Lists
Regular lists are treated as immutable by 'squarebrackets'.
But remember that a list is a
(potentially hierarchical)
structure of references to other objects.
Thus, even if a list itself is not treated as mutable,
subsets of a list which are themselves mutable classes, are mutable.
For example,
if you have a list of data.table
objects,
the data.tables themselves are mutable.
Therefore, the following will work:
x <- list(
a = data.table::data.table(cola = 1:10, colb = letters[1:10]),
b = data.table::data.table(cola = 11:20, colb = letters[11:20])
)
myref <- x$a
sb2_set(myref, vars = "cola", tf = \(x)x^2)
Notice in the above code that myref
is not a copy of x$a
,
since they have the same address.
Thus changing myref
also changes x$a
.
In other words: myref
is what could be called a "view" of x$a
.
Notice also that sb2_set(x$a, ...)
will not work,
since sb_set/sb2_set requires actual variables,
similar to in-place functions in the style of `myfun()<-`
.
The auto-coercion rules of Views of Lists,
depends entirely on the object itself.
Thus if the View is a data.table,
coercion rules of data.tables apply.
And if the View is a mutable_atomic matrix,
coercion rules of mutable_atomic matrices apply,
etc.
Examples
# Coercion examples - mutable_atomic ====
x <- as.mutable_atomic(1:16)
sb_set(x, i = 1:6, rp = 8.5) # 8.5 coerced to 8, because `x` is of type `integer`
#> coercing replacement to integer
print(x)
#> [1] 8 8 8 8 8 8 7 8 9 10 11 12 13 14 15 16
#> mutable_atomic
#> typeof: integer
#############################################################################
# Coercion examples - data.table - whole columns ====
# sb_mod():
obj <- data.table::data.table(
a = 1:10, b = letters[1:10], c = 11:20, d = factor(letters[1:10])
)
str(obj) # notice that columns "a" and "c" are INTEGER (`int`)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: int 1 2 3 4 5 6 7 8 9 10
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: int 11 12 13 14 15 16 17 18 19 20
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
sb2_mod(
obj, vars = is.numeric,
tf = sqrt # SAFE: row=NULL & obs = NULL, so coercion performed
)
#> a b c d
#> <num> <char> <num> <fctr>
#> 1: 1.000000 a 3.316625 a
#> 2: 1.414214 b 3.464102 b
#> 3: 1.732051 c 3.605551 c
#> 4: 2.000000 d 3.741657 d
#> 5: 2.236068 e 3.872983 e
#> 6: 2.449490 f 4.000000 f
#> 7: 2.645751 g 4.123106 g
#> 8: 2.828427 h 4.242641 h
#> 9: 3.000000 i 4.358899 i
#> 10: 3.162278 j 4.472136 j
# sb_set():
sb2_set(
obj, vars = is.numeric,
tf = sqrt # SAFE: row=NULL & obs = NULL, so coercion performed
)
str(obj)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: num 1 1.41 1.73 2 2.24 ...
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: num 3.32 3.46 3.61 3.74 3.87 ...
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
#############################################################################
# Coercion examples - data.table - partial columns ====
# sb_mod():
obj <- data.table::data.table(
a = 1:10, b = letters[1:10], c = 11:20, d = factor(letters[1:10])
)
str(obj) # notice that columns "a" and "c" are INTEGER (`int`)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: int 1 2 3 4 5 6 7 8 9 10
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: int 11 12 13 14 15 16 17 18 19 20
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
sb2_mod(
obj, obs = ~ (a >= 2) & (c <= 17), vars = is.numeric,
tf = sqrt # SAFE: coercion performed
)
#> a b c d
#> <num> <char> <num> <fctr>
#> 1: 1.000000 a 11.000000 a
#> 2: 1.414214 b 3.464102 b
#> 3: 1.732051 c 3.605551 c
#> 4: 2.000000 d 3.741657 d
#> 5: 2.236068 e 3.872983 e
#> 6: 2.449490 f 4.000000 f
#> 7: 2.645751 g 4.123106 g
#> 8: 8.000000 h 18.000000 h
#> 9: 9.000000 i 19.000000 i
#> 10: 10.000000 j 20.000000 j
# sb_set():
obj <- data.table::data.table(
a = 1:10, b = letters[1:10], c = 11:20, d = factor(letters[1:10])
)
str(obj) # notice that columns "a" and "c" are INTEGER (`int`)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: int 1 2 3 4 5 6 7 8 9 10
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: int 11 12 13 14 15 16 17 18 19 20
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
sb2_set(
obj, obs = ~ (a >= 2) & (c <= 17), vars = is.numeric,
tf = sqrt
# WARNING: sqrt() results in `dbl`, but columns are `int`, so decimals lost
)
#> Warning: 1.414214 (type 'double') at RHS position 1 out-of-range(NA) or truncated (precision lost) when assigning to type 'integer' (column 1 named 'a')
#> Warning: 3.464102 (type 'double') at RHS position 1 out-of-range(NA) or truncated (precision lost) when assigning to type 'integer' (column 3 named 'c')
print(obj)
#> a b c d
#> <int> <char> <int> <fctr>
#> 1: 1 a 11 a
#> 2: 1 b 3 b
#> 3: 1 c 3 c
#> 4: 2 d 3 d
#> 5: 2 e 3 e
#> 6: 2 f 4 f
#> 7: 2 g 4 g
#> 8: 8 h 18 h
#> 9: 9 i 19 i
#> 10: 10 j 20 j
obj <- data.table::data.table(
a = 1:10, b = letters[1:10], c = 11:20, d = factor(letters[1:10])
)
str(obj)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: int 1 2 3 4 5 6 7 8 9 10
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: int 11 12 13 14 15 16 17 18 19 20
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
dt_setcoe(obj, vars = is.numeric, v = as.numeric)
str(obj)
#> Classes 'data.table' and 'data.frame': 10 obs. of 4 variables:
#> $ a: num 1 2 3 4 5 6 7 8 9 10
#> $ b: chr "a" "b" "c" "d" ...
#> $ c: num 11 12 13 14 15 16 17 18 19 20
#> $ d: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
#> - attr(*, ".internal.selfref")=<externalptr>
sb2_set(obj,
obs = ~ (a >= 2) & (c <= 17), vars = is.numeric,
tf = sqrt # SAFE: coercion performed by dt_setcoe(); so no warnings
)
print(obj)
#> a b c d
#> <num> <char> <num> <fctr>
#> 1: 1.000000 a 11.000000 a
#> 2: 1.414214 b 3.464102 b
#> 3: 1.732051 c 3.605551 c
#> 4: 2.000000 d 3.741657 d
#> 5: 2.236068 e 3.872983 e
#> 6: 2.449490 f 4.000000 f
#> 7: 2.645751 g 4.123106 g
#> 8: 8.000000 h 18.000000 h
#> 9: 9.000000 i 19.000000 i
#> 10: 10.000000 j 20.000000 j
#############################################################################
# View of List ====
x <- list(
a = data.table::data.table(cola = 1:10, colb = letters[1:10]),
b = data.table::data.table(cola = 11:20, colb = letters[11:20])
)
print(x)
#> $a
#> cola colb
#> <int> <char>
#> 1: 1 a
#> 2: 2 b
#> 3: 3 c
#> 4: 4 d
#> 5: 5 e
#> 6: 6 f
#> 7: 7 g
#> 8: 8 h
#> 9: 9 i
#> 10: 10 j
#>
#> $b
#> cola colb
#> <int> <char>
#> 1: 11 k
#> 2: 12 l
#> 3: 13 m
#> 4: 14 n
#> 5: 15 o
#> 6: 16 p
#> 7: 17 q
#> 8: 18 r
#> 9: 19 s
#> 10: 20 t
#>
myref <- x$a
address(myref) == address(x$a) # they are the same
#> [1] TRUE
sb2_set(myref, vars = "cola", tf = \(x)x^2)
print(x) # notice x has been changed
#> $a
#> cola colb
#> <num> <char>
#> 1: 1 a
#> 2: 4 b
#> 3: 9 c
#> 4: 16 d
#> 5: 25 e
#> 6: 36 f
#> 7: 49 g
#> 8: 64 h
#> 9: 81 i
#> 10: 100 j
#>
#> $b
#> cola colb
#> <int> <char>
#> 1: 11 k
#> 2: 12 l
#> 3: 13 m
#> 4: 14 n
#> 5: 15 o
#> 6: 16 p
#> 7: 17 q
#> 8: 18 r
#> 9: 19 s
#> 10: 20 t
#>