【问题标题】:Create new columns with information extracted from another column on R使用从 R 上的另一列中提取的信息创建新列
【发布时间】:2021-07-21 02:45:08
【问题描述】:

我有一个数据框,其中一列有多个信息,用“;”分隔,如下所示:

DF = data.frame(a = c(1,1,1,2,2), b = c('aaa','aaa','aba','abc','ccc'), 
                extra_info = c(
                  'animal=horse;color=orange;shape=circle',
                  'animal=monkey;shape=square;value=532',
                  'animal=horse;color=blue;shape=square;value=321',
                  'animal=dog;color=green;value=678',
                  'color=pink;shape=triangle'
                ))

我不能使用 read.table ,因为我已经在使用不同的函数来读取数据(而且 extra_info 列中每一行的内容也不同,并且列会被弄乱)。我想做的是将所有这些信息分开到不同的列,并相应地分配适当的名称,例如:

a   b   animal  color   shape    value
1  aaa  horse   orange  circle   NA
1  aaa  monkey  NA      square   532
1  aba  horse   blue    square   321
2  abc  dog     green   NA       678
2  ccc  NA      pink    triangle NA

到目前为止,我已经尝试过:

new_cols = DF %>% separate(extra_info, c(LETTERS[1:4]), sep = ";")

new_cols %>% separate(A, c("key","value"), sep = '=') %>% 
  separate(B, c("key","value"), sep = '=') %>%
  separate(C, c("key","value"), sep = '=') %>%
  separate(D, c("key","value"), sep = '=') %>%
  pivot_wider(names_from = c("key"), values_from = c("value"))

但它并没有按预期工作。

【问题讨论】:

  • 查看tidyr::separate。这将为您拆分; 上的列。然后遍历每一列并解析它。如果您需要更多帮助,请提供minimal reproducible example
  • 感谢您的评论。我试过分开,效果很好,虽然我不知道可能出现的所有可能类型的信息,所以在某些列中存在不匹配。
  • 您实际上可以使用separate twice。一次用于;,一次用于所有结果列,在= 上拆分以分离为键和值列。然后,您可以使用 pivot_wider 将键和值旋转到每个键的单独列中。
  • 我了解步骤,但我还没有弄清楚如何实现它。我编辑了这篇文章,我认为它现在更易于理解和重现。非常感谢您的帮助!

标签: r string dplyr tidyverse extract


【解决方案1】:

stringr 包更简洁一些,但如果你只想要base R,你可以使用以下。在pattern 结构(?<=animal=)\\w+(?=\\b) 中,\\w+ 是实际返回的,它是任何单词字符 (\\w),并且必须至少有一个 (+)。由于需要数字,因此将其与 \\d+ 交换为“值”。或者,您可以将两者都替换为 [:alnum:]+。 然后(?<=animal=)结构用于指定它必须以“animal=”开头,(?=\\b)结构表示它必须跟一个字边界(\\b)。您可以更具体一点,将\\b 替换为(,|;|$),它代表逗号或分号或行尾(编辑:原始问题在某些地方有逗号)。可能有一种很好的方法可以在四个单词上编写一个循环来动态创建变量名称和模式。

pattern <- "(?<=animal=)\\w+(?=\\b)"
DF$animal <- sapply(regmatches(DF$extra_info, regexec(pattern, DF$extra_info , perl=T)), "[", 1)
pattern <- "(?<=color=)\\w+(?=\\b)"
DF$color<- sapply(regmatches(DF$extra_info, regexec(pattern, DF$extra_info , perl=T)), "[", 1)
pattern <- "(?<=shape=)\\w+(?=\\b)"
DF$shape<- sapply(regmatches(DF$extra_info, regexec(pattern, DF$extra_info , perl=T)), "[", 1)
pattern <- "(?<=value=)\\d+(?=\\b)"
DF$value <- sapply(regmatches(DF$extra_info, regexec(pattern, DF$extra_info , perl=T)), "[", 1)

如果你乐于使用 tidyverse/stringr,这里是代码。

DF <- DF %>% 
  mutate(animal = str_extract(extra_info, "(?<=animal=)\\w+(?=\\b)" )) %>% 
  mutate(color = str_extract(extra_info, "(?<=color=)\\w+(?=\\b)" )) %>% 
  mutate(shape = str_extract(extra_info, "(?<=shape=)\\w+(?=\\b)" )) %>% 
  mutate(value = str_extract(extra_info, "(?<=value=)\\d+(?=\\b)" ))

