【问题标题】:R data.table explode column using tstrsplitR data.table 使用 tstrsplit 分解列
【发布时间】:2021-10-23 06:36:07
【问题描述】:

我以data.table为例

df = data.table(id = c(1, 2, 3), val=c("['hello', 'world']", "['hi']", "['so', 'there']"))

我想将类似对象的列表拆分为单独的行,并重复 id。所以我想要的预期data.table如下

df2 = data.table(id = c(1, 1, 2, 3, 3), val=c("hello", "world", "hi", "so", "there"))

我尝试了以下

df[, c("test") := tstrsplit(val, ",", fixed=TRUE)]

但是,我得到了以下错误

[.data.table(df, , :=(c("test"), tstrsplit(val, ",", fixed = TRUE))) : 提供 2 个项目分配给 3 个项目 “测试”列。如果您想“回收”RHS,请使用 rep() 来 让代码的读者清楚这一意图。

有人能指出我在这里做错了什么吗?提前致谢。

【问题讨论】:

  • 这些实际上是df$val 中的字符串还是列表列?
  • 您不需要fixed。你也应该将结果包装在一个列表中。即df[, .(test=tstrsplit(val, ',')), by = id]
  • fixed 即使不需要也可能更可取
  • 是的,这些是df$val中的字符串

标签: r data.table strsplit


【解决方案1】:

这是一种方法,

df[, .(val = tstrsplit(gsub("[][']", "", val), ",", fixed=TRUE)), by = id]
#       id    val
#    <num> <list>
# 1:     1  hello
# 2:     1  world
# 3:     2     hi
# 4:     3     so
# 5:     3  there

它删除所有方括号和单引号,然后将所有 val 字符串连接成一个字符串 (,-collapsed),然后将它们按原意连接到 tstrsplits。 by=id 确保我们不会无意中组合不同的 vals,并且 id 会保留在输出中。

如果您想查看分组、合并、然后分步进行拆分,则可以这样做

df1[, .(val = paste(gsub("[][']", "", val), collapse = ",")), by = id]
#       id          val
#    <num>       <char>
# 1:     1 hello, world
# 2:     2           hi
# 3:     3    so, there
df1[, .(val = paste(gsub("[][']", "", val), collapse = ",")), by = id
  ][, .(val = tstrsplit(val, ",", fixed = TRUE)), by = id]
#       id    val
#    <num> <list>
# 1:     1  hello
# 2:     1  world
# 3:     2     hi
# 4:     3     so
# 5:     3  there

请注意,rep(id,...) 的错误建议很好,但您需要做更多的工作才能知道每个id 重复多少次;使用它作为分组变量可以减轻这种需求,但会花费一小部分执行时间(因为它对每个组执行一次tstrsplit,而不是全部一起执行)。

【讨论】:

    【解决方案2】:

    我认为你可以使用eval + str2lang

    > df[, .(val = eval(str2lang(gsub("\\[(.*)\\]", "c(\\1)", val)))), id]
       id   val
    1:  1 hello
    2:  1 world
    3:  2    hi
    4:  3    so
    5:  3 there
    

    【讨论】:

    • 看起来像一个便宜的 jsonlite::fromJSON 或类似的。我在生产中并不总是eval 的粉丝,这看起来还不错。不错。
    • @r2evans 我想说这只是一种选择,但由于它的效率而不推荐(尤其是对于长字符串)
    【解决方案3】:

    从您拥有的数据结构来看,您似乎拥有一个 python 数据集。您可以为此使用reticulate

    library(reticulate)
    ast <- import('ast')
    
    df_python <- r_to_py(df)
    df_python$assign(val = df_python$val$transform(ast$literal_eval))$explode('val')
    
        id    val
    0  1.0  hello
    0  1.0  world
    1  2.0     hi
    2  3.0     so
    2  3.0  there
    

    你可以直接做:

    df[, .(val = tstrsplit(gsub('[^a-z,]', '',val), ',')), by = 'id']
       id   val
    1:  1 hello
    2:  1 world
    3:  2    hi
    4:  3    so
    5:  3 there
    

    【讨论】:

      【解决方案4】:

      对于tidyverse,我们可以使用str_extract_allunnest

      library(dplyr)
      library(tidyr)
      library(stringr)
      df %>% 
        mutate(val = str_extract_all(val, '\\w+')) %>%
        unnest(val)
      # A tibble: 5 x 2
           id val  
        <dbl> <chr>
      1     1 hello
      2     1 world
      3     2 hi   
      4     3 so   
      5     3 there
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-30
        • 2021-12-28
        • 1970-01-01
        • 2020-10-01
        • 2021-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多