0% found this document useful (0 votes)
1 views11 pages

Module 1 - List

Uploaded by

ammubhavya2005
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views11 pages

Module 1 - List

Uploaded by

ammubhavya2005
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

4 LIS TS

In contrast to a vector, in which all ele-


ments must be of the same mode, R’s list
structure can combine objects of different
types. For those familiar with Python, an R list
is similar to a Python dictionary or, for that matter, a
Perl hash. C programmers may find it similar to a C
struct. The list plays a central role in R, forming the
basis for data frames, object-oriented programming,
and so on.
In this chapter, we’ll cover how to create lists and how to work with
them. As with vectors and matrices, one common operation with lists is
indexing. List indexing is similar to vector and matrix indexing but with
some major differences. And like matrices, lists have an analog for the
apply() function. We’ll discuss these and other list topics, including ways
to take lists apart, which often comes in handy.

4.1 Creating Lists


Technically, a list is a vector. Ordinary vectors—those of the type we’ve
been using so far in this book—are termed atomic vectors, since their
components cannot be broken down into smaller components. In contrast,
lists are referred to as recursive vectors.
For our first look at lists, let’s consider an employee database. For each
employee, we wish to store the name, salary, and a Boolean indicating union
membership. Since we have three different modes here—character, numer-
ic, and logical—it’s a perfect place for using lists. Our entire database might
then be a list of lists, or some other kind of list such as a data frame, though
we won’t pursue that here.
We could create a list to represent our employee, Joe, this way:

j <- list(name="Joe", salary=55000, union=T)

We could print out j, either in full or by component:

> j
$name
[1] "Joe"

$salary
[1] 55000

$union
[1] TRUE

Actually, the component names—called tags in the R literature—such as


salary are optional. We could alternatively do this:

> jalt <- list("Joe", 55000, T)


> jalt
[[1]]
[1] "Joe"

[[2]]
[1] 55000

[[3]]
[1] TRUE

However, it is generally considered clearer and less error-prone to use


names instead of numeric indices.
Names of list components can be abbreviated to whatever extent is possi-
ble without causing ambiguity:

> j$sal
[1] 55000

86 Chapter 4
Since lists are vectors, they can be created via vector():

> z <- vector(mode="list")


> z[["abc"]] <- 3
> z
$abc
[1] 3

4.2 General List Operations


Now that you’ve seen a simple example of creating a list, let’s look at how to
access and work with lists.

4.2.1 List Indexing


You can access a list component in several different ways:

> j$salary
[1] 55000
> j[["salary"]]
[1] 55000
> j[[2]]
[1] 55000

We can refer to list components by their numerical indices, treating


the list as a vector. However, note that in this case, we use double brackets
instead of single ones.
So, there are three ways to access an individual component c of a list lst
and return it in the data type of c:
• lst$c
• lst[["c"]]
• lst[[i]], where i is the index of c within lst

Each of these is useful in different contexts, as you will see in sub-


sequent examples. But note the qualifying phrase, “return it in the data
type of c.” An alternative to the second and third techniques listed is to
use single brackets rather than double brackets:
• lst["c"]
• lst[i], where i is the index of c within lst

Both single-bracket and double-bracket indexing access list elements


in vector-index fashion. But there is an important difference from ordi-
nary (atomic) vector indexing. If single brackets [ ] are used, the result is

Lists 87
another list—a sublist of the original. For instance, continuing the preced-
ing example, we have this:

> j[1:2]
$name
[1] "Joe"

$salary
[1] 55000
> j2 <- j[2]
> j2
$salary
[1] 55000
> class(j2)
[1] "list"
> str(j2)
List of 1
$ salary: num 55000

The subsetting operation returned another list consisting of the first two
components of the original list j. Note that the word returned makes sense
here, since index brackets are functions. This is similar to other cases you’ve
seen for operators that do not at first appear to be functions, such as +.
By contrast, you can use double brackets [[ ]] for referencing only a
single component, with the result having the type of that component.

> j[[1:2]]
Error in j[[1:2]] : subscript out of bounds
> j2a <- j[[2]]
> j2a
[1] 55000
> class(j2a)
[1] "numeric"

4.2.2 Adding and Deleting List Elements


The operations of adding and deleting list elements arise in a surprising
number of contexts. This is especially true for data structures in which lists
form the foundation, such as data frames and R classes.
New components can be added after a list is created.

> z <- list(a="abc",b=12)


> z
$a
[1] "abc"

88 Chapter 4
$b
[1] 12

> z$c <- "sailing" # add a c component


> # did c really get added?
> z
$a
[1] "abc"

$b
[1] 12

