【问题标题】:One Hot Encoding for top categories, NA, and remaining subsumed as 'others' in R顶级类别的一种热编码,NA,其余归入 R 中的“其他”
【发布时间】:2019-03-25 04:18:57
【问题描述】:

我想对我的变量进行一次热编码,只针对顶级类别以及 NA 和“其他”。

所以在这个简化的例子中,热编码 b where freq > 1 and NA:

id <- c(1, 2, 3, 4, 5, 6)
b <- c(NA, "A", "C", "A", "B", "C")
c <- c(2, 3, 6, NA, 4, 7)
df <- data.frame(id, b, c)

  id    b  c
1  1 <NA>  2
2  2    A  3
3  3    C  6
4  4    A NA
5  5    B  4
6  6    C  7

table <- as.data.frame(table(df$b))

  Var1 Freq
1    A    2
2    B    1
3    C    2

table_top <- table[table$Freq > 1,]

  Var1 Freq
1    A    2
3    C    2

现在,我想要这样的东西

  id  b_NA  c b_A b_C b_Others
    1    1  2   0   0        0
    2    0  3   1   0        0
    3    0  6   0   1        0
    4    0 NA   1   0        0
    5    0  4   0   0        1
    6    0  7   0   1        0

我已经尝试使用子集 df

table_top <- as.vector(table_top$Var1)
table_only_top <- subset(df, b %in% table_top)
table_only_top

  a b  c
2 1 A  3
3 2 C  6
4 2 A NA
6 3 C  7

但是,现在我不知道如何获得输出。在我的真实数据中,我有比这里更多的类别,因此不能选择使用输出中的名称。我的真实输出中的其他类别也存在许多类别。

高度赞赏任何提示:)

