25  NAMED LISTS

#-----------------------------------------------------------------------------.
# NAMED LISTS
#
# The entries in a list can have names associated with them.
# This makes working with lists much easier.
#-----------------------------------------------------------------------------.

#........................................................................
#
# first way to add names to a list - use the names function
#........................................................................

# let's recreate all of the data
rm(list= ls())

gradebook = list(c("bob", "charlie", "frank"), 
                 c(70,80,90), 
                 c(75,85,88), 
                 c(TRUE, FALSE,FALSE))
str(gradebook)  # see the structure
List of 4
 $ : chr [1:3] "bob" "charlie" "frank"
 $ : num [1:3] 70 80 90
 $ : num [1:3] 75 85 88
 $ : logi [1:3] TRUE FALSE FALSE
gradebook # (see the actual values)
[[1]]
[1] "bob"     "charlie" "frank"  

[[2]]
[1] 70 80 90

[[3]]
[1] 75 85 88

[[4]]
[1]  TRUE FALSE FALSE
# View(gradebooks)  # see the structure - try this command yourself
names(gradebook)  # NULL   i.e. no names
NULL
# now let's add the names
names(gradebook) = c("studs", "exam1", "exam2", "honorsStudent")

# The gradebook now displays with $dollar-signs before the names of the 
# individual pieces of information. 

gradebook  # now there are names
$studs
[1] "bob"     "charlie" "frank"  

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE
str(gradebook)   # you see the names here too
List of 4
 $ studs        : chr [1:3] "bob" "charlie" "frank"
 $ exam1        : num [1:3] 70 80 90
 $ exam2        : num [1:3] 75 85 88
 $ honorsStudent: logi [1:3] TRUE FALSE FALSE

25.1 Introducing $dollar-sign-notation for named lists

#........................................................................
# $DOLLAR-SIGN-NOTATION
#
# For "named lists" in addition to [[double-bracket-notation]] (as described above)
# you can ALSO use $dollar-sign-notion (see examples below)
#........................................................................

# ONCE WE ADDED THE NAMES ...

gradebook[[1]]    # still works
[1] "bob"     "charlie" "frank"  
gradebook$studs   # same exact thing
[1] "bob"     "charlie" "frank"  

25.2 [[double-brackets]] vs. $dollar-sign-notation (different ways to do same thing)

#--------------------------------------------------------------.
# TWO WAYS OF DOING THE SAME THING:
#    [[double-brackets-notation]] and $dollar-sign-notation
#
# [[double-brackets]] notation is just a different way of writing
# $dollar-sign-notation. The result is EXACTLY THE SAME.
#--------------------------------------------------------------.

gradebook[[2]]    # access the 2nd value of gradebook
[1] 70 80 90
gradebook$exam1   # same EXACT thing
[1] 70 80 90
#--------------------------------------------------------------.
# However, [single-brackets] are different !!!
#--------------------------------------------------------------.

gradebook[2]     # this returns a list
$exam1
[1] 70 80 90
gradebook[[2]]   # this returns a vector
[1] 70 80 90
gradebook$exam1  # this returns a vector (same as gradebook[[2]] )
[1] 70 80 90
# Proof
mode(gradebook[2])
[1] "list"
mode(gradebook[[2]]) 
[1] "numeric"
mode(gradebook$exam1)
[1] "numeric"
identical(gradebook[[2]], gradebook$exam1)   # TRUE
[1] TRUE
identical(gradebook[2], gradebook$exam1)   # FALSE
[1] FALSE
#--------------------------------------------------------------.
# All of the rules that you learned about using [[double-brackets]]
# work the same way for $names However, $names are a lot easier
# to use and are less confusing than [[double-brackets]].
#--------------------------------------------------------------.

25.3 — Practice —

################################################################.
# QUESTION
#
# Change the name of the first student to a different name.
################################################################.

###########.
# Answer
###########.

# We have a few ways to do this.

#---------------------------------------------.
# One way ... using [[double-brackets]]
#---------------------------------------------.

gradebook[[1]][2] = "avraham" 
gradebook
$studs
[1] "bob"     "avraham" "frank"  

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE
#---------------------------------------------.
# Another way ... using $names
#---------------------------------------------.

gradebook$studs[2] = "avi"   
gradebook
$studs
[1] "bob"   "avi"   "frank"

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE
#---------------------------------------------.
# A third way ... using "recursive indexing"
# change the name from "avi" to "abie".
#---------------------------------------------.

gradebook[[c(1,2)]] = "abie"
gradebook
$studs
[1] "bob"   "abie"  "frank"

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE

25.4 another way to assign names - specify names when you create the list(…)

#-------------------------------------------------------------.
# another way to assign names - give the names directly
# in the call to the list(...) function
#-------------------------------------------------------------.

# Remove all variables and start over again. 
rm(list= ls())  

