Зачем использовать R для науки о данных

R традиционно считался предпочтительным языком вычислений статистиков и некоторых академических исследователей из-за большого количества доступных статистических пакетов и большого сообщества. Возможно, самая большая сила R заключается в простоте, с которой решаются задачи обработки данных и статистического анализа. Кроме того, такие пакеты, как ggplot2, генерируют высококачественную визуализацию, делая анализ данных и отчеты приятными, без крутой кривой изучения других языков сценариев.

При этом R также может быть довольно необычным в своем поведении. Недавно я прошел курс по исследовательскому анализу данных с помощью R в рамках программы MSDS в Университете Сан-Франциско, и я усвоил некоторые из этих причуд на собственном горьком опыте. Ниже я составил список из 10 причуд, который может быть не очень интуитивно понятным. Осведомленность о причудах может сэкономить много времени и сэкономить нервы при анализе данных и отладке кода.

1) Использование require () vs library () при загрузке пакета

Как обсуждалось выше, основным преимуществом R является огромное количество пакетов, которые можно легко получить и загрузить в активный сеанс R. Обе функции, require () и library () позволяют нам загружать установленные пакеты в активную память, но есть нюансы в их выводах, когда загрузка рассматриваемого пакета бросает исключение. В то время как library () выдает ошибку, когда пакет недоступен для загрузки в активную память, require () выдать предупреждение и вернуть логический ответ в зависимости от доступности пакета.

> library(randomForest)
Error in library(randomForest) : 
  there is no package called ‘randomForest’

> b <- require(randomForest)
Loading required package: randomForest
Warning message:
In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE,  :
  there is no package called ‘randomForest’
> print(b)
[1] FALSE

require () обычно можно использовать в качестве условной проверки, чтобы узнать, присутствует ли пакет или нет, и установить его.

2) NA против NULL в R

Разница между NA и NULL может вызывать путаницу, поскольку интуитивно можно подумать, что оба они представляют собой отсутствующие / неопределенные значения. Однако R обрабатывает оба этих зарезервированных слова по-разному. NA - это логическая константа длины 1, которая содержит индикатор отсутствующего значения, а NULL представляет собой нулевой объект. Интуитивно понятное объяснение приведенного выше утверждения можно показать с помощью следующего кода:

> v <- c(1,2,3,NA)
> v
[1]  1  2  3 NA
> w <- c(1,2,3,NULL)
> w
[1] 1 2 3

Приведенный выше код помогает нам понять ключевое различие между NA и NULL. NULL является объектом своего собственного типа и не может быть приведен к какому-либо другому типу. Следовательно, когда мы пытаемся включить его в векторную форму, он игнорируется. С другой стороны, NA может быть приведен к различным типам, таким как NA_interger, NA_character и т. Д. Однако, если мы создадим список (поскольку списки могут хранить вместе разные типы), оба из них могут быть включены как элемент.

> w <- list(1,2,3,NULL)
> w
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

[[4]]
NULL

3) Подустановка с использованием значений с плавающей запятой и повторного использования логических векторов

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

> w <- c(1,2,3,4,5)
> w[3.5]
[1] 3
> w[-4.9]
[1] 1 2 3 5

Мы можем ясно видеть, что плавающая часть индекса игнорируется при подустановке независимо от знака. Итак, 3.5 дает нам третий элемент вектора, а -4.9 игнорирует четвертый элемент. Еще одна вещь, с которой следует проявлять осторожность при использовании логических векторов для подустановки, - это длина вектора. Если вектор подустановки имеет меньшую длину, вместо того, чтобы выдавать ошибку, R перерабатывает меньший вектор, чтобы сделать его такой же длины.

> w <- c(1,2,3,4,5)
> x <- c(T,F)
> x
[1]  TRUE FALSE
> w[x]
[1] 1 3 5

Мы можем видеть, что вектор x имеет длину 2, а вектор w имеет длину 5. Однако, когда мы устанавливаем подмножество с использованием x, он перерабатывается в c (T, F, T, F, T) и мы получаем все альтернативные значения в w.

4) Сохранение типов против упрощения в списках

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

> a <- list(1,2,3,c(4,5,6))
# a has 1,2,3 and c(4,5,6) as its elements
> a[1]
[[1]]
[1] 1
#Element one as list:Type preservation
> a[4]
[[1]]
[1] 4 5 6
#Element 4 as list:Type preservation
> a[[4]]
[1] 4 5 6
#Element 4 as vector, type simplification
> a[[4]][1]
[1] 4
#First element of 4th element of a, type simplification
> a[4][1]
[[1]]
[1] 4 5 6
#[] Outputs the whole vector as we did not simplify the data type to vector from list.

Из приведенного выше примера мы видим, что [] помогает в сохранении T типа, и вывод также представляет собой список, аналогичный исходному списку. С другой стороны, [[]] выполняет упрощение типов и дает нам простейший возможный тип базовых данных. Кроме того, [[]] важен, когда мы пытаемся получить доступ к элементу вектора, который содержится в списке, как показано в последней строке кода.

5) Доступ к столбцам в матрице и использование drop

Матричные операции играют большую роль при реализации множества моделей машинного обучения для векторизации и скорости. Нам нужно много времени на подмножество матрицы, чтобы получить доступ к определенному столбцу из матрицы и выполнить определенные операции. Однако, когда вы пытаетесь извлечь один столбец из матрицы, происходит странная вещь, как показано ниже:

> a <- matrix(c(1:9),ncol=3,nrow=3)
> a
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> a[,1]
[1] 1 2 3
> class(a[,1])
[1] "integer"
#When you extract a single column, it is converted to a vector
> a[,1,drop=F]
     [,1]
