将包含列表的列变成虚拟对象

Turn colum containing list into dummies

我有一个数据框,其中包含(space 分隔的)年份列表,我想将其变成每年的虚拟变量。

考虑以下玩具数据:

raw <- data.frame(textcol = c("case1", "case2", "case3"), years=c('1996 1997 1998','1997 1999 2000', '1996 1998 2000'))


  textcol          years
1   case1 1996 1997 1998
2   case2 1997 1999 2000
3   case3 1996 1998 2000

我现在想将数据框转换成这个

  textcol `1996` `1997` `1998` `1999` `2000` 
1   case1      1      1      1      0      0
2   case2      0      1      0      1      1
3   case3      1      0      1      0      1

我尝试使用 separate()str_split() 无济于事。有人能告诉我正确的方法吗?

您可以使用 strsplit 拆分成单独的年份,rep textcol 并使用 table.

. <- strsplit(raw$years, " ", TRUE)
table(rep(raw$textcol, lengths(.)), unlist(.))
#        1996 1997 1998 1999 2000
#  case1    1    1    1    0    0
#  case2    0    1    0    1    1
#  case3    1    0    1    0    1

或使用 stack 的管道版本。

strsplit(raw$years, " ", TRUE) |>
setNames(raw$textcol) |>
stack() |>
rev() |>
table()
#       values
#ind     1996 1997 1998 1999 2000
#  case1    1    1    1    0    0
#  case2    0    1    0    1    1
#  case3    1    0    1    0    1

使用 separate_rows 将每一年放在单独的行中,然后使用 table。 (如果您希望将其作为数据框,请将 %>% as.data.frame.matrix 附加到管道。)

library(tidyr)

tab <- raw %>% separate_rows(years) %>% table

给予:

tab
##        years
## textcol 1996 1997 1998 1999 2000
##   case1    1    1    1    0    0
##   case2    0    1    0    1    1
##   case3    1    0    1    0    1

我们可以将其显示为图表。将 tab 转换为 igraph,g。然后创建一个自定义布局 lay,按顺序显示顶点,因为 igraph 中通常的二分布局会尝试对它们重新排序以最小化交叉点。终于画出来了

library(igraph)

g <- graph_from_incidence_matrix(tab)
lay <- with(as.data.frame(layout_as_bipartite(g)), 
  cbind(ave(V1, V2, FUN = sort), V2))
plot(g, layout = lay, vertex.size = 2)

separate_rowspivot_wider 一起使用:

library(tidyverse)
raw %>% 
  separate_rows(years) %>% 
  mutate(value = 1) %>% 
  pivot_wider(textcol, names_from = years, values_from = value, values_fill = 0)

# A tibble: 3 x 6
  textcol `1996` `1997` `1998` `1999` `2000`
  <chr>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1 case1        1      1      1      0      0
2 case2        0      1      0      1      1
3 case3        1      0      1      0      1

另一个解决方案 fastDummies::dummyCols 和有用的 split 参数。

fastDummies::dummy_cols(raw, "years", split = " ", remove_selected_columns = T)

  textcol years_1996 years_1997 years_1998 years_2000 years_1999
1   case1          1          1          1          0          0
2   case2          0          1          0          1          1
3   case3          1          0          1          1          0

一个data.table选项

> library(data.table)

> dcast(setDT(raw)[, strsplit(years, " "), textcol], textcol ~ V1, fun = length)
   textcol 1996 1997 1998 1999 2000
1:   case1    1    1    1    0    0
2:   case2    0    1    0    1    1
3:   case3    1    0    1    0    1