# This is the data that will go into the list.
# We can remove these variables once the list is created.
students = c("bob", "charlie", "frank")
test1 = c(70,80,90)
test2 = c(75,85,88)
honors = c(TRUE, FALSE,FALSE)

# create the list and assign names all in one command.
#
# For example, the code, "studs=students", in the command below means that 
# "studs" will be the name in the list for the data that is inserted into
# the list from the students vector. (We could also call the name in 
# the list "students" instead of "studs". We'll see how to do that in the next example.)

gradebook = list(studs=students, exam1=test1, exam2=test2, honorsStudent=honors)

gradebook   
$studs
[1] "bob"     "charlie" "frank"  

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE
# REMEMBER - the list does NOT depend on the original variables.
# we can remove the original variables and the list is still there.
rm(students, test1, test2, honors)   
gradebook
$studs
[1] "bob"     "charlie" "frank"  

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honorsStudent
[1]  TRUE FALSE FALSE

25.5 REVIEW - comparison of the 2 ways to add names to a list:

#...........................................................................
# REVIEW - comparison of the 2 ways to add names to a list:
#
#     The first time we created the list we did the following
#
#        > gradebook = list(students, test1, test2, honors)
#        > names(gradebook) = c("studs", "exam1", "exam2", "honorsStudent")
#
#     an easier way is to do it all in one command like this:
#
#        > gradebook = list(studs=students, exam1=test1, exam2=test2, honorsStudent=honors)
#...........................................................................

25.6 USING THE SAME NAMES AS THE VARIABLES …

#...........................................................................
# USING THE SAME NAMES AS THE VARIABLES ...
#
# Using the same name as the variable also works but it could look a little
# confusing when you first see it. In the following command students=students,
# looks confusing but it isn't so difficult. 
#
#   > gradebook = list(students=students, test1=test1, test2=test2, honors=honors)
#
# The word, students, on the left hand side of the = sign is the name of the
# data in the list.
# 
# The word, students, on the right hand side of the = sign is the variable students
# that you are getting the data from.
#
# (See the code below ...)
#...........................................................................

# start over ... remove all variables
rm (list = ls())

students = c("bob", "charlie", "frank","david")
test1 = c(60,70,80,90)
test2 = c(65,75,85,95)
honors = c(TRUE, FALSE,FALSE)

gradebook = list(students=students, test1=test1, test2=test2, honors=honors)
gradebook
$students
[1] "bob"     "charlie" "frank"   "david"  

$test1
[1] 60 70 80 90

$test2
[1] 65 75 85 95

$honors
[1]  TRUE FALSE FALSE

25.7 Changing the names of the entries in a list

#--------------------------------------------------------------------------.
# names( SOME_LIST ) = NEW_NAMES
#
# The names function simply returns the names of the objects in the list as a vector.
# You can use this vector to display the names. 
# You can also use this to change the names. 
#
# See the examples below. 
#--------------------------------------------------------------------------.

rm(list=ls()) # start from scratch
gradebook = list(students=c("bob", "charlie", "frank"), 
                 test1=c(70,80,90), 
                 test2=c(75,85,88), 
                 honors=c(TRUE, FALSE,FALSE))

gradebook
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 90

$test2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE
names(gradebook)   # "students"  "test1"  "test2"  "honors"
[1] "students" "test1"    "test2"    "honors"  
names(gradebook)[c(2,3)] = c("exam1", "exam2")   # change tests to exams

names(gradebook)   # "students"  "exam1"  "exam2"  "honors"
[1] "students" "exam1"    "exam2"    "honors"  
gradebook   # notice the test1 and test2 were changed to exam1 and exam2
$students
[1] "bob"     "charlie" "frank"  

$exam1
[1] 70 80 90

$exam2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE

25.8 — Practice —

#..............................................................
# QUESTION - Answer this question using the list defined below.
#
# (a) write code to add 2 points to the third student's
#     grade on test1. Do this using $dollar-sign notation.
#
# (b) Do the same thing again but this 
#     time use [[double-bracket]] notation
#
# (c) Do the same thing again with "recursive-indexing"
#..............................................................

# Use this list 
rm(list=ls()) # start from scratch
gradebook = list(students=c("bob", "charlie", "frank"), 
                 test1=c(70,80,90), 
                 test2=c(75,85,88), 
                 honors=c(TRUE, FALSE,FALSE))

str(gradebook)
List of 4
 $ students: chr [1:3] "bob" "charlie" "frank"
 $ test1   : num [1:3] 70 80 90
 $ test2   : num [1:3] 75 85 88
 $ honors  : logi [1:3] TRUE FALSE FALSE
############.
# ANSWERS
############.

# (a) write code to add 2 points to the third student's
#     grade on test1. Do this using $dollar-sign notation.

gradebook$test1[3] = gradebook$test1[3] + 2 

gradebook
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 92

