onetick-py 1.177.0__py3-none-any.whl
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.
- locator_parser/__init__.py +0 -0
- locator_parser/acl.py +73 -0
- locator_parser/actions.py +262 -0
- locator_parser/common.py +368 -0
- locator_parser/io.py +43 -0
- locator_parser/locator.py +150 -0
- onetick/__init__.py +101 -0
- onetick/doc_utilities/__init__.py +3 -0
- onetick/doc_utilities/napoleon.py +40 -0
- onetick/doc_utilities/ot_doctest.py +140 -0
- onetick/doc_utilities/snippets.py +279 -0
- onetick/lib/__init__.py +4 -0
- onetick/lib/instance.py +141 -0
- onetick/py/__init__.py +293 -0
- onetick/py/_stack_info.py +89 -0
- onetick/py/_version.py +2 -0
- onetick/py/aggregations/__init__.py +11 -0
- onetick/py/aggregations/_base.py +648 -0
- onetick/py/aggregations/_docs.py +948 -0
- onetick/py/aggregations/compute.py +286 -0
- onetick/py/aggregations/functions.py +2216 -0
- onetick/py/aggregations/generic.py +104 -0
- onetick/py/aggregations/high_low.py +80 -0
- onetick/py/aggregations/num_distinct.py +83 -0
- onetick/py/aggregations/order_book.py +501 -0
- onetick/py/aggregations/other.py +1014 -0
- onetick/py/backports.py +26 -0
- onetick/py/cache.py +374 -0
- onetick/py/callback/__init__.py +5 -0
- onetick/py/callback/callback.py +276 -0
- onetick/py/callback/callbacks.py +131 -0
- onetick/py/compatibility.py +798 -0
- onetick/py/configuration.py +771 -0
- onetick/py/core/__init__.py +0 -0
- onetick/py/core/_csv_inspector.py +93 -0
- onetick/py/core/_internal/__init__.py +0 -0
- onetick/py/core/_internal/_manually_bound_value.py +6 -0
- onetick/py/core/_internal/_nodes_history.py +250 -0
- onetick/py/core/_internal/_op_utils/__init__.py +0 -0
- onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
- onetick/py/core/_internal/_op_utils/is_const.py +10 -0
- onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
- onetick/py/core/_internal/_proxy_node.py +140 -0
- onetick/py/core/_internal/_state_objects.py +2312 -0
- onetick/py/core/_internal/_state_vars.py +93 -0
- onetick/py/core/_source/__init__.py +0 -0
- onetick/py/core/_source/_symbol_param.py +95 -0
- onetick/py/core/_source/schema.py +97 -0
- onetick/py/core/_source/source_methods/__init__.py +0 -0
- onetick/py/core/_source/source_methods/aggregations.py +809 -0
- onetick/py/core/_source/source_methods/applyers.py +296 -0
- onetick/py/core/_source/source_methods/columns.py +141 -0
- onetick/py/core/_source/source_methods/data_quality.py +301 -0
- onetick/py/core/_source/source_methods/debugs.py +272 -0
- onetick/py/core/_source/source_methods/drops.py +120 -0
- onetick/py/core/_source/source_methods/fields.py +619 -0
- onetick/py/core/_source/source_methods/filters.py +1002 -0
- onetick/py/core/_source/source_methods/joins.py +1413 -0
- onetick/py/core/_source/source_methods/merges.py +605 -0
- onetick/py/core/_source/source_methods/misc.py +1455 -0
- onetick/py/core/_source/source_methods/pandases.py +155 -0
- onetick/py/core/_source/source_methods/renames.py +356 -0
- onetick/py/core/_source/source_methods/sorts.py +183 -0
- onetick/py/core/_source/source_methods/switches.py +142 -0
- onetick/py/core/_source/source_methods/symbols.py +117 -0
- onetick/py/core/_source/source_methods/times.py +627 -0
- onetick/py/core/_source/source_methods/writes.py +986 -0
- onetick/py/core/_source/symbol.py +205 -0
- onetick/py/core/_source/tmp_otq.py +222 -0
- onetick/py/core/column.py +209 -0
- onetick/py/core/column_operations/__init__.py +0 -0
- onetick/py/core/column_operations/_methods/__init__.py +4 -0
- onetick/py/core/column_operations/_methods/_internal.py +28 -0
- onetick/py/core/column_operations/_methods/conversions.py +216 -0
- onetick/py/core/column_operations/_methods/methods.py +292 -0
- onetick/py/core/column_operations/_methods/op_types.py +160 -0
- onetick/py/core/column_operations/accessors/__init__.py +0 -0
- onetick/py/core/column_operations/accessors/_accessor.py +28 -0
- onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
- onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
- onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
- onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
- onetick/py/core/column_operations/base.py +1121 -0
- onetick/py/core/cut_builder.py +150 -0
- onetick/py/core/db_constants.py +20 -0
- onetick/py/core/eval_query.py +245 -0
- onetick/py/core/lambda_object.py +441 -0
- onetick/py/core/multi_output_source.py +232 -0
- onetick/py/core/per_tick_script.py +2256 -0
- onetick/py/core/query_inspector.py +464 -0
- onetick/py/core/source.py +1744 -0
- onetick/py/db/__init__.py +2 -0
- onetick/py/db/_inspection.py +1128 -0
- onetick/py/db/db.py +1327 -0
- onetick/py/db/utils.py +64 -0
- onetick/py/docs/__init__.py +0 -0
- onetick/py/docs/docstring_parser.py +112 -0
- onetick/py/docs/utils.py +81 -0
- onetick/py/functions.py +2398 -0
- onetick/py/license.py +190 -0
- onetick/py/log.py +88 -0
- onetick/py/math.py +935 -0
- onetick/py/misc.py +470 -0
- onetick/py/oqd/__init__.py +22 -0
- onetick/py/oqd/eps.py +1195 -0
- onetick/py/oqd/sources.py +325 -0
- onetick/py/otq.py +216 -0
- onetick/py/pyomd_mock.py +47 -0
- onetick/py/run.py +916 -0
- onetick/py/servers.py +173 -0
- onetick/py/session.py +1347 -0
- onetick/py/sources/__init__.py +19 -0
- onetick/py/sources/cache.py +167 -0
- onetick/py/sources/common.py +128 -0
- onetick/py/sources/csv.py +642 -0
- onetick/py/sources/custom.py +85 -0
- onetick/py/sources/data_file.py +305 -0
- onetick/py/sources/data_source.py +1045 -0
- onetick/py/sources/empty.py +94 -0
- onetick/py/sources/odbc.py +337 -0
- onetick/py/sources/order_book.py +271 -0
- onetick/py/sources/parquet.py +168 -0
- onetick/py/sources/pit.py +191 -0
- onetick/py/sources/query.py +495 -0
- onetick/py/sources/snapshots.py +419 -0
- onetick/py/sources/split_query_output_by_symbol.py +198 -0
- onetick/py/sources/symbology_mapping.py +123 -0
- onetick/py/sources/symbols.py +374 -0
- onetick/py/sources/ticks.py +825 -0
- onetick/py/sql.py +70 -0
- onetick/py/state.py +251 -0
- onetick/py/types.py +2131 -0
- onetick/py/utils/__init__.py +70 -0
- onetick/py/utils/acl.py +93 -0
- onetick/py/utils/config.py +186 -0
- onetick/py/utils/default.py +49 -0
- onetick/py/utils/file.py +38 -0
- onetick/py/utils/helpers.py +76 -0
- onetick/py/utils/locator.py +94 -0
- onetick/py/utils/perf.py +498 -0
- onetick/py/utils/query.py +49 -0
- onetick/py/utils/render.py +1374 -0
- onetick/py/utils/script.py +244 -0
- onetick/py/utils/temp.py +471 -0
- onetick/py/utils/types.py +120 -0
- onetick/py/utils/tz.py +84 -0
- onetick_py-1.177.0.dist-info/METADATA +137 -0
- onetick_py-1.177.0.dist-info/RECORD +152 -0
- onetick_py-1.177.0.dist-info/WHEEL +5 -0
- onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
- onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
- onetick_py-1.177.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import TYPE_CHECKING, Callable, Optional, Union
|
|
5
|
+
|
|
6
|
+
from onetick import py as otp
|
|
7
|
+
from onetick.py import types as ott
|
|
8
|
+
from onetick.py.core import db_constants
|
|
9
|
+
from onetick.py.core.lambda_object import _EmulateObject, apply_lambda, apply_script
|
|
10
|
+
from onetick.py.core.query_inspector import add_pins, get_query_info
|
|
11
|
+
from onetick.py.otq import otq
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from onetick.py.core.source import Source
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def apply_query(self: 'Source', query, in_pin="IN", out_pin="OUT", **params):
|
|
18
|
+
"""
|
|
19
|
+
Apply data source to query
|
|
20
|
+
|
|
21
|
+
.. deprecated:: 1.3.77
|
|
22
|
+
|
|
23
|
+
See Also
|
|
24
|
+
--------
|
|
25
|
+
Source.apply
|
|
26
|
+
"""
|
|
27
|
+
warnings.warn(
|
|
28
|
+
'The "apply_query" method is deprecated. Please, use the .apply method instead or '
|
|
29
|
+
"call a reference queries directly.",
|
|
30
|
+
FutureWarning,
|
|
31
|
+
stacklevel=2,
|
|
32
|
+
)
|
|
33
|
+
res = otp.apply_query(query, {in_pin: self}, [out_pin], **params)
|
|
34
|
+
res.node().out_pin(out_pin)
|
|
35
|
+
|
|
36
|
+
return res
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def apply(self: 'Source', obj) -> Union['otp.Column', 'Source']:
|
|
40
|
+
"""
|
|
41
|
+
Apply object to data source.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
obj: onetick.py.query, Callable, type, onetick.query.GraphQuery
|
|
46
|
+
|
|
47
|
+
- `onetick.py.query` allows to apply external nested query
|
|
48
|
+
|
|
49
|
+
- python `Callable` allows to translate python code to similar OneTick's CASE expression.
|
|
50
|
+
There are some limitations to which python operators can be used in this callable.
|
|
51
|
+
See :ref:`Python callables parsing guide <python callable parser>` article for details.
|
|
52
|
+
In :ref:`Remote OTP with Ray<ray-remote>` any `Callable` must be decorated with `@otp.remote` decorator,
|
|
53
|
+
see :ref:`Ray usage examples<apply-remote-context>` for details.
|
|
54
|
+
|
|
55
|
+
- `type` allows to apply default type conversion
|
|
56
|
+
|
|
57
|
+
- `onetick.query.GraphQuery` allows to apply a build onetick.query.Graph
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
Column, Source
|
|
62
|
+
|
|
63
|
+
Examples
|
|
64
|
+
--------
|
|
65
|
+
|
|
66
|
+
Apply external query to a tick flow. In this case it assumes that query has
|
|
67
|
+
only one input and one output. Check the :class:`query` examples if you
|
|
68
|
+
want to use a query with multiple inputs or outputs.
|
|
69
|
+
|
|
70
|
+
>>> data = otp.Ticks(X=[1, 2, 3])
|
|
71
|
+
>>> external_query = otp.query('update.otq')
|
|
72
|
+
>>> data = data.apply(external_query)
|
|
73
|
+
>>> otp.run(data)
|
|
74
|
+
Time X
|
|
75
|
+
0 2003-12-01 00:00:00.000 2
|
|
76
|
+
1 2003-12-01 00:00:00.001 4
|
|
77
|
+
2 2003-12-01 00:00:00.002 6
|
|
78
|
+
|
|
79
|
+
Apply a predicate to a column / operation.
|
|
80
|
+
In this case value passed to a predicate is column values.
|
|
81
|
+
Result is a column.
|
|
82
|
+
|
|
83
|
+
>>> data = otp.Ticks(X=[1, 2, 3])
|
|
84
|
+
>>> data['Y'] = data['X'].apply(lambda x: x * 2)
|
|
85
|
+
>>> otp.run(data)
|
|
86
|
+
Time X Y
|
|
87
|
+
0 2003-12-01 00:00:00.000 1 2
|
|
88
|
+
1 2003-12-01 00:00:00.001 2 4
|
|
89
|
+
2 2003-12-01 00:00:00.002 3 6
|
|
90
|
+
|
|
91
|
+
Another example of applying more sophisticated operation
|
|
92
|
+
|
|
93
|
+
>>> data = otp.Ticks(X=[1, 2, 3])
|
|
94
|
+
>>> data['Y'] = data['X'].apply(lambda x: 1 if x > 2 else 0)
|
|
95
|
+
>>> otp.run(data)
|
|
96
|
+
Time X Y
|
|
97
|
+
0 2003-12-01 00:00:00.000 1 0
|
|
98
|
+
1 2003-12-01 00:00:00.001 2 0
|
|
99
|
+
2 2003-12-01 00:00:00.002 3 1
|
|
100
|
+
|
|
101
|
+
Example of applying a predicate to a Source. In this case value passed
|
|
102
|
+
to a predicate is a whole tick. Result is a column.
|
|
103
|
+
|
|
104
|
+
>>> data = otp.Ticks(X=[1, 2, 3], Y=[.5, -0.4, .2])
|
|
105
|
+
>>> data['Z'] = data.apply(lambda tick: 1 if abs(tick['X'] * tick['Y']) > 0.5 else 0)
|
|
106
|
+
>>> otp.run(data)
|
|
107
|
+
Time X Y Z
|
|
108
|
+
0 2003-12-01 00:00:00.000 1 0.5 0
|
|
109
|
+
1 2003-12-01 00:00:00.001 2 -0.4 1
|
|
110
|
+
2 2003-12-01 00:00:00.002 3 0.2 1
|
|
111
|
+
|
|
112
|
+
See Also
|
|
113
|
+
--------
|
|
114
|
+
:py:class:`onetick.py.query`
|
|
115
|
+
:py:meth:`onetick.py.Source.script`
|
|
116
|
+
:ref:`Python callables parsing guide <python callable parser>`
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
if isinstance(obj, otp.query):
|
|
120
|
+
graph = obj.graph_info
|
|
121
|
+
if graph is None:
|
|
122
|
+
return obj(IN=self)['OUT']
|
|
123
|
+
|
|
124
|
+
if len(graph.nested_inputs) != 1:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f'It is expected the query "{obj.query_name}" to have one input, but it '
|
|
127
|
+
f"has {len(graph.nested_inputs)}"
|
|
128
|
+
)
|
|
129
|
+
if len(graph.nested_outputs) > 1:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f'It is expected the query "{obj.query_name}" to have one or no output, but it '
|
|
132
|
+
f"has {len(graph.nested_outputs)}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
in_pin = graph.nested_inputs[0].NESTED_INPUT
|
|
136
|
+
|
|
137
|
+
if len(graph.nested_outputs) == 0:
|
|
138
|
+
out_pin = None # means no output
|
|
139
|
+
else:
|
|
140
|
+
out_pin = graph.nested_outputs[0].NESTED_OUTPUT
|
|
141
|
+
|
|
142
|
+
return obj(**{in_pin: self})[out_pin]
|
|
143
|
+
elif isinstance(obj, otq.GraphQuery):
|
|
144
|
+
base_dir = None
|
|
145
|
+
if os.getenv('OTP_WEBAPI_TEST_MODE'):
|
|
146
|
+
from onetick.py.otq import _tmp_otq_path
|
|
147
|
+
|
|
148
|
+
base_dir = _tmp_otq_path()
|
|
149
|
+
tmp_file = otp.utils.TmpFile(suffix=".otq", base_dir=base_dir)
|
|
150
|
+
|
|
151
|
+
obj.save_to_file(
|
|
152
|
+
tmp_file.path,
|
|
153
|
+
"graph_query_to_apply",
|
|
154
|
+
# start and end times don't matter for this query, use some constants
|
|
155
|
+
start=db_constants.DEFAULT_START_DATE,
|
|
156
|
+
end=db_constants.DEFAULT_END_DATE,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
query_info = get_query_info(tmp_file.path)
|
|
160
|
+
|
|
161
|
+
if len(query_info.sources) != 1:
|
|
162
|
+
raise ValueError(f"It is expected the query to have one input, but it has {len(query_info.sources)}")
|
|
163
|
+
if len(query_info.sinks) != 1:
|
|
164
|
+
raise ValueError(f"It is expected the query to have one output, but it has {len(query_info.sinks)}")
|
|
165
|
+
|
|
166
|
+
add_pins(
|
|
167
|
+
tmp_file.path,
|
|
168
|
+
"graph_query_to_apply",
|
|
169
|
+
[(query_info.sources[0], 1, "IN"), (query_info.sinks[0], 0, "OUT")],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return otp.query(tmp_file.path + "::graph_query_to_apply")(IN=self)["OUT"]
|
|
173
|
+
|
|
174
|
+
return apply_lambda(obj, _EmulateObject(self))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _extract_columns_from_script(script_text):
|
|
178
|
+
result = {}
|
|
179
|
+
supported_types = {
|
|
180
|
+
'byte',
|
|
181
|
+
'short',
|
|
182
|
+
'uint',
|
|
183
|
+
'long',
|
|
184
|
+
'ulong',
|
|
185
|
+
'int',
|
|
186
|
+
'float',
|
|
187
|
+
'double',
|
|
188
|
+
'string',
|
|
189
|
+
'time32',
|
|
190
|
+
'nsectime',
|
|
191
|
+
'msectime',
|
|
192
|
+
'varstring',
|
|
193
|
+
'decimal',
|
|
194
|
+
}
|
|
195
|
+
types = (
|
|
196
|
+
r"(?P<type>varstring|byte|short|uint|int|ulong|long|"
|
|
197
|
+
r"float|double|decimal|string|time32|msectime|nsectime|matrix)"
|
|
198
|
+
)
|
|
199
|
+
length = r"(\[(?P<length>\d+)\])?"
|
|
200
|
+
name = r"\s+(?P<name>\w+)\s*=\s*"
|
|
201
|
+
pattern = re.compile(types + length + name)
|
|
202
|
+
for p in re.finditer(pattern, script_text):
|
|
203
|
+
groupdict = p.groupdict()
|
|
204
|
+
type_ = ott.str2type(groupdict["type"]) if groupdict["type"] in supported_types else None
|
|
205
|
+
if type_:
|
|
206
|
+
length = groupdict["length"]
|
|
207
|
+
if length:
|
|
208
|
+
length = int(length)
|
|
209
|
+
if type_ is str and length != ott.string.DEFAULT_LENGTH:
|
|
210
|
+
type_ = ott.string[length]
|
|
211
|
+
result[groupdict["name"]] = type_
|
|
212
|
+
else:
|
|
213
|
+
warnings.warn(
|
|
214
|
+
f"{groupdict['type']} isn't supported for now, so field {groupdict['name']} won't "
|
|
215
|
+
f"be added to schema."
|
|
216
|
+
)
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def script(self: 'Source', func: Union[Callable[['Source'], Optional[bool]], str], inplace=False) -> 'Source':
|
|
221
|
+
# TODO: need to narrow Source object to get rid of undesired methods like aggregations
|
|
222
|
+
"""
|
|
223
|
+
Implements a script for every tick.
|
|
224
|
+
|
|
225
|
+
Allows to pass a ``func`` that will be applied per every tick.
|
|
226
|
+
A ``func`` can be python callable in this case it will be translated to per tick script.
|
|
227
|
+
In order to use it in Remote OTP with Ray, the function should be decorated with ``@otp.remote``,
|
|
228
|
+
see :ref:`Ray usage examples<apply-remote-context>` for details.
|
|
229
|
+
|
|
230
|
+
See :ref:`Per Tick Script Guide <python callable parser>` for more detailed description
|
|
231
|
+
of python to OneTick code translation and per-tick script features.
|
|
232
|
+
|
|
233
|
+
The script written in per tick script language can be passed itself as a string or path to a file with the code.
|
|
234
|
+
onetick-py doesn't validate the script, but configure the schema accordingly.
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
func: callable, str or path
|
|
239
|
+
- a callable that takes only one parameter - actual tick that behaves like a `Source` instance
|
|
240
|
+
|
|
241
|
+
- or the script on per-tick script language
|
|
242
|
+
|
|
243
|
+
- or a path to file with onetick script
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
:class:`Source`
|
|
248
|
+
|
|
249
|
+
See also
|
|
250
|
+
--------
|
|
251
|
+
**PER_TICK_SCRIPT** OneTick event processor
|
|
252
|
+
|
|
253
|
+
Examples
|
|
254
|
+
--------
|
|
255
|
+
>>> t = otp.Ticks({'X': [1, 2, 3], 'Y': [4, 5, 6]})
|
|
256
|
+
>>> def fun(tick):
|
|
257
|
+
... tick['Z'] = 0
|
|
258
|
+
... if tick['X'] + tick['Y'] == 5:
|
|
259
|
+
... tick['Z'] = 1
|
|
260
|
+
... elif tick['X'] + tick['Y'] * 2 == 15:
|
|
261
|
+
... tick['Z'] = 2
|
|
262
|
+
>>> t = t.script(fun)
|
|
263
|
+
>>> otp.run(t)
|
|
264
|
+
Time X Y Z
|
|
265
|
+
0 2003-12-01 00:00:00.000 1 4 1
|
|
266
|
+
1 2003-12-01 00:00:00.001 2 5 0
|
|
267
|
+
2 2003-12-01 00:00:00.002 3 6 2
|
|
268
|
+
|
|
269
|
+
See also
|
|
270
|
+
--------
|
|
271
|
+
:py:meth:`onetick.py.Source.apply`
|
|
272
|
+
:ref:`Per-Tick Script Guide <python callable parser>`
|
|
273
|
+
"""
|
|
274
|
+
res = self if inplace else self.copy()
|
|
275
|
+
changed_tick_lists = {}
|
|
276
|
+
if callable(func):
|
|
277
|
+
_new_columns, _script = apply_script(func, _EmulateObject(self))
|
|
278
|
+
changed_tick_lists = _EmulateObject.get_changed_tick_lists()
|
|
279
|
+
elif isinstance(func, str):
|
|
280
|
+
if os.path.isfile(func):
|
|
281
|
+
# path to the file with script
|
|
282
|
+
with open(func) as file:
|
|
283
|
+
_script = file.read()
|
|
284
|
+
else:
|
|
285
|
+
_script = func
|
|
286
|
+
_new_columns = _extract_columns_from_script(_script)
|
|
287
|
+
else:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
"Wrong argument was specify, please use callable or string with either script on per tick "
|
|
290
|
+
"language or path to it"
|
|
291
|
+
)
|
|
292
|
+
res.sink(otq.PerTickScript(script=_script))
|
|
293
|
+
res.schema.update(**_new_columns)
|
|
294
|
+
for tick_list_name, tick_list_schema in changed_tick_lists.items():
|
|
295
|
+
res.state_vars[tick_list_name]._schema = tick_list_schema
|
|
296
|
+
return res
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import TYPE_CHECKING, Any
|
|
3
|
+
|
|
4
|
+
from onetick import py as otp
|
|
5
|
+
from onetick.py import types as ott
|
|
6
|
+
from onetick.py.core.column_operations._methods.op_types import are_numerics, are_time
|
|
7
|
+
from onetick.py.core.column_operations._methods.op_types import are_strings
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import onetick.py as otp
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def mean(self: 'otp.Source', *columns) -> 'otp.Operation':
|
|
14
|
+
"""
|
|
15
|
+
Get average value of the specified columns.
|
|
16
|
+
|
|
17
|
+
Do not confuse it with :py:func:`otp.agg.average <onetick.py.agg.average>`,
|
|
18
|
+
this method gets average of the columns' values from single row, not doing
|
|
19
|
+
aggregation of all rows.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
columns: str, :class:`Column`
|
|
24
|
+
Columns names or columns objects.
|
|
25
|
+
All columns must have compatible types.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
:class:`~onetick.py.Operation`
|
|
30
|
+
|
|
31
|
+
Examples
|
|
32
|
+
--------
|
|
33
|
+
|
|
34
|
+
Integers and floating point values can be mixed:
|
|
35
|
+
|
|
36
|
+
>>> t = otp.Tick(A=1, B=3.3)
|
|
37
|
+
>>> t['AVG'] = t.mean('A', 'B')
|
|
38
|
+
>>> otp.run(t)
|
|
39
|
+
Time A B AVG
|
|
40
|
+
0 2003-12-01 1 3.3 2.15
|
|
41
|
+
|
|
42
|
+
You can get the average for datetime values too:
|
|
43
|
+
|
|
44
|
+
>>> t = otp.Tick(START_TIME=otp.dt(2022, 1, 1), END_TIME=otp.dt(2023, 1, 1))
|
|
45
|
+
>>> t['MID_TIME'] = t.mean('START_TIME', 'END_TIME')
|
|
46
|
+
>>> otp.run(t, timezone='GMT')
|
|
47
|
+
Time START_TIME END_TIME MID_TIME
|
|
48
|
+
0 2003-12-01 2022-01-01 2023-01-01 2022-07-02 12:00:00
|
|
49
|
+
|
|
50
|
+
Note that things may get confusing for datetimes when
|
|
51
|
+
the timezone with daylight saving time is used:
|
|
52
|
+
|
|
53
|
+
>>> t = otp.Tick(START_TIME=otp.dt(2022, 1, 1), END_TIME=otp.dt(2023, 1, 1))
|
|
54
|
+
>>> t['MID_TIME'] = t.mean('START_TIME', 'END_TIME')
|
|
55
|
+
>>> otp.run(t, timezone='EST5EDT')
|
|
56
|
+
Time START_TIME END_TIME MID_TIME
|
|
57
|
+
0 2003-12-01 2022-01-01 2023-01-01 2022-07-02 13:00:00
|
|
58
|
+
"""
|
|
59
|
+
if not columns:
|
|
60
|
+
raise ValueError('No columns were specified')
|
|
61
|
+
|
|
62
|
+
dtypes = set()
|
|
63
|
+
for column_name in map(str, columns):
|
|
64
|
+
if column_name not in self.schema:
|
|
65
|
+
raise ValueError(f"There is no '{column_name}' column in the schema")
|
|
66
|
+
dtypes.add(self.schema[column_name])
|
|
67
|
+
|
|
68
|
+
if not are_numerics(*dtypes) and not are_time(*dtypes):
|
|
69
|
+
raise ValueError(
|
|
70
|
+
'Only int, float and datetime columns are supported. Numeric and datetime columns should not be mixed.'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
op: Any = None
|
|
74
|
+
for column_name in map(str, columns):
|
|
75
|
+
column = self[column_name]
|
|
76
|
+
dtype = self.schema[column_name]
|
|
77
|
+
if dtype is otp.msectime and otp.nsectime in dtypes:
|
|
78
|
+
column = column.astype(otp.nsectime)
|
|
79
|
+
if are_time(dtype):
|
|
80
|
+
column = column.astype(int)
|
|
81
|
+
if op is None:
|
|
82
|
+
op = column
|
|
83
|
+
else:
|
|
84
|
+
op += column
|
|
85
|
+
|
|
86
|
+
op = op / len(columns)
|
|
87
|
+
|
|
88
|
+
dtype = ott.get_type_by_objects(dtypes)
|
|
89
|
+
if are_time(dtype):
|
|
90
|
+
# can't convert float to datetime, converting to int first
|
|
91
|
+
op = op.astype(int)
|
|
92
|
+
op = op.astype(dtype)
|
|
93
|
+
|
|
94
|
+
return op
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def unite_columns(self: 'otp.Source', sep="", *, apply_str=False) -> 'otp.Operation':
|
|
98
|
+
"""
|
|
99
|
+
Join values of all columns into one string
|
|
100
|
+
|
|
101
|
+
The method unite all fields to one string, just like python ``join`` method. All fields should be strings,
|
|
102
|
+
otherwise the error will be generated. To change this behavior, ``apply_str=True`` argument should be specified,
|
|
103
|
+
in this case all fields will be converted to string type before joining.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
sep: str
|
|
108
|
+
Separator between values, empty string be dafault.
|
|
109
|
+
apply_str: bool
|
|
110
|
+
If set every column will be converted to string during operation. False be default.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
result: column
|
|
115
|
+
Column with str type
|
|
116
|
+
|
|
117
|
+
Examples
|
|
118
|
+
--------
|
|
119
|
+
|
|
120
|
+
>>> # OTdirective: snippet-name: Arrange.join columns as strings;
|
|
121
|
+
>>> data = otp.Ticks(X=[1, 2, 3], A=["A", "A", "A"], B=["A", "B", "C"])
|
|
122
|
+
>>> data["S_ALL"] = data.unite_columns(sep=",", apply_str=True)
|
|
123
|
+
>>> data["S"] = data[["A", "B"]].unite_columns()
|
|
124
|
+
>>> otp.run(data)[["S", "S_ALL"]]
|
|
125
|
+
S S_ALL
|
|
126
|
+
0 AA 1,A,A
|
|
127
|
+
1 AB 2,A,B
|
|
128
|
+
2 AC 3,A,C
|
|
129
|
+
"""
|
|
130
|
+
if apply_str:
|
|
131
|
+
cols = (self[col].apply(str) for col in self.schema)
|
|
132
|
+
else:
|
|
133
|
+
not_str = [name for name, t in self.schema.items() if not are_strings(t)]
|
|
134
|
+
if not_str:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"All joining columns should be strings, while {', '.join(not_str)} "
|
|
137
|
+
f"are not. Specify `apply_str=True` for automatic type conversion"
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
cols = (self[col] for col in self.schema)
|
|
141
|
+
return functools.reduce(lambda x, y: x + sep + y, cols)
|