Удалить определенный столбец, если все значения NA в R

#r #tidyverse

#r #tidyverse

Вопрос:

У меня есть запрос к базе данных, выполняемый из R, в котором может быть несколько столбцов, заполненных NA, некоторые из этих столбцов мне нужны дальше по строке, но один конкретный столбец может быть удален, если все значения NA.

Я обычно использую purrr::discard(~все (is.na (.))) чтобы удалить столбцы, которые все являются NA, но поскольку этот фрейм данных может содержать несколько столбцов с NA, где я хочу удалить только один, я изо всех сил пытаюсь сделать это специфичным для столбца в решении tidyverse.

В настоящее время у меня есть этот обходной путь:

   if(sum(is.na(Orders$Originator)) == nrow(Orders)) {
    
    Orders <- Orders %>%
      select(-Originator)
    
  }
 

Но это улучшило бы читаемость, если бы я мог использовать это в решении tidyverse. Надеюсь, кто-нибудь может помочь!

Спасибо!

Ответ №1:

Канонический способ tidyverse решить эту проблему — использовать функцию предиката, используемую внутри select(where(...)) , и объединить это с выбором по имени переменной.

Сначала мы могли бы написать пользовательскую функцию предиката для использования, в where которой выбираются только столбцы, содержащие только NA s.

 # custom predicate function
all_na <- function(x) {
  all(is.na(x))
}
 

Мы можем использовать эту функцию вместе с логическим выражением, говорящим, что мы не хотим select y , если (читать И amp; ) это all_na :

 library(dplyr)

df <- data.frame(
  x = c(1,2,NA),
  y = NA,
  z = c(3,4,5)
)

df %>% 
  select(!(y amp; where(all_na)))
#>    x z
#> 1  1 3
#> 2  2 4
#> 3 NA 5
 

Чтобы проверить, действительно ли это работает, давайте переопределим y , чтобы он содержал не только NA s, и мы увидим, что на этот раз он не отменен:

 df2 <- data.frame(
  x = c(1,2,NA),
  y = c(1,2,NA),
  z = c(3,4,5)
)

df2 %>% 
  select(!(y amp; where(all_na)))
#>    x  y z
#> 1  1  1 3
#> 2  2  2 4
#> 3 NA NA 5
 

Вместо пользовательской функции мы можем использовать лямбда-функцию внутри where :

 df %>% 
  select(!(y amp; where(~ all(is.na(.x)))))
 

Создано 2021-12-07 пакетом reprex (версия 0.3.0)


В большем tidyverse мы могли бы также использовать purrr::lmap_at и выбрать y с .at аргументом, а затем создать лямбда-функцию, в которой говорится, что если all(is.na(.x)) затем использовать пустой list() (= удалить столбец), в противном случае сохранить столбец .x :

 library(purrr)
library(dplyr)

df %>% 
  lmap_at("y", ~ if(all(is.na(.x))) list() else .x)
#> # A tibble: 3 x 2
#>       x     z
#>   <dbl> <dbl>
#> 1     1     3
#> 2     2     4
#> 3    NA     5
 

Создано 2021-12-07 пакетом reprex (v2.0.1)

Ответ №2:

Использование данных примера:

 df <- data.frame(
  x = c(1,2,NA),
  y = NA,
  z = c(3,4,5)
)
 

Здесь столбец y является целевым столбцом для проверки all is.na . Ваш if и else будет содержаться в фигурных скобках. Фигурные скобки не позволят каналу использовать первый аргумент в функции. Обратите else внимание, что ваш фрейм данных будет сохранен в канале, если условие равно false.

 library(tidyverse)

df %>%
  { if (all(is.na(.$y))) select(., -y) else . }
 

Вывод

    x z
1  1 3
2  2 4
3 NA 5
 

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

1. Не знал, что мы можем интегрировать подобные функции. Работает как шарм! Большое спасибо!

Ответ №3:

Кажется, вы пытаетесь смешать выделение по имени (т. Е. Только По Определенному столбцу) и по логическому. Оба могут быть выполнены очень хорошо отдельно в tidyverse (используйте аккуратные селекторы или where ), но я не уверен, как их объединить!

Итак, вот грязное решение, которое не использует ни:

 library(dplyr, warn.conflicts = FALSE)
df <- data.frame(
  x = c(1,2,NA),
  y = NA,
  y2 = NA,
  z = c(3,4,5)
)

df %>% 
  select(-which(colnames(.)=="y" amp; sapply(., (x) all(is.na(x)))))
#>    x y2 z
#> 1  1 NA 3
#> 2  2 NA 4
#> 3 NA NA 5
 

Создано 2021-12-07 пакетом reprex (v2.0.1)

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

1. Проблема с этим решением заключается в том, что если вы добавите столбец, например a со всеми значениями NA, который также будет удален. У меня есть еще столбцы, которые могут содержать полные NA, но я хочу удалить только один.

2. хорошо, я обновил его.