Функции в программировании на R на примере

⚡ Умное резюме

Функции в R объединяют набор инструкций под одним именем, поэтому повторяющаяся задача выполняется с одного вызова. Встроенные вспомогательные функции для математики и статистики, определяемые пользователем функции, среда выполнения.pingЗдесь рассматриваются условные аргументы с подробными примерами.

  • 🔘 Функциональная анатомия: Функция объединяет имя, список необязательных аргументов, тело и необязательное возвращаемое значение.
  • 🧮 Встроенное покрытие: В R, помимо математических и статистических функций, есть и универсальные вспомогательные функции, такие как diff() и length().
  • 📐 Многократное масштабирование: Функция normalize() преобразует блок масштабирования, созданный путем копирования и вставки, в один многократно используемый вызов.
  • 🧭 Экологические правила: Каждый вызов открывает локальную среду, и R разрешает имя, начиная с самого внутреннего определения и двигаясь дальше.
  • 🧪 Условная логика: В функции split_data() оператор if/else возвращает либо обучающий набор данных, либо тестовый набор данных.
  • 🇧🇷 Правила именования: Иное наименование пользовательской функции по сравнению со встроенной позволяет избежать маскировки и путаницы при возникновении ошибок.

Функции в программировании на R

Что такое функция в R?

A функцияВ среде программирования — это набор инструкций. Программист создает функцию, чтобы избежать повторение одной и той же задачиили уменьшить сложность.

Функция должна быть

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

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

function (arglist)  {
  #Function body
}

R важные встроенные функции

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

Typing Имя функции без скобок выводит её исходный код, как показано в консоли ниже.

Вывод исходного кода встроенной функции в консоль R.

Мы увидим три группы функций в действии.

  • Общая функция
  • Математическая функция
  • Статистическая функция

Общие функции

Мы уже знакомы с общими функциями, такими как cbind(), rbind(), range(). Сортировать()и order(). Каждая из этих функций выполняет определенную задачу, принимает аргументы и возвращает результат. Ниже перечислены важные функции, которые необходимо знать:

функция diff()

Если ты работаешь над Временные ряды, вам нужно остановить серию, взяв их значения задержки, стационарный процесс Это позволяет сохранять постоянное среднее значение, дисперсию и автокорреляцию во времени. В основном это улучшает прогнозирование временного ряда. Это можно сделать с помощью функции diff(). Мы можем создать случайный временной ряд с трендом, а затем использовать функцию diff() для стационарности ряда. Функция diff() принимает один аргумент — вектор — и возвращает подходящую разницу с запаздыванием и итерациями.

ВниманиеНам часто нужно создавать случайные данные, но для обучения и сравнения мы хотим, чтобы числа были одинаковыми на всех машинах. Чтобы гарантировать, что все мы генерируем одинаковые данные, мы используем функцию set.seed() с произвольным значением 123. Функция set.seed() фиксирует начальную точку генератора псевдослучайных чисел, поэтому каждая машина выдает одну и ту же последовательность. Без set.seed() каждый запуск возвращает разную последовательность.

set.seed(123)
## Create the data
x = rnorm(1000)
ts <- cumsum(x)
## Stationary the serie
diff_ts <- diff(ts)
par(mfrow=c(1,2))
## Plot the series
plot(ts, type='l')
plot(diff(ts), type='l')

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

Исходный кумулятивный ряд рядом с дифференцированным стационарным рядом

функция длина()

Во многих случаях нам нужно знать длину вектора для вычислений или для использования в цикле for. Функция length() подсчитывает количество строк в векторе x. Приведенный ниже код импортирует набор данных cars и возвращает количество строк.

ВниманиеФункция `length()` возвращает количество элементов в векторе. Если функция передается в качестве аргумента... матрица или фрейм данных, возвращается количество столбцов.

dt <- cars
## number columns
length(dt)

Выход:

## [1] 1
## number rows
length(dt[,1])

Выход:

## [1] 50

Математические функции

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

