programing

그룹별 첫 번째 행 선택

testmans 2023. 6. 14. 21:43
반응형

그룹별 첫 번째 행 선택

이와 같은 데이터 프레임에서

test <- data.frame('id'= rep(1:5,2), 'string'= LETTERS[1:10])
test <- test[order(test$id), ]
rownames(test) <- 1:10

> test
    id string
 1   1      A
 2   1      F
 3   2      B
 4   2      G
 5   3      C
 6   3      H
 7   4      D
 8   4      I
 9   5      E
 10  5      J

각 ID/문자열 쌍의 첫 번째 행으로 새 행을 만들고 싶습니다.만약 sqldf가 그 안에서 R 코드를 받아들인다면, 쿼리는 다음과 같이 될 수 있습니다.

res <- sqldf("select id, min(rownames(test)), string 
              from test 
              group by id, string")

> res
    id string
 1   1      A
 3   2      B
 5   3      C
 7   4      D
 9   5      E

다음과 같은 새 열을 만드는 것 외에 해결책이 있습니까?

test$row <- rownames(test)

min(행)으로 동일한 sqldf 쿼리를 실행하시겠습니까?

사용할 수 있습니다.duplicated이 일을 아주 빨리 하기 위해서.

test[!duplicated(test$id),]

빠른 속도를 위한 벤치마크:

ju <- function() test[!duplicated(test$id),]
gs1 <- function() do.call(rbind, lapply(split(test, test$id), head, 1))
gs2 <- function() do.call(rbind, lapply(split(test, test$id), `[`, 1, ))
jply <- function() ddply(test,.(id),function(x) head(x,1))
jdt <- function() {
  testd <- as.data.table(test)
  setkey(testd,id)
  # Initial solution (slow)
  # testd[,lapply(.SD,function(x) head(x,1)),by = key(testd)]
  # Faster options :
  testd[!duplicated(id)]               # (1)
  # testd[, .SD[1L], by=key(testd)]    # (2)
  # testd[J(unique(id)),mult="first"]  # (3)
  # testd[ testd[,.I[1L],by=id] ]      # (4) needs v1.8.3. Allows 2nd, 3rd etc
}

library(plyr)
library(data.table)
library(rbenchmark)

# sample data
set.seed(21)
test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE))
test <- test[order(test$id), ]

benchmark(ju(), gs1(), gs2(), jply(), jdt(),
    replications=5, order="relative")[,1:6]
#     test replications elapsed relative user.self sys.self
# 1   ju()            5    0.03    1.000      0.03     0.00
# 5  jdt()            5    0.03    1.000      0.03     0.00
# 3  gs2()            5    3.49  116.333      2.87     0.58
# 2  gs1()            5    3.58  119.333      3.00     0.58
# 4 jply()            5    3.69  123.000      3.11     0.51

다시 한 번 시도해 보겠습니다. 하지만 첫 번째 예선부터 더 많은 데이터와 더 많은 복제를 가진 경쟁자들만 사용할 수 있습니다.

set.seed(21)
test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE))
test <- test[order(test$id), ]
benchmark(ju(), jdt(), order="relative")[,1:6]
#    test replications elapsed relative user.self sys.self
# 1  ju()          100    5.48    1.000      4.44     1.00
# 2 jdt()          100    6.92    1.263      5.70     1.15

저는 dplyr 접근법을 선호합니다.

group_by(id)그 다음에 둘 중 하나가

  • filter(row_number()==1)또는
  • slice(1)또는
  • slice_head(1)#(dplyr => 1.0)
  • top_n(n = -1)
    • top_n()내부적으로 순위 함수를 사용합니다.음수는 순위의 맨 아래에서 선택합니다.

경우에 따라 group_by 뒤에 id를 정렬해야 할 수 있습니다.

library(dplyr)

# using filter(), top_n() or slice()

m1 <-
test %>% 
  group_by(id) %>% 
  filter(row_number()==1)

m2 <-
test %>% 
  group_by(id) %>% 
  slice(1)

m3 <-
test %>% 
  group_by(id) %>% 
  top_n(n = -1)

세 가지 방법 모두 동일한 결과를 반환합니다.

# A tibble: 5 x 2
# Groups:   id [5]
     id string
  <int> <fct> 
1     1 A     
2     2 B     
3     3 C     
4     4 D     
5     5 E

어때

DT <- data.table(test)
setkey(DT, id)

DT[J(unique(id)), mult = "first"]

편집

에 대한 고유한 방법도 있습니다.data.tables첫 번째 행을 키로 반환합니다.

jdtu <- function() unique(DT)

제 생각에, 만약 당신이 주문한다면.test벤치마크를 벗어나면, 당신은 그것을 제거할 수 있습니다.setkey그리고.data.table벤치마크로부터의 변환도 마찬가지입니다(셋 키가 기본적으로 id별로 정렬되므로, 동일함).order).

