将多个因子列合并为一个数字列

时间:2019-07-23 06:39:36

标签: r dplyr

有人能想到一种更有趣的方式将多个因子列组合为一个数字列吗?

MWE数据集:

df <- data.frame(q.82=factor(c(1,2,2,1,1)), q.77=factor(c(2,1,1,1,1)), q.72=factor(c(1,1,1,2,2)))
levels(df$q.82) <- c("","$80 and above")
levels(df$q.77) <- c("", "$75 to $79")
levels(df$q.72) <- c("", "$70 to $74")

str(df$q.82)

Factor w/ 2 levels "","$80 and above": 1 2 2 1 1

df看起来像这样:

           q.82       q.77       q.72
1               $74 to $79           
2 $80 and above                      
3 $80 and above                      
4                          $70 to $74
5                          $70 to $74

我想要的是这样的东西,其中的列是数字:

  q.82 q.77 q.72  q
1    0   77    0 77
2   82    0    0 82
3   82    0    0 82
4    0    0   72 72
5    0    0   72 72

以下方法可行,但似乎很笨拙-主要是因为实际数据集具有许多列。

df$q.82  <- as.numeric(as.factor(df$q.82))
df$q.82[df$q.82 == 2] <- 82
df$q.82[df$q.82 == 1] <- 0
df$q.77  <- as.numeric(as.factor(df$q.77))
df$q.77[df$q.77 == 2] <- 77
df$q.77[df$q.77 == 1] <- 0
df$q.72  <- as.numeric(as.factor(df$q.72))
df$q.72[df$q.72 == 2] <- 72
df$q.72[df$q.72 == 1] <- 0

df <- df %>% mutate(q=q.82+q.77+q.72)

2 个答案:

答案 0 :(得分:2)

使用sapply使用基数R的可能方法:

  1. 对于每列,将非空字符串替换为列名称的数字部分,并将空字符串替换为零。
  2. 添加另一列q,其中包含每一行的总和。
out_df <- sapply(names(df), function(name) {
      ifelse(nchar(as.character(df[[name]])) > 0, as.numeric(sub("^q\\.", "", name)), 0)
    })     
out_df <- transform(out_df, q = rowSums(out_df))

out_df
#>   q.82 q.77 q.72  q
#> 1    0   77    0 77
#> 2   82    0    0 82
#> 3   82    0    0 82
#> 4    0    0   72 72
#> 5    0    0   72 72

类似地,使用tidyverse

library(tidyverse)

df_out <- imap_dfc(.x = df, .f =  ~{
          if_else(nchar(as.character(.x)) > 0, as.numeric(str_remove(.y, "^q\\.")), 0)          
        }) %>%
    mutate(q = rowSums(.))

df_out
#> # A tibble: 5 x 4
#>    q.82  q.77  q.72     q
#>   <dbl> <dbl> <dbl> <dbl>
#> 1     0    77     0    77
#> 2    82     0     0    82
#> 3    82     0     0    82
#> 4     0     0    72    72
#> 5     0     0    72    72

或使用data.table

library(data.table)

setDT(df)

for(j in names(df))
  set(df, j = j, value = ifelse(nchar(as.character(df[[j]])) > 0, as.numeric(sub("^q\\.", "", j)), 0))

df[, q := rowSums(.SD)][]
#>    q.82 q.77 q.72  q
#> 1:    0   77    0 77
#> 2:   82    0    0 82
#> 3:   82    0    0 82
#> 4:    0    0   72 72
#> 5:    0    0   72 72

数据

df <- data.frame(q.82=factor(c(1,2,2,1,1)), q.77=factor(c(2,1,1,1,1)), q.72=factor(c(1,1,1,2,2)))
levels(df$q.82) <- c("","$80 and above")
levels(df$q.77) <- c("", "$75 to $79")
levels(df$q.72) <- c("", "$70 to $74")

答案 1 :(得分:1)

这是另一种基本的R方法,其中我们使用sub将列中的非空白值替换为列名中的数字部分。

df[] <- t(as.integer(sub(".*?(\\d+)", "\\1", names(df))) * t(df != ""))
df
#  q.82 q.77 q.72
#1    0   77    0
#2   82    0    0
#3   82    0    0
#4    0    0   72
#5    0    0   72

,然后,如果要按行求和,可以使用rowSums

df$q <- rowSums(df)