为了账号安全,请及时绑定邮箱和手机立即绑定

按组选择第一行

/ 猿问

按组选择第一行

繁星coding 2019-08-19 11:10:53

按组选择第一行

来自这样的数据框架


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 / string对的第一行创建一个新的。如果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查询?


查看完整描述

3 回答

?
largeQ

您可以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


查看完整回答
反对 回复 2019-08-19
?
123456qqq

关于什么


DT <- data.table(test)

setkey(DT, id)


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

编辑

还有一种独特的方法data.tables,它将按键返回第一行


jdtu <- function() unique(DT)

我认为,如果你test在基准测试之外订购,那么你也可以从基准测试中删除setkey和data.table转换(因为setkey基本上按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        

这种独特的方法最快。


查看完整回答
反对 回复 2019-08-19
?
LEATH

我赞成dplyr方法。


group_by(id) 其次是


filter(row_number()==1) 要么

slice(1) 要么

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


查看完整回答
反对 回复 2019-08-19

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信