expr-codegen 0.10.6__tar.gz → 0.10.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/PKG-INFO +22 -23
  2. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/README.md +21 -22
  3. expr_codegen-0.10.8/expr_codegen/_version.py +1 -0
  4. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/codes.py +1 -0
  5. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/model.py +6 -2
  6. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/tool.py +12 -13
  7. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen.egg-info/PKG-INFO +22 -23
  8. expr_codegen-0.10.6/expr_codegen/_version.py +0 -1
  9. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/LICENSE +0 -0
  10. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/__init__.py +0 -0
  11. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/dag.py +0 -0
  12. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/expr.py +0 -0
  13. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/latex/__init__.py +0 -0
  14. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/latex/printer.py +0 -0
  15. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/__init__.py +0 -0
  16. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/code.py +0 -0
  17. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/helper.py +0 -0
  18. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/printer.py +0 -0
  19. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/ta.py +0 -0
  20. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/pandas/template.py.j2 +0 -0
  21. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_group/__init__.py +0 -0
  22. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_group/code.py +0 -0
  23. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_group/printer.py +0 -0
  24. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_group/template.py.j2 +0 -0
  25. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_over/__init__.py +0 -0
  26. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_over/code.py +0 -0
  27. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_over/printer.py +0 -0
  28. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen/polars_over/template.py.j2 +0 -0
  29. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen.egg-info/SOURCES.txt +0 -0
  30. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen.egg-info/dependency_links.txt +0 -0
  31. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen.egg-info/requires.txt +0 -0
  32. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/expr_codegen.egg-info/top_level.txt +0 -0
  33. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/pyproject.toml +0 -0
  34. {expr_codegen-0.10.6 → expr_codegen-0.10.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.10.6
3
+ Version: 0.10.8
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -48,9 +48,7 @@ Requires-Dist: streamlit; extra == "streamlit"
48
48
  Requires-Dist: streamlit-ace; extra == "streamlit"
49
49
  Requires-Dist: more_itertools; extra == "streamlit"
50
50
 
51
- # expr_codegen 符号表达式代码生成器
52
-
53
- 表达式转代码工具
51
+ # expr_codegen 表达式转译器
54
52
 
55
53
  ## 项目背景
56
54
 
@@ -79,14 +77,9 @@ https://exprcodegen.streamlit.app
79
77
 
80
78
  ```python
81
79
  import sys
80
+ from io import StringIO
82
81
 
83
- # from polars_ta.prefix.talib import * # noqa
84
- from polars_ta.prefix.cdl import * # noqa
85
- from polars_ta.prefix.ta import * # noqa
86
- from polars_ta.prefix.tdx import * # noqa
87
- from polars_ta.prefix.wq import * # noqa
88
-
89
- from expr_codegen.tool import codegen_exec
82
+ from expr_codegen import codegen_exec
90
83
 
91
84
 
92
85
  def _code_block_1():
@@ -114,10 +107,15 @@ def _code_block_2():
114
107
  CPV = cs_zscore(_corr) + cs_zscore(_beta)
115
108
 
116
109
 
110
+ code = StringIO()
111
+
117
112
  df = None # 替换成真实的polars数据
118
113
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file=sys.stdout) # 打印代码
119
114
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file="output.py") # 保存到文件
120
115
  df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代码
116
+ df = codegen_exec(df, _code_block_1, _code_block_2, output_file=code) # 保存到字符串
117
+ code.seek(0)
118
+ code.read() # 读取代码
121
119
 
122
120
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect() # Lazy CPU
123
121
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu") # Lazy GPU
@@ -138,7 +136,7 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
138
136
  │ sympy_define.py # 符号定义,由于太多地方重复使用到,所以统一提取到此处
139
137
  ├─expr_codegen
140
138
  │ │ expr.py # 表达式处理基本函数
141
- │ │ tool.py # 核心工具代码。一般不需修改
139
+ │ │ tool.py # 核心工具代码
142
140
  │ ├─polars
143
141
  │ │ │ code.py # 针对polars语法的代码生成功能
144
142
  │ │ │ template.py.j2 # `Jinja2`模板。用于生成对应py文件,一般不需修改
@@ -185,13 +183,20 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
185
183
  1. 根据算子前缀分类(`get_current_by_prefix`),限制算子必需以`ts_`、`cs_`、`gp_`开头
186
184
  2. 根据算子全名分类(`get_current_by_name`), 不再限制算子名。比如`cs_rank`可以叫`rank`
187
185
 
188
- ## Null处理/停牌处理
186
+ ## Null处理
187
+
188
+ `null`是如何产生的?
189
+
190
+ 1. 停牌导致。在计算前就直接过滤掉了,不会对后续计算产生影响。
191
+ 2. 不同品种交易时段不同
192
+ 3. 计算产生。`null`在数列两端不影响后续时序算子结果,但中间出现`null`会影响。例如: `if_else(close<2, None, close)`
189
193
 
190
194
  https://github.com/pola-rs/polars/issues/12925#issuecomment-2552764629
195
+
191
196
  非常棒的点子,总结下来有两种实现方式:
192
197
 
193
- 1. 将`null`分成一组,`not_null`分成另一组。要计算两次
194
- 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只计算一次,略快一些
198
+ 1. 将`null`分成一组,`not_null`分成另一组。要调用两次
199
+ 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只调用一次,略快一些
195
200
 
196
201
  ```python
197
202
  X1 = (ts_returns(CLOSE, 3)).over(CLOSE.is_not_null(), _ASSET_, order_by=_DATE_),
@@ -199,17 +204,11 @@ X2 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=[CLOSE.is_not_null(), _DATE_]
199
204
  X3 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=_DATE_),
