expr-codegen 0.8.2__tar.gz → 0.8.4__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.
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/PKG-INFO +4 -9
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/README.md +3 -8
- expr_codegen-0.8.4/expr_codegen/_version.py +1 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/codes.py +13 -18
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/expr.py +4 -29
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/printer.py +3 -3
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/printer.py +3 -3
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/tool.py +22 -1
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/PKG-INFO +4 -9
- expr_codegen-0.8.2/expr_codegen/_version.py +0 -1
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/LICENSE +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/__init__.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/dag.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/latex/__init__.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/latex/printer.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/model.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/__init__.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/code.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/pandas/template.py.j2 +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/__init__.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/code.py +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen/polars/template.py.j2 +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/SOURCES.txt +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/dependency_links.txt +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/requires.txt +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/expr_codegen.egg-info/top_level.txt +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/pyproject.toml +0 -0
- {expr_codegen-0.8.2 → expr_codegen-0.8.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
@@ -197,17 +197,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
197
197
|
4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
|
|
198
198
|
|
|
199
199
|
## 小技巧
|
|
200
|
-
|
|
201
|
-
1. `sympy`不支持`==`,而是当成两个对象比较。例如:
|
|
202
|
-
1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
|
|
203
|
-
2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
|
|
204
|
-
|
|
205
|
-
2. `sympy`不支持`bool`转`int`。例如:
|
|
200
|
+
1. `sympy`不支持`bool`转`int`。例如:
|
|
206
201
|
1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
|
|
207
202
|
2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
|
|
208
|
-
|
|
203
|
+
2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
|
|
209
204
|
|
|
210
|
-
|
|
205
|
+
以上两种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
211
206
|
|
|
212
207
|
## 下划线开头的变量
|
|
213
208
|
1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
|
|
@@ -147,17 +147,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
147
147
|
4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
|
|
148
148
|
|
|
149
149
|
## 小技巧
|
|
150
|
-
|
|
151
|
-
1. `sympy`不支持`==`,而是当成两个对象比较。例如:
|
|
152
|
-
1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
|
|
153
|
-
2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
|
|
154
|
-
|
|
155
|
-
2. `sympy`不支持`bool`转`int`。例如:
|
|
150
|
+
1. `sympy`不支持`bool`转`int`。例如:
|
|
156
151
|
1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
|
|
157
152
|
2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
|
|
158
|
-
|
|
153
|
+
2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
|
|
159
154
|
|
|
160
|
-
|
|
155
|
+
以上两种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
161
156
|
|
|
162
157
|
## 下划线开头的变量
|
|
163
158
|
1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.4"
|
|
@@ -126,14 +126,7 @@ class SympyTransformer(ast.NodeTransformer):
|
|
|
126
126
|
node.comparators[i] = ast.Name(new_com_value, ctx=ast.Load())
|
|
127
127
|
self.args_new.add(new_com_value)
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
if isinstance(node.ops[0], ast.Eq):
|
|
131
|
-
# 等号会直接比较变成False
|
|
132
|
-
node = ast.Call(
|
|
133
|
-
func=ast.Name(id='Eq', ctx=ast.Load()),
|
|
134
|
-
args=[node.left, node.comparators[0]],
|
|
135
|
-
keywords=[],
|
|
136
|
-
)
|
|
129
|
+
assert len(node.comparators) == 1, f"不支持连续等号,请手工添加括号, {ast.unparse(node)}"
|
|
137
130
|
|
|
138
131
|
self.generic_visit(node)
|
|
139
132
|
return node
|
|
@@ -153,22 +146,22 @@ class SympyTransformer(ast.NodeTransformer):
|
|
|
153
146
|
|
|
154
147
|
def visit_BinOp(self, node):
|
|
155
148
|
# TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'
|
|
156
|
-
if isinstance(node.op, ast.Mult):
|
|
149
|
+
if isinstance(node.op, (ast.Mult, ast.Add, ast.Div, ast.Sub)):
|
|
157
150
|
# (OPEN < CLOSE) * -1
|
|
158
151
|
if isinstance(node.left, ast.Compare):
|
|
159
152
|
node.left = ast.Call(
|
|
160
|
-
func=ast.Name(id='
|
|
161
|
-
args=[node.left
|
|
153
|
+
func=ast.Name(id='int_', ctx=ast.Load()),
|
|
154
|
+
args=[node.left],
|
|
162
155
|
keywords=[],
|
|
163
156
|
)
|
|
164
157
|
# -1*(OPEN < CLOSE)
|
|
165
158
|
if isinstance(node.right, ast.Compare):
|
|
166
159
|
node.right = ast.Call(
|
|
167
|
-
func=ast.Name(id='
|
|
168
|
-
args=[node.right
|
|
160
|
+
func=ast.Name(id='int_', ctx=ast.Load()),
|
|
161
|
+
args=[node.right],
|
|
169
162
|
keywords=[],
|
|
170
163
|
)
|
|
171
|
-
#
|
|
164
|
+
# 这种情况,已经包含
|
|
172
165
|
# (OPEN < CLOSE)*(OPEN < CLOSE)
|
|
173
166
|
|
|
174
167
|
if isinstance(node.left, ast.Name):
|
|
@@ -238,8 +231,10 @@ def source_replace(source):
|
|
|
238
231
|
# 其实会导致?与:错配,但无所谓,只要多执行几次即可
|
|
239
232
|
source, num = re.subn(r'\?(.+?):(.+?)', r' if( \1 )else \2', source, flags=re.S)
|
|
240
233
|
# break
|
|
241
|
-
#
|
|
242
|
-
source = source.replace('
|
|
234
|
+
# 或、与
|
|
235
|
+
source = source.replace('||', '|').replace('&&', '&')
|
|
236
|
+
# 异或转成乘方
|
|
237
|
+
source = source.replace('^', '**')
|
|
243
238
|
return source
|
|
244
239
|
|
|
245
240
|
|
|
@@ -288,7 +283,7 @@ def _add_default_type(globals_):
|
|
|
288
283
|
return globals_
|
|
289
284
|
|
|
290
285
|
|
|
291
|
-
def sources_to_exprs(globals_, *sources
|
|
286
|
+
def sources_to_exprs(globals_, *sources):
|
|
292
287
|
"""将源代码转换成表达式"""
|
|
293
288
|
|
|
294
289
|
globals_ = _add_default_type(globals_)
|
|
@@ -297,5 +292,5 @@ def sources_to_exprs(globals_, *sources, safe: bool = True):
|
|
|
297
292
|
register_symbols(funcs_new, globals_, is_function=True)
|
|
298
293
|
register_symbols(args_new, globals_, is_function=False)
|
|
299
294
|
register_symbols(targets_new, globals_, is_function=False)
|
|
300
|
-
exprs_dict = dict_to_exprs(assigns, globals_
|
|
295
|
+
exprs_dict = dict_to_exprs(assigns, globals_)
|
|
301
296
|
return raw, exprs_dict
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import re
|
|
2
1
|
from functools import reduce
|
|
3
2
|
|
|
4
|
-
from sympy import Mul, preorder_traversal, symbols, Function, simplify, Add, Basic, Symbol
|
|
3
|
+
from sympy import Mul, preorder_traversal, symbols, Function, simplify, Add, Basic, Symbol, sympify
|
|
5
4
|
|
|
6
5
|
# 预定义前缀,算子用前缀进行区分更方便。
|
|
7
6
|
# 当然也可以用是否在某容器中进行分类
|
|
@@ -44,35 +43,11 @@ def register_symbols(syms, globals_, is_function: bool):
|
|
|
44
43
|
return globals_
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
def dict_to_exprs(exprs_src, globals_
|
|
48
|
-
exprs_src = {k:
|
|
46
|
+
def dict_to_exprs(exprs_src, globals_):
|
|
47
|
+
exprs_src = {k: sympify(v, globals_, evaluate=False) for k, v in exprs_src.items()}
|
|
49
48
|
return exprs_src
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
def safe_eval(string, globals_, k=None, safe: bool = True):
|
|
53
|
-
# print(string)
|
|
54
|
-
code = compile(string, '<user input>', 'eval')
|
|
55
|
-
if safe:
|
|
56
|
-
reason = None
|
|
57
|
-
banned = ('eval', 'compile', 'exec', 'getattr', 'hasattr', 'setattr', 'delattr',
|
|
58
|
-
'classmethod', 'globals', 'help', 'input', 'isinstance', 'issubclass', 'locals',
|
|
59
|
-
'open', 'print', 'property', 'staticmethod', 'vars')
|
|
60
|
-
for name in code.co_names:
|
|
61
|
-
if re.search(r'^__\S*__$', name):
|
|
62
|
-
reason = 'attributes not allowed'
|
|
63
|
-
elif name in banned:
|
|
64
|
-
reason = 'code execution not allowed'
|
|
65
|
-
if reason:
|
|
66
|
-
raise NameError(f'{name} not allowed : {reason}')
|
|
67
|
-
|
|
68
|
-
try:
|
|
69
|
-
return eval(code, globals_)
|
|
70
|
-
except:
|
|
71
|
-
print(k)
|
|
72
|
-
print(string)
|
|
73
|
-
raise
|
|
74
|
-
|
|
75
|
-
|
|
76
51
|
def append_node(node, output_exprs):
|
|
77
52
|
"""添加到队列。其中,-x将转为x
|
|
78
53
|
|
|
@@ -361,7 +336,7 @@ def _replace__repeat(e):
|
|
|
361
336
|
node_name = get_node_name(node)
|
|
362
337
|
node_args0_name = get_node_name(node.args[0])
|
|
363
338
|
if node_name == node_args0_name:
|
|
364
|
-
if
|
|
339
|
+
if node_name in ('cs_rank', 'sign', 'Abs', 'abs_'):
|
|
365
340
|
replacements.append((node, node.args[0]))
|
|
366
341
|
for node, replacement in replacements:
|
|
367
342
|
print(node, ' -> ', replacement)
|
|
@@ -59,15 +59,15 @@ class PandasStrPrinter(StrPrinter):
|
|
|
59
59
|
|
|
60
60
|
def _print_Or(self, expr):
|
|
61
61
|
PREC = PRECEDENCE["Mul"]
|
|
62
|
-
return "
|
|
62
|
+
return " | ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
63
63
|
|
|
64
64
|
def _print_Xor(self, expr):
|
|
65
65
|
PREC = PRECEDENCE["Mul"]
|
|
66
|
-
return "
|
|
66
|
+
return " ^ ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
67
67
|
|
|
68
68
|
def _print_And(self, expr):
|
|
69
69
|
PREC = PRECEDENCE["Mul"]
|
|
70
|
-
return "
|
|
70
|
+
return " & ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
71
71
|
|
|
72
72
|
def _print_Not(self, expr):
|
|
73
73
|
PREC = PRECEDENCE["Mul"]
|
|
@@ -57,15 +57,15 @@ class PolarsStrPrinter(StrPrinter):
|
|
|
57
57
|
|
|
58
58
|
def _print_Or(self, expr):
|
|
59
59
|
PREC = PRECEDENCE["Mul"]
|
|
60
|
-
return "
|
|
60
|
+
return " | ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
61
61
|
|
|
62
62
|
def _print_Xor(self, expr):
|
|
63
63
|
PREC = PRECEDENCE["Mul"]
|
|
64
|
-
return "
|
|
64
|
+
return " ^ ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
65
65
|
|
|
66
66
|
def _print_And(self, expr):
|
|
67
67
|
PREC = PRECEDENCE["Mul"]
|
|
68
|
-
return "
|
|
68
|
+
return " & ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
69
69
|
|
|
70
70
|
def _print_Not(self, expr):
|
|
71
71
|
PREC = PRECEDENCE["Mul"]
|
|
@@ -5,13 +5,34 @@ from typing import Sequence, Dict, Optional
|
|
|
5
5
|
|
|
6
6
|
from black import Mode, format_str
|
|
7
7
|
from sympy import simplify, cse, symbols, numbered_symbols
|
|
8
|
+
from sympy.core.expr import Expr
|
|
9
|
+
from sympy.logic import boolalg
|
|
8
10
|
|
|
9
11
|
from expr_codegen.codes import sources_to_exprs
|
|
10
12
|
from expr_codegen.expr import get_current_by_prefix, get_children, replace_exprs
|
|
11
13
|
from expr_codegen.model import dag_start, dag_end, dag_middle
|
|
12
14
|
|
|
15
|
+
# ===============================
|
|
16
|
+
# TypeError: expecting bool or Boolean, not `ts_delay(X, 3)`.
|
|
17
|
+
# ts_delay(X, 3) & ts_delay(Y, 3)
|
|
18
|
+
boolalg.as_Boolean = lambda x: x
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# AttributeError: 'StrictGreaterThan' object has no attribute 'diff'
|
|
22
|
+
# ts_count(open > 1, 2) == 2
|
|
23
|
+
def _diff(self, *symbols, **assumptions):
|
|
24
|
+
assumptions.setdefault("evaluate", False)
|
|
25
|
+
from sympy.core.function import _derivative_dispatch
|
|
26
|
+
return _derivative_dispatch(self, *symbols, **assumptions)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Expr.diff = _diff
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ===============================
|
|
13
33
|
|
|
14
34
|
def simplify2(expr):
|
|
35
|
+
# return simplify(expr)
|
|
15
36
|
try:
|
|
16
37
|
expr = simplify(expr)
|
|
17
38
|
except AttributeError as e:
|
|
@@ -238,7 +259,7 @@ class ExprTool:
|
|
|
238
259
|
style='polars', template_file='template.py.j2',
|
|
239
260
|
date='date', asset='asset') -> str:
|
|
240
261
|
"""通过字符串生成代码, 加了缓存,多次调用不重复生成"""
|
|
241
|
-
raw, exprs_dict = sources_to_exprs(self.globals_, source, *more_sources
|
|
262
|
+
raw, exprs_dict = sources_to_exprs(self.globals_, source, *more_sources)
|
|
242
263
|
|
|
243
264
|
# 生成代码
|
|
244
265
|
code, G = _TOOL_.all(exprs_dict, style=style, template_file=template_file,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
@@ -197,17 +197,12 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
197
197
|
4. 可以将`删除行`与`重采样`做为分割线,一大块代码分成多个`DAG`串联。复杂不易理解,所以最终没有实现
|
|
198
198
|
|
|
199
199
|
## 小技巧
|
|
200
|
-
|
|
201
|
-
1. `sympy`不支持`==`,而是当成两个对象比较。例如:
|
|
202
|
-
1. `if_else(OPEN==CLOSE, HIGH, LOW)`, 一开始就变成了`if_else(False, HIGH, LOW)`
|
|
203
|
-
2. 可以用`Eq`来代替,`if_else(Eq(OPEN, CLOSE), HIGH, LOW)`。具体示例请参考`Alpha101`中的`alpha_021`
|
|
204
|
-
|
|
205
|
-
2. `sympy`不支持`bool`转`int`。例如:
|
|
200
|
+
1. `sympy`不支持`bool`转`int`。例如:
|
|
206
201
|
1. `(OPEN < CLOSE) * -1`报错 `TypeError: unsupported operand type(s) for *: 'StrictLessThan' and 'int'`
|
|
207
202
|
2. 可以用`if_else`代替。`if_else(OPEN<CLOSE, 1, 0)*-1`。具体示例请参考`Alpha101`中的`alpha_064`
|
|
208
|
-
|
|
203
|
+
2. Python不支持`?:`三元表达式,只支持`if else`, 而在本项目中需要转成`if_else`
|
|
209
204
|
|
|
210
|
-
|
|
205
|
+
以上两种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
211
206
|
|
|
212
207
|
## 下划线开头的变量
|
|
213
208
|
1. 输出的数据,所有以`_`开头的列,最后会被自动删除。所以需要保留的变量一定不要以`_`开头
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|