set.seed(21)
test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE))
test <- test[order(test$id), ]
DT <- data.table(DT, key = 'id')
ju <- function() test[!duplicated(test$id),]

jdt <- function() DT[J(unique(id)),mult = 'first']


 library(rbenchmark)
benchmark(ju(), jdt(), replications = 5)
##    test replications elapsed relative user.self sys.self 
## 2 jdt()            5    0.01        1      0.02        0        
## 1  ju()            5    0.05        5      0.05        0         

그리고 더 많은 데이터와 함께

고유한 방법으로 편집**

set.seed(21)
test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE))
test <- test[order(test$id), ]
DT <- data.table(test, key = 'id')
       test replications elapsed relative user.self sys.self 
2  jdt()            5    0.09     2.25      0.09     0.00    
3 jdtu()            5    0.04     1.00      0.05     0.00      
1   ju()            5    0.22     5.50      0.19     0.03        

독특한 방법이 여기서 가장 빠릅니다.

간단한ddply옵션:

ddply(test,.(id),function(x) head(x,1))

속도가 문제인 경우, 유사한 접근 방식을 취할 수 있습니다.data.table:

testd <- data.table(test)
setkey(testd,id)
testd[,.SD[1],by = key(testd)]

또는 이 작업이 상당히 빠를 수 있습니다.

testd[testd[, .I[1], by = key(testd]$V1]

자, 이제dplyr고유 카운터를 추가합니다.

df %>%
    group_by(aa, bb) %>%
    summarise(first=head(value,1), count=n_distinct(value))

그룹을 만들고 그룹 내에서 요약합니다.

데이터가 숫자인 경우 다음을 사용할 수 있습니다.
first(value)[또한 있습니다.last(value)대신에head(value, 1)

참조: http://cran.rstudio.com/web/packages/dplyr/vignettes/introduction.html

전체:

> df
Source: local data frame [16 x 3]

   aa bb value
1   1  1   GUT
2   1  1   PER
3   1  2   SUT
4   1  2   GUT
5   1  3   SUT
6   1  3   GUT
7   1  3   PER
8   2  1   221
9   2  1   224
10  2  1   239
11  2  2   217
12  2  2   221
13  2  2   224
14  3  1   GUT
15  3  1   HUL
16  3  1   GUT

> library(dplyr)
> df %>%
>   group_by(aa, bb) %>%
>   summarise(first=head(value,1), count=n_distinct(value))

Source: local data frame [6 x 4]
Groups: aa

  aa bb first count
1  1  1   GUT     2
2  1  2   SUT     2
3  1  3   SUT     3
4  2  1   221     3
5  2  2   217     3
6  3  1   GUT     2

SQLite에는 기본 제공rowid유사 열을 사용하여 다음 작업을 수행합니다.

sqldf("select min(rowid) rowid, id, string 
               from test 
               group by id")

제공:

  rowid id string
1     1  1      A
2     3  2      B
3     5  3      C
4     7  4      D
5     9  5      E

또한sqldf그 자체로row.names=인수:

sqldf("select min(cast(row_names as real)) row_names, id, string 
              from test 
              group by id", row.names = TRUE)

제공:

  id string
1  1      A
3  2      B
5  3      C
7  4      D
9  5      E

위의 두 가지 요소를 혼합하는 세 번째 대안이 훨씬 더 나을 수 있습니다.

sqldf("select min(rowid) row_names, id, string 
               from test 
               group by id", row.names = TRUE)

제공:

  id string
1  1      A
3  2      B
5  3      C
7  4      D
9  5      E

이 세 가지 모두 SQL에 대한 SQLite 확장에 의존합니다. 여기서 사용되는 SQL은min또는max다른 열이 동일한 행에서 선택되도록 보장합니다. (다른 SQL 기반 데이터베이스에서는 보장되지 않을 수 있습니다.)

기본 R 옵션은 다음과 같습니다.split()-lapply()-do.call()관용구:

> do.call(rbind, lapply(split(test, test$id), head, 1))
  id string
1  1      A
2  2      B
3  3      C
4  4      D
5  5      E

보다 직접적인 방법은lapply()[함수:

> do.call(rbind, lapply(split(test, test$id), `[`, 1, ))
  id string
1  1      A
2  2      B
3  3      C
4  4      D
5  5      E

쉼표 공간1, )말에lapply()통화는 통화와 동일하기 때문에 필수입니다.[1, ]첫 번째 행과 모든 열을 선택합니다.

dplyr 1.1.0는 인인그사수있습다니용할과 함께 사용할 수 .slice_head의 인수:

slice_head(test, n = 1, by = id)

#   id string
# 1  1      A
# 2  2      B
# 3  3      C
# 4  4      D
# 5  5      E

언급URL : https://stackoverflow.com/questions/13279582/select-the-first-row-by-group

반응형