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)