I am developing a meta-data framework for monitoring systems such that I can easily create data adapters that can pull in data from different systems. One key component is the unit of measurement. To compare datasets which have different units I need to convert them to a common unit. To build adapters I need to have a system capable of describing complicated situations.
Units are just strings that define what the numbers mean e.g. 102 kWh is 102 kiloWatt hours, this can be converted to the SI unit for energy (Joules) by multiplying by a conversion factor (3600000 in this case).
Some data sources have built in conversion systems and provide me with data to a base unit that they specify, e.g. one database will give me energy data measured in various units (one may be called 'kWh*0.01', another 'Btu*1000'). These data will be provided along with a conversion factor to calculate the 'base unit' for that system which is e.g. GJ.
Here is my code to define units
class Unit(object):
def __init__(self, base_unit, coefficient, name, suffix):
self.name = name
self.suffix = suffix
self.base_unit = base_unit
self.coefficient = float(coefficient)
def to_base(self, value):
return value * self.coefficient
def from_base(self, value):
return value / self.coefficient
def to_unit(self, value, unit):
if unit.base_unit != self.base_unit:
raise TypeError, "Units derived from %s and %s are incompatible." % (self.base_unit, unit.base_unit)
return unit.from_base(self.to_base(value))
def __repr__(self):
return "%s (%s)" % (self.name, self.suffix)
class BaseUnit(Unit):
def __init__(self, name, suffix):
super(BaseUnit, self).__init__(self, 1.0, name, suffix)
I use these classes to represent the unit information pulled from the source database. In this case, I pass in a record from the 'units' table and use the appropriate fields to construct a unit with the given description and abbreviation that can then be converted into the base unit provided by the system (GJ for energy, M3 for water).
def convert_unit(unit):
"""Convert a provided unit record into my unit"""
desc = unit.description.strip()
suffix = unit.abbreviation.strip()
if unit.type == 'energy':
base = BaseUnit("GigaJoules", "GJ")
return Unit(base, float(unit.units_per_gj), desc, suffix)
elif unit.type == 'water':
base = BaseUnit("Cubic metres", "m3")
return Unit(base, float(unit.units_per_m3), desc, suffix)
else:
return BaseUnit(desc, suffix)
When designing my classes I had in the back of my mind an idea to enforce a common base unit (e.g. Joules) for energy across multiple data sources, I think I can do this by adding another tier and simply making the current base unit a unit with a given base (may require hard coding the conversion factor between my base unit and GJ).
def convert_unit(unit):
"""Convert a provided unit record into my unit"""
desc = unit.description.strip()
suffix = unit.abbreviation.strip()
if unit.type == 'energy':
base = BaseUnit("Joules", "J")
source_base = Unit(base, 1000000000, "GigaJoules", "GJ")
return Unit(source_base, float(unit.units_per_gj), desc, suffix)
elif unit.type == 'water':
base = BaseUnit("Cubic metres", "m3")
return Unit(base, float(unit.units_per_m3), desc, suffix)
else:
return BaseUnit(desc, suffix)
The to_base, from_base and to_unit methods are intended for use with numpy arrays of measurements. I typically use the unit classes inside a dataset class that holds data as a numpy array and a unit instance.
How does this look? It feels neat to me. Are there any style issues?