@@ -335,6 +335,56 @@ function set_field_type_kwargs!(kwargs, observed, implied, loss, O, I)
335335 end
336336end
337337
338+ # build ensemble/multi-group observed from the specification and Sem(...) kwargs
339+ # used by Sem(...) and replace_observed()
340+ function build_ensemble_observed (observed_type, spec:: EnsembleParameterTable , kwargs)
341+ if ! haskey (kwargs, :data )
342+ @warn """
343+ No data provided for ensemble SEM model. Each SEM term will be constructed with empty data.
344+ To provide data for each term, pass a DataFrame with a column identifying the term groups or a Dict mapping term ids to data
345+ """
346+ semterms_data = nothing
347+ else
348+ kwdata = kwargs[:data ]
349+ if isa (kwdata, AbstractDataFrame)
350+ semterm_col = get (kwargs, :semterm_column , nothing )
351+ isnothing (semterm_col) &&
352+ throw (ArgumentError (" No semterm_column specified for ensemble data." ))
353+ semterms_data = Dict (
354+ g[semterm_col] => group_data for
355+ (g, group_data) in pairs (groupby (kwdata, semterm_col))
356+ )
357+ elseif isa (kwdata, AbstractDict)
358+ semterms_data = kwdata
359+ else
360+ """
361+ Unsupported data type for ensemble SEM model: $(typeof (kwdata)) .
362+ Provide a DataFrame with a column identifying the term groups or a Dict mapping term ids to data.
363+ """ |>
364+ ArgumentError |>
365+ throw
366+ end
367+ unused_term_ids = setdiff (keys (semterms_data), keys (spec. tables))
368+ isempty (unused_term_ids) ||
369+ @warn " Ignoring data with ids=$(collect (unused_term_ids)) : no such SEM terms exist"
370+ end
371+
372+ # construct SemObserved for each term
373+ return Dict (
374+ term_id => begin
375+ term_kwargs = copy (kwargs)
376+ if ! isnothing (semterms_data)
377+ term_data = get (semterms_data, term_id, nothing )
378+ isnothing (term_data) &&
379+ throw (ArgumentError (" No data provided for SEM term :$term_id " ))
380+ term_kwargs[:data ] = term_data
381+ delete! (term_kwargs, :semterm_column )
382+ end
383+ observed_type (; specification = term_spec, term_kwargs... )
384+ end for (term_id, term_spec) in pairs (spec. tables)
385+ )
386+ end
387+
338388# construct Sem fields
339389function get_fields! (kwargs, spec, observed, implied, loss)
340390 if ! isa (spec, SemSpecification)
@@ -344,10 +394,7 @@ function get_fields!(kwargs, spec, observed, implied, loss)
344394 # observed
345395 if ! isa (observed, SemObserved)
346396 observed = if spec isa EnsembleParameterTable
347- Dict (
348- term_id => observed (; specification = term_spec, kwargs... ) for
349- (term_id, term_spec) in pairs (spec. tables)
350- )
397+ build_ensemble_observed (observed, spec, kwargs)
351398 else
352399 observed (; specification = spec, kwargs... )
353400 end
0 commit comments