оператор Описание
абс (х) Принимает абсолютное значение x
журнал (х, база = у) Логарифмирует x по основанию y; если основание не указано, возвращает натуральный логарифм
ехр (х) Возвращает экспоненту x
sqrt (х) Возвращает квадратный корень из x
факториал(х) Возвращает факториал x (x!)
# sequence of number from 44 to 55 both including incremented by 1
x_vector <- seq(45,55, by = 1)
#logarithm
log(x_vector)

Выход:

##  [1] 3.806662 3.828641 3.850148 3.871201 3.891820 3.912023 3.931826
##  [8] 3.951244 3.970292 3.988984 4.007333
#exponential
exp(x_vector)
#squared root
sqrt(x_vector)

Выход:

##  [1] 6.708204 6.782330 6.855655 6.928203 7.000000 7.071068 7.141428
##  [8] 7.211103 7.280110 7.348469 7.416198
#factorial
factorial(x_vector)

Выход:

##  [1] 1.196222e+56 5.502622e+57 2.586232e+59 1.241392e+61 6.082819e+62
##  [6] 3.041409e+64 1.551119e+66 8.065818e+67 4.274883e+69 2.308437e+71
## [11] 1.269640e+73

Статистические функции

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

Основные статистические функции

оператор Описание
среднее (х) Среднее значение х
медиана (х) Медиана x
вар (х) Дисперсия x
сд(х) Стандартное отклонение x
масштаб(х) Стандартные оценки (z-оценки) x
квантиль(х) Квартили x
резюме(х) Сводка по x: среднее, минимальное, максимальное и т. д.
speed <- dt$speed
speed
# Mean speed of cars dataset
mean(speed)

Выход:

## [1] 15.4
# Median speed of cars dataset
median(speed)

Выход:

## [1] 15
# Variance speed of cars dataset
var(speed)

Выход:

## [1] 27.95918
# Standard deviation speed of cars dataset
sd(speed)

Выход:

## [1] 5.287644
# Standardize vector speed of cars dataset		
head(scale(speed), 5)

Выход:

##           [,1]
## [1,] -2.155969
## [2,] -2.155969
## [3,] -1.588609
## [4,] -1.588609
## [5,] -1.399489
# Quantile speed of cars dataset
quantile(speed)

Выход:

##   0%  25%  50%  75% 100%
##    4   12   15   19   25
# Summary speed of cars dataset
summary(speed)

Выход:

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
##     4.0    12.0    15.0    15.4    19.0    25.0

К этому моменту мы изучили множество встроенных функций R.

ВниманиеБудьте осторожны с класс аргументаТо есть числовое значение, логическое значение или строка. Например, если нам нужно передать строковое значение, мы должны заключить строку в кавычки: «ABC».

Написать функцию в R

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

function.name <- function(arguments) 
{
    computations on the arguments	
    some other code
}		

Внимание: Хорошей практикой является присвоение определяемой пользователем функции имени, отличного от встроенной функции. Это позволяет избежать путаницы.

Функция с одним аргументом

В следующем фрагменте мы определяем простую квадратичную функцию. Функция принимает значение и возвращает квадрат значения.

square_function<- function(n) 
{
  # compute the square of integer `n`
  n^2
}  
# calling the function and passing value 4
square_function(4)

Code объяснение

  • Функция называется Square_function; его можно называть как угодно.
  • Он получает аргумент «n». Мы не указан тип переменной, чтобы пользователь мог передать целое число, вектор или матрицу
  • Функция принимает входные данные «n» и возвращает квадрат входных данных. Когда вы закончите использовать функцию, мы можем удалить ее с помощью функции rm().

# после создания функции

rm(square_function)
square_function

На консоли мы видим сообщение об ошибке: Ошибка: объект «square_function» не найден, сообщающий, что функция не существует.

Экологическая организацияping

В R охрана окружающей среды Это набор объектов, таких как функции, переменные, фреймы данных и т. д.

R каждый раз при запуске RStudio открывает новую среду.

Доступная среда верхнего уровня — это глобальная среда, называемый R_GlobalEnv. И у нас есть местная среда.

Мы можем перечислить содержимое текущей среды.

ls(environment())

Результат

## [1] "diff_ts"         "dt"              "speed"           "square_function"
## [5] "ts"              "x"               "x_vector"

