Язык программирования R для анализа данных: лекция 1

Елена Убогоева

Обо мне

Образование:

  • Аспирантура ИЦиГ СО РАН, биоинформатика, 2020 - настоящее время

  • Магистратура НГУ, факультет естественных наук, кафедра цитологии и генетики, 2018 - 2020

  • Бакалавриат НГУ, факультет естественных наук, кафедра цитологии и генетики, 2014 - 2018

Опыт преподавания (избранное):

  • Технический ассистент на курсах бластим “Статистика, R и анализ данных” июнь 2022, октябрь 2022

  • Преподаватель семинаров по курсу “Компьютерная транскриптомика” в магистратуре НГУ, кафедра биоинформатики, 2020-2021

  • Преподаватель курса в Сириусе, направление “Математическое моделирование в биологии развития растений”, апрель 2022

Информация о курсе

  • 12 лекций по одной лекции в неделю:
    в воскресенье в 12, возможно в четверг в 19.00

  • Домашние задания

  • Итоговый проект

  • С зачетными единицами пока не ясно, возможно удасться договориться с ПМ Бородиным, чтобы мой курс мог засчитаться

  • Поддержать можно по ссылке

Установка R и RStudio и начало работы

  • Скачать и установить свежую версию R с официального сайта (CRAN);

  • Скачать и установить IDE RStudio;

  • Больше рекомендаций и решений возможных проблем при установке можно почитать здесь;

  • Advanced: настройка проектов и лайфхаки при работе в RStudio.

Рекомендуемая литература

Большая часть литературы по R на английском, однако начинают появляться в том числе материалы на русском.

План лекции

  • История языка

  • Особенности архитектуры

  • Переменные

  • Векторы:

    • Типы векторов

    • Индексация векторов

    • Неявное и явное приведение типов

  • Логические операторы, булева алгебра

  • Работа с пропущенными значениями

История языка R

  • R - язык программирования для статистической обработки данных и визуализации результатов (Википедия).

  • На самом деле R является языком широкого использования, например, с его помощью можно делать сайты, интерактивные приложения (shiny), презентации, писать книги и научные статьи.

  • R является наследником языка S, который был создан в отделе статистики AT&T в её исследовательском подразделении Bell Labs в 1976 году.

  • R был создан в 1991 в департаменте статистики Университета Окленда, Новая Зеландия. Разработчиками R поддерживается CRAN - репозиторий R-пакетов.

  • Для анализа данных есть набор пакетов tidyverse, главный разработчик Hadley Wickham.

  • Для работы с биологическими данными есть репозиторий Bioconductor.

  • iDEP - shiny app для анализа дифференциальной экспрессии генов

  • shinyGO - shiny app для функциональной аннотации списков генов

Особенности архитектуры языка

R - высокоуровневый интерпретируемый мультипарадигменный язык программирования.

  • Парадигмы программирования:

    • императивное (процедурное)

    • функциональное

    • объект-ориентированное

  • Типизация:

    • динамическая - не нужно заранее определять тип переменной и тип может меняться в процессе скрипта

    • нестрогая - происходит неявное приведение типов

R как калькулятор

Любая книга по базовому R начинается с этого раздела, так что не будем нарушать традицию:

40 + 2 # сложение
[1] 42
20 - 2 # вычитание
[1] 18
42 * 2 # умножение
[1] 84
42 / 2 # деление
[1] 21
2 ^ 4 # возведение в степень
[1] 16

R как калькулятор

Функции для арифметических операторов: sqrt(), log()

  • Квадратный корень

    sqrt(16) # квадратный корень, от слова square root
    [1] 4
  • Логарифм

    log(2) # по какому основанию логарифм?
    [1] 0.6931472
  • По умолчанию натуральный логарифм, то есть основание e

    log(exp(1)) # по умолчанию натуральный логарифм
    [1] 1

Можно задать основание логарифма. Как это сделать, можно узнать, вызвав справку функции ?log.

log(x = 8, base = 2) # логарифм от числа 8 по основанию 2
[1] 3
log2(x = 8) # тоже логарифм от 8 по основанию 2
[1] 3
# есть также отдельная функция для десятичного логарифма
log10(1000)
[1] 3

Для вызова справки можно использовать ?<название функции>, help("<название функции>") или нажать F1 на функции

Переменные

Поскольку R является в первую очередь императивным языком программирования, то переменные и оператор присваивания занимают центральную роль. В качестве оператора присваивания используется <-.

Можно набрать в RStudio, используя комбинацию клавиш Alt - (альт минус) или Option - на Mac.

Равно = тоже будет работать, но не рекомендуется к использованию.

Попробуем создать переменные:

x <- 10
y <- x + 5

Почему ничего не появилось в консоли в качестве аутпута?

Равно используется для подачи аргументов в функции: log(x = 8, base = 2)

