Question

J'ai plusieurs classes qui font toutes référence aux mêmes pandas Dataframe, mais seule une partie de la trame de données est pertinente pour chaque classe.Je souhaite également faciliter l'accès aux lignes pertinentes sans utiliser l'indexation avancée car elle est répétitive en raison du nombre si les niveaux de l'index.En conséquence, j'ai écrit du code qui génère des fonctions partielles afin que chaque classe puisse voir sa tranche.

from functools import partial
import pandas as pd
import numpy as np
import dateutil.relativedelta as rd
import datetime as dt

class baz(object):
    pass

groups = ['foo', 'foo', 'bar', 'bar']
items = ['x','y', 'x', 'y']
diff = rd.relativedelta(years=1)

dates = [dt.date(2013,1,1) + (diff * shift) for shift in xrange(4)] * 2
index = pd.MultiIndex.from_arrays([groups, items], names=['groups', 'items'])
values = np.random.randn(4,8)

data = pd.DataFrame(values, index=index, columns=dates)

def view_data(group, item):
    return data.ix[group, item]

foo = baz()
bar = baz()

# I use partial because I want lazy evaluation
foo.x = partial(view_data, 'foo', 'x')
foo.y = partial(view_data, 'foo', 'y')
bar.x = partial(view_data, 'bar', 'x')
bar.y = partial(view_data, 'bar', 'y')

foo.x()

Cependant, je préférerais si la référence n'avait pas à ressembler à FOO.X () [date] mais pourrait plutôt ressembler à FOO.X [date].

En conséquence, j'ai créé un décorateur qui envelopperait la fonction et renvoyer la valeur.

def execute_func(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner()

foo.x = execute_func(partial(view_data, 'foo', 'x'))
foo.y = execute_func(partial(view_data, 'foo', 'y'))
bar.x = execute_func(partial(view_data, 'bar', 'x'))
bar.y = execute_func(partial(view_data, 'bar', 'y'))

Mon inquiétude est que je n'aurai pas toujours l'état actuel du Dataframe.

Est-ce la bonne façon de réaliser mon objectif?

Était-ce utile?

La solution

Eh bien, je vous suggère personnellement que vous enveloppez votre Dataframe dans un objet, comme:

class MyDataFrameView(object):

    def __init__(self, df):
        self.data = df

    def x(self):
        return self.data.ix['foo', 'x']

    def y(self):
        return self.data.ix['bar', 'y']

vous l'utilisez comme si:

df = MyDataFrameView(data)
df.x()

Vous pouvez aller plus loin et ajouter les méthodes sous forme de propriétés s'il a plus de sens intuitivement.

@property
def y(self):
    return self.data.ix['bar', 'y']

Cela fait essentiellement la même chose comme vous le faites maintenant, mais c'est plus simple la programmation orientée objet et - au moins à mon avis - beaucoup mieux à comprendre.

Vous pouvez toujours accéder à votre Dataframe comme SO:

df.data

ou ensuite, vous pouvez implémenter plus de méthodes de pandas directement sur votre objet d'affichage, par exemple:

@property
def ix(self):
    return self.data.ix

def __getitem__(self, key):
    return self.data.__getitem__(key)

Donc, votre objet se comporte plus comme un Dataframe.

note qui n'est pas vraiment "dynamique".Si vous voulez une manière vraiment dynamique, vous pouvez utiliser la méthode getattr pour implémenter cela aussi bien

def __getattr__(self, attr):
   #code that "routes" to do the right thing given attr

Ce modèle est généralement appelé composition et ma manière préférée de mettre en œuvre votre "problème"

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top