【问题标题】:data.table: split columns, then wide- to long-format [duplicate]data.table:拆分列,然后从宽格式到长格式[重复]
【发布时间】:2021-09-18 09:12:16
【问题描述】:

我有以下格式的数据(摘录):

# install.packages("data.table")
# install.packages("tidyverse")

library("data.table")
library("tidyverse")

dt <- data.table(
 date = lubridate::as_date(c("2021-01-01", "2021-01-02", "2021-01-03", "2021-01-04")),
 location = c("Westpark", "Northpark", "Estpark", "Southpark"),
 'receiver_a: max' = c(20, 30, 25, 15),
 'receiver_a: min' = c(10, 15, 20, 5),
 'receiver_b: max' = c(15, 45, 10, 50),
 'receiver_b: min' = c(15, 45, 10, 50)
)
> dt
        date  location receiver_a: max receiver_a: min receiver_b: max receiver_b: min
1: 2021-01-01  Westpark              20              10              15              15
2: 2021-01-02 Northpark              30              15              45              45
3: 2021-01-03   Estpark              25              20              10              10
4: 2021-01-04 Southpark              15               5              50              50

我现在想拆分列名中以receiver_...开头的所有列,并将它们转换为长格式:

  • 将列名中带有receiver_...的所有列拆分为新列 列receivermaxmin。以receiver... 开头的“旧”列名称中: 之后的所有内容都用于新列名称,此处为maxmin
  • 新栏目 receiver 包含例如receiver_areceiver_b 等作为值。新列 maxmin 包含各自的数值。

这可以用tidyr::pivot_longer()实现:

# dt <-  dt %>% 
dt %>%
  tidyr::pivot_longer(
    cols         = dplyr::contains(":"),
    names_to     = c("receiver", ".value"),
    names_sep    = ": ", 
    names_repair = "minimal"
  )
# A tibble: 8 x 5
  date       location  receiver     max   min
  <date>     <chr>     <chr>      <dbl> <dbl>
1 2021-01-01 Westpark  receiver_a    20    10
2 2021-01-01 Westpark  receiver_b    15    15
3 2021-01-02 Northpark receiver_a    30    15
4 2021-01-02 Northpark receiver_b    45    45
5 2021-01-03 Estpark   receiver_a    25    20
6 2021-01-03 Estpark   receiver_b    10    10
7 2021-01-04 Southpark receiver_a    15     5
8 2021-01-04 Southpark receiver_b    50    50
  • cols = dplyr::contains(":"):选择名称中带有:的所有列,例如receiver_a: max
  • names_to = c("receiver", ".value"):将选定的列拆分为receiver.value.value 表示名称的组成部分定义了包含单元格值的列的名称
  • names_sep = ": " 如果names_to 包含多个值,则这些参数控制列名的拆分方式,此处为: (冒号后的空格)

我的问题:这也可以通过(更快的)data.table 解决方案(例如使用melt())来完成吗?

【问题讨论】:

  • data.table development version (1.14.1) melt 中有一个新的measure 参数。因此,您可以使用melt(dt, measure.vars = measure(rec, value.name, sep = ": "))。我试图解释它在another post 中是如何工作的。该帖子最初涉及pre-1.14.1 解决方法,用于类似于您的设置(如下面的答案中使用的)。
  • @Henrik:关于data.table 的进一步发展的绝佳提示,使用来自melt 的新参数measure:这将使此过程在未来更容易和更精简。
  • 确实如此。但你不必等到未来。您可以安装开发版本(如果您的部门没有阻止)并立即尝试;)祝您好运!
  • 已经执行并成功测试!

标签: r data.table tidyverse


【解决方案1】:

我们可以在data.table 中使用measurepatterns

library(data.table)
nm1 <- unique(sub(":.*", "", names(dt)[-(1:2)]))
melt(dt, measure = patterns("max", "min"),
    value.name = c("max", "min"), variable.name = "receiver")[, 
     receiver := nm1[receiver]][]

-输出

         date  location   receiver max min
1: 2021-01-01  Westpark receiver_a  20  10
2: 2021-01-02 Northpark receiver_a  30  15
3: 2021-01-03   Estpark receiver_a  25  20
4: 2021-01-04 Southpark receiver_a  15   5
5: 2021-01-01  Westpark receiver_b  15  15
6: 2021-01-02 Northpark receiver_b  45  45
7: 2021-01-03   Estpark receiver_b  10  10
8: 2021-01-04 Southpark receiver_b  50  50

【讨论】:

  • 另一个问题:使用您的解决方案,我们是否需要知道value.name = c("max", "min") 中新列名的确切名称?使用pivot_longer()melt 的新measure 参数(版本1.14.1,请参阅@Henrik)不需要指定新列名(receiver 除外)。
  • @MarkBlack 是的。但是,如果您可以使用开发版本,那么您可能会有这个优势。很多时候,开发版本会有一些错误
  • 我现在将使用您的解决方案,因为我不喜欢在“生产”环境中使用开发版本。不过,我会在正式版发布后立即实施@Henrik 的解决方案。
  • @MarkBlack 我建议使用 renv 在 Rstudio 中创建一个新项目,然后安装开发版本并对其进行测试,而不是安装在整个系统上。这样,您可以测试不同的版本。或者可以旋转一个 docker 容器并在其上安装开发版本。
猜你喜欢
  • 1970-01-01
  • 2015-11-24
  • 2020-05-18
  • 2015-08-04
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多