Вы можете увидеть все переменные и функции, созданные в файле R_GlobalEnv.

Приведенный выше список может отличаться для вас в зависимости от исторического кода, который вы выполняете в RStudio.

Обратите внимание, что nАргумент функции square_function находится вне этой глобальной среды.

Для каждой функции создаётся новая среда. В приведённом выше примере функция square_function() создаёт новую среду внутри глобальная среда.

Чтобы прояснить разницу между Глобальный и локальным Рассмотрим следующий пример, касающийся окружающей среды.

Эта функция принимает значение x в качестве аргумента и добавляет его к определению y снаружи и внутри функции.

Функция f, использующая переменную y, определенную в глобальной среде.

Функция f возвращает результат 15. Это связано с тем, что y определен в глобальной среде. Любая переменная, определенная в глобальной среде, может использоваться локально. Переменная y имеет значение 10 во время всех вызовов функций и доступна в любое время.

Давайте посмотрим, что произойдет, если переменная y будет определена внутри функции.

Нам нужно удалить `y` перед запуском этого кода с помощью rm r

Функция f, определяющая y внутри своей локальной среды.

Выход также равен 15, когда мы вызываем f(5), но возвращает ошибку, когда мы пытаемся напечатать значение y. Переменная y отсутствует в глобальной среде.

Наконец, R использует самое последнее определение переменной для передачи внутри тела функции. Давайте рассмотрим следующий пример:

R, используя самое внутреннее определение y внутри тела функции.

R игнорирует значения y, определенные вне функции, поскольку мы явно создали переменную ay внутри тела функции.

Функция с несколькими аргументами

Мы можем написать функцию с более чем одним аргументом. Рассмотрим функцию под названием «times». Это простая функция умножения двух переменных.

times <- function(x,y) {
  x*y
	}
times(2,4)

Выход:

## [1] 8

Когда нам следует писать функцию?

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

Формула для нормализации переменной

Мы уже знаем, как использовать функции min() и max() в R. Мы используем библиотеку tibble для создания фрейма данных. Tibble на данный момент является самой удобной функцией для создания набора данных с нуля.

library(tibble)
# Create a data frame
data_frame <- tibble(  
  c1 = rnorm(50, 5, 1.5), 
  c2 = rnorm(50, 5, 1.5),    
  c3 = rnorm(50, 5, 1.5),    
)

Для вычисления описанной выше функции мы выполним действия в два этапа. На первом этапе мы создадим переменную с именем c1_norm, которая будет представлять собой масштабирование c1. На втором этапе мы просто скопируем и вставим код c1_norm и заменим им значения c2 и c3.

Деталь функции со столбцом c1:

Номинатор: : data_frame$c1 -min(data_frame$c1))

Знаменатель: макс(data_frame$c1)-min(data_frame$c1))

Следовательно, мы можем разделить их, чтобы получить нормализованное значение столбца c1:

(data_frame$c1 -min(data_frame$c1))/(max(data_frame$c1)-min(data_frame$c1))

Мы можем создать c1_norm, c2_norm и c3_norm:

Create c1_norm: rescaling of c1		
data_frame$c1_norm <- (data_frame$c1 -min(data_frame$c1))/(max(data_frame$c1)-min(data_frame$c1))
# show the first five values
head(data_frame$c1_norm, 5)

Выход:

## [1] 0.3400113 0.4198788 0.8524394 0.4925860 0.5067991

Оно работает. Мы можем скопировать и вставить

data_frame$c1_norm <- (data_frame$c1 -min(data_frame$c1))/(max(data_frame$c1)-min(data_frame$c1))

затем измените c1_norm на c2_norm и c1 на c2. То же самое делаем для создания c3_norm

data_frame$c2_norm <- (data_frame$c2 - min(data_frame$c2))/(max(data_frame$c2)-min(data_frame$c2))
data_frame$c3_norm <- (data_frame$c3 - min(data_frame$c3))/(max(data_frame$c3)-min(data_frame$c3))

