programing

그룹화된 데이터에서 첫 번째 행과 마지막 행 선택

starjava 2023. 6. 26. 20:46
반응형

그룹화된 데이터에서 첫 번째 행과 마지막 행 선택

질문.

사용.dplyr하나의 문에서 그룹화된 데이터의 상단 및 하단 관측치/행을 선택하려면 어떻게 해야 합니까?

데이터 & 예제

주어진 데이터 프레임:

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3), 
                 stopId=c("a","b","c","a","b","c","a","b","c"), 
                 stopSequence=c(1,2,3,3,1,4,3,1,2))

다음을 사용하여 각 그룹의 상단 및 하단 관측치를 가져올 수 있습니다.slice하지만 두 개의 별개의 진술을 사용합니다.

firstStop <- df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  slice(1) %>%
  ungroup

lastStop <- df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  slice(n()) %>%
  ungroup

이 두 문장을 위쪽과 아래쪽 관측치를 모두 선택하는 하나의 문장으로 결합할 수 있습니까?

더 빠른 방법이 있을 것입니다.

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  filter(row_number()==1 | row_number()==n())

완전성을 위해:합격할 수 있습니다slice지수 벡터:

df %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n()))

이는

  id stopId stopSequence
1  1      a            1
2  1      c            3
3  2      b            1
4  2      c            4
5  3      b            1
6  3      a            3

것은 아니다.dplyr하지만 훨씬 더 직접적으로 사용할 수 있습니다.data.table:

library(data.table)
setDT(df)
df[
  df[order(id, stopSequence), .(rows = .I[c(1L,.N)]), by=id]$rows
]
    #  rows stopId stopSequence
    # 1:  1      a            1
    # 2:  1      c            3
    # 3:  2      b            1
    # 4:  2      c            4
    # 5:  3      b            1
    # 6:  3      a            3

더 자세한 설명:

# 1) get row numbers of first/last observations from each group
#    * basically, we sort the table by id/stopSequence, then,
#      grouping by id, name the row numbers of the first/last
#      observations for each id; since this operation produces
#      a data.table
#    * .I is data.table shorthand for the row number
#    * here, to be maximally explicit, I've named the variable rows
#      as row_num to give other readers of my code a clearer
#      understanding of what operation is producing what variable
first_last = df[order(id, stopSequence), .(rows = .I[c(1L,.N)]), by=id]
idx = first_last$rows

# 2) extract rows by number
df[idx]

자세한 내용은 시작하기 Wiki를 참조하십시오.data.table포함된 기본 사항

사용.which.min그리고.which.max:

library(dplyr, warn.conflicts = F)
df %>% 
  group_by(id) %>% 
  slice(c(which.min(stopSequence), which.max(stopSequence)))

#> # A tibble: 6 x 3
#> # Groups:   id [3]
#>      id stopId stopSequence
#>   <dbl> <fct>         <dbl>
#> 1     1 a                 1
#> 2     1 c                 3
#> 3     2 b                 1
#> 4     2 c                 4
#> 5     3 b                 1
#> 6     3 a                 3

기준이 되는

또한 stopSequence 열 전체를 정렬하는 대신 그룹별로 최소값과 최대값을 찾기 때문에 현재 승인된 답변보다 훨씬 빠릅니다.

# create a 100k times longer data frame
df2 <- bind_rows(replicate(1e5, df, F)) 
bench::mark(
  mm =df2 %>% 
    group_by(id) %>% 
    slice(c(which.min(stopSequence), which.max(stopSequence))),
  jeremy = df2 %>%
    group_by(id) %>%
    arrange(stopSequence) %>%
    filter(row_number()==1 | row_number()==n()))
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 mm           22.6ms     27ms     34.9     14.2MB     21.3
#> 2 jeremy      254.3ms    273ms      3.66    58.4MB     11.0

지정된 질문을 알고 있습니다.dplyr하지만 다른 사람들이 이미 다른 패키지를 사용하여 솔루션을 게시했기 때문에 저는 다른 패키지도 사용해 보기로 결정했습니다.

