Cory Kramer
Cory Kramer

Reputation: 117866

How to define and use percentage in Pint

I'm currently using Pint to handle units and unit conversions. This seems to work well for the units that are already defined in Pint, for example

>>> import pint
>>> ureg = pint.UnitRegistry()
>>> Q = ureg.Quantity
>>> a = Q(5, 'm/s')
>>> a
<Quantity(5, 'meter / second')>
>>> a.to('ft/s')
<Quantity(16.404199475065617, 'foot / second')>

I tried to define my own units, which represent percentage. As far as unit conversions go, a percentage is simply 100 times a dimensionless fraction, which is how I defined it.

>>> ureg.define('percent = dimensionless * 100 = pct')
>>> a = Q(5, 'pct')
>>> a
<Quantity(5, 'percent')>

However I cannot seem to convert back and forth between fraction ('dimensionless') and 'pct'.

>>> a.to('dimensionless')
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    a.to('dimensionless')
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\quantity.py", line 263, in to
    magnitude = self._convert_magnitude_not_inplace(other, *contexts, **ctx_kwargs)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\quantity.py", line 231, in _convert_magnitude_not_inplace
    return self._REGISTRY.convert(self._magnitude, self._units, other)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\unit.py", line 1026, in convert
    return self._convert(value, src, dst, inplace)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\unit.py", line 1042, in _convert
    src_dim = self._get_dimensionality(src)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\unit.py", line 813, in _get_dimensionality
    self._get_dimensionality_recurse(input_units, 1.0, accumulator)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\unit.py", line 837, in _get_dimensionality_recurse
    self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
  File "C:\Python35\python-3.5.1.amd64\lib\site-packages\pint\unit.py", line 835, in _get_dimensionality_recurse
    reg = self._units[self.get_name(key)]
KeyError: ''

What I'd essentially like to do is be able to convert between e.g. "0.73" and "73%". How can I define and use such a unit?

Upvotes: 10

Views: 4235

Answers (4)

Matthias Arras
Matthias Arras

Reputation: 740

In pint>0.22, percent is supported out of the box:

These do the expected thing:

import pint
ureg = pint.UnitRegistry()
pint.__version__  # 0.22
ureg.Quantity(1,'%')  # 1 percent
ureg.Quantity(1,'').to('percent')  # 100.0 percent
ureg.Quantity(100,'percent').to('')  # 1.0 dimensionless
ureg.Quantity(1,"").to('%')  # 100.0 percent

Upvotes: 5

Nicolai Weitkemper
Nicolai Weitkemper

Reputation: 634

In pint ≥0.16, try

ureg.define('percent = 1 / 100 = %')

or

ureg.define('percent = 0.01 = %')

If you want to use not only ureg('1 percent'), but also ureg('1 %'), add a preprocessor:

ureg = pint.UnitRegistry(preprocessors=[
     lambda s: s.replace('%', ' percent '),
])

Credits to this comment on GitHub for the preprocessor code example.

Upvotes: 0

GeoMatt22
GeoMatt22

Reputation: 240

I had the same problem, and came up with a simple solution which seems to work well.

import pint

ureg = pint.UnitRegistry()
ureg.define('fraction = [] = frac')
ureg.define('percent = 1e-2 frac = pct')
ureg.define('ppm = 1e-6 fraction')

print(ureg('100 pct').to('dimensionless'))
print(ureg('0.5 dimensionless').to('pct'))
print(ureg('pct').to('ppm'))
print(ureg('1e4 ppm').to('pct'))

Output:

1.0 dimensionless
50.0 percent
10000.0 ppm
0.9999999999999999 percent

(Note slight rounding error on last line.)

I got the syntax from the default units dictionary, where base units are defined like

# reference
gram = [mass] = g  # dimensional
radian = [] = rad  # dimensionless

Upvotes: 7

Lukas Graf
Lukas Graf

Reputation: 32590

It seems that GitHub issue hgrecco/pint#185 covers the case you're describing.

Using the work-around discussed in that issue works for me using Pint-0.7.2:

from pint.unit import ScaleConverter
from pint.unit import UnitDefinition
import pint

ureg = pint.UnitRegistry()
Q = ureg.Quantity

ureg.define(UnitDefinition('percent', 'pct', (), ScaleConverter(1 / 100.0)))
a = Q(5, 'pct')

print a
print a.to('dimensionless')

Output:

5 percent
0.05 dimensionless

Upvotes: 8

Related Questions