Переменные

При создании переменной результат сохраняется и не выводится в консоль, если нам нужно узнать значение переменной, то можно использовать print(x) или просто набрать x в консоли.

print(x)
[1] 10
x
[1] 10
y
[1] 15

Также можно посмотреть на переменные во вкладке environment в правом верхнем углу RStudio

Переменные

С точки зрения компьютера в нем создается объект 10, с которым связывается название переменной x. Переменные могут содержать все что угодно: числа, строки, датафреймы.

При создании переменных есть определенные правила:

  • В названии можно использовать латиницу (строчные и заглавные буквы), нижнее подчеркивание (_), точки, цифры;

  • нельзя использовать пробелы в названиях переменных;

  • нельзя использовать зарезервированные слова, например, TRUE, FALSE, if, function, с полным списком можно ознакомиться, вызвав ?Reserved.

В R для разделения длинных имен рекомендуется использовать нижнее подчеркивание, например: data_processed. Главное - последовательность в стиле написания кода.

Подробнее про рекомендуемый стиль написания кода можно почитать здесь

Логические операторы

Можно проверять на равенство переменные:

x == y
[1] FALSE
y == 15
[1] TRUE

Не путаем операторы сравнения == и присваивания = (еще одна причина не использовать равно как оператор присваивания).

Логические операторы

Чтобы проверить на неравенство используем оператор !=:

logical1 <- x != y
logical1
[1] TRUE
logical2 <- x != 10
logical2
[1] FALSE

Еще можно сравнивать больше-меньше:

x > y
[1] FALSE
x < y
[1] TRUE

Восклицательный знак означает логическое НЕ (отрицание)

!TRUE
[1] FALSE
!FALSE
[1] TRUE

Еще немного булевой алгебры

Логическое И: выдаст TRUE, если все значения TRUE

logical1 & logical2
[1] FALSE

Логическое ИЛИ: будет TRUE, если хотя бы одно из значений TRUE

logical1 | logical2
[1] TRUE

Векторы

Векторы являются ключевым типом данных в R, на основе которых строятся более сложные структуры данных. Скаляры (одиночные значения) являются векторами длиной 1.

Вектор - набор данных одного типа. В R существует 4 основных часто использующихся типов векторов.

Основные типы векторов:

  • Логические, logical: TRUE, FALSE

  • Целочисленные, integer: 1L, 190L

  • Дробные, double или с плавающей точкой: -1.5, 0.05, 5. Еще есть Inf, -Inf, NaN

  • Строковые, character: 'Hello world', 'character'

Целочисленные вектора сопровождаются знаком L и не содержат дробной части

Создание векторов

  • с помощью функции c(). Например: x <- c(5, 4, 5, 3, 2).
    gene_names <- c('ARR1', 'ARF19', 'WOX5')

  • с помощью двоеточия :, чтобы создавать набор чисел по порядку с шагом 1. Например:

    16:30
     [1] 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  • с помощью seq(), если нужна последовательность чисел, с шагом, отличным от единицы

    seq(from = 2, to = 20, by = 2)
     [1]  2  4  6  8 10 12 14 16 18 20
  • с помощью rep() для создания повторяющихся элементов:

    rep(c(1:3, 5), times = 3)
     [1] 1 2 3 5 1 2 3 5 1 2 3 5

Индексация векторов

В R индексация начинается с единицы (по-человечески). Для индексации используются квадратные скобки:

sample_vector <- 11:20
sample_vector[1] # извлечь первый элемент вектора
[1] 11

Для извлечения нескольких элементов вектора в качестве индекса используется тоже вектор:

sample_vector[2:5] # извлечь элементы со второго по пятый
[1] 12 13 14 15
sample_vector[c(3:6, 1, 10)] # извлечь элементы с третьего по шестой, первый и десятый
[1] 13 14 15 16 11 20

Индексация векторов

Можно с помощью индексации заменять элементы:

sample_vector[2] <- 0
sample_vector
 [1] 11  0 13 14 15 16 17 18 19 20

Tip

Круглые скобки используются при работе с функциями, квадратные при индексации

Индексация векторов с помощью условия

Кроме индексации по номеру, можно извлечь элементы вектора, соответствующие какому-либо условию.

Например, извлечь все значения, которые больше или равны нулю вектора x

x <- -3:3
x
[1] -3 -2 -1  0  1  2  3

Сначала используем условие:

x >= 0
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE

Получаем логический вектор, который можно использовать как индекс для отбора нужных элементов x

x[x >= 0]
[1] 0 1 2 3