[1,]    1
[2,]    2
[3,]    3
> class(a[,1,drop=F])
[1] "matrix"
# drop=F helps us retain the matrix form

Когда мы пытаемся получить доступ к одному столбцу, R переводит его в векторную форму. Иногда это может быть нежелательно, так как это преобразование может повлиять на ваш нижележащий код (выучился на собственном горьком опыте при реализации алгоритма k-means с нуля в R). Чтобы избежать этого, мы должны использовать drop=F в качестве аргумента при доступе к одному столбцу.

6) Доступ к элементам вне привязки в векторах и списках

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

> a <- c(1:5)
> a
[1] 1 2 3 4 5
# a has 5 elements as shown above
> a[7]
[1] NA
#When we try to access 7th element, we get NA instead of an error
> b <- list(c(1,2,3,4))
> b
[[1]]
[1] 1 2 3 4
#b has 1 element which is a vector of 4 elements
> b[2]
[[1]]
NULL
#When we try to access 2nd element, we get NULL instead of an error

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

7) if {} else {} vs ifelse ()

Когда у нас есть вектор и мы хотим проверить данное условие, if {} else{} работает только с первым элементом вектора и выдает предупреждение. Однако, если мы хотим, чтобы условие было доступно для каждого элемента в векторе, мы должны использовать ifelse(). Он сравнивает каждое значение в векторе и возвращает вектор. Предположим, у нас есть вектор из 5 элементов, и мы хотим проверить, четные они или нечетные.

> a <- c(5:10)
> a
[1]  5  6  7  8  9 10
> if(a%%2==0){x <- "Even"} else{x <- "Odd"}
Warning message:
In if (a%%2 == 0) { :
  the condition has length > 1 and only the first element will be used
> x
[1] "Odd"
> y <- ifelse(a%%2,"Even","Odd")
> y
[1] "Even" "Odd"  "Even" "Odd"  "Even" "Odd"

Мы видим, что if{} else{} сравнивает только первый элемент a, а ifelse() дает требуемый результат.

8) Вызов функций с недостаточным количеством аргументов

R позволяет вызывать функцию с недостаточным количеством аргументов, пока отсутствующий аргумент не вызывается. Это очень важный момент, о котором следует помнить, и он отличается от других языков программирования. Если мы явно хотим, чтобы все аргументы присутствовали в вызове функции, мы можем использовать force() среди различных других опций.

> f <- function(x,y){
           print(x)}
> f(2)
[1] 2
#Function call works with one argument as y is not used.
> f(2,3)
[1] 2
#Calling with both arguments
> f()
Error in print(x) : argument "x" is missing, with no default
#Since x is called inside function and is missing, we get error
#Explicitly checking for both x and y using force()
> f <- function(x,y){force(x,y); print(x)}
> f(2,3)
[1] 2
> f(2)
Error in force(y) : argument "y" is missing, with no default

force() проверяет, присутствуют ли оба x и y, и выдает ошибку при отсутствии.

9) Функциональная маскировка и использование:

Часто разные пакеты имеют функции с одинаковыми именами, но разную функциональность. Если мы хотим использовать определенную функцию из определенного пакета, нам может потребоваться ее специально указать. При отсутствии конкретного вызова функция из недавно загруженного пакета маскирует все другие функции с такими же именами. Например, библиотеки chron и tseries имеют функцию is.weekend().

library(chron)
library(tseries)
    ‘tseries’ version: 0.10-45

    ‘tseries’ is a package for time series analysis and computational finance.

    See ‘library(help="tseries")’ for details.


Attaching package: ‘tseries’

The following object is masked from ‘package:chron’:

    is.weekend
is.weekend(x)

Когда мы вызываем is.weekend(), используется функция из пакета tseries, поскольку это последний загруженный пакет. Если мы специально хотим использовать chron, нам нужно сделать следующее:

chron::is.weekend(x)

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

10) Сопоставление аргументов при вызове функции

При вызове функции аргументы сопоставляются в R в следующем порядке: a) точное совпадение имени
b) частичное совпадение имени
c) позиционное совпадение

> f <- function(a1,b1,a2){
    print(paste("a1:",a1,"b1:",b1,"c1:",a2))}
> f(a1=2,b1=3,a2=5)
[1] "a1: 2 b1: 3 c1: 5"
#Example of Exact match, each argument has same name

> f(b1=3,5,2)
[1] "a1: 5 b1: 3 c1: 2"
#Example of exact match and positional match. Since b1 is matched, 5 and 2 are assigned based on position
> f(3,b=5,2)
[1] "a1: 3 b1: 5 c1: 2"
#Partial name match, b matches to b1. Rest matched based on position

> f(3,5,2)
[1] "a1: 3 b1: 5 c1: 2"
> f(3,5,a=2)
Error in f(3, 5, a = 2) : argument 3 matches multiple formal arguments
#Since a matches to both a1 and a2 we get error as R does not know where the value needs to be assigned.

Конечные примечания:

R открывает для нас мир огромных возможностей, а его процветающее сообщество и экосистема делают его отличным языком программирования для изучения. Я надеюсь, что приведенные выше моменты станут полезными для всех, кто работает с R, и помогут им избежать множества распространенных ошибок. Это сообщение в блоге - моя попытка выделить несколько моментов из курса Исследовательского анализа данных в R, который преподает Пол Интревадо в Университете Сан-Франциско . Свяжитесь со мной, чтобы обсудить / отредактировать любой конкретный момент.

Обо мне: аспирант, магистр наук о данных, Университет Сан-Франциско.

LinkedIn: https://www.linkedin.com/in/jyoti-prakash-maheswari-940ab766/
GitHub: https: // github .com / jyotipmahes

использованная литература

  1. Исследовательский анализ данных с помощью R Пол Интервадо