Channels ▼
RSS

.NET

Quantities and Units in Python


When you need to perform calculations with values that have an associated unit of measure, it is very common to make mistakes by mixing different units of measure. It is also common to perform incorrect conversions between the different units that generate wrong results. The latest Python release doesn't allow developers to associate a specific numerical value with a unit of measure. In this article, I look at three Python packages that provide different solutions to this problem and allow you to work with units of measure and perform conversions.

Three Different Packages to Add Units of Measure

The need to associate quantities with units of measure in any programming language is easy to understand, even in the most basic math and physics problems. One of the simplest calculations is to sum two values that have an associated base unit. For example, say that you have two electrical resistance values. One of the values is measured in ohms and the other in kilo-ohms. To sum the values, you must choose the desired unit and convert one of the values to the chosen unit. If you want the result to be expressed in ohms, you must convert the value in kilo-ohms to ohms, sum the two values expressed in ohms, and provide the result in ohms.

The following Python code uses variables with a suffix that defines the specific unit being used in each case. You have probably used or seen similar conventions. The suffixes make the code less error-prone because you easily understand that r1_in_ohms holds a value in ohms, and r2_in_kohms holds a value in kilo-ohms. Thus, there is a line that assigns the result of converting the r2_in_kohms value to ohms to the new r2_in_ohms variable. The last line calculates the sum and holds the result in ohms because both variables hold values in the same unit of measure.

r1_in_ohms = 500
r2_in_kohms = 5.2
r2_in_ohms = r2_in_kohms * 1e3
r1_plus_r2_in_ohms = r1_in_ohms + r2_in_ohms

Obviously, the code is still error-prone because there won't be any exception or syntax error if a developer adds the following line to sum ohms and kilo-ohms without performing the necessary conversions:

r3_in_ohms = r1_in_ohms + r2_in_kohms

There is no rule that assures that all the variables included in the sum operation must use the same suffix; that is, the same unit. There aren't invalid operations between variables that hold values with incompatible units. For example, you might sum a voltage value to a resistance value and the code won't produce any error warning.

Now, imagine that Python adds support for units of measure. Each numeric value can have an associated unit of measure enclosed within <>. The following three lines would replace the previous code with an easier to understand syntax:

r1 = 500 <ohms>
r2 = 5.2 <kilo-ohms>
r1_plus_r2 = (r1 + r2) <ohms>

r1 holds a value of 500 and an associated unit of measure, ohms. r2 holds a value of 5.2 and an associated unit of measure, kilo-ohms. Because each variable includes information about its unit of measure, the sum operation is smart and it can convert compatible units such as ohms and kilo-ohms. The last line sums the two values taking into account their unit of measure, and converts the result to the specified unit, ohms. The r1_plus_r2 variable holds the result of the sum operation expressed in ohms. The following line would produce an exception because the units of measure are incompatible:

sum = (10 <volts> + 500 <ohms>) <inches>

However, the support should be smart enough to allow you to mix different length units. For example, the following line would produce a valid result in inches.

sum = (10 <inches> + 1200 <centimeters>) <inches>

Python doesn't support units of measure, but the three Python packages I examine here provide different ways to enable them. Each package takes a different approach. While none works as well as a native language feature would, these solutions do provide a baseline that you can improve according to your specific needs.

Package Name

Latest Version

Python Package Web Page

Numericalunits

1.12

http://pypi.python.org/pypi/numericalunits

Pint

0.2.1

http://pypi.python.org/pypi/Pint

Units

0.06

http://pypi.python.org/pypi/units

Numericalunits: A Bare-Bones Solution

Numericalunits is a single Python module (numericalunits.py) that provides easy unit conversion features for your operations. You just need to follow two simple rules:

  • To assign a unit of measure to a value, multiply the value by the unit.
  • To express a value generated by its multiplication by the unit in a different unit, divide the value by that unit.

You simply need to add the following lines to import the module with an alias (nu) and execute the $reset_units method to start working with the different units.

import numericalunits as nu
nu.reset_units('SI')

When you call nu.reset_units('SI'), Numericalunits uses standard SI units (short for Système Internationale d'Unités in French) for storing the values (see Figure 1). This way, any length value is stored in meters, no matter the length unit you specify in the multiplication. Read here if you want more information about SI base units.

Python
Figure 1: The SI base units and their interdependencies.

If you call the default nu.reset_units(), Numericalunits uses a random set of units instead of the standard SI units. I really don't like using a random set of units because it usually generates a loss of precision and results that lack accuracy. The only advantage of using random units is that you can check dimensional errors by running calculations twice and comparing whether the results match. You have to call nu.reset_units() before each calculation and compare the two values. I don't like this way of checking dimension errors because it adds a huge overhead and it is indeed error-prone. Thus, I suggest using Numericalunits as a unit conversion helper with the standard SI units initialization.

Numericalunits doesn't save information about the unit of measure in the numerical variable; therefore, there is no way to know which unit you used when you assigned the value. If you need more than a unit conversions helper, I suggest working with one of the other packages.

You can read the following line of code as "assign 500 ohms to r1."

r1 = 500 * nu.ohm

You can read the following line of code as "display the value of r1 expressed in kilo-ohms."

print(r1 / nu.kohm)

The following line displays the value of r1 expressed in ohms.

print(r1 / nu.ohm)

The following code uses Numericalunits to sum the values of r1 and r2. Notice that the code is self-documented because you can easily see that r1 holds a value in ohms and r2 in kilo-ohms. The r1_plus_r2 variable holds the result of the sum operation expressed in ohms and r1_plus_r2_kohms holds the result converted to kilo-ohms. Notice that you can sum the values of r1 and r2 without having to convert the units to ohms and the result will be accurate because of the way in which Numericalunits saves the values in the base units.

import numericalunits as nu 
nu.reset_units('SI')
r1 = 500 * nu.ohm
r2 = 5.2 * nu.kohm
r1_plus_r2 = r1 + r2
r1_plus_r2_kohms = r1_plus_r2 / nu.kohm

The following code uses Numericalunits to sum four distance values expressed in four different units of measure: meters, miles, centimeters, and feet. Numericalunits doesn't support plurals for the units. The total_distance variable holds the total distance expressed in feet.

import numericalunits as nu 
nu.reset_units('SI')
distance1 = 2500 * nu.m
distance2 = 2 * nu.mile
distance3 = 3000 * nu.cm
distance4 = 3500 * nu.foot
total_distance = (distance1 + distance2 + distance3 + distance4) / nu.foot

Pint: A Complete Package

Pint is a Python package that allows you to work with numerical values associated to units of measure. Because Pint saves the magnitude and the associated units for any numerical type, you are able to know which unit you used when you assigned the magnitude value. You can perform arithmetic operations between compatible units and convert from and to different units. When you try to perform arithmetic operations on magnitudes that have incompatible units of measure, Pint raises a Pint.unit.DimensionalityError exception indicating that it cannot convert from one unit to the other before performing the arithmetic operation.

The UnitRegistry class stores the definitions and relationships between units. By default, Pint uses the default_en.txt unit definitions file. This file contains the different units and the prefixes that the UnitRegistry will recognize in plain text. You can easily edit this text file to add any unit you might need to support.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video