How to capture within an expression given as a string the resolvable terms using regex and then remove the terms that become 0 after operating on them
Question:
import re
#operation string (with parameters)
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
Having a string like this which has parameters in some terms, how do I resolve the parameters without terms?, and then remove the terms that are 0 and join (by adding or subtracting) the terms that can be operated
z_j_func = 'Z = 0 + 2 + 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 1 / 2'
In the end it should look like this:
z_j_func = 'Z = 3 + M * 12 + M * 12 + M * 2'
Answers:
In general you would use a symbolic expression parser, like sympy
.
Here is how that would work out, assuming that the string is of the form: <identifier> = <expression>
import sympy
def simplify(equation):
# Identify the operands of the equation
left, right = equation.replace(" ", "").split("=")
return f"{left} = {sympy.simplify(right)}"
# Example run
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
print(simplify(z_j_func)) # Z = 26*M + 3
If you prefer not to use a parser, but want to throw your own, then have a look at the next piece of code. It makes several assumptions, including:
- The input is of the form:
<identifier> = <expression>
;
- The expression does not have parentheses;
- The expression only uses the operators
+
, -
, *
, /
;
- Where a
/
operator is used, it is the rightmost operator in the term;
- Unknowns (like
M
) only appear in numerators, not denominators
The code:
import re
from collections import defaultdict
from math import prod
from fractions import Fraction
def simplify(equation):
# Identify the operands of the equation
left, right = equation.replace(" ", "").split("=")
# Identify the numerators and denominators
terms = [
term.split("/") if "/" in term else [term, 1]
for term in re.split(r"+|(?=-)", right)
]
# Group terms by the unknowns (variable) in their numerators
d = defaultdict(list)
for num, denom in terms:
d["*".join(re.findall(r"(?i)[a-z]+", num))].append(
(re.sub(r"(?i)*[a-z]+|[a-z]+*?", "", num), denom)
)
# Evaluate the coefficients for each unknown and concatenate
return left + " = " + " + ".join(
(key + " * " + str(Fraction(sum(
prod(map(float, num.split('*'))) / float(denom)
for num, denom in expr
)))).lstrip(" *")
for key, expr in d.items()
).replace(" + -", " - ")
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
print(simplify(z_j_func)) # Z = M * 26 + 3
import re
#operation string (with parameters)
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
Having a string like this which has parameters in some terms, how do I resolve the parameters without terms?, and then remove the terms that are 0 and join (by adding or subtracting) the terms that can be operated
z_j_func = 'Z = 0 + 2 + 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 1 / 2'
In the end it should look like this:
z_j_func = 'Z = 3 + M * 12 + M * 12 + M * 2'
In general you would use a symbolic expression parser, like sympy
.
Here is how that would work out, assuming that the string is of the form: <identifier> = <expression>
import sympy
def simplify(equation):
# Identify the operands of the equation
left, right = equation.replace(" ", "").split("=")
return f"{left} = {sympy.simplify(right)}"
# Example run
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
print(simplify(z_j_func)) # Z = 26*M + 3
If you prefer not to use a parser, but want to throw your own, then have a look at the next piece of code. It makes several assumptions, including:
- The input is of the form:
<identifier> = <expression>
; - The expression does not have parentheses;
- The expression only uses the operators
+
,-
,*
,/
; - Where a
/
operator is used, it is the rightmost operator in the term; - Unknowns (like
M
) only appear in numerators, not denominators
The code:
import re
from collections import defaultdict
from math import prod
from fractions import Fraction
def simplify(equation):
# Identify the operands of the equation
left, right = equation.replace(" ", "").split("=")
# Identify the numerators and denominators
terms = [
term.split("/") if "/" in term else [term, 1]
for term in re.split(r"+|(?=-)", right)
]
# Group terms by the unknowns (variable) in their numerators
d = defaultdict(list)
for num, denom in terms:
d["*".join(re.findall(r"(?i)[a-z]+", num))].append(
(re.sub(r"(?i)*[a-z]+|[a-z]+*?", "", num), denom)
)
# Evaluate the coefficients for each unknown and concatenate
return left + " = " + " + ".join(
(key + " * " + str(Fraction(sum(
prod(map(float, num.split('*'))) / float(denom)
for num, denom in expr
)))).lstrip(" *")
for key, expr in d.items()
).replace(" + -", " - ")
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'
print(simplify(z_j_func)) # Z = M * 26 + 3