Как заставить код в моей функции возвращать отредактированные фреймы данных

#r #function #if-statement

#r #функция #if-оператор

Вопрос:

Я пытаюсь использовать инструкции ifelse в R, чтобы добавить определенный префикс к моим фреймам данных.

На данный момент у меня две проблемы с моим кодом.

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

2) параметр no = инструкции ifelse, которую я ввел, повторяется, как мне заставить это повторяться только один раз?

Был бы признателен за любую помощь.

примечание. В этом примере я использую некоторые выдуманные данные по профессиональным причинам.


dput(head(Player1)):

 structure(list(Class = structure(c(2L, 1L, 5L, 4L, 3L), .Label = c("fighter", 
"paladin", "rouge", "sorceror", "wizard"), class = "factor"), 
Race = structure(c(3L, 1L, 4L, 3L, 2L), .Label = c("elf", 
"gnome", "human", "orc"), class = "factor"), alignment = structure(c(4L, 
2L, 1L, 5L, 3L), .Label = c("CE", "CG", "LG", "NE", "NN"), class = "factor"), 
Level = c(6, 7, 1, 2, 4)), row.names = c(NA, 5L), class = "data.frame")
  

dput(head(Player2)):

 structure(list(Class = structure(c(2L, 1L, 5L, 4L, 3L), .Label = c("fighter", 
"paladin", "rouge", "sorceror", "wizard"), class = "factor"), 
Race = structure(c(3L, 1L, 4L, 3L, 2L), .Label = c("elf", 
"gnome", "human", "orc"), class = "factor"), alignment = structure(c(4L, 
2L, 1L, 5L, 3L), .Label = c("CE", "CG", "LG", "NE", "NN"), class = "factor"), 
Level = c(6, 7, 1, 2, 4)), row.names = c(NA, 5L), class = "data.frame")
  

Допустим, у нас есть два игрока, Джон (игрок1) и Люси (игрок2), и мы хотим добавить префикс к их именам. Я добился этого, используя приведенный ниже код.

 ifelse(test = grepl('Johns', names(Player1)) == F, 
         yes = colnames(Player1) <- paste('Johns', colnames(Player1), sep = '_'),
         no = print('Player info is fine'))
  

Вывод здесь работает, и все столбцы получают ‘Johns_’ в качестве префикса
Однако, когда я пытаюсь преобразовать это в функцию для обоих проигрывателей фреймов данных, изменений не происходит.

Функция:

 Addnames <- function(Player1, Player2){
  ifelse(test = grepl('Johns', names(Player1)) == F, 
         yes = colnames(Player1) <- paste('Johns', colnames(Player1), sep = '_'),
         no = print('Player info is fine'))
  ifelse(test = grepl('Lucys', names(Player2)) == F, 
         yes = colnames(Player2) <- paste('Lucys', colnames(Player2), sep = '_'),
         no = print('Player info is fine'))
return(Player1)
return(Player2)
}

Addnames(Player1, Player2) 
  

Это не изменяет имена столбцов фреймов данных.


Мой идеальный результат — иметь ‘Johns_’ и ‘Lucys_’ в качестве префикса к имени каждого столбца для фреймов данных Player1 и Player2 соответственно.

Я хотел бы сделать это в функции.

Другая проблема, с которой я сталкиваюсь, заключается в инструкции ifelse, если no = ‘Информация об игроке в порядке’ повторяется для каждого имени столбца. Как мне заставить это повторяться только один раз.

Опять же, любая помощь была бы высоко оценена.

Комментарии:

1. Ваша первая проблема заключается в том, что функция не может изменять объекты в глобальной среде (вне функции), если вы либо специально не укажете ей это, либо не присвоите выходные данные функции переменной в глобальной среде. Ваша вторая проблема заключается в том, что первый return() вызов завершает работу функции, поэтому у вас не может быть двух отдельных return() вызовов в функции.

2. Я не уверен, но вы хотите изменить имена столбцов для обоих фреймов данных? Вам нужно names(Player1) <- paste0("Johns_", names(Player1)); names(Player2) <- paste0("Lucys_", names(Player2)) ?

3. Хммм … в вашем точном воспроизводимом сообщении действительно отображаются столбцы первого фрейма данных, которые изменены. Смотрите демонстрацию: rextester.com/DYV6690 . Просто отрегулируйте возврат для обоих фреймов данных: return(list(Player1, Player2) .

4. @Parfait но не то, чтобы это возвращало единый список с двумя элементами, один для Player1 и один для Player2. Вы не можете напрямую назначить этот результат двум фреймам данных. (Я уверен, вы это знаете, просто хотел предупредить операционную систему).

5. @iod … OP должен работать со списками фреймов данных, особенно если они аналогично структурированы! Избегайте затопления глобальной среды отдельными подобными объектами.

Ответ №1:

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

 Addnames <- function(player, namestring){
  ifelse(test = grepl(namestring, names(player)) == F, 
         yes = colnames(player) <- paste(namestring, colnames(player), sep = '_'),
         no = print('Player info is fine'))
  return(player)
}


Player1 <- Addnames(player=Player1, namestring="Johns")
Player2 <- Addnames(player=Player2, namestring="Lucys")
  

Редактировать: далее, предполагая, что «Lucys» на самом деле называется «Lucy», добавление «s», конечно, также может быть выполнено внутри функции:

 Addnames <- function(player, namestring){
  ifelse(test = grepl(namestring, names(player)) == F, 
         yes = colnames(player) <- paste(namestring, "s", colnames(player), sep = '_'),
         no = print('Player info is fine'))
  return(player)
}


Player1 <- Addnames(player=Player1, namestring="John")
Player2 <- Addnames(player=Player2, namestring="Lucy")
  

Ответ №2:

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

 Addnames <- function(player_df,player_name){
   column_names_to_change <- which(!grepl('Johns', names(player_df)))
   colnames(player_df)[column_names_to_change]  <- paste(player_name,colnames(player_df)[column_names_to_change],sep="_")
   return(player_df)
}
  

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

 Player1 <- Addnames(Player1,"Johns")
Player2 <- Addnames(Player2,"Lucys")
  

Ответ №3:

Просто используйте списки и run Map для поэлементного перебора между проигрывателями и соответствующими фреймами данных без какой-либо ifelse условной логики. Даже используйте setNames для возврата функции с правой стороны.

 player_list <- c("John", "Lucy")    

df_list <- list(Player1, Player2)

# RENAME COLUMNS ELEMENTWISE
new_df_list <- Map(function(nm, df) setNames(df, paste0(nm, "_", colnames(df))),
                   player_list, df_list)

# OUTPUT DF ELEMENTS
new_df_list$John
#   John_Class John_Race John_alignment John_Level
# 1    paladin     human             NE          6
# 2    fighter       elf             CG          7
# 3     wizard       orc             CE          1
# 4   sorceror     human             NN          2
# 5      rouge     gnome             LG          4

new_df_list$Lucy
#   Lucy_Class Lucy_Race Lucy_alignment Lucy_Level
# 1    paladin     human             NE          6
# 2    fighter       elf             CG          7
# 3     wizard       orc             CE          1
# 4   sorceror     human             NN          2
# 5      rouge     gnome             LG          4