【问题标题】:split rows by condition in R data.table在 R data.table 中按条件拆分行
【发布时间】:2016-01-24 12:34:14
【问题描述】:

我有一个包含 3 列的数据表,其中之一 包含不同长度的键:值列表。 我希望重新排列表格,使每一行只有一个键,以值为条件

例如,假设我希望获取值为

input_tbl <-
data.table::data.table(a=c("AA"),b=c("{\"ha:llo\":1,\"wor:ld\":2,\"doog:bye\":3}"),
c=c(1))

那么想要的表应该是

tbl_output <- data.table::data.table(a=c("AA",
"AA"),b=c("ha:llo","wor:ld"), c=c(1,1), s=c(1,2))

我尝试了以下功能:

data_table_clean <- function(dt){

  dt[ ,"b" := data.table::tstrsplit(b, ',', fixed = T),by=c(a, c)]
  dt[,c('b', 's'):= data.table::tstrsplit(b, ':', fixed=TRUE)]
    return(dt[s <=2,])

}

这会产生以下错误

“评估错误(expr,envir,enclos):找不到对象'a'”

当然,欢迎提出任何建议。

键实际上是这样的:

input2_tbl <-
data.table::data.table(a=c("AA"),b=c("{\"99:1d:3u:7y:89:67\":1,\"99:1D:34:YY:T6:Y6\":2,\"ll:5Y:UY:56:R5:R6\":3}"),
c=c(1))

因此输出表应该是:

 tbl2_output <- data.table::data.table(a=c("AA",
 "AA"),b=c(""99:1d:3u:7y:89:67","99:1D:34:YY:T6:Y6"), 
    c=c(1,1), s=c(1,2))

谢谢!

更新

data_table_clean <- function(dt){



  res <- dt[, data.table::tstrsplit(unlist(strsplit(gsub('[{}"]', '', b),',', fixed=TRUE)), ":(?=[^:]+$)", perl=TRUE),
                     by = .(a, c)][V2 > -100]

  data.table::setnames(res, 3:4, c("b", "s"))
  res
}

运行时出现以下错误:

.subset(x, j) 中的错误:无效的下标类型“列表”

【问题讨论】:

  • 你在这里打错了。要么使用c("a", "c"),要么使用"a,c",要么使用.(a, c),要么使用list(a, c),但不能使用它们的组合。此外,您的功能没有做您认为它正在做的事情。我会一步一步走,看看结果如何。
  • 你的功能正在为我工​​作data_table_clean(input2_tbl) # a c b s #1: AA 1 99:1d:3u:7y:89:67 1 #2: AA 1 99:1D:34:YY:T6:Y6 2 #3: AA 1 ll:5Y:UY:56:R5:R6 3我正在使用data.table_1.9.6
  • 你为什么不把library(data.table)放在顶部,然后去掉所有那些data.table::引用。
  • 这不是问题,(我试图避免环境依赖)

标签: r data.table


【解决方案1】:

一种选择是在最终输出中提取我们需要的字符。在按“a”、“c”分组后,我们使用str_extract 来执行此操作。输出是list,我们unlist,将非数字和数字分成两列,然后使用条件s&lt;3对行进行子集化。

library(stringr)
library(data.table)
input_tbl[, {
     tmp <- unlist(str_extract_all(b, "[A-Za-z]+:[A-Za-z]+|\\d+"))
       list(b=tmp[c(TRUE, FALSE)], s=tmp[c(FALSE, TRUE)])
     }, by = .(a,c)][s<3]
#    a c      b s
#1: AA 1 ha:llo 1
#2: AA 1 wor:ld 2

或者如果我们使用strsplit/tstrsplit,按'a'、'c'分组,我们删除大括号和引号([{}]")和gsub,用,strsplit)分割, unlist 输出,然后使用tstrsplit: 拆分为后跟数字。子集部分同上。

res <- input_tbl[, tstrsplit(unlist(strsplit(gsub('[{}"]', '', 
        b), ',', fixed=TRUE)), ":(?=\\d)", perl=TRUE) ,.(a,c)][V2<3]
setnames(res, 3:4, c("b", "s"))
res
#    a c      b s
#1: AA 1 ha:llo 1
#2: AA 1 wor:ld 2

更新

对于更新后的数据集,我们可以在最后一个分隔符上加上tstrsplit (:)

res1 <- input2_tbl[, tstrsplit(unlist(strsplit(gsub('[{}"]', '', 
   b),',', fixed=TRUE)), ":(?=[^:]+$)", perl=TRUE) ,
         by = .(a, c)][V2 < 3]
setnames(res1, 3:4, c("b", "s"))
res1
#     a c                 b s
# 1: AA 1 99:1d:3u:7y:89:67 1
# 2: AA 1 99:1D:34:YY:T6:Y6 2

【讨论】:

  • 嗨@akrun 非常感谢!这些非常优雅,不过只是一个小问题 - 如果键也可能包含数字怎么办?
  • @shizik 感谢您的反馈。你说的“钥匙”是什么意思?请显示一个示例(通过更新您的帖子),以便清楚。
  • 谢谢你,@akrun。我这样做了
  • @this 效果很好。天呐!只有当我将它包装为一个函数时,我才会得到: .subset(x, j) 中的错误:无效的下标类型“列表”,知道为什么吗?当然我会接受。
【解决方案2】:

既然您似乎在使用 JSON 对象,为什么不使用解析 JSON 的东西,例如“jsonlite”包?

有了它,你可以创建一个简单的函数,如下所示:

myFun <- function(invec) {
  require(jsonlite)
  x <- fromJSON(invec)
  list(b = names(x), s = unlist(x))
}

现在,应用到您的数据集,您将获得:

input_tbl[, myFun(b), by = .(a, c)]
#     a c        b s
# 1: AA 1   ha:llo 1
# 2: AA 1   wor:ld 2
# 3: AA 1 doog:bye 3

并且,对于子集:

input_tbl[, myFun(b), by = .(a, c)][s <= 2]
#     a c      b s
# 1: AA 1 ha:llo 1
# 2: AA 1 wor:ld 2

您甚至还可以重写 myFun 函数来添加一个“阈值”参数,让您可以在函数本身中设置子集。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-09
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-12
    • 2021-09-01
    • 1970-01-01
    • 2018-04-30
    相关资源
    最近更新 更多