aboutsummaryrefslogtreecommitdiff
path: root/execs/R/cpdbench_ecp.R
blob: 84131c20944210756e6aae8468bfcf3d3cf04f2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#' ---
#' title: Wrapper for ecp package in TCPDBench
#' author: G.J.J. van den Burg
#' date: 2019-09-29
#' license: See LICENSE file.
#' copyright: 2019, The Alan Turing Institute
#' ---

library(argparse)
library(ecp)

load.utils <- function() {
    # get the name of the current script so we can load utils.R (yay, R!)
    cmd.args <- commandArgs(trailingOnly=F)
    file.arg <- "--file="
    this.script <- sub(file.arg, "", cmd.args[grep(file.arg, cmd.args)])
    this.dir <- dirname(this.script)
    utils.script <- file.path(this.dir, 'utils.R')
    source(utils.script)
}

parse.args <- function() {
    parser <- ArgumentParser(description='Wrapper for ecp package')
    parser$add_argument('-i', 
                        '--input',
                        help='path to the input data file',
                        required=TRUE
    )
    parser$add_argument('-o',
                        '--output',
                        help='path to the output file'
    )
    parser$add_argument('-a',
                        '--algorithm',
                        help='algorithm to use',
                        choices=c('e.agglo', 'e.divisive', 'kcpa'),
                        required=TRUE
                        )
    parser$add_argument('--alpha',
                        type='double',
                        help='alpha parameter for agglo and divisive')
    parser$add_argument('--minsize',
                        help='minsize argument for e.divisive',
                        type='integer', default=30)
    parser$add_argument('-R', '--runs',
                        help='number of random permutations to use',
                        type='integer', default=199)
    parser$add_argument('--siglvl',
                        type='double',
                        help='Significance level to use for tests')
    # No examples are provided in the ecp package documentation about 
    # reasonable values for C, so we use 1 as default.
    parser$add_argument('-C', '--cost',
                        type='double',
                        help='cost to use in the kcpa algorithm',
                        default=1)
    parser$add_argument('-L', '--maxcp',
                        help='maximum number of cps in kcpa algorithm',
                        choices=c('max', 'default')
    )
    return(parser$parse_args())
}

main <- function() {
    args <- parse.args()

    # load the dataset
    data <- load.dataset(args$input)

    # copy defaults from the ecp package
    defaults <- list()
    if (args$algorithm == 'e.divisive') {
        defaults$k <- 'null'
    }
    if (args$algorithm == 'kcpa') {
        # Again, we don't want to limit the number of change points a priori, 
        # so set the maximum to the length of the series.
        if (args$maxcp == 'max')
            defaults$L <- data$original$n_obs
        else
            defaults$L <- 5 # following binseg and segneigh default
    }
    params <- make.param.list(args, defaults)

    start.time <- Sys.time()
    result <- tryCatch({
        if (args$algorithm == 'e.agglo') {
            out <- e.agglo(data$mat, alpha=params$alpha)
            locs <- out$estimates
        } else if (args$algorithm == 'e.divisive') {
            out <- e.divisive(data$mat, sig.lvl=params$siglvl, R=params$runs,
                              min.size=params$minsize, alpha=params$alpha)
            locs <- out$estimates
        } else {
            # kcpa
            out <- kcpa(data$mat, params$L, params$cost)
            locs <- out
        }
        list(locations=locs, error=NULL)
    }, error=function(e) {
        return(list(locations=NULL, error=e$message))
    })

    stop.time <- Sys.time()
    runtime <- difftime(stop.time, start.time, units='secs')

    if (!is.null(result$error))
        exit.with.error(data$original, args, params, result$error)

    # convert to 0-based indices
    locations <- as.list(result$locations - 1)

    exit.success(data$original, args, params, locations, runtime)
}

load.utils()
main()