Home > Mobile >  Either round or truncate pandas column values to 2 decimals, based on floor/ceil conditions
Either round or truncate pandas column values to 2 decimals, based on floor/ceil conditions

Time:02-13

I have a data frame that I need to convert specifically to two decimal place resolution based on the following logic:

  • if x (in terms of the value with more than two decimals places) > math.floor(x) 0.5

    • ...then round this value to two decimals.
  • if x (in terms of the value with more than two decimals places) < math.ceil(x) - 0.5

    • ...then truncate this value to two decimals.

The main hang-up I am having is just actually seeing these newly rounded/truncated values replace the originals in the data frame.

Sample dataframe:

import math
import pandas as pd  

test_df = pd.DataFrame({'weights': ['25.2524%', '25.7578%', '35.5012%', '13.5000%', 
    "50.8782%", "10.2830%", "5.5050%", "30.5555%", "20.7550%"]})

# .. which creates:

   | weights |
|0 | 25.2524%|
|1 | 25.7578%|
|2 | 35.5012%|
|3 | 13.5000%|
|4 | 50.8782%|
|5 | 10.2830%|
|6 |  5.5050%|
|7 | 30.5555%|
|8 | 20.7550%|

Define truncate function, and also the function that will configure decimal resolution:

def truncate_decimals(target_allocation, two_decimal_places) -> float:
    decimal_exponent = 10.0 ** two_decimal_places
    return math.trunc(decimal_exponent * target_allocation) / decimal_exponent

def decimals(df):
    df["weights"] = df["weights"].str.rstrip("%").astype("float")
    decimal_precision = 2
    for x in df["weights"]:
        if x > math.floor(x)   0.5:
            x = round(x, decimal_precision)
            print("This value is being rounded", x)
            df.loc[(df.weights == x), ('weights')] = x
        elif x < math.ceil(x) - 0.5:
            y = truncate_decimals(x, decimal_precision)
            print("This value is being truncated", y)
            df.loc[(df.weights == x), ('weights')] = y
        else:
            pass
            print("This value does not meet one of the above conditions", round(x, decimal_precision))

    return df


decimals(test_df)

Expected output:

This value is being truncated 25.25
This value is being rounded 25.76
This value is being rounded 35.5
This value does not meet one of the above conditions 13.5
This value is being rounded 50.88
This value is being truncated 10.28
This value is being rounded 5.5
This value is being rounded 30.56
This value is being rounded 20.75

   | weights|
|0 | 25.25  |
|1 | 25.76  |
|2 | 35.5   |
|3 | 13.5   |
|4 | 50.88  |
|5 | 10.28  |
|6 |  5.5   |
|7 | 30.56  |
|8 | 20.75  |

Current output:

The current value is being truncated 25.25

   | weights |
|0 | 25.2524%|
|1 | 25.7578%|
|2 | 35.5012%|
|3 | 13.5000%|
|4 | 50.8782%|
|5 | 10.2830%|
|6 |  5.5050%|
|7 | 30.5555%|
|8 | 20.7550%|

CodePudding user response:

Another approach could be to define a function that applies the above rule for a generic number and then apply it to each weight in the column.

Something like this

import math
import pandas as pd  

test_df = pd.DataFrame({'weights': ['25.2524%', '25.7578%', '35.5012%', '13.5000%', 
    "50.8782%", "10.2830%", "5.5050%", "30.5555%", "20.7550%"]})

def truncate_decimals(target_allocation, two_decimal_places) -> float:
    decimal_exponent = 10.0 ** two_decimal_places
    return math.trunc(decimal_exponent * target_allocation) / decimal_exponent

def rule(number, decimal_precision=2):
    number = float(number.rstrip("%"))
    
    if number > math.floor(number)   0.5:
        number = round(number, decimal_precision)
        print("This value is being rounded", number)
    
    elif number < math.ceil(number) - 0.5:
        number = truncate_decimals(number, decimal_precision)
        print("This value is being truncated", number)
       
    else:
        print("This value does not meet one of the above conditions", round(number, decimal_precision))
    
    return number

test_df['rounded'] = test_df.weights.apply(rule)
  • Related