为每个连续序列创建一个组号
Posted
技术标签:
【中文标题】为每个连续序列创建一个组号【英文标题】:Create a group number for each consecutive sequence 【发布时间】:2012-04-26 09:26:01 【问题描述】:我有下面的data.frame。我想添加一个列“g”,它根据列h_no
中的连续序列对我的数据进行分类。也就是说,h_no 1, 2, 3, 4
的第一个序列是第 1 组,h_no
的第二个序列(1 到 7)是第 2 组,依此类推,如最后一列“g”所示。
h_no h_freq h_freqsq g
1 0.09091 0.008264628 1
2 0.00000 0.000000000 1
3 0.04545 0.002065702 1
4 0.00000 0.000000000 1
1 0.13636 0.018594050 2
2 0.00000 0.000000000 2
3 0.00000 0.000000000 2
4 0.04545 0.002065702 2
5 0.31818 0.101238512 2
6 0.00000 0.000000000 2
7 0.50000 0.250000000 2
1 0.13636 0.018594050 3
2 0.09091 0.008264628 3
3 0.40909 0.167354628 3
4 0.04545 0.002065702 3
【问题讨论】:
另见Create grouping variable for consecutive sequences and split vector 【参考方案1】:data.table
函数 rleid
对这类事情很方便。我们减去序列1:nrow(data)
将连续序列转换为常量,然后使用rleid
创建组ID:
data$g = data.table::rleid(data$h_no - 1:nrow(data))
【讨论】:
【参考方案2】:我相信使用“cbind”是在 R 中向数据框中添加列的最简单方法。下面是一个示例:
myDf = data.frame(index=seq(1,10,1), Val=seq(1,10,1))
newCol= seq(2,20,2)
myDf = cbind(myDf,newCol)
【讨论】:
【参考方案3】:基于识别组数(mapply
中的x
)及其长度(mapply
中的y
)的方法
mytb<-read.table(text="h_no h_freq h_freqsq group
1 0.09091 0.008264628 1
2 0.00000 0.000000000 1
3 0.04545 0.002065702 1
4 0.00000 0.000000000 1
1 0.13636 0.018594050 2
2 0.00000 0.000000000 2
3 0.00000 0.000000000 2
4 0.04545 0.002065702 2
5 0.31818 0.101238512 2
6 0.00000 0.000000000 2
7 0.50000 0.250000000 2
1 0.13636 0.018594050 3
2 0.09091 0.008264628 3
3 0.40909 0.167354628 3
4 0.04545 0.002065702 3", header=T, stringsAsFactors=F)
mytb$group<-NULL
positionsof1s<-grep(1,mytb$h_no)
mytb$newgroup<-unlist(mapply(function(x,y)
rep(x,y), # repeat x number y times
x= 1:length(positionsof1s), # x is 1 to number of nth group = g1:g3
y= c( diff(positionsof1s), # y is number of repeats of groups g1 to penultimate (g2) = 4, 7
nrow(mytb)- # this line and the following gives number of repeat for last group (g3)
(positionsof1s[length(positionsof1s )]-1 ) # number of rows - position of penultimate group (g2)
) ) )
mytb
【讨论】:
【参考方案4】:您可以使用各种技术向数据中添加一列。以下引用来自相关帮助文本的“详细信息”部分,[[.data.frame
。
可以在多种模式下对数据帧进行索引。当
[
和[[
与单个向量索引(x[i]
或x[[i]]
)一起使用时,它们对数据框进行索引,就像它是一个列表一样。
my.dataframe["new.col"] <- a.vector
my.dataframe[["new.col"]] <- a.vector
$
的 data.frame 方法,将x
视为一个列表
my.dataframe$new.col <- a.vector
当
[
和[[
与两个索引(x[i, j]
和x[[i, j]]
)一起使用时,它们的作用就像索引矩阵
my.dataframe[ , "new.col"] <- a.vector
由于data.frame
的方法假定如果您不指定使用列还是行,它将假定您的意思是列。
对于您的示例,这应该有效:
# make some fake data
your.df <- data.frame(no = c(1:4, 1:7, 1:5), h_freq = runif(16), h_freqsq = runif(16))
# find where one appears and
from <- which(your.df$no == 1)
to <- c((from-1)[-1], nrow(your.df)) # up to which point the sequence runs
# generate a sequence (len) and based on its length, repeat a consecutive number len times
get.seq <- mapply(from, to, 1:length(from), FUN = function(x, y, z)
len <- length(seq(from = x[1], to = y[1]))
return(rep(z, times = len))
)
# when we unlist, we get a vector
your.df$group <- unlist(get.seq)
# and append it to your original data.frame. since this is
# designating a group, it makes sense to make it a factor
your.df$group <- as.factor(your.df$group)
no h_freq h_freqsq group
1 1 0.40998238 0.06463876 1
2 2 0.98086928 0.33093795 1
3 3 0.28908651 0.74077119 1
4 4 0.10476768 0.56784786 1
5 1 0.75478995 0.60479945 2
6 2 0.26974011 0.95231761 2
7 3 0.53676266 0.74370154 2
8 4 0.99784066 0.37499294 2
9 5 0.89771767 0.83467805 2
10 6 0.05363139 0.32066178 2
11 7 0.71741529 0.84572717 2
12 1 0.10654430 0.32917711 3
13 2 0.41971959 0.87155514 3
14 3 0.32432646 0.65789294 3
15 4 0.77896780 0.27599187 3
16 5 0.06100008 0.55399326 3
【讨论】:
最后两种添加列的方法有什么区别? @huon-dbaupp 带有逗号的方法是显式的,也适用于矩阵,而最后一个方法仅适用于 data.frames。如果没有提供逗号,R 假定您的意思是列。【参考方案5】:Data.frame[,'h_new_column'] <- as.integer(Data.frame[,'h_no'], breaks=c(1, 4, 7))
【讨论】:
【参考方案6】:很简单:你的数据框是 A
b <- A[,1]
b <- b==1
b <- cumsum(b)
然后你得到b列。
【讨论】:
又好又短。我将只更改最后一个元素,以便将结果作为列直接添加到原始数据框中,而不是cumsum(b) -> b
,例如A$groups <- cumsum(b)
。
cumsum(b)
会给你一个长度为 3 的向量,还是我遗漏了什么?
@RomanLuštrik,请参阅 dbaupp's solution,它解释了 cumsum 在这种情况下如何工作。
@RomanLuštrik,这个解决方案可以在一行中很好地重写。使用您的your.df
数据,您只需执行your.df$group = cumsum(your.df[, 1]==1)
即可获得新的组列。【参考方案7】:
如果我正确理解了这个问题,您想检测h_no
何时不增加,然后增加class
。 (我将介绍我是如何解决这个问题的,最后有一个自包含的函数。)
工作
我们目前只关心h_no
列,因此我们可以从数据框中提取它:
> h_no <- data$h_no
我们想检测h_no
何时没有上升,我们可以通过计算连续元素之间的差异何时为负或为零来实现。 R 提供了diff
函数,它为我们提供了差异向量:
> d.h_no <- diff(h_no)
> d.h_no
[1] 1 1 1 -3 1 1 1 1 1 1 -6 1 1 1
一旦我们有了这个,就很容易找到那些不是肯定的:
> nonpos <- d.h_no <= 0
> nonpos
[1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
[13] FALSE FALSE
在R中,TRUE
和FALSE
与1
和0
基本相同,所以如果我们得到nonpos
的累积和,它会在(几乎)合适的位置增加1 . cumsum
函数(基本上是 diff
的反面)可以做到这一点。
> cumsum(nonpos)
[1] 0 0 0 1 1 1 1 1 1 1 2 2 2 2
但是,有两个问题:数字太小;而且,我们缺少第一个元素(第一类应该有四个)。
第一个问题简单解决了:1+cumsum(nonpos)
。第二个只需要在向量前面添加一个1
,因为第一个元素总是在1
类中:
> classes <- c(1, 1 + cumsum(nonpos))
> classes
[1] 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3
现在,我们可以使用 cbind
将其附加到我们的数据框中(通过使用 class=
语法,我们可以为该列指定 class
标题):
> data_w_classes <- cbind(data, class=classes)
data_w_classes
现在包含结果。
最终结果
我们可以将这些行压缩在一起,并将它们全部包装成一个函数,以使其更易于使用:
classify <- function(data)
cbind(data, class=c(1, 1 + cumsum(diff(data$h_no) <= 0)))
或者,因为class
是一个因素是有意义的:
classify <- function(data)
cbind(data, class=factor(c(1, 1 + cumsum(diff(data$h_no) <= 0))))
您可以使用以下任一功能:
> classified <- classify(data) # doesn't overwrite data
> data <- classify(data) # data now has the "class" column
(这种解决这个问题的方法很好,因为它避免了显式迭代,这通常推荐用于 R,并且避免生成大量中间向量和列表等。而且它可以写在一行上也很整洁: ))
【讨论】:
【参考方案8】:除了 Roman 的回答之外,这样的事情可能更简单。请注意,我没有测试它,因为我现在无法访问 R。
# Note that I use a global variable here
# normally not advisable, but I liked the
# use here to make the code shorter
index <<- 0
new_column = sapply(df$h_no, function(x)
if(x == 1) index = index + 1
return(index)
)
该函数遍历n_ho
中的值并始终返回当前值所属的类别。如果检测到1
的值,我们增加全局变量index
并继续。
【讨论】:
我喜欢使用全局变量的 hack。所以西施。 :P以上是关于为每个连续序列创建一个组号的主要内容,如果未能解决你的问题,请参考以下文章