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.
Files changed (152) hide show
  1. locator_parser/__init__.py +0 -0
  2. locator_parser/acl.py +73 -0
  3. locator_parser/actions.py +262 -0
  4. locator_parser/common.py +368 -0
  5. locator_parser/io.py +43 -0
  6. locator_parser/locator.py +150 -0
  7. onetick/__init__.py +101 -0
  8. onetick/doc_utilities/__init__.py +3 -0
  9. onetick/doc_utilities/napoleon.py +40 -0
  10. onetick/doc_utilities/ot_doctest.py +140 -0
  11. onetick/doc_utilities/snippets.py +279 -0
  12. onetick/lib/__init__.py +4 -0
  13. onetick/lib/instance.py +141 -0
  14. onetick/py/__init__.py +293 -0
  15. onetick/py/_stack_info.py +89 -0
  16. onetick/py/_version.py +2 -0
  17. onetick/py/aggregations/__init__.py +11 -0
  18. onetick/py/aggregations/_base.py +648 -0
  19. onetick/py/aggregations/_docs.py +948 -0
  20. onetick/py/aggregations/compute.py +286 -0
  21. onetick/py/aggregations/functions.py +2216 -0
  22. onetick/py/aggregations/generic.py +104 -0
  23. onetick/py/aggregations/high_low.py +80 -0
  24. onetick/py/aggregations/num_distinct.py +83 -0
  25. onetick/py/aggregations/order_book.py +501 -0
  26. onetick/py/aggregations/other.py +1014 -0
  27. onetick/py/backports.py +26 -0
  28. onetick/py/cache.py +374 -0
  29. onetick/py/callback/__init__.py +5 -0
  30. onetick/py/callback/callback.py +276 -0
  31. onetick/py/callback/callbacks.py +131 -0
  32. onetick/py/compatibility.py +798 -0
  33. onetick/py/configuration.py +771 -0
  34. onetick/py/core/__init__.py +0 -0
  35. onetick/py/core/_csv_inspector.py +93 -0
  36. onetick/py/core/_internal/__init__.py +0 -0
  37. onetick/py/core/_internal/_manually_bound_value.py +6 -0
  38. onetick/py/core/_internal/_nodes_history.py +250 -0
  39. onetick/py/core/_internal/_op_utils/__init__.py +0 -0
  40. onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
  41. onetick/py/core/_internal/_op_utils/is_const.py +10 -0
  42. onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
  43. onetick/py/core/_internal/_proxy_node.py +140 -0
  44. onetick/py/core/_internal/_state_objects.py +2312 -0
  45. onetick/py/core/_internal/_state_vars.py +93 -0
  46. onetick/py/core/_source/__init__.py +0 -0
  47. onetick/py/core/_source/_symbol_param.py +95 -0
  48. onetick/py/core/_source/schema.py +97 -0
  49. onetick/py/core/_source/source_methods/__init__.py +0 -0
  50. onetick/py/core/_source/source_methods/aggregations.py +809 -0
  51. onetick/py/core/_source/source_methods/applyers.py +296 -0
  52. onetick/py/core/_source/source_methods/columns.py +141 -0
  53. onetick/py/core/_source/source_methods/data_quality.py +301 -0
  54. onetick/py/core/_source/source_methods/debugs.py +272 -0
  55. onetick/py/core/_source/source_methods/drops.py +120 -0
  56. onetick/py/core/_source/source_methods/fields.py +619 -0
  57. onetick/py/core/_source/source_methods/filters.py +1002 -0
  58. onetick/py/core/_source/source_methods/joins.py +1413 -0
  59. onetick/py/core/_source/source_methods/merges.py +605 -0
  60. onetick/py/core/_source/source_methods/misc.py +1455 -0
  61. onetick/py/core/_source/source_methods/pandases.py +155 -0
  62. onetick/py/core/_source/source_methods/renames.py +356 -0
  63. onetick/py/core/_source/source_methods/sorts.py +183 -0
  64. onetick/py/core/_source/source_methods/switches.py +142 -0
  65. onetick/py/core/_source/source_methods/symbols.py +117 -0
  66. onetick/py/core/_source/source_methods/times.py +627 -0
  67. onetick/py/core/_source/source_methods/writes.py +986 -0
  68. onetick/py/core/_source/symbol.py +205 -0
  69. onetick/py/core/_source/tmp_otq.py +222 -0
  70. onetick/py/core/column.py +209 -0
  71. onetick/py/core/column_operations/__init__.py +0 -0
  72. onetick/py/core/column_operations/_methods/__init__.py +4 -0
  73. onetick/py/core/column_operations/_methods/_internal.py +28 -0
  74. onetick/py/core/column_operations/_methods/conversions.py +216 -0
  75. onetick/py/core/column_operations/_methods/methods.py +292 -0
  76. onetick/py/core/column_operations/_methods/op_types.py +160 -0
  77. onetick/py/core/column_operations/accessors/__init__.py +0 -0
  78. onetick/py/core/column_operations/accessors/_accessor.py +28 -0
  79. onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
  80. onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
  81. onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
  82. onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
  83. onetick/py/core/column_operations/base.py +1121 -0
  84. onetick/py/core/cut_builder.py +150 -0
  85. onetick/py/core/db_constants.py +20 -0
  86. onetick/py/core/eval_query.py +245 -0
  87. onetick/py/core/lambda_object.py +441 -0
  88. onetick/py/core/multi_output_source.py +232 -0
  89. onetick/py/core/per_tick_script.py +2256 -0
  90. onetick/py/core/query_inspector.py +464 -0
  91. onetick/py/core/source.py +1744 -0
  92. onetick/py/db/__init__.py +2 -0
  93. onetick/py/db/_inspection.py +1128 -0
  94. onetick/py/db/db.py +1327 -0
  95. onetick/py/db/utils.py +64 -0
  96. onetick/py/docs/__init__.py +0 -0
  97. onetick/py/docs/docstring_parser.py +112 -0
  98. onetick/py/docs/utils.py +81 -0
  99. onetick/py/functions.py +2398 -0
  100. onetick/py/license.py +190 -0
  101. onetick/py/log.py +88 -0
  102. onetick/py/math.py +935 -0
  103. onetick/py/misc.py +470 -0
  104. onetick/py/oqd/__init__.py +22 -0
  105. onetick/py/oqd/eps.py +1195 -0
  106. onetick/py/oqd/sources.py +325 -0
  107. onetick/py/otq.py +216 -0
  108. onetick/py/pyomd_mock.py +47 -0
  109. onetick/py/run.py +916 -0
  110. onetick/py/servers.py +173 -0
  111. onetick/py/session.py +1347 -0
  112. onetick/py/sources/__init__.py +19 -0
  113. onetick/py/sources/cache.py +167 -0
  114. onetick/py/sources/common.py +128 -0
  115. onetick/py/sources/csv.py +642 -0
  116. onetick/py/sources/custom.py +85 -0
  117. onetick/py/sources/data_file.py +305 -0
  118. onetick/py/sources/data_source.py +1045 -0
  119. onetick/py/sources/empty.py +94 -0
  120. onetick/py/sources/odbc.py +337 -0
  121. onetick/py/sources/order_book.py +271 -0
  122. onetick/py/sources/parquet.py +168 -0
  123. onetick/py/sources/pit.py +191 -0
  124. onetick/py/sources/query.py +495 -0
  125. onetick/py/sources/snapshots.py +419 -0
  126. onetick/py/sources/split_query_output_by_symbol.py +198 -0
  127. onetick/py/sources/symbology_mapping.py +123 -0
  128. onetick/py/sources/symbols.py +374 -0
  129. onetick/py/sources/ticks.py +825 -0
  130. onetick/py/sql.py +70 -0
  131. onetick/py/state.py +251 -0
  132. onetick/py/types.py +2131 -0
  133. onetick/py/utils/__init__.py +70 -0
  134. onetick/py/utils/acl.py +93 -0
  135. onetick/py/utils/config.py +186 -0
  136. onetick/py/utils/default.py +49 -0
  137. onetick/py/utils/file.py +38 -0
  138. onetick/py/utils/helpers.py +76 -0
  139. onetick/py/utils/locator.py +94 -0
  140. onetick/py/utils/perf.py +498 -0
  141. onetick/py/utils/query.py +49 -0
  142. onetick/py/utils/render.py +1374 -0
  143. onetick/py/utils/script.py +244 -0
  144. onetick/py/utils/temp.py +471 -0
  145. onetick/py/utils/types.py +120 -0
  146. onetick/py/utils/tz.py +84 -0
  147. onetick_py-1.177.0.dist-info/METADATA +137 -0
  148. onetick_py-1.177.0.dist-info/RECORD +152 -0
  149. onetick_py-1.177.0.dist-info/WHEEL +5 -0
  150. onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
  151. onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
  152. 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)