$c
[1] "sailing"

Adding components can also be done via a vector index:

> z[[4]] <- 28


> z[5:7] <- c(FALSE,TRUE,TRUE)
> z
$a
[1] "abc"

$b
[1] 12

$c
[1] "sailing"

[[4]]
[1] 28

[[5]]
[1] FALSE

[[6]]
[1] TRUE

[[7]]
[1] TRUE

You can delete a list component by setting it to NULL.

> z$b <- NULL


> z
$a
[1] "abc"

Lists 89
$c
[1] "sailing"

[[3]]
[1] 28

[[4]]
[1] FALSE

[[5]]
[1] TRUE

[[6]]
[1] TRUE

Note that upon deleting z$b, the indices of the elements after it moved
up by 1. For instance, the former z[[4]] became z[[3]].
You can also concatenate lists.

> c(list("Joe", 55000, T),list(5))


[[1]]
[1] "Joe"

[[2]]
[1] 55000

[[3]]
[1] TRUE

[[4]]
[1] 5

4.2.3 Getting the Size of a List


Since a list is a vector, you can obtain the number of components in a list via
length().

> length(j)
[1] 3

4.2.4 Extended Example: Text Concordance


Web search and other types of textual data mining are of great interest
today. Let’s use this area for an example of R list code.
We’ll write a function called findwords() that will determine which words
are in a text file and compile a list of the locations of each word’s occur-
rences in the text. This would be useful for contextual analysis, for example.

90 Chapter 4
Suppose our input file, [Link], has the following contents (taken
from this book!):

The [1] here means that the first item in this line of output is
item 1. In this case, our output consists of only one line (and one
item), so this is redundant, but this notation helps to read
voluminous output that consists of many items spread over many
lines. For example, if there were two rows of output with six items
per row, the second row would be labeled [7].

In order to identify words, we replace all nonletter characters with blanks


and get rid of capitalization. We could use the string functions presented in
Chapter 11 to do this, but to keep matters simple, such code is not shown
here. The new file, [Link], looks like this:

the here means that the first item in this line of output is
item in this case our output consists of only one line and one
item so this is redundant but this notation helps to read
voluminous output that consists of many items spread over many
lines for example if there were two rows of output with six items
per row the second row would be labeled

Then, for instance, the word item has locations 7, 14, and 27, which
means that it occupies the seventh, fourteenth, and twenty-seventh word
positions in the file.
Here is an excerpt from the list that is returned when our function
findwords() is called on this file:

> findwords("[Link]")
Read 68 items
$the
[1] 1 5 63

$here
[1] 2

$means
[1] 3

$that
[1] 4 40

$first
[1] 6

$item
[1] 7 14 27
...

Lists 91
The list consists of one component per word in the file, with a word’s
component showing the positions within the file where that word occurs.
Sure enough, the word item is shown as occurring at positions 7, 14, and 27.
Before looking at the code, let’s talk a bit about our choice of a list struc-
ture here. One alternative would be to use a matrix, with one row per word
in the text. We could use rownames() to name the rows, with the entries within
a row showing the positions of that word. For instance, row item would con-
sist of 7, 14, 27, and then 0s in the remainder of the row. But the matrix
approach has a couple of major drawbacks:
• There is a problem in terms of the columns to allocate for our matrix.
If the maximum frequency with which a word appears in our text is, say,
10, then we would need 10 columns. But we would not know that ahead
of time. We could add a new column each time we encountered a new
word, using cbind() (in addition to using rbind() to add a row for the
word itself). Or we could write code to do a preliminary run through
the input file to determine the maximum word frequency. Either of
these would come at the expense of increased code complexity and
possibly increased runtime.
• Such a storage scheme would be quite wasteful of memory, since most
rows would probably consist of a lot of zeros. In other words, the matrix
would be sparse—a situation that also often occurs in numerical analysis
contexts.

Thus, the list structure really makes sense. Let’s see how to code it.

1 findwords <- function(tf) {


2 # read in the words from the file, into a vector of mode character
3 txt <- scan(tf,"")
4 wl <- list()
5 for (i in 1:length(txt)) {
6 wrd <- txt[i] # ith word in input file
7 wl[[wrd]] <- c(wl[[wrd]],i)
8 }
9 return(wl)
10 }

We read in the words of the file (words simply meaning any groups of let-
ters separated by spaces) by calling scan(). The details of reading and writing
files are covered in Chapter 10, but the important point here is that txt will
now be a vector of strings: one string per instance of a word in the file. Here
is what txt looks like after the read:

