将任意列拆分为融化的数据框

Split arbitrary column into melted data frame

我有一个 data.frame,其中包含结构化数据的丑陋列。每个列可以包含 1 到 40 个感兴趣的值。每个值都用 html 分隔符 "<br />" 分隔。提取的值是1.1的形式,即一个整数一个句点,另一个整数。

如何将这些列分开并合并成不同的行?

我知道 lapply 和 tidy::separate 可能是可行的方法。但我还没有成功。所以求助

测试数据在这里:

testdata <- dget("http://pastebin.com/download.php?i=VS2cq2rB")

数据框包含两列:"id""moduler"

我想要 "id" 和 "value"。最终结果应该是这样的。

"id", "value"
1, 1.1
1, 1.2
1, 1.3
1, 2.4
2, 1.1
2, 1.3
2, 3.3

这是我的最新作品 - 与我从 lapply 开始的地方相去甚远。

origdf <- data.frame()
#names(newdf) <- c("id", 'pnummer', 'moduler')

for (i in 1:nrow(hs)) {
  newdf <- data.frame()
  newdf[i, 'id'] <- hs[i, 'id']
  newdf[i, 'pnummer'] <- hs[i, 'pnummer']
  tmp <- unlist(strsplit(as.character(hs[i,'moduler']), "<br />", fixed=T))
  for (m in 3:length(tmp)+3) {
    newdf[i, m] <- tmp[m]
  }
  origdf <- dplyr::bind_rows(newdf, origdf)
}

这是一种可能的 data.table 方法。基本上我只是将 moduler 拆分为 "<br />" 或将 "<br />Installationsmontør" 拆分为 id

library(data.table)
setDT(testdata)[, .(value = unlist(strsplit(as.character(moduler), 
                            "<br />|<br />Installationsmontør"))), by = id]
#        id value
#   1: 2862   1.1
#   2: 2862   1.2
#   3: 2862   1.3
#   4: 2862   1.4
#   5: 2862   1.5
# ---           
# 132: 2877   3.6
# 133: 2877   4.1
# 134: 2877   4.4
# 135: 2877   4.5
# 136: 2877   4.6

或与 splitstackshape 包类似

library(splitstackshape)
cSplit(testdata, splitCols = "moduler", 
       sep = "<br />|<br />Installationsmontør", 
       direction = "long", fixed = FALSE, stripWhite = FALSE)

我会尝试在一个简单的循环中使用 strsplit 函数:

newdata  <-  NULL
a <- 1
b <- 0
for (k in 1:length(testdata$moduler)) {
  M <- unlist(strsplit(as.character(testdata$moduler[k]),"<br />|<br />Installationsmontør"))
  b <- b + length(M)
  newdata$moduler[a:b] <- M
  newdata$id[a:b] <- testdata$id[k]
  a <- b + 1
}
newdata <- as.data.frame(newdata)

这是另一个使用 tidyr 中的 unnest 的选项。我们使用 library(stringr) 中的 str_extract_all 提取数字部分 ([0-9.]+)。输出是 list。我们将 list 元素的名称设置为 'testdata' 和 unnest

的 'id' 列
 library(tidyr)
 library(stringr) 
 res <- unnest(setNames(lapply(str_extract_all(testdata$moduler, '[0-9.]+'), 
                   as.numeric), testdata$id), id)
 colnames(res)[2] <- 'value'
 head(res)
 #   id  value
 #1 2862 1.1
 #2 2862 1.2
 #3 2862 1.3
 #4 2862 1.4
 #5 2862 1.5
 #6 2862 1.6

 dim(res)
 #[1] 136   2

或者 base R 方法是在 list 中提取带有 regmatches/gregexpr 的数字元素,获取 list 元素的 length lengths,基于 'testdata' 复制 'id' 列,unlist 'lst' 并创建一个新的 'data.frame'.

 lst <- lapply(regmatches(testdata$moduler, gregexpr('[0-9.]+',
              testdata$moduler)), as.numeric)
 res2 <- data.frame(id = testdata$id[rep(1:nrow(testdata), lengths(lst))],
                            value= unlist(lst))