Skip to content

Commit e3d9ae4

Browse files
committed
add chart nodes
1 parent 6bcc370 commit e3d9ae4

14 files changed

+653
-402
lines changed

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ Collate:
3434
'chart_class.R'
3535
'stato_class.R'
3636
'DatasetExperiment_class.R'
37-
'dag_execute.R'
3837
'struct_node_class.R'
38+
'chart_node_class.R'
39+
'dag_execute.R'
3940
'data_node_class.R'
4041
'entity_class.R'
4142
'entity_stato_class.R'

NAMESPACE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
S3method(.DollarNames,DatasetExperiment)
44
S3method(.DollarNames,chart)
5+
S3method(.DollarNames,chart_node)
56
S3method(.DollarNames,data_node)
67
S3method(.DollarNames,iterator)
78
S3method(.DollarNames,metric)
@@ -14,6 +15,7 @@ S3method(.DollarNames,preprocess)
1415
S3method(.DollarNames,resampler)
1516
S3method(.DollarNames,struct_class)
1617
S3method(.DollarNames,struct_node)
18+
export("chart<-")
1719
export("data_value<-")
1820
export("model<-")
1921
export("output_list<-")
@@ -30,6 +32,8 @@ export(as_data_frame)
3032
export(calculate)
3133
export(chart)
3234
export(chart_names)
35+
export(chart_node)
36+
export(chart_node_example)
3337
export(chart_plot)
3438
export(citations)
3539
export(dag_execute)
@@ -104,6 +108,7 @@ exportMethods("*")
104108
exportMethods("+")
105109
exportMethods("[")
106110
exportMethods("[<-")
111+
exportMethods("chart<-")
107112
exportMethods("data_value<-")
108113
exportMethods("max_length<-")
109114
exportMethods("model<-")
@@ -123,6 +128,7 @@ exportMethods(as.DatasetExperiment)
123128
exportMethods(as.SummarizedExperiment)
124129
exportMethods(as.code)
125130
exportMethods(calculate)
131+
exportMethods(chart)
126132
exportMethods(chart_names)
127133
exportMethods(chart_plot)
128134
exportMethods(citations)

