R “查找第一行/最后一行”;递归地;分组

R “查找第一行/最后一行”;递归地;分组,r,data.table,R,Data.table,我试图找到一种有效的方法,按组查找第一行和最后一行 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 M

我试图找到一种有效的方法,按组查找第一行和最后一行

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);
}

您想要什么还不完全清楚,但您肯定可以在索引中有多个列:

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

by不完全清楚您想要什么,但您可以在索引中有多个列:

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

如果问题是这样的:

对于一组列(x,y,z),我想添加一个整数列,标记每组第一项的位置
by=“x”
by=“x,y”
by=“x,y,z”
(三个新列)。每个新列的第一行将始终为1,因为它始终是第一组的第一项。我还想再添加3列,用同样的3个分组分别标记最后一项。不过,我可能不止有3个分组,所以有没有一些程序化的可能呢

那么:

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
但正如@Roland所评论的,可能有更好的方法来实现你的最终目标

根据要求,下面是使用
.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()
rep()
)。

如果这是问题:

对于一组列(x,y,z),我想添加一个整数列,标记每组第一项的位置
by=“x”
by=“x,y”
by=“x,y,z”
(三个新列)。每个新列的第一行将始终为1,因为它始终是第一组的第一项。我还想再添加3列,用同样的3个分组分别标记最后一项。不过,我可能不止有3个分组,所以有没有一些程序化的可能呢

那么:

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
但正如@Roland所评论的,可能有更好的方法来实现你的最终目标

根据要求,下面是使用
.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()
rep()
),这有什么不对?如果你有N个分组变量,你需要重复“先查找后查找”的指令N次。我很好奇你为什么要这样做。也许有更好的方法来实现你的最终目标。重新审视你的最后一句话,有什么错吗?如果你有N个分组变量,你需要重复“先发现后发现”的指令N次。我很好奇你为什么要这样做。也许有更好的方法来实现你的最终目标。这正是我试图以自动化的方式做的,如果我有一组10人,我宁愿避免写10人lines@Andrie:
署名这正是我试图以自动化方式做的事情,如果我有一组10人,我宁愿避免写10人lines@Andrie:
署名这看起来不错。我想知道
data.table
是否包含类似
.I
.GRP
。。。我想我现在知道了,这本可以帮助更快地实现这一目标。干杯,伙计们。@statquant你们是说这个解决方案很慢,你们想知道它是否能更快?你的英语不是很清楚。听起来你好像不是100%高兴。我仍然在等待你提到的其他语言的“相当普通”的解决方案,它们可以轻松完成这项任务;e、 SQL和SAS(您发布的SAS解决方案是逐列写出的)。慢是相对的,这是迄今为止我见过的最好的,我自己也做不到更快。我想知道它是否可以使用.variables,因为使用它通常是最快的方法,但是你知道的更好。。。关于香草,我认为SAS代码在简单性方面相当不错,不是在代码行数方面,而是在语法方面。关于我的英语,我承认有罪,我想这对我们很多非母语人士来说是很常见的…@statquant好的,谢谢你的澄清,我将使用.variables添加解决方案。但是在SAS语法方面,我想到了你的抱怨:“如果你有N个分组变量,你需要重复先查找然后查找最后N次的指令。”。这看起来就是你在SAS里所做的。在这一点上是有意义的,我真的不想回到所有的评论流。关键是您的解决方案是快速、高效的(没有无用的对象副本)。这就是我想要的,关键是使用.variables来利用数据的内部分组过程。我想知道
data.table
是否包含类似
.I
.GRP
。。。我想我现在知道了,这本可以帮助更快地实现这一目标。干杯,伙计们。@statquant你们是说这个解决方案很慢,你们想知道它是否能更快?你的英语不是很清楚。听起来你好像不是100%高兴。我仍然在等待你提到的其他语言的“相当普通”的解决方案,它们可以轻松完成这项任务;e、 SQL和SAS(您发布的SAS解决方案是逐列写出的)。慢是相对的,这是迄今为止我见过的最好的,我自己也做不到更快。我