$test2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE
# (b) Do the same thing again but this
#     time use [[double-bracket]] notation

gradebook[[2]][3] = gradebook[[2]][3] + 2

gradebook
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 94

$test2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE
# (c)

gradebook[[c(2,3)]] = gradebook[[c(2,3)]] + 2
gradebook
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 96

$test2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE

25.9 is.list( SOME_OBJECT )     mode( SOME_OBJECT )

#----------------------------------------.
# is.list
# mode
#----------------------------------------.

rm(list=ls()) # start from scratch
gradebook = list(students=c("bob", "charlie", "frank"), 
                 test1=c(70,80,90), 
                 test2=c(75,85,88), 
                 honors=c(TRUE, FALSE,FALSE))

str(gradebook)
List of 4
 $ students: chr [1:3] "bob" "charlie" "frank"
 $ test1   : num [1:3] 70 80 90
 $ test2   : num [1:3] 75 85 88
 $ honors  : logi [1:3] TRUE FALSE FALSE
is.list(gradebook)           # TRUE
[1] TRUE
is.list(gradebook[[1]])      # FALSE
[1] FALSE
is.list(gradebook$students)  # FALSE
[1] FALSE
mode(gradebook)              # "list"
[1] "list"
mode(gradebook[[1]])         # "character"
[1] "character"
mode(gradebook$students)     # "character"
[1] "character"
gradebook[[1]][2]       # "charlie"
[1] "charlie"
mode(gradebook[[1]][2]) # "character"
[1] "character"

25.10 A list can contain other lists as well as vectors.

#######################################################.
#
# A list can contain other lists as well as vectors.
#
#######################################################.

rm(list = ls())

section1 = list ( students = c("abe","bob","charlie"),
                  test1 = c(70,80,90),
                  test2 = c(75,85,95))

section2 = list( students = c("fran", "anne", "sue", "bertha", "maxine"),
                 test1 = c(100,90,80,70,60),
                 test2 = c(95,85,75,65,55),
                 test3 = c(93,83,73,63,53))

classes = list( year = 2021,
                semester = "fall",
                section1 = section1,
                section2 = section2 )

# we don't need the original variables anymore
rm( section1, section2 )


classes            # data for all of my sections

classes$section2   # just get the section2 data

$section2  # ERROR - you need to start with the list, classes

section2   # ERROR - variable section2 doesn't exist (we removed it above)

# modes of various portions of the structure
mode(classes)  # list (obviously)
mode(classes$section2 )  # list
mode(classes$section2$test3 )   # numeric

# display the average grade for section 2 on test 3
mean ( classes$section2$test3 )
Error: <text>:31:1: unexpected '$'
30: 
31: $
    ^

25.11 Setting up a complex list structure without extra variables

#..................................................
# remember - you don't need extra variables. 
#            the following works too.
#..................................................

rm(list = ls())

classes = list( year = 2021,
                semester = "fall",
                section1 = list ( students = c("abe","bob","charlie"),
                                  test1 = c(70,80,90),
                                  test2 = c(75,85,95)),
                section2 = list( students = c("fran", "anne", "sue", "bertha", "maxine"),
                                 test1 = c(100,90,80,70,60),
                                 test2 = c(95,85,75,65,55),
                                 test3 = c(93,83,73,63,53)) )

classes
$year
[1] 2021

$semester
[1] "fall"

$section1
$section1$students
[1] "abe"     "bob"     "charlie"

$section1$test1
[1] 70 80 90

$section1$test2
[1] 75 85 95


$section2
$section2$students
[1] "fran"   "anne"   "sue"    "bertha" "maxine"

$section2$test1
[1] 100  90  80  70  60

$section2$test2
[1] 95 85 75 65 55

$section2$test3
[1] 93 83 73 63 53
classes$section2
$students
[1] "fran"   "anne"   "sue"    "bertha" "maxine"

$test1
[1] 100  90  80  70  60

$test2
[1] 95 85 75 65 55

$test3
[1] 93 83 73 63 53
mean( classes$section2$test3 )
[1] 73
mean( classes[[4]][[4]] )   # same thing
[1] 73

25.12 — Practice —

#------------------------------------------------------------------------------.
# QUESTION - Display the grade for the last student in section 2 on test3
#------------------------------------------------------------------------------.

# ANSWER - with $ notation
classes$section2$test3[   length(classes$section2$test3)       ]
[1] 53
# NOTE - you CAN use "recursive indexing" to get the grade using the code
# below, but the code to get the "last" test3 grade is harder to read.
# ANSWER - using nested indexing 

classes[[ c(4,    4,           5                 ) ]]  # get the 5th test3 grade
[1] 53
classes[[ c(4,    4,   length(classes[[c(4,4)]]) ) ]]  # get the last test3 grade
[1] 53
#------------------------------------------------------------------------------.
# QUESTION 
# write code to display anne's grade on test2
# anne is in section2
# do NOT assume that anne is the 2nd person, i.e. 
# write the code so that no matter how many people are in the class
# and no matter the order of those people in the data, you will get anne's grade
#------------------------------------------------------------------------------.

