0

I am am trying to round numbers in a dataframe that has lists as values for each row. I need whole numbers to have no decimal and floats to only have two places after the decimal. There is an unknown number of values for each list (some lists have 2 values, some have 4 or 5 or more). Here is what I have:

df = pd.DataFrame({"A": [[16.0, 24.4175], [14.9687, 16.06], [22.75, 23.00]]})

def remove_exponent(num):
    return num.to_integral() if num == num.to_integral() else num.normalize()

def round_string_float(x):
    try:
        return remove_exponent(Decimal(x).quantize(TWOPLACES))
    except:
        return x  
df['A']=df['A'].apply(lambda x: [round_string_float(num) for num in x])
But this gives me: [Decimal('16'), Decimal('24.42')]

Here is what I am trying:

def round(num):
     if str(numbers).find('/') > -1:
          nom, den = numbers.split(',')
          number=round_string_float(nom)
          second=round_string_float(den)
          return f'[{number}, {second}]'
but there has to be an easier way to do this

Here is what I want:

df = pd.DataFrame({"A": [[16, 24.42], [14.97, 16.06], [22.75, 23]]})

I would like to know have to use **args to do this but really anything that works would be good

Shawn Jamal
  • 170
  • 8
  • You should not do this. You can create an additional column that indicates which list the number corresponds to. Just by reset index and then you should explode the list numbers. After that you can use all of the pandas functionality. Inserting lists to dataframe cells will force you to apply all kinds of hacks and it will be really slow. – alparslan mimaroğlu Aug 17 '21 at 07:20
  • @alparslanmimaroğlu, Unfortunately you are right but it has to be in a list for my employer and there is no way to get around it. Think of finding the length of a set of pots and pans. You might have 10 different lengths and the program that we upload the data only takes data in this format. – Shawn Jamal Aug 17 '21 at 13:59
  • That is sad to hear. You can always implode your data after exploding like [here](https://stackoverflow.com/questions/64235312/how-to-implodereverse-of-pandas-explode-based-on-a-column) but that is a preference I suppose – alparslan mimaroğlu Aug 17 '21 at 14:15

3 Answers3

1

Have you tried a for loop. For example

list = []
for i in range(len(df)):
    for j in range(len(df[i])):
        list .append(round(df[i][j]))
Tony Dean
  • 132
  • 7
1

That's a weird format for a DataFrame, but if you want it you can do something like this:

import pandas as pd

df = pd.DataFrame({"A": [[16.0, 24.4175], [14.9687, 16.06], [22.75, 23.00]]})
print(df.applymap(lambda x: [round(v, None if v.is_integer() else 2) for v in x]))

Given that

The return value [of round] is an integer if ndigits is omitted or None.

this evaluates, for each nested number v, round(v) if v is an integer else round(v, 2).

This outputs

                A
0     [16, 24.42]
1  [14.97, 16.06]
2     [22.75, 23]
enzo
  • 9,861
  • 3
  • 15
  • 38
0

I created an answer to this question that goes above and beyond what I wanted but I think it will help anyone looking for something similar. The problem with my company is we have to upload lists as values in a dataframe to the database. This is why the code is so ad-hoc:

from decimal import *
TWOPLACES = Decimal(10) ** -2
from natsort import natsorted
import ast
from fractions import Fraction
#----------------------------------------------------------------
# remove_exponent and round string float are designed to round whole numbers 16.00 to 16, and rounds numbers with 3 or more decimals to 2 decimals 16.254 to 16.25
def remove_exponent(num): 
    return num.to_integral() if num == num.to_integral() else num.normalize()

def round_string_float(x):
    try:
        return remove_exponent(Decimal(x).quantize(TWOPLACES))
    except:
        return x 
#------------------------------------------------------------------------------
# frac2string converts fractions to decimals: 1 1/2 to 1.5
def frac2string(s):
    i, f = s.groups(0)
    f = round_string_float(Fraction(f))
    return str(int(i) + round_string_float(float(f)))

#------------------------------------------
#remove duplicates is self explanitory
def remove_duplicates(A):
    [A.pop(count) for count,elem in enumerate(A) if A.count(elem)!=1]
    return A


# converts fractions and rounds numbers
df['matches'] = df['matches'].apply(lambda x:[re.sub(r'(?:(\d+)[-\s])?(\d+/\d+)', frac2string, x)])

# removes duplicates( this needs to be in the format ["\d","\d"]
df['matches'] = df['matches'].apply(lambda x: remove_duplicates([n.strip() for n in ast.literal_eval(x)]))
Shawn Jamal
  • 170
  • 8