如何在 R 中导入 Excel table 以获得二进制变量?

How to import Excel table in R in order to get binary variables?

我的数据集看起来完全像这样(只是有更多的观察和属性):

我希望每个属性都有一个二进制变量,如果 PersonX 有,则值为 1,否则为 0,但每个人还必须包括其他人的属性。它应该看起来像这样,当然,如果 Person1 也具有与 Person2 相同的属性,则不应再次生成变量:

ID       Class_Label    A469 T593 K022K A835 Z935 U83F W5326
Person1  TRUE           1    1    1     0    0    0    0
Person2  FALSE          0    1    0     1    1    0    0
Person3  FALSE          0    0    1     0    0    1    1

如您所见,Person1 和 Person3 的共同属性为:K022K,Person1 和 Person2 为 T593。 有什么办法可以解决这个问题吗?

library(tidyverse)

df <- tibble(
  id = paste0("Person", 1:3),
  class_label = c(TRUE, FALSE, FALSE),
  attribute = c("A469/T593/K022K", "A835/Z935/T593", "U835F/W5326/K022K")
)
df
#> # A tibble: 3 x 3
#>   id      class_label attribute        
#>   <chr>   <lgl>       <chr>            
#> 1 Person1 TRUE        A469/T593/K022K  
#> 2 Person2 FALSE       A835/Z935/T593   
#> 3 Person3 FALSE       U835F/W5326/K022K

df %>%
  separate_rows(attribute, sep = "/") %>%
  mutate(i = 1) %>%
  spread(attribute, i, fill = 0)
#> # A tibble: 3 x 9
#>   id      class_label  A469  A835 K022K  T593 U835F W5326  Z935
#>   <chr>   <lgl>       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Person1 TRUE            1     0     1     1     0     0     0
#> 2 Person2 FALSE           0     1     0     1     0     0     1
#> 3 Person3 FALSE           0     0     1     0     1     1     0

请注意,您尝试执行的操作通常被称为 one-hot encodingbinary encoding。此外,您可能需要注意 df %>% separate_rows(attribute, sep = "/") 您的数据采用整洁的格式,这可能还有其他好处。

更新: 要扩展到更多列,您可能需要首先确定要对哪些属性进行编码。所以像 select(df, contains("attribute"))select(df, 3:4).

df <- tibble(
  id = paste0("Person", 1:3),
  class_label = c(TRUE, FALSE, FALSE),
  attribute = c("A469/T593/K022K", "A835/Z935/T593", "U835F/W5326/K022K"),
  attribute2 = c("one/two/three", "four/five/six", "one/five/six")
)
df
#> # A tibble: 3 x 4
#>   id      class_label attribute         attribute2   
#>   <chr>   <lgl>       <chr>             <chr>        
#> 1 Person1 TRUE        A469/T593/K022K   one/two/three
#> 2 Person2 FALSE       A835/Z935/T593    four/five/six
#> 3 Person3 FALSE       U835F/W5326/K022K one/five/six

one_hot <- function(data, att) {
  quo_att <- enquo(att)
  data %>%
    select(id, class_label, !! quo_att) %>% 
    separate_rows(!! quo_att, sep = "/") %>%
    mutate(i = 1) %>%
    spread(!! quo_att, i, fill = 0) %>%
    select(-id, -class_label)
}


attributes_to_map <- select(df, contains("attribute")) %>% names
attributes_to_map
#> [1] "attribute"  "attribute2"

attributes_to_map %>%
  map_dfc(~ one_hot(df, .)) %>%
  bind_cols(select(df, id, class_label)) %>%
  select(id, class_label, everything())
#> # A tibble: 3 x 15
#>   id    class_label  A469  A835 K022K  T593 U835F W5326  Z935  five  four
#>   <chr> <lgl>       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Pers~ TRUE            1     0     1     1     0     0     0     0     0
#> 2 Pers~ FALSE           0     1     0     1     0     0     1     1     1
#> 3 Pers~ FALSE           0     0     1     0     1     1     0     1     0
#> # ... with 4 more variables: one <dbl>, six <dbl>, three <dbl>, two <dbl>

但在这一点上,您可能还想考虑 recipes 包或对多个变量的单热编码进行搜索。

好的,使用您的示例 minimal.xlsx:

install.packages('readxl')  # if you don't have this already
library(readxl)

example <- read_excel('./minimal.xlsx')  # assuming file is in working directory
example$Attribute <- as.character(example$Attribute)  # convert to character

attrs <- strsplit(example$Attribute, '/')  # split by /
attrs <- unlist(attrs)  # flatten the list
attrs <- unique(attrs)  # extract uniques

for (attr in attrs) {
  attr_row <- grepl(attr, example$Attribute)  # boolean of True/false
  attr_row <- attr_row * 1  # convert to 0, 1
  example[attr] <- attr_row
}

我试图在评论中解释它,但本质上:

  • 将属性转换为字符并在您的指示字符上拆分它们
  • 将它们组合成一个 "set" 独特属性向量
  • 遍历它们,生成每一行
  • 将每一行追加回 DataFrame

结果在这里:

您之后也可以删除原始属性列,但这应该可以满足您的需求,是一种通用的解决方案,不需要外部库。

编辑: 另一个答案更短,绝对可以用它来快速解决这个问题,就我个人而言,如果可以的话,我经常喜欢使用 base R 来完成这样的小任务,尤其是对于我想与他人分享的脚本。