# ANSWER

classes$section2$test2[ classes$section2$students == "anne" ]
[1] 85
#------------------------------------------------------------------------------.
# QUESTION - write code to find the name of the person in 
# section 1 who scored highest on test2
# AGAIN - do not assume that the data is any particular order
#------------------------------------------------------------------------------.

# ANSWER 

classes$section1$students[  classes$section1$test2 == max ( classes$section1$test2 )     ]
[1] "charlie"

25.13 REVIEW OF WAYS TO ACCESS DATA IN A LIST

#--------------------------------------------------------------------------.
# REVIEW OF WAYS TO ACCESS DATA IN A LIST
#
#   SOME_LIST[[ A_SINGLE_POSITIVE_NUMBER ]]
#
#   SOME_LIST[[ VECTOR_OF_POSITIVE_NUMBERS ]]   # i.e. recursive indexing
#
#   SOME_LIST[ SOME_VECTOR_INDEX ]     # a list
#
#   SOME_LIST$SOME_ENTRY_NAME      # same as double bracket notation
#
#--------------------------------------------------------------------------.
#
# With [[double-bracket]] notation you always get back ONE object.
#
#   SOME_LIST [[ A_SINGLE_POSITIVE_NUMBER ]]
#       returns the object in the list at the specified position
#
#       Example:
#         > stuff = list ( c("apple", "orange", "pear") , c("table", "chair") )
#
#         > stuff[[1]]
#         [1]  "apple"  "orange"  "pear"
#
#         > stuff[[2]]
#         [1] "table"  "chair"
#
#
#   SOME_LIST [[ A_VECTOR_OF_POSITIVE_NUMBERS ]]
#       If the subscript in the [[double brackets]] contains more than one
#       positive number then the values in the subscript are applied one after the other
#       until you wind up with a single object. This is known as "recursive indexing".
#       For example:
# 
#       Example:
#         > stuff = list ( c("apple", "orange", "pear") , c("table", "chair") )
#
#         > stuff[[1]]
#         [1]  "apple"  "orange"  "pear"
#
#         > # get the 1st object from the list then the 2nd item from that vector
#
#         > stuff[[c(1,2)]]    
#         [1] "orange"
#
#       Unfortunately, most R programmers are not familiar with using subscripts that 
#       are vectors with more than one number. This feature is not documented well.
#       It's hard to find a good explanation of this in the official R documentation.
#
#       Therefore I don't recommend that you use this in your code a lot
#       as many other R programmers will not understand what you are doing.
#       However, it does work. 
#
#
#   SOME_LIST[ SOME_VECTOR_INDEX ]   
#          
#       You can use [single-bracket] notation to get back a NEW LIST
#       of values from the original list.
#
#       With [single-bracket] notation, you can use any of methods that
#       we learned about for indexing vectors, i.e. 
#         - positive position numbers
#         - negative position numbers
#         - logical values
#       to get as many values back as you want. 
#
#       All the values are put into a NEW LIST.
#
#
#
#---------------------------------------------------------------.

25.14 Removing items from a list (THIS WAS COVERED IN PREVIOUS SECTION)

#---------------------------------------------------------------.
# Removing items from a list.
#
# NULL is a "special value" that can be understood as "nothing".
# To remove an item from a list, you can assign NULL to that value.
# This works whether you specify the item to remove
#   with the [[double-bracket]] notation, 
#   with the [single-bracket] notation or
#   with the $dollar-sign-notation
#
# NULL is DIFFERERNT from NA.
# ---------------------------.
# NA means there is some information being recorded but the exact
# value of that information is not available.
#
# NULL means that there is no information being recorded.
#
# Therefore if you set an item in a list to NULL, the item is
# removed from the list. However, if you set an item in a list to NA
# then the item becomes NA.
#---------------------------------------------------------------.

# let's recreate all of the data
rm(list= ls())

# gradebook = list(students, test1, test2, honors)

gradebook = list(students=c("bob", "charlie", "frank"), 
                 test1=c(70,80,90), 
                 test2=c(75,85,88), 
                 honors=c(TRUE, FALSE,FALSE))

gradebook
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 90

$test2
[1] 75 85 88

$honors
[1]  TRUE FALSE FALSE
str(gradebook)
List of 4
 $ students: chr [1:3] "bob" "charlie" "frank"
 $ test1   : num [1:3] 70 80 90
 $ test2   : num [1:3] 75 85 88
 $ honors  : logi [1:3] TRUE FALSE FALSE
#gradebook = list(students=students, test1=test1, test2=test2, honors=honors)
#gradebook               # gradebook contains 4 items

