ygo 1.0.5__tar.gz → 1.0.6b0__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.

Potentially problematic release.


This version of ygo might be problematic. Click here for more details.

Files changed (32) hide show
  1. {ygo-1.0.5/ygo.egg-info → ygo-1.0.6b0}/PKG-INFO +2 -1
  2. {ygo-1.0.5 → ygo-1.0.6b0}/pyproject.toml +2 -1
  3. ygo-1.0.6b0/qdf/errors.py +65 -0
  4. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/expr.py +34 -27
  5. ygo-1.0.6b0/qdf/qdf.py +166 -0
  6. {ygo-1.0.5 → ygo-1.0.6b0/ygo.egg-info}/PKG-INFO +2 -1
  7. {ygo-1.0.5 → ygo-1.0.6b0}/ygo.egg-info/requires.txt +1 -0
  8. {ygo-1.0.5 → ygo-1.0.6b0}/ylog/core.py +2 -2
  9. ygo-1.0.5/qdf/errors.py +0 -31
  10. ygo-1.0.5/qdf/qdf.py +0 -158
  11. {ygo-1.0.5 → ygo-1.0.6b0}/LICENSE +0 -0
  12. {ygo-1.0.5 → ygo-1.0.6b0}/README.md +0 -0
  13. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/__init__.py +0 -0
  14. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/__init__.py +0 -0
  15. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/base_udf.py +0 -0
  16. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/cs_udf.py +0 -0
  17. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/d_udf.py +0 -0
  18. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/ind_udf.py +0 -0
  19. {ygo-1.0.5 → ygo-1.0.6b0}/qdf/udf/ts_udf.py +0 -0
  20. {ygo-1.0.5 → ygo-1.0.6b0}/setup.cfg +0 -0
  21. {ygo-1.0.5 → ygo-1.0.6b0}/ycat/__init__.py +0 -0
  22. {ygo-1.0.5 → ygo-1.0.6b0}/ycat/client.py +0 -0
  23. {ygo-1.0.5 → ygo-1.0.6b0}/ycat/dtype.py +0 -0
  24. {ygo-1.0.5 → ygo-1.0.6b0}/ycat/parse.py +0 -0
  25. {ygo-1.0.5 → ygo-1.0.6b0}/ycat/yck.py +0 -0
  26. {ygo-1.0.5 → ygo-1.0.6b0}/ygo/__init__.py +0 -0
  27. {ygo-1.0.5 → ygo-1.0.6b0}/ygo/exceptions.py +0 -0
  28. {ygo-1.0.5 → ygo-1.0.6b0}/ygo/ygo.py +0 -0
  29. {ygo-1.0.5 → ygo-1.0.6b0}/ygo.egg-info/SOURCES.txt +0 -0
  30. {ygo-1.0.5 → ygo-1.0.6b0}/ygo.egg-info/dependency_links.txt +0 -0
  31. {ygo-1.0.5 → ygo-1.0.6b0}/ygo.egg-info/top_level.txt +0 -0
  32. {ygo-1.0.5 → ygo-1.0.6b0}/ylog/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ygo
3
- Version: 1.0.5
3
+ Version: 1.0.6b0
4
4
  Project-URL: homepage, https://github.com/link-yundi/ygo
5
5
  Project-URL: repository, https://github.com/link-yundi/ygo
6
6
  Requires-Python: >=3.8
@@ -8,6 +8,7 @@ Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: clickhouse-driver>=0.2.9
10
10
  Requires-Dist: dynaconf>=3.2.11
11
+ Requires-Dist: exchange-calendars>=4.2.8
11
12
  Requires-Dist: joblib>=1.4.2
12
13
  Requires-Dist: lark>=1.2.2
13
14
  Requires-Dist: loguru>=0.7.3
@@ -4,13 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ygo"
7
- version = "1.0.5"
7
+ version = "1.0.6beta"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
11
  dependencies = [
12
12
  "clickhouse-driver>=0.2.9",
13
13
  "dynaconf>=3.2.11",
14
+ "exchange-calendars>=4.2.8",
14
15
  "joblib>=1.4.2",
15
16
  "lark>=1.2.2",
16
17
  "loguru>=0.7.3",
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ---------------------------------------------
4
+ Created on 2025/5/16 10:47
5
+ @author: ZhangYundi
6
+ @email: yundi.xxii@outlook.com
7
+ ---------------------------------------------
8
+ """
9
+
10
+ from dataclasses import dataclass
11
+
12
+ @dataclass
13
+ class ParseError(Exception):
14
+ message: str
15
+
16
+ def __str__(self):
17
+ return self.message
18
+
19
+ def __repr__(self):
20
+ return self.__str__()
21
+
22
+ @dataclass
23
+ class CalculateError(Exception):
24
+ message: str
25
+
26
+ def __str__(self):
27
+ return self.message
28
+
29
+ def __repr__(self):
30
+ return self.__str__()
31
+
32
+ @dataclass
33
+ class CompileError(Exception):
34
+ message: str
35
+
36
+ def __str__(self):
37
+ return self.message
38
+
39
+ def __repr__(self):
40
+ return self.__str__()
41
+
42
+ @dataclass
43
+ class PolarsError(Exception):
44
+ message: str
45
+
46
+ def __str__(self):
47
+ return self.message
48
+
49
+ def __repr__(self):
50
+ return self.__str__()
51
+
52
+ @dataclass
53
+ class FailError:
54
+ expr: str
55
+ error: Exception
56
+
57
+ def __str__(self):
58
+ return f"""
59
+ [失败表达式]: {self.expr}
60
+ [错误类型]: {self.error.__class__.__name__}
61
+ [错误信息]: \n{self.error}
62
+ """
63
+
64
+ def __repr__(self):
65
+ return self.__str__()
@@ -41,40 +41,40 @@ with warnings.catch_warnings():
41
41
  start: expr
42
42
  ?expr: ternary_expr
43
43
  ?ternary_expr: or_expr
44
- | or_expr "?" or_expr ":" ternary_expr -> ternary
44
+ | or_expr "?" or_expr ":" ternary_expr -> ternary
45
45
  ?or_expr: and_expr
46
- | or_expr "|" and_expr -> or_
46
+ | or_expr "|" and_expr -> or_
47
47
  ?and_expr: comp_expr
48
- | and_expr "&" comp_expr -> and_
48
+ | and_expr "&" comp_expr -> and_
49
49
  ?comp_expr: eq_expr
50
- | comp_expr "<" eq_expr -> lt
51
- | comp_expr ">" eq_expr -> gt
52
- | comp_expr "<=" eq_expr -> le
53
- | comp_expr ">=" eq_expr -> ge
50
+ | comp_expr "<" eq_expr -> lt
51
+ | comp_expr ">" eq_expr -> gt
52
+ | comp_expr "<=" eq_expr -> le
53
+ | comp_expr ">=" eq_expr -> ge
54
54
  ?eq_expr: arith_expr
55
- | eq_expr "==" arith_expr -> eq
56
- | eq_expr "!=" arith_expr -> neq
55
+ | eq_expr "==" arith_expr -> eq
56
+ | eq_expr "!=" arith_expr -> neq
57
57
  ?arith_expr: term
58
- | arith_expr "+" term -> add
59
- | arith_expr "-" term -> sub
58
+ | arith_expr "+" term -> add
59
+ | arith_expr "-" term -> sub
60
60
  ?term: pow_expr
61
- | term "*" pow_expr -> mul
62
- | term "/" pow_expr -> div
63
- | term "//" pow_expr -> floordiv // 取整
64
- | term "%" pow_expr -> mod // 求余
61
+ | term "*" pow_expr -> mul
62
+ | term "/" pow_expr -> div
63
+ | term "//" pow_expr -> floordiv // 取整
64
+ | term "%" pow_expr -> mod // 求余
65
65
  ?pow_expr: factor
66
- | factor "**" pow_expr -> pow
66
+ | factor "**" pow_expr -> pow
67
67
  ?factor: atom
68
- | "-" factor -> neg
69
- | "!" factor -> not_
70
- | "~" factor -> not_
68
+ | "-" factor -> neg
69
+ | "!" factor -> not_
70
+ | "~" factor -> not_
71
71
  ?atom: function
72
- | NAME
73
- | NUMBER
74
- | FLOAT
75
- | "(" expr ")"
76
- | implicit_mul // 隐式乘法
77
- | attribute_access // 新增:属性访问
72
+ | NAME
73
+ | NUMBER
74
+ | FLOAT
75
+ | "(" expr ")"
76
+ | implicit_mul // 隐式乘法
77
+ | attribute_access // 新增:属性访问
78
78
  implicit_mul: (NUMBER | FLOAT) NAME -> implicit_mul // 隐式乘法
79
79
  attribute_access: atom "." NAME -> attribute_access // 新增:属性访问
80
80
  function: NAME "(" expr_list ")" -> function
@@ -83,7 +83,7 @@ with warnings.catch_warnings():
83
83
  expr_list: (expr | keyword_arg) ("," (expr | keyword_arg))* // 支持关键字参数
84
84
  NAME: /[a-zA-Z_$,][a-zA-Z0-9_$]*/
85
85
  NUMBER: /\d+/ // regex for numbers
86
- FLOAT: /\d+\.\d+/
86
+ FLOAT: /\d+\.\d+([eE][+-]?\d+)?/ | /\d+[eE][+-]?\d+/ // 支持科学计数法
87
87
  %import common.WS
88
88
  %ignore WS
89
89
  """
@@ -181,7 +181,6 @@ parser = Lark(grammar, parser='lalr', transformer=ExprParser())
181
181
  def parse_expr(expression: str) -> Expr:
182
182
  return parser.parse(expression).children[0]
183
183
 
184
-
185
184
  class Expr:
186
185
 
187
186
  def __init__(self, expr: str | None = None):
@@ -203,6 +202,13 @@ class Expr:
203
202
  expr.alias = alias if alias is not None else str(expr)
204
203
  return expr
205
204
 
205
+ def __hash__(self):
206
+ return hash(str(self).strip())
207
+
208
+ def __eq__(self, other):
209
+ return str(self).strip() == str(other).strip()
210
+
211
+
206
212
  def to_rpn(self) -> list[Token]:
207
213
  """生成逆波兰表达式: (后缀表达式: 运算符在后)"""
208
214
  rpn = list()
@@ -285,6 +291,7 @@ class Expr:
285
291
  else:
286
292
  self.fn_name, self.args = expr_.fn_name, expr_.args
287
293
 
294
+
288
295
  @property
289
296
  def n_args(self) -> int:
290
297
  """返回表达式的参数个数"""
ygo-1.0.6b0/qdf/qdf.py ADDED
@@ -0,0 +1,166 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ---------------------------------------------
4
+ Created on 2025/3/5 21:40
5
+ @author: ZhangYundi
6
+ @email: yundi.xxii@outlook.com
7
+ ---------------------------------------------
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import importlib.util
12
+ import sys
13
+ from functools import lru_cache
14
+ from pathlib import Path
15
+
16
+ import polars as pl
17
+ from toolz import partial
18
+
19
+ import ygo
20
+ import ylog
21
+ from .errors import CalculateError, CompileError, PolarsError, FailError
22
+ from .expr import Expr
23
+
24
+ # 动态加载模块
25
+ module_name = "udf"
26
+ module_path = Path(__file__).parent / "udf" / "__init__.py"
27
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
28
+ module = importlib.util.module_from_spec(spec)
29
+ sys.modules[module_name] = module
30
+ spec.loader.exec_module(module)
31
+
32
+
33
+ @lru_cache(maxsize=512)
34
+ def parse_expr(expr: str) -> Expr:
35
+ return Expr(expr)
36
+
37
+
38
+ class QDF:
39
+
40
+ def __init__(self,
41
+ data: pl.LazyFrame | pl.DataFrame,
42
+ index: tuple[str] = ("date", "time", "asset"),
43
+ align: bool = True, ):
44
+ assert isinstance(data, (pl.LazyFrame, pl.DataFrame)), "data must be a polars DataFrame or LazyFrame"
45
+ self.data = data.with_columns(pl.col(pl.Decimal).cast(pl.Float32).round(5))
46
+ if isinstance(self.data, pl.LazyFrame):
47
+ self.data = self.data.collect()
48
+ self.index = index
49
+ self.dims = [self.data[name].drop_nulls().n_unique() for name in index]
50
+ if align:
51
+ lev_vals: list[pl.DataFrame] = [self.data.select(name).drop_nulls().unique() for name in index]
52
+ full_index = lev_vals[0]
53
+ for lev_val in lev_vals[1:]:
54
+ full_index = full_index.join(lev_val, how="cross")
55
+ self.data = full_index.join(self.data, on=index, how='left').sort(index)
56
+ self.failed = list()
57
+ self._expr_cache = dict() # type: dict[Expr, str]
58
+ self._cur_expr_cache = dict()
59
+
60
+ def __str__(self):
61
+ return self.data.__str__()
62
+
63
+ def __repr__(self):
64
+ return self.data.__str__()
65
+
66
+ def register_udf(self, func: callable, name: str = None):
67
+ name = name if name is not None else func.__name__
68
+ setattr(module, name, func)
69
+
70
+ def _compile_expr(self, expr: str, cover: bool):
71
+ """str表达式 -> polars 表达式"""
72
+ try:
73
+ expr_parsed = Expr(expr)
74
+ alias = expr_parsed.alias # if expr_parsed.alias is not None else str(expr_parsed)
75
+ current_cols = set(self.data.columns)
76
+ # columns = self.data.columns
77
+ if alias in current_cols and not cover:
78
+ return pl.col(alias), alias
79
+ # 如果该表达式已有对应列,直接复用
80
+ if expr_parsed in self._expr_cache and not cover:
81
+ expr_pl: pl.Expr = pl.col(self._expr_cache[expr_parsed]).alias(alias)
82
+ return expr_pl, alias
83
+ elif expr_parsed in self._cur_expr_cache and not cover:
84
+ expr_pl: pl.Expr = pl.col(self._cur_expr_cache[expr_parsed]).alias(alias)
85
+ return expr_pl, alias
86
+
87
+ def recur_compile(expr_: Expr):
88
+ """递归编译"""
89
+ alias_ = expr_.alias
90
+ if alias_ in current_cols and not cover:
91
+ # 已存在:直接select数据源
92
+ return pl.col(alias_)
93
+ if expr_ in self._expr_cache:
94
+ return pl.col(self._expr_cache[expr_]).alias(alias_)
95
+ elif expr_ in self._cur_expr_cache:
96
+ return pl.col(self._cur_expr_cache[expr_]).alias(alias_)
97
+ func = getattr(module, expr_.fn_name)
98
+ _params = ygo.fn_signature_params(func)
99
+ if "dims" in _params:
100
+ func = partial(func, dims=self.dims)
101
+ args = list()
102
+ kwargs = dict()
103
+ for arg in expr_.args:
104
+ if isinstance(arg, Expr):
105
+ args.append(recur_compile(arg))
106
+ elif isinstance(arg, dict):
107
+ kwargs.update(arg)
108
+ elif isinstance(arg, str):
109
+ args.append(pl.col(arg))
110
+ else:
111
+ args.append(arg) # or args.append(pl.lit(arg))
112
+ try:
113
+ expr_pl: pl.Expr = func(*args, **kwargs).alias(alias_)
114
+ self._cur_expr_cache[expr_] = alias_
115
+ return expr_pl
116
+ except Exception as e:
117
+ raise CompileError(message=f"{expr_.fn_name}({', '.join([str(arg) for arg in args])})\n{e}") from e
118
+
119
+ return recur_compile(expr_parsed), alias
120
+ except (CalculateError, CompileError, PolarsError) as e:
121
+ # 已经是你自己的错误类
122
+ raise e
123
+ except Exception as e:
124
+ # 所有未处理的错误统一抛出为 CompileError
125
+ raise CompileError(message=f"[编译器外层]\n{e}") from e
126
+
127
+ def sql(self, *exprs: str, cover: bool = False, ) -> pl.LazyFrame:
128
+ """
129
+ 表达式查询
130
+ Parameters
131
+ ----------
132
+ exprs: str
133
+ 表达式,比如 "ts_mean(close, 5) as close_ma5"
134
+ cover: bool
135
+ 当遇到已经存在列名的时候,是否重新计算覆盖原来的列, 默认False,返回已经存在的列,跳过计算
136
+ - True: 重新计算并且返回新的结果,覆盖掉原来的列
137
+ - False, 返回已经存在的列,跳过计算
138
+ Returns
139
+ -------
140
+ polars.DataFrame
141
+ """
142
+ self.failed = list()
143
+ exprs_to_add = list()
144
+ exprs_select = list()
145
+ self._cur_expr_cache = {}
146
+ data = self.data.lazy()
147
+
148
+ for expr in exprs:
149
+ try:
150
+ compiled, alias = self._compile_expr(expr, cover)
151
+ if compiled is not None:
152
+ exprs_to_add.append(compiled)
153
+ exprs_select.append(alias)
154
+ except Exception as e:
155
+ self.failed.append(FailError(expr, e))
156
+ if self.failed:
157
+ ylog.warning(f"QDF.sql 失败:{len(self.failed)}/{len(exprs)}: \n {self.failed}")
158
+ for expr in exprs_to_add:
159
+ data = data.with_columns(expr).fill_nan(None)
160
+ try:
161
+ self.data = data.collect()
162
+ final_df = self.data.select(*self.index, *exprs_to_add)
163
+ self._expr_cache.update(self._cur_expr_cache)
164
+ return final_df
165
+ except Exception as e:
166
+ raise PolarsError(message=f"LazyFrame.collect() 阶段出错\n{e}") from e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ygo
3
- Version: 1.0.5
3
+ Version: 1.0.6b0
4
4
  Project-URL: homepage, https://github.com/link-yundi/ygo
5
5
  Project-URL: repository, https://github.com/link-yundi/ygo
6
6
  Requires-Python: >=3.8
@@ -8,6 +8,7 @@ Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: clickhouse-driver>=0.2.9
10
10
  Requires-Dist: dynaconf>=3.2.11
11
+ Requires-Dist: exchange-calendars>=4.2.8
11
12
  Requires-Dist: joblib>=1.4.2
12
13
  Requires-Dist: lark>=1.2.2
13
14
  Requires-Dist: loguru>=0.7.3
@@ -1,5 +1,6 @@
1
1
  clickhouse-driver>=0.2.9
2
2
  dynaconf>=3.2.11
3
+ exchange-calendars>=4.2.8
3
4
  joblib>=1.4.2
4
5
  lark>=1.2.2
5
6
  loguru>=0.7.3
@@ -94,7 +94,7 @@ class _Logger:
94
94
  colorize=True,
95
95
  backtrace=self.debug_mode,
96
96
  diagnose=self.debug_mode,
97
- enqueue=True # 异步写入
97
+ # enqueue=True # 异步写入
98
98
  )
99
99
 
100
100
  def _setup_file_logging(self, retention_days: int, error_retention_days: int):
@@ -128,7 +128,7 @@ class _Logger:
128
128
  compression="zip",
129
129
  backtrace=True,
130
130
  diagnose=self.debug_mode,
131
- enqueue=True, # 异步写入
131
+ # enqueue=True, # 异步写入
132
132
  filter=lambda record, lvl=level: record["level"].name == lvl,
133
133
  catch=True # 捕获格式化异常
134
134
  )
ygo-1.0.5/qdf/errors.py DELETED
@@ -1,31 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- ---------------------------------------------
4
- Created on 2025/5/16 10:47
5
- @author: ZhangYundi
6
- @email: yundi.xxii@outlook.com
7
- ---------------------------------------------
8
- """
9
-
10
- from dataclasses import dataclass
11
-
12
- @dataclass
13
- class ParseError(Exception):
14
- message: str
15
-
16
- def __str__(self):
17
- return f"ParseError(message={self.message})"
18
-
19
- @dataclass
20
- class CalculateError(Exception):
21
- message: str
22
-
23
- def __str__(self):
24
- return f"CalculateError(message={self.message})"
25
-
26
- @dataclass
27
- class PolarsError(Exception):
28
- message: str
29
-
30
- def __str__(self):
31
- return f"PolarsError(message={self.message})"
ygo-1.0.5/qdf/qdf.py DELETED
@@ -1,158 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- ---------------------------------------------
4
- Created on 2025/3/5 21:40
5
- @author: ZhangYundi
6
- @email: yundi.xxii@outlook.com
7
- ---------------------------------------------
8
- """
9
- from __future__ import annotations
10
-
11
- import importlib.util
12
- import sys
13
- from dataclasses import dataclass
14
- from pathlib import Path
15
-
16
- import polars as pl
17
- from toolz import partial
18
- from functools import lru_cache
19
-
20
- import ygo
21
- import ylog
22
- from .errors import CalculateError, PolarsError
23
- from .expr import Expr
24
- import time
25
-
26
- # 动态加载模块
27
- module_name = "udf"
28
- module_path = Path(__file__).parent / "udf" / "__init__.py"
29
- spec = importlib.util.spec_from_file_location(module_name, module_path)
30
- module = importlib.util.module_from_spec(spec)
31
- sys.modules[module_name] = module
32
- spec.loader.exec_module(module)
33
-
34
-
35
- @dataclass
36
- class FailInfo:
37
- expr: str
38
- error: Exception
39
-
40
- def __str__(self):
41
- return f"""
42
- expr={self.expr}
43
- =================================================
44
- {self.error}
45
- =================================================
46
- """
47
-
48
- def __repr__(self):
49
- return self.__str__()
50
-
51
- @lru_cache(maxsize=512)
52
- def parse_expr(expr: str) -> Expr:
53
- return Expr(expr)
54
-
55
- class QDF:
56
-
57
- def __init__(self,
58
- data: pl.LazyFrame,
59
- index: tuple[str] = ("date", "time", "asset"),
60
- align: bool = True,):
61
- self.data = data.with_columns(pl.col(pl.Decimal).cast(pl.Float32))
62
- self.dims = [self.data.select(index_).drop_nulls().unique().count().collect().item() for index_ in index]
63
- if align:
64
- lev_vals: list[pl.DataFrame] = [self.data.select(name).drop_nulls().unique() for name in index]
65
- full_index = lev_vals[0]
66
- for lev_val in lev_vals[1:]:
67
- full_index = full_index.join(lev_val, how="cross")
68
- self.data = full_index.join(self.data, on=index, how='left') #.sort(index).collect().lazy()
69
-
70
- self.index = index
71
- self.failed = list()
72
-
73
- def __str__(self):
74
- return self.data.__str__()
75
-
76
- def __repr__(self):
77
- return self.data.__str__()
78
-
79
- def register_udf(self, func: callable, name: str = None):
80
- name = name if name is not None else func.__name__
81
- setattr(module, name, func)
82
-
83
- def _compile_expr(self, expr: str, cover: bool):
84
- expr_parsed = Expr(expr)
85
- alias = expr_parsed.alias # if expr_parsed.alias is not None else str(expr_parsed)
86
- current_cols = set(self.data.collect_schema().keys())
87
- columns = self.data.collect_schema().names()
88
- if alias in current_cols and not cover:
89
- return alias
90
-
91
- def calc(expr_: Expr):
92
- alias_ = expr_.alias
93
- # _cols = self.data.collect_schema().names()
94
- if alias_ in current_cols and not cover:
95
- # 已存在:直接select数据源
96
- return alias_
97
- func = getattr(module, expr_.fn_name)
98
- _params = ygo.fn_signature_params(func)
99
- if "dims" in _params:
100
- func = partial(func, dims=self.dims)
101
- args = list()
102
- kwargs = dict()
103
- for arg in expr_.args:
104
- if isinstance(arg, Expr):
105
- args.append(pl.col(calc(arg)))
106
- elif isinstance(arg, dict):
107
- kwargs.update(arg)
108
- elif isinstance(arg, str):
109
- args.append(pl.col(arg))
110
- else:
111
- args.append(arg) # or args.append(pl.lit(arg))
112
- try:
113
- expr_pl: pl.Expr = func(*args, **kwargs).alias(alias_)
114
- except Exception as e:
115
- raise CalculateError(f"{expr_.fn_name}({', '.join([str(arg) for arg in args])})\n{e}")
116
- try:
117
- self.data = self.data.with_columns(expr_pl)
118
- except Exception as e:
119
- raise PolarsError(f"{expr_}\n{e}")
120
- return alias_
121
-
122
- calc(expr_parsed)
123
-
124
- columns.append(alias)
125
- drop = current_cols.difference(set(columns))
126
- self.data = self.data.drop(*drop)
127
-
128
- return alias
129
-
130
- def sql(self, *exprs: str, cover: bool = False,) -> pl.LazyFrame:
131
- """
132
- 表达式查询
133
- Parameters
134
- ----------
135
- exprs: str
136
- 表达式,比如 "ts_mean(close, 5) as close_ma5"
137
- cover: bool
138
- 当遇到已经存在列名的时候,是否重新计算覆盖原来的列, 默认False,返回已经存在的列,跳过计算
139
- - True: 重新计算并且返回新的结果,覆盖掉原来的列
140
- - False, 返回已经存在的列,跳过计算
141
- Returns
142
- -------
143
- polars.DataFrame
144
- """
145
- self.failed = list()
146
- exprs_to_add = list()
147
- for expr in exprs:
148
- try:
149
- compiled = self._compile_expr(expr, cover)
150
- if compiled is not None:
151
- exprs_to_add.append(compiled)
152
- except Exception as e:
153
- self.failed.append(FailInfo(expr, e))
154
- if self.failed:
155
- ylog.warning(f"QDF.sql 失败:{len(self.failed)}/{len(exprs)}: \n {self.failed}")
156
- final_df = self.data.with_columns(exprs_to_add).select(*self.index, *exprs_to_add).fill_nan(None).drop_nulls().sort(self.index)
157
- return final_df.collect()
158
-
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
File without changes
File without changes
File without changes