200
205
  ```
201
206
 
202
- 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入`null`区域可能有数据
207
+ 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入时`null`区域可能有数据
203
208
 
204
209
  1. `over_null='partition_by'`。分到两个区域
205
210
  2. `over_null='order_by'`。分到一个区域,`null`排在前面
206
- 3. `over_null=None`。不处理,直接计算,速度更快
207
-
208
- ## 二次开发
209
-
210
- 1. 备份后编辑`demo_express.py`, `import`需要引入的函数
211
- 2. 然后`printer.py`有可能需要添加对应函数的打印代码
212
- - 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
211
+ 3. `over_null=None`。不处理,直接调用,速度更快。如果确信不会中段产生`null`建议使用此参数
213
212
 
214
213
  ## `expr_codegen`局限性
215
214
 
@@ -1,6 +1,4 @@
1
- # expr_codegen 符号表达式代码生成器
2
-
3
- 表达式转代码工具
1
+ # expr_codegen 表达式转译器
4
2
 
5
3
  ## 项目背景
6
4
 
@@ -29,14 +27,9 @@ https://exprcodegen.streamlit.app
29
27
 
30
28
  ```python
31
29
  import sys
30
+ from io import StringIO
32
31
 
33
- # from polars_ta.prefix.talib import * # noqa
34
- from polars_ta.prefix.cdl import * # noqa
35
- from polars_ta.prefix.ta import * # noqa
36
- from polars_ta.prefix.tdx import * # noqa
37
- from polars_ta.prefix.wq import * # noqa
38
-
39
- from expr_codegen.tool import codegen_exec
32
+ from expr_codegen import codegen_exec
40
33
 
41
34
 
42
35
  def _code_block_1():
@@ -64,10 +57,15 @@ def _code_block_2():
64
57
  CPV = cs_zscore(_corr) + cs_zscore(_beta)
65
58
 
66
59
 
60
+ code = StringIO()
61
+
67
62
  df = None # 替换成真实的polars数据
68
63
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file=sys.stdout) # 打印代码
69
64
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file="output.py") # 保存到文件
70
65
  df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代码
66
+ df = codegen_exec(df, _code_block_1, _code_block_2, output_file=code) # 保存到字符串
67
+ code.seek(0)
68
+ code.read() # 读取代码
71
69
 
