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

⚡ Умно обобщение

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

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

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

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

A функция, в среда за програмиране, е набор от инструкции. Програмистът изгражда функция, която да избягва повтаряне на една и съща задачаили намалете сложност.

Една функция трябва да бъде

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

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

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')

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

Оригинални кумулативни серии до диференцирани стационарни серии

функция length().

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

Забележка: length() връща броя на елементите във вектор. Ако функцията се предаде в матрица или рамка от данни, се връща броят на колоните.

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

Изход:

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

Изход:

## [1] 50

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

Освен общите помощни функции, R разполага с масив от математически функции, които работят елемент по елемент върху вектор.

OperaTor Descriptйон
абс (x) Взема абсолютната стойност на x
log(x,основа=y) Взема логаритъм от x с основа y; ако основата не е посочена, връща натурален логаритъм
опит (x) Връща експоненциала на x
sqrt (x) Връща корен квадратен от x
факториел(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 съдържа широк набор от статистически функции. В този урок ще разгледаме накратко най-важните от тях.

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

OperaTor Descriptйон
средно (x) Средно на x
медиана (x) Медиана на x
променлива (x) Дисперсия на x
sd(x) Стандартно отклонение на x
мащаб (x) Стандартни резултати (z-резултати) на x
квантил(x) Квартилите на 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

На конзолата можем да видим съобщение за грешка :Error: object 'square_function' not found, което казва, че функцията не съществува.

Околна среда Шотландия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 <- 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))

Знаменател: max(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) За да върнем стойност към извикващата функция, трябва да предадем normalize вътре 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 реда от набора от данни. Например, можем да видим, че разделянето на нашата променлива събира стойностите 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. Това се прави с помощта на – пред split.

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)		
  }

Това е, можем да напишем функцията. Трябва само да променим airquality на 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

Въпроси и Отговори

Присвойте стойност в дефиницията, например 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 Desktop 2023.09.0 и по-нови версии предлагат опция за включване Копилот на GitHub интеграция. Той допълва телата на функциите от коментар, но преглежда всяко предложение, тъй като не може да вижда вашите данни.

Обобщете тази публикация с: