Проверьте несколько переменных (столбцов) для значения в каждой строке с помощью dplyr - PullRequest
1 голос
/ 11 июня 2019

Я хотел бы создать новый столбец на основе, если значение появляется в одном из 30+ столбцов.

Вот пример данных:

df <- read.table(text =
"ACT_1   ACT_2    ACT_3 ACT_4 ACT_5 ACT_6  ACT_7
DBA     ABC     ABC    ABC    ABC    ABC       ABC
ABC     DBA     ABC    ABC    ABC    ABC       ABC
ABC     ABC     ABC    ABC    ABC    ABC       ABC",
header = TRUE, stringsAsFactors = FALSE)

Я хотел бы проверить все столбцы, которые содержат «ACT» в названии, и создать новый столбец, который является двоичным 1 - если «DBA"в строке и 0 - если это не так.Я хотел бы использовать dplyr.

Ответы [ 2 ]

1 голос
/ 11 июня 2019

Другой метод, который включает изменение формы с использованием dplyr / tidyr, будет

library(dplyr)
library(tidyr)

df %>%
  mutate(row = row_number()) %>%
  gather(key, value, starts_with("ACT")) %>%
  group_by(row) %>%
  mutate(flag = as.integer(any(value == "DBA"))) %>%
  spread(key, value) %>%
  ungroup() %>%
  select(-row)

# A tibble: 3 x 8
#   flag ACT_1 ACT_2 ACT_3 ACT_4 ACT_5 ACT_6 ACT_7
#  <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#1     1 DBA   ABC   ABC   ABC   ABC   ABC   ABC  
#2     1 ABC   DBA   ABC   ABC   ABC   ABC   ABC  
#3     0 ABC   ABC   ABC   ABC   ABC   ABC   ABC  

Или в базе R мы также можем использовать apply

df$flag <- as.integer(apply(df[grep("^ACT", names(df))] == "DBA", 1, any))
0 голосов
/ 11 июня 2019

В base R мы используем grep для подстановки столбцов, начинающихся с «ACT» (или startsWith), проверяем, равно ли оно «DBA» для создания логической матрицы, затем получаем rowSums, преобразовать его в логический vector, проверив количество ИСТИННЫХ элементов больше 0. Этот логический вектор преобразуется в двоичный файл с as.integer (или +)

df$newCol <- +(rowSums(df[grep("^ACT", names(df))] == "DBA") > 0)
df$newCol
#[1] 1 1 0

Или другой base R подход будет использовать Reduce с lapply

df$newCol <- +(Reduce(`|`, lapply(df[grep("^ACT", names(df))], `==`, "DBA")))

ПРИМЕЧАНИЕ: оба решения векторизованы


Или используя tidyverse без изменения формы

library(tidyverse)
df %>% 
      mutate(newCol = map(., ~.x == "DBA") %>% 
                        reduce(`|`) %>%
                        as.integer)
#  ACT_1 ACT_2 ACT_3 ACT_4 ACT_5 ACT_6 ACT_7 newCol
#1   DBA   ABC   ABC   ABC   ABC   ABC   ABC      1
#2   ABC   DBA   ABC   ABC   ABC   ABC   ABC      1
#3   ABC   ABC   ABC   ABC   ABC   ABC   ABC      0

или используя data.table

library(data.table)
setDT(df)[, newCol := +(Reduce(`+`, lapply(.SD, `==`, "DBA")))]

В этом примере есть только столбцы «ACT». Если есть другие столбцы, обязательно укажите .SDcols с grep, как показано в первом решении

Тесты

#data
df1 <- df[rep(seq_len(nrow(df)), 1e6), ]

на базе R

system.time(+(rowSums(df1[grep("^ACT", names(df1))] == "DBA") > 0))
# user  system elapsed 
#  0.319   0.101   0.419 
system.time(+(Reduce(`|`, lapply(df1[grep("^ACT", names(df1))], `==`, "DBA"))))
# user  system elapsed 
#  0.152   0.029   0.179 

system.time(as.integer(apply(df1[grep("^ACT", names(df1))] == "DBA", 1, any)))
# user  system elapsed 
#  5.200   0.177   5.344 

-tidyverse

system.time({df1 %>%
  mutate(row = row_number()) %>%
  gather(key, value, starts_with("ACT")) %>%
  group_by(row) %>%
  mutate(flag = as.integer(any(value == "DBA"))) %>%
  spread(key, value) %>%
  ungroup() %>%
  select(-row)})
#  user  system elapsed 
# 42.750   4.378  47.202 

system.time({
 df1 %>% 
          mutate(newCol = map(., ~.x == "DBA") %>% 
                            reduce(`|`))
})
#   user  system elapsed 
#  0.188   0.016   0.203 

-data.table

system.time({
   setDT(df1)[, newCol := +(Reduce(`+`, lapply(.SD, `==`, "DBA")))]
})
#  user  system elapsed 
#  0.152   0.011   0.163 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...