Index
Pyoframe's public API. Also applies the monkey patch to the DataFrame libraries.
Config
Configuration options that apply to the entire library.
integer_tolerance = 1e-08
class-attribute
instance-attribute
For convenience, Pyoframe returns the solution of integer and binary variables as integers not floating point values. To do so, Pyoframe must convert the solver-provided floating point values to integers. To avoid unexpected rounding errors, Pyoframe uses this tolerance to check that the floating point result is an integer as expected. Overly tight tolerances can trigger unexpected errors. Setting the tolerance to zero disables the check.
print_max_set_elements = 50
class-attribute
instance-attribute
Number of elements to show when printing a set to the console (additional elements are replaced with ...)
reset_defaults()
classmethod
Constraint(lhs, sense)
Bases: ModelElementWithId
A linear programming constraint.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lhs
|
Expression
|
The left hand side of the constraint. |
required |
sense
|
ConstraintSense
|
The sense of the constraint. |
required |
Source code in src\pyoframe\core.py
relax(cost, max=None)
Relaxes the constraint by adding a variable to the constraint that can be non-zero at a cost.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
cost
|
SupportsToExpr
|
The cost of relaxing the constraint. Costs should be positives as they will automatically become negative for maximization problems. |
required |
max
|
Optional[SupportsToExpr]
|
The maximum value of the relaxation variable. |
None
|
Returns:
Type | Description |
---|---|
Constraint
|
The same constraint |
Examples:
>>> m = pf.Model()
>>> m.hours_sleep = pf.Variable(lb=0)
>>> m.hours_day = pf.Variable(lb=0)
>>> m.hours_in_day = m.hours_sleep + m.hours_day == 24
>>> m.maximize = m.hours_day
>>> m.must_sleep = (m.hours_sleep >= 8).relax(cost=2, max=3)
>>> m.optimize()
>>> m.hours_day.solution
16.0
>>> m.maximize += 2 * m.hours_day
>>> m.optimize()
>>> m.hours_day.solution
19.0
Note: .relax() can only be called after the sense of the model has been defined.
>>> m = pf.Model()
>>> m.hours_sleep = pf.Variable(lb=0)
>>> m.hours_day = pf.Variable(lb=0)
>>> m.hours_in_day = m.hours_sleep + m.hours_day == 24
>>> m.must_sleep = (m.hours_sleep >= 8).relax(cost=2, max=3)
Traceback (most recent call last):
...
ValueError: Cannot relax a constraint before the objective sense has been set. Try setting the objective first or using Model(sense=...).
One way to solve this is by setting the sense directly on the model. See how this works fine:
>>> m = pf.Model(sense="max")
>>> m.hours_sleep = pf.Variable(lb=0)
>>> m.hours_day = pf.Variable(lb=0)
>>> m.hours_in_day = m.hours_sleep + m.hours_day == 24
>>> m.must_sleep = (m.hours_sleep >= 8).relax(cost=2, max=3)
And now an example with dimensions:
>>> homework_due_tomorrow = pl.DataFrame({"project": ["A", "B", "C"], "cost_per_hour_underdelivered": [10, 20, 30], "hours_to_finish": [9, 9, 9], "max_underdelivered": [1, 9, 9]})
>>> m.hours_spent = pf.Variable(homework_due_tomorrow[["project"]], lb=0)
>>> m.must_finish_project = (m.hours_spent >= homework_due_tomorrow[["project", "hours_to_finish"]]).relax(homework_due_tomorrow[["project", "cost_per_hour_underdelivered"]], max=homework_due_tomorrow[["project", "max_underdelivered"]])
>>> m.only_one_day = sum("project", m.hours_spent) <= 24
>>> # Relaxing a constraint after it has already been assigned will give an error
>>> m.only_one_day.relax(1)
Traceback (most recent call last):
...
ValueError: .relax() must be called before the Constraint is added to the model
>>> m.attr.Silent = True
>>> m.optimize()
>>> m.maximize.value
-50.0
>>> m.hours_spent.solution
shape: (3, 2)
┌─────────┬──────────┐
│ project ┆ solution │
│ --- ┆ --- │
│ str ┆ f64 │
╞═════════╪══════════╡
│ A ┆ 8.0 │
│ B ┆ 7.0 │
│ C ┆ 9.0 │
└─────────┴──────────┘
Source code in src\pyoframe\core.py
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 |
|
Expression(data)
Bases: ModelElement
, SupportsMath
, SupportPolarsMethodMixin
A linear or quadratic expression.
Examples:
>>> import pandas as pd
>>> df = pd.DataFrame({"item" : [1, 1, 1, 2, 2], "time": ["mon", "tue", "wed", "mon", "tue"], "cost": [1, 2, 3, 4, 5]}).set_index(["item", "time"])
>>> m = pf.Model()
>>> m.Time = pf.Variable(df.index)
>>> m.Size = pf.Variable(df.index)
>>> expr = df["cost"] * m.Time + df["cost"] * m.Size
>>> expr
<Expression size=5 dimensions={'item': 2, 'time': 3} terms=10>
[1,mon]: Time[1,mon] + Size[1,mon]
[1,tue]: 2 Time[1,tue] +2 Size[1,tue]
[1,wed]: 3 Time[1,wed] +3 Size[1,wed]
[2,mon]: 4 Time[2,mon] +4 Size[2,mon]
[2,tue]: 5 Time[2,tue] +5 Size[2,tue]
Source code in src\pyoframe\core.py
is_quadratic
property
Returns True if the expression is quadratic, False otherwise.
Computes in O(1) since expressions are quadratic if and only if self.data contain the QUAD_VAR_KEY column.
Examples:
terms
property
Number of terms across all subexpressions.
Expressions equal to zero count as one term.
Examples:
__add__(other)
Examples:
>>> import pandas as pd
>>> m = pf.Model()
>>> add = pd.DataFrame({"dim1": [1,2,3], "add": [10, 20, 30]}).to_expr()
>>> m.v = Variable(add)
>>> m.v + add
<Expression size=3 dimensions={'dim1': 3} terms=6>
[1]: v[1] +10
[2]: v[2] +20
[3]: v[3] +30
>>> m.v + add + 2
<Expression size=3 dimensions={'dim1': 3} terms=6>
[1]: v[1] +12
[2]: v[2] +22
[3]: v[3] +32
>>> m.v + pd.DataFrame({"dim1": [1,2], "add": [10, 20]})
Traceback (most recent call last):
...
pyoframe.constants.PyoframeError: Failed to add expressions:
<Expression size=3 dimensions={'dim1': 3} terms=3> + <Expression size=2 dimensions={'dim1': 2} terms=2>
Due to error:
Dataframe has unmatched values. If this is intentional, use .drop_unmatched() or .keep_unmatched()
shape: (1, 2)
┌──────┬────────────┐
│ dim1 ┆ dim1_right │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞══════╪════════════╡
│ 3 ┆ null │
└──────┴────────────┘
>>> m.v2 = Variable()
>>> 5 + 2 * m.v2
<Expression size=1 dimensions={} terms=2>
2 v2 +5
Source code in src\pyoframe\core.py
constant(constant)
classmethod
Examples:
Source code in src\pyoframe\core.py
degree()
Returns the degree of the expression (0=constant, 1=linear, 2=quadratic).
Examples:
>>> import pandas as pd
>>> m = pf.Model()
>>> m.v1 = pf.Variable()
>>> m.v2 = pf.Variable()
>>> expr = pd.DataFrame({"dim1": [1, 2, 3], "value": [1, 2, 3]}).to_expr()
>>> expr.degree()
0
>>> expr *= m.v1
>>> expr.degree()
1
>>> expr += (m.v2 ** 2).add_dim("dim1")
>>> expr.degree()
2
Source code in src\pyoframe\core.py
evaluate()
The value of the expression. Only available after the model has been solved.
Examples:
>>> m = pf.Model()
>>> m.X = pf.Variable({"dim1": [1, 2, 3]}, ub=10)
>>> m.expr_1 = 2 * m.X + 1
>>> m.expr_2 = pf.sum(m.expr_1)
>>> m.maximize = m.expr_2 - 3
>>> m.attr.Silent = True
>>> m.optimize()
>>> m.expr_1.evaluate()
shape: (3, 2)
┌──────┬──────────┐
│ dim1 ┆ solution │
│ --- ┆ --- │
│ i64 ┆ f64 │
╞══════╪══════════╡
│ 1 ┆ 21.0 │
│ 2 ┆ 21.0 │
│ 3 ┆ 21.0 │
└──────┴──────────┘
>>> m.expr_2.evaluate()
63.0
Source code in src\pyoframe\core.py
map(mapping_set, drop_shared_dims=True)
Replaces the dimensions that are shared with mapping_set with the other dimensions found in mapping_set.
This is particularly useful to go from one type of dimensions to another. For example, to convert data that is indexed by city to data indexed by country (see example).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mapping_set
|
SetTypes
|
The set to map the expression to. This can be a DataFrame, Index, or another Set. |
required |
drop_shared_dims
|
bool
|
If True, the dimensions shared between the expression and the mapping set are dropped from the resulting expression and repeated rows are summed. If False, the shared dimensions are kept in the resulting expression. |
True
|
Returns:
Type | Description |
---|---|
Expression
|
A new Expression containing the result of the mapping operation. |
Examples:
>>> import polars as pl
>>> pop_data = pl.DataFrame({"city": ["Toronto", "Vancouver", "Boston"], "year": [2024, 2024, 2024], "population": [10, 2, 8]}).to_expr()
>>> cities_and_countries = pl.DataFrame({"city": ["Toronto", "Vancouver", "Boston"], "country": ["Canada", "Canada", "USA"]})
>>> pop_data.map(cities_and_countries)
<Expression size=2 dimensions={'year': 1, 'country': 2} terms=2>
[2024,Canada]: 12
[2024,USA]: 8
>>> pop_data.map(cities_and_countries, drop_shared_dims=False)
<Expression size=3 dimensions={'city': 3, 'year': 1, 'country': 2} terms=3>
[Toronto,2024,Canada]: 10
[Vancouver,2024,Canada]: 2
[Boston,2024,USA]: 8
Source code in src\pyoframe\core.py
rolling_sum(over, window_size)
Calculates the rolling sum of the Expression over a specified window size for a given dimension.
This method applies a rolling sum operation over the dimension specified by over
,
using a window defined by window_size
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
over
|
The name of the dimension (column) over which the rolling sum is calculated. This dimension must exist within the Expression's dimensions. |
required | |
window_size
|
The size of the moving window in terms of number of records. The rolling sum is calculated over this many consecutive elements. |
required |
Returns:
Type | Description |
---|---|
Expression
|
A new Expression instance containing the result of the rolling sum operation. This new Expression retains all dimensions (columns) of the original data, with the rolling sum applied over the specified dimension. |
Examples:
>>> import polars as pl
>>> cost = pl.DataFrame({"item" : [1, 1, 1, 2, 2], "time": [1, 2, 3, 1, 2], "cost": [1, 2, 3, 4, 5]})
>>> m = pf.Model()
>>> m.quantity = pf.Variable(cost[["item", "time"]])
>>> (m.quantity * cost).rolling_sum(over="time", window_size=2)
<Expression size=5 dimensions={'item': 2, 'time': 3} terms=8>
[1,1]: quantity[1,1]
[1,2]: quantity[1,1] +2 quantity[1,2]
[1,3]: 2 quantity[1,2] +3 quantity[1,3]
[2,1]: 4 quantity[2,1]
[2,2]: 4 quantity[2,1] +5 quantity[2,2]
Source code in src\pyoframe\core.py
sum(over)
Examples:
>>> import pandas as pd
>>> m = pf.Model()
>>> df = pd.DataFrame({"item" : [1, 1, 1, 2, 2], "time": ["mon", "tue", "wed", "mon", "tue"], "cost": [1, 2, 3, 4, 5]}).set_index(["item", "time"])
>>> m.quantity = Variable(df.reset_index()[["item"]].drop_duplicates())
>>> expr = (m.quantity * df["cost"]).sum("time")
>>> expr.data
shape: (2, 3)
┌──────┬─────────┬───────────────┐
│ item ┆ __coeff ┆ __variable_id │
│ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ u32 │
╞══════╪═════════╪═══════════════╡
│ 1 ┆ 6.0 ┆ 1 │
│ 2 ┆ 9.0 ┆ 2 │
└──────┴─────────┴───────────────┘
Source code in src\pyoframe\core.py
within(set)
Examples:
>>> import pandas as pd
>>> general_expr = pd.DataFrame({"dim1": [1, 2, 3], "value": [1, 2, 3]}).to_expr()
>>> filter_expr = pd.DataFrame({"dim1": [1, 3], "value": [5, 6]}).to_expr()
>>> general_expr.within(filter_expr).data
shape: (2, 3)
┌──────┬─────────┬───────────────┐
│ dim1 ┆ __coeff ┆ __variable_id │
│ --- ┆ --- ┆ --- │
│ i64 ┆ f64 ┆ u32 │
╞══════╪═════════╪═══════════════╡
│ 1 ┆ 1.0 ┆ 0 │
│ 3 ┆ 3.0 ┆ 0 │
└──────┴─────────┴───────────────┘
Source code in src\pyoframe\core.py
Model(name=None, solver=None, solver_env=None, use_var_names=False, sense=None)
The object that holds all the variables, constraints, and the objective.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
name
|
Optional[str]
|
The name of the model. Currently it is not used for much. |
None
|
solver
|
Optional[SUPPORTED_SOLVER_TYPES]
|
The solver to use. If |
None
|
solver_env
|
Optional[Dict[str, str]]
|
Gurobi only: a dictionary of parameters to set when creating the Gurobi environment. |
None
|
use_var_names
|
bool
|
Whether to pass variable names to the solver. Set to |
False
|
sense
|
Union[ObjSense, ObjSenseValue, None]
|
Either "min" or "max". Indicates whether it's a minmization or maximization problem.
Typically, this parameter can be omitted ( |
None
|
Examples:
>>> m = pf.Model()
>>> m.X = pf.Variable()
>>> m.my_constraint = m.X <= 10
>>> m
<Model vars=1 constrs=1 objective=False>
Try setting the Gurobi license:
>>> m = pf.Model(solver="gurobi", solver_env=dict(ComputeServer="myserver", ServerPassword="mypassword"))
Traceback (most recent call last):
...
RuntimeError: Could not resolve host: myserver (code 6, command POST http://myserver/api/v1/cluster/jobs)
Source code in src\pyoframe\model.py
binary_variables
property
integer_variables
property
compute_IIS()
Computes the Irreducible Infeasible Set (IIS) of the model.
Gurobi only
This method only works with the Gurobi solver. Open an issue if you'd like to see support for other solvers.
Examples:
>>> m = pf.Model(solver="gurobi")
>>> m.X = pf.Variable(lb=0, ub=2)
>>> m.Y = pf.Variable(lb=0, ub=2)
>>> m.bad_constraint = m.X >= 3
>>> m.minimize = m.X + m.Y
>>> m.optimize()
>>> m.attr.TerminationStatus
<TerminationStatusCode.INFEASIBLE: 3>
>>> m.bad_constraint.attr.IIS
Traceback (most recent call last):
...
RuntimeError: Unable to retrieve attribute 'IISConstr'
>>> m.compute_IIS()
>>> m.bad_constraint.attr.IIS
True
Source code in src\pyoframe\model.py
convert_to_fixed()
Turns a mixed integer program into a continuous one by fixing all the integer and binary variables to their solution values.
Gurobi only
This method only works with the Gurobi solver. Open an issue if you'd like to see support for other solvers.
Examples:
>>> m = pf.Model(solver="gurobi")
>>> m.X = pf.Variable(vtype=pf.VType.BINARY, lb=0)
>>> m.Y = pf.Variable(vtype=pf.VType.INTEGER, lb=0)
>>> m.Z = pf.Variable(lb=0)
>>> m.my_constraint = m.X + m.Y + m.Z <= 10
>>> m.maximize = 3 * m.X + 2 * m.Y + m.Z
>>> m.optimize()
>>> m.X.solution, m.Y.solution, m.Z.solution
(1, 9, 0.0)
>>> m.my_constraint.dual
Traceback (most recent call last):
...
RuntimeError: Unable to retrieve attribute 'Pi'
>>> m.convert_to_fixed()
>>> m.optimize()
>>> m.my_constraint.dual
1.0
Only works for Gurobi:
>>> m = pf.Model("max", solver="highs")
>>> m.convert_to_fixed()
Traceback (most recent call last):
...
NotImplementedError: Method 'convert_to_fixed' is not implemented for solver 'highs'.
Source code in src\pyoframe\model.py
dispose()
Disposes of the model and cleans up the solver environment.
When using Gurobi compute server, this cleanup will ensure your run is not marked as 'ABORTED'.
Note that once the model is disposed, it cannot be used anymore.
Examples:
>>> m = pf.Model()
>>> m.X = pf.Variable(ub=1)
>>> m.maximize = m.X
>>> m.optimize()
>>> m.X.solution
1.0
>>> m.dispose()
Source code in src\pyoframe\model.py
optimize()
write(file_path, pretty=False)
Output the model to a file.
Typical usage includes writing the solution to a .sol
file as well as writing the problem to a .lp
or .mps
file.
Set use_var_names
in your model constructor to True
if you'd like the output to contain human-readable names (useful for debugging).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file_path
|
Union[Path, str]
|
The path to the file to write to. |
required |
pretty
|
bool
|
Only used when writing .sol files in HiGHS. If |
False
|
Source code in src\pyoframe\model.py
Set(*data, **named_data)
Bases: ModelElement
, SupportsMath
, SupportPolarsMethodMixin
A set which can then be used to index variables.
Examples:
>>> pf.Set(x=range(2), y=range(3))
<Set size=6 dimensions={'x': 2, 'y': 3}>
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Source code in src\pyoframe\core.py
Variable(*indexing_sets, lb=None, ub=None, vtype=VType.CONTINUOUS, equals=None)
Bases: ModelElementWithId
, SupportsMath
, SupportPolarsMethodMixin
Represents one or many decision variable in an optimization model.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*indexing_sets
|
SetTypes | Iterable[SetTypes]
|
If no indexing_sets are provided, a single variable with no dimensions is created. Otherwise, a variable is created for each element in the Cartesian product of the indexing_sets (see Set for details on behaviour). |
()
|
lb
|
float | int | SupportsToExpr | None
|
The lower bound for all variables. |
None
|
ub
|
float | int | SupportsToExpr | None
|
The upper bound for all variables. |
None
|
vtype
|
VType | VTypeValue
|
The type of the variable. Can be either a VType enum or a string. Default is VType.CONTINUOUS. |
CONTINUOUS
|
equals
|
Optional[SupportsMath]
|
When specified, a variable is created and a constraint is added to make the variable equal to the provided expression. |
None
|
Examples:
>>> import pandas as pd
>>> m = pf.Model()
>>> df = pd.DataFrame({"dim1": [1, 1, 2, 2, 3, 3], "dim2": ["a", "b", "a", "b", "a", "b"]})
>>> v = Variable(df)
>>> v
<Variable size=6 dimensions={'dim1': 3, 'dim2': 2} added_to_model=False>
Variables cannot be used until they're added to the model.
>>> m.constraint = v <= 3
Traceback (most recent call last):
...
ValueError: Cannot use 'Variable' before it has beed added to a model.
>>> m.v = v
>>> m.constraint = m.v <= 3
>>> m.v
<Variable name=v size=6 dimensions={'dim1': 3, 'dim2': 2}>
[1,a]: v[1,a]
[1,b]: v[1,b]
[2,a]: v[2,a]
[2,b]: v[2,b]
[3,a]: v[3,a]
[3,b]: v[3,b]
>>> m.v2 = Variable(df[["dim1"]])
Traceback (most recent call last):
...
ValueError: Duplicate rows found in input data.
>>> m.v3 = Variable(df[["dim1"]].drop_duplicates())
>>> m.v3
<Variable name=v3 size=3 dimensions={'dim1': 3}>
[1]: v3[1]
[2]: v3[2]
[3]: v3[3]
Source code in src\pyoframe\core.py
solution
property
Retrieve a variable's optimal value after the model has been solved. Returned as a DataFrame if the variable has dimensions, otherwise as a single value. Binary and integer variables are returned as integers.
Examples:
>>> m = pf.Model()
>>> m.var_continuous = pf.Variable({"dim1": [1, 2, 3]}, lb=5, ub=5)
>>> m.var_integer = pf.Variable({"dim1": [1, 2, 3]}, lb=4.5, ub=5.5, vtype=VType.INTEGER)
>>> m.var_dimensionless = pf.Variable(lb=4.5, ub=5.5, vtype=VType.INTEGER)
>>> m.var_continuous.solution
Traceback (most recent call last):
...
RuntimeError: Failed to retrieve solution for variable. Are you sure the model has been solved?
>>> m.optimize()
>>> m.var_continuous.solution
shape: (3, 2)
┌──────┬──────────┐
│ dim1 ┆ solution │
│ --- ┆ --- │
│ i64 ┆ f64 │
╞══════╪══════════╡
│ 1 ┆ 5.0 │
│ 2 ┆ 5.0 │
│ 3 ┆ 5.0 │
└──────┴──────────┘
>>> m.var_integer.solution
shape: (3, 2)
┌──────┬──────────┐
│ dim1 ┆ solution │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞══════╪══════════╡
│ 1 ┆ 5 │
│ 2 ┆ 5 │
│ 3 ┆ 5 │
└──────┴──────────┘
>>> m.var_dimensionless.solution
5
next(dim, wrap_around=False)
Creates an expression where the variable at each index is the next variable in the specified dimension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dim
|
str
|
The dimension over which to shift the variable. |
required |
wrap_around
|
bool
|
If True, the last index in the dimension is connected to the first index. |
False
|
Examples:
>>> import pandas as pd
>>> time_dim = pd.DataFrame({"time": ["00:00", "06:00", "12:00", "18:00"]})
>>> space_dim = pd.DataFrame({"city": ["Toronto", "Berlin"]})
>>> m = pf.Model()
>>> m.bat_charge = pf.Variable(time_dim, space_dim)
>>> m.bat_flow = pf.Variable(time_dim, space_dim)
>>> # Fails because the dimensions are not the same
>>> m.bat_charge + m.bat_flow == m.bat_charge.next("time")
Traceback (most recent call last):
...
pyoframe.constants.PyoframeError: Failed to add expressions:
<Expression size=8 dimensions={'time': 4, 'city': 2} terms=16> + <Expression size=6 dimensions={'city': 2, 'time': 3} terms=6>
Due to error:
Dataframe has unmatched values. If this is intentional, use .drop_unmatched() or .keep_unmatched()
shape: (2, 4)
┌───────┬─────────┬────────────┬────────────┐
│ time ┆ city ┆ time_right ┆ city_right │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ str │
╞═══════╪═════════╪════════════╪════════════╡
│ 18:00 ┆ Toronto ┆ null ┆ null │
│ 18:00 ┆ Berlin ┆ null ┆ null │
└───────┴─────────┴────────────┴────────────┘
>>> (m.bat_charge + m.bat_flow).drop_unmatched() == m.bat_charge.next("time")
<Constraint sense='=' size=6 dimensions={'time': 3, 'city': 2} terms=18>
[00:00,Berlin]: bat_charge[00:00,Berlin] + bat_flow[00:00,Berlin] - bat_charge[06:00,Berlin] = 0
[00:00,Toronto]: bat_charge[00:00,Toronto] + bat_flow[00:00,Toronto] - bat_charge[06:00,Toronto] = 0
[06:00,Berlin]: bat_charge[06:00,Berlin] + bat_flow[06:00,Berlin] - bat_charge[12:00,Berlin] = 0
[06:00,Toronto]: bat_charge[06:00,Toronto] + bat_flow[06:00,Toronto] - bat_charge[12:00,Toronto] = 0
[12:00,Berlin]: bat_charge[12:00,Berlin] + bat_flow[12:00,Berlin] - bat_charge[18:00,Berlin] = 0
[12:00,Toronto]: bat_charge[12:00,Toronto] + bat_flow[12:00,Toronto] - bat_charge[18:00,Toronto] = 0
>>> (m.bat_charge + m.bat_flow) == m.bat_charge.next("time", wrap_around=True)
<Constraint sense='=' size=8 dimensions={'time': 4, 'city': 2} terms=24>
[00:00,Berlin]: bat_charge[00:00,Berlin] + bat_flow[00:00,Berlin] - bat_charge[06:00,Berlin] = 0
[00:00,Toronto]: bat_charge[00:00,Toronto] + bat_flow[00:00,Toronto] - bat_charge[06:00,Toronto] = 0
[06:00,Berlin]: bat_charge[06:00,Berlin] + bat_flow[06:00,Berlin] - bat_charge[12:00,Berlin] = 0
[06:00,Toronto]: bat_charge[06:00,Toronto] + bat_flow[06:00,Toronto] - bat_charge[12:00,Toronto] = 0
[12:00,Berlin]: bat_charge[12:00,Berlin] + bat_flow[12:00,Berlin] - bat_charge[18:00,Berlin] = 0
[12:00,Toronto]: bat_charge[12:00,Toronto] + bat_flow[12:00,Toronto] - bat_charge[18:00,Toronto] = 0
[18:00,Berlin]: bat_charge[18:00,Berlin] + bat_flow[18:00,Berlin] - bat_charge[00:00,Berlin] = 0
[18:00,Toronto]: bat_charge[18:00,Toronto] + bat_flow[18:00,Toronto] - bat_charge[00:00,Toronto] = 0
Source code in src\pyoframe\core.py
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 |
|
sum(over, expr=None)
Sum an expression over specified dimensions. If no dimensions are specified, the sum is taken over all of the expression's dimensions.
Examples:
>>> expr = pl.DataFrame({
... "time": ["mon", "tue", "wed", "mon", "tue"],
... "place": ["Toronto", "Toronto", "Toronto", "Vancouver", "Vancouver"],
... "tiktok_posts": [1e6, 3e6, 2e6, 1e6, 2e6]
... }).to_expr()
>>> expr
<Expression size=5 dimensions={'time': 3, 'place': 2} terms=5>
[mon,Toronto]: 1000000
[tue,Toronto]: 3000000
[wed,Toronto]: 2000000
[mon,Vancouver]: 1000000
[tue,Vancouver]: 2000000
>>> pf.sum("time", expr)
<Expression size=2 dimensions={'place': 2} terms=2>
[Toronto]: 6000000
[Vancouver]: 3000000
>>> pf.sum(expr)
<Expression size=1 dimensions={} terms=1>
9000000
Source code in src\pyoframe\core.py
sum_by(by, expr)
Like pf.sum()
, but the sum is taken over all dimensions except those specified in by
(just like a groupby operation).
Examples:
>>> expr = pl.DataFrame({
... "time": ["mon", "tue", "wed", "mon", "tue"],
... "place": ["Toronto", "Toronto", "Toronto", "Vancouver", "Vancouver"],
... "tiktok_posts": [1e6, 3e6, 2e6, 1e6, 2e6]
... }).to_expr()
>>> expr
<Expression size=5 dimensions={'time': 3, 'place': 2} terms=5>
[mon,Toronto]: 1000000
[tue,Toronto]: 3000000
[wed,Toronto]: 2000000
[mon,Vancouver]: 1000000
[tue,Vancouver]: 2000000
>>> pf.sum_by("place", expr)
<Expression size=2 dimensions={'place': 2} terms=2>
[Toronto]: 6000000
[Vancouver]: 3000000