@@ -486,6 +486,12 @@ impl MetaBuilder {
486486
487487 /// Adds a function/transformation term
488488 pub fn push_function_term ( & mut self , fname : & str , args : & [ Argument ] ) {
489+ // Special handling for categorical functions
490+ if fname == "c" || fname == "factor" {
491+ self . push_categorical_term_with_name ( fname, args) ;
492+ return ;
493+ }
494+
489495 let base_ident = args. iter ( ) . find_map ( |a| match a {
490496 Argument :: Ident ( s) => Some ( s. as_str ( ) ) ,
491497 _ => None ,
@@ -510,6 +516,47 @@ impl MetaBuilder {
510516 }
511517 }
512518
519+ /// Handles categorical variables with reference level specification
520+ fn push_categorical_term_with_name ( & mut self , fname : & str , args : & [ Argument ] ) {
521+ // Extract the variable name (first argument)
522+ let var_name = args. iter ( ) . find_map ( |a| match a {
523+ Argument :: Ident ( s) => Some ( s. as_str ( ) ) ,
524+ _ => None ,
525+ } ) ;
526+
527+ if let Some ( var_name) = var_name {
528+ self . ensure_variable ( var_name) ;
529+
530+ // Add both Categorical and FixedEffect roles
531+ self . add_role ( var_name, VariableRole :: Categorical ) ;
532+ self . add_role ( var_name, VariableRole :: FixedEffect ) ;
533+
534+ // Extract reference level from named arguments
535+ let ref_level = args. iter ( ) . find_map ( |a| match a {
536+ Argument :: Named ( key, value) if key == "ref" => Some ( value. clone ( ) ) ,
537+ _ => None ,
538+ } ) ;
539+
540+ // Create transformation info with reference level
541+ let mut parameters = self . extract_function_parameters ( fname, args) ;
542+ if let Some ( ref_level) = ref_level {
543+ if let serde_json:: Value :: Object ( ref mut params_map) = parameters {
544+ params_map. insert ( "ref" . to_string ( ) , serde_json:: Value :: String ( ref_level) ) ;
545+ }
546+ }
547+
548+ let generates_columns = self . generate_transformation_columns ( fname, args) ;
549+
550+ let transformation = Transformation {
551+ function : fname. to_string ( ) ,
552+ parameters,
553+ generates_columns,
554+ } ;
555+
556+ self . add_transformation ( var_name, transformation) ;
557+ }
558+ }
559+
513560 /// Handles random effects with variable-centric approach
514561 pub fn push_random_effect ( & mut self , random_effect : & RandomEffect ) {
515562 self . is_random_effects_model = true ;
@@ -674,6 +721,10 @@ impl MetaBuilder {
674721 "log" => {
675722 // No additional parameters for log
676723 }
724+ "factor" => {
725+ // Handle factor function parameters (same as c function)
726+ // Parameters are handled by the generic case below
727+ }
677728 _ => {
678729 // Generic parameter handling
679730 for ( i, arg) in args. iter ( ) . enumerate ( ) {
@@ -683,6 +734,11 @@ impl MetaBuilder {
683734 Argument :: String ( s) => serde_json:: Value :: String ( s. clone ( ) ) ,
684735 Argument :: Boolean ( b) => serde_json:: Value :: Bool ( * b) ,
685736 Argument :: Ident ( s) => serde_json:: Value :: String ( s. clone ( ) ) ,
737+ Argument :: Named ( key, value) => {
738+ // For named arguments, use the key directly
739+ params. insert ( key. clone ( ) , serde_json:: Value :: String ( value. clone ( ) ) ) ;
740+ continue ; // Skip the generic arg_N handling
741+ }
686742 } ;
687743 params. insert ( key, value) ;
688744 }
@@ -713,6 +769,11 @@ impl MetaBuilder {
713769 }
714770 }
715771 "log" => vec ! [ format!( "{}_log" , base_name) ] ,
772+ "c" | "factor" => {
773+ // For categorical variables, we generate dummy variables for each level
774+ // The reference level is excluded (handled by the ref parameter)
775+ vec ! [ format!( "{}_categorical" , base_name) ]
776+ }
716777 _ => vec ! [ format!( "{}_{}" , base_name, fname) ] ,
717778 }
718779 }
0 commit comments