来自像这样的数据框
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(row)运行相同的sqldf查询?
答案 0 :(得分:97)
您可以使用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
答案 1 :(得分:15)
怎么样?
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
独特的方法在这里最快。
答案 2 :(得分:11)
一个简单的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]
答案 3 :(得分:10)
我赞成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
答案 4 :(得分:7)
(1)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
(2)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
(3)混合上述两种元素的第三种选择可能更好:
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扩展,其中min
或max
的使用保证会导致从同一行中选择其他列。 (在其他基于SQL的数据库中,可能无法保证。)
答案 5 :(得分:6)
现在,对于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
答案 6 :(得分:4)
基础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, ]
来选择第一行和所有列