72
70
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect() # Lazy CPU
73
71
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu") # Lazy GPU
@@ -88,7 +86,7 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
88
86
  │ sympy_define.py # 符号定义,由于太多地方重复使用到,所以统一提取到此处
89
87
  ├─expr_codegen
90
88
  │ │ expr.py # 表达式处理基本函数
91
- │ │ tool.py # 核心工具代码。一般不需修改
89
+ │ │ tool.py # 核心工具代码
92
90
  │ ├─polars
93
91
  │ │ │ code.py # 针对polars语法的代码生成功能
94
92
  │ │ │ template.py.j2 # `Jinja2`模板。用于生成对应py文件,一般不需修改
@@ -135,13 +133,20 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
135
133
  1. 根据算子前缀分类(`get_current_by_prefix`),限制算子必需以`ts_`、`cs_`、`gp_`开头
136
134
  2. 根据算子全名分类(`get_current_by_name`), 不再限制算子名。比如`cs_rank`可以叫`rank`
137
135
 
138
- ## Null处理/停牌处理
136
+ ## Null处理
137
+
138
+ `null`是如何产生的?
139
+
140
+ 1. 停牌导致。在计算前就直接过滤掉了,不会对后续计算产生影响。
141
+ 2. 不同品种交易时段不同
142
+ 3. 计算产生。`null`在数列两端不影响后续时序算子结果,但中间出现`null`会影响。例如: `if_else(close<2, None, close)`
139
143
 
140
144
  https://github.com/pola-rs/polars/issues/12925#issuecomment-2552764629
145
+
141
146
  非常棒的点子,总结下来有两种实现方式:
142
147
 
143
- 1. 将`null`分成一组,`not_null`分成另一组。要计算两次
144
- 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只计算一次,略快一些
148
+ 1. 将`null`分成一组,`not_null`分成另一组。要调用两次
149
+ 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只调用一次,略快一些
145
150
 
146
151
  ```python
147
152
  X1 = (ts_returns(CLOSE, 3)).over(CLOSE.is_not_null(), _ASSET_, order_by=_DATE_),
@@ -149,17 +154,11 @@ X2 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=[CLOSE.is_not_null(), _DATE_]
149
154
  X3 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=_DATE_),
150
155
  ```
151
156
 
152
- 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入`null`区域可能有数据
157
+ 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入时`null`区域可能有数据
153
158
 
154
159
  1. `over_null='partition_by'`。分到两个区域
155
160
  2. `over_null='order_by'`。分到一个区域,`null`排在前面
156
- 3. `over_null=None`。不处理,直接计算,速度更快
157
-
158
- ## 二次开发
159
-
160
- 1. 备份后编辑`demo_express.py`, `import`需要引入的函数
161
- 2. 然后`printer.py`有可能需要添加对应函数的打印代码
162
- - 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
161
+ 3. `over_null=None`。不处理,直接调用,速度更快。如果确信不会中段产生`null`建议使用此参数
163
162
 
164
163
  ## `expr_codegen`局限性
165
164
 
@@ -0,0 +1 @@
1
+ __version__ = "0.10.8"
@@ -125,6 +125,7 @@ class RenameTransformer(ast.NodeTransformer):
125
125
  def __init__(self, funcs_map, targets_map, args_map=None):
126
126
 
127
127
  if args_map is None:
128
+ # 保留字
128
129
  args_map = {'True': "_TRUE_", 'False': "_FALSE_", 'None': "_NONE_"}
129
130
  self.funcs_old = set()
130
131
  self.args_old = set()
@@ -7,6 +7,8 @@ from sympy import symbols
7
7
  from expr_codegen.dag import zero_indegree, hierarchy_pos, remove_paths_by_zero_outdegree
8
8
  from expr_codegen.expr import CL, get_symbols, get_children, get_key, is_simple_expr
9
9
 
10
+ _RESERVED_WORD_ = {'_NONE_', '_TRUE_', '_FALSE_'}
11
+
10
12
 
