Основы программирования: списки

Копия List_Cat
"Список - это упорядоченный набор объектов (компонентов). Список может объединять разные (возможно, не связанные между собой) объекты под одним именем. К примеру, список может представлять собой сочетание векторов, матриц, таблиц данных и даже других списков" (Роберт Кабаков. Анализ и визуализация данных на языке R). Главное преимущество списков связано с их гибкостью. Во-первых, как уже упомянуто, они могут включать данные и объекты разных типов. Во-вторых, размер списков не задается при их создании и может меняться в соответствии с интересами исследователя. Эти две особенности отличают списки от векторов. Третьим, но менее важным отличием является то, что доступ к элементам списков возможно как по индексам, так и по ключевым словам. В этом смысле списки в R объединяют свойства как списков, так и словарей (если использовать Python-овскую терминологию).
Именно списки очень удобно использовать в работе циклов - добавление новых элементов будет идти не по пути перезаписи списка, а по пути его расширения. Благодаря использованию циклов, условных операторов и списков открываются широкие возможности решения творческих задач как в рамках программирования в целом, так и в рамках анализа данных в частности.
Создание списков
Список создается с помощью функции list(). При этом есть два варианта - с указанием имен и без него. Если при создании имена указываются (см. 1 строку синтаксиса), то доступ к соответствующему элементу может осуществляться как с помощью индексирования, так и с помощью самих имен. Если же имена не указываются (см. 6 строку синтаксиса), то остается только доступ с помощью индексирования.
1
2
3
4
5
6
7
8
  > test <- list(money=c(300,400,500),name=c("Аня","Люда","Таня"))
  > test$money
  [1] 300 400 500
  > test[[2]]
  [1] "Аня"  "Люда" "Таня"
  > testNew <- list(c(200,300,400),c("Коля","Паша","Вася"))
  > testNew[[2]]
  [1] "Коля" "Паша" "Вася"
Как видно, первый список (test), включает два элемента: числовой вектор с именем "money" и текстовый с именем "name". Далее продемонстрированы способы доступа к его элементам с помощью имени и с помощью индекса. Следующий список (testNew) идентичен первому с тем отличием, что его элементы не содержат имен.
Рассмотрим еще один пример, в котором список еще более похож на склад различных элементов:
 9
10
11
12
13
14
15
16
17
18
19
20
21
  > dolla <- c(300,400,500)
  > ppl <- c("Аня","Люда","Таня")
  > someData <- data.frame(dolla,ppl)
  > testNew <- list(df=someData,c(T,T,F),end=88)
  > testNew[[2]][3]
  [1] FALSE
  > testNew$df
    dolla  ppl
  1   300  Аня
  2   400 Люда
  3   500 Таня
  > testNew[[3]]
  [1] 88
В данном случае сначала создается фрейм данных someData, после чего он и другие данные записываются в список. При этом два элемента списка имеют имена, один - нет.
Доступ к элементам списка
Как уже было показано, доступ к элементам списка осуществляется с помощью указания индекса в квадратных скобках, а также с помощью указания имени элемента после знака "$" (конечно если элемент имеет имя).
При использовании индекса можно использовать как одинарные квадратные скобки, так и двойные. Различие в том, что в первом случае возвращаемый объект будет новым списком с одним элементом, во втором - будет возвращен тот тип объекта, к которому мы возвращаемся.
Чтобы понять о чем идет речь рассмотрим пример:
22
23
  > itIsNewList <- testNew[1]
  > itIsNewDataFrame <- testNew[[1]]
В первой строке предыдущего синтаксиса создается новый список с одним элементом - фреймом данных. Во второй строке будет получен сам фрейм данных, а не список.
При использовании ключевых слов для доступа к элементам списка, они указываются либо после знака "$", либо в квадратных скобках (само слово при этом указывается в кавычках):
24
25
26
  > oneNumber <- testNew$end
  > oneNumber <- testNew["end"]
  > oneNumber <- testNew[["end"]]
Первый способ ведет к созданию скаляра, второй - к созданию нового списка с одним элементом-скаляром, третий - к созданию скаляра.
Добавление и удаление элементов списка
Новые элементы списка добавляются с помощью присваивания. При этом указывается индекс или имя элемента, определяющие куда будет записана информация. Здесь возможны следующие варианты: а) если указывается индекс уже имеющегося элемента списка (например, индекс 3 для списка с тремя элементами), то новая инфомация записывается вместо старой; б) то же самое справедливо, когда используется имя уже имеющегося элемента; в) если указывается индекс, элемента которого еще нет (например, индекс 5 для списка с тремя элементами), то будет соответствующая информация появится на указанной позиции, а все предыдущие элементы (в нашем случае 4 элемент) будут обозначены как не имеющие данных (NULL); г) если при создании используется еще не существующее имя, то соответствующий элемент с этим именем будет записан на новую свободную позицию, созданную в конце списка (в нашем случае это будет позиция с индексом 6).
Рассмотрим синтаксис для каждого из вариантов:
27
28
29
30
31
32
33
34
35
36
37
38
  > testNew[[3]] <- 99 # вариант а
  > testNew[[3]]
  [1] 99
  > testNew$end <- 111 # вариант б
  > testNew[[3]]
  [1] 111
  > testNew[[5]] <- c(88,99) # вариант в
  > testNew[[4]]
  NULL
  > testNew$last <- FALSE # вариант г
  > testNew[[6]]
  [1] FALSE
Удаляются элементы с помощью присваивания им значения NULL (можно прописать как с помощью индекса, так и с помощью имени). При этом если удаляется любой элемент кроме последнего, то все остальные сдвигаются влево. Например, если в списке из пяти элементов удалить третий, то первый и второй останутся на своих местах, четвертый станет третим (займет место удаленного элемента), а пятый - четвертым.
В предыдущем примере удалим четвертый пустой элемент:
39
40
41
42
43
  > length(testNew) # проверяем длину объекта (количество элементов)
  [1] 6
  > testNew[[4]] <- NULL # удаляем пустой элемент
  > length(testNew) # опять проверяем длину объекта
  [1] 5
В завершение рассмотрим синтаксис c использованием списков из реальной практики:
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  > for (i in 1:17) {
      test <- allDatatest[which(allDatatest$godNew == filt[i]) , ]
      testNew <- data.frame(c(1:length(test$godNew)))
      for (j in (1:31)) {
        if (mean(test[,j])!=-999 & sd(test[,j], na.rm=T)!= 0) {
          testNew <- cbind(testNew,test[,j])
          names(testNew)[length(testNew)] <- nameS[j]
        }
      }
      testNew$godNew <- NULL
      testNew$c.1.length.test.godNew.. <- NULL
      matr <- as.matrix(testNew)
      matr[matr==-999]<-NA
      testNew <- as.data.frame(matr)
      listForPrestVar[[i]] <- testNew
    } 
Задача этого синтаксиса подготовка фреймов данных для каждого года (testNew) исследования и их запись в список listForPrestVar (строка синтаксиса 58). После завершения цикла список будет состоять из 17 элементов. В дальнейшем он используется для обработки каждого фрейма данных по отдельности.

статью подготовил кандидат социологических наук Сергей Дембицкий

Вверх
blog comments powered by Disqus