Regex in Python Equation Replacement - python

I'm somewhat new to regex and Python and am in the following situation. I'd like to take an equation string, like "A + B + C + 4D", and place the number 1 in front of all variables that have no number in front of them. So something like:
>>> foo_eqn = "A + B + C + 4D"
>>> bar_eqn = fill_in_ones(foo_eqn)
>>> bar_eqn
"1A + 1B + 1C + 4D"
After some research and asking, I came up with
def fill_in_ones(in_eqn):
out_eqn = re.sub(r"(\b[A-Z]\b)", "1"+ r"\1", in_eqn, re.I)
return(out_eqn)
However, it looks like this only works for the first two variables:
>>> fill_in_ones("A + B")
1A + 1B
>>> fill_in_ones("A + B + E")
1A + 1B + E
>>> fill_in_ones("2A + B + C + D")
2A + 1B + 1C + D
Anything really obvious I'm missing? Thanks!

Looks like the re.I (ignore case flag) is the culprit:
>>> def fill_in_ones(in_eqn):
... out_eqn = re.sub(r"(\b[A-Z]\b)", "1"+ r"\1", in_eqn)
... return(out_eqn)
...
>>>
>>> fill_in_ones("A + 3B + C + 2D + E")
'1A + 3B + 1C + 2D + 1E'
This is because the next positional argument to re.sub is count, not flags. You'll need:
def fill_in_ones(in_eqn):
out_eqn = re.sub(r"(\b[A-Z]\b)", "1"+ r"\1", in_eqn, flags=re.I)
return(out_eqn)
Unfortunately, the re.I flag happens to be 2:
>>> import re
>>> re.I
2

Related

simplify (a + b*(c+d)) / (c+d) in sympy

Given an expression like
a + b⋅(c + d)
─────────────
c + d
I would like to use sympy to simplify it to:
a
───── + b
c + d
It works when I substitute (c+d) to e and back:
import sympy as sp
a,b,c,d,e = sp.symbols('a b c d e')
expr = (a + b*(c+d)) / (c+d)
expr = expr.subs({(c+d):e}).simplify().subs({e:c+d})
print( sp.pretty(expr) )
# prints
# a
# ───── + b
# c + d
Why is this? Is there a way to do it without substitution?
Using apart helps simplifying fractions :
expr = sp.apart((a + b*(c + d))/(c + d), a)
Output is:
a
───── + b
c + d

Setting terms of specific powers in a sympy expression to zero

I have a sympy expression of the following form:
a, b = symbols('a b')
expr = a + a/b + a/b**2 + a**2/b**2 + a/b**3
I want to set any terms where the exponent of b is larger than the exponent of a to zero, such that the result is like this:
newexpr = a + a/b + a**2/b**2
How can I achieve this?
I managed to solve this by doing:
import sympy as sp
a, b, c = sp.symbols('a b c')
expr = a + a/b + a/b**2 + a**2/b**2 + a/b**3
newexpr = sp.limit(expr.subs(a/b, c), b, sp.oo).subs(c, a/b)
newexpr
out[1]: a + a/b + a**2/b**2
Here is a short plan for this:
we make the substitution 1/b -> c
we rewrite the resulting expression as a multivariate polynomial in a,c
we decompose the poly into monomials and only keep those terms that have exp_a >= exp_c , and we finish by substituting back c -> 1/b
#!/usr/bin/python3
from sympy import *
a, b = symbols('a b')
expr = a + a/b + a/b**2 + a**2/b**2 + a/b**3
def truncate(e):
c = symbols('c')
e1 = e.subs(S(1)/b,c)
p = 0
for m in poly(e1,a,c).monoms():
exp_a,exp_c = m
if exp_c <= exp_a:
p += a**exp_a * (1/b)**exp_c
return p
truncated_expr = truncate(expr)
print(truncated_expr)
OUTPUT:
a**2/b**2 + a + a/b

Align the latex output of sympy?

The display of LaTeX equations in sympy appear as an unbroken line:
a = b + c + d(e - fg) + hij
I would like this to appear as something like
a = b
+ c
+ d(e
-
fg)
+ hij
or perhaps customizing how long the line can be before wrapping and aligning, like for example:
a = b + c
+ d(e - fg)
+ hij
Is such a thing possible?

String manipulation in Python

