Mercurial > repos > guerler > charts
comparison charts.r @ 4:b079b17dcb4a draft
Uploaded
author | guerler |
---|---|
date | Thu, 17 Apr 2014 11:19:54 -0400 |
parents | |
children | f294f5c9608c |
comparison
equal
deleted
inserted
replaced
3:65f4abe945aa | 4:b079b17dcb4a |
---|---|
1 #!/usr/bin/Rscript | |
2 | |
3 #' Returns file name of calling Rscript | |
4 #' | |
5 #' \code{get_Rscript_filename} returns the file name of calling Rscript | |
6 #' @return A string with the filename of the calling script. | |
7 #' If not found (i.e. you are in a interactive session) returns NA. | |
8 #' | |
9 #' @export | |
10 get_Rscript_filename <- function() { | |
11 prog <- sub("--file=", "", grep("--file=", commandArgs(), value=TRUE)[1]) | |
12 if( .Platform$OS.type == "windows") { | |
13 prog <- gsub("\\\\", "\\\\\\\\", prog) | |
14 } | |
15 prog | |
16 } | |
17 | |
18 #' Recursively sorts a list | |
19 #' | |
20 #' \code{sort_list} returns a sorted list | |
21 #' @param unsorted_list A list. | |
22 #' @return A sorted list. | |
23 #' @export | |
24 sort_list <- function(unsorted_list) { | |
25 for(ii in seq(along=unsorted_list)) { | |
26 if(is.list(unsorted_list[[ii]])) { | |
27 unsorted_list[[ii]] <- sort_list(unsorted_list[[ii]]) | |
28 } | |
29 } | |
30 unsorted_list[sort(names(unsorted_list))] | |
31 } | |
32 | |
33 #' #!/path/to/Rscript | |
34 #' library('getopt'); | |
35 #' #get options, using the spec as defined by the enclosed list. | |
36 #' #we read the options from the default: commandArgs(TRUE). | |
37 #' spec = matrix(c( | |
38 #' 'verbose', 'v', 2, "integer", | |
39 #' 'help' , 'h', 0, "logical", | |
40 #' 'count' , 'c', 1, "integer", | |
41 #' 'mean' , 'm', 1, "double", | |
42 #' 'sd' , 's', 1, "double" | |
43 #' ), byrow=TRUE, ncol=4); | |
44 #' opt = getopt(spec); | |
45 #' | |
46 #' # if help was asked for print a friendly message | |
47 #' # and exit with a non-zero error code | |
48 #' if ( !is.null(opt$help) ) { | |
49 #' cat(getopt(spec, usage=TRUE)); | |
50 #' q(status=1); | |
51 #' } | |
52 #' | |
53 #' #set some reasonable defaults for the options that are needed, | |
54 #' #but were not specified. | |
55 #' if ( is.null(opt$mean ) ) { opt$mean = 0 } | |
56 #' if ( is.null(opt$sd ) ) { opt$sd = 1 } | |
57 #' if ( is.null(opt$count ) ) { opt$count = 10 } | |
58 #' if ( is.null(opt$verbose ) ) { opt$verbose = FALSE } | |
59 #' | |
60 #' #print some progress messages to stderr, if requested. | |
61 #' if ( opt$verbose ) { write("writing...",stderr()); } | |
62 #' | |
63 #' #do some operation based on user input. | |
64 #' cat(paste(rnorm(opt$count,mean=opt$mean,sd=opt$sd),collapse="\n")); | |
65 #' cat("\n"); | |
66 #' | |
67 #' #signal success and exit. | |
68 #' #q(status=0); | |
69 getopt = function (spec=NULL,opt=commandArgs(TRUE),command=get_Rscript_filename(),usage=FALSE,debug=FALSE) { | |
70 | |
71 # littler compatibility - map argv vector to opt | |
72 if (exists("argv", where = .GlobalEnv, inherits = FALSE)) { | |
73 opt = get("argv", envir = .GlobalEnv); | |
74 } | |
75 | |
76 ncol=4; | |
77 maxcol=6; | |
78 col.long.name = 1; | |
79 col.short.name = 2; | |
80 col.has.argument = 3; | |
81 col.mode = 4; | |
82 col.description = 5; | |
83 | |
84 flag.no.argument = 0; | |
85 flag.required.argument = 1; | |
86 flag.optional.argument = 2; | |
87 | |
88 result = list(); | |
89 result$ARGS = vector(mode="character"); | |
90 | |
91 #no spec. fail. | |
92 if ( is.null(spec) ) { | |
93 stop('argument "spec" must be non-null.'); | |
94 | |
95 #spec is not a matrix. attempt to coerce, if possible. issue a warning. | |
96 } else if ( !is.matrix(spec) ) { | |
97 if ( length(spec)/4 == as.integer(length(spec)/4) ) { | |
98 warning('argument "spec" was coerced to a 4-column (row-major) matrix. use a matrix to prevent the coercion'); | |
99 spec = matrix( spec, ncol=ncol, byrow=TRUE ); | |
100 } else { | |
101 stop('argument "spec" must be a matrix, or a character vector with length divisible by 4, rtfm.'); | |
102 } | |
103 | |
104 #spec is a matrix, but it has too few columns. | |
105 } else if ( dim(spec)[2] < ncol ) { | |
106 stop(paste('"spec" should have at least ",ncol," columns.',sep='')); | |
107 | |
108 #spec is a matrix, but it has too many columns. | |
109 } else if ( dim(spec)[2] > maxcol ) { | |
110 stop(paste('"spec" should have no more than ",maxcol," columns.',sep='')); | |
111 | |
112 #spec is a matrix, and it has some optional columns. | |
113 } else if ( dim(spec)[2] != ncol ) { | |
114 ncol = dim(spec)[2]; | |
115 } | |
116 | |
117 #sanity check. make sure long names are unique, and short names are unique. | |
118 if ( length(unique(spec[,col.long.name])) != length(spec[,col.long.name]) ) { | |
119 stop(paste('redundant long names for flags (column ',col.long.name,').',sep='')); | |
120 } | |
121 if ( length(na.omit(unique(spec[,col.short.name]))) != length(na.omit(spec[,col.short.name])) ) { | |
122 stop(paste('redundant short names for flags (column ',col.short.name,').',sep='')); | |
123 } | |
124 # convert numeric type to double type | |
125 spec[,4] <- gsub("numeric", "double", spec[,4]) | |
126 | |
127 # if usage=TRUE, don't process opt, but generate a usage string from the data in spec | |
128 if ( usage ) { | |
129 ret = ''; | |
130 ret = paste(ret,"Usage: ",command,sep=''); | |
131 for ( j in 1:(dim(spec))[1] ) { | |
132 ret = paste(ret,' [-[-',spec[j,col.long.name],'|',spec[j,col.short.name],']',sep=''); | |
133 if (spec[j,col.has.argument] == flag.no.argument) { | |
134 ret = paste(ret,']',sep=''); | |
135 } else if (spec[j,col.has.argument] == flag.required.argument) { | |
136 ret = paste(ret,' <',spec[j,col.mode],'>]',sep=''); | |
137 } else if (spec[j,col.has.argument] == flag.optional.argument) { | |
138 ret = paste(ret,' [<',spec[j,col.mode],'>]]',sep=''); | |
139 } | |
140 } | |
141 # include usage strings | |
142 if ( ncol >= 5 ) { | |
143 max.long = max(apply(cbind(spec[,col.long.name]),1,function(x)length(strsplit(x,'')[[1]]))); | |
144 ret = paste(ret,"\n",sep=''); | |
145 for (j in 1:(dim(spec))[1] ) { | |
146 ret = paste(ret,sprintf(paste(" -%s|--%-",max.long,"s %s\n",sep=''), | |
147 spec[j,col.short.name],spec[j,col.long.name],spec[j,col.description] | |
148 ),sep=''); | |
149 } | |
150 } | |
151 else { | |
152 ret = paste(ret,"\n",sep=''); | |
153 } | |
154 return(ret); | |
155 } | |
156 | |
157 #XXX check spec validity here. e.g. column three should be convertible to integer | |
158 | |
159 i = 1; | |
160 | |
161 while ( i <= length(opt) ) { | |
162 if ( debug ) print(paste("processing",opt[i])); | |
163 | |
164 current.flag = 0; #XXX use NA | |
165 optstring = opt[i]; | |
166 | |
167 | |
168 #long flag | |
169 if ( substr(optstring, 1, 2) == '--' ) { | |
170 if ( debug ) print(paste(" long option:",opt[i])); | |
171 | |
172 optstring = substring(optstring,3); | |
173 | |
174 this.flag = NA; | |
175 this.argument = NA; | |
176 kv = strsplit(optstring, '=')[[1]]; | |
177 if ( !is.na(kv[2]) ) { | |
178 this.flag = kv[1]; | |
179 this.argument = paste(kv[-1], collapse="="); | |
180 } else { | |
181 this.flag = optstring; | |
182 } | |
183 | |
184 rowmatch = grep( this.flag, spec[,col.long.name],fixed=TRUE ); | |
185 | |
186 #long flag is invalid, matches no options | |
187 if ( length(rowmatch) == 0 ) { | |
188 stop(paste('long flag "', this.flag, '" is invalid', sep='')); | |
189 | |
190 #long flag is ambiguous, matches too many options | |
191 } else if ( length(rowmatch) > 1 ) { | |
192 # check if there is an exact match and use that | |
193 rowmatch = which(this.flag == spec[,col.long.name]) | |
194 if(length(rowmatch) == 0) { | |
195 stop(paste('long flag "', this.flag, '" is ambiguous', sep='')); | |
196 } | |
197 } | |
198 | |
199 #if we have an argument | |
200 if ( !is.na(this.argument) ) { | |
201 #if we can't accept the argument, bail out | |
202 if ( spec[rowmatch, col.has.argument] == flag.no.argument ) { | |
203 stop(paste('long flag "', this.flag, '" accepts no arguments', sep='')); | |
204 | |
205 #otherwise assign the argument to the flag | |
206 } else { | |
207 storage.mode(this.argument) = spec[rowmatch, col.mode]; | |
208 result[spec[rowmatch, col.long.name]] = this.argument; | |
209 i = i + 1; | |
210 next; | |
211 } | |
212 | |
213 #otherwise, we don't have an argument | |
214 } else { | |
215 #if we require an argument, bail out | |
216 ###if ( spec[rowmatch, col.has.argument] == flag.required.argument ) { | |
217 ### stop(paste('long flag "', this.flag, '" requires an argument', sep='')); | |
218 | |
219 #long flag has no attached argument. set flag as present. set current.flag so we can peek ahead later and consume the argument if it's there | |
220 ###} else { | |
221 result[spec[rowmatch, col.long.name]] = TRUE; | |
222 current.flag = rowmatch; | |
223 ###} | |
224 } | |
225 | |
226 #short flag(s) | |
227 } else if ( substr(optstring, 1, 1) == '-' ) { | |
228 if ( debug ) print(paste(" short option:",opt[i])); | |
229 | |
230 these.flags = strsplit(optstring,'')[[1]]; | |
231 | |
232 done = FALSE; | |
233 for ( j in 2:length(these.flags) ) { | |
234 this.flag = these.flags[j]; | |
235 rowmatch = grep( this.flag, spec[,col.short.name],fixed=TRUE ); | |
236 | |
237 #short flag is invalid, matches no options | |
238 if ( length(rowmatch) == 0 ) { | |
239 stop(paste('short flag "', this.flag, '" is invalid', sep='')); | |
240 | |
241 #short flag is ambiguous, matches too many options | |
242 } else if ( length(rowmatch) > 1 ) { | |
243 stop(paste('short flag "', this.flag, '" is ambiguous', sep='')); | |
244 | |
245 #short flag has an argument, but is not the last in a compound flag string | |
246 } else if ( j < length(these.flags) & spec[rowmatch,col.has.argument] == flag.required.argument ) { | |
247 stop(paste('short flag "', this.flag, '" requires an argument, but has none', sep='')); | |
248 | |
249 #short flag has no argument, flag it as present | |
250 } else if ( spec[rowmatch,col.has.argument] == flag.no.argument ) { | |
251 result[spec[rowmatch, col.long.name]] = TRUE; | |
252 done = TRUE; | |
253 | |
254 #can't definitively process this flag yet, need to see if next option is an argument or not | |
255 } else { | |
256 result[spec[rowmatch, col.long.name]] = TRUE; | |
257 current.flag = rowmatch; | |
258 done = FALSE; | |
259 } | |
260 } | |
261 if ( done ) { | |
262 i = i + 1; | |
263 next; | |
264 } | |
265 } | |
266 | |
267 #invalid opt | |
268 if ( current.flag == 0 ) { | |
269 stop(paste('"', optstring, '" is not a valid option, or does not support an argument', sep='')); | |
270 #TBD support for positional args | |
271 #if ( debug ) print(paste('"', optstring, '" not a valid option. It is appended to getopt(...)$ARGS', sep='')); | |
272 #result$ARGS = append(result$ARGS, optstring); | |
273 | |
274 # some dangling flag, handle it | |
275 } else if ( current.flag > 0 ) { | |
276 if ( debug ) print(' dangling flag'); | |
277 if ( length(opt) > i ) { | |
278 peek.optstring = opt[i + 1]; | |
279 if ( debug ) print(paste(' peeking ahead at: "',peek.optstring,'"',sep='')); | |
280 | |
281 #got an argument. attach it, increment the index, and move on to the next option. we don't allow arguments beginning with '-' UNLESS | |
282 #specfile indicates the value is an "integer" or "double", in which case we allow a leading dash (and verify trailing digits/decimals). | |
283 if ( substr(peek.optstring, 1, 1) != '-' | | |
284 #match negative double | |
285 ( substr(peek.optstring, 1, 1) == '-' | |
286 & regexpr('^-[0123456789]*\\.?[0123456789]+$',peek.optstring) > 0 | |
287 & spec[current.flag, col.mode]== 'double' | |
288 ) | | |
289 #match negative integer | |
290 ( substr(peek.optstring, 1, 1) == '-' | |
291 & regexpr('^-[0123456789]+$',peek.optstring) > 0 | |
292 & spec[current.flag, col.mode]== 'integer' | |
293 ) | |
294 ) { | |
295 if ( debug ) print(paste(' consuming argument *',peek.optstring,'*',sep='')); | |
296 | |
297 storage.mode(peek.optstring) = spec[current.flag, col.mode]; | |
298 result[spec[current.flag, col.long.name]] = peek.optstring; | |
299 i = i + 1; | |
300 | |
301 #a lone dash | |
302 } else if ( substr(peek.optstring, 1, 1) == '-' & length(strsplit(peek.optstring,'')[[1]]) == 1 ) { | |
303 if ( debug ) print(' consuming "lone dash" argument'); | |
304 storage.mode(peek.optstring) = spec[current.flag, col.mode]; | |
305 result[spec[current.flag, col.long.name]] = peek.optstring; | |
306 i = i + 1; | |
307 | |
308 #no argument | |
309 } else { | |
310 if ( debug ) print(' no argument!'); | |
311 | |
312 #if we require an argument, bail out | |
313 if ( spec[current.flag, col.has.argument] == flag.required.argument ) { | |
314 stop(paste('flag "', this.flag, '" requires an argument', sep='')); | |
315 | |
316 #otherwise set flag as present. | |
317 } else if ( | |
318 spec[current.flag, col.has.argument] == flag.optional.argument | | |
319 spec[current.flag, col.has.argument] == flag.no.argument | |
320 ) { | |
321 x = TRUE; | |
322 storage.mode(x) = spec[current.flag, col.mode]; | |
323 result[spec[current.flag, col.long.name]] = x; | |
324 } else { | |
325 stop(paste("This should never happen.", | |
326 "Is your spec argument correct? Maybe you forgot to set", | |
327 "ncol=4, byrow=TRUE in your matrix call?")); | |
328 } | |
329 } | |
330 #trailing flag without required argument | |
331 } else if ( spec[current.flag, col.has.argument] == flag.required.argument ) { | |
332 stop(paste('flag "', this.flag, '" requires an argument', sep='')); | |
333 | |
334 #trailing flag without optional argument | |
335 } else if ( spec[current.flag, col.has.argument] == flag.optional.argument ) { | |
336 x = TRUE; | |
337 storage.mode(x) = spec[current.flag, col.mode]; | |
338 result[spec[current.flag, col.long.name]] = x; | |
339 | |
340 #trailing flag without argument | |
341 } else if ( spec[current.flag, col.has.argument] == flag.no.argument ) { | |
342 x = TRUE; | |
343 storage.mode(x) = spec[current.flag, col.mode]; | |
344 result[spec[current.flag, col.long.name]] = x; | |
345 } else { | |
346 stop("this should never happen (2). please inform the author."); | |
347 } | |
348 #no dangling flag, nothing to do. | |
349 } else { | |
350 } | |
351 | |
352 i = i+1; | |
353 } | |
354 return(result); | |
355 } | |
356 | |
357 # convert multi parameter string (i.e. key1: value, key2: value, ...) to object | |
358 split <- function(argument){ | |
359 # process parameter string | |
360 options <- list() | |
361 list <- gsub("\\s","", argument) | |
362 list <- strsplit(list, ",") | |
363 if (length(list) > 0) { | |
364 list <- list[[1]] | |
365 for (entry in list) { | |
366 pair <- strsplit(entry, ":") | |
367 if (length(pair) > 0) { | |
368 pair <- pair[[1]] | |
369 if (length(pair) == 2) { | |
370 options[[pair[1]]] <- pair[2] | |
371 } | |
372 } | |
373 } | |
374 } | |
375 return(options) | |
376 } | |
377 | |
378 # get options, using the spec as defined by the enclosed list. | |
379 spec = matrix(c( | |
380 'workdir', 'w', 1, 'character', 'Work directory', | |
381 'module', 'm', 1, 'character', 'Module name', | |
382 'input', 'i', 1, 'character', 'Input tabular file', | |
383 'columns', 'c', 1, 'character', 'Columns string', | |
384 'settings', 's', 1, 'character', 'Settings string', | |
385 'output', 'o', 1, 'character', 'Output tabular file', | |
386 'help', 'h', 0, '', 'Help', | |
387 'verbose', 'v', 0, '', 'Verbose' | |
388 ), byrow=TRUE, ncol=5); | |
389 opt = getopt(spec); | |
390 | |
391 # show help | |
392 if ( !is.null(opt$help) || | |
393 is.null(opt$module) || | |
394 is.null(opt$input) || | |
395 is.null(opt$columns) || | |
396 is.null(opt$output)) { | |
397 cat(getopt(spec, usage=TRUE)) | |
398 q(status=1); | |
399 } | |
400 | |
401 # read columns/settings | |
402 columns = split(opt$columns) | |
403 settings = split(opt$settings) | |
404 | |
405 # read table | |
406 table <- read.table(opt$input) | |
407 | |
408 # identify module file | |
409 module_file = paste(opt$workdir, opt$module, '.r', sep='') | |
410 | |
411 # source module | |
412 source(module_file) | |
413 | |
414 # run module | |
415 l = wrapper (table, columns, settings) | |
416 | |
417 # header | |
418 header_title <- '# title - Chart Utilities (charts)' | |
419 header_date <- paste('# date -', Sys.time(), sep=' ') | |
420 header_module <- paste('# module -', opt$module, sep=' ') | |
421 header_settings <- paste('# settings -', opt$settings, sep=' ') | |
422 header_columns <- paste('# columns -', opt$columns, sep=' ') | |
423 | |
424 # fill gaps | |
425 if (length(l) > 0) { | |
426 # print details | |
427 if (!is.null(opt$verbose)) { | |
428 print ('Columns:') | |
429 print (columns) | |
430 print ('Settings:') | |
431 print (settings) | |
432 print ('Result:') | |
433 print (l) | |
434 } | |
435 | |
436 # create output file | |
437 output <- file(opt$output, open='wt') | |
438 | |
439 # write header | |
440 writeLines('#', output) | |
441 writeLines(header_title, output) | |
442 writeLines(header_date, output) | |
443 writeLines(header_module, output) | |
444 writeLines(header_settings, output) | |
445 writeLines(header_columns, output) | |
446 writeLines('#', output) | |
447 | |
448 # write table | |
449 write.table(l, file=output, row.names=FALSE, col.names = FALSE, quote=FALSE, sep='\t') | |
450 | |
451 # close file | |
452 close(output) | |
453 } else { | |
454 print ('Columns:') | |
455 print (columns) | |
456 print ('Settings:') | |
457 print (settings) | |
458 print ('No output generated.') | |
459 } |