Применение функций, хранящихся в фрейме данных, к другому фрейму данных в R - PullRequest
0 голосов
/ 15 октября 2019

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

  • необработанные данные (чьи позиции столбцов могут меняться, поэтому мы полагаемся на заголовки столбцов)
  • фрейм данных с заголовками столбцов и соответствующей применяемой функцией
### The raw data set

df1 <- tibble(A=c(NA, 1, 2, 3), B = c(1,2,1,NA), 
C = c(NA,NA,NA,2), D = c(2,3,NA,1), E = c(NA,NA,NA,1))

# A tibble: 4 x 5
      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1    NA     1    NA     2    NA
2     1     2    NA     3    NA
3     2     1    NA    NA    NA
4     3    NA     2     1     1

### The dataframe containing functions

funcDf <- tibble(colNames = names(df1), type = c(rep("Compulsory", 4), "Conditional"))
funcDf$func <- c("is.na()", "is.na()", "is.na()", "is.na()", 
"ifelse(!is.na(D) & is.na(E), 0, ifelse(!is.na(D) & !is.na(E), 1, 0))")

# A tibble: 5 x 3
  colNames type        func                                                             
  <chr>    <chr>       <chr>                                                            
1 A        Compulsory  is.na()                                                          
2 B        Compulsory  is.na()                                                          
3 C        Compulsory  is.na()                                                          
4 D        Compulsory  is.na()                                                          
5 E        Conditional ifelse(!is.na(D) & is.na(E), 0, ifelse(!is.na(D) & !is.na(E), 1,~


Я могу запустить простую сумму, например:

df1 %>% summarise_at(.vars = funcDf$colNames, .funs = list(~sum(., na.rm = T)))

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

Любые указания, пожалуйста:)

Редактировать

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

# A tibble: 1 x 5
      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     1     3     1     2

@ YinYan , большое спасибо за потворство мне, но замой комментарий, что если мне понадобится следующий вывод (с группировкой, как вы можете видеть в моем коде):

df1 %>% group_by(A, B) %>% summarise_all(.funs = list(~sum(., na.rm = T)))

# A tibble: 4 x 5
# Groups:   A [4]
      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     2     0     3     0
2     2     1     0     0     0
3     3    NA     2     1     1
4    NA     1     0     2     0

1 Ответ

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

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

funcDf$func <- c(
    function(x) is.na(x),
    function(x) is.na(x),
    function(x) is.na(x),
    function(x) is.na(x),
    function(x) with(data = df1, data.frame(E = ifelse(!is.na(D) & is.na(E), 0, ifelse(!is.na(D) & !is.na(E), 1, 0))))
)

result <- map_dfc(funcDf$colNames,function(colName){
    colFunc <- dplyr::pull(funcDf[funcDf$colNames == colName,"func"])[[1]]
    data.frame(colFunc(df1[,colName]))
})
> result
      A     B     C     D E
1  TRUE FALSE  TRUE FALSE 0
2 FALSE FALSE  TRUE FALSE 0
3 FALSE FALSE  TRUE  TRUE 0
4 FALSE  TRUE FALSE FALSE 1

Чтобы получить окончательный результат:

> summarise_all(result,sum)
  A B C D E
1 1 1 3 1 1

Ответ основан на новом вопросе

Мне нужно изменить столбец функции, поскольку функция времени этого столбца E зависит от другого фрейма данных. После использования group_split() разбить исходный фрейм данных на список фреймов данных. Затем вы можете использовать цикл или функцию map для итерации процесса. Мне лично нравится использовать функции map, так как коды более лаконичны.

funcDf$func <- c(
    function(x,...) is.na(x),
    function(x,...) is.na(x),
    function(x,...) is.na(x),
    function(x,...) is.na(x),
    function(x,df) with(data = df, data.frame(E = ifelse(!is.na(D) & is.na(E), 0, ifelse(!is.na(D) & !is.na(E), 1, 0))))
)
df_list <- df1 %>% group_by(A, B) %>% group_split()
map_dfr(df_list, function(parent_df){
    map_dfc(funcDf$colNames,function(colName){
        colFunc <- dplyr::pull(funcDf[funcDf$colNames == colName,"func"])[[1]]
        data.frame(colFunc(parent_df[,colName],df = parent_df))
    }) %>%
        summarise_all(sum)
})
  A B C D E
1 0 0 1 0 0
2 0 0 1 1 0
3 0 1 0 0 1
4 1 0 1 0 0
...