对data.frame进行分组并根据几种条件进行过滤

时间:2019-02-04 09:01:59

标签: r dataframe group-by dplyr unique

我有一个data.frame,其中有几列:

df <- data.frame(sgid = c("sg1","sg1","sg2","sg3"), stid = c(NA,"st1",NA,NA), spid = c(NA,NA,NA,"sp3"), sch = c("sch1","sch1","sch2","sch2"), sst = c(11,11,32,21), snd = c(21,21,46,34),
                 qgid = c("qg1","qg1","qg1","qg1"), qtid = c("qt1","qt1","qt1","qt1"), qpid = c("qp1","qp1","qp1","qp1"), qch = c("qch1","qch1","qch1","qch1"), qst = c(111,111,234,21), qnd = c(211,211,267,34))

data.frame描述了将一个序列(即查询)映射到其他序列(即搜索)的数据库中。

搜索和查询序列由三组ID标识: gidtidpid(分别以s和q前缀进行搜索和查询),并且匹配项的坐标由字符串ch和两个整数:stnd(再次以s和q为前缀分别表示搜索和查询)。

tidpid在搜索的情况下是gid的子集,因此在搜索数据库中它们被保存为单独的行。因此,查询可能会在不同的坐标中“击中” gidtid和/或pid

但是,与df中第1行和第2行的情况一样,查询正在tid内部进行搜索,因此第1行和第2行的搜索和查询坐标是相同的。 / p>

所以我要寻找的是function(可能通过dplyr::groupdplyr::filter),它将根据上述定义返回唯一的df

这是我实现此目标的粗略方法:

tmp.df <- df %>% dplyr::select(-stid,-spid) %>% unique()

uniq.df <- do.call(rbind,lapply(1:nrow(tmp.df),function(i){
  tmp.df.i <- tmp.df[i,,drop=F] %>% dplyr::left_join(df)
  if(!(all(is.na(tmp.df.i$stid) & is.na(tmp.df.i$spid)))){
    tmp.df.i <- tmp.df.i[which(!is.na(tmp.df.i$stid) | !is.na(tmp.df.i$spid)),,drop=F]
  } else{
    tmp.df.i <- tmp.df.i %>%
      dplyr::select(-stid,-spid) %>%
      dplyr::mutate(stid=NA,spid=NA)
  }
  return(tmp.df.i)
}))

#organize the columns of uniq.df to the order of df:
uniq.df <- uniq.df %>% dplyr::select_(.dots = colnames(df))

> uniq.df
   sgid stid spid  sch sst snd qgid qtid qpid  qch qst qnd
2   sg1  st1 <NA> sch1  11  21  qg1  qt1  qp1 qch1 111 211
1   sg2 <NA> <NA> sch2  32  46  qg1  qt1  qp1 qch1 234 267
11  sg3 <NA>  sp3 sch2  21  34  qg1  qt1  qp1 qch1  21  34

正在寻找更优雅的东西。

2 个答案:

答案 0 :(得分:1)

data.table解决方案

样本数据

#    sgid stid spid  sch sst snd qgid qtid qpid  qch qst qnd
# 1:  sg1 <NA> <NA> sch1  11  21  qg1  qt1  qp1 qch1 111 211
# 2:  sg1  st1 <NA> sch1  11  21  qg1  qt1  qp1 qch1 111 211
# 3:  sg2 <NA> <NA> sch2  32  46  qg1  qt1  qp1 qch1 234 267
# 4:  sg3 <NA>  sp3 sch2  21  34  qg1  qt1  qp1 qch1  21  34

代码

library( data.table )
setDT( df )
#get columns you wish to exclude from duplication-check
cols <- c( "stid", "spid" )
#keep non-duplicated rows, based on a subset of df (without the columns in `cols`)
df[ !duplicated( df[, !..cols] ), ][]

#    sgid stid spid  sch sst snd qgid qtid qpid  qch qst qnd
# 1:  sg1 <NA> <NA> sch1  11  21  qg1  qt1  qp1 qch1 111 211
# 2:  sg2 <NA> <NA> sch2  32  46  qg1  qt1  qp1 qch1 234 267
# 3:  sg3 <NA>  sp3 sch2  21  34  qg1  qt1  qp1 qch1  21  34

替代
如果您不想保留重复的第一行,而是最后一行,请使用:

df[ !duplicated( df[, !..cols], fromLast = TRUE ), ][]  #<-- note fromlast-argument!

#    sgid stid spid  sch sst snd qgid qtid qpid  qch qst qnd
# 1:  sg1  st1 <NA> sch1  11  21  qg1  qt1  qp1 qch1 111 211
# 2:  sg2 <NA> <NA> sch2  32  46  qg1  qt1  qp1 qch1 234 267
# 3:  sg3 <NA>  sp3 sch2  21  34  qg1  qt1  qp1 qch1  21  34

答案 1 :(得分:1)

使用dplyr这样的事情怎么样:

cols <- setdiff(names(df), c("stid", "spid"))

df %>% group_by_at(cols) %>% 
  summarise(stid = ifelse(length(unique(stid)) == 1,
                          unique(stid), 
                          unique(stid)[! is.na(unique(stid))]),
            spid = ifelse(length(unique(spid)) == 1,
                          unique(spid), 
                          unique(spid)[! is.na(unique(spid))]))

或者您可以使用Coalesce包中的函数DescTools(甚至定义您自己的函数来选择第一个非NA值):

df %>% group_by_at(cols) %>% 
  summarise(stid = DescTools::Coalesce(stid),
            spid = DescTools::Coalesce(spid))