有关字符串操作和正则表达式的更多信息,请参阅此处的 stringr 备忘单:https://github.com/rstudio/cheatsheets/blob/master/strings.pdf

【讨论】:

  • “可能有一种很好的方法可以在四个单词上编写一个循环”,是的,我认为这对于一个好的通用解决方案是必要的。我的假设是 OP 正在分享一个只有 4 个单词的玩具示例,但可能还有更多......
【解决方案2】:

这是一种方法,我将键值对的语法更改为有效的 JSON 语法并使用 jsonlite::fromJSON 解析它:

library(purrr)
library(dplyr)
library(stringr)
library(jsonlite)
DF %>%
  mutate(
    json = str_replace_all(extra_info, pattern = "\\b", replacement = '"'),
    json = str_replace_all(json, pattern = fixed("="), replacement = ":"),
    json = str_replace_all(json, pattern = fixed(";"), replacement = ","),
    json = paste("{", json, "}"),
  ) %>%
  pull(json) %>%
  map(jsonlite::fromJSON) %>%
  map(as.data.frame) %>%
  bind_rows %>%
  cbind(DF, .)
#   a   b                                     extra_info animal  color    shape value
# 1 1 aaa         animal=horse;color=orange;shape=circle  horse orange   circle  <NA>
# 2 1 aaa           animal=monkey;shape=square;value=532 monkey   <NA>   square   532
# 3 1 aba animal=horse;color=blue;shape=square;value=321  horse   blue   square   321
# 4 2 abc               animal=dog;color=green;value=678    dog  green     <NA>   678
# 5 2 ccc                      color=pink;shape=triangle   <NA>   pink triangle  <NA>

【讨论】:

    【解决方案3】:
    library(stringr) 
    col_names <- unlist(str_extract_all(DF$extra_info[3], "(?<=^|;)\\w+"))
    DF %>% 
      mutate(animal =  str_extract(extra_info, paste0("(?<=", col_names[1], "=)\\w+")),
             color = str_extract(extra_info, paste0("(?<=", col_names[2], "=)\\w+")), 
             shape = str_extract(extra_info, paste0("(?<=", col_names[3], "=)\\w+")),
             value = str_extract(extra_info, paste0("(?<=", col_names[4], "=)\\w+"))
      a   b                                     extra_info animal  color    shape value
    1 1 aaa         animal=horse;color=orange;shape=circle  horse orange   circle  <NA>
    2 1 aaa           animal=monkey;shape=square;value=532 monkey   <NA>   square   532
    3 1 aba animal=horse;color=blue;shape=square;value=321  horse   blue   square   321
    4 2 abc               animal=dog;color=green;value=678    dog  green     <NA>   678
    5 2 ccc                      color=pink;shape=triangle   <NA>   pink triangle  <NA>
    

    【讨论】:

      【解决方案4】:

      这是使用gsub + eval + str2expression 的基本 R 选项

      v <- DF$extra_info
      p <- gsub(";", ",", gsub("(?<=\\=)(\\w+)", "'\\1'", v, perl = TRUE))
      nms <- unique(unlist(regmatches(v, gregexpr("\\w+(?=\\=)", v, perl = TRUE))))
      q <- unname(Map(function(x) setNames(eval(str2expression(x))[nms], nms), sprintf("c(%s)", p)))
      cbind(DF[c("a","b")], type.convert(data.frame(do.call(rbind, q)), as.is = TRUE))
      

      给了

        a   b animal  color    shape value
      1 1 aaa  horse orange   circle    NA
      2 1 aaa monkey   <NA>   square   532
      3 1 aba  horse   blue   square   321
      4 2 abc    dog  green     <NA>   678
      5 2 ccc   <NA>   pink triangle    NA
      

      【讨论】:

      • 哦,我开始走这条路,但有点卡住并切换到 JSON。 str2expression 是一个不错的实用程序。
      • 这是我最喜欢的解决方案,因为它是基础 R 并且可推广(我确实有比 4 多的列,而且我也不知道所有列)。但是,我无法在我的数据中使用它,因为我没有涵盖我的示例中可能出现的所有可能情况(对此我深表歉意)。在现实生活中,我有带有空格、逗号、-、{} 和十进制数字的字符串。我可以通过用 _ 替换空格和逗号来绕过它们,但我想知道我们是否可以调整 RE 以包含带有 -、{} 和十进制数字的情况。我只是问,因为我对 RE 不是很熟悉,但我正在尝试这样做。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-10
      • 2014-05-30
      • 1970-01-01
      相关资源
      最近更新 更多