【问题标题】:pick rows from every group in data frame only when condition satisfies仅当条件满足时才从数据框中的每个组中选择行
【发布时间】:2015-01-18 23:19:53
【问题描述】:

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

目前,我已经编写了一个迭代解决方案,如下所示,它基本上将每个组读入一个临时数据帧,并在 column2 中的值为 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>=100,] 之类的条件,它只会给我 col2 值大于等于 100 的行。这不是正确的输出,因为我们想要像这样的行(1, 75) 被包含在 75

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

【问题讨论】:

    标签: r rstudio


    【解决方案1】:

    这可以通过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 函数总是返回第一次出现)然后我们只需向下选择匹配后的所有值直到组结束。

    【讨论】:

    • 啊哈哈哈,谢谢!我以为我在之前的评论中写了一些奇怪的东西,我无法弄清楚它是什么!
    【解决方案2】:

    你可以简单地使用:

    # 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)
    

    【讨论】:

    • 不,col2 中的值不是按升序排列的。 100之后的值可能小于100。对不起,如果发布的数据快照给你那种感觉。已编辑问题,请在上方查看。
    • 在这种情况下也有效。我只是在检查数学上大于 100 的值,无论顺序如何,除非我误解了你的问题。你会添加一个上面不起作用的例子吗?
    • @IAMTubby 老实说,我不太同意投反对票,但重要的是你解决了这个问题!
    • 这并没有给出问题中finaldf 显示的输出
    • @ScottWilson 好的,现在我明白你的意思了。删除了原始答案并留下了您的编辑。
    【解决方案3】:
    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)
    

    【讨论】:

    • 谢谢它的工作。我已经调试并理解了它。我们可以删除行范围
    • 是的,对不起,那是我的开发代码。它已被删除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-24
    • 1970-01-01
    • 2015-03-11
    • 2020-01-10
    • 1970-01-01
    • 2021-08-02
    • 2021-08-21
    相关资源
    最近更新 更多