An R flaw: unexpected attribute droppings

February 6, 2014
By

(This article was first published on Odd Hypothesis, and kindly contributed to R-bloggers)

Today I was putting some code together that made plots from slices of a 3-dimensional array object aa. A couple of the dimensions in aa had names defined by named vectors. For example:

> aa = array(runif(2*3*4), 
dim
=c(2,3,4),
dimnames
=list(id = c(good='id1', evil='id2'),
x
= c(1,2,3),
var = c(up='a', dn='b', lt='c', rt='d')))
> str(aa)
num
[1:2, 1:3, 1:4] 0.0138 0.2942 0.7988 0.3465 0.8751 ...
- attr(*, "dimnames")=List of 3
..$ id : Named chr [1:2] "id1" "id2"
.. ..- attr(*, "names")= chr [1:2] "good" "evil"
..$ x : chr [1:3] "1" "2" "3"
..$ var: Named chr [1:4] "a" "b" "c" "d"
.. ..- attr(*, "names")= chr [1:4] "up" "dn" "lt" "rt"

Thus, I could access “aliases” for dimension names in id and var by:

> names(dimnames(aa)$id)
[1] "good" "evil"
> names(dimnames(aa)$var)
[1] "up" "dn" "lt" "rt"

The code I wrote would iterate over the 3rd dimension, using the resulting 2D array’s to produce a series of plots using matplot(). To make legends more readable, I made use of the names attribute for dimnames as above. In the first version, I used apply() to do the iterating:

> apply(aa, 3, function(xy) {
x
= as.numeric(dimnames(xy)$x)
matplot
(x, y=t(xy))
legend
('topleft', legend=names(dimnames(xy)$id), fill=1:nrow(xy))

NULL
})

This worked perfectly fine, however, later I decided it would be more informative to use the names in the iterating dimension for a plot title. So I refactored a bit to use sapply():

> sapply(1:dim(aa)[3], function(k) {
xy
= aa[,,k]

x
= as.numeric(dimnames(xy)$x)
matplot
(x, y=t(xy))
legend
('topleft', legend=names(dimnames(xy)$id), fill=1:nrow(xy))

title
(main=names(dimnames(aa)$var[k]))

NULL
})

I was a little surprised that this threw an error indicating that the names associated with dimnames(aa)$id were non-existant:

 Error in legend("topleft", legend = names(dimnames(xy)$id), fill = 1:nrow(xy)) : 
'legend' is of length 0

Upon inspection, it seems that it is R’s default behavior to drop attributes on dimnames when an array is subsetted.

> str(aa[,,1])
num
[1:2, 1:3] 0.0138 0.2942 0.7988 0.3465 0.8751 ...
- attr(*, "dimnames")=List of 2
..$ id: chr [1:2] "id1" "id2"
..$ x : chr [1:3] "1" "2" "3"

Adding a drop=FALSE to the indexing doesn’t work. The only fix I could come up with was to reassign the additional attributes after subsetting:

> sapply(1:dim(aa)[3], function(k) {
xy
= aa[,,k]

# !! recover additional dimname attributes
# dropped by subsetting !! #
dimnames
(xy) = dimnames(aa)[names(dimnames(aa)) %in% names(dimnames(xy))]

x
= as.numeric(dimnames(xy)$x)
matplot
(x, y=t(xy))
legend
('topleft', legend=names(dimnames(xy)$id), fill=1:nrow(xy))

title
(main=names(dimnames(aa)$var[k]))

NULL
})

To the greater R community, I ask – is this behavior a flaw, or was it done on purpose? If the latter, I pleadingly ask WHYYYYyyyyyyyy!

Written with StackEdit.

To leave a comment for the author, please follow the link and comment on their blog: Odd Hypothesis.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...



If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.

Sponsors

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)