R/chart_node_class.R

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#' chart_node class
2+
#'
3+
#' A class for representing chart nodes in a directed acyclic graph (DAG) of struct models.
4+
#' Each node contains a struct chart object that will be executed using chart_plot.
5+
#' Chart nodes can have multiple input objects (e.g., a model and a dataset).
6+
#'
7+
#' @section \code{chart} slot:
8+
#' The "chart" slot holds a struct chart object that will be executed when the node is processed using chart_plot.
9+
#'
10+
#' @section \code{input_objects} slot:
11+
#' The "input_objects" slot holds a list of input objects that will be passed to chart_plot.
12+
#' The order of objects in this list corresponds to the order of parameters in the chart_plot method.
13+
#'
14+
#' @export chart_node
15+
#' @param name the name of the node
16+
#' @param description a description of the node
17+
#' @param chart a struct chart object
18+
#' @param ... additional parameters to pass to struct_class
19+
#' @include generics.R struct_class.R struct_node_class.R chart_class.R
20+
#' @examples
21+
#' C = example_chart()
22+
#' NODE = chart_node(name = 'Example Chart Node', description = 'A simple example chart node', chart = C)
23+
#' @rdname chart_node
24+
chart_node = function(name = character(0), description = character(0), chart = NULL, ...) {
25+
# new object
26+
out = new_struct('chart_node',
27+
name = name,
28+
description = description,
29+
chart = chart,
30+
...)
31+
return(out)
32+
}
33+
34+
.chart_node <- setClass(
35+
"chart_node",
36+
contains = c('struct_node'),
37+
slots = c(
38+
chart = 'struct_class',
39+
input_objects = 'list'
40+
),
41+
prototype = list(
42+
chart = NULL,
43+
input_objects = list()
44+
)
45+
)
46+
47+
#' @rdname chart
48+
#' @export
49+
#' @examples
50+
#' C = example_chart()
51+
#' NODE = chart_node(chart = C)
52+
#' chart(NODE)
53+
#' @return the chart object in the node
54+
setGeneric("chart", function(object) standardGeneric("chart"))
55+
56+
#' @rdname chart
57+
#' @export
58+
setMethod(f = "chart",
59+
signature = c("chart_node"),
60+
definition = function(object) {
61+
return(object@chart)
62+
}
63+
)
64+
65+
#' @rdname chart
66+
#' @export
67+
#' @examples
68+
#' C = example_chart()
69+
#' NODE = chart_node()
70+
#' chart(NODE) = C
71+
#' @return the modified node object
72+
setGeneric("chart<-", function(object, value) standardGeneric("chart<-"))
73+
74+
#' @rdname chart
75+
#' @export
76+
setMethod(f = "chart<-",
77+
signature = c("chart_node", "struct_class"),
78+
definition = function(object, value) {
79+
object@chart = value
80+
return(object)
81+
}
82+
)
83+
84+
setMethod(f = "show",
85+
signature = c("chart_node"),
86+
definition = function(object) {
87+
callNextMethod()
88+
if (!is.null(object@chart)) {
89+
cat('chart: ', class(object@chart)[1], '\n', sep = '')
90+
} else {
91+
cat('chart: NULL\n', sep = '')
92+
}
93+
if (length(object@input_objects) > 0) {
94+
cat('input objects: ', length(object@input_objects), ' objects\n', sep = '')
95+
} else {
96+
cat('input objects: none\n', sep = '')
97+
}
98+
cat('\n')
99+
}
100+
)
101+
102+
# autocompletion
103+
#' @export
104+
#' @rdname autocompletion
105+
#' @method .DollarNames chart_node
106+
.DollarNames.chart_node <- function(x, pattern = "") {
107+
.DollarNames.struct_class(x, pattern)
108+
}
109+
110+
#' @export
111+
#' @rdname autocompletion
112+
setMethod('.DollarNames','chart_node',.DollarNames.chart_node)

