将 python-like 列表转换为 R 嵌套向量

Convert python-like list to R nested vectors

我正在尝试 "automatically" 将 data.frame 列转换为多列。

这是 df 的样子:

library(dplyr)
foo <- data_frame(ID = c(1,2),
                  Val =  c("A", "B"),
                  Geom = c("[{X11,Y11,Z11}, {X12,Y12,Z12}, {X13,Y13,Z13}]", "[{X21,Y21,Z21},{X22,Y22,Z22},{X23,Y23,Z23}]"))

这是我希望的样子:

bar <- data_frame(ID = c(1,1,1,2,2,2),
                  Val=c("A", "A", "A", "B", "B", "B"),
                  Geom1 = c("X11", "X12", "X13", "X21", "X22", "X23"),
                  Geom2 = c("Y11", "Y12", "Y13", "Y21", "Y22", "Y23"),
                  Geom3 = c("Z11", "Z12", "Z13", "Z21", "Z22", "Z23"))

我考虑进行此类转换的工作流程由两部分组成:

1 - 将 Geom 转换为 R 结构,例如:

list(c("X11","Y11","Z11"), c(...), ...)

2 - 使用 tidyr::unnest()tidyr::separate() 将此类列表拆分为列

我想我可以处理第二部分,但找不到第一部分的好指针。我可以将此专栏写入 csv 并在之后自动读取它,但考虑到我的 data.frame 将是一个闪亮的反应对象,这将涉及很多 writing/reading.

我尝试使用 fromJSON()(jsonlite、rjson 和 RJSONIO),但由于这不是有效的 json-字符串,所以它不不解析它。

data.table/splitstackshape的解决方案:

library(data.table)
library(splitstackshape)

dt = setDT(foo)[,strsplit(gsub('\[{|}\]','', Geom, perl=T), '}, *{', perl=T), .(ID, Val)]

cSplit(dt, 'V1')
#   ID Val V1_1 V1_2 V1_3
#1:  1   A  X11  Y11  Z11
#2:  1   A  X12  Y12  Z12
#3:  1   A  X13  Y13  Z13
#4:  2   B  X21  Y21  Z21
#5:  2   B  X22  Y22  Z22
#6:  2   B  X23  Y23  Z23

这是一种使用 base R 的方法:

# vector to work with
geom <- c("[{X11,Y11,Z11}, {X12,Y12,Z12}, {X13,Y13,Z13}]", "[{X21,Y21,Z21},{X22,Y22,Z22},{X23,Y23,Z23}]")
# remove extraneous characters and split into list using "},"
geom <- strsplit(gsub("[]{ []", "", Geom), split="},")
# remove two "}"s
geom <- sapply(geom, function(i) gsub("}", "", i))
# make a list of elements
geom <- strsplit(geom, split=",")

# construct the variables
geomData <- data.frame(t(sapply(geom, function(i) sapply(1:3, function(row) c(i[row])))))
# give names to data frame
names(geomData) <- c("Geom1", "Geom2", "Geom3")

# final data.frame
fooNew <- cbind(foo[, 1:2], geomData)

1) dplyr 这将数据帧拆分为行,对于每一行,使用 gsub 将每个三元组拆分为单独的一行,并且 read.table进一步解析 Geom。然后它修复列名并执行 ungroup。 (如果 V1、V2 和 V3 可以代替 Geom1、Geom2 和 Geom3,则可以省略 setNames 行。)

library(dplyr)

foo %>% 
   group_by(ID, Val) %>% 
   do(read.table(text=gsub("^..|..$|}, *{", "\n", .$Geom, perl=T), sep=",", as.is=T)) %>% 
   setNames(sub("^V(\d+)", "Geom\1", colnames(.))) %>%
   ungroup()

给予:

Source: local data frame [6 x 5]

     ID   Val Geom1 Geom2 Geom3
  (dbl) (chr) (chr) (chr) (chr)
1     1     A   X11   Y11   Z11
2     1     A   X12   Y12   Z12
3     1     A   X13   Y13   Z13
4     2     B   X21   Y21   Z21
5     2     B   X22   Y22   Z22
6     2     B   X23   Y23   Z23

2) 无包 这使用相同的方法但没有任何包。如果用V1,V2,V3代替Geom1,Geom2,Geom3就可以省略最后一行代码

bar <- do.call("rbind", by(foo, foo$ID, function(x) 
   cbind(x[1:2], read.table(text = gsub("^..|..$|}, *{", "\n", x$Geom, perl=T), sep=","))))
names(bar) <- sub("^V(\d+)", "Geom\1", names(bar))

给予:

> bar
    ID Val Geom1 Geom2 Geom3
1.1  1   A   X11   Y11   Z11
1.2  1   A   X12   Y12   Z12
1.3  1   A   X13   Y13   Z13
2.1  2   B   X21   Y21   Z21
2.2  2   B   X22   Y22   Z22
2.3  2   B   X23   Y23   Z23