如何在 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 encoding
或 binary 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 来完成这样的小任务,尤其是对于我想与他人分享的脚本。
我的数据集看起来完全像这样(只是有更多的观察和属性):
我希望每个属性都有一个二进制变量,如果 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 encoding
或 binary 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 来完成这样的小任务,尤其是对于我想与他人分享的脚本。