Вывод сообщения на экран

В R для вывода сообщения на экран есть две команды: print() и cat().

print("There are two commands") # с указанием номера строки и в кавычках
## [1] "There are two commands"
cat("There are two commands") # без указания номера строки и без кавычек
## There are two commands

При желании, если используем print(), кавычки в выдаче можно убрать, скорректировав параметр quote:

print("There are two commands", quote=FALSE)
## [1] There are two commands

Команда cat() позволяет не только выводить на экран уже “готовые” элементы, но и склеивать выдачу из нескольких:

w1 <- "there"
w2 <- "are"
w3 <- "two"
w4 <- "commands"

cat(c(w1, w2, w3, w4), sep=" ") # разделитель - пробел
## there are two commands
cat(c(w1, w2, w3, w4), sep="_") # разделитель - знак подчеркивания
## there_are_two_commands

Кавычки (внешние или внутренние) могут быть любые, но если в тексте уже встречаются кавычки, то одни кавычки должны быть одинарные, а другие двойные.

cat('Не хочу писать 
    "Hello world"!')
## Не хочу писать 
##     "Hello world"!
cat("Не хочу писать 
    'Hello world'!")
## Не хочу писать 
##     'Hello world'!

Можно выводить каждый элемент с новой строки или с отступом (табуляция):

cat(' 1\n','2\n','3\n') # \n - переход на новую строку 
##  1
##  2
##  3
cat(' 1\t','2\t','3\t') # \t - табуляция
##  1    2   3  

Конкатенация (склевание) строк:

paste('group', 341) # по умолчанию разделяются пробелом
## [1] "group 341"
paste('01', '10', '2015', sep = '-') # можем задавать разделитель между строками (sep)
## [1] "01-10-2015"

Циклы

Часто, чтобы повторять действия в R и при этом не копировать одни и те же команды много раз, используются циклы. Есть два цикла: цикл for и цикл while.

Цикл for

Пусть у нас есть вектор u:

u <- c(1, NA, 0, NA, 1, 0, 0, 1, NA)

Нам нужно вывести все элементы вектора на экран. Для этого надо пройтись по каждому элемента вектора u и выполнить команду cat():

for (i in u) {
  cat(i,'\n') # \n чтобы каждый элемент выводился с новой строки
  }   
## 1 
## NA 
## 0 
## NA 
## 1 
## 0 
## 0 
## 1 
## NA

То, по чему итерируем (пробегаемся), указывается в круглых скобках, а то, что делаем в ходе цикла - в фигурных.

Для перебора можно задействовать не только векторы, но и просто промежутки (аналог range() в Python).

for (j in 1:10) {
cat(j)
}
## 12345678910

Цикл while

Цикл while устроен немного по-другому: действие повторяется до тех пор, пока выполняется некоторое условие.

t <- 1 # стартуем со значения 1

while (t < 10){
  cat(t, "\n") # пока значение меньше 10, выводим его на экран
  t <- t + 1 # переходим к следующему значению
}
## 1 
## 2 
## 3 
## 4 
## 5 
## 6 
## 7 
## 8 
## 9

Условия с if и else

Пусть у нас есть вектор w, состоящий из нулей и единиц, и мы хотим по очереди проверять каждый элемент и выводить на экран “yes”, если он равен 1 и “no”, если он равен 0.

w <- c(1, 0, 0, 1, 0, 1)

Для этого помимо цикла нам понадобятся операторы условия if и else. Конструкция с if устроена следующим образом: после if в круглых скобках указывается условие, затем прописывается, что необходимо сделать, если это условие выполняется.

# в круглых скобках - условие, затем - что надо сделать в случае, если оно выполняется
# в случае равенства - не теряем двойной знак =

for (k in w){
  if (k == 1) cat('yes\n') else cat('no\n')
}
## yes
## no
## no
## yes
## no
## yes

При этом необязательно, чтобы else был всегда, все зависит от задачи. Например, выведем только те элементы вектора q, которые больше 5:

q <- 1:10
for (j in q){
  if (j > 1) cat(j, " ") 
}
## 2  3  4  5  6  7  8  9  10

Если есть более сложные, разветвленные условия, то они просто указываются друг за другом. Если необходимо использовать множественные условия (например, какие-то два условия должны выполняться одновременно), то потребуются те же символы, которые используются в логических выражениях (&, |). Рассмотрим следующий пример.

Пусть у нас есть список (вектор) оценок студентов.

grades <- c(7, 4, 10, 6, 3, 11) # вектор оценок

И мы хотим на каждую оценку выдать какой-нибудь комментарий (“хорошо”, “плохо”, “отлично” и др.). Это можно сделать следующим образом:

grades <- c(7, 4, 10, 6, 3, 11) # вектор оценок

for (j in grades) {
  if (j < 4) cat(j, 'Bad\n') else 
    if (j>=4 && j<6) cat(j, 'Not Bad \n') # для одновременности - двойной &
    if (j>=6 && j<8) cat(j, 'Good \n')
    if (j>=8 && j<=10) cat(j, 'Excellent \n')
    if (j>10) cat(j, '"We are the champions - my friend..." \n')
}
## 7 Good 
## 4 Not Bad 
## 10 Excellent 
## 6 Good 
## 3 Bad
## 11 "We are the champions - my friend..."

Конечно, в данном примере можно было обойтись без else. Он включен в пример только для того, чтобы показать, что при использовании разветвленных условий никакого нагромождения скобок не требуется.

Функция assign()

Иногда возникает необходимость создавать переменные (векторы) с похожими названиями, например, group1, group2, group3 и так далее. Если бы названия переменных были бы обычными строками (текстом), это выглядело бы просто:

for(i in 1:4){
  cat(paste("group", i, sep="")) # склеиваем слово и номер и выводим на экран
}
## group1group2group3group4

Но в случае с переменными все не так. Нужно не только создать определенное название, но и сообщить R, что это переменная, которой будет присваиваться значение. Для этого используется функция assign(). Создадим четыре переменных group1 - group4 и присвоим им значения от 1 до 4 соответственно.

for (i in 1:4){
  gr <- paste("group", i, sep="") # название переменной
  assign(gr, i) # значение переменной - (чему присваиваем, что присваиваем)
}

Теперь можно проверить, создались ли нужные нам переменные:

group1; group2; group3; group4
## [1] 1
## [1] 2
## [1] 3
## [1] 4

Все на месте.

Функция assign() удобна тем, что она используется для присвоения значений совсем разным объектам (переменным, векторам, матрицам и целым базам данных) и при этом тип объекта специально указывать не нужно. Если бы мы хотели создать векторы group1 - group4 и заполнить их числами от 1 до 10, то выглядело это бы почти так же, как и в примере выше:

for (i in 1:4){
  gr <- paste("group", i, sep="") 
  assign(gr, 1:10)
}

group1; group2; group3; group4
##  [1]  1  2  3  4  5  6  7  8  9 10
##  [1]  1  2  3  4  5  6  7  8  9 10
##  [1]  1  2  3  4  5  6  7  8  9 10
##  [1]  1  2  3  4  5  6  7  8  9 10

Чтобы объединить все, что было рассмотрено на этом занятии, рассмотрим более жизненные примеры - задачи, с которыми можно столкнуться, работая с реальными данными.

Задача 1. Пусть у нас есть данные по некоторым социально-экономическим показателям в регионах за несколько лет (2000-2012 годы). Нас интересуют 25 регионов. Переменная year в базе данных, которую можно рассматривать как вектор, выглядит следующим образом:

year <- rep(2000:2012, 25) # повторяем значения от 2000 до 2012 25 раз
year[1:26] # для краткости выведем на экран первые 26 значений
##  [1] 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2000
## [15] 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012

В исследовании нам нужно сравнить социально-экономические показатели регионов до 2004 года включительно и после 2004 (например, оцениваем эффект какой-нибудь реформы и перед построением более сложных моделей хотим просто сравнить величины). Для этого нам понадобится создать переменную, которая будет выполнять роль классификатора: принимать значение 0, если год не превышает 2004, и 1 - в противном случае.

Для начала создадим вектор из пропущенных значений (NA) такой же длины, что и year:

bin <- rep(NA, length(year))

А теперь с помощью цикла и условий будем его заполнять:

for (i in 1:length(year)){
if (year[i] <= 2004) bin[i] = 1

  # если i-тый элемент в year не больше 2004
  # то на i-ое место в bin ставим 1

else bin[i] = 0 
}

Выведем первые 26 элементов нового вектора bin и убедимся, что у нас получилось то, что нужно - бинарная переменная для года:

bin[1:26]
##  [1] 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0

Задача 2. Предположим, что был проведен какой-то опрос среди молодежи. Опрашивались респонденты в возрасте от 16 до 27 лет. Есть следующее деление на возрастные группы: 16-18, 19-21, 22-24, 25-27. У нас есть список возрастов респондентов, и мы хотим понять, сколько представителей каждой возрастной группы было опрошено. При этом мы хотим не только их посчитать, но и сохранить их отдельным списком для каждой группы. Другими словами, для каждой возрастной группы мы хотим создать вектор и узнать его длину.

age <- c(16, 23, 25, 25, 27, 17, 16, 24, 17, 19, 21, 18, 25, 26, 22) # список всех возрастов

# конечно, 15 человек - мало для хорошего опроса, но для примера сойдет

for (i in 1:4){
  gr <- paste("group", i, sep="")
  if (i == 1) assign(gr, age[age>=16 & age<=18]) # первая группа 16-18
  if (i == 2) assign(gr, age[age>=19 & age<=21]) # вторая группа 19-21
  if (i == 3) assign(gr, age[age>=22 & age<=24]) # третья группа 22-24
  if (i == 4) assign(gr, age[age>=25 & age<=27]) # четвертая группа 25-27
}
group1; group2; group3; group4 # сами группы
## [1] 16 17 16 17 18
## [1] 19 21
## [1] 23 24 22
## [1] 25 25 27 25 26
length(group1); length(group2); length(group3); length(group4) # их численность
## [1] 5
## [1] 2
## [1] 3
## [1] 5