web-dev-qa-db-fra.com

Comment sous-classer pandas DataFrame?

Sous-classement pandas classes semble un besoin commun mais je n'ai pas pu trouver de références sur le sujet. (Il semble que pandas les développeurs y travaillent toujours: https://github.com/pydata/pandas/issues/6 ).

Il y a quelques SO discussions sur le sujet, mais j'espère que quelqu'un ici pourra fournir un compte-rendu plus systématique sur actuellement la meilleure façon de sous-classer les pandas. :

import numpy as np
import pandas as pd

class MyDF(pd.DataFrame):
    # how to subclass pandas DataFrame?
    pass

mydf = MyDF(np.random.randn(3,4), columns=['A','B','C','D'])
print type(mydf)  # <class '__main__.MyDF'>

# Requirement 1: Instances of MyDF, when calling standard methods of DataFrame,
# should produce instances of MyDF.
mydf_sub = mydf[['A','C']]
print type(mydf_sub)  # <class 'pandas.core.frame.DataFrame'>

# Requirement 2: Attributes attached to instances of MyDF, when calling standard 
# methods of DataFrame, should still attach to the output.
mydf.myattr = 1
mydf_cp1 = MyDF(mydf)
mydf_cp2 = mydf.copy()
print hasattr(mydf_cp1, 'myattr')  # False
print hasattr(mydf_cp2, 'myattr')  # False

Et y a-t-il des différences importantes pour le sous-classement des pandas. Je vous remercie.

30
Lei

Il existe maintenant un guide officiel sur la façon de sous-classer les structures de données Pandas, qui inclut DataFrame ainsi que Series.

Le guide est disponible ici: http://pandas.pydata.org/pandas-docs/stable/internals.html#subclassing-pandas-data-structures

Le guide mentionne ce DataFrame sous-classé du projet Geopandas comme un bon exemple: https://github.com/geopandas/geopandas/blob/master/geopandas/geodataframe.py

Comme dans la réponse d'HYRY, il semble qu'il y ait deux choses que vous essayez d'accomplir:

  1. Lorsque vous appelez des méthodes sur une instance de votre classe, renvoyez des instances du type correct (votre type). Pour cela, vous pouvez simplement ajouter la propriété _constructor Qui devrait retourner votre type.
  2. Ajout d'attributs qui seront attachés aux copies de votre objet. Pour ce faire, vous devez stocker les noms de ces attributs dans une liste, en tant qu'attribut spécial _metadata.

Voici un exemple:

class SubclassedDataFrame(DataFrame):
    _metadata = ['added_property']
    added_property = 1  # This will be passed to copies

    @property
    def _constructor(self):
        return SubclassedDataFrame
21
cjrieds

Pour l'exigence 1, définissez simplement _constructor:

import pandas as pd
import numpy as np

class MyDF(pd.DataFrame):
    @property
    def _constructor(self):
        return MyDF


mydf = MyDF(np.random.randn(3,4), columns=['A','B','C','D'])
print type(mydf)

mydf_sub = mydf[['A','C']]
print type(mydf_sub)

Je pense qu'il n'y a pas de solution simple pour l'exigence 2, je pense que vous devez définir __init__, copy, ou faites quelque chose dans _constructor, par exemple:

import pandas as pd
import numpy as np

class MyDF(pd.DataFrame):
    _attributes_ = "myattr1,myattr2"

    def __init__(self, *args, **kw):
        super(MyDF, self).__init__(*args, **kw)
        if len(args) == 1 and isinstance(args[0], MyDF):
            args[0]._copy_attrs(self)

    def _copy_attrs(self, df):
        for attr in self._attributes_.split(","):
            df.__dict__[attr] = getattr(self, attr, None)

    @property
    def _constructor(self):
        def f(*args, **kw):
            df = MyDF(*args, **kw)
            self._copy_attrs(df)
            return df
        return f

mydf = MyDF(np.random.randn(3,4), columns=['A','B','C','D'])
print type(mydf)

mydf_sub = mydf[['A','C']]
print type(mydf_sub)

mydf.myattr1 = 1
mydf_cp1 = MyDF(mydf)
mydf_cp2 = mydf.copy()
print mydf_cp1.myattr1, mydf_cp2.myattr1
12
HYRY