기본 패키지:

df <- df[with(df, order(id, stopSequence, stopId)), ]
merge(df[!duplicated(df$id), ], 
      df[!duplicated(df$id, fromLast = TRUE), ], 
      all = TRUE)

data.table:

df <-  setDT(df)
df[order(id, stopSequence)][, .SD[c(1,.N)], by=id]

sqldf:

library(sqldf)
min <- sqldf("SELECT id, stopId, min(stopSequence) AS StopSequence
      FROM df GROUP BY id 
      ORDER BY id, StopSequence, stopId")
max <- sqldf("SELECT id, stopId, max(stopSequence) AS StopSequence
      FROM df GROUP BY id 
      ORDER BY id, StopSequence, stopId")
sqldf("SELECT * FROM min
      UNION
      SELECT * FROM max")

하나의 쿼리에서:

sqldf("SELECT * 
        FROM (SELECT id, stopId, min(stopSequence) AS StopSequence
              FROM df GROUP BY id 
              ORDER BY id, StopSequence, stopId)
        UNION
        SELECT *
        FROM (SELECT id, stopId, max(stopSequence) AS StopSequence
              FROM df GROUP BY id 
              ORDER BY id, StopSequence, stopId)")

출력:

  id stopId StopSequence
1  1      a            1
2  1      c            3
3  2      b            1
4  2      c            4
5  3      a            3
6  3      b            1

다음과 같은 것:

library(dplyr)

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3),
                 stopId=c("a","b","c","a","b","c","a","b","c"),
                 stopSequence=c(1,2,3,3,1,4,3,1,2))

first_last <- function(x) {
  bind_rows(slice(x, 1), slice(x, n()))
}

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  do(first_last(.)) %>%
  ungroup

## Source: local data frame [6 x 3]
## 
##   id stopId stopSequence
## 1  1      a            1
## 2  1      c            3
## 3  2      b            1
## 4  2      c            4
## 5  3      b            1
## 6  3      a            3

와 함께do당신은 그룹에서 많은 수의 작업을 수행할 수 있지만 @tftcg의 대답은 이 작업에만 더 적합합니다.

사용.data.table:

# convert to data.table
setDT(df) 
# order, group, filter
df[order(stopSequence)][, .SD[c(1, .N)], by = id]

   id stopId stopSequence
1:  1      a            1
2:  1      c            3
3:  2      b            1
4:  2      c            4
5:  3      b            1
6:  3      a            3

이것은 잘 작동합니다.

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  slice(1,n())

# A tibble: 6 × 3
# Groups:   id [3]
#     id stopId stopSequence
#  <dbl> <chr>         <dbl>
#1     1 a                 1
#2     1 c                 3
#3     2 b                 1
#4     2 c                 4
#5     3 b                 1
#6     3 a                 3

lapply 및 applyr 문을 사용한 또 다른 접근법.임의의 수의 요약 함수를 동일한 문에 적용할 수 있습니다.

lapply(c(first, last), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>% 
bind_rows()

예를 들어 max stopSequence 값이 있는 행에 관심을 갖고 다음을 수행할 수도 있습니다.

lapply(c(first, last, max("stopSequence")), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>%
bind_rows()

다른 기본 R 대안은 첫 번째가 될 것입니다.order타고id그리고.stopSequence,split에 근거한 그들.id그리고 모두에게id첫 번째 인덱스와 마지막 인덱스만 선택하고 이러한 인덱스를 사용하여 데이터 프레임을 부분 집합화합니다.

df[sapply(with(df, split(order(id, stopSequence), id)), function(x) 
                   c(x[1], x[length(x)])), ]


#  id stopId stopSequence
#1  1      a            1
#3  1      c            3
#5  2      b            1
#6  2      c            4
#8  3      b            1
#7  3      a            3

또는 이와 유사한 사용법by

df[unlist(with(df, by(order(id, stopSequence), id, function(x) 
                   c(x[1], x[length(x)])))), ]

언급URL : https://stackoverflow.com/questions/31528981/select-first-and-last-row-from-grouped-data

반응형