Вложенный оператор ifelse с программным числом вложенных уровней

У меня есть матрица из 3 столбцов. Для каждой строки должно быть выбрано непропущенное значение, - если значение не найдено в столбце 1, будет выполняться поиск в столбце 2, затем в столбце 3, и порядок будет задан пользователем.

Я умеренно доволен своим запутанным вложенным ifelse подходом — увы, это зависит от одинаковой длины заданных столбцов. Но количество столбцов должно быть гибким (таким образом, гибкое количество вложенных операторов ifelse). Это означает, что если пользователь выбирает только один или два столбца, результат будет NA, даже если нежелательный столбец содержит значение.

foo_mat <- structure(c(
  NA, 30L, 15, 0, NA, 100L, 87L, NA, 0, NA, 2L, NA,
  10, 0, NA
), .Dim = c(5L, 3L), .Dimnames = list(NULL, c(
  "a", "b", "c"
)))

foo <- function(x, preced) {
    ifelse(!is.na(x[, preced[1]]), x[, preced[1]],
      ifelse(!is.na(x[, preced[2]]), x[, preced[2]],
        x[, preced[3]]
      )
    )
}

foo_mat
#>       a   b  c
#> [1,] NA 100  2
#> [2,] 30  87 NA
#> [3,] 15  NA 10
#> [4,]  0   0  0
#> [5,] NA  NA NA

foo(foo_mat, c("a", "c", "b"))
#> [1]  2 30 15  0 NA

foo(foo_mat, preced = c("b", "a"))
#> Error in x[, preced[3]]: subscript out of bounds #(of course)

# desired output
#> [1]  100 87 15 0 NA

Ваши образцы данных — хорошее начало, но отсутствует последняя часть. Я думаю, что нужно добавить "b", "c"))), верно?

r2evans 23.12.2020 22:28

мой плохой, спасибо, что увидел это ... но мое перетаскивание мышью-копирование / вставка не захватило его, поэтому в любом случае может быть полезно отредактировать его вместе. Спасибо, извините за шум.

r2evans 23.12.2020 22:31
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
92
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вместо вложенного ifelse может быть лучше создать функцию с coalesce

foo <- function(data, preced) {
        do.call(dplyr::coalesce, as.data.frame(data[, preced]))
       }


foo(foo_mat, c("a", "c", "b"))
#[1]  2 30 15  0 NA
foo(foo_mat, c("b", "a"))
#[1] 100  87  15   0  NA

coalesce автоматически выбирает первый не-NA для каждой строки на основе столбцов в выбранном наборе данных


Или мы можем использовать векторизованный вариант в base R с max.col

foo1 <- function(data, preced) {
         tmp <- data[, preced]
         i1 <- seq_len(nrow(tmp))
         j1 <- max.col(!is.na(tmp), "first")
         out <- tmp[cbind(i1, j1)]
         out
    }

foo1(foo_mat, c("a", "c", "b"))
#[1]  2 30 15  0 NA
foo1(foo_mat, c("b", "a"))
#[1] 100  87  15   0  NA
Ответ принят как подходящий

База Р:

apply(foo_mat[,c("a","c","b")], 1, function(z) c(na.omit(z), NA)[1])
# [1]  2 30 15  0 NA

Анон-функция представляет собой двухэтапный процесс:

  • во-первых, удалите все NA, чтобы мы могли получить первое значение, отличное от NA
  • во-вторых, вполне возможно, что na.omit(.) вернет integer(0), а это не то, что вам нужно, поэтому c(., NA)[1] гарантирует, что после na.omit(.) у нас всегда будет хотя бы одно значение в векторе c(.), и нам нужно первое из них; если na.omit ничего не возвращает, то, по крайней мере, у нас есть один NA.

Выполнение этого по рядам выполняется с помощью apply(foo_mat, 1, ...). Вы управляете порядком предпочтения, перестраивая столбцы, входящие в данные apply, как в моем использовании foo_mat[,c("a","c","b")].

Как функция:

foo <- function(data, preced = names(data)) apply(data[,preced,drop=FALSE], 1, function(z) c(na.omit(z), NA)[1])
foo(foo_mat, c("a", "c", "b"))
# [1]  2 30 15  0 NA

(drop=FALSE является защитным. База R по умолчанию поведение foo_mat[,"a"] представляет собой вектор, а не матрицу с 1 столбцом. Это нарушает многие вещи, в том числе apply. Таким образом, добавление drop=FALSE предотвращает поведение сокращения по умолчанию.)

Альтернатива, которая примерно такая же быстрая, как и другие ответы:

foo <- function(data, preced) apply(data[,preced,drop=FALSE], 1, function(z) z[!is.na(z)][1])

Та же функциональность, меньше вызовов, простая логика.

(Атрибуция: эта альтернатива представляет собой комбинацию работы @tmfmnk, @Tjebo и меня. Спасибо!)

К вашему сведению, ответ, предоставленный @RuiBarradas, дает те же результаты. Но на данных такого размера эта функция более чем в два раза быстрее.

r2evans 23.12.2020 22:38

Я думаю, что c(z[!is.na(z)], NA)[1] можно даже написать z[!is.na(z)][1], потому что integer(0)[1] это NA

tjebo 24.12.2020 00:10

Вот функция foo, которая работает так, как требуется с опубликованными примерами.

foo <- function(x, preced){
  apply(x[, preced], 1, function(y){
    w <- !is.na(y)
    if (any(w)) y[w][1] else NA
  })
}

foo(foo_mat, c("a", "c", "b"))
#[1]  2 30 15  0 NA
foo(foo_mat, preced = c("b", "a"))
#[1] 100  87  15   0  NA

Другие вопросы по теме