Как применить одну и ту же функцию к нескольким переменным в R? - PullRequest
2 голосов
/ 30 октября 2019

Я знаю, что подобные вопросы уже задавались (например, Передача имен элементов списка в качестве переменных функциям внутри lapply или R - итеративно применять функцию списка переменных ), но мне не удалось найти решение для моей проблемы на основе этих сообщений.

У меня есть набор данных события (~ 100 переменных,> 2000 наблюдений), который содержит переменные с информацией об задействованных субъектах. Одна переменная может содержать только одного актера, поэтому, если в событии участвуют несколько акторов, они распределены по нескольким переменным (например, actor1 , actor2 , ...). Этих актеров можно разделить на две группы («с» и «без»). Для последующего использования, Мне нужны два списка актеров : один содержит все акторы категории «s», а второй содержит все акторы «nons». «s» состоит только из трех действующих лиц, а «не» состоит из десятков действующих лиц.

# create example data
df <- data.frame(id = c(1:8),
                 actor1 = c("A", "B", "D", "E", "F", "G", "H", NA),
                 actor2 = c("A", NA, "B", "C", "E", "I", "D", "G"))

df <-  
  df %>%
  mutate(actor1 = as.character(actor1),
         actor2 = as.character(actor2))

Поскольку сценарий, который я собираюсь подготовить, предполагается использовать в обновленных версиях набора данных в будущем,Я хотел бы максимально автоматизировать и сохранить как можно более ограниченные части скрипта, которые необходимо адаптировать. Моя идея состояла в том, чтобы создать одну функцию для каждой категории, которая извлекает акторов соответствующей категории (например, «nons») из одной переменной (например, actor1 ) в списке, а затем «зацикливает» эту функцию над другими переменными. (в идеале для семейства apply ).

Я знаю, к какой категории относится каждый актер («A», «B» и «C» - это категория «s»), что позволяетЧтобы определить правило разделения, используемое в приведенной ниже функции (команда filter ).

# create function
nons_function <- function(col) {
  col_ <- enquo(col)
  nons_list <-
    df %>%
    filter(!is.na(!!col_), !!col_ != "A", !!col_ != "B", !!col_ != "C") %>%
    distinct(!!col_) %>%
    pull()
  nons_list
}

# create list of variables to "loop" over
actorlist <- c("actor1", "actor2")

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

> lapply(actorlist, nons_function)
[[1]]
[1] "actor1"

[[2]]
[1] "actor2"

Я хотел бы получить что-то вроде следующего:

> lapply(actorlist, nons_function)
[[1]]
[1] "D" "E" "F" "G" "H"

[[2]]
[1] "E" "I" "D" "G"

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

Любая помощь приветствуется!

РЕДАКТИРОВАТЬ: Первоначально я назвал актеров вводящим в заблуждение образом (имена актеров указывали, к какой категории принадлежит актер), что приводит к ответам, которые на самом деле не помогают в моем случае. Я изменил имена актеров с «s1», «s2», «nons1», «nons2» и т. Д. На «A», «B», «C» и т. Д.

Ответы [ 3 ]

1 голос
/ 30 октября 2019

здесь есть опция с использованием базы r.

для неакторов:

lapply( df[, 2:3], function(x) grep( "^nons", x, value = TRUE ) )

#$actor1
#[1] "nons1" "nons2" "nons3" "nons4" "nons5"
#
#$actor2
#[1] "nons2" "nons6" "nons1" "nons4"

и для s-актеров:

lapply( df[, 2:3], function(x) grep( "^s", x, value = TRUE ) )

# $actor1
# [1] "s1" "s2"
# 
# $actor2
# [1] "s1" "s2" "s3"
0 голосов
/ 30 октября 2019

Проверьте мое решение и посмотрите, работает ли оно для вас.

require("dplyr")


# create example data
df <- data.frame(id = c(1:8),
                 actor1 = c("s1", "s2", "nons1", "nons2", "nons3", "nons4", "nons5", NA),
                 actor2 = c("s1", NA, "s2", "s3", "nons2", "nons6", "nons1", "nons4"))

df <-  
  df %>%
  mutate(actor1 = as.character(actor1),
         actor2 = as.character(actor2))


# Function for getting the category
category_function <- function(col,categ){

  if(categ == "non"){
    outp = grep("^non",col,value = T)
  }else{
    outp = grep("^s",col,value = T)
  }

  return(outp)  

}

# Apply the function to all variables whose name starts with "actor"
sapply(df[grep("actor",names(df),value=T)],category_function,categ="non")
sapply(df[grep("actor",names(df),value=T)],category_function,categ="s")

Мой вывод был следующим:

> sapply(df[grep("actor",names(df),value=T)],category_function,categ="non")
$actor1
[1] "nons1" "nons2" "nons3" "nons4" "nons5"

$actor2
[1] "nons2" "nons6" "nons1" "nons4"

> sapply(df[grep("actor",names(df),value=T)],category_function,categ="s")
$actor1
[1] "s1" "s2"

$actor2
[1] "s1" "s2" "s3"
0 голосов
/ 30 октября 2019

Вот опция

library(dplyr)
library(stringr)
library(purrr)
map(actorlist, ~ df %>% 
                  select(.x) %>%
                  filter(!str_detect(!! rlang::sym(.x), "^s\\d+$")) %>% 
                   pull(1))
#[[1]]
#[1] "nons1" "nons2" "nons3" "nons4" "nons5"

#[[2]]
#[1] "nons2" "nons6" "nons1" "nons4"

Она также может быть включена в качестве функции. Обратите внимание, что ввод является строкой, поэтому вместо enquo используйте sym для преобразования в символ и затем оцените (!!)

f1 <- function(dat, colNm) {
                dat %>%
                  select(colNm) %>%
                   filter(!str_detect(!! rlang::sym(colNm), "^s\\d+$")) %>%
                    pull(1) %>%
                    unique
         }

map(actorlist, f1, dat = df)

ПРИМЕЧАНИЕ. Это можно сделать проще, но здесьмы используем аналогичный код из поста ОП


Другой вариант - использовать split с grepl в base R, и это возвращает list как 'nons', так и 's'после удаления NA s

lapply(df[2:3], function(x)  {
           x1 <- x[!is.na(x)]
            split(x1, grepl("nons", x1))})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...