将具有不同数量值的大字符列拆分为多列

Split a large character column with different amount of values into multiple columns

我有一个字符列,每行的值数量不同。这只是一个小例子:

GoodForMeal %>% head(5)
# A tibble: 5 x 1
GoodForMeal                                                                                
<chr>
1 dessert': False, 'latenight': False, 'lunch': True, 'dinner': True
2 dessert': False, 'latenight': False, 'lunch': True, 'dinner': True 
3 <NA>
4 dessert': False, 'latenight': False, 'lunch': True, 'dinner': True 
5 dessert': False, 'latenight': False, 'lunch': True, 'dinner': True

这是第一行的dput()列:

structure(list(GoodForMeal = "dessert': False, 'latenight': False, 'lunch': True, 'dinner': True, 'breakfast': False, 'brunch': False}"), .Names = "GoodForMeal", row.names = c(NA, 
-1L), class = c("tbl_df", "tbl", "data.frame"))

我想将冒号前的值指定为列名,将冒号后的值指定为相应列的值。

示例:

   desert latenight lunch diner 
1  False  False     True  True
2  False  False     True  True
3  NA     NA        NA    NA  
4  False  False     True  True
5  False  False     True  True

我尝试使用 tidyr packadge 和 separate 以及 spread 函数:

separate(GoodForMeal, c("key", "value"), sep = ":", extra = "merge") %>% spread(key, value)

问题是 r 没有拆分冒号前的所有值,而是拆分第一个值。

所以结果是这样的:

GoodForMeal %>% str()

Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   4464 obs. of  2 variables:
 $ dessert': chr  " False, 'latenight': False, 'lunch': True, 'dinner': False, 'breakfast': False, 'brunch': False}" " False, 'latenight': False, 'lunch': True, 'dinner': True, 'breakfast': False, 'brunch': False}" " False, 'latenight': False, 'lunch': False, 'dinner': False, 'breakfast': False, 'brunch': False}" " False, 'latenight': False, 'lunch': True, 'dinner': True, 'breakfast': False, 'brunch': False}" ...
 $ <NA>    : chr  NA NA NA NA ...

知道如何拆分值以使其看起来像示例中的那样吗?谢谢

这不是一个优雅的解决方案(而且很长),但似乎可行。我确实更改了数据以使其更通用。希望这是一个好的开始。

# i made some changes in the data; remove lunch entry in the 4th element and remove dessert in the 1st
sampleData <- c("'dessert': False, 'latenight': False, 'lunch': True, 'dinner': True",
            "'dessert': False, 'latenight': False, 'lunch': True, 'dinner': True",
            NA,
            "'dessert': False, 'latenight': False, 'dinner': True",
            "'latenight': False, 'lunch': True, 'dinner': True")

# [1] "'dessert': False, 'latenight': False, 'lunch': True, 'dinner': True"
# [2] "'dessert': False, 'latenight': False, 'lunch': True, 'dinner': True"
# [3] NA                                                                   
# [4] "'dessert': False, 'latenight': False, 'dinner': True"               
# [5] "'latenight': False, 'lunch': True, 'dinner': True" 

# not sure if this is necessary, but jsut to clean the data
sampleData <- gsub(x = sampleData, pattern = "'| ", replacement = "")

# i'm a data.table user, so i'll jsut use tstrsplit
# split the pairs within each elements first
x <- data.table::tstrsplit(sampleData, ",")

# split the header and the entry
test <- lapply(x, function(x) data.table::tstrsplit(x, ":", fixed = TRUE))

# get the headers
indexHeader <- do.call("rbind", lapply(test, function(x) x[[1]]))

# get the entries
indexValue <- do.call("rbind",
                      lapply(test, function(x){if(length(x) > 1){ return(x[[2]])}else{ return(x[[1]])} }))

# get unique headers
colNames <- unique(as.vector(indexHeader))

colNames <- colNames[!is.na(colNames)]

# determine the order of the entries using the header matrix
indexUse <- apply(indexHeader, 2, function(x) match(colNames, x))

# index the entry matrix using the above matching
resA <- mapply(FUN = function(x,y) x[y], 
               x = as.data.frame(indexValue), 
               y = as.data.frame(indexUse))

# convert to data frame
final <- as.data.frame(t(resA))

# rename columns
colnames(final) <- colNames

# should give something like this 
final 
# dessert latenight lunch dinner
# False     False  True   True
# False     False  True   True
# <NA>      <NA>  <NA>   <NA>
# False     False  <NA>   True
# <NA>     False  True   True

使用您提供的测试数据,我将首先使用 mutate 删除 ': 等字符列以及用餐时​​间关键字。这使您可以使用分隔不同用餐时间的逗号进行拆分。以下为示意图:

df <- structure(list(GoodForMeal = "dessert': False, 'latenight': False, 'lunch': True, 'dinner': True, 'breakfast': False, 'brunch': False}"),
                .Names = "GoodForMeal", row.names = c(NA, -1L),
                class = c("tbl_df", "tbl", "data.frame"))

df %>%
  mutate(GoodForMeal = trimws(gsub("[':]|dessert|lunch|dinner|latenight|brunch",
                                   "",
                                   GoodForMeal))) %>%
  separate(GoodForMeal,
           c("dessert", "latenight", "lunch", "dinner"),
           ", ",
           extra="drop")

它应该产生:

# A tibble: 1 x 4
# dessert latenight lunch dinner
# * <chr>     <chr> <chr>  <chr>
#   False     False  True   True

希望这有用。