I saw +0L
used in an answer to a question and found out that it works well with matrices / data frames / data tables where as.integer()
would be unable to preserve the initial data classes.
> a <- matrix(TRUE, nrow=3, ncol=3)
> a
[,1] [,2] [,3]
[1,] TRUE TRUE TRUE
[2,] TRUE TRUE TRUE
[3,] TRUE TRUE TRUE
> as.integer(a)
[1] 1 1 1 1 1 1 1 1 1
> a+0L
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
- Is there other differences between these approaches?
- What are the pros and cons and caveats when using one or the other?
[edit:] lots of wisdom in comments! Apparently there is many different ways to achieve the same result, some of which I had no idea about, so:
- What are the other ways to achieve what
a+0L
does?
(This answer adds no other alternative to the ones already present, but I'm posting just to tidy up comments in this thread.)
as.integer
, by definition, behaves likeas.vector
, i.e. it strips all attributes ("dim" included) to create an R vector. It won't, just, return the same object with a changedtypeof
. To restore attributes after the coercion,"dim<-"
,"names<-"
,"class<-"
etc. need to be called explicitly or via a function that stores attributes of its arguments (e.g."[<-"
). E.g."dim<-"(as.integer(a), dim(a))
orarray(as.integer(a), dim(a))
ora[] <- as.integer(a)
. A benchmark:In the above,
"dim<-"
justs adds an attribute to the createdas.integer(x)
,array
allocates a new vector to store the createdas.integer(x)
, and"[<-"
changes "x" so that it can accept the values of the createdas.integer(x)
and, then, iterates through "x" to insert its new values.The
"[<-"
method, though, has a disadvantage:Or:
But:
I.e.
"[<-"
won't change thetypeof
of the replaceable object if thetypeof
of replacement object is higher. Subassignment (i.e."[<-"
) coerces either the object to be replaced or the replacing object or none depending on theirtypeof
s (this is done bySubassignTypeFix
). @Josh O'Brien notes the possibility for a difference to exist in the behaviour of"[<-"
if the indices are missing. To be honest, I could not find a specific treatment in such case, as in, for exampledo_subset_dflt
("["
) that indirectly handles missingness.As already mentioned, there is, also,
"storage.mode<-"
to change thetypeof
of an object:Similar in efficiency to
"dim<-"
since they both coerce once and store an attribute.Binary operations (as mentioned by James and Konrad Rudolph) coerce their arguments to suitable
typeof
and keep attributes ("dim", "names", "class" etc.) depending on rules regarding the two arguments. (Section "Value" in?Arithmetic
)x + 0L
is an element wise operation onx
; as such, it often preserves the shape of the data.as.integer
isn’t: it takes the whole structure – here, a matrix – and converts it into a one-dimensional integer vector.That said, in the general case I’d strongly suggest using
as.integer
and discourage+ 0L
as a clever hack (remember: often, clever ≠ good). If you want to preserve the shape of data I suggest using David’s method from the comments, rather than the+ 0L
hack:This uses the normal meaning of
as.integer
, but the result is assigned to the individual elements ofa
, rather thana
itself. In other words,a
’s shape remains untouched.Adding
0L
promotesa
to integer as described in?Arithmetic
:As a consequence any arithmetic operation using
a
and the identity element for that operation (but doesn't have to go to numeric at some point, eg/
and^
) will work:Unary operations will also work, so perhaps the "best" code golf version is:
This has a parallel with the common trick of using
!!a
to convert a numeric object to logical.Converting back to logical:
An alternative, and perhaps clearer, approach is to change the
storage.mode
ofa
directly: