Как создать ленивые вычисляемые столбцы dataframe в Pandas

много раз, у меня есть большой фрейм данных df для хранения основных данных и нужно создать еще много столбцов для хранения производных данных, рассчитанных по столбцам базовых данных.

Я могу сделать это в панд, как:

df['derivative_col1'] = df['basic_col1'] + df['basic_col2']
df['derivative_col2'] = df['basic_col1'] * df['basic_col2']
....
df['derivative_coln'] = func(list_of_basic_cols)

etc. Панды будут вычислять и выделять память для всех производных столбцов сразу.

теперь я хочу иметь ленивый механизм оценки, чтобы отложить вычисление и выделение памяти производных столбцов на действительно нужен момент. Несколько определите lazy_eval_columns как:

df['derivative_col1'] = pandas.lazy_eval(df['basic_col1'] + df['basic_col2'])
df['derivative_col2'] = pandas.lazy_eval(df['basic_col1'] * df['basic_col2'])

это сэкономит время / память, как генератор "yield" Python, если я выдам df['derivative_col2'] команда будет только тригер конкретного расчета и выделения памяти.

как сделать lazy_eval() в Пандах ? Любой совет/мысль/ref приветствуются.

2 ответов


начиная с 0.13 (выпуск очень скоро), вы можете сделать что-то вроде этого. Это использование генераторов для оценки динамической формулы. Встроенное назначение через eval будет дополнительной функцией в 0.13, см. здесь

In [19]: df = DataFrame(randn(5, 2), columns=['a', 'b'])

In [20]: df
Out[20]: 
          a         b
0 -1.949107 -0.763762
1 -0.382173 -0.970349
2  0.202116  0.094344
3 -1.225579 -0.447545
4  1.739508 -0.400829

In [21]: formulas = [ ('c','a+b'), ('d', 'a*c')]

создайте генератор, который оценивает формулу с помощью eval; присваивает результат, затем дает результат.

In [22]: def lazy(x, formulas):
   ....:     for col, f in formulas:
   ....:         x[col] = x.eval(f)
   ....:         yield x
   ....:         

в действиях

In [23]: gen = lazy(df,formulas)

In [24]: gen.next()
Out[24]: 
          a         b         c
0 -1.949107 -0.763762 -2.712869
1 -0.382173 -0.970349 -1.352522
2  0.202116  0.094344  0.296459
3 -1.225579 -0.447545 -1.673123
4  1.739508 -0.400829  1.338679

In [25]: gen.next()
Out[25]: 
          a         b         c         d
0 -1.949107 -0.763762 -2.712869  5.287670
1 -0.382173 -0.970349 -1.352522  0.516897
2  0.202116  0.094344  0.296459  0.059919
3 -1.225579 -0.447545 -1.673123  2.050545
4  1.739508 -0.400829  1.338679  2.328644

таким образом, его пользователь определил заказ для оценки (а не по требованию). В теории numba собирается поддержать это, поэтому панды, возможно, поддерживают это как бэкэнд для eval (который в настоящее время использует numexpr для немедленной оценки).

мой 2c.

ленивая оценка хороша, но может быть легко достигнута с помощью собственных функций продолжения/генерации python, поэтому создание его в панд, хотя это возможно, довольно сложно, и потребуется действительно хороший usecase, чтобы быть в целом полезным.


вы могли бы подкласс DataFrame, и добавьте столбец как свойства. Например,

import pandas as pd

class LazyFrame(pd.DataFrame):
    @property
    def derivative_col1(self):
        self['derivative_col1'] = result = self['basic_col1'] + self['basic_col2']
        return result

x = LazyFrame({'basic_col1':[1,2,3],
               'basic_col2':[4,5,6]})
print(x)
#    basic_col1  basic_col2
# 0           1           4
# 1           2           5
# 2           3           6

доступ к собственности (через x.derivative_col1 ниже) называет derivative_col1 функция, определенная в LazyFrame. Эта функция вычисляет результат и добавляет производный столбец в экземпляр LazyFrame:

print(x.derivative_col1)
# 0    5
# 1    7
# 2    9

print(x)
#    basic_col1  basic_col2  derivative_col1
# 0           1           4                5
# 1           2           5                7
# 2           3           6                9

обратите внимание, что при изменении базового столбца:

x['basic_col1'] *= 10

производный столбец не автоматически обновлено:

print(x['derivative_col1'])
# 0    5
# 1    7
# 2    9

но если вы получаете доступ к свойству, значениям вычисляются:

print(x.derivative_col1)
# 0    14
# 1    25
# 2    36

print(x)
#    basic_col1  basic_col2  derivative_col1
# 0          10           4               14
# 1          20           5               25
# 2          30           6               36