我试图找到一种有效的方法来按组找到第一行和最后一行。
R) ex=data.table(state=c("az","fl","fl","fl","fl","fl","oh"),city=c("TU","MI","MI","MI","MI","MI","MI"),code=c(85730,33133,33133,33133,33146,33146,45056))
R) ex
state city code
1: az TU 85730
2: fl MI 33133
3: fl MI 33133
4: fl MI 33133
5: fl MI 33146
6: fl MI 33146
7: oh MI 45056
我想找到一个组的每个变量的第一个和最后一个
R) ex
state city code first.state last.state first.city last.city first.code last.code
1: az TU 85730 1 1 1 1 1 1
2: fl MI 33133 1 0 1 0 1 0
3: fl MI 33133 0 0 0 0 0 0
4: fl MI 33133 0 0 0 0 0 1
5: fl MI 33146 0 0 0 0 1 0
6: fl MI 33146 0 1 0 1 0 1
7: oh MI 45056 1 1 1 1 1 1
据我所知data.table
无法轻易帮助解决此问题,因为by="state,city,code"
会查看4
三元组。
我知道的唯一方法是在by =“state,city,code”中查找first / last.code,然后在by =“state,city”中查找first / last.city。
这就是我的意思:
applyAll <- function(DT, by){
f<- function(n, vec){ return(vec[1:n]) }
by <- lapply(1:length(by), FUN=f, by)
out <- Reduce(f=firstLast, init=DT, x=by)
return(out)
}
firstLast <- function(DT, by){
addNames <- paste(c("first", "last"),by[length(by)], sep=".")
DT[DT[,list(IDX=.I[1]), by=by]$IDX, addNames[1]:=1]
DT[DT[,list(IDX=.I[.N]), by=by]$IDX, addNames[2]:=1]
return(DT);
}
结果:applyAll(ex,c("state","city","code"))
但是这会产生DT
的NUMEROUS副本,我的问题是,是否有某些计划或已经存在,以便我们无法获得第一组/最后一组。 (对于SAS
或kdb
或SQL
)
在SAS
:
data DT;
set ex;
by state city code;
if first.code then firstcode=1;
if last.code then lastcode=1;
if first.city then firstcity=1;
if last.city then lastcity=1;
if first.state then firststate=1;
if last.state then laststate=1;
run;
答案 0 :(得分:5)
如果这是问题:
那么怎么样:对于一组列(x,y,z),我想添加一个整数列,标记每个组
by="x"
,by="x,y"
和{{1}的第一项的位置(三个新专栏)。每个新列的第一行始终为1,因为它始终是第一个组的第一个项目。我还想在相同的3个分组中添加另外3列标记最后一个项目。不过,我可能会有很多不仅仅是3个分组,所以可以编程吗?
by="x,y,z"
但正如@Roland评论的那样,可能有更好的方法来实现你的最终目标。
而且,根据要求,以下是使用ex=data.table(state=c("az","fl","fl","fl","fl","fl","oh"),
city=c("TU","MI","MI","MI","MI","MI","MI"),
code=c(85730,33133,33133,33133,33146,33146,45056))
ex
state city code
1: az TU 85730
2: fl MI 33133
3: fl MI 33133
4: fl MI 33133
5: fl MI 33146
6: fl MI 33146
7: oh MI 45056
cols = c("state","city","code")
for (i in seq_along(cols)) {
ex[,paste0("f.",cols[i]):=c(1L,rep(0L,.N-1L)),by=eval(head(cols,i))] # first
ex[,paste0("l.",cols[i]):=c(rep(0L,.N-1L),1L),by=eval(head(cols,i))] # last
}
ex
state city code f.state l.state f.city l.city f.code l.code
1: az TU 85730 1 1 1 1 1 1
2: fl MI 33133 1 0 1 0 1 0
3: fl MI 33133 0 0 0 0 0 0
4: fl MI 33133 0 0 0 0 0 1
5: fl MI 33146 0 0 0 0 1 0
6: fl MI 33146 0 1 0 1 0 1
7: oh MI 45056 1 1 1 1 1 1
和.I
的更快解决方案:
.N
它应该更快,因为每个列只进行一次分组,并且与第一个解决方案不同,不会创建许多小向量(不为每个组调用cols = c("state","city","code")
for (i in seq_along(cols)) {
w = ex[,list(f=.I[1],l=.I[.N]),by=eval(head(cols,i))]
ex[,paste0(c("f.","l."),cols[i]):=0L] # add the two 0 columns
ex[w$f,paste0("f.",cols[i]):=1L] # mark the firsts
ex[w$l,paste0("l.",cols[i]):=1L] # mark the lasts
}
或c()
)。 / p>
答案 1 :(得分:2)
目前还不完全清楚你想要什么,但你当然可以在索引中有多个专栏:
ex[, list(first=head(code, 1), last=tail(code, 1)), by=c("state", "city")]
state city first last
1: az TU 85730 85730
2: fl MI 33133 33146
3: oh MI 45056 45056
您可以像这样在您的群组中自动执行此操作:
by <- c("state", "city", "code")
byList <- lapply(seq_along(by), function(i)by[sequence(i)])
lapply(byList,
function(i) ex[, list(first=head(code, 1), last=tail(code, 1)), by=i] )
[[1]]
state first last
1: az 85730 85730
2: fl 33133 33146
3: oh 45056 45056
[[2]]
state city first last
1: az TU 85730 85730
2: fl MI 33133 33146
3: oh MI 45056 45056
[[3]]
state city code first last
1: az TU 85730 85730 85730
2: fl MI 33133 33133 33133
3: fl MI 33146 33146 33146
4: oh MI 45056 45056 45056