save state: wrong idea for indices repartition
[epclust.git] / epclust / R / main.R
index 31ce390..a039d1c 100644 (file)
 #'   }
 #' @param K1 Number of clusters to be found after stage 1 (K1 << N [number of series])
 #' @param K2 Number of clusters to be found after stage 2 (K2 << K1)
-#' @param nb_per_chunk (Maximum) number of items to retrieve in one batch, for both types of
-#'   retrieval: resp. series and contribution; in a vector of size 2
+#' @param nb_series_per_chunk (Maximum) number of series to retrieve in one batch
 #' @param algo_clust1 Clustering algorithm for stage 1. A function which takes (data, K)
 #'   as argument where data is a matrix in columns and K the desired number of clusters,
 #'   and outputs K medoids ranks. Default: PAM
 #' @param algo_clust2 Clustering algorithm for stage 2. A function which takes (dists, K)
 #'   as argument where dists is a matrix of distances and K the desired number of clusters,
 #'   and outputs K clusters representatives (curves). Default: k-means
-#' @param nb_items_clust1 (Maximum) number of items in input of the clustering algorithm
-#'   for stage 1
+#' @param nb_items_clust1 (~Maximum) number of items in input of the clustering algorithm
+#'   for stage 1. At worst, a clustering algorithm might be called with ~2*nb_items_clust1
+#'   items; but this could only happen at the last few iterations.
 #' @param wav_filt Wavelet transform filter; see ?wavelets::wt.filter
 #' @param contrib_type Type of contribution: "relative", "logit" or "absolute" (any prefix)
 #' @param WER "end" to apply stage 2 after stage 1 has fully iterated, or "mix" to apply
 #' series = do.call( cbind, lapply( 1:6, function(i)
 #'   do.call(cbind, wmtsa::wavBootstrap(ref_series[i,], n.realization=400)) ) )
 #' #dim(series) #c(2400,10001)
-#' medoids_ascii = claws(series, K1=60, K2=6, nb_per_chunk=c(200,500), verbose=TRUE)
+#' medoids_ascii = claws(series, K1=60, K2=6, 200, verbose=TRUE)
 #'
 #' # Same example, from CSV file
 #' csv_file = "/tmp/epclust_series.csv"
 #' write.table(series, csv_file, sep=",", row.names=FALSE, col.names=FALSE)
-#' medoids_csv = claws(csv_file, K1=60, K2=6, nb_per_chunk=c(200,500))
+#' medoids_csv = claws(csv_file, K1=60, K2=6, 200)
 #'
 #' # Same example, from binary file
 #' bin_file <- "/tmp/epclust_series.bin"
@@ -97,7 +97,7 @@
 #' endian <- "little"
 #' binarize(csv_file, bin_file, 500, nbytes, endian)
 #' getSeries <- function(indices) getDataInFile(indices, bin_file, nbytes, endian)
-#' medoids_bin <- claws(getSeries, K1=60, K2=6, nb_per_chunk=c(200,500))
+#' medoids_bin <- claws(getSeries, K1=60, K2=6, 200)
 #' unlink(csv_file)
 #' unlink(bin_file)
 #'
 #'   df_series <- dbGetQuery(series_db, request)
 #'   as.matrix(df_series[,"value"], nrow=serie_length)
 #' }
-#' medoids_db = claws(getSeries, K1=60, K2=6, nb_per_chunk=c(200,500))
+#' medoids_db = claws(getSeries, K1=60, K2=6, 200))
 #' dbDisconnect(series_db)
 #'
 #' # All computed medoids should be the same:
 #' digest::sha1(medoids_db)
 #' }
 #' @export
-claws <- function(getSeries, K1, K2, nb_per_chunk,
+claws <- function(getSeries, K1, K2, nb_series_per_chunk,
        nb_items_clust1=7*K1,
        algo_clust1=function(data,K) cluster::pam(data,K,diss=FALSE),
        algo_clust2=function(dists,K) stats::kmeans(dists,K,iter.max=50,nstart=3),
-       wav_filt="d8",contrib_type="absolute",
+       wav_filt="d8", contrib_type="absolute",
        WER="end",
        random=TRUE,
        ntasks=1, ncores_tasks=1, ncores_clust=4,
@@ -155,12 +155,11 @@ claws <- function(getSeries, K1, K2, nb_per_chunk,
        }
        K1 <- .toInteger(K1, function(x) x>=2)
        K2 <- .toInteger(K2, function(x) x>=2)
-       if (!is.numeric(nb_per_chunk) || length(nb_per_chunk)!=2)
-               stop("'nb_per_chunk': numeric, size 2")
-       nb_per_chunk[1] <- .toInteger(nb_per_chunk[1], function(x) x>=1)
-       # A batch of contributions should have at least as many elements as a batch of series,
-       # because it always contains much less values
-       nb_per_chunk[2] <- max(.toInteger(nb_per_chunk[2],function(x) x>=1), nb_per_chunk[1])
+       nb_series_per_chunk <- .toInteger(nb_series_per_chunk, function(x) x>=1)
+       # K1 (number of clusters at step 1) cannot exceed nb_series_per_chunk, because we will need
+       # to load K1 series in memory for clustering stage 2.
+       if (K1 > nb_series_per_chunk)
+               stop("'K1' cannot exceed 'nb_series_per_chunk'")
        nb_items_clust1 <- .toInteger(nb_items_clust1, function(x) x>K1)
        random <- .toLogical(random)
        tryCatch
@@ -236,8 +235,8 @@ claws <- function(getSeries, K1, K2, nb_per_chunk,
                # under Linux. All necessary variables are passed to the workers.
                cl = parallel::makeCluster(ncores_tasks, outfile="")
                varlist = c("getSeries","getContribs","K1","K2","algo_clust1","algo_clust2",
-                       "nb_per_chunk","nb_items_clust","ncores_clust","sep","nbytes","endian",
-                       "verbose","parll")
+                       "nb_series_per_chunk","nb_items_clust","ncores_clust","sep",
+                       "nbytes","endian","verbose","parll")
                if (WER=="mix")
                        varlist = c(varlist, "medoids_file")
                parallel::clusterExport(cl, varlist, envir = environment())