Правильное построение функции в R

Я пишу свою первую функцию когда-либо (включая любой другой язык программирования), и я немного запутался в правильной структуре для if, else и ifelse. Я искал массу примеров, но мне все это не ясно.

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

#Sample Data
clientID <- round(runif(2,min=2000, max=3000),0)
MonthsSinceSignUp <- round(runif(20,min=1, max=60),0)
df <- data.frame(cbind(clientID,MonthsSinceSignUp))

Для данного клиента я хотел бы определить, были ли они так меньше года, больше года, но менее 2 и т.д.

Это моя первая трещина в функции:

ClientAgeRange <- function(MonthsSinceSignUp) {
  if (MonthsSinceSignUp < 13) {ClientAgeRange <- '1 year'}
} else {
  if (MonthsSinceSignUp > 13 & MonthsSinceSignUps < 25) {ClientAgeRange <- '2 years'}
} else {ClientAgeRage <- '3+ years'}

Ошибка, которую я продолжаю получать: Error: unexpected '}' in "}", что указывает на то, что я отсутствует или у вас есть дополнительная закрывающая скобка. Однако, несмотря на мою проблему, я не могу ее найти. Но - я думаю, в общем, я не применяю правильную структуру к функции. Я пытаюсь создать if this, then set this variable as that. Как я могу правильно структурировать эту функцию?

Наконец - если бы я хотел добавить вывод функции в dataframe, это apply правильный способ сделать это?

Ответ 1

Ответ в двух частях:

  • Совет
  • Исправить

Совет:

Мой первый совет - использовать редактор кода, который выполняет сопоставление скобок. Например, в Notepad++ вы получите следующее:

PS. Я не рекомендую Notepad++ - вместо этого использовать Rstudio - я просто использую Notepad++ из-за ярких (и, следовательно, легко различимых) цветов

enter image description here

Обратите внимание, что выделенная фигурная скобка (в красном) соответствует скобке в середине вашей функции. Это показывает, что в конце вашего первого if есть избыточная скобка. Итак, сначала исправьте это:

enter image description here

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

enter image description here


Исправление:

Но вы можете значительно упростить свою функцию, если используете cut, который предназначен для этого типа анализа:

ClientAgeRange <- function(x) {
  cut(x, breaks=c(0, 13, 25, Inf), labels=c("1 year", "2 years", "3+ years"))
}

Попробуйте в своем коде:

ClientAgeRange(df$MonthsSinceSignUp)
 [1] 2 years  1 year   3+ years 2 years  3+ years 3+ years 2 years  2 years  3+ years 3+ years 1 year  
[12] 3+ years 2 years  3+ years 3+ years 3+ years 3+ years 3+ years 3+ years 3+ years
Levels: 1 year 2 years 3+ years

Ответ 2

if (MonthsSinceSignUp < 13) {ClientAgeRange <- '1 year'}
}

У вас есть дополнительный} здесь.

Как правило, рекомендуется принять соглашения для форматирования вашего кода. Одна из конвенций, которую я очень рекомендую, всегда ставит тело "блока" (здесь я использую блок как общий термин для "stuff inside {}", который включает тела функций, если инструкции и циклы) на своей собственной строке, как показано ниже:

ClientAgeRange <- function(MonthsSinceSignUp) {
  if (MonthsSinceSignUp < 13) {
    ClientAgeRange <- '1 year'
  } else if (MonthsSinceSignUp > 13 & MonthsSinceSignUps < 25) {
    ClientAgeRange <- '2 years'
  } else {
    ClientAgeRage <- '3+ years'
  }
}

Посмотрите, как все это становится понятным?


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

Попробуйте следующее:

ClientAgeRange <- function(MonthsSinceSignUp) {
  if (MonthsSinceSignUp < 13) {
    result <- '1 year'
  } else if (MonthsSinceSignUp > 13 & MonthsSinceSignUps < 25) {
    result <- '2 years'
  } else {
    result <- '3+ years'
  }
  return(result)
}

return( является необязательным в R, но это поможет вам более четко продумать функции.

Ответ 3

Попробуйте следующее (обратите внимание, что я использовал else if, чтобы упростить его):

ClientAgeRange <- function(MonthsSinceSignUp) {
  if (MonthsSinceSignUp < 13) {
      ClientAgeRange <- '1 year'
  } else if (MonthsSinceSignUp > 13 & MonthsSinceSignUp < 25) {
      ClientAgeRange <- '2 years'
  } else {ClientAgeRage <- '3+ years'}
} 

Затем вы можете добавить его в свой файл данных следующим образом:

df$ClientAgeRange <- sapply(MonthsSinceSignUp, ClientAgeRange)

Как вы сказали, примените (я использовал sapply в этом случае, есть несколько мест, которые вы можете прочитать, чтобы прочитать о разных прикладных функциях, например здесь) это правильный путь. Это связано с тем, что мы не можем просто передать весь вектор в функцию; он требует, чтобы отдельные элементы выполняли необходимые сравнения.