gradebook[[3]] = NULL   # remove the third item from the gradebook (i.e. $test2)
gradebook               # $test2 (which was the 3rd value is removed)
$students
[1] "bob"     "charlie" "frank"  

$test1
[1] 70 80 90

$honors
[1]  TRUE FALSE FALSE
str(gradebook)
List of 3
 $ students: chr [1:3] "bob" "charlie" "frank"
 $ test1   : num [1:3] 70 80 90
 $ honors  : logi [1:3] TRUE FALSE FALSE
gradebook$test1 = NULL  # remove the $test1 item from the gradebook
gradebook               # test1 is removed
$students
[1] "bob"     "charlie" "frank"  

$honors
[1]  TRUE FALSE FALSE
str(gradebook)
List of 2
 $ students: chr [1:3] "bob" "charlie" "frank"
 $ honors  : logi [1:3] TRUE FALSE FALSE
gradebook[2] = NULL    # single brackets also works, honors is currently the 2nd item
gradebook              # honors was removed from the gradebook
$students
[1] "bob"     "charlie" "frank"  
str(gradebook)
List of 1
 $ students: chr [1:3] "bob" "charlie" "frank"
gradebook[[1]] = NULL  # you can remove the last item too
gradebook              # named list()   - i.e. an empty list that has names
named list()
# add an item to the now empty list (see below for more info
# about how to add items)

gradebook[[1]] = c("apple", "cucumber", "tomato")
gradebook
[[1]]
[1] "apple"    "cucumber" "tomato"  
str(gradebook)
List of 1
 $ : chr [1:3] "apple" "cucumber" "tomato"
gradebook[[1]] = NULL
gradebook
named list()

25.15 Adding items to a list - using $newNames (or [[double-brackets]])

#---------------------------------------------------------------.
# Adding items to a list.
#---------------------------------------------------------------.

#...................................................................
# You can assign a new value in the list using either
# [[double-bracket]] notation
# or $dollar-sign notation
#...................................................................
rm(list=ls())

students = c("bob", "charlie", "frank")

stuff = list()  # let's start with an empty list
stuff           # list()
list()
stuff[[1]] = students     # position 1 is created
stuff
[[1]]
[1] "bob"     "charlie" "frank"  
stuff$exam1 = c(71,81,91) # add another entry to the list
stuff
[[1]]
[1] "bob"     "charlie" "frank"  

$exam1
[1] 71 81 91
# You can use either notation, [[double-brackets]] or $dollar-signs
# to refer to the data you just added
stuff$exam1
[1] 71 81 91
stuff[[2]]   #same thing
[1] 71 81 91

25.17 Using names another way, e.g. - someList[c(“vector”,“of”,“quoted”,“names”)]

#---------------------------------------------------------------------------.
# someList[c("vector","of","quoted","names")]       (with [single-brackets])
# 
# You can use a vector of quoted "names" as an index for the list.
# The result is a list that contains just the specified elements. 
#---------------------------------------------------------------------------.

length(gradebook)   # 3
[1] 3
length(gradebook$classes) # 2
[1] 2
gradebook[c("year", "semester")]   
$year
[1] 2022

$semester
[1] "fall"
# $year
# [1] 2022
# 
# $semester
# [1] "fall"

gradebook[ c(1,2) ]  # same result
$year
[1] 2022

$semester
[1] "fall"

25.18 RECURSIVE INDEXING WITH A VECTOR OF NAMES, eg. someList[[c(“vec”,“of”,“names”)]]

#------------------------------------------------------------------------.
# RECURSIVE INDEXING WITH A VECTOR OF NAMES, eg.
# someList[[c("vector","of","quoted","names")]]       (with [[double-brackets]])
#
# We are adding this section to be complete. However, this technique
# is not used very often (see below for why).
#
# Just like you can use "recursive indexing" with
# a vector of numbers in [[double-brackets]]
# you can also use "recursive indexing" with
# a vector of names in [[double-brackets]]
#
# Remember that when using code such as someList[[c(3,2,4)]], 
# ie. [[double-brackets]] with a vector of positive numbers
# R performs "recursive indexing" (see above for more info).
#
# In a similar way, you use similar code with a vector of names instead of
# a vector of numbers, R will also do "recursive indexing", based on the names
# instead of the numbers. However, this technique is not used very often,
# because $dollar-sign notation accomplishes the same thing and is much
# easier to read and type.
#
#      someList[[c("name","anotherName","yetAThirdName","etc")]]
#
#             is the same as 
#
#      someList$name$anotherName$yetAThirdName$etc
#
#------------------------------------------------------------------------.

# You may use [["oneQuotedName"]] in double-brackets.

gradebook[["semester"]]   # [1] "fall"
[1] "fall"
gradebook$semester        # "fall" - same thing 
[1] "fall"
# If you use more than one quoted name in the [[double-brackets]]
# R does "recursive indexing" and finds the one item that you specified
# which may be inside nested named lists or nested named vectors
#
# The following returns JUST the vector of student 
# names that is in section311 in ids2030 in the classes list

