Shawn Jamal
Shawn Jamal

Reputation: 180

Python Creating a function with unkown number arguments with splat operator

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

Upvotes: 0

Views: 112

Answers (3)

Shawn Jamal
Shawn Jamal

Reputation: 180

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)]))

Upvotes: 0

enzo
enzo

Reputation: 11496

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]

Upvotes: 1

Tony Dean
Tony Dean

Reputation: 152

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]))

Upvotes: 1

Related Questions