如何在Pandas中创建lazy_evaluated嵌套框架列

b5lpy0ml  于 2023-10-14  发布在  其他
关注(0)|答案(3)|浏览(139)

很多时候,我有一个很大的数据库df来保存基本数据,并且需要创建更多的列来保存由基本数据列计算的派生数据。
我可以在Pandas中这样做:

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)

Pandas将一次性为所有派生列计算和分配内存。
我现在想要的是有一个懒惰的评估机制,将派生列的计算和内存分配推迟到实际需要的时刻。将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'])

这将像Python 'yield'生成器一样保存时间/内存,因为如果我发出df['derivative_col2']命令,只会触发特定的计算和内存分配。
如何在Pandas中实现lazy_eval()?任何提示/想法/参考都欢迎。

z9smfwbn

z9smfwbn1#

从0.13开始(很快就会发布),你可以这样做。这是使用生成器来计算动态公式。通过eval进行内联赋值将是0.13中的一个附加特性,请参见here

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将支持这一点,因此pandas可能会作为eval的后端(目前使用numexpr进行即时计算)来支持这一点。
我的2C。
lazy evaluation很好,但是可以很容易地通过使用python自己的continuation/generate特性来实现,所以将其构建到pandas中,虽然可能,但相当棘手,并且需要一个非常好的用例才能普遍有用。

monwx1rj

monwx1rj2#

您可以子类化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)调用LazyFrame中定义的derivative_col1函数。此函数计算结果并将派生列添加到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
lzfw57am

lzfw57am3#

在对Pandas的数据结构进行任何修改或扩展之前,最好使用DaskApache Spark等库来减轻进程负载。

Pandas的文档提出了一些扩展其数据结构的替代方案:
在考虑对pandas数据结构进行子类化之前,有一些更简单的替代方案。

  • 带管道的可扩展方法链
  • 使用合成。看这里。
  • 通过注册访问器进行扩展
  • 按扩展类型扩展

子类化pandas.DataFrame

使用

让我们假设您已经有了名为dfDataFrame(根据下面的CustomDataFrame类定义构造),并填充了数据(出于本示例的目的)

def column1_column2_sum(data): return data['column1'] + data['column2']

df[column1_column2_sum.__name__] = column1_column2_sum

类定义

为简单起见,all_columns方法定义假设这里只有一个列级别。

from typing import Callable, Dict
import pandas as pd

class CustomDataFrame(pd.DataFrame):
    _metadata = ["_lazy_columns"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._lazy_columns = {}

    @property
    def _constructor(self):
        return CustomDataFrame

    @property
    def all_columns(self):
        return list(super().columns) + list(self._lazy_columns.keys())

    def register_lazy_series(self, columns: Dict[str, Callable]):
        self._lazy_columns.update(((self._format_key(key), value)
                                   for key, value in columns.items()))

    def __setitem__(self, key, value) -> None:
        if callable(value):
            return self._lazy_columns.__setitem__(key, value)
        return super().__setitem__(key, value)

    def __getitem__(self, key):
        if key in self._lazy_columns.keys():
            compute = self._lazy_columns[key]
            self[key] = compute(self)
            self._lazy_columns.pop(key)

        return super().__getitem__(key)

请在评论中对上述代码提出任何修改建议。

相关问题