gradebook[[c("classes","ids2030","section311","students")]] # "abe" "carl" "dave"
[1] "abe"  "carl" "dave"
# This is the same thing ... and much easier to type

gradebook$classes$ids2030$section311$students        # same thing - "abe" "carl" "dave"
[1] "abe"  "carl" "dave"
gradebook [[ c(3,2,1,1)]]  # same thing - 
[1] "abe"  "carl" "dave"
gradebook [[ c(3,2,1,1,2)]]  # "carl"
[1] "carl"

25.19 NAMED VECTORS

##############################################################################.
##############################################################################.
## NAMED VECTORS
##
## Just like you can have named lists, you can also have vectors with names.
##
## You can use a vector of quoted "names" as an index for a vector or a list.
##############################################################################.
##############################################################################.

#-------------------------------------------------------.
# HOW TO CREATE A NAMED VECTOR - FIRST WAY
#
#   someVector = c(name1=value1, name2=value2, etc)
#
#   (No need to put "quotes" around the names)
#-------------------------------------------------------.

# create a numeric vector with names
test1 = c(joe=50, sam=60, sue=70, bob=80, anne=90)

#-------------------------------------------------------.
# The names are displayed with the vector.
#-------------------------------------------------------.
test1
 joe  sam  sue  bob anne 
  50   60   70   80   90 
# joe  sam  sue  bob anne 
#  50   60   70   80   90 

#-------------------------------------------------------.
# HOW TO CREATE A NAMED VECTOR - SECOND WAY
#
#   names(someVector) = c("name1", "name2", ... etc.)
#-------------------------------------------------------.

# Create a vector without names
calories = c(95, 102, 45)
calories 
[1]  95 102  45
# [1] 95 102 45

# Add names.
names(calories) = c("apple", "pear", "orange")
calories
 apple   pear orange 
    95    102     45 
# apple   pear orange 
#    95    102     45

25.19.1 A vector with names has the same mode as if it didn’t have names.

#------------------------------------------------------------------------.
# A vector with names has the same mode as if it didn't have names.
# You can continue to treat numeric data as numeric, logical data as logical, etc.
#------------------------------------------------------------------------.

test1
 joe  sam  sue  bob anne 
  50   60   70   80   90 
# joe  sam  sue  bob anne 
#  50   60   70   80   90 

mode(test1)   # "numeric"
[1] "numeric"
mean(test1)   # this still works
[1] 70
# [1] 70

test1 + 1     # this still works
 joe  sam  sue  bob anne 
  51   61   71   81   91 
# joe  sam  sue  bob anne 
#  51   61   71   81   91


#------------------------------------------------------------------------.
# You can see from the output of str that a vector has names
#------------------------------------------------------------------------.

str(test1) 
 Named num [1:5] 50 60 70 80 90
 - attr(*, "names")= chr [1:5] "joe" "sam" "sue" "bob" ...
# Named num [1:5] 50 50 70 80 90
# - attr(*, "names")= chr [1:5] "joe" "sam" "suzanne" "bob" ...

25.19.2 names(SOME_VECTOR) returns a character vector of the names - you can use this to change the names

#------------------------------------------------------------------------.
# You can use the names() function to access just the names of a vector or list.
# The value returned from names() is a "character" vector.
# You can change the names by using the names() function.
#------------------------------------------------------------------------.

# You can access just the names with the names function
names(test1)
[1] "joe"  "sam"  "sue"  "bob"  "anne"
# [1] "joe"  "sam"  "sue"  "bob"  "anne"


# You can change SOME of the names 
names(test1)[3] = "suzanne"

test1
    joe     sam suzanne     bob    anne 
     50      60      70      80      90 
# joe     sam suzanne     bob    anne 
#  50      60      70      80      90 

# ... or you can change ALL of the names 
names(test1) = c("joseph", "samuel", "suzanne", "robert", "anne")
test1
 joseph  samuel suzanne  robert    anne 
     50      60      70      80      90 
# joseph  samuel suzanne  robert    anne 
#     50      50      70      80      90 

25.19.3 INDEXING A NAMED VECTOR WITH THE NAMES

############################################################################.
# INDEXING WITH NAMES
#
# You can use a vector of names as the index to a named vector. 
# You must use "quotes" around the names in the index.
# This also works for lists that have names.
############################################################################.

rm(list =ls())

# create a named vector
test1 = c(joe=50, sam=60, sue=70, bob=80, anne=90)
test1
 joe  sam  sue  bob anne 
  50   60   70   80   90 
# Use a vector of quoted "names" as the index

test1[c("sue", "anne")]  
 sue anne 
  70   90 
# sue anne 
#  70   90



# add 5 points to just sue and anne's grades
test1[c("sue", "anne")] = test1[c("sue", "anne")] + 5

test1
 joe  sam  sue  bob anne 
  50   60   75   80   95 
# joe  sam  sue  bob anne 
#  50   60   75   80   95 

#--------------------------------------------------------------------------.
# Even though a vector or a list have names, all the other 
# indexing methods (i.e. positive numbers, negative numbers, TRUE/FALSE)
# continue to work.
#--------------------------------------------------------------------------.

test1
 joe  sam  sue  bob anne 
  50   60   75   80   95 
# joe  sam  sue  bob anne 
# 50   60   75   80   95 

test1[c(3,5)]                # use positive position numbers as the index
 sue anne 
  75   95 
# sue anne 
#  75   95

test1[c(-1,-2,-4)]           # use negative position numbers as the index
 sue anne 
  75   95 
# sue anne 
#  75   95

test1[c(FALSE,FALSE,TRUE,FALSE,TRUE)]   # use a logical vector as the index
 sue anne 
  75   95 
# sue anne 
#  75   95

25.20 someVector = unlist( SOME_LIST ) # convert a list into a vector

#############################################################################.
#############################################################################.
##
## someVector = unlist( SOME_LIST )
##
##    Creates a vector from the contents of SOME_LIST
##
##############################################################################.
##############################################################################.

rm(list=ls())

#...........................................................................
# example 1 - a simple list - all values are put into a single vector
#...........................................................................

listOfNumerics = list( c(1,2,3),
                       c(10,20,30),
                       c(100,200,300))
listOfNumerics
[[1]]
[1] 1 2 3

[[2]]
[1] 10 20 30

[[3]]
[1] 100 200 300
nums = unlist(listOfNumerics)
nums      # [1] 1 2 3 10 20 30 100 200 300 
[1]   1   2   3  10  20  30 100 200 300
#-----------------------------------------------------------------------------.
# example 2
# If the list contains nested lists, you still get a single vector that
# contains all of the values from all of the lists.
#
# NOTE this can be modified by specifying 
#   unlist(SOME_VECTOR, recursive=FALSE)
#
# See the documentation for more info: ?unlist
#-----------------------------------------------------------------------------.

# create a list with multiple nested lists

listOfLists = list( c(10,20),
                    list (
                      c(30,40),
                      c(50,60),
                      list(
                        c(70,80),
                        c(90,100)
                      )
                    ),
                    list ( 200, 400 )
)

# unlist takes all values and puts them into a single vector.

unlist(listOfLists) # [1]  10  20  30  40  50  60  70  80  90 100 200 400
 [1]  10  20  30  40  50  60  70  80  90 100 200 400
#...........................................................................
# example 3 - 
#   If only SOME of the items in the list have names,
#   so only some of the values in the resulting vector will have names.
#   The resulting vector positions "without" names
#   actually have "" as their name. (see code below)
#...........................................................................

# setup a list with only some names
listWithSomeNames = list( c(1,2),
                          tens=c(10,20,30),
                          c(100,200),
                          famous=round(c(pi, exp(1)), digits=2)
)
str(listWithSomeNames)
List of 4
 $       : num [1:2] 1 2
 $ tens  : num [1:3] 10 20 30
 $       : num [1:2] 100 200
 $ famous: num [1:2] 3.14 2.72
# convert to a vector
vec = unlist(listWithSomeNames)  # see result below
vec
                  tens1   tens2   tens3                 famous1 famous2 
   1.00    2.00   10.00   20.00   30.00  100.00  200.00    3.14    2.72 
# Result:
#                tens1   tens2   tens3                 famous1 famous2 
# 1.00    2.00   10.00   20.00   30.00  100.00  200.00    3.14    2.72 


# The value in the names vector for those values that "dont have names"
# is the empty string, i.e. "" (nothing - not even a space - is between the quotes)
names(vec) # ""  ""  "tens1" "tens2" "tens3"  ""  ""  "famous1" "famous2"
[1] ""        ""        "tens1"   "tens2"   "tens3"   ""        ""       
[8] "famous1" "famous2"
#...........................................................................
# example 4 - 
#   For nested list with names. The names in the vector are a combination
#   of the names from each list in the hierarchy (see example)
#...........................................................................

# Setup a nested list with names at each level.
nestedList = 
  list( 
    fin = list (
      taxes = c(10, 20),
      invest = c(30, 40)
    ),
    shop = list(
      fruit   = c("apples", "oranges"),
      amount = c(5,        4)
    )
  )

str(nestedList)
List of 2
 $ fin :List of 2
  ..$ taxes : num [1:2] 10 20
  ..$ invest: num [1:2] 30 40
 $ shop:List of 2
  ..$ fruit : chr [1:2] "apples" "oranges"
  ..$ amount: num [1:2] 5 4
