Domanda

Ho diverse classi che tutti fanno riferimento allo stesso valore Pandas DataFrame, ma solo una parte del telaio dei dati è rilevante per ogni classe.Voglio anche semplificare accedere alle righe pertinenti senza utilizzare l'indicizzazione avanzata in quanto viene ripetitivo a causa del numero se livelli nell'indice.Di conseguenza, ho scritto il codice che genera funzioni parziali in modo che ogni classe possa vedere la sua fetta.

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

Tuttavia, preferirei se il riferimento non ha da sembrare foo.x () [data] ma potrebbe invece sembrare foo.x [data].

Di conseguenza, ho creato un decoratore che avvolgerà la funzione e restituirebbe il valore.

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

La mia preoccupazione è che non otterrò sempre lo stato attuale del DataFrame.

è questo il modo giusto per andare a realizzare il mio obiettivo?

È stato utile?

Soluzione

Bene, ti suggerirei personalmente di avvolgere il tuo dataFrame in un oggetto come:

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 usi come:

df = MyDataFrameView(data)
df.x()
.

Puoi andare oltre e aggiungere i metodi come proprietà se rende più senso intuitivamente.

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

È essenzialmente facendo la stessa cosa come te ora, ma è più semplice la programmazione orientata agli oggetti e - almeno a mio parere - molto meglio da capire.

Puoi sempre accedere al tuo DataFrame come così:

df.data
.

O poi, è possibile implementare più metodi Pandas direttamente sull'oggetto di visualizzazione, ad esempio:

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

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

Quindi l'oggetto si comporta più come un dataframe.

nota che non è davvero "dinamica".Se vuoi un modo veramente dinamico, puoi usare il metodo Getattr per implementarlo anche

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

Questo modello è generalmente chiamato composizione e il mio modo preferito di implementare il tuo "problema"

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top