@@ -58,6 +58,71 @@ def _positive_int_arg(value: Any) -> int:
5858 return parsed
5959
6060
61+ def _validate_formula_list (value : list [Any ]) -> list [str ]:
62+ """Validate and normalize a JSON/list formula argument."""
63+ if not value :
64+ raise ArgumentTypeError (
65+ "--formula JSON list must contain at least one formula string"
66+ )
67+
68+ normalized : list [str ] = []
69+ for item in value :
70+ if not isinstance (item , str ):
71+ raise ArgumentTypeError (
72+ "--formula JSON list must contain only non-empty formula strings"
73+ )
74+ stripped_item = item .strip ()
75+ if not stripped_item :
76+ raise ArgumentTypeError (
77+ "--formula JSON list must contain only non-empty formula strings"
78+ )
79+ normalized .append (stripped_item )
80+
81+ return normalized
82+
83+
84+ def _formula_arg (value : str | list [str ] | None ) -> str | list [str ] | None :
85+ """Parse a CLI formula value as a string formula or JSON list of formulas.
86+
87+ Args:
88+ value: Raw formula argument supplied by argparse or tests.
89+
90+ Returns:
91+ A formula string, a list of formula strings, or ``None``.
92+
93+ Raises:
94+ ArgumentTypeError: If a formula string is empty, or if a JSON/list
95+ formula is malformed.
96+ """
97+ if value is None :
98+ return None
99+ if isinstance (value , list ):
100+ return _validate_formula_list (value )
101+ if not isinstance (value , str ):
102+ raise ArgumentTypeError (
103+ "--formula must be a formula string, 'None', or a JSON list of formula strings"
104+ )
105+
106+ stripped = value .strip ()
107+ if stripped == "None" :
108+ return None
109+ if not stripped :
110+ raise ArgumentTypeError ("--formula must not be empty" )
111+ if stripped [0 ] not in "[{" :
112+ return stripped
113+
114+ try :
115+ parsed = json .loads (stripped )
116+ except json .JSONDecodeError as exc :
117+ raise ArgumentTypeError ("--formula JSON list must be valid JSON" ) from exc
118+
119+ if not isinstance (parsed , list ):
120+ raise ArgumentTypeError (
121+ "--formula JSON value must be a list of formula strings"
122+ )
123+ return _validate_formula_list (parsed )
124+
125+
61126def _parse_csv_columns_arg (value : Optional [str ], arg_name : str ) -> List [str ]:
62127 """Parse a comma-separated CLI columns argument into a validated list.
63128
@@ -118,7 +183,7 @@ def __init__(self, args: Namespace) -> None:
118183
119184 # Create attributes (to be populated later, which will be used in main)
120185 self ._transformations : Dict [str , Any ] | str | None = None
121- self ._formula : str | None = None
186+ self ._formula : str | list [ str ] | None = None
122187 self ._penalty_factor : None = None
123188 self ._one_hot_encoding : bool = False
124189 self ._max_de : float | None = None
@@ -523,19 +588,19 @@ def transformations(self) -> str | None:
523588 else :
524589 return self .args .transformations
525590
526- def formula (self ) -> str | None :
527- """Return the formula string used for model matrices.
591+ def formula (self ) -> str | list [ str ] | None :
592+ """Return the formula string or formula list used for model matrices.
528593
529594 Returns:
530- Formula string or ``None`` if unset.
595+ Formula string, formula list, or ``None`` if unset.
531596
532597 Examples:
533598 .. code-block:: python
534599 from argparse import Namespace
535600 BalanceCLI(Namespace(formula="age + gender")).formula()
536601 # 'age + gender'
537602 """
538- return self .args .formula
603+ return _formula_arg ( self .args .formula )
539604
540605 def one_hot_encoding (self ) -> bool | None :
541606 """Return the parsed one-hot encoding flag.
@@ -660,7 +725,7 @@ def process_batch(
660725 self ,
661726 batch_df : pd .DataFrame ,
662727 transformations : Dict [str , Any ] | str | None = "default" ,
663- formula : str | None = None ,
728+ formula : str | list [ str ] | None = None ,
664729 penalty_factor : None = None ,
665730 one_hot_encoding : bool = False ,
666731 max_de : float | None = 1.5 ,
@@ -1252,7 +1317,7 @@ def add_arguments_to_parser(parser: ArgumentParser) -> ArgumentParser:
12521317 # True
12531318 """
12541319 # TODO: add checks for validity of input (including None as input)
1255- # TODO: add arguments for formula when used as a list and for penalty_factor
1320+ # TODO: add arguments for penalty_factor
12561321 parser .add_argument (
12571322 "--input_file" ,
12581323 type = Path ,
@@ -1469,13 +1534,16 @@ def add_arguments_to_parser(parser: ArgumentParser) -> ArgumentParser:
14691534 "'default' for default transformations."
14701535 ),
14711536 )
1472- # TODO: we currently support only the option of a string formula (or None), not a list of formulas.
14731537 parser .add_argument (
14741538 "--formula" ,
1539+ type = _formula_arg ,
14751540 default = None ,
14761541 required = False ,
14771542 help = (
1478- "The formula of the model matrix (in ipw or cbps). If None (default), the formula will be setted to an additive formula using all the covariates."
1543+ "The formula of the model matrix (in ipw or cbps). If None "
1544+ "(default), the formula is set to an additive formula using all "
1545+ 'covariates. Pass a JSON list (for example, \' ["age", "gender"]\' ) '
1546+ "to build and concatenate multiple model-matrix formula terms."
14791547 ),
14801548 )
14811549 parser .add_argument (
0 commit comments