【问题讨论】:

    标签: r one-hot-encoding


    【解决方案1】:

    data.tablemltools 快速而性感:

    > one_hot(dt, naCols = TRUE, sparsifyNAs = TRUE)
    
       id cat_NA cat_A cat_C cat_Others freq
    1:  1      1     0     0          0    2
    2:  2      0     1     0          0    3
    3:  3      0     0     1          0    6
    4:  4      0     1     0          0   NA
    5:  5      0     0     0          1    4
    6:  6      0     0     1          0    7
    

    代码

    加载库
    library(dplyr)
    library(data.table)
    library(mltools)
    
    转换数据
    # Kick out all with freq == 1 and below
    df <- df %>%
        # Group by variables that will be onehotted
        group_by(cat) %>%
        # Add a count per group item column 
        mutate(count = n()) %>%
        # Ungroup for next steps
        ungroup() %>%
        # Change all that have a count of 1 or below to "Others".
        # If cat was a factor, we would get numeric results at this step.
        mutate(cat = ifelse(!is.na(cat) & count <= 1, "Others", cat),
        # Only now we turn it into a factor for the one_hot function 
                            cat = as.factor(cat)) %>%
        # Drop the count column
        select(id, cat, freq)
    
    # Turn into data.table
    dt <- as.data.table(df)
    
    检查中间结果
    > dt
           id    cat freq
    1:  1   <NA>    2
    2:  2      A    3
    3:  3      C    6
    4:  4      A   NA
    5:  5 Others    4
    6:  6      C    7
    

    数据

    id <- c(1, 2, 3, 4, 5, 6)
    cat <- c(NA, "A", "C", "A", "B", "C")
    freq <- c(2, 3, 6, NA, 4, 7)
    # It is important to have no other factor variables other
    # than the variable(s) you one want to one hot. For that reason
    # the automatic factoring is turned off.
    df <- data.frame(id, cat, freq, 
                     stringsAsFactors = FALSE)          
    
    > df
      id  cat freq
    1  1 <NA>    2
    2  2    A    3
    3  3    C    6
    4  4    A   NA
    5  5    B    4
    6  6    C    7
    

    【讨论】:

    • 这是天才@Roman,非常感谢!在虚拟数据上它工作得很好。当我将您的逻辑应用于我的真实数据时,发生了一些奇怪的事情 - 我希望它只包括那些频率 > 66 的活动,但它现在在第 4 行(下一条评论)中包含 19 个 - 任何线索我可能有做错了吗? test_2 &lt;- test %&gt;% group_by(activity_new) %&gt;% mutate(count = n()) %&gt;% ungroup() %&gt;% mutate(activity_new = ifelse(!is.na(activity_new) &amp; count &lt;= 66, "Others", activity_new), activity_new = as.factor(activity_new)) %&gt;% select(id, activity_new, cons)
    • test_2 # A tibble: 16,096 x 3 id activity_new cons &lt;fct&gt; &lt;fct&gt; &lt;int&gt; 1 A1 NA 22034 2 A2 NA 4060 3 A3 Others 7440 4 B2 19 4199490
    • @Sarah,不客气。那些奇怪的东西是什么?请注意one_hot() 仅适用于因子变量(请参阅mutate(..., cat = as.factor(cat)))。治疗 NA 也有不同的方法。
    • 嗨@Sarah,我添加了一些cmets以进行澄清。如果您可以在原始帖子中添加str(test),将会很有帮助。在我看来,您的数据构建确实出了问题。 activity_new 可能已经是突变之前的因子变量了吗?
    • 非常感谢 - 您的额外 cmets 清除了我的错误:我有两个因素,而 activity_new 之前也是一个因素。事先将两者都转换为字符可以解决问题,并且您的代码运行良好! :) @罗马
    【解决方案2】:

    绝对不是一个优雅的解决方案,但它应该可以工作:

    library(tideverse)
    library(reshape2)
    
    df %>% 
      gather(var, val, -id) %>%
      add_count(var, val) %>% 
      mutate(res = ifelse(var == "b" & n > 1, 1, 0),
             val = paste("b_", val, sep = "")) %>% 
      filter(var == "b" & n != 1) %>% 
      dcast(id ~ val, value.var = "res") %>% 
      full_join(df, by = c("id" = "id")) %>%
      mutate(b_NA = ifelse(is.na(b), 1, 0)) %>%
      mutate_at(vars(contains("b_")), funs(replace(., is.na(.), 0))) %>%
      mutate(b_OTHERS = ifelse(rowSums(.[grep("b_", names(.))]) != 0, 0, 1))
    
      id b_A b_C    b  c b_NA b_OTHERS
    1  2   1   0    A  3    0        0
    2  3   0   1    C  6    0        0
    3  4   1   0    A NA    0        0
    4  6   0   1    C  7    0        0
    5  1   0   0 <NA>  2    1        0
    6  5   0   0    B  4    0        1
    

    【讨论】:

      【解决方案3】:

      您可以根据您的不同标准cbinddata.frames。

      # simple conditions -------------------------------------------------------
      df <-  df_orig[,-1]
      df_na <- is.na(df)
      colnames(df_na) <- paste0(colnames(df),"_NA")
      df_A <- df=="A"
      colnames(df_A) <- paste0(colnames(df),"_A")
      df_C <- df=="C"
      colnames(df_C) <- paste0(colnames(df),"_C")
      
      # for counts you can use sapply with one loop -----------------------------
      df_counts <- df
      for(j in 1:ncol(df)) {
        counts <- sapply(1:nrow(df), function(x) sum(df[x,j]==df[,j], na.rm=T) )
       df_counts[,j] <- counts
      }
      
      df_counts <- df
      # or avoid explicit loops altogether --------------------------------------
      df_counts2 <- sapply(1:ncol(df), function(y) sapply(1:nrow(df), function(x) sum(df[x,y]==df[,y], na.rm=T) ) )
      colnames(df_counts2 ) <- paste0(colnames(df),"_counts")
      
      # cbind df's  -------------------------------------------------------------
      df_full <- cbind(df_orig, df_na, df_A, df_C, df_counts2)
      # check if frequency greater then 1 or NA ---------------------------------
      df_full$result <- df_full[,10:11] >=2 | df_full[,4:5]
      df_full
      

      更难的部分是我想计算频率,这里我包括了两种方法。结果是:

        id    b  c  b_NA  c_NA   b_A   c_A   b_C   c_C b_counts c_counts result.b_NA result.c_NA
      1  1 <NA>  2 FALSE FALSE FALSE FALSE FALSE FALSE        1        1       FALSE       FALSE
      2  2    A  3 FALSE FALSE  TRUE FALSE FALSE FALSE        2        1        TRUE       FALSE
      3  3    C  6 FALSE FALSE FALSE FALSE  TRUE FALSE        2        1        TRUE       FALSE
      4  4    A NA FALSE  TRUE  TRUE    NA FALSE    NA        2        0        TRUE        TRUE
      5  5    B  4 FALSE FALSE FALSE FALSE FALSE FALSE        1        1       FALSE       FALSE
      6  6    C  7 FALSE FALSE FALSE FALSE  TRUE FALSE        2        1        TRUE       FALSE
      

      您可以根据自己的条件修改列。希望有帮助

      【讨论】:

      • 非常感谢您的回答。不幸的是,这并不是我的意思,因为我只想在我的新 df 中包含那些满足 if 子句(频率 > 1)的类别。因为我有很多类别,所以我不能这样做,然后删除不需要的列。
      • 好的,df_countsdf_counts2 呢?这些只有这个标准
      猜你喜欢
      • 2018-12-17
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 2018-04-08
      • 2021-02-09
      • 2018-06-26
      • 1970-01-01
      • 2021-11-13
      相关资源
      最近更新 更多