将数据聚类到可变大小的 bin 中

机器算法验证 算法 变化点 聚合 分箱 时间序列分割
2022-04-21 16:47:38

我想建立一个模型(在 R 或 excel 中),它接收大量线性数据并将其分割成“bins”。线性数据是反映该部分/记录处于何种条件的属性。

我想简化数据并将其概括为如下示例所示的部分。这也许可以基于具有置信区间的正态分布或您可能推荐的任何其他方法来完成。这个想法是将数据聚合到更通用的箱中。每个 bin 必须至少有 4 个部分/记录,但不超过 10 个。

简而言之,我想做以下事情:

  1. 我想要一种聚类方法来对我的数据进行分组
  2. 我希望每个小组总是有 4-10 名成员
  3. 我希望每个组中的项目是连续的,例如A001_002不会与A001_999

这是我想要执行的操作的示例,然后是数据转储。请注意,我有大约 100,000 个这些点。

条件评级示例

Section_ID  Condition_Rating
A001_001    3
A001_002    2
A001_003    1
A001_004    3
A001_005    4
A001_006    5
A001_007    0
A001_008    0
A001_009    0
A001_010    1
A001_011    2
A001_012    3
A001_013    2
A001_014    8
A001_015    9
A001_016    10
A001_017    9
A001_018    8
A001_019    2
A001_020    3
A001_021    4
A001_022    9
A001_023    5
A001_024    3
1个回答

使受这些约束的组方差总和最小化的动态程序是简单且相当快的,特别是对于如此狭窄的组大小范围。它重现了发布的解决方案。

数字

数据被绘制为点符号。这些组采用颜色编码并由垂直线分隔。组均值绘制为水平线。

注释R代码如下。它递归地计算解决方案,通过在执行过程中缓存结果来提高效率。该程序通过在所有可行的长度窗口中搜索从index开始,找到(并记录)从indexcluster(x,i)开始的最佳解决方案它返回到目前为止找到的最佳值(并且,在全局变量中,留下了每个组开始的索引的指示符)。它可以在几秒钟内处理数千个元素的数组,具体取决于范围的大小。对于较大的问题,必须改进它以包括一些分支定界启发式算法以限制搜索量。ixn.minn.maxicache$Breaksn.max-n.min

#
# Univariate minimum-variance clustering with constraints.
# Requires a global data structure `cache`.
#
cluster <- function(x, i) { 
  #
  # Cluster x[i:length(x)] recursively.
  # Begin with the terminal cases.
  #
  if (i > cache$Length) return(0)                    # Nothing to process   $
  cache$Breaks[i] <<- FALSE                          # Unmark this break    $
  if (i + cache$n.min - 1 > cache$Length) return(Inf)# Interval is too short
  if (!is.na(v <- cache$Cache[i])) return(v)         # Use the cached value $
  n.min <- cache$n.min + i-1                         # Start of search      $
  n.max <- min(cache$n.max + i-1, cache$Length)      # End of search
  if (n.max < n.min) return(0)                       # Prevents `R` errors
  #
  # The recursion: accumulate the best total within-group variances.
  # To implement other objective functions, replace `var` by any measure of
  # within-group homogeneity.
  #
  values <- sapply(n.min:n.max, function(k) var(x[i:k]) + cluster(x, k+1))
  #
  # Find and store the best result.
  #              
  j <- which.min(values) 
  cache$Breaks[n.min + j] <<- TRUE  # Mark this as a good break $
  cache$Cache[i] <<- values[j]      # Cache the result          $
  return(values[j])                 # Pass it to the caller
}
#
# The data.
#
x <- c(3,2,1,3,4,5,0,0,0,1,2,3,2,8,9,10,9,8,2,3,4,9,5,3)
#
# Initialize `cache` to specify the constraints; and run the clustering.
#
system.time({
  n <- length(x)
  cache <- list(n.min=4, n.max=10,      # The length constraints
                Cache=rep(NA, n),       # Values already found
                Breaks=rep(FALSE, n+1), # Group start indexes
                Length=n)               # Cache size
  cluster(x, 1)           # I.e., process x[1:n]
  cache$Breaks[1] <- TRUE # Indicate the start of the first group $
})
#
# Display the results.
#
breaks <- (1:(n+1))[cache$Breaks]                # Group start indexes $
groups <- cumsum(cache$Breaks[-(n+1)])           # Group identifiers
averages <- tapply(x, groups, mean)              # Group summaries
colors <- terrain.colors(max(groups))            # Group plotting colors

plot(x, pch=21, bg=colors[groups], ylab="Rating")
abline(v = breaks-1/2, col="Gray")
invisible(mapply(function(left, right, height, color) {
  lines(c(left, right)-1/2, c(height, height), col=color, lwd=2)
}, breaks[-length(breaks)], breaks[-1], averages, colors))