仅在条件满足时才从数据框中的每个组中选择行

时间:2015-01-18 23:19:54

标签: r rstudio

我有一个由两列组成的数据框。假设它已经按第一列分组。对于每个组,我只需要在第2列中的值为100时从此数据框中选择行。是否有最佳方法可以执行此操作?

目前,我编写了一个如下的迭代解决方案,它基本上将每个组读入一个临时数据框,并在第二列中的值为100时将行选入一个名为finaldf的最终数据框。

编辑:请注意col2中的数据不是按升序排列的,所以我不能使用诸如mydf $ col2>之类的条件。 100. 100只是一个占位符,它说,从那时起我应该开始挑选行。

myfun = function()
{
  col1 = c(1,1,1,2,2,3,3,3,3,3)
  col2 = c(80,100,75,90,100,75,100,12,14,150)
  mydf = data.frame(col1,col2)
  finaldf = NULL;

  uniquecol1values = unique(col1)
  for(i in 1:length(uniquecol1values))
  {
    tempdf = mydf[which(mydf$col1 == uniquecol1values[i]),]
    print(tempdf)

    startincluding = 0;
    for(j in 1:nrow(tempdf))
    {
      if(tempdf[j,2] == 100)
      {
        startincluding = 1;
      }

      if(startincluding == 1)
      {
        finaldf = rbind(finaldf,tempdf[j,])
      }
    }
  }

  print(finaldf)
}

> mydf
   col1 col2
1     1   80
2     1  100
3     1   75
4     2   90
5     2  100
6     3   75
7     3  100
8     3   12
9     3   14
10    3  150

> finaldf
   col1 col2
2     1  100
3     1   75
5     2  100
7     3  100
8     3   12
9     3   14
10    3  150

编辑:如果我应用mydf [mydf $ col2&gt; = 100,]这样的条件,它只给我col2值大于等于100的行。这不是正确的输出,因为我们想要像(1,75)要包括在75 <100,因为我们已经看到组1的值为100。

> mydf[mydf$col2>=100,]
   col1 col2
2     1  100
5     2  100
7     3  100
10    3  150

3 个答案:

答案 0 :(得分:4)

使用data.table包可以轻松完成此操作而不需要for / lapply个循环

library(data.table)
setDT(mydf)[, .SD[which(match(col2, 100) == 1):.N], col1]
#    col1 col2
# 1:    1  100
# 2:    1   75
# 3:    2  100
# 4:    3  100
# 5:    3   12
# 6:    3   14
# 7:    3  150

<强>解释: 这个想法很简单,我们每组使用match来查找100的第一个匹配项(因为match函数总是返回第一个匹配项)然后我们只需在匹配后选择所有值向下直到小组结束。

答案 1 :(得分:3)

您可以简单地使用:

# Split the data frame by col1
mydf.split <- split(mydf, mydf$col1)

# Apply to each group of elements (defined by col1)
# a function
res <- lapply(mydf.split, function(x)
  {
  # Find the position of the first element >= 100
  pos=which(x$col2>=100)[[1]]
  # Get all of the elements afterwards
  x[pos:nrow(x),]
  })

# Convert back to a df
res <- do.call("rbind", res)

答案 2 :(得分:0)

bycol <- split(mydf,as.factor(mydf$col1))
newdf <- data.frame()
for (i in 1:length(bycol)) {
    col <- bycol[[i]][2]
    lcol <- col >= 100
    start <- min(which(lcol == TRUE))
    fin <- nrow(col)
    newdf <- rbind(newdf, bycol[[i]][start:fin,])
}

这显示了OP首先要求的内容,即:

> newdf
   col1 col2
2     1  100
3     1   75
5     2  100
7     3  100
8     3   12
9     3   14
10    3  150

使用来自@nico的想法,这个算法的更紧凑的实现将是:

bycol <- split(mydf,as.factor(mydf$col1))
temp <- lapply(bycol, function(x) {
    col <- x[2]
    lcol <- col >= 100
    x[min(which(lcol == TRUE)) :  nrow(col),]
})
newdf <- do.call("rbind", temp)