9.7 Investigate Your Object: str()
and Lists
Let’s reconsider the Meetup Simulation from Section 6.4:
<- function(reps = 10000, table = FALSE, seed = NULL) {
meetupSim if ( !is.null(seed) ) {
set.seed(seed)
}<- runif(reps, 0, 60)
anna <- runif(reps, 0, 60)
raj <- (abs(anna - raj) < 10)
connect if ( table ) {
cat("Here is a table of the results:\n\n")
print(table(connect))
cat("\n")
}cat("The proportion of tims they met was ", mean(connect), ".\n", sep = "")
}
You will recall that when the user asks for a table of results, the function prints out a table that looks like this:
## Here is a table of the results:
## connect
## FALSE TRUE
## 69781 30219
There are a couple of small irritations, here:
- The name of the table (“connect”) appears in the output, even though it was a name that was given in the code internal to the function. As a name for the output-table, it’s not the most descriptive choice. Besides, we really don’t need a name here, because have just
cat
-ed out a sentence that introduces the table. - The names for the columns (
FALSE
andTRUE
) again pertain to features internal to the code of the function. The user should see more descriptive names.
In order to investigate how we might deal with these issues, let’s create a small table here:
<- c(rep(TRUE, 6), rep(FALSE, 4))
logicalVector <- table(logicalVector)
tab tab
## logicalVector
## FALSE TRUE
## 4 6
One way to deal with the column-name issues might be to isolate each table value and then repackage the values. We can access the individual table-values with sub-setting. For example, the first value is:
1] tab[
## FALSE
## 4
Hence we could grab the values, create a vector from them, and then provide names for the vector that we like. Thus:
<- c(tab[1], tab[2])
results names(results) <- c("did not meet", "met")
results
## did not meet met
## 4 6
Another approach—and this is the more instructive and generally-useful procedure—is to begin by looking carefully at the structure of the problematic object:
str(tab)
## 'table' int [1:2(1d)] 4 6
## - attr(*, "dimnames")=List of 1
## ..$ logicalVector: chr [1:2] "FALSE" "TRUE"
We see that
- the table has an attribute called
dimnames
dimnames
is a list of length one.- It is a named list. The name of its only element is
logicalVector
. - The elements of this vector are the column names for the table.
If you would like to see the dimnames
attribute all by itself, you can access it with the attr()
function :
attr(tab, which = "dimnames") # "which" says which attribute you want!
## $logicalVector
## [1] "FALSE" "TRUE"
You can also use attr()
to set the values of an attribute. Here, we want dimnames
to be a list of length one that does not have a name for its sole element. The following should do the trick:
attr(tab, which = "dimnames") <- list(c("did not meet", "met"))
Let’s see if this worked:
tab
## did not meet met
## 4 6
It appears to have worked very nicely! Hence we may rewrite meetupSim()
as follows:
<- function(reps = 10000, table = FALSE, seed = NULL) {
meetupSim if ( !is.null(seed) ) {
set.seed(seed)
}<- runif(reps, 0, 60)
anna <- runif(reps, 0, 60)
raj <- (abs(anna - raj) < 10)
connect if ( table ) {
cat("Here is a table of the results:\n\n")
<- table(connect)
tab attr(tab, which = "dimnames") <- list(c("did not meet", "met"))
print(tab)
cat("\n")
}cat("The proportion of tims they met was ", mean(connect), ".\n", sep = "")
}
Let’s try it out:
meetupSim(reps = 100000, table = TRUE, seed = 3939)
## Here is a table of the results:
##
## did not meet met
## 69781 30219
##
## The proportion of tims they met was 0.30219.
Much better!
The moral of the story is:
Make a habit of examining your objects with the
str()
function. Combiningstr()
with your abilities to manipulate lists allows you to access and set pieces of the object in helpful ways.
Note: the dimnames
attribute for tables and matrices is so frequently used that it has its own special function for accessing and setting: dimnames()
. Other popular attributes, such as names
for a vector and levels
for a factor, also have dedicated access/set functions—names()
and levels()
respectively. But keep in mind that you can access and set the values for any attribute at all with the attr()
function.
9.7.1 Practice Exercises
Consider the following matrix:
<- matrix(1:24, nrow = 4) myMat rownames(myMat) <- letters[1:4] colnames(myMat) <- LETTERS[1:6] myMat
## A B C D E F ## a 1 5 9 13 17 21 ## b 2 6 10 14 18 22 ## c 3 7 11 15 19 23 ## d 4 8 12 16 20 24
Find a way to change the row names of
myMAT
to “x,” “y,” “z” and “w,” using theattr()
function rather than therownames()
function.
9.7.2 Solutions to Practice Exercises
First, run
str(myMat)
. You find that it has an attribute calleddimnames
that is a list of length 2. The first element of this list is the vector of row names. Hence you need to assign new row names to this element. You can do so as follows:attr(myMat, which = "dimnames")[[1]] <- c("x", "y", "z", "w") myMat
## A B C D E F ## x 1 5 9 13 17 21 ## y 2 6 10 14 18 22 ## z 3 7 11 15 19 23 ## w 4 8 12 16 20 24
It worked!