按事件序列分组并获取每个序列的摘要统计信息

时间:2019-02-25 00:55:14

标签: r dataframe time-series

我有一个带事件序列日志的data.frame。这里,序列1由事件A,然后B,然后C组成,每个事件都在特定的时间戳(以秒为单位)开始。

df=data.frame(id=runif(10, 1e6, 1e7), sequence = c(1,1,1,2,2,3,3,3,4,4), event=c("A", "B", "C", "B", "C", "A", "B", "C", "B", "C"), starts_at=c(20,22,24,20,30,20,21,23,20,40))

我想要的是按序列类型(有数十种类型,长度从2到6)对data.frame进行分组:A-> B-> C或B-> C,然后得到一些结果这些类型。所需的输出将是:

####                      sequence_type number.appearances mean.delay.between.events
####                    1           ABC                  2                   1.5 / 2
####                    2            BC                  2                        15

最后一列“平均延迟”将是由序列中连续事件之间的平均差异时间组成的字符串:在ABC序列中,A和B之间的平均间隔为1.5秒,B和C之间的平均间隔为2秒。 我还想到了在新列diff.1,diff.2 ...中“分散”每个均值差异,但是由于序列的长度不同,因此看起来很复杂。尽管我愿意采用不同的方式来显示此信息。

到目前为止,我已经提出:

library(dplyr)
df %>% group_by(sequence) %>% arrange(starts_at) %>% summarise(sequence_type = paste0(event, collapse="")) %>% group_by(sequence_type) %>% tally

我没有找到实现第二部分的方法。感谢您的帮助...

2 个答案:

答案 0 :(得分:1)

这可能不是用dplyr会得到的优雅解决方案,但我认为它已经足够通用,可以与您的真实数据一起使用了。
首先,您只需要获取数据每一行的相应顺序,即ayuda_seq

library(zoo)
df=data.frame(id=runif(14, 1e6, 1e7), sequence = c(1,1,1,2,2,3,3,3,4,4,5,5,5,5), 
              event=c("A", "B", "C", "B", "C", "A", "B", "C", "B", "C","A","B","C","D"), 
              starts_at=c(20,22,24,20,30,20,21,23,20,40,20,22,21,15))
ayuda_seq = sapply(df$sequence, function(x) paste0(df[df$sequence == x,3],collapse = ""))

,然后循环浏览唯一的序列并按每2个元素生成子序列。

vec_means = NULL
for(x in unique(ayuda_seq)){
  data_temp = df[ayuda_seq == x,]
  diff_temp = diff(data_temp$starts_at)
  temp_sub = apply(rollapply(data_temp[,3],FUN = paste0,width = 2),1,paste0,collapse = "")
  mean_temp = aggregate(diff_temp,by = list(temp_sub),mean)
  if(all(!duplicated(temp_sub))){
    averages = paste0(mean_temp[,2],collapse = " / ")
  } else{
    averages = paste0(mean_temp[match(temp_sub[duplicated(temp_sub)],mean_temp[,1]),2],collapse = " / ")
  }
  vec_means = c(vec_means,averages)
}


df_res = data.frame(sequence_type = unique(ayuda_seq),
                    number.appearances = as.numeric(table(ayuda_seq)/nchar(unique(ayuda_seq))),
                    mean.delay.between.events = vec_means)

变量temp_sub在您要循环的原始字符串中将具有不同的组合。在"ABC"的情况下,可能存在“ CA”的组合,因为它是唯一的,因此未考虑在内。

答案 1 :(得分:1)

不漂亮,但是可以用

tmp<-df %>% group_by(sequence) %>% dplyr::arrange(sequence, starts_at) %>%  dplyr::mutate(seq_row_num=dplyr::row_number(), lead_starts_at=dplyr::lead(starts_at, n = 1)) %>% base::as.data.frame()
tmp<- tmp %>% dplyr::group_by(sequence) %>% mutate(max_seq_len=max(seq_row_num)) %>% base::as.data.frame()
tmp$seq_len_id<- paste0(tmp$sequence, tmp$max_seq_len)
tmp$next_seq_val<- tmp$seq_row_num + 1
tmp$next_seq_val<- base::ifelse(tmp$next_seq_val >= tmp$max_seq_len, tmp$max_seq_len, tmp$next_seq_val)
tmp_seq_labels<- stats::aggregate(tmp$event, list(tmp$seq_len_id), paste, collapse='')
tmp<- base::merge(tmp, tmp_seq_labels, by.x="seq_len_id", by.y="Group.1")
colnames(tmp)[which(colnames(tmp)=="x")]<- "seq_group"
tmp$within_group_step<-"ZZ"


tmp$within_group_step<- base::ifelse(tmp$seq_row_num != tmp$max_seq_len, substr(tmp$seq_group, start = tmp$seq_row_num, stop =tmp$next_seq_val), tmp$within_group_step)
tmp$within_step_by_group_id<- paste0(tmp$seq_group, tmp$within_group_step)
tmp$time_diff<- 0
tmp$time_diff<- base::ifelse(!is.na(tmp$lead_starts_at), tmp$lead_starts_at - tmp$starts_at, tmp$time_diff)

res<- stats::aggregate(time_diff ~ within_step_by_group_id + seq_group + within_group_step, data=tmp, FUN=mean)
drops<- grep(pattern = "ZZ", x = res$within_step_by_group_id)
if(length(drops)>=1){
  res<- res[-drops,]
}


colnames(res)<- c("Full_Group_Pattern", "Group_Pattern", "Sub_Group_Pattern", "Mean_Time_Difference")
res<- res %>% dplyr::group_by(Group_Pattern) %>%
  dplyr::mutate(Number_of_Appearances=n()) %>% base::as.data.frame()

结果如下: enter image description here