How can I handle fractional numeric expressions using pyparsing?

We just started kicking the pyparsing buses, and as it is until now, but we couldn't get him to help us parse fractional number strings to turn them into numeric data types.

For example, if the column value in the database table contains the row:

1 1/2

We would like to somehow convert it to the numerical equivalent of python:

1,5

We would like to make a parser that does not care about whether the numbers in the fraction are integer or real. For example, we would like to:

1.0 1.0 / 2.0

... still translate to:

1,5

Essentially, we would like the parser to conceptually perform the following actions:

"1 1/2" = 1 + 0.5 = 1.5

The following code example seems to bring us closer ...

http://pyparsing.wikispaces.com/file/view/parsePythonValue.py

... but not close enough to move forward. All our tests for processing fractional numbers return only the first part of expression (1). Tips? Tips? Timely wisdom? :)

+4
source share
4 answers

Since you are quoting some tests, it looks like you at least tackled the problem. I assume that you have already determined one number, which can be integer or real - it does not matter, you still convert everything to float - and the fraction of two numbers, maybe something like this:

from pyparsing import Regex, Optional number = Regex(r"\d+(\.\d*)?").setParseAction(lambda t: float(t[0])) fraction = number("numerator") + "/" + number("denominator") fraction.setParseAction(lambda t: t.numerator / t.denominator) 

(Pay attention to the use of parsing operations that perform floating point conversion and fractional division immediately during parsing. I prefer to do this during parsing when I know that something is a number or a fraction or something else, instead to come back later and sifting through a bunch of fragmented lines, trying to recreate the recognition logic that the parser has already done.)

Here are the test cases I wrote for your problem, consisting of an integer, a fraction, and an integer and a fraction, using both integers and numbers:

 tests = """\ 1 1.0 1/2 1.0/2.0 1 1/2 1.0 1/2 1.0 1.0/2.0""".splitlines() for t in tests: print t, fractExpr.parseString(t) 

The final step is to define a fractional expression, which can be a single number, a fraction, or a single number and a fraction.

Since pyparsing is from left to right, it does not perform the same return as regexen. Therefore, this expression will not work so well:

 fractExpr = Optional(number) + Optional(fraction) 

To sum the numerical values ​​that may come from the number and parts of a fraction, add this parsing action:

 fractExpr.setParseAction(lambda t: sum(t)) 

Our tests are printed:

 1 [1.0] 1.0 [1.0] 1/2 [1.0] 1.0/2.0 [1.0] 1 1/2 [1.5] 1.0 1/2 [1.5] 1.0 1.0/2.0 [1.5] 

For test example 1/2 , containing only the fractional part, the leading numerator matches the term Optional(number) , but this leaves us only with "/ 2", which does not match Optional(fraction) - fortunately, since the second term is optional, this "passes", but in fact he does not do what we want.

We need to make fractExpr a little smarter, and at first it will look like a lone fraction, since there is such a potential confusion between a lone number and a leading numerator of a fraction. The easiest way to do this is to make fractExpr as follows:

 fractExpr = fraction | number + Optional(fraction) 

Now with this change, our tests look better:

 1 [1.0] 1.0 [1.0] 1/2 [0.5] 1.0/2.0 [0.5] 1 1/2 [1.5] 1.0 1/2 [1.5] 1.0 1.0/2.0 [1.5] 

There are a couple of classic pyparsing traps, and this is one of them. Just remember that pyparsing has only the idea you are talking about, otherwise it is just a direct analysis from left to right.

+6
source

Not quite what you are looking for, but ...

 >>> import fractions >>> txt= "1 1/2" >>> sum( map( fractions.Fraction, txt.split() ) ) Fraction(3, 2) >>> float(_) 1.5 
+3
source

This recipe may be helpful:

Look at line 39:

 mixed = Combine(numeral + fraction, adjacent=False, joinString=' ') 
+2
source

This is a kind of double with S. Lott, but in any case it is:

 from fractions import Fraction print sum(Fraction(part) for part in '1 1/2'.split()) 

Working with integers float will be quite confusing, though:

 from fractions import Fraction clean = '1.0 1.0/2.0'.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split() print(clean) print(sum(Fraction(part) for part in clean)) 

And other posters, plus one s / s space:

 from fractions import Fraction tests = """\ 1 1.0 1/2 1.0/2.0 1 1/2 1.0 1/2 1.0 1.0/2.0 1.0 1.0 / 2.0 """.splitlines() for t in tests: clean = t.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split() value = sum(Fraction(part) for part in clean) print('%s -> %s, %s = %f' % (t, clean, value, float(value))) 
+1
source

All Articles