pythonnumpytimedelta

numpy timedelta highest unit without loss of information


I have several numpy timedelta values. I want to convert them to the a format that is better to read for humans without losing information.
Let's say I have td = np.timedelta64(10800000000001, 'ns'). Then I can only have it in ns because if I convert it to msor higher it will lose information. If I have td = np.timedelta64(1080000000000, 'ns') I can convert it to 3 hours without losing information. What is a good way to do this automatically?
I tried it by taking the number of trailing zeros into account:

import numpy as np

if __name__ == "__main__":
    td = np.timedelta64(10800000000001, 'ns')
    number_of_zeros = len(str(td.item())) - len(str(td.item()).rstrip('0'))
    if number_of_zeros==0:
        print("[ns]")
    elif number_of_zeros<7:
        print("ms")
    elif number_of_zeros<10:
        print("s")
    elif number_of_zeros<12:
        print("min")
    else:
        print("h")

This is probably not a very elegant way to do it (not to mention that it will be wrong when we get to minutes and higher). Any recommendations?


Solution

  • If I understand the problem statement, you only want to express the time in terms of one unit: the largest that retains precision using only whole numbers.

    import numpy as np
    td = np.timedelta64(10800000000000, 'ns')
    units = ['h', 'm', 's', 'ms', 'ns']
    
    # Convert to each of the desired units
    deltas = [td.astype(f'timedelta64[{unit}]') for unit in units]
    # Retain only the ones that are equivalent to the original representation
    delta = [delta for delta in deltas if delta == td]
    delta[0]  # extract the zeroth
    # np.timedelta64(3,'h')
    

    It it a little faster to divide the integer representation of the timedelta by the the number of nanoseconds in each unit and check whether the result is a whole number.

    # Check whether the number of nanoseconds in each unit is a whole number
    divisible = np.divmod(td.astype(int), ns_per_unit)[1] == 0
    # Use the first unit for which this is true
    unit = units[np.where(divisible)[0][0]]
    td.astype(f'timedelta64[{unit}]')  # perform the conversion
    

    But the first bit of code is probably a little easier to interpret. Depends on what your goals are.