Индексация векторов: проверка на четность

  • Допустим, у нас есть вектор vect_for_even

    vect_for_even <- sample(1:100, 10)
    vect_for_even
     [1] 26 96 18 94 33 38 78 25  6 47

    Задача вывести все четные элементы, используя знания об индексации векторов. Оператор остатка от деления: %%

  • Это решается без циклов. Сначала делаем проверку условия, что остаток от деления на два каждого элемента равен нулю:

    vect_for_even %% 2 == 0
     [1]  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE
  • Далее используем полученный логический вектор в качестве индекса к исходному:

    vect_for_even[vect_for_even %% 2 == 0]
    [1] 26 96 18 94 38 78  6

Динамическая нестрогая типизация

  • Динамическая: не нужно при создании вектора указывать его тип, как бывает в языках со статичной типизацией. Также тип вектора может поменяться в процессе кода:

    vect <- seq(3, 11, 2)
    vect
    [1]  3  5  7  9 11
    vect <- 'Hello world'
  • Нестрогая (слабая, weak): происходит неявное приведение типов

R на этой картинке должен быть в левом нижнем углу

Неявное приведение типов

  • Векторы в R по определению могут быть только одного типа. Что произойдет, если мы попытаемся объединить в одном векторе элементы разных типов?

    Например так:

    x <- c(10, TRUE, FALSE)

    Будет ли ошибка или каким-то образом произойдет объединение?

  • Ответ:

    x
    [1] 10  1  0
  • Произошло неявное приведение типов (implicit coercion): логический тип превратился в числа, TRUE -> 1, FALSE -> 0

Порядок неявного приведения типов

Логика порядка приведения типов: от менее общего к более общему

logical -> integer -> numeric -> character

Часто происходит превращение числового вектора в строку.

Полезный пример неявного приведения типов: подсчет суммы истинных значений логического вектора

sum(1:10 > 5)
[1] 5
set.seed(1)
p_val <- c(0.03, sample(seq(0.00001, 1, length.out = 100), 10))
p_val
 [1] 0.0300000 0.6767709 0.3838445 0.0000100 0.3333400 0.8686882 0.4242482
 [8] 0.1313218 0.8181836 0.5858627 0.5050555
sum(p_val < 0.05)
[1] 2

Неявное приведение типов: иллюстрация

c(1, 2, 3, 4, 5, '6')
[1] "1" "2" "3" "4" "5" "6"

Проверка типа вектора

Чтобы проверить тип вектора, можно использовать функцию class():

x <- c(1, 2, 3, 4, 5, '6')
class(x)
[1] "character"

Также можно использовать функции проверки вектора на нужный тип: is.numeric(), is.character(), is.logical()

is.numeric(x)
[1] FALSE
is.character(x)
[1] TRUE

Чтобы ознакомиться со списком функций is.* можно набрать is. в консоли и нажать Tab

Явное приведение типов

Чтобы превратить вектор в нужный тип можно использовать функции: as.numeric(), as.character(), as.logical()

x
[1] "1" "2" "3" "4" "5" "6"
as.numeric(x)
[1] 1 2 3 4 5 6

Создадим вектор, который содержит строки и превратим в numeric обратно

char_vector <- c(1:6, 'seven')
char_vector
[1] "1"     "2"     "3"     "4"     "5"     "6"     "seven"
as.numeric(char_vector)
[1]  1  2  3  4  5  6 NA

Пропущенные значения

При работе с данными могут быть пропущенные значения в силу разных причин. В R обозначается NA - Not Available

vector_missed <- c(1:10, NA)
vector_missed
 [1]  1  2  3  4  5  6  7  8  9 10 NA

Проблема пропущенных значений: неопределенность “заражает”

sum(vector_missed)
[1] NA
mean(vector_missed)
[1] NA

Аргумент na.rm - способ вычислить среднее (сумму и тп) без учета пропущенных значений

mean(vector_missed, na.rm = TRUE)
[1] 5.5

Как находить пропущенные значения?

  • Давайте попробуем сравнить значения нашего вектора с NA

    vector_missed == NA
     [1] NA NA NA NA NA NA NA NA NA NA NA
  • Ой
    Почему в сравнении NA == NA получается NA?

    NA == NA
    [1] NA
  • Пропущенное значение - это отсутствие информации о чем-либо, следовательно, мы не можем сделать вывод, является ли отсутствие информации равным другому отсутствию.

Для выявления пропущенных значений используем функцию is.na()

is.na(vector_missed)
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE

Теперь с помощью логического отрицания (NOT, !) этого вектора можно отобрать не-пропущенные элементы:

!is.na(vector_missed)
 [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
vector_missed[!is.na(vector_missed)]
 [1]  1  2  3  4  5  6  7  8  9 10

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

Чек-лист, что нужно знать после лекции

  • Что такое переменные, как их создавать

  • Как проверять переменные на равенство/неравенство, больше/меньше

  • Правила индексации векторов

  • Неявное приведение типов

  • Работа с пропущенными значениями

Спасибо за внимание!

Подписывайтесь на телеграм-канал о статистике: