R find value in dataframe (rowwise for factor variables)

Viewed 91

I have a df with string (factor) variables like this

id v1 v2 v3 v4
1  1  1  1  0
1  0  0  0  0
1  1  1  1  0
1  1  1  1  0
1  1  1  1  0

What I want is to create a new column that equals 1 or 0 (numeric) if either 1 appears or not on a list of columns say (v2, v3, v4)

id v1 v2 v3 v4 lu
1  1  1  1  0  1
2  0  0  0  0  0
3  1  1  1  0  1
4  0  0  1  0  1
5  1  0  1  0  1

I don't want to convert to numeric (as I need them for future analysis) so something like

df <- df %>% mutate(lu = select(., v2:v4) %>% rowSums(na.rm = TRUE))
df <- df %>% mutate(lu = if_else(lu_1 > 0, 1, lu))

Is not possible. Actually the list of variables to consider is fairly long (around 150)

Thx!

5 Answers

Hi i dont know if it's something like this you want. im not good at dplyr so here is an base R version

dummy data:

a <-as.character(sample(0:1,100,replace = T))
b <-as.character(sample(0:1,100,replace = T))
c <-as.character(sample(0:1,100,replace = T))
d <-as.character(sample(0:1,100,replace = T))

df <- as.data.frame(cbind(a,b,c,d), stringsAsFactors = T)

actual code:

test <- function(x) {
  out <- sum(as.numeric(x))
  ifelse(out>0,1,0)
}

df$lu <- as.factor(apply(df,MARGIN = 1, test))

output:

id  a   b   c   d   lu
1   0   1   0   0   1
2   0   1   0   0   1
3   0   1   0   0   1
4   0   1   0   1   1
5   0   0   0   0   0

This can be quite expensive to compute as well

library(tidyverse)



df_example <- data.table::fread("id v1 v2 v3 v4
1  1  1  1  0
1  0  0  0  0
1  1  1  1  0
1  1  1  1  0
1  1  1  1  0") %>% tibble()


df_example %>%
  mutate(across(-id,.fns = as_factor)) %>% 
  rowwise() %>% 
  mutate(lu = c_across(-id) %>%
           as.character() %>%
           as.numeric() %>%
           sum()) %>% 
  mutate(expensive = if_else(lu == 0,0,1))
#> # A tibble: 5 x 7
#> # Rowwise: 
#>      id v1    v2    v3    v4       lu expensive
#>   <int> <fct> <fct> <fct> <fct> <dbl>     <dbl>
#> 1     1 1     1     1     0         3         1
#> 2     1 0     0     0     0         0         0
#> 3     1 1     1     1     0         3         1
#> 4     1 1     1     1     0         3         1
#> 5     1 1     1     1     0         3         1

Created on 2020-06-13 by the reprex package (v0.3.0)

This can maybe be a bit faster

df_example %>%
  mutate(across(-id,.fns = as_factor)) %>% 
  mutate(lu = rowSums(across(-id, .fns = ~ .x %>% as.character() %>% as.numeric()))) %>% 
  mutate(lu = if_else(lu == 0,0,1))

Just

df$lu <- as.numeric(rowSums(df[,3:5]=="1")>0)

or, like sharmajee499

df %>%
  mutate(lu=as.numeric(rowSums(df[,3:5]=="1")>0))

first of all make a new column to sum of the row like:

df<- df %>% mutate(sum= rowSums(df[,2:5])

After that you can do a if condition on that column to make new column:

df<- df %>% mutate(lu= ifelse(sum>=1,1,0))

Using dplyr 1.0, you can use across() to transform to numeric, then apply rowSums():

df %>%
  mutate(lu = rowSums(across(starts_with("v"), .fns = as.numeric)))

Here we have the sums in our new column, if we prefer a binary outcome:

df %>%
  mutate(lu = 1*(rowSums(across(starts_with("v"), .fns = as.numeric)) > 0))
Related