R - 保留所有列,但只选择满足多列标准的行

时间:2015-08-19 15:38:46

标签: r loops select dataframe subset

我的数据框中有许多眼动追踪数据(更长的固定和更短的扫视),每行对应于眼动仪拍摄的样本(参见专栏timestamp)。数据逐步进行,包括修复和囊的计数,它们的持续时间以及每个眼球运动是否击中目标(AOI)。这是一个示例:

subj    timestamp   fix_indx    sac_indx    eventtype   dur AOI_1   AOI_2   AOI_3
MT  409 1   NA  Fix 83  NA  NA  NA
MT  426 1   NA  Fix 83  NA  NA  NA
MT  443 NA  1   Sac 17  NA  NA  NA
MT  459 NA  1   Sac 17  NA  NA  NA
MT  476 2   NA  Fix 100 0   NA  NA
MT  493 2   NA  Fix 100 0   NA  NA
MT  509 2   NA  Fix 100 0   NA  NA
MT  526 NA  2   Sac 10  NA  NA  NA
MT  543 NA  NA  Unclas  20  NA  NA  NA
MT  559 3   NA  Fix 233 1   NA  NA
MT  576 3   NA  Fix 233 1   NA  NA
MT  593 3   NA  Fix 233 1   NA  NA
MT  609 NA  3   Sac 11  1   NA  NA
MT  626 4   NA  Fix 240 NA  1   NA
MT  643 4   NA  Fix 240 NA  1   NA
MT  643 4   NA  Fix 240 NA  1   NA
MT  659 4   NA  Fix 240 NA  1   NA
MT  676 NA  4   Sac 13  NA  NA  0
MT  693 5   NA  Fix 250 NA  NA  1
MT  709 5   NA  Fix 250 NA  NA  1
MT  726 5   NA  Fix 250 NA  NA  1
MT  743 5   NA  Fix 250 NA  NA  1
MT  809 NA  5   Sac 9   NA  NA  0
MT  826 6   NA  Fix 256 NA  NA  0
MT  842 6   NA  Fix 256 NA  NA  0
MT  859 6   NA  Fix 256 NA  NA  0

我希望只选择数据集中每个AOI列中的值为0(=外部目标)和1(=内部目标)的部分,即消除任何AOI列所具有的行{{ 1}} (所有列主要是具有1和0的NA)。我有2个条件符合每个AOI列。我使用了NA函数: subset

但是,我的数据集中有118个不同的AOI列,因此使用df1<-subset(df, + AOI_1 ==0 | AOI_1 ==1 | + AOI_2 ==0 | AOI_2 ==1)意味着最终会得到一个极长的代码字符串。有没有办法更整齐地做到这一点?

我运行AntoniosK示例但是出现错误: subset#创建用户ID列(mydfnew$uid <- 1:nrow(mydfnew)uid > mydfnew %>% + select(uid, starts_with("AOI")) %>% + filter(complete.cases(.)) %>% + select(uid) %>% + inner_join(mydfnew, by="uid")

由于Error in select_(.data, .dots = lazyeval::lazy_dots(...)) : object 'uid' not founduid已确认tail(mydfnew)列已正确创建,因此很奇怪...有人知道这里发生了什么?

3 个答案:

答案 0 :(得分:1)

如果您不知道哪些列是AOI列,或者它们之间是否有列,则最好与complete.cases一起创建AOI列名称的索引,如下所示(在基地R):

AOIcols <- names(df)[grepl("^AOI",names(df))]
df[complete.cases(df[,AOIcols]),]

或来自development version of data.tablena.omit cols参数:

library(data.table) #v1.9.5+
setDT(df)
na.omit(na.omit(df, names(dt)[grepl("^AOI",names(df))]))

在问题中显示的数据中,没有complete.cases语句为真的行。在@AntoniosK给出的小数据集上应用上述代码可得到相同的结果:

  id subj AOI_1 AOI_2
1  1    A     0     1
2  2    A     0     0

另一种dplyr方法:

library(dplyr)
df %>% select(id, starts_with("AOI")) %>% filter(complete.cases(.)) %>% semi_join(df, by="id")

据我所知,数据集包含眼动追踪数据。由于这样的数据集可能非常大,让我们在一个更大的数据集上尝试不同的方法来模拟问题中的数据集:

# load the need packages
library(dplyr)
library(data.table)

