【问题标题】:identified distinct arrangement of values by groups in data.frame在 data.frame 中按组识别不同的值排列
【发布时间】:2020-12-17 04:25:04
【问题描述】:

我有一个大型数据框,它的主要组织是一行,其中包含长度相同的组(在玩具示例 3 中)。

df <- data.frame(groups = c("gr1","gr1","gr1","gr2","gr2","gr2","gr3","gr3","gr3"),
               no = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
               colA = c("a", "b", "c", "a", "b", "c", "a", "b", "c"),
               colB = c("a", "b", "c", "X_", "b", "c", "a", "b", "c"),
               colC = c("a", "b", "c", "X_", "b", "c", "c", "b", "a"))

df

> df
>   groups no colA colB colC
> 1    gr1  1    a    a    a
> 2    gr1  2    b    b    b
> 3    gr1  3    c    c    c
> 4    gr2  1    a   X_   X_
> 5    gr2  2    b    b    b
> 6    gr2  3    c    c    c
> 7    gr3  1    a    a    c
> 8    gr3  2    b    b    b
> 9    gr3  3    c    c    a

我想为每一列确定哪个组是唯一值排列的第一个示例。所以对于 colA 它应该返回 (T, F, F) 因为所有三个组都是相同的,所以只有第一组是第一个唯一的。对于 colB 它应该返回 (T, T, F) 因为有两个不同的组并且只有第 3 个与第 1 个相同。对于 colC,它应该是 (T, T, T),因为项目的顺序很重要。

所以最终的输出可能是这样的矩阵

       colA  colB  colC
> gr1     T     T     T
> gr2     F     T     T
> gr3     F     F     T

我想我可以通过将数据框分解成组和 colA/B/B 对来解决这个问题,确定哪些是相同的,将结果存储在一个向量中,然后重新组装整个交易。但是我看到了大量的 for 循环,并且很难考虑如何对其进行矢量化。我一直在使用 dplyr,但我还没有看到它有什么帮助。

也许有一种不错的方法可以根据组取消堆叠每个列,然后对新(和更短)列的相关子集进行比较?


编辑添加:

也许 group_by %>% summarise 是解决这个问题的一种方法。如果摘要基本上可以将每列组中的所有值连接成一个非常长的字符串,那么我可以看到每个组中哪些是不同的?


第二次编辑:

我做到了:

d1 <- df %>% group_by(groups) %>% summarise(colB = paste(unique(colB), collapse = ', ')) %>% distinct(colB)

放出来

> # A tibble: 2 x 1
>   colB    
>   <chr>   
> 1 a, b, c 
> 2 X_, b, c

它标识了不同的组,但我现在必须弄清楚如何将它与其余的完整列进行比较以获得每个组的 T/F。

【问题讨论】:

  • 很棒的收获。谢谢。

标签: r dataframe


【解决方案1】:

这是一个基本的 R 方法:

cols <- grep('col', names(df))
cbind(unique(df[1]), sapply(df[cols], function(x) 
      !duplicated(by(x, df$groups, paste0, collapse = '-'))))

#  groups  colA  colB colC
#1    gr1  TRUE  TRUE TRUE
#4    gr2 FALSE  TRUE TRUE
#7    gr3 FALSE FALSE TRUE

【讨论】:

  • 太棒了,@Ronak。一如既往的快。还有另一个很棒的学习经历。我将开始解包嵌套代码的每个部分,以了解每个步骤实际执行的操作。
【解决方案2】:

你的总结思路很准确:

df %>%
  group_by(groups) %>%
  summarize(across(starts_with("col"), paste, collapse = ""), .groups = "drop") %>%
  mutate(across(starts_with("col"), ~!duplicated(.)))
# # A tibble: 3 x 4
#   groups colA  colB  colC 
#   <chr>  <lgl> <lgl> <lgl>
# 1 gr1    TRUE  TRUE  TRUE 
# 2 gr2    FALSE TRUE  TRUE 
# 3 gr3    FALSE FALSE TRUE 

【讨论】:

  • 你们能多快解决这个问题真是太神奇了。我仍然需要一段时间才能理解这行代码。我希望你不介意我给“base R”的答案打勾,因为我认为如果其他人偶然发现这个问题,它可能是首选。
  • 是的,不用担心 - 接受哪个答案完全取决于您,Ronak 一如既往地出色。如果它可以帮助您理解代码,那么所有三个答案在方法上都是相同的,只是使用 base/dplyr/data.table。我们都 paste 一起列(虽然 toString() 保存需要指定 collapse 参数来粘贴 - 它默认这样做),然后我们都使用 !duplicated() 来获得结果。
  • !duplicated() 确实是关键。在uniquedistinct 之后,它又是一个出色的工具。不幸的是,我还没有到那里。我花了大约 20 分钟才弄清楚如何使用带有我想要比较的列名的向量替换 starts_with()(我终于想通了)。毕竟,我得到了一个完整的空表——我认为这是因为所有列都是因子或整数,而不是字符。所以我必须在某个地方转换成角色。
  • all_of() 使用带引号的列名,c() 使用不带引号的列名。所以你可以使用all_of("colA", "colB")c(colA, colB)
  • 列名在一个向量中,向量中的每个元素都是一个字符串。所以 all_of(col_names) 给了我正确的结果。
【解决方案3】:

用“data.table”你可以试试:

library(data.table)

cols <- c("colA", "colB", "colC")
fun <- function(x) !duplicated(x)
as.data.table(df)[, lapply(.SD, toString), groups, .SDcols = cols][
  , (cols) := lapply(.SD, fun), .SDcols = cols][]
#    groups  colA  colB colC
# 1:    gr1  TRUE  TRUE TRUE
# 2:    gr2 FALSE  TRUE TRUE
# 3:    gr3 FALSE FALSE TRUE

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多