opencode-pyneruntime 6.6.4__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 (261) hide show
  1. opencode_pyneruntime-6.6.4.dist-info/METADATA +281 -0
  2. opencode_pyneruntime-6.6.4.dist-info/RECORD +261 -0
  3. opencode_pyneruntime-6.6.4.dist-info/WHEEL +5 -0
  4. opencode_pyneruntime-6.6.4.dist-info/entry_points.txt +6 -0
  5. opencode_pyneruntime-6.6.4.dist-info/licenses/LICENSE +201 -0
  6. opencode_pyneruntime-6.6.4.dist-info/licenses/NOTICE +21 -0
  7. opencode_pyneruntime-6.6.4.dist-info/top_level.txt +1 -0
  8. pynecore/__init__.py +6 -0
  9. pynecore/cli/__init__.py +2 -0
  10. pynecore/cli/app.py +238 -0
  11. pynecore/cli/commands/__init__.py +343 -0
  12. pynecore/cli/commands/benchmark.py +186 -0
  13. pynecore/cli/commands/compile.py +198 -0
  14. pynecore/cli/commands/data.py +857 -0
  15. pynecore/cli/commands/debug.py +63 -0
  16. pynecore/cli/commands/optimize.py +956 -0
  17. pynecore/cli/commands/plugin.py +242 -0
  18. pynecore/cli/commands/run.py +2006 -0
  19. pynecore/cli/pluggable.py +132 -0
  20. pynecore/cli/utils/__init__.py +0 -0
  21. pynecore/cli/utils/api_error_handler.py +168 -0
  22. pynecore/cli/utils/broker_picker.py +330 -0
  23. pynecore/cli/utils/error_hook.py +28 -0
  24. pynecore/cli/utils/keyreader.py +178 -0
  25. pynecore/cli/utils/provider_picker.py +19 -0
  26. pynecore/cli/utils/symbol_browser.py +1149 -0
  27. pynecore/core/__init__.py +0 -0
  28. pynecore/core/aggregator.py +257 -0
  29. pynecore/core/bar_magnifier.py +168 -0
  30. pynecore/core/broker/__init__.py +64 -0
  31. pynecore/core/broker/defaults.py +113 -0
  32. pynecore/core/broker/disappearance.py +927 -0
  33. pynecore/core/broker/emulator.py +345 -0
  34. pynecore/core/broker/exceptions.py +346 -0
  35. pynecore/core/broker/idempotency.py +401 -0
  36. pynecore/core/broker/intent_builder.py +334 -0
  37. pynecore/core/broker/journal.py +1785 -0
  38. pynecore/core/broker/models.py +1600 -0
  39. pynecore/core/broker/native_failsafe_manager.py +1436 -0
  40. pynecore/core/broker/one_way_emulator.py +1128 -0
  41. pynecore/core/broker/position.py +787 -0
  42. pynecore/core/broker/run_identity.py +126 -0
  43. pynecore/core/broker/software_entry_stop_engine.py +351 -0
  44. pynecore/core/broker/software_partial_bracket_engine.py +1379 -0
  45. pynecore/core/broker/spot_inventory.py +1327 -0
  46. pynecore/core/broker/storage.py +2655 -0
  47. pynecore/core/broker/store_helpers.py +2161 -0
  48. pynecore/core/broker/sync_engine.py +16070 -0
  49. pynecore/core/broker/validation.py +382 -0
  50. pynecore/core/class_property.py +7 -0
  51. pynecore/core/config.py +392 -0
  52. pynecore/core/csv_file.py +547 -0
  53. pynecore/core/currency.py +262 -0
  54. pynecore/core/data_converter.py +1002 -0
  55. pynecore/core/datetime.py +296 -0
  56. pynecore/core/download_info.py +71 -0
  57. pynecore/core/download_runner.py +274 -0
  58. pynecore/core/htf_aggregator.py +181 -0
  59. pynecore/core/import_hook.py +358 -0
  60. pynecore/core/instance_state.py +494 -0
  61. pynecore/core/live_ltf_collector.py +442 -0
  62. pynecore/core/live_ltf_window.py +189 -0
  63. pynecore/core/live_runner.py +1347 -0
  64. pynecore/core/module_property.py +26 -0
  65. pynecore/core/ohlcv_file.py +1888 -0
  66. pynecore/core/overload.py +371 -0
  67. pynecore/core/pine_cast.py +113 -0
  68. pynecore/core/pine_export.py +95 -0
  69. pynecore/core/pine_method.py +244 -0
  70. pynecore/core/pine_range.py +86 -0
  71. pynecore/core/pine_udt.py +69 -0
  72. pynecore/core/plugin/__init__.py +394 -0
  73. pynecore/core/plugin/broker.py +781 -0
  74. pynecore/core/plugin/cli.py +96 -0
  75. pynecore/core/plugin/live_provider.py +208 -0
  76. pynecore/core/plugin/provider.py +331 -0
  77. pynecore/core/provider_string.py +148 -0
  78. pynecore/core/random.py +40 -0
  79. pynecore/core/resampler.py +686 -0
  80. pynecore/core/safe_convert.py +64 -0
  81. pynecore/core/script.py +1011 -0
  82. pynecore/core/script_runner.py +3202 -0
  83. pynecore/core/security.py +1749 -0
  84. pynecore/core/security_process.py +1253 -0
  85. pynecore/core/security_shm.py +456 -0
  86. pynecore/core/series.py +417 -0
  87. pynecore/core/strategy_stats.py +669 -0
  88. pynecore/core/symbol_map.py +134 -0
  89. pynecore/core/syminfo.py +505 -0
  90. pynecore/core/viz.py +591 -0
  91. pynecore/lib/__init__.py +1771 -0
  92. pynecore/lib/_fixnan.py +32 -0
  93. pynecore/lib/_math_stateful.py +202 -0
  94. pynecore/lib/_timeframe_change.py +101 -0
  95. pynecore/lib/adjustment.py +6 -0
  96. pynecore/lib/alert.py +39 -0
  97. pynecore/lib/alert.pyi +14 -0
  98. pynecore/lib/array.py +1051 -0
  99. pynecore/lib/barmerge.py +60 -0
  100. pynecore/lib/barstate.py +30 -0
  101. pynecore/lib/box.py +415 -0
  102. pynecore/lib/chart.py +128 -0
  103. pynecore/lib/color.py +152 -0
  104. pynecore/lib/color.pyi +50 -0
  105. pynecore/lib/currency.py +62 -0
  106. pynecore/lib/dayofweek.py +36 -0
  107. pynecore/lib/dayofweek.pyi +18 -0
  108. pynecore/lib/display.py +8 -0
  109. pynecore/lib/dividends.py +9 -0
  110. pynecore/lib/earnings.py +11 -0
  111. pynecore/lib/extend.py +6 -0
  112. pynecore/lib/font.py +5 -0
  113. pynecore/lib/footprint.py +79 -0
  114. pynecore/lib/format.py +11 -0
  115. pynecore/lib/hline.py +67 -0
  116. pynecore/lib/hline.pyi +24 -0
  117. pynecore/lib/label.py +409 -0
  118. pynecore/lib/line.py +433 -0
  119. pynecore/lib/linefill.py +93 -0
  120. pynecore/lib/location.py +11 -0
  121. pynecore/lib/log.py +362 -0
  122. pynecore/lib/map.py +150 -0
  123. pynecore/lib/math.py +385 -0
  124. pynecore/lib/matrix.py +708 -0
  125. pynecore/lib/order.py +8 -0
  126. pynecore/lib/pivotpointtype.py +8 -0
  127. pynecore/lib/plot.py +95 -0
  128. pynecore/lib/plot.pyi +33 -0
  129. pynecore/lib/polyline.py +91 -0
  130. pynecore/lib/position.py +15 -0
  131. pynecore/lib/request.py +281 -0
  132. pynecore/lib/runtime.py +5 -0
  133. pynecore/lib/scale.py +9 -0
  134. pynecore/lib/session.py +267 -0
  135. pynecore/lib/session.pyi +12 -0
  136. pynecore/lib/shape.py +18 -0
  137. pynecore/lib/size.py +12 -0
  138. pynecore/lib/splits.py +4 -0
  139. pynecore/lib/strategy/__init__.py +4778 -0
  140. pynecore/lib/strategy/closedtrades.py +347 -0
  141. pynecore/lib/strategy/closedtrades.pyi +53 -0
  142. pynecore/lib/strategy/commission.py +9 -0
  143. pynecore/lib/strategy/direction.py +9 -0
  144. pynecore/lib/strategy/oca.py +13 -0
  145. pynecore/lib/strategy/opentrades.py +281 -0
  146. pynecore/lib/strategy/opentrades.pyi +49 -0
  147. pynecore/lib/strategy/risk.py +109 -0
  148. pynecore/lib/string.py +649 -0
  149. pynecore/lib/syminfo.py +84 -0
  150. pynecore/lib/ta.py +2230 -0
  151. pynecore/lib/table.py +290 -0
  152. pynecore/lib/text.py +17 -0
  153. pynecore/lib/ticker.py +207 -0
  154. pynecore/lib/timeframe.py +293 -0
  155. pynecore/lib/volume_row.py +67 -0
  156. pynecore/lib/xloc.py +4 -0
  157. pynecore/lib/yloc.py +5 -0
  158. pynecore/providers/__init__.py +0 -0
  159. pynecore/providers/ccxt.py +664 -0
  160. pynecore/providers/replay.py +187 -0
  161. pynecore/pynesys/__init__.py +0 -0
  162. pynecore/pynesys/api.py +498 -0
  163. pynecore/pynesys/compiler.py +112 -0
  164. pynecore/standalone.py +99 -0
  165. pynecore/testing/__init__.py +1 -0
  166. pynecore/testing/broker_lab/__init__.py +41 -0
  167. pynecore/testing/broker_lab/__main__.py +5 -0
  168. pynecore/testing/broker_lab/cli.py +87 -0
  169. pynecore/testing/broker_lab/generate.py +47 -0
  170. pynecore/testing/broker_lab/model.py +84 -0
  171. pynecore/testing/broker_lab/reference.py +645 -0
  172. pynecore/testing/broker_lab/runner.py +372 -0
  173. pynecore/testing/broker_lab/scheduler.py +50 -0
  174. pynecore/testing/broker_lab/subprocess.py +73 -0
  175. pynecore/transformers/__init__.py +0 -0
  176. pynecore/transformers/builtin_shadow.py +136 -0
  177. pynecore/transformers/closure_arguments_transformer.py +428 -0
  178. pynecore/transformers/display_rewrite.py +140 -0
  179. pynecore/transformers/dynamic_default.py +147 -0
  180. pynecore/transformers/function_isolation.py +757 -0
  181. pynecore/transformers/import_lifter.py +61 -0
  182. pynecore/transformers/import_normalizer.py +328 -0
  183. pynecore/transformers/inline_series_hoist.py +178 -0
  184. pynecore/transformers/input_transformer.py +175 -0
  185. pynecore/transformers/lib_series.py +201 -0
  186. pynecore/transformers/locations.py +70 -0
  187. pynecore/transformers/module_properties.json +3387 -0
  188. pynecore/transformers/module_property.py +221 -0
  189. pynecore/transformers/ne_guard.py +70 -0
  190. pynecore/transformers/persistent.py +320 -0
  191. pynecore/transformers/persistent_series.py +76 -0
  192. pynecore/transformers/safe_convert_transformer.py +97 -0
  193. pynecore/transformers/safe_division_transformer.py +95 -0
  194. pynecore/transformers/script_requirements.py +308 -0
  195. pynecore/transformers/security.py +752 -0
  196. pynecore/transformers/security_instantiation.py +274 -0
  197. pynecore/transformers/series.py +275 -0
  198. pynecore/transformers/slot_layout.py +381 -0
  199. pynecore/transformers/type_checking_stripper.py +25 -0
  200. pynecore/transformers/unused_series_detector.py +267 -0
  201. pynecore/types/__init__.py +21 -0
  202. pynecore/types/alert.py +5 -0
  203. pynecore/types/barmerge.py +5 -0
  204. pynecore/types/base.py +39 -0
  205. pynecore/types/box.py +37 -0
  206. pynecore/types/chart.py +17 -0
  207. pynecore/types/color.py +107 -0
  208. pynecore/types/currency.py +5 -0
  209. pynecore/types/datetime.py +6 -0
  210. pynecore/types/display.py +5 -0
  211. pynecore/types/dividends.py +5 -0
  212. pynecore/types/earnings.py +5 -0
  213. pynecore/types/extend.py +5 -0
  214. pynecore/types/font.py +5 -0
  215. pynecore/types/footprint.py +41 -0
  216. pynecore/types/format.py +5 -0
  217. pynecore/types/hline.py +24 -0
  218. pynecore/types/ib_persistent.py +8 -0
  219. pynecore/types/ib_persistent.pyi +10 -0
  220. pynecore/types/label.py +35 -0
  221. pynecore/types/line.py +32 -0
  222. pynecore/types/linefill.py +13 -0
  223. pynecore/types/location.py +5 -0
  224. pynecore/types/matrix.py +999 -0
  225. pynecore/types/na.py +237 -0
  226. pynecore/types/na.pyi +83 -0
  227. pynecore/types/ohlcv.py +12 -0
  228. pynecore/types/order.py +5 -0
  229. pynecore/types/persistent.py +8 -0
  230. pynecore/types/persistent.pyi +13 -0
  231. pynecore/types/pine_types.py +11 -0
  232. pynecore/types/pine_types.pyi +15 -0
  233. pynecore/types/pivotpointtype.py +5 -0
  234. pynecore/types/plot.py +12 -0
  235. pynecore/types/plot_meta.py +60 -0
  236. pynecore/types/polyline.py +40 -0
  237. pynecore/types/position.py +5 -0
  238. pynecore/types/scale.py +5 -0
  239. pynecore/types/script_type.py +15 -0
  240. pynecore/types/series.py +23 -0
  241. pynecore/types/series.pyi +19 -0
  242. pynecore/types/session.py +35 -0
  243. pynecore/types/shape.py +5 -0
  244. pynecore/types/size.py +5 -0
  245. pynecore/types/source.py +33 -0
  246. pynecore/types/splits.py +5 -0
  247. pynecore/types/strategy.py +45 -0
  248. pynecore/types/table.py +87 -0
  249. pynecore/types/text.py +13 -0
  250. pynecore/types/type_checker.py +7 -0
  251. pynecore/types/type_checker.pyi +48 -0
  252. pynecore/types/volume_row.py +36 -0
  253. pynecore/types/weekdays.py +11 -0
  254. pynecore/types/xloc.py +5 -0
  255. pynecore/types/yloc.py +5 -0
  256. pynecore/utils/__init__.py +0 -0
  257. pynecore/utils/file_utils.py +50 -0
  258. pynecore/utils/rich/__init__.py +0 -0
  259. pynecore/utils/rich/date_column.py +25 -0
  260. pynecore/utils/sequence_view.py +92 -0
  261. pynecore/utils/stdlib_checker.py +17 -0
