在R中将一个变量拆分成多个变量

3

我对R语言还比较陌生。我的问题并不完全像标题那样简单直接。以下是df的一个示例:

id    amenities
1     wireless internet, air conditioning, pool, kitchen
2     pool, kitchen, washer, dryer
3     wireless internet, kitchen, dryer
4     
5     wireless internet

这就是我希望的df的样子:

id    wireless internet   air conditioning   pool   kitchen   washer   dryer
1     1                   1                  1      1         0        0
2     0                   0                  1      1         1        1
3     1                   0                  0      1         0        1
4     0                   0                  0      0         0        0
5     1                   0                  0      0         0        0

重现数据的示例代码

df <- data.frame(id = c(1, 2, 3, 4, 5),
      amenities = c("wireless internet, air conditioning, pool, kitchen",  
                    "pool, kitchen, washer, dryer", 
                    "wireless internet, kitchen, dryer", 
                    "", 
                    "wireless internet"), 
      stringsAsFactors = FALSE)
4个回答

6

FWIW,这里是一种基于R语言的方法(假设df包含问题中展示的数据)

dat <- with(df, strsplit(amenities, ', '))
df2 <- data.frame(id = factor(rep(df$id, times = lengths(dat)),
                              levels = df$id),
                  amenities = unlist(dat))
df3 <- as.data.frame(cbind(id = df$id,
                     table(df2$id, df2$amenities)))

这将导致:
> df3
  id air conditioning dryer kitchen pool washer wireless internet
1  1                1     0       1    1      0                 1
2  2                0     1       1    1      1                 0
3  3                0     1       1    0      0                 1
4  4                0     0       0    0      0                 0
5  5                0     0       0    0      0                 1

分解正在发生的事情:

  1. dat <- with(df, strsplit(amenities, ', ')) splits the amenities variable on ', ', resulting in

    > dat
    [[1]]
    [1] "wireless internet" "air conditioning"  "pool"             
    [4] "kitchen"          
    
    [[2]]
    [1] "pool"    "kitchen" "washer"  "dryer"  
    
    [[3]]
    [1] "wireless internet" "kitchen"           "dryer"            
    
    [[4]]
    character(0)
    
    [[5]]
    [1] "wireless internet"
    
  2. The second line takes dat and turns it into a vector, and we add on and id column by repeating the origina id values as many times as the number of amenities for that id. This results in

    > df2
       id         amenities
    1   1 wireless internet
    2   1  air conditioning
    3   1              pool
    4   1           kitchen
    5   2              pool
    6   2           kitchen
    7   2            washer
    8   2             dryer
    9   3 wireless internet
    10  3           kitchen
    11  3             dryer
    12  5 wireless internet
    
  3. Use the table() function to create the contingency table and then we add on an id column.


1
我有一个小变化 - df2 <- stack(setNames(dat,seq_along(dat)))[2:1] 然后 cbind(df["id"], unclass(table(df2))) - thelatemail

3

使用 dplyrtidyr 的解决方案。请注意,我将 "" 替换为 None,因为稍后处理列名时更容易。

library(dplyr)
library(tidyr)

df2 <- df %>%
  separate_rows(amenities, sep = ",") %>%
  mutate(amenities = ifelse(amenities %in% "", "None", amenities)) %>%
  mutate(value = 1) %>%
  spread(amenities, value , fill = 0) %>%
  select(-None)
df2
#   id  air conditioning  dryer  kitchen  pool  washer pool wireless internet
# 1  1                 1      0        1     1       0    0                 1
# 2  2                 0      1        1     0       1    1                 0
# 3  3                 0      1        1     0       0    0                 1
# 4  4                 0      0        0     0       0    0                 0
# 5  5                 0      0        0     0       0    0                 1

0

dummies包在这里可能很有用。试试看。

library(dplyr); library(tidyr); library(dummies)
df2 <- df %>% separate_rows(amenities, sep = ",")
df2$amenities <- trimws(df2$amenities, "both") # remove spaces (left and right) - so that you will not have 2 "pool" columns in your final data frame
df2 <- dummy.data.frame(df2)[, -2]
colnames(df2) <- trimws(gsub("amenities", "", colnames(df2)), "both") # arrange colnames
df3 <- df2 %>% 
  group_by(id) %>%
  summarise_all(funs(sum)) ## aggregate by column and id
df3

# A tibble: 5 x 7
#id `air conditioning` dryer kitchen  pool washer `wireless internet`
#<dbl>              <int> <int>   <int> <int>  <int>               <int>
#     1                  1     0       1     1      0                   1
#     2                  0     1       1     1      1                   0
#     3                  0     1       1     0      0                   1
#     4                  0     0       0     0      0                   0
#     5                  0     0       0     0      0                   1

0
为了完整起见,这里还有一个简洁的data.table解决方案:
library(data.table)
setDT(df)[, strsplit(amenities, ", "), by = id][
  , dcast(.SD, id ~ V1, length)]
   id air conditioning dryer kitchen pool washer wireless internet
1:  1                1     0       1    1      0                 1
2:  2                0     1       1    1      1                 0
3:  3                0     1       1    0      0                 1
4:  5                0     0       0    0      0                 1
在转换为 data.table 后,amenities 按照 ", " 进行拆分,以将每个项目拆分为单独的行(长格式)。然后再使用 length() 函数对其进行聚合,并将其重塑为宽格式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接