如何将 key/value 字符串转换为不同的行?

How to transform a key/value string into distinct rows?

我有一个包含键值字符串的 R 数据集,如下所示:

quest<-data.frame(city=c("Atlanta","New York","Atlanta","Tampa"), key_value=c("rev=63;qty=1;zip=45987","rev=10.60|34;qty=1|2;zip=12686|12694","rev=12;qty=1;zip=74268","rev=3|24|8;qty=1|6|3;zip=33684|36842|30254"))

转换为:

      city                                  key_value
1  Atlanta                     rev=63;qty=1;zip=45987
2 New York       rev=10.60|34;qty=1|2;zip=12686|12694
3  Atlanta                     rev=12;qty=1;zip=74268
4    Tampa rev=3|24|8;qty=1|6|3;zip=33684|36842|30254

基于上面的数据框,我如何创建一个如下所示的新数据框:

      city  rev qty   zip
1  Atlanta 63.0   1 45987
2 New York 10.6   1 12686
3 New York 34.0   2 12686
4  Atlanta 12.0   1 74268
5    Tampa  3.0   1 33684
6    Tampa 24.0   6 33684
7    Tampa  8.0   3 33684

"|"是通用分隔符,它将确定要创建的行数。

我们可以使用tidyverse。使用 separate_rows,将 'key_value' 拆分为 ; 并展开行,然后将 separate 列分为两列('key','value' 在 =,展开|处的行(separate_rows),按'city','key'分组,得到序号(row_number())和spread 到 'wide' 格式

library(tidyverse)
separate_rows(quest, key_value, sep=";") %>% 
     separate(key_value, into = c("key", "value"), sep="=") %>% 
     separate_rows(value, sep="[|]", convert = TRUE) %>% 
     group_by(city, key) %>% 
     mutate(rn = row_number()) %>% 
     spread(key, value) %>%
     select(-rn)
# A tibble: 7 x 4
# Groups:   city [3]
#      city   qty   rev   zip
#*   <fctr> <dbl> <dbl> <dbl>
#1  Atlanta     1  63.0 45987
#2  Atlanta     1  12.0 74268
#3 New York     1  10.6 12686
#4 New York     2  34.0 12694
#5    Tampa     1   3.0 33684
#6    Tampa     6  24.0 36842
#7    Tampa     3   8.0 30254

;分割,再由=|分割,组合成矩阵,以第一部分为名。然后重复原始数据框的行,无论为每行找到多少行,然后合并。我没有在这里将任何列转换为数字,它们保留为字符。

a <- strsplit(as.character(quest$key_value), ";")
a <- lapply(a, function(x) {
    x <- do.call(cbind, strsplit(x, "[=|]"))
    colnames(x) <- x[1,]
    x[-1,,drop=FALSE]
})
b <- quest[rep(seq_along(a), sapply(a, nrow)), colnames(quest) != "key_value", drop=FALSE]
out <- cbind(b, do.call(rbind, a), stringsAsFactors=FALSE)
rownames(out) <- NULL
out
##       city   rev qty   zip
## 1  Atlanta    63   1 45987
## 2 New York 10.60   1 12686
## 3 New York    34   2 12694
## 4  Atlanta    12   1 74268
## 5    Tampa     3   1 33684
## 6    Tampa    24   6 36842
## 7    Tampa     8   3 30254