87 ) -> Type[DataFrameAction]:
88 """Factory function for producing ConfigurableAction classes which are
89 realizations of arithmetic operations.
90
91 Parameters
92 ----------
93 className : `str`
94 The name of the class that will be produced
95 expr : `str`
96 An arithmetic expression that will be parsed to produce the output
97 ConfigurableAction. Individual variable names will be the name of
98 individual `ConfigActions` inside the expression (i.e. "x+y" will
99 produce an action with configAction.actions.x and
100 configAction.actions.y). Expression can contain arithmatic python
101 operators as well as; sin, cos, sinh, cosh, log (which is base 10).
102 exprDefaults : `Mapping` of `str` to `DataFrameAction` optional
103 A mapping of strings which correspond to the names in the expression to
104 values which are default `ConfigurableActions` to assign in the
105 expression. If no default for a action is supplied `SingleColumnAction`
106 is set as the default.
107 docstring : `str`
108 A string that is assigned as the resulting classes docstring
109
110 Returns
111 -------
112 action : `Type` of `DataFrameAction`
113 A `DataFrameAction` class that was programatically constructed from the
114 input expression.
115 """
116
117
118
119
120 import inspect
121 new_module = inspect.stack()[1].frame.f_locals['__name__']
122 node = ast.parse(expr, mode='eval')
123
124
125 names: Set[str] =
set()
126 for elm in ast.walk(node):
127 if isinstance(elm, ast.Name):
128 names.add(elm.id)
129
130
131 names -= EXTRA_MATH.keys()
132
133 fields: Mapping[str, ConfigurableActionField] = {}
134 for name in sorted(names):
135 if exprDefaults is not None and (value := exprDefaults.get(name)) is not None:
136 kwargs = {"default": value}
137 else:
138 kwargs = {}
139 fields[name] = ConfigurableActionField(doc=f"expression action {name}", **kwargs)
140
141
142
143 def __call__(self, df: pd.DataFrame, **kwargs) -> pd.Series:
144 values_map = {}
145 for name in fields:
146 values_map[name] = getattr(self, name)(df, **kwargs)
147
148 parser = ExpressionParser(**values_map)
149 return parser.visit(node.body)
150
151
152 def columns(self) -> Iterable[str]:
153 for name in fields:
154 yield from getattr(self, name).columns
155
156 dct: MutableMapping[str, Any] = {"__call__": __call__, "columns": property(columns)}
157 if docstring is not None:
158 dct['__doc__'] = docstring
159 dct.update(**fields)
160 dct['__module__'] = new_module
161
162 return type(className, (DataFrameAction, ), dct)
daf::base::PropertySet * set