import pandas as pd

df = pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col']))


class Test:
    def __init__(self, data):
        self.data = data
        self.data.set_index('index', inplace = True)


test1 = Test(df)
test2 = Test(df)

print(test1.data)
print(test2.data)

Esto arroja un error: KeyError: "Ninguno de ['index'] está en las columnas"

Me di cuenta de que el uso de set_index() con inplace = True en el método __init__ no manipula la variable self.data que pertenece a la instancia del objeto. De hecho, establece data como una variable de clase compartida por todas las instancias.

Cuando evito usar inplace no obtengo el error ya que ahora la variable self.data de la instancia del objeto está configurada.

import pandas as pd

df = pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col']))


class Test:
    def __init__(self, data):
        self.data = data
        self.data = self.data.set_index('index', inplace=False)


test1 = Test(df)
test2 = Test(df)

print(test1.data)
print(test2.data)

Salida:

       col
index    
1      li
2      la
3      lu
       col
index    
1      li
2      la
3      lu

¿Cuál es la razón de este comportamiento? Me parece un poco contradictorio que se establezca una variable de clase mientras se usa una función en una variable precedida por .self

¿Existe alguna razón o ventaja para usar inplace = True?

2
Johann Kraft 25 ago. 2020 a las 18:12

2 respuestas

La mejor respuesta

No creo que esté relacionado con los pandas, sino más bien con el hecho de que Python es un lenguaje de referencia de paso por objeto (ver explicaciones aquí).

Considere el siguiente ejemplo que tiene un comportamiento similar al de su ejemplo:

class Test2:
    def __init__(self, data):
        self.data = data
        self.data.append(2)

A=[0,1]
test1 = Test2(A)
print(A)

Salida:

[0, 1, 2]

Las modificaciones al objeto subyacente A se conservan (porque es una lista y las listas son modificables como los marcos de datos de pandas).

En su ejemplo, cuando use self.data.set_index('index', inplace = True), NO se creará un nuevo marco de datos de manera similar al ejemplo anterior, el objeto subyacente df se mantendrá.

Considere la siguiente adición a su código:

import pandas as pd

df = pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col']))

class Test:
    def __init__(self, data):
        self.data = data
        self.data.set_index('index', inplace = True)


print(df.columns)
test1 = Test(df)
print(df.columns)

Salidas:

Index(['index', 'col'], dtype='object')
Index(['col'], dtype='object')

df fue cambiado.

Finalmente, lo siguiente habría funcionado:

import pandas as pd

df = pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col']))

class Test:
    def __init__(self, data):
        self.data = data
        self.data.set_index('index', inplace = True)

test1 = Test(pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col'])))
test2 = Test(pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col'])))

print(test1.data)
print(test2.data)
1
Gillu13 25 ago. 2020 a las 15:58

No cree el objeto de la clase de prueba por segunda vez. Una vez que haya establecido el índice para el objeto test1, no habrá más columna 'índice' en el marco de datos para test2. Simplemente modifique el mismo código que:

df = pd.DataFrame([[1, 'li'], [2, 'la'], [3, 'lu']], columns=(['index', 'col']))


class Test:
    def __init__(self, data):
        self.data = data
        print(self.data)
        self.data.set_index('index', inplace = True)


test1 = Test(df)
print(test1.data)
1
Surya Lohia. 25 ago. 2020 a las 15:32