Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

419
Views
¿En lugar de cambiar o sobrescribir dataframe?

Estoy trabajando en una tubería de manipulaciones en un marco de datos de pandas en clase, y me pregunto cuáles son los buenos pasos para concatenar algunos procedimientos uno tras otro: ¿debería copiar y recrear el marco de datos original, o simplemente cambiarlo en su lugar? ?

De acuerdo con la documentación de pandas, no siempre se recomienda trabajar con vistas, y no estoy seguro si este es el caso aquí.

Por ejemplo:

 # first approach import pandas as pd class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = pd.DataFrame(data, columns = ['Name', 'Age']) self.df_with_double_age_col = self._double_age() self.df_with_double_age_col_and_first_letter = self._get_first_letter_of_name() def _double_age(self): self.df['double_age'] = self.df['Age'] * 2 return self.df def _get_first_letter_of_name(self): self.df['first_letter'] = self.df['Name'].str[0] return self.df

En la implementación anterior, obtenemos que self.df es igual a self.df_with_double_age_col e igual a self.df_with_double_age_col .

Puedes ver que si vas a ejecutar:

 my_df = MyDataframe() print(my_df.df.equals(my_df.df_with_double_age_col)) # True

No creo que sea una buena situación (una cosa con varios nombres). Obtendré muchos alias si agrego más pasos de procesamiento. Alternativamente, me preocupaba usar solo un marco de datos y sobrescribirlo en cada paso.

Entonces, ¿qué ves como problema en esa situación? He incluido dos implementaciones opcionales adicionales para ese caso a continuación.


Sobrescribir (segundo enfoque):

 import pandas as pd class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = pd.DataFrame(data, columns = ['Name', 'Age']) self.df = self._double_age() self.df = self._get_first_letter_of_name() def _double_age(self): self.df['double_age'] = self.df['Age'] * 2 return self.df def _get_first_letter_of_name(self): self.df['first_letter'] = self.df['Name'].str[0] return self.df

Cambio en el lugar (sin return s, tercer enfoque)):

 import pandas as pd class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = pd.DataFrame(data, columns = ['Name', 'Age']) self._double_age() self._get_first_letter_of_name() def _double_age(self): self.df['double_age'] = self.df['Age'] * 2 def _get_first_letter_of_name(self): self.df['first_letter'] = self.df['Name'].str[0]

Creo que la última opción es la más compacta y elegante, pero cambiar el marco de datos dado puede ser inconveniente y arriesgado (en el contexto de SettingWithCopyWarning ).

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

En realidad, ambas implementaciones (las tres) están mutando el df de su clase en el lugar y, por lo tanto, son prácticamente equivalentes en cuanto a su efecto en la clase. La diferencia es que en la primera y segunda implementaciones, después de mutar en el lugar, devuelve el df mutado. En la primera implementación asignándolo a diferentes atributos de clase (alias como los llame) y en la segunda implementación asignándolo a sí mismo (lo que no tiene efecto).

Entonces, si hay alguna, la tercera implementación es la más válida.

No obstante, en caso de que desee lograr una sintaxis más funcional, que se puede decir que es la forma de pandas "para concatenar algunos procedimientos uno tras otro" como indicó, puede usar funciones en lugar de métodos y pasarles el self.df como parámetro. Luego puede asignar los resultados a nuevas columnas en su self.df

Por ejemplo:

 class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = pd.DataFrame(data, columns = ['Name', 'Age']) self.df['double_age'] = _double_age(self.df) self.df['first_letter'] = _get_first_letter_of_name(self.df) def _double_age(df): return df['Age'] * 2 def _get_first_letter_of_name(df): return df['Name'].str[0]

O en caso de que prefiera funciones que siempre devuelvan un nuevo DataFrame (para que tenga acceso a estados intermedios):

 class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = pd.DataFrame(data, columns = ['Name', 'Age']) self.df_with_double_age_col = _double_age(self.df) self.df_with_double_age_col_and_first_letter = _get_first_letter_of_name(self.df_with_double_age_col) def _double_age(df): new_df = df.copy() new_df['double_age'] = new_df['Age'] * 2 return new_df def _get_first_letter_of_name(df): new_df = df.copy() new_df['first_letter'] = new_df['Name'].str[0] return new_df

Preste atención a que en ambos ejemplos la función es a nivel de módulo. Por supuesto, puede optar por convertirlos en métodos, pero probablemente desee anotarlos como @staticmethod

over 4 years ago · Santiago Trujillo Report

0

Personalmente, no me gusta modificar los datos en su lugar. Me preocupa lo que podría suceder si se llama al método más de una vez, o lo que podría suceder si el programa falla después de algunas modificaciones en el lugar, o si otras partes del programa hacen referencia al objeto de la tabla.

Probablemente resolvería la tarea usando .pipe o .assign .

Haciendo solo pequeños cambios:

 import pandas as pd class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = (pd .DataFrame(data, columns = ['Name', 'Age']) .pipe(self._double_age) .pipe(self._get_first_letter_of_name) ) def _double_age(self): return self.df.assign(double_age = lambda x: x['Age'] * 2) def _get_first_letter_of_name(self): return self.df.assign(first_letter = lambda x: x['Name'].str[0])

Dicho esto, también podrías hacer esto:

 import pandas as pd class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = (pd .DataFrame(data, columns = ['Name', 'Age']) .assign(double_age = lambda x: x['Age'] * 2) .assign(first_letter = lambda x: x['Name'].str[0]) )

A menos que quieras ser un salvavidas, en cuyo caso:

 class MyDataframe: def __init__(self): data = [['alice', 7], ['bob', 15], ['carol', 2]] self.df = (pd .DataFrame(data, columns = ['Name', 'Age']) .assign(**{ 'double_age': lambda x: x['Age'] * 2, 'first_letter': lambda x: x['Name'].str[0] }) )

Si este fuera mi código base, probablemente terminaría con

 df = pd.DataFrame({'name':['Alice', 'Bob', 'Carol'], 'Age':[7,15,2]}) def add_features(df:pd.DataFrame)->pd.DataFrame: ans = df.assign(**{ 'double_age': lambda x: x['Age'] * 2, 'first_letter': lambda x: x['Name'].str[0] }) return ans df.pipe(add_features)
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!