expr-codegen 0.8.7__tar.gz → 0.9.1__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.7 → expr_codegen-0.9.1}/PKG-INFO +2 -1
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/README.md +1 -0
- expr_codegen-0.9.1/expr_codegen/_version.py +1 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/codes.py +52 -58
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/expr.py +17 -11
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/model.py +19 -9
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/pandas/code.py +2 -2
- {expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/code.py +3 -3
- {expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/printer.py +10 -5
- expr_codegen-0.9.1/expr_codegen/polars_over/__init__.py +0 -0
- expr_codegen-0.9.1/expr_codegen/polars_over/code.py +113 -0
- expr_codegen-0.9.1/expr_codegen/polars_over/printer.py +83 -0
- expr_codegen-0.9.1/expr_codegen/polars_over/template.py.j2 +79 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/tool.py +20 -17
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen.egg-info/PKG-INFO +2 -1
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen.egg-info/SOURCES.txt +8 -4
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/pyproject.toml +4 -2
- expr_codegen-0.8.7/expr_codegen/_version.py +0 -1
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/LICENSE +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/__init__.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/dag.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/latex/__init__.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/latex/printer.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/pandas/__init__.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/pandas/printer.py +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen/pandas/template.py.j2 +0 -0
- {expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/__init__.py +0 -0
- {expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/template.py.j2 +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen.egg-info/dependency_links.txt +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen.egg-info/requires.txt +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/expr_codegen.egg-info/top_level.txt +0 -0
- {expr_codegen-0.8.7 → expr_codegen-0.9.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
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
|
|
@@ -206,6 +206,7 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
206
206
|
6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
|
|
207
207
|
7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
|
|
208
208
|
8. 支持`~A`,底层会转换成`Not(A)`
|
|
209
|
+
9. `gp_`开头的函数都会返回对应的`cs_`函数。如`gp_func(A,B,C)`会替换成`cs_func(B,C)`,其中`A`用在了`groupby([date, A])`
|
|
209
210
|
|
|
210
211
|
## 下划线开头的变量
|
|
211
212
|
|
|
@@ -156,6 +156,7 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
156
156
|
6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
|
|
157
157
|
7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
|
|
158
158
|
8. 支持`~A`,底层会转换成`Not(A)`
|
|
159
|
+
9. `gp_`开头的函数都会返回对应的`cs_`函数。如`gp_func(A,B,C)`会替换成`cs_func(B,C)`,其中`A`用在了`groupby([date, A])`
|
|
159
160
|
|
|
160
161
|
## 下划线开头的变量
|
|
161
162
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.1"
|
|
@@ -10,8 +10,9 @@ from expr_codegen.expr import register_symbols, dict_to_exprs
|
|
|
10
10
|
class SyntaxTransformer(ast.NodeTransformer):
|
|
11
11
|
"""修改语法。注意:一定要修改语法后才能改名"""
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
def __init__(self, convert_xor):
|
|
14
|
+
# ^ 是异或还是乘方呢?
|
|
15
|
+
self.convert_xor = convert_xor
|
|
15
16
|
|
|
16
17
|
def visit_Compare(self, node):
|
|
17
18
|
assert len(node.comparators) == 1, f"不支持连续等号,请手工添加括号, {ast.unparse(node)}"
|
|
@@ -24,7 +25,7 @@ class SyntaxTransformer(ast.NodeTransformer):
|
|
|
24
25
|
# 只要body区域,出现了or True,就认为是特殊处理过的
|
|
25
26
|
if isinstance(node.body, ast.BoolOp) and isinstance(node.body.op, ast.Or):
|
|
26
27
|
if isinstance(node.body.values[-1], ast.Constant):
|
|
27
|
-
if node.body.values[-1].value
|
|
28
|
+
if node.body.values[-1].value:
|
|
28
29
|
node.test, node.body = node.body.values[0], node.test
|
|
29
30
|
|
|
30
31
|
node = ast.Call(
|
|
@@ -87,7 +88,9 @@ class SyntaxTransformer(ast.NodeTransformer):
|
|
|
87
88
|
return node
|
|
88
89
|
|
|
89
90
|
def visit_Subscript(self, node):
|
|
90
|
-
if node.slice.value == 0:
|
|
91
|
+
if isinstance(node.slice, ast.Constant) and node.slice.value == 0:
|
|
92
|
+
node = node.value
|
|
93
|
+
elif isinstance(node.slice, ast.UnaryOp) and isinstance(node.slice.operand, ast.Constant) and node.slice.operand.value == 0:
|
|
91
94
|
node = node.value
|
|
92
95
|
else:
|
|
93
96
|
node = ast.Call(
|
|
@@ -102,25 +105,22 @@ class SyntaxTransformer(ast.NodeTransformer):
|
|
|
102
105
|
class RenameTransformer(ast.NodeTransformer):
|
|
103
106
|
"""改名处理。改名前需要语法规范"""
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# 由于None等常量无法在sympy中正确处理,只能改成Symbol变量
|
|
117
|
-
# !!!一定要在drop_symbols时排除
|
|
118
|
-
args_map = {'True': "_TRUE_", 'False': "_FALSE_", 'None': "_NONE_"}
|
|
119
|
-
targets_map = {} # 只对非下划线开头的生效
|
|
120
|
-
|
|
121
|
-
def config_map(self, funcs_map, args_map, targets_map):
|
|
108
|
+
def __init__(self, funcs_map, targets_map, args_map=None):
|
|
109
|
+
|
|
110
|
+
if args_map is None:
|
|
111
|
+
args_map = {'True': "_TRUE_", 'False': "_FALSE_", 'None': "_NONE_"}
|
|
112
|
+
self.funcs_old = set()
|
|
113
|
+
self.args_old = set()
|
|
114
|
+
self.targets_old = set()
|
|
115
|
+
self.funcs_new = set()
|
|
116
|
+
self.args_new = set()
|
|
117
|
+
self.targets_new = set()
|
|
118
|
+
# 映射
|
|
122
119
|
self.funcs_map = funcs_map
|
|
120
|
+
# 由于None等常量无法在sympy中正确处理,只能改成Symbol变量
|
|
121
|
+
# !!!一定要在drop_symbols时排除
|
|
123
122
|
self.args_map = args_map
|
|
123
|
+
# 只对非下划线开头的生效
|
|
124
124
|
self.targets_map = targets_map
|
|
125
125
|
|
|
126
126
|
def visit_Call(self, node):
|
|
@@ -288,21 +288,6 @@ class RenameTransformer(ast.NodeTransformer):
|
|
|
288
288
|
return node
|
|
289
289
|
|
|
290
290
|
|
|
291
|
-
def sources_to_asts(*sources, convert_xor: bool):
|
|
292
|
-
"""输入多份源代码"""
|
|
293
|
-
raw = []
|
|
294
|
-
assigns = {}
|
|
295
|
-
funcs_new, args_new, targets_new = set(), set(), set()
|
|
296
|
-
for arg in sources:
|
|
297
|
-
r, a, funcs_, args_, targets_ = _source_to_asts(arg, convert_xor)
|
|
298
|
-
raw.append(r)
|
|
299
|
-
assigns.update(a)
|
|
300
|
-
funcs_new.update(funcs_)
|
|
301
|
-
args_new.update(args_)
|
|
302
|
-
targets_new.update(targets_)
|
|
303
|
-
return '\n'.join(raw), assigns, funcs_new, args_new, targets_new
|
|
304
|
-
|
|
305
|
-
|
|
306
291
|
def source_replace(source: str) -> str:
|
|
307
292
|
# 三元表达式转换成 错误版if( )else,一定得在Transformer中修正
|
|
308
293
|
num = 1
|
|
@@ -316,24 +301,43 @@ def source_replace(source: str) -> str:
|
|
|
316
301
|
return source
|
|
317
302
|
|
|
318
303
|
|
|
319
|
-
def
|
|
320
|
-
"""
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
304
|
+
def assigns_to_dict(assigns):
|
|
305
|
+
"""赋值表达式转成字典"""
|
|
306
|
+
return {ast.unparse(a.targets): ast.unparse(a.value) for a in assigns}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def raw_to_code(raw):
|
|
310
|
+
"""导入语句转字符列表"""
|
|
311
|
+
return '\n'.join([ast.unparse(a) for a in raw])
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def sources_to_asts(*sources, convert_xor: bool):
|
|
315
|
+
"""输入多份源代码"""
|
|
316
|
+
|
|
317
|
+
def _source_to_asts(source):
|
|
318
|
+
"""源代码"""
|
|
319
|
+
tree = ast.parse(source_replace(source))
|
|
320
|
+
|
|
321
|
+
if isinstance(tree.body[0], ast.FunctionDef):
|
|
322
|
+
body = tree.body[0].body
|
|
323
|
+
else:
|
|
324
|
+
body = tree.body
|
|
325
|
+
|
|
326
|
+
return body
|
|
327
|
+
|
|
328
|
+
tree = ast.parse("")
|
|
329
|
+
for arg in sources:
|
|
330
|
+
tree.body.extend(_source_to_asts(arg))
|
|
331
|
+
|
|
332
|
+
t1 = SyntaxTransformer(convert_xor)
|
|
324
333
|
t1.visit(tree)
|
|
325
|
-
t = RenameTransformer()
|
|
334
|
+
t = RenameTransformer({}, {})
|
|
326
335
|
t.visit(tree)
|
|
327
336
|
|
|
328
337
|
raw = []
|
|
329
338
|
assigns = []
|
|
330
339
|
|
|
331
|
-
|
|
332
|
-
body = tree.body[0].body
|
|
333
|
-
else:
|
|
334
|
-
body = tree.body
|
|
335
|
-
|
|
336
|
-
for node in body:
|
|
340
|
+
for node in tree.body:
|
|
337
341
|
# 特殊处理的节点
|
|
338
342
|
if isinstance(node, ast.Assign):
|
|
339
343
|
assigns.append(node)
|
|
@@ -345,16 +349,6 @@ def _source_to_asts(source, convert_xor: bool):
|
|
|
345
349
|
return raw_to_code(raw), assigns_to_dict(assigns), t.funcs_new, t.args_new, t.targets_new
|
|
346
350
|
|
|
347
351
|
|
|
348
|
-
def assigns_to_dict(assigns):
|
|
349
|
-
"""赋值表达式转成字典"""
|
|
350
|
-
return {ast.unparse(a.targets): ast.unparse(a.value) for a in assigns}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
def raw_to_code(raw):
|
|
354
|
-
"""导入语句转字符列表"""
|
|
355
|
-
return '\n'.join([ast.unparse(a) for a in raw])
|
|
356
|
-
|
|
357
|
-
|
|
358
352
|
def _add_default_type(globals_):
|
|
359
353
|
# 这种写法可以省去由用户导入Eq一类的工作
|
|
360
354
|
globals_['Add'] = Add
|
|
@@ -116,7 +116,7 @@ def is_simple_expr(expr):
|
|
|
116
116
|
return False
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def get_current_by_prefix(expr, **kwargs):
|
|
119
|
+
def get_current_by_prefix(expr, date, asset, **kwargs):
|
|
120
120
|
"""表达式根节点信息。按名称前缀。例如
|
|
121
121
|
|
|
122
122
|
OPEN取的是OPEN,得cl
|
|
@@ -130,16 +130,16 @@ def get_current_by_prefix(expr, **kwargs):
|
|
|
130
130
|
prefix2 = expr.name[:2]
|
|
131
131
|
|
|
132
132
|
if prefix2 == TS:
|
|
133
|
-
return TS,
|
|
133
|
+
return TS, asset
|
|
134
134
|
if prefix2 == CS:
|
|
135
|
-
return CS,
|
|
135
|
+
return CS, date
|
|
136
136
|
if prefix2 == GP:
|
|
137
|
-
return GP,
|
|
137
|
+
return GP, date, expr.args[0].name
|
|
138
138
|
# 不需分组
|
|
139
139
|
return CL_TUP
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def get_current_by_name(expr, ts_names, cs_names, gp_names, **kwargs):
|
|
142
|
+
def get_current_by_name(expr, ts_names, cs_names, gp_names, date, asset, **kwargs):
|
|
143
143
|
"""表达式根节点信息。按名称。
|
|
144
144
|
|
|
145
145
|
Parameters
|
|
@@ -151,17 +151,21 @@ def get_current_by_name(expr, ts_names, cs_names, gp_names, **kwargs):
|
|
|
151
151
|
横截面算子名称字符串集合
|
|
152
152
|
gp_names
|
|
153
153
|
分组算子名称字符串集合
|
|
154
|
+
date
|
|
155
|
+
日期字符串
|
|
156
|
+
asset
|
|
157
|
+
资产字符串
|
|
154
158
|
kwargs
|
|
155
159
|
|
|
156
160
|
"""
|
|
157
161
|
if expr.is_Function:
|
|
158
162
|
if hasattr(expr, 'name'): # Or 没有名字
|
|
159
163
|
if expr.name in ts_names:
|
|
160
|
-
return TS,
|
|
164
|
+
return TS, asset
|
|
161
165
|
if expr.name in cs_names:
|
|
162
|
-
return CS,
|
|
166
|
+
return CS, date
|
|
163
167
|
if expr.name in gp_names:
|
|
164
|
-
return GP,
|
|
168
|
+
return GP, date, expr.args[0].name
|
|
165
169
|
|
|
166
170
|
# 不需分组
|
|
167
171
|
return CL_TUP
|
|
@@ -171,7 +175,7 @@ def get_current_by_name(expr, ts_names, cs_names, gp_names, **kwargs):
|
|
|
171
175
|
# __level__ = 0
|
|
172
176
|
|
|
173
177
|
|
|
174
|
-
def get_children(func, func_kwargs, expr, output_exprs, output_symbols):
|
|
178
|
+
def get_children(func, func_kwargs, expr, output_exprs, output_symbols, date, asset):
|
|
175
179
|
"""表达式整体信息。例如
|
|
176
180
|
|
|
177
181
|
-ts_corr返回{ts}而不是 {cl}
|
|
@@ -190,6 +194,8 @@ def get_children(func, func_kwargs, expr, output_exprs, output_symbols):
|
|
|
190
194
|
输出分割后的了表达式
|
|
191
195
|
output_symbols
|
|
192
196
|
输出每个子表达式中的符号
|
|
197
|
+
date
|
|
198
|
+
asset
|
|
193
199
|
|
|
194
200
|
Returns
|
|
195
201
|
-------
|
|
@@ -199,8 +205,8 @@ def get_children(func, func_kwargs, expr, output_exprs, output_symbols):
|
|
|
199
205
|
# __level__ += 1
|
|
200
206
|
|
|
201
207
|
try:
|
|
202
|
-
curr = func(expr, **func_kwargs)
|
|
203
|
-
children = [get_children(func, func_kwargs, a, output_exprs, output_symbols) for a in expr.args]
|
|
208
|
+
curr = func(expr, date, asset, **func_kwargs)
|
|
209
|
+
children = [get_children(func, func_kwargs, a, output_exprs, output_symbols, date, asset) for a in expr.args]
|
|
204
210
|
|
|
205
211
|
# print(expr, curr, children, __level__)
|
|
206
212
|
# if __level__ == 6:
|
|
@@ -206,14 +206,14 @@ def create_dag_exprs(exprs):
|
|
|
206
206
|
return G
|
|
207
207
|
|
|
208
208
|
|
|
209
|
-
def init_dag_exprs(G, func, func_kwargs):
|
|
209
|
+
def init_dag_exprs(G, func, func_kwargs, date, asset):
|
|
210
210
|
"""使用表达式信息初始化DAG"""
|
|
211
211
|
for i, generation in enumerate(nx.topological_generations(G)):
|
|
212
212
|
# print(i, generation)
|
|
213
213
|
for node in generation:
|
|
214
214
|
expr = G.nodes[node]['expr']
|
|
215
215
|
syms = []
|
|
216
|
-
children = get_children(func, func_kwargs, expr, [], syms)
|
|
216
|
+
children = get_children(func, func_kwargs, expr, [], syms, date, asset)
|
|
217
217
|
G.nodes[node]['children'] = children
|
|
218
218
|
G.nodes[node]['key'] = get_key(children)
|
|
219
219
|
G.nodes[node]['symbols'] = [str(s) for s in syms]
|
|
@@ -248,7 +248,11 @@ def merge_nodes_1(G: nx.DiGraph, keep_nodes, *args):
|
|
|
248
248
|
succ = G.succ[node]
|
|
249
249
|
# 下游只有一个,直接替换。
|
|
250
250
|
if len(succ) == 1:
|
|
251
|
-
|
|
251
|
+
for s in succ:
|
|
252
|
+
# if_else(_A>_B,_A,_B)会出现量次,不能删
|
|
253
|
+
if G.nodes[s]['symbols'].count(node) > 1:
|
|
254
|
+
continue
|
|
255
|
+
skip_expr_node(G, node, keep_nodes)
|
|
252
256
|
else:
|
|
253
257
|
# 复制一次,防止修改后报错
|
|
254
258
|
for p in pred.copy():
|
|
@@ -263,7 +267,10 @@ def merge_nodes_1(G: nx.DiGraph, keep_nodes, *args):
|
|
|
263
267
|
succ = G.succ[p]
|
|
264
268
|
# 下游只有一个,直接替换。
|
|
265
269
|
if len(succ) == 1:
|
|
266
|
-
|
|
270
|
+
for s in succ:
|
|
271
|
+
if G.nodes[s]['symbols'].count(p) > 1:
|
|
272
|
+
continue
|
|
273
|
+
skip_expr_node(G, p, keep_nodes)
|
|
267
274
|
next_pred.extend(pred)
|
|
268
275
|
# 更新下一次循环
|
|
269
276
|
this_pred = list(set(next_pred))
|
|
@@ -288,7 +295,10 @@ def merge_nodes_2(G: nx.DiGraph, keep_nodes, *args):
|
|
|
288
295
|
if len(succ) > 1:
|
|
289
296
|
# 上游节点只有一个下游,当前就是自己了
|
|
290
297
|
continue
|
|
291
|
-
|
|
298
|
+
for s in succ:
|
|
299
|
+
if G.nodes[s]['symbols'].count(p) > 1:
|
|
300
|
+
continue
|
|
301
|
+
skip_expr_node(G, p, keep_nodes)
|
|
292
302
|
# 只做根节点,所以没有下一次了
|
|
293
303
|
# next_pred.extend(pred)
|
|
294
304
|
# 更新下一次循环
|
|
@@ -352,16 +362,16 @@ def skip_expr_node(G: nx.DiGraph, node, keep_nodes):
|
|
|
352
362
|
return G
|
|
353
363
|
|
|
354
364
|
|
|
355
|
-
def dag_start(exprs_dict, func, func_kwargs):
|
|
365
|
+
def dag_start(exprs_dict, func, func_kwargs, date, asset):
|
|
356
366
|
"""初始生成DAG"""
|
|
357
367
|
G = create_dag_exprs(exprs_dict)
|
|
358
|
-
G = init_dag_exprs(G, func, func_kwargs)
|
|
368
|
+
G = init_dag_exprs(G, func, func_kwargs, date, asset)
|
|
359
369
|
|
|
360
370
|
# 分层输出
|
|
361
371
|
return G
|
|
362
372
|
|
|
363
373
|
|
|
364
|
-
def dag_middle(G, exprs_names, func, func_kwargs):
|
|
374
|
+
def dag_middle(G, exprs_names, func, func_kwargs, date, asset):
|
|
365
375
|
"""删除几个没有必要的节点"""
|
|
366
376
|
G = remove_paths_by_zero_outdegree(G, exprs_names)
|
|
367
377
|
# 以下划线开头的节点,不保留
|
|
@@ -370,7 +380,7 @@ def dag_middle(G, exprs_names, func, func_kwargs):
|
|
|
370
380
|
G = merge_nodes_2(G, keep_nodes, *keep_nodes)
|
|
371
381
|
|
|
372
382
|
# 由于表达式修改,需再次更新表达式
|
|
373
|
-
G = init_dag_exprs(G, func, func_kwargs)
|
|
383
|
+
G = init_dag_exprs(G, func, func_kwargs, date, asset)
|
|
374
384
|
|
|
375
385
|
# 分层输出
|
|
376
386
|
return G
|
|
@@ -30,8 +30,8 @@ def get_groupby_from_tuple(tup, func_name, drop_cols):
|
|
|
30
30
|
def symbols_to_code(syms, alias):
|
|
31
31
|
a = [f"{s}" for s in syms]
|
|
32
32
|
b = [f"'{alias.get(s, s)}'" for s in syms]
|
|
33
|
-
return f"""_ =
|
|
34
|
-
|
|
33
|
+
return f"""_ = [{','.join(b)}]
|
|
34
|
+
[{','.join(a)}] = _"""
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def codegen(exprs_ldl: ListDictList, exprs_src, syms_dst,
|
{expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/code.py
RENAMED
|
@@ -6,7 +6,7 @@ from jinja2 import FileSystemLoader, TemplateNotFound
|
|
|
6
6
|
|
|
7
7
|
from expr_codegen.expr import TS, CS, GP
|
|
8
8
|
from expr_codegen.model import ListDictList
|
|
9
|
-
from expr_codegen.
|
|
9
|
+
from expr_codegen.polars_group.printer import PolarsStrPrinter
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def get_groupby_from_tuple(tup, func_name, drop_cols):
|
|
@@ -31,8 +31,8 @@ def symbols_to_code(syms, alias):
|
|
|
31
31
|
a = [f"{s}" for s in syms]
|
|
32
32
|
b = [f"r'{alias.get(s, s)}'" for s in syms] #
|
|
33
33
|
b = [f"'{alias.get(s, s)}'" for s in syms]
|
|
34
|
-
return f"""_ =
|
|
35
|
-
|
|
34
|
+
return f"""_ = [{','.join(b)}]
|
|
35
|
+
[{','.join(a)}] = [pl.col(i) for i in _]"""
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def codegen(exprs_ldl: ListDictList, exprs_src, syms_dst,
|
{expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/printer.py
RENAMED
|
@@ -40,6 +40,11 @@ class PolarsStrPrinter(StrPrinter):
|
|
|
40
40
|
c.__name__.endswith("Base")) + classes[i:]
|
|
41
41
|
for cls in classes:
|
|
42
42
|
printmethodname = '_print_' + cls.__name__
|
|
43
|
+
|
|
44
|
+
# 所有以gp_开头的函数都转换成cs_开头
|
|
45
|
+
if printmethodname.startswith('_print_gp_'):
|
|
46
|
+
printmethodname = "_print_gp_"
|
|
47
|
+
|
|
43
48
|
printmethod = getattr(self, printmethodname, None)
|
|
44
49
|
if printmethod is not None:
|
|
45
50
|
return printmethod(expr, **kwargs)
|
|
@@ -71,8 +76,8 @@ class PolarsStrPrinter(StrPrinter):
|
|
|
71
76
|
PREC = PRECEDENCE["Mul"]
|
|
72
77
|
return "~%s" % self.parenthesize(expr.args[0], PREC)
|
|
73
78
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return "
|
|
79
|
+
def _print_gp_(self, expr):
|
|
80
|
+
"""gp_函数都转换成cs_函数,但要丢弃第一个参数"""
|
|
81
|
+
new_args = [self._print(arg) for arg in expr.args[1:]]
|
|
82
|
+
func_name = expr.func.__name__[3:]
|
|
83
|
+
return "cs_%s(%s)" % (func_name, ",".join(new_args))
|
|
File without changes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Sequence, Dict
|
|
3
|
+
|
|
4
|
+
import jinja2
|
|
5
|
+
from jinja2 import FileSystemLoader, TemplateNotFound
|
|
6
|
+
|
|
7
|
+
from expr_codegen.expr import TS, CS, GP
|
|
8
|
+
from expr_codegen.model import ListDictList
|
|
9
|
+
from expr_codegen.polars_over.printer import PolarsStrPrinter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_groupby_from_tuple(tup, func_name, drop_cols):
|
|
13
|
+
"""从传入的元组中生成分组运行代码"""
|
|
14
|
+
prefix2, *_ = tup
|
|
15
|
+
|
|
16
|
+
if prefix2 == TS:
|
|
17
|
+
# 组内需要按时间进行排序,需要维持顺序
|
|
18
|
+
prefix2, asset = tup
|
|
19
|
+
return f'df = {func_name}(df.sort(_ASSET_, _DATE_)).drop(*{drop_cols})'
|
|
20
|
+
if prefix2 == CS:
|
|
21
|
+
prefix2, date = tup
|
|
22
|
+
return f'df = {func_name}(df.sort(_DATE_)).drop(*{drop_cols})'
|
|
23
|
+
if prefix2 == GP:
|
|
24
|
+
prefix2, date, group = tup
|
|
25
|
+
return f'df = {func_name}(df.sort(_DATE_, "{group}")).drop(*{drop_cols})'
|
|
26
|
+
|
|
27
|
+
return f'df = {func_name}(df).drop(*{drop_cols})'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def symbols_to_code(syms, alias):
|
|
31
|
+
a = [f"{s}" for s in syms]
|
|
32
|
+
b = [f"r'{alias.get(s, s)}'" for s in syms] #
|
|
33
|
+
b = [f"'{alias.get(s, s)}'" for s in syms]
|
|
34
|
+
return f"""_ = [{','.join(b)}]
|
|
35
|
+
[{','.join(a)}] = [pl.col(i) for i in _]"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def codegen(exprs_ldl: ListDictList, exprs_src, syms_dst,
|
|
39
|
+
filename='template.py.j2',
|
|
40
|
+
date='date', asset='asset',
|
|
41
|
+
alias: Dict[str, str] = {},
|
|
42
|
+
extra_codes: Sequence[str] = ()):
|
|
43
|
+
"""基于模板的代码生成"""
|
|
44
|
+
# 打印Polars风格代码
|
|
45
|
+
p = PolarsStrPrinter()
|
|
46
|
+
|
|
47
|
+
# polars风格代码
|
|
48
|
+
funcs = {}
|
|
49
|
+
# 分组应用代码。这里利用了字典按插入顺序排序的特点,将排序放在最前
|
|
50
|
+
groupbys = {'sort': ''}
|
|
51
|
+
# 处理过后的表达式
|
|
52
|
+
exprs_dst = []
|
|
53
|
+
syms_out = []
|
|
54
|
+
|
|
55
|
+
drop_symbols = exprs_ldl.drop_symbols()
|
|
56
|
+
j = -1
|
|
57
|
+
for i, row in enumerate(exprs_ldl.values()):
|
|
58
|
+
for k, vv in row.items():
|
|
59
|
+
j += 1
|
|
60
|
+
if len(vv) == 0:
|
|
61
|
+
continue
|
|
62
|
+
# 函数名
|
|
63
|
+
func_name = f'func_{i}_{"__".join(k)}'
|
|
64
|
+
func_code = []
|
|
65
|
+
for kv in vv:
|
|
66
|
+
if kv is None:
|
|
67
|
+
func_code.append(f" )")
|
|
68
|
+
func_code.append(f"# " + '=' * 40)
|
|
69
|
+
func_code.append(f" df = df.with_columns(")
|
|
70
|
+
exprs_dst.append(f"#" + '=' * 40 + func_name)
|
|
71
|
+
else:
|
|
72
|
+
va, ex, sym = kv
|
|
73
|
+
s1 = str(ex)
|
|
74
|
+
s2 = p.doprint(ex)
|
|
75
|
+
if s1 != s2:
|
|
76
|
+
# 不想等,打印注释,显示会更直观察
|
|
77
|
+
func_code.append(f"# {va} = {s1}")
|
|
78
|
+
if k[0] == TS:
|
|
79
|
+
func_code.append(f"{va}=({s2}).over(_ASSET_, order_by=_DATE_),")
|
|
80
|
+
elif k[0] == CS:
|
|
81
|
+
func_code.append(f"{va}=({s2}).over(_DATE_),")
|
|
82
|
+
elif k[0] == GP:
|
|
83
|
+
func_code.append(f"{va}=({s2}).over(_DATE_, '{k[2]}'),")
|
|
84
|
+
else:
|
|
85
|
+
func_code.append(f"{va}={s2},")
|
|
86
|
+
exprs_dst.append(f"{va} = {s1}")
|
|
87
|
+
if va not in syms_dst:
|
|
88
|
+
syms_out.append(va)
|
|
89
|
+
func_code.append(f" )")
|
|
90
|
+
func_code = func_code[1:]
|
|
91
|
+
|
|
92
|
+
# polars风格代码列表
|
|
93
|
+
funcs[func_name] = '\n'.join(func_code)
|
|
94
|
+
# 只有下划线开头再删除
|
|
95
|
+
ds = [x for x in drop_symbols[j] if x.startswith('_')]
|
|
96
|
+
# 分组应用代码
|
|
97
|
+
groupbys[func_name] = get_groupby_from_tuple(k, func_name, ds)
|
|
98
|
+
|
|
99
|
+
syms1 = symbols_to_code(syms_dst, alias)
|
|
100
|
+
syms2 = symbols_to_code(syms_out, alias)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
env = jinja2.Environment(loader=FileSystemLoader(os.path.dirname(__file__)))
|
|
104
|
+
template = env.get_template(filename)
|
|
105
|
+
except TemplateNotFound:
|
|
106
|
+
env = jinja2.Environment(loader=FileSystemLoader(os.path.dirname(filename)))
|
|
107
|
+
template = env.get_template(os.path.basename(filename))
|
|
108
|
+
|
|
109
|
+
return template.render(funcs=funcs, groupbys=groupbys,
|
|
110
|
+
exprs_src=exprs_src, exprs_dst=exprs_dst,
|
|
111
|
+
syms1=syms1, syms2=syms2,
|
|
112
|
+
date=date, asset=asset,
|
|
113
|
+
extra_codes=extra_codes)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from sympy import Basic, Function, StrPrinter
|
|
2
|
+
from sympy.printing.precedence import precedence, PRECEDENCE
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# TODO: 如有新添加函数,但表达式有变更才需要在此补充对应的打印代码,否则可以省略
|
|
6
|
+
|
|
7
|
+
class PolarsStrPrinter(StrPrinter):
|
|
8
|
+
def _print(self, expr, **kwargs) -> str:
|
|
9
|
+
"""Internal dispatcher
|
|
10
|
+
|
|
11
|
+
Tries the following concepts to print an expression:
|
|
12
|
+
1. Let the object print itself if it knows how.
|
|
13
|
+
2. Take the best fitting method defined in the printer.
|
|
14
|
+
3. As fall-back use the emptyPrinter method for the printer.
|
|
15
|
+
"""
|
|
16
|
+
self._print_level += 1
|
|
17
|
+
try:
|
|
18
|
+
# If the printer defines a name for a printing method
|
|
19
|
+
# (Printer.printmethod) and the object knows for itself how it
|
|
20
|
+
# should be printed, use that method.
|
|
21
|
+
if self.printmethod and hasattr(expr, self.printmethod):
|
|
22
|
+
if not (isinstance(expr, type) and issubclass(expr, Basic)):
|
|
23
|
+
return getattr(expr, self.printmethod)(self, **kwargs)
|
|
24
|
+
|
|
25
|
+
# See if the class of expr is known, or if one of its super
|
|
26
|
+
# classes is known, and use that print function
|
|
27
|
+
# Exception: ignore the subclasses of Undefined, so that, e.g.,
|
|
28
|
+
# Function('gamma') does not get dispatched to _print_gamma
|
|
29
|
+
classes = type(expr).__mro__
|
|
30
|
+
# if AppliedUndef in classes:
|
|
31
|
+
# classes = classes[classes.index(AppliedUndef):]
|
|
32
|
+
# if UndefinedFunction in classes:
|
|
33
|
+
# classes = classes[classes.index(UndefinedFunction):]
|
|
34
|
+
# Another exception: if someone subclasses a known function, e.g.,
|
|
35
|
+
# gamma, and changes the name, then ignore _print_gamma
|
|
36
|
+
if Function in classes:
|
|
37
|
+
i = classes.index(Function)
|
|
38
|
+
classes = tuple(c for c in classes[:i] if \
|
|
39
|
+
c.__name__ == classes[0].__name__ or \
|
|
40
|
+
c.__name__.endswith("Base")) + classes[i:]
|
|
41
|
+
for cls in classes:
|
|
42
|
+
printmethodname = '_print_' + cls.__name__
|
|
43
|
+
|
|
44
|
+
# 所有以gp_开头的函数都转换成cs_开头
|
|
45
|
+
if printmethodname.startswith('_print_gp_'):
|
|
46
|
+
printmethodname = "_print_gp_"
|
|
47
|
+
|
|
48
|
+
printmethod = getattr(self, printmethodname, None)
|
|
49
|
+
if printmethod is not None:
|
|
50
|
+
return printmethod(expr, **kwargs)
|
|
51
|
+
# Unknown object, fall back to the emptyPrinter.
|
|
52
|
+
return self.emptyPrinter(expr)
|
|
53
|
+
finally:
|
|
54
|
+
self._print_level -= 1
|
|
55
|
+
|
|
56
|
+
def _print_Symbol(self, expr):
|
|
57
|
+
return expr.name
|
|
58
|
+
|
|
59
|
+
def _print_Equality(self, expr):
|
|
60
|
+
PREC = precedence(expr)
|
|
61
|
+
return "%s==%s" % (self.parenthesize(expr.args[0], PREC), self.parenthesize(expr.args[1], PREC))
|
|
62
|
+
|
|
63
|
+
def _print_Or(self, expr):
|
|
64
|
+
PREC = PRECEDENCE["Mul"]
|
|
65
|
+
return " | ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
66
|
+
|
|
67
|
+
def _print_Xor(self, expr):
|
|
68
|
+
PREC = PRECEDENCE["Mul"]
|
|
69
|
+
return " ^ ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
70
|
+
|
|
71
|
+
def _print_And(self, expr):
|
|
72
|
+
PREC = PRECEDENCE["Mul"]
|
|
73
|
+
return " & ".join(self.parenthesize(arg, PREC) for arg in expr.args)
|
|
74
|
+
|
|
75
|
+
def _print_Not(self, expr):
|
|
76
|
+
PREC = PRECEDENCE["Mul"]
|
|
77
|
+
return "~%s" % self.parenthesize(expr.args[0], PREC)
|
|
78
|
+
|
|
79
|
+
def _print_gp_(self, expr):
|
|
80
|
+
"""gp_函数都转换成cs_函数,但要丢弃第一个参数"""
|
|
81
|
+
new_args = [self._print(arg) for arg in expr.args[1:]]
|
|
82
|
+
func_name = expr.func.__name__[3:]
|
|
83
|
+
return "cs_%s(%s)" % (func_name, ",".join(new_args))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# this code is auto generated by the expr_codegen
|
|
2
|
+
# https://github.com/wukan1986/expr_codegen
|
|
3
|
+
# 此段代码由 expr_codegen 自动生成,欢迎提交 issue 或 pull request
|
|
4
|
+
|
|
5
|
+
import numpy as np # noqa
|
|
6
|
+
import pandas as pd # noqa
|
|
7
|
+
import polars as pl # noqa
|
|
8
|
+
import polars.selectors as cs # noqa
|
|
9
|
+
from loguru import logger # noqa
|
|
10
|
+
|
|
11
|
+
# ===================================
|
|
12
|
+
# 导入优先级,例如:ts_RSI在ta与talib中都出现了,优先使用ta
|
|
13
|
+
# 运行时,后导入覆盖前导入,但IDE智能提示是显示先导入的
|
|
14
|
+
_ = 0 # 只要之前出现了语句,之后的import位置不参与调整
|
|
15
|
+
# from polars_ta.prefix.talib import * # noqa
|
|
16
|
+
from polars_ta.prefix.tdx import * # noqa
|
|
17
|
+
from polars_ta.prefix.ta import * # noqa
|
|
18
|
+
from polars_ta.prefix.wq import * # noqa
|
|
19
|
+
from polars_ta.prefix.cdl import * # noqa
|
|
20
|
+
|
|
21
|
+
# ===================================
|
|
22
|
+
|
|
23
|
+
{{ syms1 }}
|
|
24
|
+
|
|
25
|
+
{{ syms2 }}
|
|
26
|
+
|
|
27
|
+
_DATE_ = '{{ date }}'
|
|
28
|
+
_ASSET_ = '{{ asset }}'
|
|
29
|
+
_NONE_ = None
|
|
30
|
+
_TRUE_ = True
|
|
31
|
+
_FALSE_ = False
|
|
32
|
+
|
|
33
|
+
{%-for row in extra_codes %}
|
|
34
|
+
{{ row-}}
|
|
35
|
+
{% endfor %}
|
|
36
|
+
|
|
37
|
+
{% for key, value in funcs.items() %}
|
|
38
|
+
def {{ key }}(df: pl.DataFrame) -> pl.DataFrame:
|
|
39
|
+
{{ value }}
|
|
40
|
+
return df
|
|
41
|
+
{% endfor %}
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
{%-for row in exprs_dst %}
|
|
45
|
+
{{ row-}}
|
|
46
|
+
{% endfor %}
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
{%-for key, value in exprs_src.items() %}
|
|
51
|
+
{{ key }} = {{ value-}}
|
|
52
|
+
{% endfor %}
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main(df: pl.DataFrame) -> pl.DataFrame:
|
|
57
|
+
# logger.info("start...")
|
|
58
|
+
{% for key, value in groupbys.items() %}
|
|
59
|
+
{{ value-}}
|
|
60
|
+
{% endfor %}
|
|
61
|
+
|
|
62
|
+
# drop intermediate columns
|
|
63
|
+
# df = df.select(pl.exclude(r'^_x_\d+$'))
|
|
64
|
+
df = df.select(~cs.starts_with("_"))
|
|
65
|
+
|
|
66
|
+
# shrink
|
|
67
|
+
df = df.select(cs.all().shrink_dtype())
|
|
68
|
+
df = df.shrink_to_fit()
|
|
69
|
+
|
|
70
|
+
# logger.info('done')
|
|
71
|
+
|
|
72
|
+
# save
|
|
73
|
+
# df.write_parquet('output.parquet')
|
|
74
|
+
|
|
75
|
+
return df
|
|
76
|
+
|
|
77
|
+
if __name__ in ("__main__", "builtins"):
|
|
78
|
+
# TODO: 数据加载或外部传入
|
|
79
|
+
df_output = main(df_input)
|
|
@@ -53,7 +53,7 @@ class ExprTool:
|
|
|
53
53
|
self.get_current_func = func
|
|
54
54
|
self.get_current_func_kwargs = kwargs
|
|
55
55
|
|
|
56
|
-
def extract(self, expr):
|
|
56
|
+
def extract(self, expr, date, asset):
|
|
57
57
|
"""抽取分割后的子公式
|
|
58
58
|
|
|
59
59
|
Parameters
|
|
@@ -73,12 +73,13 @@ class ExprTool:
|
|
|
73
73
|
syms = []
|
|
74
74
|
get_children(self.get_current_func, self.get_current_func_kwargs,
|
|
75
75
|
expr,
|
|
76
|
-
output_exprs=exprs, output_symbols=syms
|
|
76
|
+
output_exprs=exprs, output_symbols=syms,
|
|
77
|
+
date=date, asset=asset)
|
|
77
78
|
# print('=' * 20, expr)
|
|
78
79
|
# print(exprs)
|
|
79
80
|
return exprs, syms
|
|
80
81
|
|
|
81
|
-
def merge(self, **kwargs):
|
|
82
|
+
def merge(self, date, asset, **kwargs):
|
|
82
83
|
"""合并多个表达式
|
|
83
84
|
|
|
84
85
|
1. 先抽取分割子公式
|
|
@@ -93,7 +94,7 @@ class ExprTool:
|
|
|
93
94
|
-------
|
|
94
95
|
表达式列表
|
|
95
96
|
"""
|
|
96
|
-
exprs_syms = [self.extract(v) for v in kwargs.values()]
|
|
97
|
+
exprs_syms = [self.extract(v, date, asset) for v in kwargs.values()]
|
|
97
98
|
exprs = []
|
|
98
99
|
syms = []
|
|
99
100
|
for e, s in exprs_syms:
|
|
@@ -164,14 +165,14 @@ class ExprTool:
|
|
|
164
165
|
|
|
165
166
|
return self.exprs_dict
|
|
166
167
|
|
|
167
|
-
def dag(self, merge: bool):
|
|
168
|
+
def dag(self, merge: bool, date, asset):
|
|
168
169
|
"""生成DAG"""
|
|
169
|
-
G = dag_start(self.exprs_dict, self.get_current_func, self.get_current_func_kwargs)
|
|
170
|
+
G = dag_start(self.exprs_dict, self.get_current_func, self.get_current_func_kwargs, date, asset)
|
|
170
171
|
if merge:
|
|
171
|
-
G = dag_middle(G, self.exprs_names, self.get_current_func, self.get_current_func_kwargs)
|
|
172
|
+
G = dag_middle(G, self.exprs_names, self.get_current_func, self.get_current_func_kwargs, date, asset)
|
|
172
173
|
return dag_end(G)
|
|
173
174
|
|
|
174
|
-
def all(self, exprs_src, style: str = '
|
|
175
|
+
def all(self, exprs_src, style: str = 'polars_over', template_file: str = 'template.py.j2',
|
|
175
176
|
replace: bool = True, regroup: bool = False, format: bool = True,
|
|
176
177
|
date='date', asset='asset',
|
|
177
178
|
alias: Dict[str, str] = {},
|
|
@@ -183,7 +184,7 @@ class ExprTool:
|
|
|
183
184
|
exprs_src: dict
|
|
184
185
|
表达式字典
|
|
185
186
|
style: str
|
|
186
|
-
代码风格。可选值 ('
|
|
187
|
+
代码风格。可选值 ('polars_group', 'polars_over', 'pandas')
|
|
187
188
|
template_file: str
|
|
188
189
|
根据需求可定制模板
|
|
189
190
|
replace:bool
|
|
@@ -206,24 +207,26 @@ class ExprTool:
|
|
|
206
207
|
代码字符串
|
|
207
208
|
|
|
208
209
|
"""
|
|
209
|
-
assert style in ('
|
|
210
|
+
assert style in ('polars_group', 'polars_over', 'pandas')
|
|
210
211
|
|
|
211
212
|
if replace:
|
|
212
213
|
exprs_src = replace_exprs(exprs_src)
|
|
213
214
|
|
|
214
215
|
# 子表达式在前,原表式在最后
|
|
215
|
-
exprs_dst, syms_dst = self.merge(**exprs_src)
|
|
216
|
+
exprs_dst, syms_dst = self.merge(date, asset, **exprs_src)
|
|
216
217
|
|
|
217
218
|
# 提取公共表达式
|
|
218
219
|
self.cse(exprs_dst, symbols_repl=numbered_symbols('_x_'), symbols_redu=exprs_src.keys())
|
|
219
220
|
# 有向无环图流转
|
|
220
|
-
exprs_ldl, G = self.dag(True)
|
|
221
|
+
exprs_ldl, G = self.dag(True, date, asset)
|
|
221
222
|
|
|
222
223
|
if regroup:
|
|
223
224
|
exprs_ldl.optimize()
|
|
224
225
|
|
|
225
|
-
if style == '
|
|
226
|
-
from expr_codegen.
|
|
226
|
+
if style == 'polars_group':
|
|
227
|
+
from expr_codegen.polars_group.code import codegen
|
|
228
|
+
elif style == 'polars_over':
|
|
229
|
+
from expr_codegen.polars_over.code import codegen
|
|
227
230
|
else:
|
|
228
231
|
from expr_codegen.pandas.code import codegen
|
|
229
232
|
|
|
@@ -257,7 +260,7 @@ class ExprTool:
|
|
|
257
260
|
source: str, *more_sources: str,
|
|
258
261
|
extra_codes: str, output_file: str,
|
|
259
262
|
convert_xor: bool,
|
|
260
|
-
style='
|
|
263
|
+
style='polars_over', template_file='template.py.j2',
|
|
261
264
|
date='date', asset='asset') -> str:
|
|
262
265
|
"""通过字符串生成代码, 加了缓存,多次调用不重复生成"""
|
|
263
266
|
raw, exprs_dict = sources_to_exprs(self.globals_, source, *more_sources, convert_xor=convert_xor)
|
|
@@ -288,7 +291,7 @@ def codegen_exec(df,
|
|
|
288
291
|
extra_codes: str = r'CS_SW_L1 = pl.col(r"^sw_l1_\d+$")',
|
|
289
292
|
output_file: Optional[str] = None,
|
|
290
293
|
convert_xor: bool = False,
|
|
291
|
-
style: str = '
|
|
294
|
+
style: str = 'polars_over', template_file: str = 'template.py.j2',
|
|
292
295
|
date: str = 'date', asset: str = 'asset'
|
|
293
296
|
):
|
|
294
297
|
"""快速转换源代码并执行
|
|
@@ -306,7 +309,7 @@ def codegen_exec(df,
|
|
|
306
309
|
convert_xor: bool
|
|
307
310
|
^ 转成异或还是乘方
|
|
308
311
|
style: str
|
|
309
|
-
代码风格。可选值 ('
|
|
312
|
+
代码风格。可选值 ('polars_group', 'polars_over', 'pandas')
|
|
310
313
|
template_file: str
|
|
311
314
|
代码模板
|
|
312
315
|
date: str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
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
|
|
@@ -206,6 +206,7 @@ df = codegen_exec(df, _code_block_1, _code_block_2) # 只执行,不保存代
|
|
|
206
206
|
6. 不支持`A<=B<=C`,需手工替换成`(A<=B)&(B<=C)`
|
|
207
207
|
7. 支持`A[0]+B[1]+C[2]`,底层会转成`A+ts_delay(B,1)+ts_delay(C,2)`
|
|
208
208
|
8. 支持`~A`,底层会转换成`Not(A)`
|
|
209
|
+
9. `gp_`开头的函数都会返回对应的`cs_`函数。如`gp_func(A,B,C)`会替换成`cs_func(B,C)`,其中`A`用在了`groupby([date, A])`
|
|
209
210
|
|
|
210
211
|
## 下划线开头的变量
|
|
211
212
|
|
|
@@ -19,7 +19,11 @@ expr_codegen/pandas/__init__.py
|
|
|
19
19
|
expr_codegen/pandas/code.py
|
|
20
20
|
expr_codegen/pandas/printer.py
|
|
21
21
|
expr_codegen/pandas/template.py.j2
|
|
22
|
-
expr_codegen/
|
|
23
|
-
expr_codegen/
|
|
24
|
-
expr_codegen/
|
|
25
|
-
expr_codegen/
|
|
22
|
+
expr_codegen/polars_group/__init__.py
|
|
23
|
+
expr_codegen/polars_group/code.py
|
|
24
|
+
expr_codegen/polars_group/printer.py
|
|
25
|
+
expr_codegen/polars_group/template.py.j2
|
|
26
|
+
expr_codegen/polars_over/__init__.py
|
|
27
|
+
expr_codegen/polars_over/code.py
|
|
28
|
+
expr_codegen/polars_over/printer.py
|
|
29
|
+
expr_codegen/polars_over/template.py.j2
|
|
@@ -38,12 +38,14 @@ packages = [
|
|
|
38
38
|
"expr_codegen",
|
|
39
39
|
"expr_codegen.latex",
|
|
40
40
|
"expr_codegen.pandas",
|
|
41
|
-
"expr_codegen.
|
|
41
|
+
"expr_codegen.polars_group",
|
|
42
|
+
"expr_codegen.polars_over",
|
|
42
43
|
]
|
|
43
44
|
|
|
44
45
|
[tool.setuptools.package-data]
|
|
45
46
|
"expr_codegen.pandas" = ["*.j2"]
|
|
46
|
-
"expr_codegen.
|
|
47
|
+
"expr_codegen.polars_group" = ["*.j2"]
|
|
48
|
+
"expr_codegen.polars_over" = ["*.j2"]
|
|
47
49
|
|
|
48
50
|
[tool.setuptools.dynamic]
|
|
49
51
|
version = { attr = "expr_codegen._version.__version__" }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.7"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{expr_codegen-0.8.7/expr_codegen/polars → expr_codegen-0.9.1/expr_codegen/polars_group}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|