这是我的第二篇文章,让我们说它早于第一篇文章,我将在此处链接:
creating a matrix/dataframe with two for loops in R
我不会重复我在那里犯的新手错误,所以在这里你会得到一份数据副本:
> dput(head(dfn,1))
structure(c(-0.936707666207839, 0.684585833497428, -1.15671769161442,
-0.325882814790034, 0.334512025995239, 0.335054315282587, 0.0671142954097706,
-0.544867778136127, -0.958378799317135, 1.26734044843021, -0.483611966400142,
-0.0781514731365092, -0.671994127070641, 0.332218249471269, 0.942550991112822,
0.15534532610427, 0.192944412985922, 0.206169118270958, 0.424191119850985,
-0.193936625653784, -0.574273356856365, -0.176553706556564, 0.696013509222779,
0.118827262744793, 0.0649996884597108, 0.470171960447926, -0.570575475596488,
0.336490371668436, 0.475005575251838, 0.010357165551236, 0.284525279467858,
0.523668394513643, -0.0290958105736766, 0.62018540798656, 1.37452329937098,
0.456726128895017), .Dim = c(1L, 36L), .Dimnames = list(NULL,
c("2015-01-30", "2015-02-27", "2015-03-31", "2015-04-30",
"2015-05-29", "2015-06-30", "2015-07-31", "2015-08-31", "2015-09-30",
"2015-10-30", "2015-11-30", "2015-12-31", "2016-01-29", "2016-02-29",
"2016-03-31", "2016-04-29", "2016-05-31", "2016-06-30", "2016-07-29",
"2016-08-31", "2016-09-30", "2016-10-31", "2016-11-30", "2016-12-30",
"2017-01-31", "2017-02-28", "2017-03-31", "2017-04-28", "2017-05-31",
"2017-06-30", "2017-07-31", "2017-08-31", "2017-09-29", "2017-10-31",
"2017-11-30", "2017-12-29")))
这是一个包含36个时间帧的417行的时间序列数据库(过去3年中每个月)。
这是我用来创建数据框列表的代码:
ProgrSubset <- function(x,i) { x[,i:sum(i,11)] }
dfList <- lapply(1:25, function(x) ProgrSubset(dfn, x) )
dfList是一个包含25个数据帧的列表,通过12个月的滚动窗口从原始数据帧中进行子集化。
现在我想在列表的每个数据帧上运行k-means算法,并将每次迭代的簇数存储在名为it_mat的矩阵中。
但是这里有悲伤,我希望质心能够成为前一次运行的质心(如果它们从第一次运行中得到修复,那么无论如何都会很棒。)
我没有问题&#34;手工&#34;:
it_mat <- cbind(ref_data$sec_id)
k = 18
cl <- kmeans(dfList[[1]], centers = k, nstart = 10)
it_mat <- cbind(it_mat, cl$cluster)
head(it_mat) #first iteration
colnames(cl$centers) <- colnames(dfn[,2:13])
k <- cl$centers
cl <- kmeans(dfList[[2]], centers = k, nstart = 10)
it_mat <- cbind(it_mat, cl$cluster)
head(it_mat) #second iteration
然后应该直接将它循环到数据库列表中,但它是一个没有显示:我设计的for循环只返回一个只有第一次迭代的矩阵:
it_mat <- cbind(ref_data$sec_id)
for(i in 1:25){
if(i == 1){
k = 18
cl <- kmeans(dfList[[i]], centers = k, nstart = 10)
it_mat <- cbind(it_mat, cl$cluster)
}else{
colnames(cl$centers) <- colnames(dfn[,i:i+11])
k = cl$centers
cl <- kmeans(dfList[[i]], centers = k, nstart = 10)
it_mat <- cbind(it_mat, cl$cluster)
}
}
错误之后可能会停止:Error: empty cluster: try a better set of initial centers
?
但我不在乎群集是否为空。
我还尝试在第一次迭代之后循环后续迭代,以便在没有if
和else
的情况下更简单:
for(i in 2:25){
colnames(cl$centers) <- colnames(dfn[,2:13])
k <- cl$centers
cl <- kmeans(dfList[[i]], centers = k, nstart = 10)
it_mat <- cbind(it_mat, cl$cluster)
}
结果仍然相同:只有第一次迭代的矩阵。
我也尝试使用it_mat[ ,i] <- cl$cluster
代替it_mat <- cbind(it_mat, cl$cluster)
,但它是一样的。
我会感谢任何帮助,评论或建议:我可能会像我之前的问题那样犯一些非常愚蠢的错误,或者我选择了一条使我的工作变得复杂的艰难道路。
我的主要目标是了解群集组成在特定时间序列中的变化。
感谢大家的时间。
答案 0 :(得分:1)
这是一种方法,但我无法使用您的小数据集和k
。也许它可以更好地处理您的实际数据。如果您不想知道为什么/如何运作,请跳至 TL; DR 。
Reduce
我使用的技巧是Reduce
,其第一个参数是带有两个参数的函数。一个简单的演示是:
Reduce(function(a,b) 2*a+b, 1:4)
这相当于2*1+2
,然后是2*(2*1+2)+3
等等。也许它目前的形式没有吸引力。让我们进行一些打印,然后“累积”数据:
Reduce(function(a,b) {
cat(paste(c(a,b), collapse=","), "\n")
return(2*a+b)
}, 1:4, accumulate=TRUE)
# 1,2
# 4,3
# 11,4
# [1] 1 4 11 26
因此,函数的第一次调用采用向量1
的第一个元素和第二个元素2
并调用函数。然后它返回值(2*1+2
是4
)和向量3
的第三个元素,并发挥其魔力。等等。
处理Reduce
时通常会做出一个“假设”,即两个值必须与对象的“类型”相同。这不是必须的,所以我会稍微处理一下。
另外需要注意的是,它是从列表的前两个元素开始,这也不是一个严格的要求。如果我们设置init
,我们就可以控制第一次调用时a
的内容。
Reduce(function(a,b) {
cat(paste(c(a,b), collapse=","), "\n")
return(2*a+b)
}, 1:4, init=99, accumulate=TRUE)
# 99,1
# 199,2
# 400,3
# 803,4
# [1] 99 199 400 803 1610
注意列表中的每个元素只用于一次函数调用吗?
kmeans
所以我的技巧是在函数的n
调用中考虑我们想要的东西:我们想要来自n-1
的先前集群对象和n
数据。意识到“previous cluster object”看起来很像上一个例子中的199,400和803。我们将编写一个函数,假定前一个集群对象是第一个参数,数据是第二个参数。
my_cascade_kmeans <- function(prevclust, dat) {
kmeans(dat, centers = prevclust$centers, nstart = 10)
}
Reduce(my_cascade_kmeans, dfList, accumulate = TRUE)
(顺便说一句:我正在收集整个集群输出而不仅仅是中心,因为最终我们想要得到一个集群对象列表。)
问题,因为你很快就会发现(并回想起),第一次调用它时,会使用前两个元素调用它。所以相反,我们想要声明初始值。有两种方法可以解决这个问题:
Reduce(my_cascade_kmeans, dfList, init=list(centers=5), accumulate=TRUE)
这样做的便利性是来自kmeans
的群集对象和静态list(centers=5)
都可以使用$centers
编制索引,并且它们会返回我认为我们需要的内容。
Reduce(my_cascade_kmeans, dfList, init=NULL, accumulate=TRUE)
为了实现这一点,我们需要修改我们的函数以期望NULL
中的prevclust
并相应地处理它。有时这可能会更好。
我更喜欢选项1,因为它在原始k
调用中放置了“默认Reduce
值”,而不一定隐藏在功能代码中。但你可能更喜欢它,而不是你。
对于这个答案,我将初始集群从18减少到4 ......任何更高的集群都会失败Error: empty cluster: try a better set of initial centers
,我猜测这是由于截断的样本数据集。
my_cascade_kmeans <- function(prevclust, dat) {
kmeans(dat, centers = prevclust$centers, nstart = 10)
}
clusters <- Reduce(my_cascade_kmeans, dfList, init = list(centers=4), accumulate = TRUE)
length(clusters)
# [1] 26
你可能会对此犹豫不决,但这就是我们告诉它的事情:“通过将list(centers=4)
添加到开头来初始化向量,然后累积结果”,所以我们不应该感到惊讶的是它比我们开始的时间长一点。
clusters[[1]]
# $centers
# [1] 4
这证实了这一点。用
清理它clusters <- clusters[-1]
现在clusters
中的每一个都是使用前一个
kmeans(...)
返回的
clusters[[1]]
# K-means clustering with 4 clusters of sizes 2, 4, 3, 3
# Cluster means:
# [,1]
# 1 0.9759631
# 2 0.1646323
# 3 -0.4514542
# 4 -1.0172681
# Clustering vector:
# 2015-01-30 2015-02-27 2015-03-31 2015-04-30 2015-05-29 2015-06-30 2015-07-31 2015-08-31 2015-09-30 2015-10-30 2015-11-30
# 4 1 4 3 2 2 2 3 4 1 3
# 2015-12-31
# 2
# Within cluster sum of squares by cluster:
# [1] 0.16980147 0.12635651 0.02552839 0.02940412
# (between_SS / total_SS = 94.0 %)
# Available components:
# [1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss" "size" "iter"
# [9] "ifault"
锦上添花,这也适用于2或2000个数据集。