没有循环的分组和条件(大数据)

时间:2013-12-03 03:30:24

标签: r loops bigdata

我对同一组有几个观察,每次观察我都有一年。

dat = data.frame(group = rep(c(“a”,“b”,“c”),每个= 3),年= c(2000,1996,1975,2002,2010,1980,1990 ,1986,1995))

group   year
1   a   2000
2   a   1996
3   a   1975
4   b   2002
5   b   2010
6   b   1980
7   c   1990
8   c   1986
9   c   1995

对于每次观察,我想知道在相对于焦点观察的给定条件下是否可以发现同一组的另一次观察。例如:“在同一组中,过去6年(从焦点年份开始)是否有任何其他观察(而不是焦点观察)”。

理想情况下,数据框应该是那样的

group   year  six_years
1   a   2000          1  # there is another member of group a that is year = 1996 (2000-6 = 1994, this value is inside the threshold)
2   a   1996          0
3   a   1975          0
4   b   2002          0
5   b   2010          0
6   b   1980          0
7   c   1990          1
8   c   1986          0
9   c   1995          1

基本上对于每一行,我们应该查看组的子集,看看是否有任何(dat $ year == conditions)。使用for循环很容易,但这里没用:数据帧很大(数百万行),循环需要永远。 我正在寻找一种有效的矢量化函数或快速包。

谢谢!

3 个答案:

答案 0 :(得分:0)

<强> EDITED

实际上考虑一下,你可能会有很多反复出现的年/组组合,在这种情况下使用count()预先计算频率要快得多 - 这也是plyr函数:< / p>

90M行需要~4秒

require(plyr)
dat <- data.frame(group = sample(c("a","b","c"),size=9000000,replace=TRUE), 
             year = sample(c(2000, 1996, 1975, 2002, 2010, 1980, 1990,1986,1995),size=9000000,replace=TRUE))


 test<-function(y,g,df){
 d<-df[df$year>=y-6 & 
          df$year<y &
          df$group== g,]
 return(nrow(d))
}

rollup<-function(){
  summ<-count(dat)                   # add a frequency to each combination
  return(ddply(summ,.(group,year),transform,t=test(as.numeric(year),group,summ)*freq))
}

system.time(rollup())

user  system elapsed 
3.44    0.42    3.90 

答案 1 :(得分:0)

我的数据集有太多不同的组,而Troy提出的plyr选项太慢了。 我发现了一个hack(专家可能会说&#34;一个丑陋的&#34;)和package data.table:我的想法是使用快速合并功能快速合并data.table。它给出了一组中给定年份与同一组中所有其他年份之间的所有可能组合。 然后按照您要查找的条件为每一行继续ifelse。 最后,使用求和函数汇总所有内容,以了解在给定的时间跨度内相对于另一年可以找到每个给定年份的次数。 在我的电脑上,花了几毫秒,而不是plyr可能需要的时间

dat = data.table(group = rep(c("a","b","c"),each = 3), year = c(2000, 1996, 1975, 2002, 2010, 1980, 1990,1986,1995), key = "group")

产生这个:

group   year
1   a   2000
2   a   1996
3   a   1975
4   b   2002
5   b   2010
6   b   1980
7   c   1990
8   c   1986
9   c   1995

然后:

z = merge(dat, dat, by = "group", all = T, allow.cartesian = T) # super fast

z$sixyears = ifelse(z$year.y >= z$year.x - 6 & z$year.y < z$year.x, 1, 0) # creates a 0/1 column for our condition
z$sixyears = as.numeric(z$sixyears) # we want to sum this up after
z$year.y = NULL # useless column now
z2 = z[ , list(sixyears = sum(sixyears)), by = list(group, year.x)]

(过去六年中同一组别的另一年的年数被给予&#34; 1&#34;:

  group year x
1     a 1975 0
2     b 1980 0
3     c 1986 0
4     c 1990 1  # e.g. here there is another "c" which was in the timespan 1990 -6 ..
5     c 1995 1  # <== this one. This one too has another reference in the last 6 years, two rows above.
6     a 1996 0
7     a 2000 1
8     b 2002 0
9     b 2010 0

锦上添花:它无缝地处理NA。

答案 2 :(得分:0)

这是另一种使用data.table的可能性,但包括diff()

dat <- data.table(group = rep(c("a","b","c"), each = 3), 
                  year = c(2000, 1996, 1975, 2002, 2010, 1980, 1990,1986,1995), 
                  key = "group")
valid_case <- subset(dt[,list(valid_case = diff(year)), by=key(dt)], 
                     abs(valid_case)<6)
dat$valid_case <- ifelse(dat$group %in% valid_case$group, 1, 0)

我不确定这在速度或NA处理方面是如何比较的(我认为它应该适用于NAs,因为它们在diff()abs()中传播),但我当然觉得它更具可读性。 data.table中的联接速度非常快,但我必须考虑避免共同帮助。使用ifelse连接在data.table语句中执行该条件可能更为惯用。尽管我的经验从未发现%in%是限制因素,但这可能会加快速度。