Pregunta

Tengo varias clases que todos hacen referencia al mismo pandas DataFrame, pero solo parte del cuadro de datos es relevante para cada clase.También quiero facilitar el acceso a las filas relevantes sin usar la indexación avanzada, ya que se vuelve repetitiva debido al número si los niveles en el índice.Como resultado, escribí el código que genera funciones parciales para que cada clase pueda ver su rebanada.

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()

Sin embargo, preferiría si la referencia no tenía que parecerse a FOO.x () [FECHA], pero en su lugar podría parecer foo.x [Fecha].

Como resultado, creé un decorador que envolvería la función y devolvería el valor.

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'))

Mi preocupación es que no siempre obtendré el estado actual delframe.

¿Es esta la manera correcta de lograr el logro de mi objetivo?

¿Fue útil?

Solución

Bueno, personalmente, le sugeriría que envuelva su estructura de datos en un objeto así:

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']

lo usas así:

df = MyDataFrameView(data)
df.x()

Puede ir más allá y agregar los métodos como propiedades si tiene más sentido intuitivamente.

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

Esencialmente está haciendo lo mismo como lo haces ahora, pero es una programación más orientada hacia el objeto y, al menos en mi opinión, mucho mejor entender.

Siempre puede acceder a su estructura de datos así:

df.data

O entonces, podría implementar más métodos de pandas directamente en su objeto de vista, por ejemplo:

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

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

Para que el objeto se comporte más como un conteo de datos.

Nota que no es realmente "dinámica".Si desea una forma verdaderamente dinámica, podría usar el método getattr para implementarlo también

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

Este patrón generalmente se llama composición, y mi forma favorita de implementar su "problema"

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top