Gurobi / gurobipy-pandas

Convenience wrapper for building optimization models from pandas data
https://gurobipy-pandas.readthedocs.io
Apache License 2.0
92 stars 15 forks source link

Edge cases for string expression evaluation #43

Closed simonbowly closed 1 year ago

simonbowly commented 2 years ago

Due to the way we evaluate expressions and look up columns we have issues with column names containing spaces (or presumably other characters which might make them invalid python variables names):

>>> m = gp.Model()
>>> df = pd.DataFrame({"a": [1, 2, 3], "b": [4,5,6]})
>>> df = df.grb.pd_add_vars(m, name="ab cd")
>>> m.update()
>>> df
   a  b                  ab cd
0  1  4  <gurobi.Var ab cd[0]>
1  2  5  <gurobi.Var ab cd[1]>
2  3  6  <gurobi.Var ab cd[2]>
>>> df.grb.pd_add_constrs(m, "ab cd", gp.GRB.EQUAL, 1, name="c1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/bowly/workspaces/gurobipy-pandas/src/gurobipy_pandas/accessors.py", line 173, in pd_add_constrs
    return self._add_constrs_by_args(model, lhs, sense, rhs, name=name)
  File "/Users/bowly/workspaces/gurobipy-pandas/src/gurobipy_pandas/accessors.py", line 177, in _add_constrs_by_args
    c = [
  File "/Users/bowly/workspaces/gurobipy-pandas/src/gurobipy_pandas/accessors.py", line 180, in <listcomp>
    lhs=getattr(row, lhs) if lhs in self._obj.columns else lhs,
AttributeError: 'Pandas' object has no attribute 'ab cd'
>>> df.grb.pd_add_constrs(m, "`ab cd` == 1", name="c2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/bowly/workspaces/gurobipy-pandas/src/gurobipy_pandas/accessors.py", line 171, in pd_add_constrs
    return self._add_constrs_by_expression(model, lhs, name=name)
  File "/Users/bowly/workspaces/gurobipy-pandas/src/gurobipy_pandas/accessors.py", line 207, in _add_constrs_by_expression
    "lhs": eval(lhs, None, self._obj),
  File "<string>", line 1
    `ab cd` 
    ^
SyntaxError: invalid syntax

The first case should definitely work and is easy to fix. The second case should work in theory since df.eval("ab cd", engine='python') works, but we can't use pandas's eval method as it doesn't play well with operators on object dtype arrays.

simonbowly commented 1 year ago

Done. User can reference column names correctly even if the names are not valid python names. Backtick syntax from pd.DataFrame.eval is also now supported.