【问题标题】:Is there an easy way to tell if many data frames stored in one list contain the same columns?有没有一种简单的方法来判断存储在一个列表中的许多数据帧是否包含相同的列?
【发布时间】:2019-11-11 06:37:06
【问题描述】:

我有一个包含许多数据框的列表:

df1 <- data.frame(A = 1:5, B = 2:6, C = LETTERS[1:5])
df2 <- data.frame(A = 1:5, B = 2:6, C = LETTERS[1:5])
df3 <- data.frame(A = 1:5, C = LETTERS[1:5])
my_list <- list(df1, df2, df3)

我想知道这个列表中的每个数据框是否都包含相同的列(即相同数量的列,都具有相同的名称和相同的顺序)。

我知道您可以使用lapply 在列表中轻松找到数据框的列名:

lapply(my_list, colnames)

有没有办法确定列名是否存在差异?我意识到这是一个涉及成对比较的复杂问题。

【问题讨论】:

  • all(sapply(my_list[-1], function(x) identical(names(x), names(my_list[[1]])))) 也许

标签: r lapply


【解决方案1】:

您可以通过简单地检查每个列名称的计数是否为== length(my_list) 来避免成对比较。这将同时检查您的数据框的dimnames -

lapply(my_list, names) %>%
  unlist() %>% 
  table() %>% 
  all(. == length(my_list))

[1] FALSE

在基础 R 中,即没有 %&gt;% -

all(table(unlist(lapply(my_list, names))) == length(my_list))

[1] FALSE

或明显更优化 -

!any(table(unlist(lapply(my_list, names))) != length(my_list))

【讨论】:

  • 酷!有基本的 R 解决方案吗?
  • 这是基础 R。您只需要 magrittr 即可获得 %&gt;%。如果您只想使用基本 R,您可以将这些步骤分开。
  • all(table(Reduce(c, lapply(my_list, names)))==length(my_list)) 我们真的需要magrittr吗?
  • @Shree 是有道理的。管道确实使溶液更清洁。干杯。 +1
  • unlistReduce(c, ...) 简单
【解决方案2】:

这是另一个base 解决方案Reduce

!is.logical(
  Reduce(function(x,y) if(identical(x,y)) x else FALSE
         , lapply(my_list, names)
         )
)

您还可以使用不同的顺序来说明相同的列

!is.logical(
  Reduce(function(x,y) if(identical(x,y)) x else FALSE
         , lapply(my_list, function(z) sort(names(z)))
         )
)

至于发生了什么,Reduce() 在遍历列表时会累积。首先,评估identical(names_df1, names_df2)。如果它是真的,我们希望它返回相同的向量评估!然后我们可以继续使用它来与列表中的其他成员进行比较。

最后,如果一切都为真,我们会返回一个字符向量。由于您可能想要一个逻辑输出,!is.logical(...) 用于将该字符向量转换为布尔值。

另请参阅此处,因为我受到另一篇文章的启发:

check whether all elements of a list are in equal in R

还有一个我在编辑后看到的类似的:

Test for equality between all members of list

【讨论】:

  • 如果所有数据框列都匹配,则不会返回 TRUE。试试 -df1 &lt;- data.frame(A = 1:5, B = 2:6, C = LETTERS[1:5]); df2 &lt;- data.frame(A = 1:5, B = 2:6, C = LETTERS[1:5]); df3 &lt;- data.frame(A = 1:5, B = 2:6, C = LETTERS[1:5]); my_list &lt;- list(df1, df2, df3);Reduce(identical, lapply(my_list, names)) 这是因为在第一对之后 identical 会尝试将逻辑(TRUEFALSE)与字符向量(名称)进行比较,而字符向量(名称)将始终返回 FALSE
  • 我用两个相同的 DF 对其进行了测试,并且它工作正常(即,list(df1, df2))——应该测试第三个。问题是Reduce()identical(names(df1),names(df2)) 评估为TRUE。显然,TRUE != names(df3)。在找到另一个关于 SO 的问题后,我修改了答案。 stackoverflow.com/questions/18813526/…
【解决方案3】:

这是我的答案:

k <- 1
output <- NULL
for(i in 1:(length(my_list) - 1)) {
 for(j in (i + 1):length(my_list)) {
  output[k] <- identical(colnames(my_list[[i]]), colnames(my_list[[j]]))
  k <- k + 1
 }
}
all(output)

【讨论】:

    【解决方案4】:

    我们可以使用dplyr::bind_rows:

    !any(is.na(dplyr::bind_rows(my_list)))
    
     # [1] FALSE
    

    【讨论】:

    • 不错。尽管绑定所有行本身没有任何价值。我建议!any(is.na(dplyr::bind_rows(lapply(my_list, head)))) 以提高大型数据帧的速度。不过不完全确定。
    • @Shree 取决于数据框的大小(可能是数千行甚至更多),您的建议可能会有所改进。
    • 这假设事先没有任何NAs
    猜你喜欢
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    相关资源
    最近更新 更多