【问题标题】:group by sequence of events and get summary statistics for each sequence按事件序列分组并获取每个序列的汇总统计信息
【发布时间】:2019-07-18 09:08:57
【问题描述】:

我有一个带有事件序列日志的 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

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

【问题讨论】:

  • 随着sequence 的增加,您是否总是交替使用“ABC”和“AB”?例如,sequence = 1event = "A", "B", "C"sequence = 2event = "A", "B"sequence = 3sequence = 1 具有相同的事件; sequence = 4sequence = 2 具有相同的事件。
  • 哦不,它真的可以是任何东西(我编辑了问题);有几十种类型的序列,其中大多数长度在 2 到 6 之间。我的原始 data.frame 中没有真正的顺序,一切都被打乱了,这就是我使用“排列”的原因
  • 我认为这是从基地使用rle 的机会

标签: r dataframe time-series


【解决方案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”的组合,因为它是唯一的,所以没有考虑到它。

【讨论】:

  • 谢谢,它确实给出了假数据的预期输出;但由于某种原因,如果我打乱这些数据或添加新类型的新序列,它不会给出正确的答案..?
  • @agenis 洗牌玩具数据是什么意思?任何方式有人添加了不同的答案希望它有效。如果不发布更接近您真实数据的数据,我会看看是否可以修复代码。
  • 我的意思是如果你随机重新排序假数据的行,它应该工作相同......
  • 好的,我明白你的意思了。该代码假定变量序列的顺序正确,就像您的数据样本中一样。如果不是,它将不起作用,并且它还假定所有序列(ABC ....)不是唯一的,但这是代码中的一个简单修复。
  • 好的,我明白了。这不是问题,因为无论如何我对独特的序列不感兴趣:-)
【解决方案2】:

不漂亮,但很管用

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()

结果如下:

【讨论】:

    猜你喜欢
    • 2012-04-08
    • 1970-01-01
    • 2022-11-10
    • 2021-09-27
    • 2018-02-14
    • 2020-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多