使用dplyr获取R数据框基于某个变量的第n行、第一行或最后一行

4

nth(y, -1)返回的是值而不是数据框,而我使用row_number()或slice()没有成功,所以我想知道是否漏掉了什么明显的东西或者应该重新考虑我的方法:

我创建了一个函数:

ranking <- function(df, state, num = "last"){
    ## Setting num for "last" condition to n() gives error
    num <- ifelse(num=="first", 1, ifelse(num=="last", -1, num)) 
    df %>%
        filter(State == state) %>%
        arrange(y, State) %>%
        slice(num)  ## Does not work for -1
}

因此,对于以下的数据框:

df <- data.frame(State=c("TX","TX","TX","MD"),y=c(5,2,3,4))
   State y
1     TX 5
2     TX 2
3     TX 3
4     MD 4

我希望您能为适当的“last”和“nth”函数调用返回以下内容:
ranking(df, "TX", "last")
   State y
1     TX 5
ranking(df, "TX", 2)
   State y
1     TX 3

你能否更新一下你的问题,提供一个更好的第三种情况的例子,因为我不太清楚。也就是说,根据你的函数,'num' 取值 'first'、'last',如果不是这种情况,应该返回什么数据集? - akrun
编辑显示num为第n行。@epi99的答案虽然有效,但我只需要在切片中放置ifelse,以便将last设置为n()即可。 - Daniel Stallworth
2个回答

2
library(dplyr)
ranking <- function(df, state, num = "last"){
  ## Setting num for "last" condition to n() gives error
  df %>%
    filter(State == state) %>%
    arrange(y) %>%
    slice(ifelse(num=="first", 1, ifelse(num=="last", n(), num)) )
}


df <- data.frame(State=c("TX","TX","TX","MD", "MD"),y=c(5,2,3,4,9))
ranking(df, "TX", "last")
ranking(df, "TX", 2)
ranking(df, "MD", "first")

# State     y
#    TX     5
# State     y
#    TX     3
# State     y
#    MD     4  

这适用于第一个和最后一个,但无法获取第n行。在使用top_n并获取最后结果后排序可能有效。 - Daniel Stallworth

2
我们可以略微修改这个函数,并利用即将发布的 dplyr 的开发版本中的新的 quosure 来改进它。请注意,这个版本号为 0.6.0
library(dplyr)
rankingN <- function(dat, stateVal, num){

      state <- quo_name(enquo(stateVal))
      num <- quo_name(enquo(num))
       
      numF <- function(x, val) ifelse(x == "first", 1, ifelse(x == "last", val[1], x))
      
      dat %>%
          filter(State == state) %>%
          arrange(y, State) %>%
          mutate(n = n()) %>%
          slice(numF(num, n)) %>%
          select(-n)
 }

rankingN(df, TX, last)
# A tibble: 1 × 2
#   State     y
#  <fctr> <dbl>
#1     TX     5

rankingN(df, TX, first)    
# A tibble: 1 × 2
#   State     y
#   <fctr> <dbl>
#1     TX     2

rankingN(df, MD, first)
# A tibble: 1 × 2
#    State     y
#    <fctr> <dbl>
#1     MD     4

或者另一种选择是在第一个slice中进行双重切片,然后根据case_when函数的返回值选择第一个或最后一个行。
rankingN1 <- function(dat, stateVal, num){
            
        state <- quo_name(enquo(stateVal))
        num <- quo_name(enquo(num))
        numF <- function(x) case_when(x == "first" ~1L, 
                                      x== "last" ~2L,
                                      TRUE ~NA_integer_)
         dat %>%
            filter(State == state) %>%
            arrange(y, State) %>%
            slice(c(1, n())) %>%
            slice(numF(num))

 }



rankingN1(df, TX, last)
# A tibble: 1 × 2
#   State     y
#  <fctr> <dbl>
#1     TX     5

rankingN1(df, TX, first)
# A tibble: 1 × 2
#   State     y
#  <fctr> <dbl>
#1     TX     2
 rankingN1(df, MD, first)
# A tibble: 1 × 2
#   State     y
#  <fctr> <dbl>
#1     MD     4




              

enquo将输入参数转换为quosure,而quo_name将其转换为字符串以供需要字符串评估的地方使用。我们不使用n()ifelse内部,而是基于它创建一个列,然后根据从该列返回的值slice行。

数据

df <- data.frame(State=c("TX","TX","TX","MD"),y=c(5,2,3,4))

1
使用quosures非常有趣。我认为我需要适应它,但它真的很好。 - FlorianGD
@FlorianGD 是的,这是在函数中传递变量非常方便的方法。以前需要使用interp和其他东西。现在,甚至可以通过传递变量来命名列,而无需使用setNames或rename,就像这里一样。 - akrun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接