charmingcompanions.com

Mastering the Pint Package in Python for Physical Quantities

Written on

Chapter 1: Introduction to the Pint Package

In fields like engineering, science, and even supply chain management, professionals often find themselves needing to programmatically handle physical quantities such as mass, time, and length. If you're a data scientist or software engineer using Python, you might have resorted to creating lookup tables to convert units (like kg to lb) or to perform operations with different physical dimensions (e.g., volume versus time). The Python ecosystem is rich with libraries that can address these challenges, one of which is Pint. This post will introduce you to Pint, a Python package designed to manage units in your data science or software projects 🍻. You’ll learn about its core features and how to incorporate them into your projects seamlessly 🧩.

Pint Quantity Explained

Pint employs an object-oriented programming (OOP) approach. One of its key components is the Quantity object, which allows you to store both the magnitude and the unit of a physical quantity. Below is a simple illustration of how to create a Quantity instance and access its properties:

from pint import Quantity

medium_q = Quantity("2 kg") # Alternatively, use Quantity((2, "kg"))

print("Magnitude: ", medium_q.m)

print("Magnitude type: ", type(medium_q.m))

print("Dimensionality: ", medium_q.dimensionality)

print("Dimensionality type: ", type(medium_q.dimensionality))

print("Unit: ", medium_q.u)

print("Unit type: ", type(medium_q.u))

Output:

Magnitude: 2

Magnitude type:

Dimensionality: [mass]

Dimensionality type:

Unit: kilogram

Unit type:

The magnitude is stored as an integer (or float), while dimensionality and unit are represented as UnitsContainer and Unit, respectively.

What if your Python backend needs to send this information to other services via RESTful API endpoints? You'll need to serialize these objects for transfer. Here’s how straightforward it is:

dimensionality_dict = dict(medium_q.dimensionality)

unit_string = str(medium_q.u)

print("Dimensionality: ", dimensionality_dict)

print("Dimensionality type: ", type(dimensionality_dict))

print("Unit: ", unit_string)

print("Unit type: ", type(unit_string))

Output:

Dimensionality: {'[mass]': 1}

Dimensionality type:

Unit: kilogram

Unit type:

Now, you have objects that are easily serializable, allowing for display on dashboards or integration into GUIs, as well as for further programming logic like property conversions.

Unit Conversions Made Easy

Let’s explore how simple it is to convert units within the same dimension. The Quantity object has a method called to, which enables you to specify the desired unit for conversion:

print(medium_q.to("ton"))

Output:

0.002204622621848776 ton

Pint also integrates with the uncertainties package, allowing you to manage measurements with tolerance levels related to human error, experimental inaccuracies, or randomness. Tolerance can be presented as absolute (e.g., +/- 0.02 mm) or relative (+/- 0.5%):

print("Absolute: ", medium_q.plus_minus(0.1, relative=False))

print("Relative: ", medium_q.plus_minus(0.1, relative=True))

Output:

(2.00 ± 0.10) kilogram

(2.00 ± 0.20) kilogram

Error propagation is handled effortlessly by Pint, requiring no additional coding on your part:

q_mass = Quantity("2 kg").plus_minus(0.1, relative=True)

q_volume = Quantity("3 m**3").plus_minus(0.1, relative=True)

print(q_mass / q_volume)

Output:

(0.67 ± 0.09) kilogram/meter3

Pint's Unit Registry

Pint allows for the creation of a UnitRegistry object to isolate the unit systems your application will utilize. From an OOP perspective, it employs a composite design pattern, primarily involving Quantity and Unit.

from pint import UnitRegistry

medium_ureg = UnitRegistry()

medium_q = medium_ureg.Quantity("2 kg").plus_minus(0.1, relative=True)

print(medium_q)

Output:

2.00+/-0.20 kilogram

Serialization of Quantity Instances

Quantity instances can be serialized and transformed into tuples using the to_tuple() method, and you can convert them back into Quantity instances via the from_tuple() method of the UnitRegistry class:

medium_q_2 = medium_ureg.Quantity.from_tuple(medium_q.to_tuple())

print(medium_q_2)

Output:

2.00+/-0.20 kilogram

Python tuples are pickable, meaning you can easily save and load Quantity object attributes using these methods:

import pickle

medium_q_tuple = pickle.loads(pickle.dumps(medium_q.to_tuple()))

new_medium_q = medium_ureg.Quantity.from_tuple(medium_q_tuple)

print(new_medium_q)

Output:

2.00+/-0.20 kilogram

Handling Multiple Unit Registries

If you mistakenly use a Quantity instance from a different registry, Pint will raise an error:

Quantity("2 kg") / medium_ureg.Quantity("5 m**3")

Output:

ValueError: Cannot operate with Quantity and Quantity of different registries.

As previously mentioned, Quantity is part of the UnitRegistry class, so mixing units from distinct registries will result in a crash 🙈.

Integrating with Python Lists and Numpy Arrays

Pint extends its functionality to Python lists and NumPy arrays. Here’s how easy it is to specify units for physical quantities in a list:

medium_q_list = [0.5, 0.4] * medium_ureg.kg

print(medium_q_list.to("g"))

Output:

[500.0 400.0] gram

You can also use nested lists to represent different physical properties:

medium_q_list = [[0.3, 0.2] * medium_ureg.kg,

[0.3, 0.2] * medium_ureg.J]

print(medium_q_list)

For NumPy arrays, Pint allows you to define separate arrays with their own dimensions and units, enabling standard NumPy operations like element-wise multiplication and mean calculations 🦾.

import numpy as np

import warnings

warnings.simplefilter("ignore")

medium_mass = np.array([0.4, 0.2]) * medium_ureg.kilogram

medium_density = np.array([0.4, 0.2]) * medium_ureg.Unit("kg/m**3")

medium_volume = medium_mass / medium_density

print(medium_volume)

print(medium_mass.mean())

Output:

[1.0 1.0] meter ** 3

0.30000000000000004 kilogram

Unlike Python lists, NumPy arrays cannot express different units for columns or rows, which may be limiting if your data structure requires it. However, Pint continues to evolve, integrating its features with frameworks like Pandas and Xarray 🚀🌝.

Defining and Redefining Units

One of the standout features of Pint is its capability to define new dimensions and units, as well as to redefine existing unit conversions.

New Unit Definition

For instance, if you want to define a new unit within the mass dimension, you could create a unit called medium_silly with aliases msu and msum, where 0.2 kg equals 1 medium_silly:

medium_ureg.define("medium_silly = 0.2 * kg = msu = msum")

This newly defined unit can be utilized for calculations:

new_quantity = (1 * medium_ureg.medium_silly +

1 * medium_ureg.msu +

1 * medium_ureg.msum +

1 * medium_ureg.kg)

print(new_quantity)

Output:

8.0 medium_silly

New Dimension Definition

You can also introduce a new dimension into the registry, for example, silly_dim, referencing the medium_silly_dim_ref unit:

medium_ureg.define("medium_silly_dim_ref = [silly_dim] = msd_ref")

print(medium_ureg.msd_ref.dimensionality)

Output:

[silly_dim]

Existing Unit Redefinition

To redefine existing units, you need to use the Context object to specify the context for the redefinition. Here’s how:

from pint import Context

# Define context

ctx = Context(name="Medium test")

medium_ureg.add_context(ctx)

# Redefine unit

ctx.redefine("BTU = 1055 J")

# Create unit and convert it

q = medium_ureg.Quantity("1 BTU")

print(q.to("J"))

Output:

1055.056 joule

The result can differ if the context isn’t specified during the conversion:

print(q.to("J", ctx))

Output:

1055.0 joule

Programmatic Definitions

Pint stores unit definitions in a text file named default_en.txt located in its installation path. You can modify this file or create a new one to load custom definitions:

medium_ureg.load_definitions("/your/path/to/customized_def.txt")

While modifying this file programmatically may be resource-intensive, it’s essential to understand how to leverage the Pint ecosystem effectively.

Conclusion

In this article, we've delved into the Pint package for managing physical quantities in Python projects. We've covered core components of the OOP paradigm that underpin Pint, such as Quantity, Unit, UnitRegistry, and Context. Additionally, we've explored how to define and redefine unit conversions and dimensions in your application. In the next post, I'll share strategies for overcoming challenges associated with storing programmatically defined units. Let's continue this journey of exploration and learning together 📖. Thank you for reading my post 🤗.

If you enjoyed this content, consider following me on Medium for more insightful articles 🚀…

The first video titled "Using Pints for Units in Python" provides a comprehensive overview of how to effectively use Pint for unit handling in Python.

The second video, "Python Unit Conversions with Pint," dives deeper into unit conversions and practical applications of the Pint package.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Transform Your Video Content Strategy with Fliki's Innovative Software

Discover how Fliki can streamline your video creation process and elevate your content strategy effortlessly.

Raspberry Robin Worm Threatens Windows Networks: A Wake-Up Call

Microsoft warns of the Raspberry Robin worm infecting numerous Windows networks via USB devices, raising security concerns.

A Time of Reckoning: The Race Against Fate (Part 50)

As chaos reigns, Lara and her companions navigate danger with a flicker of hope amid a civil war.

Navigating the Infinite Content Paradox of Our Digital Age

Explore how to manage the overwhelming digital content in our lives and find value in what we consume.

# High-Risk Vulnerability in WordPress Plugin Threatens 20,000 Sites

A critical flaw in a popular WordPress plugin puts over 20,000 websites at risk of phishing attacks and code injection.

Unlocking the Secrets of Crypto Investment with ChatGPT

Explore how ChatGPT assists investors in navigating the cryptocurrency market and making informed decisions.

Exploring the Enigma of Bigfoot: Insights from Whitehall, NY

Dive into the captivating world of Bigfoot sightings in Whitehall, NY, and explore the rich history and evidence surrounding this elusive creature.

Navigating the Heartbreak: Moving On from Lost Love

A heartfelt exploration of the challenges in getting over a lost love and the journey toward self-acceptance.