Вопрос

У меня есть несколько классов, которые все ссылаются на одни и те же DataFrame PandaS, но только часть кадра данных имеет отношение к каждому классу.Я также хочу легко получить доступ к соответствующим строкам без использования расширенной индексации, так как он повторяется из-за номера, если уровни в индексе.В результате я писал код, который генерирует частичные функции, чтобы каждый класс мог просмотреть его ломтик.

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

Однако я бы предпочел, если ссылка не должна выглядеть как foo.x () [дата], но вместо этого может выглядеть как foo.x [дата].

В результате я создал декоратор, который будет обернуть функцию и вернуть значение.

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

Моя проблема заключается в том, что я не всегда буду получать текущее состояние датафарама.

Это правильный способ пойти на достижение моей цели?

Это было полезно?

Решение

Ну, лично я предложил бы вам обернуть ваше dataframe в так, как это так:

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

Вы используете это так:

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

Вы можете пойти дальше и добавлять методы в качестве свойств, если он имеет больше смысла интуитивно.

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

По сути, это то же самое, что и вы, как вы делаете сейчас, но это более простым объектно-ориентированным программированием и - по крайней мере, на мой взгляд - намного лучше понять.

Вы всегда можете получить доступ к вашему DataFrame так:

df.data
.

или затем, вы можете реализовать больше методов Pandas непосредственно на объекте вашего просмотра, например:

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

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

Итак, вы объект ведут себя больше похоже на файл dataframe.

Примечание, что на самом деле не «динамично».Если вы хотите по-настоящему динамичному способу, вы можете использовать метод Getattr для реализации этого, а также

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

Эта модель обычно называется композицией, и мой любимый способ внедрения вашей «проблемы»

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top