I have a randomly generated string from 6 letters in this form, example:
A' B F2 E' B2 A2 C' D2 C D' E2 F
Some letters have " ' " added to them some have number "2". What i want is to add letter "x" to every letter that is on its own.
So it would look like this:
A' Bx F2 E' B2 A2 C' D2 Cx D' E2 Fx
The trick is that it would add the "x" only to those letters that are on their own. No, Bx -> Bx2.
Any ideas?
Transform your string into list with split()
s = """A' B F2 E' B2 A2 C' D2 C D' E2 F"""
L = s.split(' ')
for i in xrange(len(L)):
if len(L[i]) == 1:
L[i] += 'x'
str_out = ' '.join(L)
The split-comprehend-join version:
' '.join(n+'x' if len(n)==1 else n for n in inputstr.split(' '))
The regex version:
>>> inputstr = "A' F B2 C"
>>> re.sub(r'([A-Z])(?=\s|$)', r'\1x', inputstr)
"A' Fx B2 Cx"
In essence, find any uppercase letter not followed by either a space or the end of the string, and replace it with that character followed by an x.
I ran a few tests with timeit; the former (list comprehension) appears to run slightly faster than the latter (about 15-20% faster on average). This does not appear to change no matter the number of replacements that need to be done (a string 10 times as long still has about the same ratio of processing time as the original).
Ugly or Pythonic?
items = "A' B F2 E' B2 A2 C' D2 C D' E2 F".split()
itemsx = ((a+'x' if len(a)==1 else a) for a in items)
out = ' '.join(itemsx)
With a regular expression,
import re
newstring = re.sub(r"\b(\w)(?![2'])", r'\1x', oldstring)
should be fine. If you're allergic to res,
news = ' '.join(x + 'x' if len(x)==1 else x for x in olds.split())
is a concise way of expressing a similar transformation (if length-one is really the only thing you need to check before appending 'x' to an item).
' '.join(n if len(n) == 2 else n + 'x' for n in s.split(' '))
>>> s="A' B F2 E' B2 A2 C' D2 C D' E2 F".split()
>>> import string
>>> letters=list(string.letters)
>>> for n,i in enumerate(s):
... if i in letters:
... s[n]=i+"x"
...
>>> ' '.join(s)
"A' Bx F2 E' B2 A2 C' D2 Cx D' E2 Fx"
>>>
>>> ' '.join((i+'x')[:2] for i in items.split())
"A' Bx F2 E' B2 A2 C' D2 Cx D' E2 Fx"

How can I get the next string, in alphanumeric ordering, in Python?

I need a simple program that given a string, returns to me the next one in the alphanumeric ordering (or just the alphabetic ordering).
f("aaa")="aab"
f("aaZ")="aba"
And so on.
Is there a function for this in one of the modules already?
I don't think there's a built-in function to do this. The following should work:
def next_string(s):
strip_zs = s.rstrip('z')
if strip_zs:
return strip_zs[:-1] + chr(ord(strip_zs[-1]) + 1) + 'a' * (len(s) - len(strip_zs))
else:
return 'a' * (len(s) + 1)
Explanation: you find the last character which is not a z, increment it, and replace all of the characters after it with a's. If the entire string is z's, then return a string of all a's that is one longer.
Are the answers at How would you translate this from Perl to Python? sufficient? Not 100% what you're asking, but close...
A different, longer, but perhaps more readable and flexible solution:
def toval(s):
"""Converts an 'azz' string into a number"""
v = 0
for c in s.lower():
v = v * 26 + ord(c) - ord('a')
return v
def tostr(v, minlen=0):
"""Converts a number into 'azz' string"""
s = ''
while v or len(s) < minlen:
s = chr(ord('a') + v % 26) + s
v /= 26
return s
def next(s, minlen=0):
return tostr(toval(s) + 1, minlen)
s = ""
for i in range(100):
s = next(s, 5)
print s
You convert the string into a number where each letter represents a digit in base 26, increase the number by one and convert the number back into the string. This way you can do arbitrary math on values represented as strings of letters.
The ''minlen'' parameter controls how many digits the result will have (since 0 == a == aaaaa).
Sucks that python doesn't have what ruby has: String#next So here's a shitty solution to deal with alpha-numerical strings:
def next_string(s):
a1 = range(65, 91) # capital letters
a2 = range(97, 123) # letters
a3 = range(48, 58) # numbers
char = ord(s[-1])
for a in [a1, a2, a3]:
if char in a:
if char + 1 in a:
return s[:-1] + chr(char + 1)
else:
ns = next_string(s[:-1]) if s[:-1] else chr(a[0])
return ns + chr(a[0])
print next_string('abc') # abd
print next_string('123') # 124
print next_string('ABC') # ABD
# all together now
print next_string('a0') # a1
print next_string('1a') # 1b
print next_string('9A') # 9B
# with carry-over
print next_string('9') # 00
print next_string('z') # aa
print next_string('Z') # AA
# cascading carry-over
print next_string('a9') # b0
print next_string('0z') # 1a
print next_string('Z9') # AA0
print next_string('199') # 200
print next_string('azz') # baa
print next_string('Zz9') # AAa0
print next_string('$a') # $b
print next_string('$_') # None... fix it yourself
Not great. Kinda works for me.

Categories

Resources