The aim of the package listArray is to create a data
object which looks like an array, but behaves like a list. Thus,
something like
x[letters[1:3]] should address one element andx[letters[1:5]] should be possible,
too.Additionally, any R object should possible as index, thus it should work:
x[1],x["a"],x[1,2],x[c(1,2)],x[mean],x[NULL], orx[iris].The package hash does something similar. But the keys follow the list element naming convention and does not allow array indices.
Note that neither speed nor memory efficiency have any importance for this implementation.
listArray from a vector, matrix or
arrayA listArray is a list (or environment), therefore list
(or environment) operations can be applied:
l <- listArray(1)
length(l)
#> [1] 1
names(l)
#> [1] "58,0a,00,00,00,03,00,04,06,00,00,03,05,00,00,00,00,05,55,54,46,2d,38,00,00,00,13,00,00,00,01,00,00,00,0e,00,00,00,01,3f,f0,00,00,00,00,00,00"
l[[1]]
#> [1] 1
class(l)
#> [1] "listArray" "list"Thus, every R object which used in the index is translated to a unique name. The original indices can be obtained as string by:
For creating vectors the listArray function can be
used.
For matrices or arrays:
Since for listArrays l[1] and
l["A"] is something different, you have to decide with
named vectors, matrices or arrays if you use the names or numbers. The
default is to use names if available.
m <- matrix(1:4, 2, 2)
colnames(m) <- LETTERS[1:2]
l <- listArray(m)
keys(l)
#> [1] "1, \"A\"" "2, \"A\"" "1, \"B\"" "2, \"B\""You can force with use.names=FALSE that always numerical
indices will used
Sometimes you may not want to store certain elements of vector, matrix or array; just think in terms of sparse objects.
The parameter ignore can be either a table of values to
exclude or a function which returns for a vector a logical vector with
TRUE (= value excluded) and FALSE (= value
included).
Rather than using a list it is possible to use an environment as base which might be of interest for package developers.
listArray objectYou simply use the [ operator to access
listArray elements.
listArray and vectorA listArray considers each index element as different.
The following works for vectors:
But l[1:2] returns NULL since the index
1:2 does not exist.
listArray and matrix/arraySimilarly, it holds
listArray.XXXTo achieve, e.g. that l[1:3] and
l[c(1,2,3)] address the same element, as we would expect,
we need some kind of normalization. Since 1:3 and
c(1,2,3) a different R objects, a normalization is
internally done.
identical(1:2, c(1,2)) # delivers FALSE!
#> [1] FALSE
# but
m <- matrix(1:9, 3, 3)
m[1:2,2]
#> [1] 4 5
m[c(1,2),2]
#> [1] 4 5There are two problems
1:3 is of class integer whereas
c(1,2,3) is of class numeric and1:3 is a compact sequence in R whereas
c(1,2,3) is a full vectorTherefore, normalization currently consists of
integer to
numeric and1:3 to real vectors,
e.g. c(1,2,3).The normalization steps can be switched off by setting the options
listArray.expand and listArray.int2num.
l <- listArray()
l[1:3] <- 1
l[c(1,2,3)]
#> [1] 1
options(listArray.expand=FALSE) # now 1:3 != c(1,2,3)
l <- listArray()
l[1:3] <- 1
l[c(1,2,3)]
#> NULLThe default is that listArray.expand and
listArray.int2num are not set which is interpreted as
listArray.expand=TRUE and
listArray.int2num=TRUE.
keyThe main function to create a string from a set of R objects is
key. By using l[...] internally is called
l[[key(...)]]. Thus, you could only use key
rather than a listArray object.
The normalization are done via
rapply(l, expand, classes=c("numeric", "integer"), how="replace")
with
expand <- function(x) { unserialize(serialize(x, connection=NULL, version=2)) }
andrapply(l, as.numeric, classes="integer", how="replace").In future might be further normalization necessary then the two above.
Thanks to Henrik Bengtsson and Duncan Murdoch which hinted me how to normalize a compact sequence in R without writing C++ code.