【问题标题】:Combine single unique variable in otherwise identical rows在其他相同的行中合并单个唯一变量
【发布时间】:2017-12-14 19:44:29
【问题描述】:

我正在从 API 跟踪项目所花费的时间中提取数据,该项目的变量包括时间、客户、项目和 - 可能 - 描述项目的多个标签。但是,当我提取数据时,具有多个标签的条目会被复制到其他相同的行中,每行只有一个唯一的标签,如下所示:

 duration client project    tag
       60      A       X  first
       45      B       Y second
       45      B       Y  third
       30      C       Z fourth

如何在组合标签时删除重复的行?我的想法是这样的:

A)
  duration client project    tags
1       60      A       X   first
2       45      B       Y  second, third
3       30      C       Z  fourth

或者这个:

B)
  duration client project    tag1   tag2
1       60      A       X   first     NA
2       45      B       Y  second  third
3       30      C       Z  fourth     NA

我也很感激关于哪种建议安排(A 或 B)最适合快速总结项目所花费的时间量的建议,例如标签“第一”和“第三”(例如 105 分钟)?

这是示例数据框:

df <- data.frame(
  duration = c(60, 45, 45, 30),
  client = c("A", "B", "B", "C"),
  project = c("X", "Y", "Y", "Z"),
  tag = c("first", "second", "third", "fourth")
  )

我感谢任何建议(我觉得这对 dplyr/tidyr 来说应该不会太难,但还不能完全正确)。谢谢!

【问题讨论】:

  • 时长数据应该如何处理?
  • 除标签外的所有内容都是行之间的完全重复,应替换/删除,例如客户 B/项目 X 的​​持续时间为 45 分钟,而不是 90 分钟。这是否回答了您的问题,还是我误解了?
  • 如果标签数量有限,可以reshape df获取标签为列名require(tidyr) spread(df, key = tag, value = project)
  • 不同的建议:制作两张表projDF = unique(df[c("client", "project", "duration")]); tagDF = df[c("project", "tag")] 或任何适当的列选择。 (然后您可以使用 left_join 或类似的方法来组合表格以进行任何分析。)

标签: r dplyr tidyr


【解决方案1】:

我们可以将dplyr 用于您的输出A。group_by_at(vars(-tag)) 是一种指定分组变量应该是除tag 之外的所有列的方法,因为您希望所有其他列在行中完全相同。

library(dplyr)

df2 <- df %>%
  group_by_at(vars(-tag)) %>%
  summarise(tags = toString(tag)) %>%
  ungroup()
df2
# # A tibble: 3 x 4
#   duration client project          tags
#      <dbl> <fctr>  <fctr>         <chr>
# 1       30      C       Z        fourth
# 2       45      B       Y second, third
# 3       60      A       X         first

然后我们可以将splitstackshape 用于您的输出B

library(splitstackshape)
df3 <- df2 %>% cSplit(splitCols = "tags")
df3
#    duration client project tags_1 tags_2
# 1:       30      C       Z fourth     NA
# 2:       45      B       Y second  third
# 3:       60      A       X  first     NA

【讨论】:

  • 您可以留在 tidyverse 中以使用:df2 %&gt;% separate(tags,c("tags1","tags2" )) 生成输出 B,但是您必须专门指定标签列名称,或者先进行额外的步骤来计算它们。
  • @Johnpaul 感谢您的 cmets。我曾想过使用tidyr包中的separateextract,但后来我意识到每行中的标签数量可能不同,所以cSplit更灵活。
  • 感谢@www,这正是我所希望的那种优雅的解决方案。还要感谢@Johnpaul 为输出 B 提供 tidyverse 解决方案——我认为我不会有 >5 个标签,所以我可以使用你的 separate 行,而忽略“XX 位置的值太少”的警告结果
【解决方案2】:

您的解决方案 A 对我来说看起来不错。我会这样做:-

library(data.table)

setDT(df)
df <- df[, tags := paste0(tag, collapse = ", "), by = project]
df[, tag := NULL]
df <- unique(df)

它会给你A方法中想要的结果:

duration client project   tags
1:  60      A       X     first
2:  45      B       Y     second, third
3:  30      C       Z     fourth

【讨论】:

  • 谢谢!这也可以解决问题,但类似于我对 ge.org 解决方案的评论,我真的希望不必为此步骤使用额外的包(以及相关的不熟悉的语法),因为我使用 dplyr/tidyr其余的操作。有什么办法可以用 dplyr/tidyr 代替?
  • df %&gt;% group_by(project, client, duration) %&gt;% summarise(tags = paste0(tag, collapse = ", "))
  • @JMDR,上面的代码可以使用dplyr 来解决问题。它会给出与 A 相同的输出。
  • 仅供参考,您不必总是在 data.table 中使用 :=,如果确实需要,您可以使用 df[, .(tags = paste0(tag, collapse = ", ")), by = project] 或将所有相关列添加到 by=
  • @Frank 我想在现有的data.table 中添加标签列。这就是我使用:= 的原因。我知道我可以使用df[, .(tags = paste0(tag, collapse = ", ")), by = c("project", "client", "duration")] 来实现这一点。但是,总是在data.table 中使用:= 有什么问题。
【解决方案3】:

我会用 plyr 作为 A)

library(plyr)
df2 <- ddply(df, .(client), function(df){
  tags<- paste(df$tag, collapse=",")
  df$tag <- tags
  df[1,]
})

【讨论】:

  • 谢谢!这就是诀窍,但我真的宁愿不使用 plyr,因为它与我经常使用的 dplyr 冲突太多。有什么办法可以用 dplyr/tidyr 代替?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
相关资源
最近更新 更多