Мы прекрасно изменили масштаб переменных c1, c2 и c3.

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

  • Название: normalize.
  • количество аргументов: нам нужен только один аргумент — столбец, который мы используем в наших вычислениях.
  • Тело: это просто формула, которую мы хотим вернуть.

Мы продолжим шаг за шагом создавать функцию нормализации.

Шаг 1) Мы создаём числитель, который равен x – min(x). В R мы можем сохранить числитель в переменной следующим образом:

nominator <- x-min(x)

Шаг 2) Вычисляем знаменатель: max(x) – min(x). Мы можем повторить идею шага 1 и сохранить результат вычисления в переменной:

denominator <- max(x)-min(x)

Шаг 3) Выполняем деление между числителем и знаменателем.

normalize <- nominator/denominator

Шаг 4) Чтобы вернуть значение вызывающей функции, нам нужно передать нормализацию внутри return(), чтобы получить выходные данные функции.

return(normalize)

Шаг 5) Мы готовы использовать функцию обертки.ping Всё, что находится внутри скобок.

normalize <- function(x){
  # step 1: create the nominator
  nominator <- x-min(x)
  # step 2: create the denominator
  denominator <- max(x)-min(x)
  # step 3: divide nominator by denominator
  normalize <- nominator/denominator
  # return the value
  return(normalize)
}

Давайте проверим нашу функцию с переменной c1:

normalize(data_frame$c1)

Он работает отлично. Мы создали нашу первую функцию.

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

data_frame$c1_norm_function <- normalize (data_frame$c1)
data_frame$c2_norm_function <- normalize	(data_frame$c2)
data_frame$c3_norm_function <- normalize	(data_frame$c3)

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

Функции с условием

Функция normalize() всегда делает одно и то же. Иногда нам нужно добавить в функцию условия, чтобы код мог возвращать разные результаты.

В задачах машинного обучения нам необходимо разделить набор данных между набором поездов и набором тестов. Набор поездов позволяет алгоритму учиться на данных. Чтобы проверить производительность нашей модели, мы можем использовать тестовый набор для возврата показателя производительности. В R нет функции для создания двух наборов данных. Для этого мы можем написать собственную функцию. Наша функция принимает два аргумента и называется Split_data(). Идея проста: мы умножаем длину набора данных (т. е. количество наблюдений) на 0.8. Например, если мы хотим разделить набор данных 80/20, а наш набор данных содержит 100 строк, тогда наша функция умножит 0.8 * 100 = 80. 80 строк будут выбраны в качестве наших обучающих данных.

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

nrow(airquality)

Выход:

## [1] 153

Мы будем действовать следующим образом:

split_data <- function(df, train = TRUE)
Arguments:
-df: Define the dataset
-train: Specify if the function returns the train set or test set. By default, set to TRUE

Наша функция имеет два аргумента. Поезд аргументов является логическим параметром. Если для него установлено значение TRUE, наша функция создает набор данных поезда, в противном случае она создает тестовый набор данных.

Мы можем поступить так же, как и с функцией normalize(). Мы пишем код так, как будто это одноразовый код, а затем заключаем все с условием в тело функции, чтобы создать саму функцию.

Шаг 1:

Нам нужно вычислить длину набора данных. Это делается с помощью функции nrow(). Nrow возвращает общее количество строк в наборе данных. Мы называем переменную длину.

length<- nrow(airquality)
length

Выход:

## [1] 153

Шаг 2:

Умножаем длину на 0.8. Он вернет количество строк для выбора. Должно быть 153*0.8 = 122.4.

total_row <- length*0.8
total_row

Выход:

## [1] 122.4

Мы хотим выбрать 122 строки из 153 строк набора данных о качестве воздуха. Мы создаем список, содержащий значения от 1 до total_row. Мы сохраняем результат в переменной с именем Split

split <- 1:total_row
split[1:5]

Выход:

## [1] 1 2 3 4 5

Split выбирает первые 122 строки из набора данных. Например, мы видим, что наша переменная Split собирает значения 1, 2, 3, 4, 5 и так далее. Эти значения будут индексом, когда мы будем выбирать строки для возврата.

Шаг 3:

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

train_df <- airquality[split, ] 
head(train_df)

