【问题标题】:New columns using str_extract_all in a tidyverse way以整洁的方式使用 str_extract_all 的新列
【发布时间】:2019-03-10 14:36:03
【问题描述】:

问题:

我想创建一个与长字符串列匹配的字符串的数据框。在数据框“d”中,流派列是具有不同流派的字符串列。出现问题是因为每行匹配的项目数量不同。

library(tidyverse)

d <- structure(list(genres = c("[{'id': 35, 'name': 'Comedy'}]", "[{'id': 35, 'name': 'Comedy'}, {'id': 18, 'name': 'Drama'}, {'id': 10751, 'name': 'Family'}, {'id': 10749, 'name': 'Romance'}]", 
                               "[{'id': 16, 'name': 'Animation'}, {'id': 12, 'name': 'Adventure'}, {'id': 10751, 'name': 'Family'}]"
), budget = c(1.4e+07, 4e+07, 8e+06)), row.names = c(NA, -3L), class = c("tbl_df", 
                                                                         "tbl", "data.frame"))
d
#> # A tibble: 3 x 2
#>   genres                                                             budget
#>   <chr>                                                               <dbl>
#> 1 [{'id': 35, 'name': 'Comedy'}]                                     1.40e7
#> 2 [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'name': 'Drama'}, {'id…  4.00e7
#> 3 [{'id': 16, 'name': 'Animation'}, {'id': 12, 'name': 'Adventure'…  8.00e6

我的不雅方式

我找到了一个工作流程来解决这个问题,这有点不雅。首先,提取与str_extract_allsimplify = T 的所有匹配项,这将返回一个数据框。然后用列名创建一个向量字符串,分配给提取的数据框,最后使用bind_cols


foo <- str_extract_all(d$genres, '(?<=\'name\':\\s\')([^\']*)', simplify = T)
colnames(foo) <- paste0("genre_", 1:ncol(foo), "_extract")
foo <- foo %>% as_tibble()
foo_final <- bind_cols(d, foo)

foo_final 
#> # A tibble: 3 x 6
#>   genres budget genre_1_extract genre_2_extract genre_3_extract
#>   <chr>   <dbl> <chr>           <chr>           <chr>          
#> 1 [{'id… 1.40e7 Comedy          ""              ""             
#> 2 [{'id… 4.00e7 Comedy          Drama           Family         
#> 3 [{'id… 8.00e6 Animation       Adventure       Family         
#> # … with 1 more variable: genre_4_extract <chr>

reprex package (v0.2.1) 于 2019-03-10 创建

我想知道是否有办法在管道运算符、mutate 或 map_df 中以一种 tidyverse 的方式实现它...我确信有更好的方法来做到这一点。

【问题讨论】:

  • 你为什么不使用jsonlite包像JSON一样处理它们,这样使用键值对会容易得多
  • 这是一个通用数据框,我有更多没有 json 模式的列,但有同样的问题。
  • @TitoSanz 您能否在示例数据集中包含“没有 json 模式但存在相同问题”的列?

标签: r dplyr purrr stringr


【解决方案1】:

回答原问题

我不确定这是否比您的解决方案更优雅,但它是纯管道:

# Use `str_count` to determine the maximum number of genres associated with any
# single row, and create one column for each genre.  Stored here for
# convenience, since it's needed twice below.
genre.col.names = paste("genre",
                        1:(max(str_count(d$genres, "\\}, \\{")) + 1),
                        sep = "_")
# Use `separate` to split the `genres` column into one column for each genre.
# Then use `mutate` and some regular expressions to populate each column with
# just the genre name and no other material (quotes, brackets, etc.).
d %>%
  separate(genres,
           into = genre.col.names,
           sep = "\\}, \\{") %>%
  mutate_(.dots = setNames(paste("gsub(\".*'name': '([A-Za-z]+)'.*\", \"\\\\1\", ",
                                 genre.col.names,
                                 ")",
                                 sep = ""),
                           genre.col.names))

把数据变成长格式

在我看来,更整洁的格式会很长:每种类型的电影每部电影一行。 (我假设每张唱片都是一部电影......?)如果这种格式适合你,这里有一种方法可以得到它(我相信还有更简单的方法):

library(fuzzyjoin)
# Get all the genres appearing in any record and put them in a dataframe.
all.genres = data.frame(
  genre.name = unique(unlist(str_extract_all(d$genres, '(?<=\'name\':\\s\')([^\']*)')))
)
# Left join the main data frame to the genre list using a regular expression.
d %>%
  rownames_to_column() %>%
  regex_left_join(all.genres, by = c("genres" = "genre.name")) %>%
  select(rowname, genre.name)

标志列

如果你真的需要宽格式的数据,每个可能的流派一列怎么样,值为TRUE/FALSE?当然,如果有很多可能的类型,这将变得笨拙。但好处是,例如关于这部电影是否是喜剧的信息总是在同一列中。 (对于genre_1genre_2 等,您必须检查每一列,以确定该电影是否为喜剧。)这里有一个方法:

# Using the list of unique genres that we created earlier, add one column for
# each genre that contains a flag for whether the record is an example of that
# genre.
d %>%
  mutate_(.dots = setNames(paste("grepl(\"", all.genres$genre.name, "\", genres)",
                                 sep = ""),
                           all.genres$genre.name))

【讨论】:

  • 感谢您的回答!很有启发性,我学到了很多。您介意详细说明最后一个选项 FLAG COLUMNS 吗?这似乎是处理后验分析的最佳方法。但我无法了解它是如何工作的。再次提前谢谢!!!
  • 这个想法是为每个流派创建一个列,它将标记该流派的所有记录。因此,Comedy 列将包含 TRUE 用于喜剧,FALSE 用于所有其他记录。我喜欢它,因为它可以更轻松地找到特定类型(例如,喜剧的平均预算为 mean(d$budget[d$Comedy])。但如果完整数据集有很多类型,表格会变得非常宽。创建动态列也不容易dplyr 中的名称,所以我们必须做类似this 的事情,而且代码不是最漂亮的。
猜你喜欢
  • 1970-01-01
  • 2023-03-07
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 2017-12-22
  • 1970-01-01
  • 2018-01-10
  • 2019-08-12
相关资源
最近更新 更多