# create a datatable
DT <- data.table(id=seq_len(1e5),
                 subj=rep(LETTERS[1:10],each=1e4),
                 dur=rnorm(1e5, mean=70, sd=10),
                 event=sample(c("fix","sac"), 1e5, TRUE, c(3,1)),
                 matrix(sample(c(0,0,1,1,1,0,0,1,0,NA), 1e7, TRUE, prob=c(10,10,10,10,10,10,10,10,10,1)), ncol=100))

names(DT) <- gsub("V","AOI_",names(DT))

# create an identical dataframe
DF <- copy(DT)
setDF(DF)

# the benchmarks
library(rbenchmark)
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"),
          jaap = na.omit(DT, names(DT)[grepl("^AOI",names(DT))]),
          nico = DF[complete.cases(DF[,5:ncol(DF)]),],
          ant1 = DF %>% select(id, starts_with("AOI")) %>% filter(complete.cases(.)) %>% select(id) %>% inner_join(DF, by="id"),
          ant2 = {DF %>% select(id, starts_with("AOI")) %>% filter(complete.cases(.)) %>% select(id) -> IDs
            DF[IDs$id,]
          })

  test elapsed relative
1 jaap   0.467    1.000
4 ant2   1.093    2.340
3 ant1   1.191    2.550
2 nico   1.430    3.062

答案 1 :(得分:0)

假设所有AOI列都在最后,您可以使用以下内容选择它们:

my.data[,7:ncol(my.data)]

现在,您可以使用complete.cases选择那些没有NA的人。

complete.cases(my.data[,7:ncol(my.data)])

答案 2 :(得分:0)

假设您的“AOI”列只有0,1,NA作为值,您可以尝试类似以下(简单)示例。您(提前)唯一需要做的就是为您的行创建一个id并将其作为列:

library(dplyr)

# example dataset
dt = data.frame(id = 1:5,
                subj = "A",
                AOI_1 = c(0,0,1,NA,NA),
                AOI_2 = c(1,0,NA,0,1))

dt

#   id subj AOI_1 AOI_2
# 1  1    A     0     1
# 2  2    A     0     0
# 3  3    A     1    NA
# 4  4    A    NA     0
# 5  5    A    NA     1


dt %>% 
  select(id, starts_with("AOI")) %>%  # keep only id and columns that start with "AOI"
  filter(complete.cases(.)) %>%       # keep only rows that don't have NAs
  select(id) %>%                      # select the ids of those rows
  inner_join(dt, by="id")             # join back info for those rows only

#   id subj AOI_1 AOI_2
# 1  1    A     0     1
# 2  2    A     0     0

逐步运行示例以查看其工作原理,然后进行所需的任何修改。如果您愿意,可以在流程中创建行ID。

最后不需要内部连接,因为您只需在AOI列中找到没有NA的行,只需选择那些,而不是加入。如果你必须处理数百万行,它会更快:

dt %>% 
  select(id, starts_with("AOI")) %>% 
  filter(complete.cases(.)) %>% 
  select(id) -> IDs

dt[IDs$id,]
@paap每次运行它们时,基准测试似乎都会发生变化(有点)。 我将根据您的数据集发布一些示例:

A)

> # the benchmarks
>  system.time(na.omit(dt, names(dt)[grepl("^AOI",names(dt))]))
   user  system elapsed 
      0       0       0 
> 
>  system.time(df[complete.cases(df[,3:ncol(df)]),])
   user  system elapsed 
   0.02    0.00    0.02 
>  
>  system.time(df %>% select(id, subj, starts_with("AOI")) %>% filter(complete.cases(.)) %>% semi_join(df, by="id"))
   user  system elapsed 
   0.02    0.00    0.02 
> 
>  system.time(df %>% select(id, starts_with("AOI")) %>% filter(complete.cases(.)) %>% select(id) %>% inner_join(df, by="id"))
   user  system elapsed 
   0.02    0.00    0.01

B)

    >  system.time(na.omit(dt, names(dt)[grepl("^AOI",names(dt))]))
   user  system elapsed 
   0.01    0.00    0.02 
> 
>  system.time(df[complete.cases(df[,3:ncol(df)]),])
   user  system elapsed 
   0.02    0.00    0.01 
>  
>  system.time(df %>% select(id, subj, starts_with("AOI")) %>% filter(complete.cases(.)) %>% semi_join(df, by="id"))
   user  system elapsed 
   0.02    0.00    0.01 
> 
>  system.time(df %>% select(id, starts_with("AOI")) %>% filter(complete.cases(.)) %>% select(id) %>% inner_join(df, by="id"))
   user  system elapsed 
   0.03    0.00    0.04