R/dag_execute.R

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ setMethod(f = "dag_execute",
6565
# Check that all nodes are valid
6666
for (node_name in names(nodes)) {
6767
node = nodes[[node_name]]
68-
if (!is(node, 'model_node') && !is(node, 'data_node') && !is(node, 'prediction_node')) {
69-
stop('Node "', node_name, '" must be a model_node, data_node, or prediction_node object')
68+
if (!is(node, 'model_node') && !is(node, 'data_node') && !is(node, 'prediction_node') && !is(node, 'chart_node')) {
69+
stop('Node "', node_name, '" must be a model_node, data_node, prediction_node, or chart_node object')
7070
}
7171
}
7272

@@ -333,6 +333,93 @@ setMethod(f = "dag_execute",
333333
cat(' Model output: ', class(result)[1], '\n')
334334
}
335335
}
336+
} else if (is(current_node, 'chart_node')) {
337+
# Chart nodes: execute chart_plot
338+
339+
# Find all input objects for this node
340+
input_objects = list()
341+
342+
# Look for edges pointing to this node
343+
for (edge in dag$edges) {
344+
if (edge$to == current) {
345+
from_node = edge$from
346+
from_param = edge$from_param
347+
to_param = edge$to_param
348+
349+
if (from_node %in% names(results)) {
350+
from_output = results[[from_node]]
351+
from_node_obj = nodes[[from_node]]
352+
353+
# Handle different from_param values
354+
if (from_param == "asis" || is.null(from_param)) {
355+
# Pass the object as-is (for data nodes)
356+
input_value = from_output
357+
} else if (from_param == "predicted") {
358+
# Use predicted() output (for model nodes to other model nodes)
359+
if (is(from_output, 'struct_class') && is(from_output, 'model')) {
360+
tryCatch({
361+
input_value = predicted(from_output)
362+
}, error = function(e) {
363+
stop('Failed to get predicted output from ', from_node, ': ', e$message)
364+
})
365+
} else {
366+
stop('Cannot get predicted output from non-model node: ', from_node)
367+
}
368+
} else {
369+
# Use named slot/parameter
370+
if (is(from_output, 'struct_class')) {
371+
tryCatch({
372+
input_value = from_output[[from_param]]
373+
}, error = function(e) {
374+
stop('Failed to get parameter "', from_param, '" from ', from_node, ': ', e$message)
375+
})
376+
} else {
377+
stop('Cannot get parameter from non-struct object: ', from_node)
378+
}
379+
}
380+
381+
# Handle different to_param values for chart nodes
382+
if (to_param == "input_object" || is.null(to_param)) {
383+
# Add to input_objects list in order
384+
input_objects = c(input_objects, list(input_value))
385+
} else {
386+
# Set named parameter for the chart
387+
chart_obj = chart(current_node)
388+
chart_obj[[to_param]] = input_value
389+
current_node@chart = chart_obj
390+
}
391+
}
392+
}
393+
}
394+
395+
# Set the input_objects for the current node
396+
current_node@input_objects = input_objects
397+
398+
if (verbose) {
399+
cat(' Executing chart: ', class(chart(current_node))[1], ' with chart_plot\n')
400+
}
401+
402+
# Execute the chart using chart_plot
403+
chart_obj = chart(current_node)
404+
405+
# Call chart_plot with the chart object and input objects
406+
if (length(input_objects) == 0) {
407+
# No input objects, just call chart_plot with the chart
408+
result = chart_plot(chart_obj)
409+
} else if (length(input_objects) == 1) {
410+
# Single input object
411+
result = chart_plot(chart_obj, input_objects[[1]])
412+
} else {
413+
# Multiple input objects - use do.call to pass them as arguments
414+
result = do.call(chart_plot, c(list(chart_obj), input_objects))
415+
}
416+
417+
# Store the result (chart_plot returns a plot object)
418+
results[[current]] = result
419+
420+
if (verbose) {
421+
cat(' Chart output: plot object of class ', class(result)[1], '\n')
422+
}
336423
}
337424

338425
# Update in-degrees and add new sources to queue

R/example_objects.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,42 @@ setMethod(f = "chart_plot",
165165
}
166166
)
167167

168+
#' @export
169+
#' @examples
170+
#' # Example of using chart_node with multiple inputs
171+
#' # This shows how to create a chart node that takes both a model and a dataset
172+
#' D = iris_DatasetExperiment()
173+
#' M = structToolbox::glog_transform(qc_label='versicolor',factor_name='Species')
174+
#' M = model_apply(M,D)
175+
#' C = structToolbox::glog_opt_plot()
176+
#'
177+
#' # Create chart node
178+
#' chart_node1 = chart_node(name = 'Glog Chart', description = 'Glog optimization plot', chart = C)
179+
#'
180+
#' # Create DAG with chart node
181+
#' dag = model_dag(
182+
#' name = 'Chart Example DAG',
183+
#' description = 'Example DAG with chart node',
184+
#' edges = list(
185+
#' list(from = 'Data', from_param = 'asis', to = 'Model', to_param = 'input_data'),
186+
#' list(from = 'Model', from_param = 'asis', to = 'Chart', to_param = 'input_object'),
187+
#' list(from = 'Data', from_param = 'asis', to = 'Chart', to_param = 'input_object')
188+
#' ),
189+
#' nodes = list(
190+
#' 'Data' = data_node(name = 'Data', data = D),
191+
#' 'Model' = model_node(name = 'Model', model = M),
192+
#' 'Chart' = chart_node1
193+
#' )
194+
#' )
195+
#'
196+
#' # Execute DAG
197+
#' dag = dag_execute(dag)
198+
chart_node_example = function() {
199+
# This is a placeholder for the example
200+
# The actual example is in the documentation above
201+
return(NULL)
202+
}
203+
168204

169205

170206

0 commit comments

Comments
 (0)