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.
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:
_constructor
Qui devrait retourner votre type._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
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