> txt
[1] "the" "here" "means" "that" "the"
[6] "first" "item" "in" "this" "line"
[11] "of" "output" "is" "item" "in"
[16] "this" "case" "our" "output" "consists"

92 Chapter 4
[21] "of" "only" "one" "line" "and"
[26] "one" "item" "so" "this" "is"
[31] "redundant" "but" "this" "notation" "helps"
[36] "to" "read" "voluminous" "output" "that"
[41] "consists" "of" "many" "items" "spread"
[46] "over" "many" "lines" "for" "example"
[51] "if" "there" "were" "two" "rows"
[56] "of" "output" "with" "six" "items"
[61] "per" "row" "the" "second" "row"
[66] "would" "be" "labeled"

The list operations in lines 4 through 8 build up our main variable, a list
wl (for word list). We loop through all the words from our long line, with wrd
being the current one.
Let’s see what happens with the code in line 7 when i = 4, so that wrd =
"that" in our example file [Link]. At this point, wl[["that"]] will not
yet exist. As mentioned, R is set up so that in such a case, wl[["that"]] = NULL,
which means in line 7, we can concatenate it! Thus wl[["that"]] will become
the one-element vector (4). Later, when i = 40, wl[["that"]] will become
(4,40), representing the fact that words 4 and 40 in the file are both "that".
Note how convenient it is that list indexing can be done through quoted
strings, such as in wl[["that"]].
An advanced, more elegant version of this code uses R’s split() func-
tion, as you’ll see in Section 6.2.2.

4.3 Accessing List Components and Values


If the components in a list do have tags, as is the case with name, salary, and
union for j in Section 4.1, you can obtain them via names():

> names(j)
[1] "name" "salary" "union"

To obtain the values, use unlist():

> ulj <- unlist(j)


> ulj
name salary union
"Joe" "55000" "TRUE"
> class(ulj)
[1] "character"

The return value of unlist() is a vector—in this case, a vector of charac-


ter strings. Note that the element names in this vector come from the com-
ponents in the original list.

Lists 93
On the other hand, if we were to start with numbers, we would get
numbers.

> z <- list(a=5,b=12,c=13)


> y <- unlist(z)
> class(y)
[1] "numeric"
> y
a b c
5 12 13

So the output of unlist() in this case was a numeric vector. What about a
mixed case?

> w <- list(a=5,b="xyz")


> wu <- unlist(w)
> class(wu)
[1] "character"
> wu
a b
"5" "xyz"

Here, R chose the least common denominator: character strings. This


sounds like some kind of precedence structure, and it is. As R’s help for
unlist() states:

Where possible the list components are coerced to a common


mode during the unlisting, and so the result often ends up as a
character vector. Vectors will be coerced to the highest type of the
components in the hierarchy NULL < raw < logical < integer
< real < complex < character < list < expression: pairlists are
treated as lists.

But there is something else to deal with here. Though wu is a vector and
not a list, R did give each of the elements a name. We can remove them by
setting their names to NULL, as you saw in Section 2.11.

> names(wu) <- NULL


> wu
[1] "5" "xyz"

We can also remove the elements’ names directly with unname(), as


follows:

> wun <- unname(wu)


> wun
[1] "5" "xyz"

94 Chapter 4
This also has the advantage of not destroying the names in wu, in case
they are needed later. If they will not be needed later, we could simply
assign back to wu instead of to wun in the preceding statement.

4.4 Applying Functions to Lists


Two functions are handy for applying functions to lists: lapply and sapply.

4.4.1 Using the lapply() and sapply() Functions


The function lapply() (for list apply) works like the matrix apply() function,
calling the specified function on each component of a list (or vector coerced
to a list) and returning another list. Here’s an example:

> lapply(list(1:3,25:29),median)
[[1]]
[1] 2

[[2]]
[1] 27

R applied median() to 1:3 and to 25:29, returning a list consisting of 2 and 27.
In some cases, such as the example here, the list returned by lapply()
could be simplified to a vector or matrix. This is exactly what sapply() (for
simplified [l]apply) does.

> sapply(list(1:3,25:29),median)
[1] 2 27

You saw an example of matrix output in Section 2.6.2. There, we


applied a vectorized, vector-valued function—a function whose return
value is a vector, each of whose components is vectorized— to a vector
input. Using sapply(), rather than applying the function directly, gave us
the desired matrix form in the output.

4.4.2 Extended Example: Text Concordance, Continued


The text concordance creator, findwords(), which we developed in Sec-
tion 4.2.4, returns a list of word locations, indexed by word. It would be
nice to be able to sort this list in various ways.
Recall that for the input file [Link], we got this output:

$the
[1] 1 5 63

$here
[1] 2

Lists 95

You might also like