11
13
  class ListDictList:
12
14
  """嵌套列表
@@ -109,8 +111,7 @@ class ListDictList:
109
111
  l2 = [set()]
110
112
  s = set()
111
113
  for i in reversed(l1):
112
- # 这三变量需要排除
113
- s = s | i - {'_NONE_', '_TRUE_', '_FALSE_'}
114
+ s = s | i # - {'_NONE_', '_TRUE_', '_FALSE_'}
114
115
  l2.append(s)
115
116
  l2 = list(reversed(l2))
116
117
 
@@ -396,6 +397,9 @@ def dag_end(G):
396
397
  key = G.nodes[node]['key']
397
398
  expr = G.nodes[node]['expr']
398
399
  symbols = G.nodes[node]['symbols']
400
+ # 这几个特殊的不算成字段名
401
+ symbols = list(set(symbols) - _RESERVED_WORD_)
402
+
399
403
  exprs_ldl.append(key, (node, expr, symbols))
400
404
 
401
405
  exprs_ldl._list = exprs_ldl.values()[1:]
@@ -1,8 +1,8 @@
1
1
  import inspect
2
2
  import pathlib
3
3
  from functools import lru_cache
4
- from io import TextIOWrapper
5
- from typing import Sequence, Dict, Union, TextIO, TypeVar, Optional, Literal
4
+ from io import TextIOBase
5
+ from typing import Sequence, Dict, Union, TypeVar, Optional, Literal
6
6
 
7
7
  from black import Mode, format_str
8
8
  from loguru import logger
@@ -12,7 +12,7 @@ from sympy.logic import boolalg
12
12
 
13
13
  from expr_codegen.codes import sources_to_exprs
14
14
  from expr_codegen.expr import get_current_by_prefix, get_children, replace_exprs
15
- from expr_codegen.model import dag_start, dag_end, dag_middle
15
+ from expr_codegen.model import dag_start, dag_end, dag_middle, _RESERVED_WORD_
16
16
 
17
17
  try:
18
18
  from pandas import DataFrame as _pd_DataFrame
@@ -232,6 +232,7 @@ class ExprTool:
232
232
 
233
233
  # 子表达式在前,原表式在最后
234
234
  exprs_dst, syms_dst = self.merge(date, asset, **exprs_src)
235
+ syms_dst = list(set(syms_dst) - _RESERVED_WORD_)
235
236
 
236
237
  # 提取公共表达式
237
238
  self.cse(exprs_dst, symbols_repl=numbered_symbols('_x_'), symbols_redu=exprs_src.keys())
@@ -286,7 +287,7 @@ class ExprTool:
286
287
  **kwargs)
287
288
 
288
289
  # 移回到cache,防止多次调用多次保存
289
- if isinstance(output_file, TextIOWrapper):
290
+ if isinstance(output_file, TextIOBase):
290
291
  # 输出到控制台
291
292
  output_file.write(code)
292
293
  elif output_file is not None:
@@ -305,6 +306,8 @@ def _exec_code(code: str, df_input):
305
306
 
306
307
 
307
308
  def _exec_file(file, df_input):
309
+ file = pathlib.Path(file)
310
+ logger.info(f'run file "{file.absolute()}"')
308
311
  with open(file, 'r', encoding='utf-8') as f:
309
312
  code = f.read()
310
313
  return _exec_code(code, df_input)
@@ -313,6 +316,7 @@ def _exec_file(file, df_input):
313
316
  def _exec_module(module: str, df_input):
314
317
  """"可下断点调试"""
315
318
  m = __import__(module, fromlist=['*'])
319
+ logger.info(f'run module {m}')
316
320
  return m.main(df_input)
317
321
 
318
322
 