# Names in the vector combine names from different levels in the nested list
# (see result below)
unlist(nestedList)
  fin.taxes1   fin.taxes2  fin.invest1  fin.invest2  shop.fruit1  shop.fruit2 
        "10"         "20"         "30"         "40"     "apples"    "oranges" 
shop.amount1 shop.amount2 
         "5"          "4" 
# Result: 
# fin.taxes1   fin.taxes2  fin.invest1  fin.invest2  shop.fruit1  shop.fruit2 shop.amount1 shop.amount2 
#       "10"         "20"         "30"         "40"     "apples"    "oranges"          "5"          "4" 


#...........................................................................
# example 5 - 
#   For nested list with names, if you specify use.names=FALSE
#   the vector will NOT have names.
#...........................................................................

# using same list as previous example
str(nestedList)
List of 2
 $ fin :List of 2
  ..$ taxes : num [1:2] 10 20
  ..$ invest: num [1:2] 30 40
 $ shop:List of 2
  ..$ fruit : chr [1:2] "apples" "oranges"
  ..$ amount: num [1:2] 5 4
unlist(nestedList)  # generates a NAMED vector
  fin.taxes1   fin.taxes2  fin.invest1  fin.invest2  shop.fruit1  shop.fruit2 
        "10"         "20"         "30"         "40"     "apples"    "oranges" 
shop.amount1 shop.amount2 
         "5"          "4" 
unlist(nestedList, use.names = FALSE)  # gernerates an UNnamed vector
[1] "10"      "20"      "30"      "40"      "apples"  "oranges" "5"      
[8] "4"      
#--------------------------------------------------------------------------.
# ADDITIONAL FEATURES OF unlist() FUNCTION
#
# The unlist function has two additional arguments that control how it works
# with nested lists and with named lists.
# See the help page ?unlist for more info.
# 
#   - The recursive argument controls exactly how unlist works when the list
#     contains nested lists. The default is recursive=TRUE. See the help
#     file for more info, ?unlist
#
#   - The use.names argument controls exactly how unlist works when the list
#     contains names. (see example 5 above).
#     The default is use.names=TRUE.  For more info see ?unlist
#
#--------------------------------------------------------------------------.

25.21 REVIEW

############################################################################.
# REVIEW
#
# SIMILARITIES BETWEEN VECTORS AND LISTS 
#
# - Both VECTORS and LISTS can have names
#
# - Indexing with [single-brackets]
# 
#   VECTORS and LISTS can be indexed with [single-brackets] in the same ways. 
#   With VECTORS the result is a VECTOR and  with LISTS the results is a LIST.
#
#   There are 4 ways of using [single-brackets] for indexing.
#
#     o position position numbers, eg. someVector[c(1,2)]        or someList[c(1,2)]
#     o negative position numbers, eg. someVector[c(-1,-2)]      or someList[c(-1,-2)]
#     o logicals (TRUE/FALSE),     eg. someVector[c(TRUE,FALSE)] or someList[c(TRUE,FALSE)]
#     o names, eg.  someVector[c("oneName","anotherName")] or someList[c("oneName","anotherName")]
#
# - Indexing with [[double-brackets]]
#   
#     o Retrieves exactly one value from the VECTOR or the LIST
#
#     o Only positive numbers or names can be used for the index
#
#     o If more than one positive number is used e.g. someList[[c(3,2,4)]]
#       or more than one name is used, eg. someList[c("classes","ids2030","sectionC")]
#       then recursive indexing is used. 
#       This is really the same concept for vectors, however, since a vector can
#       only contain individual values (and not lists) you cannot have more 
#       than one positive number or one name in the double brackets.
#
# - length() returns the length of both vectors and lists
#
# DIFFERENCES BETWEEN VECTORS AND LISTS
#
# - You can use $dollar-sign notation with lists but NOT with vectors
############################################################################.

25.22 TERMINOLOGY: “atomic vector” (is a vector) “recursive vector” (is a list)

###########################################################################.
# TERMINOLOGY
#
#    "atomic vector"    is just a fancy name for a "vector"
#    "recursive vector" is just a fancy name for a "list"
#
# Most books, tutorials, etc. refer to "vectors" and "lists" as distinct
# and separate types of objects - exactly as we have.
#
# However, sometimes what we have been calling "vectors" are referred to 
# as "atomic vectors" (since they contain individual "atomic" values that 
# cannot be broken down further.) Sometimes what we have been calling
# "lists" are referred to as "recursive vectors", i.e. vectors that can
# have other vectors inside of them. Most people who use R are not
# familiar with these more complicated terms. However, it is good to
# be aware of these terms in case you come across them while
# reading documentation or other advanced information about R. 
# For example: https://r4ds.had.co.nz/vectors.html
#
# Bottom line:
#    "atomic vector" is just a fancy name for a "vector"
#    "recursive vector" is just a fancy name for a "list"
###########################################################################.