@@ -0,0 +1,999 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TypeVar, Generic, Any, cast
4
+ import copy
5
+ from collections import Counter
6
+ from .na import NA, na_float
7
+
8
+ T = TypeVar('T')
9
+
10
+
11
+ class Matrix(Generic[T]):
12
+ """
13
+ A matrix implementation in pure Python
14
+ """
15
+ data: list[list[Any]]
16
+
17
+ def __init__(self, rows: int = 0, cols: int = 0, initial_value: T = NA(T)):
18
+ self.rows = rows
19
+ self.cols = cols
20
+ self.data = [[initial_value for _ in range(cols)] for _ in range(rows)]
21
+
22
+ def __iter__(self):
23
+ """Iterate over the matrix rows (Pine `for row in myMatrix`), each row
24
+ being the underlying list of that row's values."""
25
+ return iter(self.data)
26
+
27
+ def __len__(self) -> int:
28
+ """The number of rows (so len(matrix) mirrors matrix.rows())."""
29
+ return len(self.data)
30
+
31
+ @property
32
+ def rows(self) -> int:
33
+ """Return the number of rows in the matrix."""
34
+ return self._rows
35
+
36
+ @rows.setter
37
+ def rows(self, value: int) -> None:
38
+ self._rows = value
39
+
40
+ def add_row(self, row: int | None = None, array_id: list[Any] | None = None) -> None:
41
+ """
42
+ Add a row at the specified index of the matrix.
43
+
44
+ The row can consist of NA values, or an array can be used to provide values.
45
+
46
+ :param row: The index where the new row will be inserted. If None, appends to the end.
47
+ :param array_id: Array to use for providing values to the new row. If shorter than matrix
48
+ width, remaining cells are filled with NA. If longer, array is truncated.
49
+ If matrix is empty, the array size determines the column count.
50
+ :raises IndexError: If row index is out of bounds.
51
+ """
52
+ row_idx: int = self.rows if row is None else row
53
+
54
+ if row_idx < 0 or row_idx > self.rows:
55
+ raise IndexError(f"Row index {row_idx} out of bounds for matrix with {self.rows} rows")
56
+
57
+ if array_id is not None:
58
+ # If matrix is empty (0 columns), use array size to set column count
59
+ if self.cols == 0 and len(array_id) > 0:
60
+ self.cols = len(array_id)
61
+
62
+ new_row = []
63
+ for i in range(self.cols):
64
+ if i < len(array_id):
65
+ new_row.append(array_id[i])
66
+ else:
67
+ new_row.append(NA(T))
68
+ else:
69
+ new_row = [NA(T) for _ in range(self.cols)]
70
+
71
+ self.data.insert(row_idx, new_row)
72
+ self.rows += 1
73
+
74
+ def avg(self) -> float | int | NA[float]:
75
+ """
76
+ Calculate the average of all elements in the matrix.
77
+
78
+ :return: The average value of all non-NA elements in the matrix.
79
+ :rtype: Union[float, int]
80
+ """
81
+ total = 0
82
+ count = 0
83
+ for row in self.data:
84
+ for val in row:
85
+ if not (isinstance(val, NA) or val != val):
86
+ total += val
87
+ count += 1
88
+ return total / count if count > 0 else na_float
89
+
90
+ def col(self, column: int) -> list[T]:
91
+ """
92
+ Create a one-dimensional array from the elements of a matrix column.
93
+
94
+ :param column: Index of the required column.
95
+ :return: An array containing the column values.
96
+ :raises IndexError: If column index is out of bounds.
97
+ """
98
+ if column < 0 or column >= self.cols:
99
+ raise IndexError(f"Column index {column} out of bounds")
100
+ return [self.data[row][column] for row in range(self.rows)]
101
+
102
+ def columns(self) -> int:
103
+ """
104
+ Return the number of columns in the matrix.
105
+
106
+ :return: The number of columns.
107
+ """
108
+ return self.cols
109
+
110
+ def concat(self, other: Matrix) -> Matrix:
111
+ """
112
+ Append another matrix to this matrix.
113
+
114
+ :param other: Matrix object whose elements will be appended.
115
+ :return: This matrix after concatenation.
116
+ :raises ValueError: If matrices don't have the same number of columns.
117
+ """
118
+ if self.cols != other.cols:
119
+ raise ValueError("Matrices must have the same number of columns")
120
+
121
+ # Append all rows from other matrix
122
+ for row in other.data:
123
+ self.data.append(row[:])
124
+ self.rows += other.rows
125
+ return self
126
+
127
+ def copy(self) -> Matrix:
128
+ """
129
+ Create a new matrix which is a copy of the original.
130
+
131
+ :return: A new matrix object containing a deep copy of this matrix.
132
+ """
133
+ new_matrix = Matrix(self.rows, self.cols)
134
+ new_matrix.data = copy.deepcopy(self.data)
135
+ return new_matrix
136
+
137
+ def det(self) -> float | int:
138
+ """
139
+ Return the determinant of a square matrix.
140
+
141
+ :return: The determinant value of the matrix.
142
+ :raises ValueError: If matrix is not square.
143
+ """
144
+ if self.rows != self.cols:
145
+ raise ValueError("Determinant can only be calculated for square matrices")
146
+
147
+ if self.rows == 0:
148
+ return 1
149
+ if self.rows == 1:
150
+ return self.data[0][0]
151
+ if self.rows == 2:
152
+ return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0]
153
+
154
+ # For larger matrices, use LU decomposition
155
+ return self._lu_determinant()
156
+
157
+ def diff(self, other: Matrix | int | float) -> Matrix:
158
+ """
159
+ Return a new matrix resulting from subtraction.
160
+
161
+ :param other: Matrix object or scalar value to be subtracted.
162
+ :return: A new matrix containing the difference.
163
+ :raises ValueError: If matrix dimensions don't match.
164
+ """
165
+ result = Matrix(self.rows, self.cols)
166
+
167
+ if isinstance(other, Matrix):
168
+ if self.rows != other.rows or self.cols != other.cols:
169
+ raise ValueError("Matrices must have same dimensions")
170
+ for i in range(self.rows):
171
+ for j in range(self.cols):
172
+ result.data[i][j] = self.data[i][j] - other.data[i][j]
173
+ else:
174
+ # Scalar subtraction
175
+ for i in range(self.rows):
176
+ for j in range(self.cols):
177
+ result.data[i][j] = self.data[i][j] - other
178
+
179
+ return result
180
+
181
+ def elements_count(self) -> int:
182
+ """
183
+ Return the total number of all matrix elements.
184
+
185
+ :return: The total number of elements (rows * columns).
186
+ """
187
+ return self.rows * self.cols
188
+
189
+ def fill(self, value: T, from_row: int = 0, to_row: int | None = None,
190
+ from_column: int = 0, to_column: int | None = None) -> None:
191
+ """
192
+ Fill a rectangular area of the matrix with the specified value.
193
+
194
+ :param value: The value to fill with.
195
+ :param from_row: Row index from which the fill will begin (inclusive).
196
+ :param to_row: Row index where the fill will end (exclusive). If None, fills to end.
197
+ :param from_column: Column index from which the fill will begin (inclusive).
198
+ :param to_column: Column index where the fill will end (exclusive). If None, fills to end.
199
+ """
200
+ _to_row: int = self.rows if to_row is None else to_row
201
+ _to_col: int = self.cols if to_column is None else to_column
202
+
203
+ for i in range(from_row, _to_row):
204
+ for j in range(from_column, _to_col):
205
+ if 0 <= i < self.rows and 0 <= j < self.cols:
206
+ self.data[i][j] = value
207
+
208
+ def get(self, row: int, column: int) -> T:
209
+ """
210
+ Return the element with the specified index of the matrix.
211
+
212
+ :param row: Index of the required row.
213
+ :param column: Index of the required column.
214
+ :return: The value at the specified position.
215
+ :raises IndexError: If indices are out of bounds.
216
+ """
217
+ if row < 0 or row >= self.rows or column < 0 or column >= self.cols:
218
+ raise IndexError(f"Index ({row}, {column}) out of bounds")
219
+ return self.data[row][column]
220
+
221
+ def set(self, row: int, column: int, value: T) -> None:
222
+ """
223
+ Assign value to the element at the specified row and column.
224
+
225
+ :param row: The row index of the element to be modified.
226
+ :param column: The column index of the element to be modified.
227
+ :param value: The new value to be set.
228
+ :raises IndexError: If indices are out of bounds.
229
+ """
230
+ if row < 0 or row >= self.rows or column < 0 or column >= self.cols:
231
+ raise IndexError(f"Index ({row}, {column}) out of bounds")
232
+ self.data[row][column] = value
233
+
234
+ def max(self) -> float | int | NA[float]:
235
+ """
236
+ Return the largest value from the matrix elements.
237
+
238
+ :return: The maximum value from the matrix.
239
+ """
240
+ max_val: float | int | None = None
241
+ for row in self.data:
242
+ for val in row:
243
+ if not (isinstance(val, NA) or val != val):
244
+ if max_val is None or val > max_val:
245
+ max_val = val
246
+ return max_val if max_val is not None else na_float
247
+
248
+ def min(self) -> float | int | NA[float]:
249
+ """
250
+ Return the smallest value from the matrix elements.
251
+
252
+ :return: The minimum value from the matrix.
253
+ """
254
+ min_val: float | int | None = None
255
+ for row in self.data:
256
+ for val in row:
257
+ if not (isinstance(val, NA) or val != val):
258
+ if min_val is None or val < min_val:
259
+ min_val = val
260
+ return min_val if min_val is not None else na_float
261
+
262
+ def median(self) -> float | int | NA[float]:
263
+ """
264
+ Calculate the median ("middle" value) of matrix elements.
265
+
266
+ :return: The median value of all non-NA elements.
267
+ """
268
+ values = []
269
+ for row in self.data:
270
+ for val in row:
271
+ if not (isinstance(val, NA) or val != val):
272
+ values.append(val)
273
+
274
+ if not values:
275
+ return na_float
276
+
277
+ values.sort()
278
+ n = len(values)
279
+ if n % 2 == 0:
280
+ return (values[n // 2 - 1] + values[n // 2]) / 2
281
+ else:
282
+ return values[n // 2]
283
+
284
+ def mode(self) -> float | int | NA[float]:
285
+ """
286
+ Calculate the mode of the matrix.
287
+
288
+ Returns the most frequently occurring value. When there are multiple
289
+ values occurring equally frequently, returns the smallest.
290
+
291
+ :return: The most frequently occurring value from the matrix.
292
+ """
293
+ values = []
294
+ for row in self.data:
295
+ for val in row:
296
+ if not (isinstance(val, NA) or val != val):
297
+ values.append(val)
298
+
299
+ if not values:
300
+ return na_float
301
+
302
+ counter = Counter(values)
303
+ if not counter:
304
+ return na_float
305
+
306
+ # Get all values with max count
307
+ max_count = max(counter.values())
308
+ modes = [val for val, count in counter.items() if count == max_count]
309
+
310
+ # Return smallest if multiple modes
311
+ return min(modes)
312
+
313
+ def mult(self, other: Matrix | list[T] | int | float) -> Matrix | list[T]:
314
+ """
315
+ Return the product of matrices, matrix and vector, or matrix and scalar.
316
+
317
+ :param other: Second matrix object, array, or scalar value.
318
+ :return: A new matrix (matrix multiplication) or array (vector multiplication).
319
+ :raises ValueError: If dimensions are incompatible for multiplication.
320
+ """
321
+ if isinstance(other, Matrix):
322
+ # Matrix multiplication
323
+ if self.cols != other.rows:
324
+ raise ValueError("Invalid dimensions for matrix multiplication")
325
+
326
+ result = Matrix(self.rows, other.cols)
327
+ for i in range(self.rows):
328
+ for j in range(other.cols):
329
+ sum_val = 0
330
+ for k in range(self.cols):
331
+ sum_val += self.data[i][k] * other.data[k][j]
332
+ result.data[i][j] = sum_val
333
+ return result
334
+
335
+ elif isinstance(other, list):
336
+ # Matrix-vector multiplication
337
+ if self.cols != len(other):
338
+ raise ValueError("Invalid dimensions for matrix-vector multiplication")
339
+
340
+ result = []
341
+ for i in range(self.rows):
342
+ sum_val = 0
343
+ for j in range(self.cols):
344
+ sum_val += self.data[i][j] * other[j]
345
+ result.append(sum_val)
346
+ return result
347
+
348
+ else:
349
+ # Scalar multiplication
350
+ result = Matrix(self.rows, self.cols)
351
+ for i in range(self.rows):
352
+ for j in range(self.cols):
353
+ result.data[i][j] = self.data[i][j] * other
354
+ return result
355
+
356
+ def remove_col(self, column: int | None = None) -> list[T]:
357
+ """
358
+ Remove the column at the specified index and return its values.
359
+
360
+ :param column: The index of the column to be removed. If None, removes last column.
361
+ :return: An array containing the removed column's values.
362
+ :raises IndexError: If column index is out of bounds.
363
+ """
364
+ col_idx: int = (self.cols - 1) if column is None else column
365
+
366
+ if col_idx < 0 or col_idx >= self.cols:
367
+ raise IndexError(f"Column index {col_idx} out of bounds")
368
+
369
+ removed = []
370
+ for row in self.data:
371
+ removed.append(row.pop(col_idx))
372
+ self.cols -= 1
373
+ return removed
374
+
375
+ def remove_row(self, row: int | None = None) -> list[T]:
376
+ """
377
+ Remove the row at the specified index and return its values.
378
+
379
+ :param row: The index of the row to be removed. If None, removes last row.
380
+ :return: An array containing the removed row's values.
381
+ :raises IndexError: If row index is out of bounds.
382
+ """
383
+ row_idx: int = (self.rows - 1) if row is None else row
384
+
385
+ if row_idx < 0 or row_idx >= self.rows:
386
+ raise IndexError(f"Row index {row_idx} out of bounds")
387
+
388
+ removed = self.data.pop(row_idx)
389
+ self.rows -= 1
390
+ return removed
391
+
392
+ def reshape(self, rows: int, columns: int) -> None:
393
+ """
394
+ Rebuild the matrix to the specified dimensions.
395
+
396
+ :param rows: The number of rows of the reshaped matrix.
397
+ :param columns: The number of columns of the reshaped matrix.
398
+ :raises ValueError: If new shape doesn't have same number of elements.
399
+ """
400
+ if rows * columns != self.rows * self.cols:
401
+ raise ValueError("New shape must have same number of elements")
402
+
403
+ # Flatten to 1D
404
+ flat = []
405
+ for row in self.data:
406
+ flat.extend(row)
407
+
408
+ # Reshape to new dimensions
409
+ self.data = []
410
+ for i in range(rows):
411
+ self.data.append(flat[i * columns:(i + 1) * columns])
412
+
413
+ self.rows = rows
414
+ self.cols = columns
415
+
416
+ def reverse(self) -> None:
417
+ """
418
+ Reverse the order of rows and columns in the matrix.
419
+
420
+ The first row and first column become the last, and the last become the first.
421
+ """
422
+ # Reverse rows
423
+ self.data.reverse()
424
+ # Reverse each row
425
+ for row in self.data:
426
+ row.reverse()
427
+
428
+ def row(self, row: int) -> list[T]:
429
+ """
430
+ Create a one-dimensional array from the elements of a matrix row.
431
+
432
+ :param row: Index of the required row.
433
+ :return: An array containing the row values.
434
+ :raises IndexError: If row index is out of bounds.
435
+ """
436
+ if row < 0 or row >= self.rows:
437
+ raise IndexError(f"Row index {row} out of bounds")
438
+ return self.data[row][:]
439
+
440
+ def sort(self, column: int = 0, order: str = 'ascending') -> None:
441
+ """
442
+ Rearrange rows following the sorted order of values in the specified column.
443
+
444
+ :param column: Index of the column whose sorted values determine the new order of rows.
445
+ :param order: The sort order. 'ascending' (default) or 'descending'.
446
+ :raises IndexError: If column index is out of bounds.
447
+ """
448
+ if column < 0 or column >= self.cols:
449
+ raise IndexError(f"Column index {column} out of bounds")
450
+
451
+ reverse = order == 'descending'
452
+ self.data.sort(key=lambda row: row[column], reverse=reverse)
453
+
454
+ def submatrix(self, from_row: int = 0, to_row: int | None = None,
455
+ from_column: int = 0, to_column: int | None = None) -> Matrix:
456
+ """
457
+ Extract a submatrix within the specified indices.
458
+
459
+ :param from_row: Row index from which extraction begins (inclusive).
460
+ :param to_row: Row index where extraction ends (exclusive). If None, extracts to end.
461
+ :param from_column: Column index from which extraction begins (inclusive).
462
+ :param to_column: Column index where extraction ends (exclusive). If None, extracts to end.
463
+ :return: A new matrix containing the submatrix.
464
+ """
465
+ _to_row: int = self.rows if to_row is None else to_row
466
+ _to_col: int = self.cols if to_column is None else to_column
467
+
468
+ result = Matrix(_to_row - from_row, _to_col - from_column)
469
+ for i in range(from_row, _to_row):
470
+ for j in range(from_column, _to_col):
471
+ if 0 <= i < self.rows and 0 <= j < self.cols:
472
+ result.data[i - from_row][j - from_column] = self.data[i][j]
473
+
474
+ return result
475
+
476
+ def sum(self, other: Matrix | int | float) -> Matrix:
477
+ """
478
+ Return a new matrix resulting from addition.
479
+
480
+ :param other: Second matrix object or scalar value.
481
+ :return: A new matrix containing the sum.
482
+ :raises ValueError: If matrix dimensions don't match.
483
+ """
484
+ result = Matrix(self.rows, self.cols)
485
+
486
+ if isinstance(other, Matrix):
487
+ if self.rows != other.rows or self.cols != other.cols:
488
+ raise ValueError("Matrices must have same dimensions")
489
+ for i in range(self.rows):
490
+ for j in range(self.cols):
491
+ result.data[i][j] = self.data[i][j] + other.data[i][j]
492
+ else:
493
+ # Scalar addition
494
+ for i in range(self.rows):
495
+ for j in range(self.cols):
496
+ result.data[i][j] = self.data[i][j] + other
497
+
498
+ return result
499
+
500
+ def swap_columns(self, column1: int, column2: int) -> None:
501
+ """
502
+ Swap the columns at the specified indices.
503
+
504
+ :param column1: Index of the first column to be swapped.
505
+ :param column2: Index of the second column to be swapped.
506
+ :raises IndexError: If column indices are out of bounds.
507
+ """
508
+ if column1 < 0 or column1 >= self.cols or column2 < 0 or column2 >= self.cols:
509
+ raise IndexError("Column index out of bounds")
510
+
511
+ for row in self.data:
512
+ row[column1], row[column2] = row[column2], row[column1]
513
+
514
+ def swap_rows(self, row1: int, row2: int) -> None:
515
+ """
516
+ Swap the rows at the specified indices.
517
+
518
+ :param row1: Index of the first row to be swapped.
519
+ :param row2: Index of the second row to be swapped.
520
+ :raises IndexError: If row indices are out of bounds.
521
+ """
522
+ if row1 < 0 or row1 >= self.rows or row2 < 0 or row2 >= self.rows:
523
+ raise IndexError("Row index out of bounds")
524
+
525
+ self.data[row1], self.data[row2] = self.data[row2], self.data[row1]
526
+
527
+ def trace(self) -> float | int:
528
+ """
529
+ Calculate the trace of a matrix (sum of the main diagonal's elements).
530
+
531
+ :return: The trace value of the matrix.
532
+ :raises ValueError: If matrix is not square.
533
+ """
534
+ if self.rows != self.cols:
535
+ raise ValueError("Trace can only be calculated for square matrices")
536
+
537
+ trace_sum = 0
538
+ for i in range(self.rows):
539
+ trace_sum += self.data[i][i]
540
+ return trace_sum
541
+
542
+ def transpose(self) -> Matrix:
543
+ """
544
+ Create a new transposed version of the matrix.
545
+
546
+ This interchanges the row and column index of each element.
547
+
548
+ :return: A new matrix containing the transposed version.
549
+ """
550
+ result = Matrix(self.cols, self.rows)
551
+ for i in range(self.rows):
552
+ for j in range(self.cols):
553
+ result.data[j][i] = self.data[i][j]
554
+ return result
555
+
556
+ # Helper methods
557
+ def _lu_determinant(self) -> float | int:
558
+ """
559
+ Calculate determinant using LU decomposition.
560
+
561
+ :return: The determinant value.
562
+ """
563
+ # Simplified LU for determinant calculation
564
+ n = self.rows
565
+ lu = [row[:] for row in self.data] # Copy matrix
566
+
567
+ det = 1
568
+ for i in range(n):
569
+ # Find pivot
570
+ max_row = i
571
+ for k in range(i + 1, n):
572
+ if abs(lu[k][i]) > abs(lu[max_row][i]):
573
+ max_row = k
574
+
575
+ # Swap rows if needed
576
+ if max_row != i:
577
+ lu[i], lu[max_row] = lu[max_row], lu[i]
578
+ det *= -1
579
+
580
+ # Check for zero pivot
581
+ if lu[i][i] == 0:
582
+ return 0
583
+
584
+ det *= lu[i][i]
585
+
586
+ # Eliminate column
587
+ for k in range(i + 1, n):
588
+ factor = lu[k][i] / lu[i][i]
589
+ for j in range(i + 1, n):
590
+ lu[k][j] -= factor * lu[i][j]
591
+
592
+ return det
593
+
594
+ # is_* check methods
595
+ def is_antidiagonal(self) -> bool:
596
+ """
597
+ Determine if the matrix is anti-diagonal.
598
+
599
+ All elements outside the secondary diagonal are zero.
600
+
601
+ :return: True if matrix is anti-diagonal, False otherwise.
602
+ """
603
+ if self.rows != self.cols:
604
+ return False
605
+
606
+ for i in range(self.rows):
607
+ for j in range(self.cols):
608
+ if i + j != self.rows - 1 and self.data[i][j] != 0:
609
+ return False
610
+ return True
611
+
612
+ def is_antisymmetric(self) -> bool:
613
+ """
614
+ Determine if a matrix is antisymmetric.
615
+
616
+ A matrix is antisymmetric if its transpose equals its negative.
617
+
618
+ :return: True if matrix is antisymmetric, False otherwise.
619
+ """
620
+ if self.rows != self.cols:
621
+ return False
622
+
623
+ for i in range(self.rows):
624
+ for j in range(self.cols):
625
+ if self.data[i][j] != -self.data[j][i]:
626
+ return False
627
+ return True
628
+
629
+ def is_binary(self) -> bool:
630
+ """
631
+ Determine if the matrix is binary.
632
+
633
+ A matrix is binary when all elements are 0 or 1.
634
+
635
+ :return: True if matrix is binary, False otherwise.
636
+ """
637
+ for row in self.data:
638
+ for val in row:
639
+ if not (isinstance(val, NA) or val != val) and val != 0 and val != 1:
640
+ return False
641
+ return True
642
+
643
+ def is_diagonal(self) -> bool:
644
+ """
645
+ Determine if the matrix is diagonal.
646
+
647
+ All elements outside the main diagonal are zero.
648
+
649
+ :return: True if matrix is diagonal, False otherwise.
650
+ """
651
+ if self.rows != self.cols:
652
+ return False
653
+
654
+ for i in range(self.rows):
655
+ for j in range(self.cols):
656
+ if i != j and self.data[i][j] != 0:
657
+ return False
658
+ return True
659
+
660
+ def is_identity(self) -> bool:
661
+ """
662
+ Determine if a matrix is an identity matrix.
663
+
664
+ Elements on the main diagonal are ones and zeros elsewhere.
665
+
666
+ :return: True if matrix is identity, False otherwise.
667
+ """
668
+ if self.rows != self.cols:
669
+ return False
670
+
671
+ for i in range(self.rows):
672
+ for j in range(self.cols):
673
+ expected = 1 if i == j else 0
674
+ if self.data[i][j] != expected:
675
+ return False
676
+ return True
677
+
678
+ def is_square(self) -> bool:
679
+ """
680
+ Determine if the matrix is square.
681
+
682
+ A matrix is square if it has the same number of rows and columns.
683
+
684
+ :return: True if matrix is square, False otherwise.
685
+ """
686
+ return self.rows == self.cols
687
+
688
+ def is_stochastic(self) -> bool:
689
+ """
690
+ Determine if the matrix is stochastic.
691
+
692
+ A matrix is stochastic if all row sums equal 1.
693
+
694
+ :return: True if matrix is stochastic, False otherwise.
695
+ """
696
+ for row in self.data:
697
+ row_sum = sum(val for val in row if not (isinstance(val, NA) or val != val))
698
+ if abs(row_sum - 1.0) > 1e-10:
699
+ return False
700
+ return True
701
+
702
+ def is_symmetric(self) -> bool:
703
+ """
704
+ Determine if a square matrix is symmetric.
705
+
706
+ Elements are symmetric with respect to the main diagonal.
707
+
708
+ :return: True if matrix is symmetric, False otherwise.
709
+ """
710
+ if self.rows != self.cols:
711
+ return False
712
+
713
+ for i in range(self.rows):
714
+ for j in range(i + 1, self.cols):
715
+ if self.data[i][j] != self.data[j][i]:
716
+ return False
717
+ return True
718
+
719
+ def is_triangular(self) -> bool:
720
+ """
721
+ Determine if the matrix is triangular.
722
+
723
+ A matrix is triangular if all elements above or below the main diagonal are zero.
724
+
725
+ :return: True if matrix is triangular, False otherwise.
726
+ """
727
+ if self.rows != self.cols:
728
+ return False
729
+
730
+ # Check if upper triangular
731
+ upper = True
732
+ for i in range(1, self.rows):
733
+ for j in range(i):
734
+ if self.data[i][j] != 0:
735
+ upper = False
736
+ break
737
+ if not upper:
738
+ break
739
+
740
+ # Check if lower triangular
741
+ lower = True
742
+ for i in range(self.rows - 1):
743
+ for j in range(i + 1, self.cols):
744
+ if self.data[i][j] != 0:
745
+ lower = False
746
+ break
747
+ if not lower:
748
+ break
749
+
750
+ return upper or lower
751
+
752
+ def is_zero(self) -> bool:
753
+ """
754
+ Determine if all elements of the matrix are zero.
755
+
756
+ :return: True if all elements are zero, False otherwise.
757
+ """
758
+ for row in self.data:
759
+ for val in row:
760
+ if not (isinstance(val, NA) or val != val) and val != 0:
761
+ return False
762
+ return True
763
+
764
+ # Additional methods
765
+ def kron(self, other: Matrix) -> Matrix:
766
+ """
767
+ Return the Kronecker product of two matrices.
768
+
769
+ :param other: Second matrix object.
770
+ :return: A new matrix containing the Kronecker product.
771
+ """
772
+ result_rows = self.rows * other.rows
773
+ result_cols = self.cols * other.cols
774
+ result = Matrix(result_rows, result_cols)
775
+
776
+ for i in range(self.rows):
777
+ for j in range(self.cols):
778
+ for k in range(other.rows):
779
+ for l in range(other.cols):
780
+ result.data[i * other.rows + k][j * other.cols + l] = \
781
+ self.data[i][j] * other.data[k][l]
782
+
783
+ return result
784
+
785
+ def pow(self, power: int) -> Matrix:
786
+ """
787
+ Calculate the product of the matrix by itself power times.
788
+
789
+ :param power: The number of times the matrix will be multiplied by itself.
790
+ :return: The matrix raised to the specified power.
791
+ :raises ValueError: If matrix is not square.
792
+ """
793
+ if self.rows != self.cols:
794
+ raise ValueError("Power can only be calculated for square matrices")
795
+
796
+ if power == 0:
797
+ # Return identity matrix
798
+ result = Matrix(self.rows, self.cols)
799
+ for i in range(self.rows):
800
+ result.data[i][i] = 1
801
+ return result
802
+
803
+ result = self.copy()
804
+ for _ in range(power - 1):
805
+ result = cast(Matrix, result.mult(self))
806
+
807
+ return result
808
+
809
+ def rank(self) -> int:
810
+ """
811
+ Calculate the rank of the matrix.
812
+
813
+ :return: The rank of the matrix.
814
+ """
815
+ # Simple rank calculation using row echelon form
816
+ m = [row[:] for row in self.data] # Copy matrix
817
+ rows, cols = self.rows, self.cols
818
+ rank = 0
819
+
820
+ for col in range(cols):
821
+ # Find pivot
822
+ pivot_row = None
823
+ for row in range(rank, rows):
824
+ if m[row][col] != 0:
825
+ pivot_row = row
826
+ break
827
+
828
+ if pivot_row is None:
829
+ continue
830
+
831
+ # Swap rows
832
+ m[rank], m[pivot_row] = m[pivot_row], m[rank]
833
+
834
+ # Eliminate column
835
+ for row in range(rank + 1, rows):
836
+ if m[row][col] != 0:
837
+ factor = m[row][col] / m[rank][col]
838
+ for j in range(col, cols):
839
+ m[row][j] -= factor * m[rank][j]
840
+
841
+ rank += 1
842
+
843
+ return rank
844
+
845
+ def inv(self) -> Matrix:
846
+ """
847
+ Return the inverse of a square matrix.
848
+
849
+ :return: A new matrix which is the inverse of this matrix.
850
+ :raises ValueError: If matrix is not square or is singular.
851
+ """
852
+ if self.rows != self.cols:
853
+ raise ValueError("Inverse can only be calculated for square matrices")
854
+
855
+ n = self.rows
856
+ # Create augmented matrix [A|I]
857
+ aug = [row[:] + [0] * n for row in self.data]
858
+ for i in range(n):
859
+ aug[i][n + i] = 1
860
+
861
+ # Forward elimination
862
+ for i in range(n):
863
+ # Find pivot
864
+ max_row = i
865
+ for k in range(i + 1, n):
866
+ if abs(aug[k][i]) > abs(aug[max_row][i]):
867
+ max_row = k
868
+
869
+ aug[i], aug[max_row] = aug[max_row], aug[i]
870
+
871
+ # Check for singular matrix
872
+ if aug[i][i] == 0:
873
+ raise ValueError("Matrix is singular")
874
+
875
+ # Scale pivot row
876
+ pivot = aug[i][i]
877
+ for j in range(2 * n):
878
+ aug[i][j] /= pivot
879
+
880
+ # Eliminate column
881
+ for k in range(n):
882
+ if k != i:
883
+ factor = aug[k][i]
884
+ for j in range(2 * n):
885
+ aug[k][j] -= factor * aug[i][j]
886
+
887
+ # Extract inverse from augmented matrix
888
+ result = Matrix(n, n)
889
+ for i in range(n):
890
+ for j in range(n):
891
+ result.data[i][j] = aug[i][n + j]
892
+
893
+ return result
894
+
895
+ def pinv(self) -> Matrix:
896
+ """
897
+ Return the pseudoinverse of a matrix.
898
+
899
+ Uses Moore-Penrose inverse formula. For non-singular square matrices,
900
+ this returns the same result as inv().
901
+
902
+ :return: A new matrix containing the pseudoinverse.
903
+ """
904
+ # For simplicity, if matrix is square and non-singular, return regular inverse
905
+ if self.is_square():
906
+ try:
907
+ return self.inv()
908
+ except ValueError:
909
+ pass
910
+
911
+ # Otherwise, use A+ = (A^T A)^(-1) A^T for overdetermined systems
912
+ # or A+ = A^T (A A^T)^(-1) for underdetermined systems
913
+ at = self.transpose()
914
+
915
+ if self.rows >= self.cols:
916
+ # Overdetermined
917
+ ata = cast(Matrix, at.mult(self))
918
+ return cast(Matrix, ata.inv().mult(at))
919
+ else:
920
+ # Underdetermined
921
+ aat = cast(Matrix, self.mult(at))
922
+ return cast(Matrix, at.mult(aat.inv()))
923
+
924
+ def eigenvalues(self) -> list[float | int]:
925
+ """
926
+ Return an array containing the eigenvalues of a square matrix.
927
+
928
+ :return: An array containing the eigenvalues.
929
+ :raises ValueError: If matrix is not square.
930
+ """
931
+ if self.rows != self.cols:
932
+ raise ValueError("Eigenvalues can only be calculated for square matrices")
933
+
934
+ # For 2x2 matrix, use analytical solution
935
+ if self.rows == 2:
936
+ a, b = self.data[0][0], self.data[0][1]
937
+ c, d = self.data[1][0], self.data[1][1]
938
+
939
+ trace = a + d
940
+ det = a * d - b * c
941
+ discriminant = trace * trace - 4 * det
942
+
943
+ if discriminant >= 0:
944
+ sqrt_disc = discriminant ** 0.5
945
+ return [(trace + sqrt_disc) / 2, (trace - sqrt_disc) / 2]
946
+ else:
947
+ # Complex eigenvalues - return real parts only
948
+ return [trace / 2, trace / 2]
949
+
950
+ # For larger matrices, this would need a proper algorithm
951
+ # For now, return empty list
952
+ return []
953
+
954
+ def eigenvectors(self) -> Matrix:
955
+ """
956
+ Return a matrix of eigenvectors.
957
+
958
+ Each column is an eigenvector of the matrix.
959
+
960
+ :return: A new matrix containing the eigenvectors.
961
+ :raises ValueError: If matrix is not square.
962
+ """
963
+ if self.rows != self.cols:
964
+ raise ValueError("Eigenvectors can only be calculated for square matrices")
965
+
966
+ # Return identity matrix as placeholder
967
+ result = Matrix(self.rows, self.cols)
968
+ for i in range(self.rows):
969
+ result.data[i][i] = 1
970
+ return result
971
+
972
+ def add_col(self, col: int | None = None, array_id: list[Any] | None = None) -> None:
973
+ """
974
+ Add a column at the specified index of the matrix.
975
+
976
+ The column can consist of NA values, or an array can be used to provide values.
977
+
978
+ :param col: The index where the new column will be inserted. If None, appends to the end.
979
+ :param array_id: Array to use for providing values to the new column.
980
+ If matrix is empty, the array size determines the row count.
981
+ :raises IndexError: If column index is out of bounds.
982
+ """
983
+ col_idx: int = self.cols if col is None else col
984
+
985
+ if col_idx < 0 or col_idx > self.cols:
986
+ raise IndexError(f"Column index {col_idx} out of bounds")
987
+
988
+ # If matrix is empty (0 rows) and array provided, create rows first
989
+ if self.rows == 0 and array_id and len(array_id) > 0:
990
+ self.rows = len(array_id)
991
+ self.data = [[] for _ in range(self.rows)]
992
+
993
+ for i in range(self.rows):
994
+ if array_id and i < len(array_id):
995
+ self.data[i].insert(col_idx, array_id[i])
996
+ else:
997
+ self.data[i].insert(col_idx, NA(T))
998
+
999
+ self.cols += 1