@@ -322,13 +326,13 @@ _TOOL_ = ExprTool()
322
326
  def codegen_exec(df: Optional[DataFrame],
323
327
  *codes,
324
328
  extra_codes: str = r'CS_SW_L1 = r"^sw_l1_\d+$"',
325
- output_file: Union[str, TextIO, None] = None,
329
+ output_file: Union[str, TextIOBase, None] = None,
326
330
  run_file: Union[bool, str] = False,
327
331
  convert_xor: bool = False,
328
332
  style: Literal['pandas', 'polars_group', 'polars_over'] = 'polars_over',
329
333
  template_file: str = 'template.py.j2',
330
334
  date: str = 'date', asset: str = 'asset',
331
- over_null: Literal['order_by', 'partition_by', None] = 'partition_by',
335
+ over_null: Literal['partition_by', 'order_by', None] = 'partition_by',
332
336
  **kwargs) -> Optional[DataFrame]:
333
337
  """快速转换源代码并执行
334
338
 
@@ -340,7 +344,7 @@ def codegen_exec(df: Optional[DataFrame],
340
344
  函数体。此部分中的表达式会被翻译成目标代码
341
345
  extra_codes: str
342
346
  额外代码。不做处理,会被直接复制到目标代码中
343
- output_file: str
347
+ output_file: str| TextIOBase
344
348
  保存生成的目标代码到文件中
345
349
  run_file: bool or str
346
350
  是否不生成脚本,直接运行代码。
@@ -362,8 +366,8 @@ def codegen_exec(df: Optional[DataFrame],
362
366
  资产字段
363
367
  over_null: str
364
368
  时序中遇到null时的处理方式
365
- - order_by: 空值排同一分区的前排
366
369
  - partition_by: 空值划分到不同分区
370
+ - order_by: 空值排同一分区的前排
367
371
  - None: 不做处理
368
372
 
369
373
  Returns
@@ -374,17 +378,12 @@ def codegen_exec(df: Optional[DataFrame],
374
378
  if df is not None:
375
379
  if run_file is True:
376
380
  assert output_file is not None, 'output_file is required'
377
- output_file = pathlib.Path(output_file)
378
- logger.info(f'run file "{output_file.absolute()}"')
379
381
  return _exec_file(output_file, df)
380
382
  if run_file is not False:
381
383
  run_file = str(run_file)
382
384
  if run_file.endswith('.py'):
383
- run_file = pathlib.Path(run_file)
384
- logger.info(f'run file "{run_file.absolute()}"')
385
385
  return _exec_file(run_file, df)
386
386
  else:
387
- logger.info(f'run module "{run_file}"')
388
387
  return _exec_module(run_file, df) # 可断点调试
389
388
 
390
389
  # 此代码来自于sympy.var
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: expr_codegen
3
- Version: 0.10.6
3
+ Version: 0.10.8
4
4
  Summary: symbol expression to polars expression tool
5
5
  Author-email: wukan <wu-kan@163.com>
6
6
  License: BSD 3-Clause License
@@ -48,9 +48,7 @@ Requires-Dist: streamlit; extra == "streamlit"
48
48
  Requires-Dist: streamlit-ace; extra == "streamlit"
49
49
  Requires-Dist: more_itertools; extra == "streamlit"
50
50
 
51
- # expr_codegen 符号表达式代码生成器
52
-
53
- 表达式转代码工具
51
+ # expr_codegen 表达式转译器
54
52
 
55
53
  ## 项目背景
56
54
 
@@ -79,14 +77,9 @@ https://exprcodegen.streamlit.app
79
77
 
80
78
  ```python
81
79
  import sys
80
+ from io import StringIO
82
81
 
83
- # from polars_ta.prefix.talib import * # noqa
84
- from polars_ta.prefix.cdl import * # noqa
85
- from polars_ta.prefix.ta import * # noqa
86
- from polars_ta.prefix.tdx import * # noqa
87
- from polars_ta.prefix.wq import * # noqa
88
-
89
- from expr_codegen.tool import codegen_exec
82
+ from expr_codegen import codegen_exec
90
83
 
91
84
 
92
85
  def _code_block_1():
@@ -114,10 +107,15 @@ def _code_block_2():
114
107
  CPV = cs_zscore(_corr) + cs_zscore(_beta)
115
108
 
116
109
 
110
+ code = StringIO()
111
+
117
112
  df = None # 替换成真实的polars数据
118
113
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file=sys.stdout) # 打印代码
119
114
  df = codegen_exec(df, _code_block_1, _code_block_2, output_file="output.py") # 保存到文件
120
115
  df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代码
116
+ df = codegen_exec(df, _code_block_1, _code_block_2, output_file=code) # 保存到字符串
117
+ code.seek(0)
118
+ code.read() # 读取代码
121
119
 
122
120
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect() # Lazy CPU
123
121
  df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu") # Lazy GPU
@@ -138,7 +136,7 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
138
136
  │ sympy_define.py # 符号定义,由于太多地方重复使用到,所以统一提取到此处
139
137
  ├─expr_codegen
140
138
  │ │ expr.py # 表达式处理基本函数
141
- │ │ tool.py # 核心工具代码。一般不需修改
139
+ │ │ tool.py # 核心工具代码
142
140
  │ ├─polars
143
141
  │ │ │ code.py # 针对polars语法的代码生成功能
144
142
  │ │ │ template.py.j2 # `Jinja2`模板。用于生成对应py文件,一般不需修改
@@ -185,13 +183,20 @@ df = codegen_exec(df.lazy(), _code_block_1, _code_block_2).collect(engine="gpu")
185
183
  1. 根据算子前缀分类(`get_current_by_prefix`),限制算子必需以`ts_`、`cs_`、`gp_`开头
186
184
  2. 根据算子全名分类(`get_current_by_name`), 不再限制算子名。比如`cs_rank`可以叫`rank`
187
185
 
188
- ## Null处理/停牌处理
186
+ ## Null处理
187
+
188
+ `null`是如何产生的?
189
+
190
+ 1. 停牌导致。在计算前就直接过滤掉了,不会对后续计算产生影响。
191
+ 2. 不同品种交易时段不同
192
+ 3. 计算产生。`null`在数列两端不影响后续时序算子结果,但中间出现`null`会影响。例如: `if_else(close<2, None, close)`
189
193
 
190
194
  https://github.com/pola-rs/polars/issues/12925#issuecomment-2552764629
195
+
191
196
  非常棒的点子,总结下来有两种实现方式:
192
197
 
193
- 1. 将`null`分成一组,`not_null`分成另一组。要计算两次
194
- 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只计算一次,略快一些
198
+ 1. 将`null`分成一组,`not_null`分成另一组。要调用两次
199
+ 2. 仅一组,但复合排序,将`null`排在前面,`not_null`排后面。只调用一次,略快一些
195
200
 
196
201
  ```python
197
202
  X1 = (ts_returns(CLOSE, 3)).over(CLOSE.is_not_null(), _ASSET_, order_by=_DATE_),
@@ -199,17 +204,11 @@ X2 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=[CLOSE.is_not_null(), _DATE_]
199
204
  X3 = (ts_returns(CLOSE, 3)).over(_ASSET_, order_by=_DATE_),
200
205
  ```
201
206
 
202
- 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入`null`区域可能有数据
207
+ 第2种开头的`null`区域,是否影响结果由算子所决定,特别时是多列输入时`null`区域可能有数据
203
208
 
204
209
  1. `over_null='partition_by'`。分到两个区域
205
210
  2. `over_null='order_by'`。分到一个区域,`null`排在前面
206
- 3. `over_null=None`。不处理,直接计算,速度更快
207
-
208
- ## 二次开发
209
-
210
- 1. 备份后编辑`demo_express.py`, `import`需要引入的函数
211
- 2. 然后`printer.py`有可能需要添加对应函数的打印代码
212
- - 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
211
+ 3. `over_null=None`。不处理,直接调用,速度更快。如果确信不会中段产生`null`建议使用此参数
213
212
 
214
213
  ## `expr_codegen`局限性
215
214
 
@@ -1 +0,0 @@
1
- __version__ = "0.10.6"
File without changes
File without changes