如何根据列名称拆分数据帧列表?

时间:2018-08-03 09:38:04

标签: r list for-loop split conditional

我有一个600多个数据帧的列表,这些数据帧没有完全相同的结构(列名,列的顺序和变量的类型)。我需要做的是识别出那些数据框中哪些没有所需的结构并对其进行修改,以便我可以将所有数据用于不同的目的(汇总,分析等)。

我试图根据所需的名称和列顺序从主列表创建两个列表。为此,我尝试执行以下操作:

# some random dfs for the example
v1 <- c(1:15)
v2 <- c(20:34)
v3 <- c("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o")
v3b <- c("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o")

df1 <- data.frame(v1, v2, v3)
df2 <- data.frame(v1, v2, v3)
df3 <- data.frame(v1, v2, v3b)

mylist <- list(df1, df2, df3)

names <- colnames(mylist[[1]]) #remember I have over 600 dfs in the original list
listA <- list()
listB <- list()

#I suppose this piece of code should work    
colnames(mylist[[1]]) == names
colnames(mylist[[2]]) == names
colnames(mylist[[3]]) == names

for (k in 1:length(mylist)){
  if(colnames(mylist[[k]]) == names){
    listA[[k]] <- mylist[[k]]
  }else{
    listB[[k]] <- mylist[[k]]
  }
}

现在的问题是,带有条件语句的循环会生成一个包含所有数据帧的列表和另一个空列表。它还会生成以下警告:

1:如果if(colnames(mylist [[k]])==名称){:   条件的长度> 1,并且只会使用第一个元素

我已经阅读并在堆栈流中查找了大量内容来解决此问题,但我感到束手无策...

有人知道代码有什么问题吗? 更重要的是,这是否是一种适当的方法来根据姓氏拆分我的数据帧列表,或者有更好的名字?

4 个答案:

答案 0 :(得分:1)

您可以使用identical而不是==来更正方法,如果您不希望使用k元素,也应该修复NULL索引:

for (k in 1:length(mylist)){
  if(identical(colnames(mylist[[k]]), names)){
    listA[[length(listA)+1]] <- mylist[[k]]
  }else{
    listB[[length(listB)+1]] <- mylist[[k]]
  }
}

我宁愿使用split,这是一个建议:

split(mylist,sapply(mylist,function(x) identical(colnames(x),names)))

$`FALSE`
$`FALSE`[[1]]
   v1 v2 v3b
1   1 20   a
2   2 21   b
3   3 22   c
4   4 23   d
5   5 24   e
6   6 25   f
7   7 26   g
8   8 27   h
9   9 28   i
10 10 29   j
11 11 30   k
12 12 31   l
13 13 32   m
14 14 33   n
15 15 34   o


$`TRUE`
$`TRUE`[[1]]
   v1 v2 v3
1   1 20  a
2   2 21  b
3   3 22  c
4   4 23  d
5   5 24  e
6   6 25  f
7   7 26  g
8   8 27  h
9   9 28  i
10 10 29  j
11 11 30  k
12 12 31  l
13 13 32  m
14 14 33  n
15 15 34  o

$`TRUE`[[2]]
   v1 v2 v3
1   1 20  a
2   2 21  b
3   3 22  c
4   4 23  d
5   5 24  e
6   6 25  f
7   7 26  g
8   8 27  h
9   9 28  i
10 10 29  j
11 11 30  k
12 12 31  l
13 13 32  m
14 14 33  n
15 15 34  o

答案 1 :(得分:1)

通过将名称与match()匹配,然后使用split()来创建您要获得的组。

f <- sapply(mylist, function(x) length(na.omit(match(names(x), names))))
listNew <- setNames(split(mylist, f), c("listB", "listA"))

屈服

> str(listNew)
List of 2
 $ listB:List of 1
  ..$ :'data.frame':    15 obs. of  3 variables:
  .. ..$ v1 : int [1:15] 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ v2 : int [1:15] 20 21 22 23 24 25 26 27 28 29 ...
  .. ..$ v3b: Factor w/ 15 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ listA:List of 2
  ..$ :'data.frame':    15 obs. of  3 variables:
  .. ..$ v1: int [1:15] 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ v2: int [1:15] 20 21 22 23 24 25 26 27 28 29 ...
  .. ..$ v3: Factor w/ 15 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...
  ..$ :'data.frame':    15 obs. of  3 variables:
  .. ..$ v1: int [1:15] 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ v2: int [1:15] 20 21 22 23 24 25 26 27 28 29 ...
  .. ..$ v3: Factor w/ 15 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10 ...

答案 2 :(得分:0)

如果我正确理解了您想要的内容,则以下代码将原始列表分为两个列表:

  1. listA具有名称等于mylist[[1]]的所有数据框;
  2. listB具有所有其他数据框。

它使用*apply函数而不是显式的for循环。

nms <- lapply(mylist, names)
inx <- sapply(nms[-1], function(nm) all(nm == nms[[1]]))
inx <- c(TRUE, inx)
listA <- mylist[inx]
listB <- mylist[!inx]

答案 3 :(得分:0)

这是一个tidyverse解决方案,定义时使用mylistnames

library(tidyverse)

listA <- 
 mylist %>%
 keep(~ all(names(.) %in% names)

listB <-
 mylist %>%
 discard(~ all(names(.) %in% names)