Выход:

##[1]    Ozone Solar.R Wind Temp Month Day
##[2]  51    13     137 10.3   76     6  20
##[3]  15    18      65 13.2   58     5  15
##[4]  64    32     236  9.2   81     7   3
##[5]  27    NA      NA  8.0   57     5  27
##[6]  58    NA      47 10.3   73     6  27
##[7]  44    23     148  8.0   82     6  13

Шаг 4:

Мы можем создать тестовый набор данных, используя оставшиеся строки 123:153. Это делается с помощью – перед разделением.

test_df <- airquality[-split, ] 
head(test_df)

Выход:

##[1] Ozone Solar.R Wind Temp Month Day
##[2]  123    85     188  6.3   94     8  31
##[3]  124    96     167  6.9   91     9   1
##[4]  125    78     197  5.1   92     9   2
##[5]  126    73     183  2.8   93     9   3
##[6]  127    91     189  4.6   93     9   4
##[7]  128    47      95  7.4   87     9   5

Шаг 5:

Мы можем создать условие внутри тела функции. Помните, что у нас есть поезд аргументов, который по умолчанию имеет логическое значение TRUE, чтобы вернуть набор поездов. Чтобы создать условие, мы используем синтаксис if:

  if (train ==TRUE){ 
    train_df <- airquality[split, ] 
      return(train)		
  } else {
    test_df <- airquality[-split, ] 
      return(test)		
  }

Вот и все, мы можем написать функцию. Нам нужно изменить качество воздуха только на df, потому что мы хотим опробовать нашу функцию на любом фрейм данных, не только качество воздуха:

split_data <- function(df, train = TRUE){
  length<- nrow(df)
  total_row <- length *0.8
  split <- 1:total_row
  if (train ==TRUE){ 
    train_df <- df[split, ] 
      return(train_df)		
  } else {
    test_df <- df[-split, ] 
      return(test_df)		
  }
}

Давайте попробуем нашу функцию на наборе данных о качестве воздуха. У нас должен быть один обучающий набор из 122 строк и тестовый набор из 31 строки.

train <- split_data(airquality, train = TRUE)
dim(train)

Выход:

## [1] 122   6
test <- split_data(airquality, train = FALSE)
dim(test)

Выход:

## [1] 31  6

Часто задаваемые вопросы (FAQ)

Присвойте значение в определении, например, function(df, train = TRUE). Если вызывающая сторона не указывает этот аргумент, R использует значение по умолчанию. Значения по умолчанию вычисляются лениво, поэтому одно значение по умолчанию может даже ссылаться на другой аргумент той же функции.

Да. Функция может вызывать саму себя, пока стоит остановка.ping Условие завершает цепочку — например, факториал, возвращающий 1, когда n достигает 1. Глубокая рекурсия может достичь предела вложенности выражений, поэтому итерация часто безопаснее.

Нет. R автоматически возвращает значение последнего вычисленного выражения, поэтому функция square_function() работает без return(). Используйте return(), когда необходим досрочный выход или когда явный результат упрощает чтение функции.

R сначала выполняет поиск по точному имени, затем по его части, а затем по позиции для оставшегося значения. Явное именование аргументов, как в seq(45, 55, by = 1), обеспечивает читаемость вызова и предотвращает случайную перестановку.

Функция, созданная без присвоения ей имени, обычно передается напрямую в функции sapply() или lapply(). В R 4.1 добавлена ​​сокращенная форма с обратной косой чертой, поэтому \(x) x^2 выполняет ту же работу, что и function(x) x^2, но с гораздо меньшими затратами времени.ping.

Новое определение скрывает встроенные функции в этой среде, поэтому вызов функции length() может запустить вашу версию вместо базовой. Удалите копию с помощью функции rm() или выберите уникальное имя с самого начала.

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

Да. RStudio В настольных версиях 2023.09.0 и более поздних версиях доступна опция подключения по желанию. Второй пилот GitHub Интеграция. Она дополняет тела функций на основе комментария, но проверяйте каждое предложение, поскольку она не видит ваши данные.

Подведем итог этой публикации следующим образом: