core
Classes:
Name | Description |
---|---|
Constraint |
A linear programming constraint. |
Expression |
A linear or quadratic expression. |
Set |
A set which can then be used to index variables. |
SupportsMath |
Any object that can be converted into an expression. |
SupportsToExpr |
|
Variable |
Represents one or many decision variable in an optimization model. |
Functions:
Name | Description |
---|---|
sum |
Sum an expression over specified dimensions. |
sum_by |
Like |
Attributes:
Name | Type | Description |
---|---|---|
SetTypes |
|
SetTypes = Union[pl.DataFrame, pd.Index, pd.DataFrame, SupportsMath, Mapping[str, Sequence[object]], 'Set', 'Constraint']
module-attribute
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 |
Methods:
Name | Description |
---|---|
__repr__ |
|
filter |
|
get_id_column_name |
|
on_add_to_model |
|
relax |
Relaxes the constraint by adding a variable to the constraint that can be non-zero at a cost. |
to_str |
|
Attributes:
Name | Type | Description |
---|---|---|
attr |
Container
|
Allows reading and writing constraint attributes similarly to Model.attr. |
dual |
Union[DataFrame, float]
|
|
lhs |
|
|
sense |
|
|
to_relax |
Optional[FuncArgs]
|
|
Source code in pyoframe/core.py
attr
property
Allows reading and writing constraint attributes similarly to Model.attr.
dual
property
lhs = lhs
instance-attribute
sense = sense
instance-attribute
to_relax = None
instance-attribute
__repr__()
filter(*args, **kwargs)
get_id_column_name()
classmethod
on_add_to_model(model, name)
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
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 pyoframe/core.py
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 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 |
|
to_str()
Source code in pyoframe/core.py
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]
Methods:
Name | Description |
---|---|
__add__ |
Examples: |
__mul__ |
|
__repr__ |
|
__str__ |
|
constant |
Examples: |
degree |
Returns the degree of the expression (0=constant, 1=linear, 2=quadratic). |
evaluate |
The value of the expression. Only available after the model has been solved. |
map |
Replaces the dimensions that are shared with mapping_set with the other dimensions found in mapping_set. |
rolling_sum |
Calculates the rolling sum of the Expression over a specified window size for a given dimension. |
sum |
Examples: |
to_expr |
|
to_poi |
|
to_str |
|
to_str_create_prefix |
|
to_str_table |
|
within |
Examples: |
Attributes:
Name | Type | Description |
---|---|---|
constant_terms |
|
|
is_quadratic |
bool
|
Returns True if the expression is quadratic, False otherwise. |
terms |
int
|
Number of terms across all subexpressions. |
variable_terms |
|
Source code in pyoframe/core.py
constant_terms
property
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:
variable_terms
property
__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 pyoframe/core.py
__mul__(other)
Source code in pyoframe/core.py
__repr__()
constant(constant)
classmethod
Examples:
Source code in 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 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 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 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 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 pyoframe/core.py
to_poi()
Source code in pyoframe/core.py
to_str(include_const_term=True, include_header=False, include_data=True)
Source code in pyoframe/core.py
to_str_create_prefix(data)
Source code in pyoframe/core.py
to_str_table(include_const_term=True)
Source code in 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 pyoframe/core.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)]
Methods:
Name | Description |
---|---|
__add__ |
|
__mul__ |
|
__repr__ |
|
to_expr |
|
Source code in pyoframe/core.py
__add__(other)
Source code in pyoframe/core.py
__mul__(other)
__repr__()
SupportsMath(**kwargs)
Bases: ABC
, SupportsToExpr
Any object that can be converted into an expression.
Methods:
Name | Description |
---|---|
__eq__ |
Equality constraint. |
__ge__ |
Equality constraint. |
__le__ |
Equality constraint. |
__neg__ |
|
__pow__ |
Support squaring expressions: |
__radd__ |
|
__rmul__ |
|
__rsub__ |
Support right subtraction. |
__sub__ |
|
__truediv__ |
Examples: |
add_dim |
|
drop_unmatched |
|
keep_unmatched |
|
to_expr |
|
Attributes:
Name | Type | Description |
---|---|---|
__add__ |
|
|
__mul__ |
|
|
allowed_new_dims |
List[str]
|
|
map |
|
|
sum |
|
|
unmatched_strategy |
|
Source code in pyoframe/core.py
__add__ = _forward_to_expression('__add__')
class-attribute
instance-attribute
__mul__ = _forward_to_expression('__mul__')
class-attribute
instance-attribute
allowed_new_dims = []
instance-attribute
map = _forward_to_expression('map')
class-attribute
instance-attribute
sum = _forward_to_expression('sum')
class-attribute
instance-attribute
unmatched_strategy = UnmatchedStrategy.UNSET
instance-attribute
__eq__(value)
__ge__(other)
__le__(other)
__neg__()
__pow__(power)
Support squaring expressions:
m = pf.Model() m.v = pf.Variable() m.v2
v * v m.v 3 Traceback (most recent call last): ... ValueError: Raising an expressions to 3 is not supported. Expressions can only be squared (2).
Source code in pyoframe/core.py
__radd__(other)
__rmul__(other)
__rsub__(other)
__sub__(other)
import polars as pl m = pf.Model() df = pl.DataFrame({"dim1": [1, 2, 3], "value": [1, 2, 3]}) m.v = pf.Variable(df["dim1"]) m.v - df
[1]: v[1] -1 [2]: v[2] -2 [3]: v[3] -3
Source code in pyoframe/core.py
__truediv__(other)
add_dim(*dims)
drop_unmatched()
keep_unmatched()
SupportsToExpr
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]
Methods:
Name | Description |
---|---|
__repr__ |
|
get_id_column_name |
|
next |
Creates an expression where the variable at each index is the next variable in the specified dimension. |
on_add_to_model |
|
to_expr |
|
Attributes:
Name | Type | Description |
---|---|---|
attr |
Container
|
Allows reading and writing variable attributes similarly to Model.attr. |
solution |
Retrieve a variable's optimal value after the model has been solved. |
|
vtype |
VType
|
|
Source code in pyoframe/core.py
attr
property
Allows reading and writing variable attributes similarly to Model.attr.
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
vtype = VType(vtype)
instance-attribute
__repr__()
Source code in pyoframe/core.py
get_id_column_name()
classmethod
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 pyoframe/core.py
1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 |
|
on_add_to_model(model, name)
Source code in pyoframe/core.py
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
If the given dimensions don't exist, an error will be raised:
>>> pf.sum("city", expr)
Traceback (most recent call last):
...
AssertionError: Cannot sum over ['city'] as it is not in ['time', 'place']
See also
pyoframe.sum_by for summing over all dimensions except those that are specified.
Source code in 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 group_by
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
>>> total_sum = pf.sum_by([], expr)
>>> total_sum
<Expression size=1 dimensions={} terms=1>
9000000
If the specified dimensions don't exist, an error will be raised:
>>> pf.sum_by("city", expr)
Traceback (most recent call last):
...
AssertionError: Cannot sum by ['city'] because the expression's dimensions are ['time', 'place'].
>>> pf.sum_by("time", total_sum)
Traceback (most recent call last):
...
AssertionError: Cannot sum by ['time'] because the expression has no dimensions.
See also
pyoframe.sum for summing over specified dimensions.
Source code in pyoframe/core.py
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 |
|