Rustam Guliev
Rustam Guliev

Reputation: 954

How to make an accessor class like in pandas?

I have a class with a lot of methods which are logically can be grouped. I would like to group them like in pandas. For example, in pandas all plotting method are accessible by df.plot i.e. df.plot.hist(...), df.plot.bar(...). Rolling methods also grouped and accessible like df.rolling(2).sum().

I looked at the source code of pandas but it did not help.

How can I implement such intermediate accessor classes like .plot.<plot method>, .normalize.<normalize method>, .smooth.<smooth method>, etc. for my custom class?

Upvotes: 0

Views: 163

Answers (1)

ForceBru
ForceBru

Reputation: 44878

Here's a very basic implementation:

>>> class FancyPlotting:
...     def __init__(self, info: dict):
...         self.info = info
...     def plot(self, *args):
...         ... # use `self.info` and `args` to decide what and how to plot
...         
>>> class FancyDataframe:
...     def __init__(self, data: list):
...         self.data = data
...     def rolling(self, number: int):
...         return FancyRoller(self.data, number)
...     @property
...     def plot(self):
...         return FancyPlotting({'points': self.data})
...
>>> class FancyRoller:
...     def __init__(self, data: list, window_length: int):
...         self.data, self.window_length = data, window_length
...     def sum(self):
...         return sum(self.data[::self.window_length]) # this is not an actual implementation
...          
>>> df = FancyDataframe([1,2,3,4,5,6,7,8])
>>> df.plot.plot()
# does stuff
>>> df.rolling(1).sum()
36

Of course, reading pandas' source should give you a better idea of how this is done.

EDIT: Python won't unnecessarily copy data, so excessive memory usage is not a problem:

class FancyRoller:
    def __init__(self, data: list, **settings):
        self.data, self.settings = data, settings

    def sum(self):
        return sum(self.data[::self.settings['size']])

class FancyDataframe:
    def __init__(self, data):
        self.data = data

    def rolling(self, size):
        # again, this is just an example
        return FancyRoller(self.data, size=size)

df = FancyDataframe([1,2,3,4,5,6,7])
assert id(df.data) == id(df.rolling(2).data)
print(df.rolling(1).sum())

Upvotes: 2

Related Questions