Estoy atascado en cómo escribir una función para calcular el valor de una columna en función de otra columna.
Por ejemplo, mi marco de datos se ve así:
Name Total Bonus 0 Amy 15000 10k 1 Bob 14000 10% 2 Cathy 13400 5.5k 3 David 14800 10% - 20% 4 Emma 15200 8% 5 Fay 13800 0 - 5% 6 Gina 14500 5k - 10k ...Código para recrearlo:
df = pd.DataFrame() df["Name"] = ["Amy", "Bob", "Cathy", "David", "Emma", "Fay", "Gina"] df["Total"] = ["15000", "14000", "13400", "14800","15200","13800","14500"] df["Bonus"] = ["10k", "10%", "5.5k", "10% - 20%", "8%", "0 - 5%", "5k - 10k"] El dtype de todas las columnas en el marco de datos es Object .
Lo que quiero es convertir la columna Bonus de forma organizada:
El resultado df debería verse así:
Name Total Bonus 0 Amy 15000 10000 1 Bob 14000 1400 2 Cathy 13400 5500 3 David 14800 1480 4 Emma 15200 1216 5 Fay 13800 0 6 Gina 14500 5000 ...Estoy luchando con la función. Pude escribir una función para convertir stringNum a int y hacer las operaciones. Pero no pude volver a un dafaframe.
Aquí está mi código:
def convertToNumber(df): for i in df.index: bonus = df.Bonus[i] if bonus == "0": bonus = 0 elif bonus.endswith('k'): kb = bonus.split("k") #print(kb) bonus = int(float(kb[0]) * 1000) elif bonus.endswith("%"): total = int(df.Total[i]) if len(bonus) > 2: b = int(bonus[: 2]) else: b = int(bonus[0]) bonus = total * b // 100 else: bonus = -1 print(bonus) convertToNumber(df) dfY mi resultado es:
10000 1400 5500 1480 1216 0 5000 Name Total Bonus 0 Amy 15000 10k 1 Bob 14000 10% 2 Cathy 13400 5.5k 3 David 14800 10% - 20% 4 Emma 15200 8% 5 Fay 13800 0 - 5% 6 Gina 14500 5k - 10kPrimero, maneje los rangos y seleccione el valor más bajo, luego cree dos máscaras booleanas para k y % separado y luego aplique toda la lógica relacionada. Por ejemplo:
# Handle ranges df['Bonus'] = df['Bonus'].str.split('-').str[0].str.strip() # Create boolean masks ks = df['Bonus'].str.endswith('k') ps = df['Bonus'].str.endswith('%') # Remove 'k' and '%' and convert the type to float df.loc[ks | ps, 'Bonus'] = df.loc[ks | ps, 'Bonus'].str[:-1] df['Bonus'] = df['Bonus'].astype(float) # Apply the mask logic and convert to int df.loc[ks, 'Bonus'] = df.loc[ks, 'Bonus'] * 1000 df.loc[ps, 'Bonus'] = df.loc[ps, 'Total'] * df.loc[ps, 'Bonus'] / 100 df['Bonus'] = df['Bonus'].astype(int)Resultado:
Name Total Bonus 0 Amy 15000 10000 1 Bob 14000 1400 2 Cathy 13400 5500 3 David 14800 1480 4 Emma 15200 1216 5 Fay 13800 0 6 Gina 14500 5000Tengo una solución basada en expresiones regulares:
import re import pandas as pd import numpy as np p = re.compile("^([\d\.]+).*?([%|k]?)$") def calc(v, total): this_match = p.findall(v) if len(this_match) > 0: try: this_match = this_match[0] if this_match[1] == "k": return int(float(this_match[0]) * 1000) elif this_match[1] == "%": return int(int(total) * float(this_match[0]) / 100) else: return int(this_match[0]) except ValueError: print(f'Problems in parsing numeric patterns in {v}') else: print(f'Cannot match pattern {v}') return np.nan No me malinterpreten: la solución vectorizada propuesta anteriormente siempre es preferible, especialmente si tiene que trabajar con marcos grandes. Para marcos más pequeños, re resolución es más rápida, así que creo que realmente depende de lo que tengas que hacer.