numdaplot 0.1.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 (474) hide show
  1. numdaplot/__init__.py +506 -0
  2. numdaplot/arrays/__init__.py +5 -0
  3. numdaplot/arrays/array.py +92 -0
  4. numdaplot/arrays/array_arithmetic.py +404 -0
  5. numdaplot/arrays/array_calculation.py +143 -0
  6. numdaplot/arrays/array_conversion.py +150 -0
  7. numdaplot/arrays/array_creation.py +371 -0
  8. numdaplot/arrays/array_data.py +281 -0
  9. numdaplot/arrays/array_layout.py +179 -0
  10. numdaplot/arrays/array_logic.py +227 -0
  11. numdaplot/arrays/array_reflected_arithmetic.py +334 -0
  12. numdaplot/arrays/array_refusals.py +339 -0
  13. numdaplot/arrays/array_selection.py +195 -0
  14. numdaplot/arrays/array_sorting.py +59 -0
  15. numdaplot/arrays/axis_number.py +58 -0
  16. numdaplot/arrays/calculation/__init__.py +5 -0
  17. numdaplot/arrays/calculation/counting_statistics.py +325 -0
  18. numdaplot/arrays/calculation/location_statistics.py +268 -0
  19. numdaplot/arrays/calculation/math_function_values.py +183 -0
  20. numdaplot/arrays/calculation/statistic_values.py +209 -0
  21. numdaplot/arrays/calculation/time_function_values.py +419 -0
  22. numdaplot/arrays/conversion/__init__.py +5 -0
  23. numdaplot/arrays/conversion/array_text.py +62 -0
  24. numdaplot/arrays/conversion/equality_check.py +72 -0
  25. numdaplot/arrays/conversion/python_values.py +70 -0
  26. numdaplot/arrays/conversion/time_text.py +290 -0
  27. numdaplot/arrays/creation/__init__.py +5 -0
  28. numdaplot/arrays/creation/date_parts.py +89 -0
  29. numdaplot/arrays/creation/date_parts_values.py +437 -0
  30. numdaplot/arrays/creation/distribution.py +54 -0
  31. numdaplot/arrays/creation/filled_values.py +58 -0
  32. numdaplot/arrays/creation/list_conversion.py +311 -0
  33. numdaplot/arrays/creation/list_shape.py +97 -0
  34. numdaplot/arrays/creation/normal.py +65 -0
  35. numdaplot/arrays/creation/numpy_conversion.py +149 -0
  36. numdaplot/arrays/creation/random_integers.py +104 -0
  37. numdaplot/arrays/creation/random_values.py +97 -0
  38. numdaplot/arrays/creation/range_values.py +73 -0
  39. numdaplot/arrays/creation/spacing.py +84 -0
  40. numdaplot/arrays/creation/spacing_values.py +63 -0
  41. numdaplot/arrays/creation/uniform.py +63 -0
  42. numdaplot/arrays/equality.py +68 -0
  43. numdaplot/arrays/layout/__init__.py +5 -0
  44. numdaplot/arrays/layout/sorting_values.py +113 -0
  45. numdaplot/arrays/layout/unique_values.py +49 -0
  46. numdaplot/arrays/mapping/__init__.py +5 -0
  47. numdaplot/arrays/mapping/mapped_values.py +286 -0
  48. numdaplot/arrays/mapping/unmatched.py +50 -0
  49. numdaplot/arrays/mapping/value_change.py +55 -0
  50. numdaplot/arrays/mapping/value_mapping.py +161 -0
  51. numdaplot/arrays/math_functions/__init__.py +5 -0
  52. numdaplot/arrays/math_functions/absolute.py +27 -0
  53. numdaplot/arrays/math_functions/cosine.py +28 -0
  54. numdaplot/arrays/math_functions/exponential.py +27 -0
  55. numdaplot/arrays/math_functions/logarithm.py +72 -0
  56. numdaplot/arrays/math_functions/math_function.py +58 -0
  57. numdaplot/arrays/math_functions/round.py +58 -0
  58. numdaplot/arrays/math_functions/sine.py +28 -0
  59. numdaplot/arrays/math_functions/square_root.py +27 -0
  60. numdaplot/arrays/operations/__init__.py +5 -0
  61. numdaplot/arrays/operations/boolean_operands.py +48 -0
  62. numdaplot/arrays/operations/combination.py +157 -0
  63. numdaplot/arrays/operations/float_errors.py +170 -0
  64. numdaplot/arrays/operations/integer_bounds.py +175 -0
  65. numdaplot/arrays/operations/integer_rules.py +397 -0
  66. numdaplot/arrays/operations/operand.py +139 -0
  67. numdaplot/arrays/operations/time_arithmetic.py +479 -0
  68. numdaplot/arrays/operations/time_bounds.py +286 -0
  69. numdaplot/arrays/operations/time_result_bounds.py +397 -0
  70. numdaplot/arrays/operations/transformation.py +54 -0
  71. numdaplot/arrays/operations/type_requirements.py +94 -0
  72. numdaplot/arrays/replacement.py +66 -0
  73. numdaplot/arrays/selection/__init__.py +5 -0
  74. numdaplot/arrays/selection/comparison_masks.py +131 -0
  75. numdaplot/arrays/selection/membership_masks.py +118 -0
  76. numdaplot/arrays/selection/missing_mask.py +50 -0
  77. numdaplot/arrays/selection/region_index.py +178 -0
  78. numdaplot/arrays/selection/replaced_values.py +120 -0
  79. numdaplot/arrays/selection/rule_mask.py +83 -0
  80. numdaplot/arrays/selection/time_reference.py +172 -0
  81. numdaplot/arrays/shape.py +74 -0
  82. numdaplot/arrays/type_match.py +51 -0
  83. numdaplot/data_types/__init__.py +5 -0
  84. numdaplot/data_types/library.py +57 -0
  85. numdaplot/data_types/numpy_mapping.py +207 -0
  86. numdaplot/data_types/pandas_mapping.py +114 -0
  87. numdaplot/data_types/time_of_day.py +292 -0
  88. numdaplot/data_types/type.py +224 -0
  89. numdaplot/data_types/value_family.py +149 -0
  90. numdaplot/data_types/value_type.py +147 -0
  91. numdaplot/dates/__init__.py +5 -0
  92. numdaplot/dates/custom_date_format.py +83 -0
  93. numdaplot/dates/date_format.py +120 -0
  94. numdaplot/dates/date_part.py +59 -0
  95. numdaplot/dates/date_part_unit.py +153 -0
  96. numdaplot/dates/date_period.py +57 -0
  97. numdaplot/dates/duration_in.py +58 -0
  98. numdaplot/dates/duration_unit.py +90 -0
  99. numdaplot/dates/period_unit.py +101 -0
  100. numdaplot/dates/time_function.py +56 -0
  101. numdaplot/dates/time_zone.py +68 -0
  102. numdaplot/dates/time_zone_change.py +71 -0
  103. numdaplot/dates/time_zone_mode.py +81 -0
  104. numdaplot/dates/to_duration.py +58 -0
  105. numdaplot/errors/__init__.py +7 -0
  106. numdaplot/errors/broadcast_warning.py +36 -0
  107. numdaplot/errors/calculation_warning.py +35 -0
  108. numdaplot/errors/cell_out_of_range_error.py +77 -0
  109. numdaplot/errors/column_not_found_error.py +64 -0
  110. numdaplot/errors/conflicting_settings_error.py +60 -0
  111. numdaplot/errors/dimension_error.py +62 -0
  112. numdaplot/errors/display_warning.py +32 -0
  113. numdaplot/errors/duplicate_column_error.py +82 -0
  114. numdaplot/errors/duplicate_label_error.py +55 -0
  115. numdaplot/errors/duplicate_statistic_error.py +55 -0
  116. numdaplot/errors/duplicate_value_error.py +54 -0
  117. numdaplot/errors/empty_array_error.py +55 -0
  118. numdaplot/errors/empty_list_error.py +52 -0
  119. numdaplot/errors/empty_region_error.py +34 -0
  120. numdaplot/errors/empty_text_error.py +34 -0
  121. numdaplot/errors/empty_total_error.py +62 -0
  122. numdaplot/errors/empty_values_error.py +34 -0
  123. numdaplot/errors/file_access_error.py +70 -0
  124. numdaplot/errors/file_exists_error.py +62 -0
  125. numdaplot/errors/file_not_found_error.py +56 -0
  126. numdaplot/errors/incompatible_charts_error.py +80 -0
  127. numdaplot/errors/incompatible_types_error.py +104 -0
  128. numdaplot/errors/integer_overflow_error.py +61 -0
  129. numdaplot/errors/invalid_base_error.py +52 -0
  130. numdaplot/errors/invalid_bin_width_error.py +66 -0
  131. numdaplot/errors/invalid_bounds_error.py +58 -0
  132. numdaplot/errors/invalid_chart_data_error.py +81 -0
  133. numdaplot/errors/invalid_color_count_error.py +63 -0
  134. numdaplot/errors/invalid_color_error.py +51 -0
  135. numdaplot/errors/invalid_column_name_error.py +52 -0
  136. numdaplot/errors/invalid_conversion_error.py +119 -0
  137. numdaplot/errors/invalid_count_error.py +61 -0
  138. numdaplot/errors/invalid_date_format_error.py +51 -0
  139. numdaplot/errors/invalid_deviation_error.py +52 -0
  140. numdaplot/errors/invalid_fraction_error.py +51 -0
  141. numdaplot/errors/invalid_label_count_error.py +61 -0
  142. numdaplot/errors/invalid_limits_error.py +111 -0
  143. numdaplot/errors/invalid_opacity_error.py +52 -0
  144. numdaplot/errors/invalid_point_size_error.py +52 -0
  145. numdaplot/errors/invalid_position_error.py +106 -0
  146. numdaplot/errors/invalid_resolution_error.py +51 -0
  147. numdaplot/errors/invalid_rotation_error.py +53 -0
  148. numdaplot/errors/invalid_seed_error.py +53 -0
  149. numdaplot/errors/invalid_separator_error.py +52 -0
  150. numdaplot/errors/invalid_shape_error.py +52 -0
  151. numdaplot/errors/invalid_sheet_name_error.py +54 -0
  152. numdaplot/errors/invalid_size_error.py +58 -0
  153. numdaplot/errors/invalid_step_error.py +77 -0
  154. numdaplot/errors/invalid_suffixes_error.py +61 -0
  155. numdaplot/errors/invalid_time_zone_change_error.py +63 -0
  156. numdaplot/errors/invalid_type_error.py +79 -0
  157. numdaplot/errors/irregular_list_error.py +36 -0
  158. numdaplot/errors/join_relation_error.py +183 -0
  159. numdaplot/errors/missing_dependency_error.py +58 -0
  160. numdaplot/errors/missing_type_error.py +36 -0
  161. numdaplot/errors/missing_value_error.py +62 -0
  162. numdaplot/errors/not_single_value_error.py +53 -0
  163. numdaplot/errors/numdaplot_error.py +25 -0
  164. numdaplot/errors/numdaplot_warning.py +34 -0
  165. numdaplot/errors/occupied_cell_error.py +63 -0
  166. numdaplot/errors/out_of_domain_error.py +70 -0
  167. numdaplot/errors/readability_warning.py +35 -0
  168. numdaplot/errors/refused_operation_error.py +64 -0
  169. numdaplot/errors/refused_setting_error.py +60 -0
  170. numdaplot/errors/rejected_dates.py +60 -0
  171. numdaplot/errors/repeated_key.py +36 -0
  172. numdaplot/errors/shape_mismatch_error.py +67 -0
  173. numdaplot/errors/time_overflow_error.py +131 -0
  174. numdaplot/errors/time_zone_mismatch_error.py +83 -0
  175. numdaplot/errors/unknown_time_zone_error.py +51 -0
  176. numdaplot/errors/unmatched_value_error.py +81 -0
  177. numdaplot/errors/unsupported_image_format_error.py +56 -0
  178. numdaplot/errors/unsupported_index_error.py +38 -0
  179. numdaplot/errors/unsupported_missing_error.py +129 -0
  180. numdaplot/errors/unsupported_type_error.py +62 -0
  181. numdaplot/errors/value_conversion_error.py +215 -0
  182. numdaplot/ordering/__init__.py +5 -0
  183. numdaplot/ordering/missing_position.py +46 -0
  184. numdaplot/ordering/order.py +46 -0
  185. numdaplot/package_version.py +36 -0
  186. numdaplot/plots/__init__.py +5 -0
  187. numdaplot/plots/chart_layer.py +47 -0
  188. numdaplot/plots/charts/__init__.py +5 -0
  189. numdaplot/plots/charts/bar_chart.py +98 -0
  190. numdaplot/plots/charts/bar_layout.py +58 -0
  191. numdaplot/plots/charts/bin_count.py +61 -0
  192. numdaplot/plots/charts/bin_width.py +78 -0
  193. numdaplot/plots/charts/box_chart.py +97 -0
  194. numdaplot/plots/charts/cell_values.py +61 -0
  195. numdaplot/plots/charts/chart.py +59 -0
  196. numdaplot/plots/charts/chart_settings.py +105 -0
  197. numdaplot/plots/charts/color_scale.py +147 -0
  198. numdaplot/plots/charts/heatmap_chart.py +92 -0
  199. numdaplot/plots/charts/histogram_chart.py +93 -0
  200. numdaplot/plots/charts/line_chart.py +100 -0
  201. numdaplot/plots/charts/line_type.py +75 -0
  202. numdaplot/plots/charts/marker_shape.py +113 -0
  203. numdaplot/plots/charts/missing_chart.py +60 -0
  204. numdaplot/plots/charts/missing_map_chart.py +27 -0
  205. numdaplot/plots/charts/orientation.py +58 -0
  206. numdaplot/plots/charts/outliers.py +49 -0
  207. numdaplot/plots/charts/percentages.py +55 -0
  208. numdaplot/plots/charts/pie_chart.py +87 -0
  209. numdaplot/plots/charts/scatter_chart.py +159 -0
  210. numdaplot/plots/charts/value_axis.py +66 -0
  211. numdaplot/plots/data/__init__.py +5 -0
  212. numdaplot/plots/data/array_values.py +128 -0
  213. numdaplot/plots/data/chart_data.py +143 -0
  214. numdaplot/plots/data/chart_values.py +109 -0
  215. numdaplot/plots/data/column_reading.py +468 -0
  216. numdaplot/plots/data/heatmap_values.py +63 -0
  217. numdaplot/plots/data/missing_cells.py +165 -0
  218. numdaplot/plots/data/pie_values.py +215 -0
  219. numdaplot/plots/data/point_values.py +249 -0
  220. numdaplot/plots/data/position_kind.py +83 -0
  221. numdaplot/plots/data/renamed_layers.py +340 -0
  222. numdaplot/plots/data/sample_values.py +160 -0
  223. numdaplot/plots/data/shown_columns.py +248 -0
  224. numdaplot/plots/data/table_values.py +256 -0
  225. numdaplot/plots/data/unique_labels.py +45 -0
  226. numdaplot/plots/drawing/__init__.py +5 -0
  227. numdaplot/plots/drawing/bar_drawing.py +316 -0
  228. numdaplot/plots/drawing/box_drawing.py +344 -0
  229. numdaplot/plots/drawing/cell_merging.py +237 -0
  230. numdaplot/plots/drawing/chart_texts.py +124 -0
  231. numdaplot/plots/drawing/color_bar.py +57 -0
  232. numdaplot/plots/drawing/color_scale_values.py +120 -0
  233. numdaplot/plots/drawing/default_labels.py +41 -0
  234. numdaplot/plots/drawing/drawn_chart.py +50 -0
  235. numdaplot/plots/drawing/grid_axes.py +75 -0
  236. numdaplot/plots/drawing/heatmap_drawing.py +313 -0
  237. numdaplot/plots/drawing/histogram_drawing.py +350 -0
  238. numdaplot/plots/drawing/label_room.py +344 -0
  239. numdaplot/plots/drawing/label_thinning.py +216 -0
  240. numdaplot/plots/drawing/layer_alignment.py +319 -0
  241. numdaplot/plots/drawing/layer_colors.py +123 -0
  242. numdaplot/plots/drawing/line_drawing.py +85 -0
  243. numdaplot/plots/drawing/marker_codes.py +20 -0
  244. numdaplot/plots/drawing/missing_drawing.py +360 -0
  245. numdaplot/plots/drawing/moment_period_formatter.py +253 -0
  246. numdaplot/plots/drawing/pie_drawing.py +185 -0
  247. numdaplot/plots/drawing/plot_axes.py +38 -0
  248. numdaplot/plots/drawing/plot_drawing.py +288 -0
  249. numdaplot/plots/drawing/plot_theme.py +45 -0
  250. numdaplot/plots/drawing/position_axis.py +278 -0
  251. numdaplot/plots/drawing/scatter_drawing.py +364 -0
  252. numdaplot/plots/drawing/series_colors.py +177 -0
  253. numdaplot/plots/drawing/size_inches.py +45 -0
  254. numdaplot/plots/drawing/style_drawing.py +410 -0
  255. numdaplot/plots/drawing/text_contrast.py +44 -0
  256. numdaplot/plots/drawing/tick_rotation.py +354 -0
  257. numdaplot/plots/drawing/time_axis.py +212 -0
  258. numdaplot/plots/drawing/whole_numbers.py +72 -0
  259. numdaplot/plots/figure/__init__.py +5 -0
  260. numdaplot/plots/figure/cell.py +95 -0
  261. numdaplot/plots/figure/figure.py +256 -0
  262. numdaplot/plots/figure/figure_drawing.py +99 -0
  263. numdaplot/plots/figure/figure_style.py +60 -0
  264. numdaplot/plots/figure/layout.py +71 -0
  265. numdaplot/plots/figure/placement.py +150 -0
  266. numdaplot/plots/layer_reading.py +166 -0
  267. numdaplot/plots/output/__init__.py +5 -0
  268. numdaplot/plots/output/image_saving.py +187 -0
  269. numdaplot/plots/output/plot_display.py +181 -0
  270. numdaplot/plots/output/shown_object.py +45 -0
  271. numdaplot/plots/overlay_checks.py +245 -0
  272. numdaplot/plots/plot.py +31 -0
  273. numdaplot/plots/plot_data.py +140 -0
  274. numdaplot/plots/plot_distribution_creation.py +237 -0
  275. numdaplot/plots/plot_output.py +124 -0
  276. numdaplot/plots/plot_overlays.py +273 -0
  277. numdaplot/plots/plot_overview_creation.py +195 -0
  278. numdaplot/plots/plot_series_creation.py +213 -0
  279. numdaplot/plots/style/__init__.py +5 -0
  280. numdaplot/plots/style/axis_settings.py +60 -0
  281. numdaplot/plots/style/color.py +168 -0
  282. numdaplot/plots/style/custom_color.py +65 -0
  283. numdaplot/plots/style/grid_lines.py +49 -0
  284. numdaplot/plots/style/image_options.py +71 -0
  285. numdaplot/plots/style/legend_position.py +149 -0
  286. numdaplot/plots/style/plot_style.py +136 -0
  287. numdaplot/plots/style/size.py +70 -0
  288. numdaplot/plots/style/size_unit.py +62 -0
  289. numdaplot/positions/__init__.py +5 -0
  290. numdaplot/positions/axis.py +54 -0
  291. numdaplot/positions/index.py +51 -0
  292. numdaplot/positions/range.py +90 -0
  293. numdaplot/positions/region.py +92 -0
  294. numdaplot/py.typed +0 -0
  295. numdaplot/ratios/__init__.py +5 -0
  296. numdaplot/ratios/fraction.py +40 -0
  297. numdaplot/ratios/measure.py +48 -0
  298. numdaplot/rules/__init__.py +5 -0
  299. numdaplot/rules/between.py +78 -0
  300. numdaplot/rules/bounds.py +83 -0
  301. numdaplot/rules/case.py +48 -0
  302. numdaplot/rules/contains.py +63 -0
  303. numdaplot/rules/equal.py +43 -0
  304. numdaplot/rules/greater.py +39 -0
  305. numdaplot/rules/greater_or_equal.py +38 -0
  306. numdaplot/rules/is_in.py +67 -0
  307. numdaplot/rules/is_missing.py +25 -0
  308. numdaplot/rules/is_present.py +25 -0
  309. numdaplot/rules/less.py +39 -0
  310. numdaplot/rules/less_or_equal.py +38 -0
  311. numdaplot/rules/not_equal.py +45 -0
  312. numdaplot/rules/rule.py +62 -0
  313. numdaplot/rules/rule_values.py +44 -0
  314. numdaplot/rules/value_rule.py +110 -0
  315. numdaplot/settings/__init__.py +5 -0
  316. numdaplot/settings/argument_types.py +327 -0
  317. numdaplot/settings/field_types.py +593 -0
  318. numdaplot/statistics/__init__.py +5 -0
  319. numdaplot/statistics/count.py +71 -0
  320. numdaplot/statistics/count_distinct.py +27 -0
  321. numdaplot/statistics/counted_missing.py +46 -0
  322. numdaplot/statistics/maximum.py +27 -0
  323. numdaplot/statistics/mean.py +27 -0
  324. numdaplot/statistics/median.py +27 -0
  325. numdaplot/statistics/minimum.py +27 -0
  326. numdaplot/statistics/most_frequent.py +27 -0
  327. numdaplot/statistics/most_frequent_count.py +27 -0
  328. numdaplot/statistics/quantile.py +61 -0
  329. numdaplot/statistics/standard_deviation.py +29 -0
  330. numdaplot/statistics/statistic.py +63 -0
  331. numdaplot/statistics/sum.py +27 -0
  332. numdaplot/tables/__init__.py +5 -0
  333. numdaplot/tables/aggregation/__init__.py +5 -0
  334. numdaplot/tables/aggregation/aggregated_frame.py +189 -0
  335. numdaplot/tables/aggregation/aggregation.py +97 -0
  336. numdaplot/tables/aggregation/calculation.py +123 -0
  337. numdaplot/tables/aggregation/result_column.py +108 -0
  338. numdaplot/tables/aggregation/row_groups.py +96 -0
  339. numdaplot/tables/aggregation/statistic_name.py +122 -0
  340. numdaplot/tables/columns/__init__.py +5 -0
  341. numdaplot/tables/columns/added_column.py +89 -0
  342. numdaplot/tables/columns/boolean_conversion.py +144 -0
  343. numdaplot/tables/columns/boolean_rule.py +59 -0
  344. numdaplot/tables/columns/bracket_end.py +50 -0
  345. numdaplot/tables/columns/bracket_names.py +346 -0
  346. numdaplot/tables/columns/bracketed_column.py +179 -0
  347. numdaplot/tables/columns/brackets.py +234 -0
  348. numdaplot/tables/columns/chosen_columns.py +55 -0
  349. numdaplot/tables/columns/column_choice.py +84 -0
  350. numdaplot/tables/columns/column_mode.py +64 -0
  351. numdaplot/tables/columns/column_names.py +186 -0
  352. numdaplot/tables/columns/conversion.py +103 -0
  353. numdaplot/tables/columns/converted_column.py +669 -0
  354. numdaplot/tables/columns/date_conversion.py +526 -0
  355. numdaplot/tables/columns/date_range.py +135 -0
  356. numdaplot/tables/columns/name_change.py +67 -0
  357. numdaplot/tables/columns/new_column.py +79 -0
  358. numdaplot/tables/columns/number_conversion.py +281 -0
  359. numdaplot/tables/columns/renamed_columns.py +60 -0
  360. numdaplot/tables/columns/renaming.py +81 -0
  361. numdaplot/tables/columns/text_conversion.py +70 -0
  362. numdaplot/tables/columns/time_conversion.py +313 -0
  363. numdaplot/tables/columns/time_zone_conversion.py +128 -0
  364. numdaplot/tables/conflict.py +68 -0
  365. numdaplot/tables/description/__init__.py +5 -0
  366. numdaplot/tables/description/described_frame.py +335 -0
  367. numdaplot/tables/description/description.py +155 -0
  368. numdaplot/tables/duplicates/__init__.py +5 -0
  369. numdaplot/tables/duplicates/deduplicated_frame.py +66 -0
  370. numdaplot/tables/duplicates/deduplication.py +88 -0
  371. numdaplot/tables/duplicates/keep.py +81 -0
  372. numdaplot/tables/files/__init__.py +5 -0
  373. numdaplot/tables/files/column_names.py +60 -0
  374. numdaplot/tables/files/csv_file.py +265 -0
  375. numdaplot/tables/files/csv_read_options.py +89 -0
  376. numdaplot/tables/files/csv_separator.py +41 -0
  377. numdaplot/tables/files/csv_write_options.py +79 -0
  378. numdaplot/tables/files/decimal_count.py +42 -0
  379. numdaplot/tables/files/encoding.py +82 -0
  380. numdaplot/tables/files/excel_file.py +171 -0
  381. numdaplot/tables/files/excel_read_options.py +79 -0
  382. numdaplot/tables/files/excel_sheet.py +50 -0
  383. numdaplot/tables/files/excel_time_cells.py +146 -0
  384. numdaplot/tables/files/excel_write_options.py +63 -0
  385. numdaplot/tables/files/file_access.py +152 -0
  386. numdaplot/tables/files/file_paths.py +106 -0
  387. numdaplot/tables/files/json_decimals.py +387 -0
  388. numdaplot/tables/files/json_file.py +203 -0
  389. numdaplot/tables/files/json_layout.py +63 -0
  390. numdaplot/tables/files/json_read_options.py +64 -0
  391. numdaplot/tables/files/json_write_options.py +71 -0
  392. numdaplot/tables/files/missing_texts.py +63 -0
  393. numdaplot/tables/files/optional_package.py +42 -0
  394. numdaplot/tables/files/pandas_backend.py +11 -0
  395. numdaplot/tables/files/parquet_file.py +91 -0
  396. numdaplot/tables/files/parquet_read_options.py +56 -0
  397. numdaplot/tables/files/sas_file.py +94 -0
  398. numdaplot/tables/files/sas_read_options.py +67 -0
  399. numdaplot/tables/files/stata_file.py +83 -0
  400. numdaplot/tables/files/stata_read_options.py +56 -0
  401. numdaplot/tables/files/xpt_read_options.py +65 -0
  402. numdaplot/tables/filtering/__init__.py +5 -0
  403. numdaplot/tables/filtering/checked_frame.py +60 -0
  404. numdaplot/tables/filtering/column_mask.py +71 -0
  405. numdaplot/tables/filtering/column_match.py +62 -0
  406. numdaplot/tables/filtering/condition.py +94 -0
  407. numdaplot/tables/filtering/filter.py +74 -0
  408. numdaplot/tables/filtering/filter_mode.py +67 -0
  409. numdaplot/tables/filtering/row_mask.py +152 -0
  410. numdaplot/tables/filtering/rule_moments.py +164 -0
  411. numdaplot/tables/frames/__init__.py +5 -0
  412. numdaplot/tables/frames/array_frame.py +95 -0
  413. numdaplot/tables/frames/cleaned_names.py +132 -0
  414. numdaplot/tables/frames/column.py +15 -0
  415. numdaplot/tables/frames/column_values.py +273 -0
  416. numdaplot/tables/frames/frame_equality.py +130 -0
  417. numdaplot/tables/frames/frame_normalization.py +459 -0
  418. numdaplot/tables/frames/named_column_errors.py +85 -0
  419. numdaplot/tables/frames/pandas_decimals.py +113 -0
  420. numdaplot/tables/frames/region_positions.py +113 -0
  421. numdaplot/tables/frames/table_text.py +222 -0
  422. numdaplot/tables/frames/time_columns.py +345 -0
  423. numdaplot/tables/frames/time_range_errors.py +117 -0
  424. numdaplot/tables/frames/time_zones.py +271 -0
  425. numdaplot/tables/joining/__init__.py +5 -0
  426. numdaplot/tables/joining/join.py +110 -0
  427. numdaplot/tables/joining/join_keys.py +367 -0
  428. numdaplot/tables/joining/join_mode.py +93 -0
  429. numdaplot/tables/joining/join_relation.py +149 -0
  430. numdaplot/tables/joining/join_suffixes.py +71 -0
  431. numdaplot/tables/joining/joined_frame.py +166 -0
  432. numdaplot/tables/joining/joined_names.py +107 -0
  433. numdaplot/tables/joining/joined_rows.py +235 -0
  434. numdaplot/tables/joining/key_types.py +57 -0
  435. numdaplot/tables/joining/relation.py +78 -0
  436. numdaplot/tables/missing/__init__.py +5 -0
  437. numdaplot/tables/missing/missing_count.py +59 -0
  438. numdaplot/tables/missing/missing_counts.py +214 -0
  439. numdaplot/tables/missing/missing_drop.py +98 -0
  440. numdaplot/tables/missing/missing_mode.py +63 -0
  441. numdaplot/tables/missing/missing_ratio.py +71 -0
  442. numdaplot/tables/missing/missing_removal.py +257 -0
  443. numdaplot/tables/missing/missing_summary.py +174 -0
  444. numdaplot/tables/missing/ratio_comparison.py +70 -0
  445. numdaplot/tables/pivot/__init__.py +5 -0
  446. numdaplot/tables/pivot/pivot_table.py +104 -0
  447. numdaplot/tables/pivot/pivoted_frame.py +244 -0
  448. numdaplot/tables/preview/__init__.py +5 -0
  449. numdaplot/tables/preview/notebook_display.py +238 -0
  450. numdaplot/tables/preview/preview.py +100 -0
  451. numdaplot/tables/preview/preview_rows.py +55 -0
  452. numdaplot/tables/preview/shown_columns.py +62 -0
  453. numdaplot/tables/preview/side.py +82 -0
  454. numdaplot/tables/sorting/__init__.py +5 -0
  455. numdaplot/tables/sorting/sort_key.py +71 -0
  456. numdaplot/tables/sorting/sorted_frame.py +81 -0
  457. numdaplot/tables/sorting/sorting.py +82 -0
  458. numdaplot/tables/table.py +71 -0
  459. numdaplot/tables/table_calculation.py +172 -0
  460. numdaplot/tables/table_columns.py +214 -0
  461. numdaplot/tables/table_conversion.py +351 -0
  462. numdaplot/tables/table_data.py +321 -0
  463. numdaplot/tables/table_display.py +56 -0
  464. numdaplot/tables/table_joining.py +80 -0
  465. numdaplot/tables/table_layout.py +105 -0
  466. numdaplot/tables/table_missing.py +146 -0
  467. numdaplot/tables/table_reading.py +451 -0
  468. numdaplot/tables/table_refusals.py +295 -0
  469. numdaplot/tables/table_selection.py +163 -0
  470. numdaplot/tables/table_writing.py +269 -0
  471. numdaplot-0.1.0.dist-info/METADATA +176 -0
  472. numdaplot-0.1.0.dist-info/RECORD +474 -0
  473. numdaplot-0.1.0.dist-info/WHEEL +4 -0
  474. numdaplot-0.1.0.dist-info/licenses/LICENSE +28 -0
@@ -0,0 +1,117 @@
1
+ """
2
+ Refusal of the dates and durations pandas cannot store in a Table column.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from types import TracebackType
8
+
9
+ import pandas
10
+
11
+ from numdaplot.data_types.type import Type
12
+ from numdaplot.errors.time_overflow_error import TimeOverflowError
13
+
14
+
15
+ class TimeRangeErrors:
16
+ """
17
+ Context giving the out of range errors of pandas in a block as ``TimeOverflowError``, naming the column.
18
+
19
+ .. versionadded:: 0.1.0
20
+
21
+ Parameters
22
+ ----------
23
+ operation_name
24
+ The operation shown in the message.
25
+ column_name
26
+ The name of the column the block works on.
27
+
28
+ Examples
29
+ --------
30
+ >>> import numpy
31
+ >>> import pandas
32
+ >>> import numdaplot as ndp
33
+ >>> from numdaplot.tables.frames.time_range_errors import TimeRangeErrors
34
+ >>> far_moments = pandas.Series(numpy.array(["3000-01-01"], dtype="datetime64[s]"))
35
+ >>> try:
36
+ ... with TimeRangeErrors("from_arrays", "day"):
37
+ ... far_moments.astype("datetime64[ns]")
38
+ ... except ndp.TimeOverflowError as error:
39
+ ... print(error)
40
+ The operation from_arrays gives values of the column 'day' outside the range of DATETIME, from 1677 to 2262.
41
+ """
42
+
43
+ def __init__(self, operation_name: str, column_name: str) -> None:
44
+ """
45
+ Keep the operation and the column of the block.
46
+
47
+ .. versionadded:: 0.1.0
48
+
49
+ Parameters
50
+ ----------
51
+ operation_name
52
+ The operation shown in the message.
53
+ column_name
54
+ The name of the column the block works on.
55
+ """
56
+ self.operation_name = operation_name
57
+ self.column_name = column_name
58
+
59
+ def __enter__(self) -> None:
60
+ """
61
+ Start the block.
62
+
63
+ .. versionadded:: 0.1.0
64
+ """
65
+
66
+ def __exit__(
67
+ self,
68
+ error_type: type[BaseException] | None,
69
+ error: BaseException | None,
70
+ traceback: TracebackType | None,
71
+ ) -> None:
72
+ """
73
+ Replace an out of range error of pandas raised by the block.
74
+
75
+ .. versionadded:: 0.1.0
76
+
77
+ Parameters
78
+ ----------
79
+ error_type
80
+ The type of the error raised by the block, or None.
81
+ error
82
+ The error raised by the block, or None.
83
+ traceback
84
+ Its traceback, or None.
85
+
86
+ Raises
87
+ ------
88
+ TimeOverflowError
89
+ When the block raised ``OutOfBoundsDatetime`` or ``OutOfBoundsTimedelta``.
90
+ """
91
+ if isinstance(error, pandas.errors.OutOfBoundsDatetime):
92
+ self.raise_overflow(Type.DATETIME, error)
93
+
94
+ if isinstance(error, pandas.errors.OutOfBoundsTimedelta):
95
+ self.raise_overflow(Type.DURATION, error)
96
+
97
+ def raise_overflow(self, data_type: Type, error: BaseException) -> None:
98
+ """
99
+ Raise the error of a value out of the range of a type.
100
+
101
+ .. versionadded:: 0.1.0
102
+
103
+ Parameters
104
+ ----------
105
+ data_type
106
+ The type that cannot hold the value.
107
+ error
108
+ The error of pandas.
109
+
110
+ Raises
111
+ ------
112
+ TimeOverflowError
113
+ Always.
114
+ """
115
+ overflow_error = TimeOverflowError(self.operation_name, data_type.name)
116
+ overflow_error.name_column(self.column_name)
117
+ raise overflow_error from error
@@ -0,0 +1,271 @@
1
+ """
2
+ Time zones of the datetime columns of a Table.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from typing import cast
8
+
9
+ import pandas
10
+
11
+ from numdaplot.data_types.library import Library
12
+ from numdaplot.data_types.type import Type
13
+ from numdaplot.dates.time_zone import TimeZone
14
+ from numdaplot.errors.time_zone_mismatch_error import TimeZoneMismatchError
15
+ from numdaplot.errors.unknown_time_zone_error import UnknownTimeZoneError
16
+ from numdaplot.errors.unsupported_type_error import UnsupportedTypeError
17
+ from numdaplot.tables.frames.column import Column
18
+
19
+ UTC_ZONE_NAME = "UTC"
20
+ NANOSECOND_UNIT = "ns"
21
+ TYPE_AND_ZONE = "{type_name} {zone_name}"
22
+ DATETIME_KIND = "M"
23
+
24
+
25
+ def find_column_time_zone(column: Column) -> TimeZone | None:
26
+ """
27
+ Return the time zone of a Table column.
28
+
29
+ .. versionadded:: 0.1.0
30
+
31
+ Parameters
32
+ ----------
33
+ column
34
+ A Table column.
35
+
36
+ Returns
37
+ -------
38
+ TimeZone or None
39
+ The zone of a datetime column with a time zone, ``None`` for any other column.
40
+
41
+ Examples
42
+ --------
43
+ >>> import pandas
44
+ >>> from numdaplot.tables.frames.time_zones import find_column_time_zone
45
+ >>> find_column_time_zone(pandas.Series(pandas.to_datetime(["2026-07-01 10:30"])).dt.tz_localize("Europe/Paris"))
46
+ TimeZone(name='Europe/Paris')
47
+ """
48
+ column_dtype = column.dtype
49
+ if not isinstance(column_dtype, pandas.DatetimeTZDtype):
50
+ return None
51
+
52
+ zone_name = str(column_dtype.tz)
53
+ time_zone = TimeZone(zone_name)
54
+
55
+ return time_zone
56
+
57
+
58
+ def name_column_type(column: Column, column_type: Type) -> str:
59
+ """
60
+ Return the type shown under the name of a Table column, followed by its time zone when it has one.
61
+
62
+ .. versionadded:: 0.1.0
63
+
64
+ Parameters
65
+ ----------
66
+ column
67
+ A Table column.
68
+ column_type
69
+ The numdaplot type of the column.
70
+
71
+ Returns
72
+ -------
73
+ str
74
+ The name of the type, such as ``"FLOAT64"`` or ``"DATETIME Europe/Paris"``.
75
+
76
+ Examples
77
+ --------
78
+ >>> import pandas
79
+ >>> import numdaplot as ndp
80
+ >>> from numdaplot.tables.frames.time_zones import name_column_type
81
+ >>> name_column_type(pandas.Series([1.5]), ndp.Type.FLOAT64)
82
+ 'FLOAT64'
83
+ """
84
+ time_zone = find_column_time_zone(column)
85
+ if time_zone is None:
86
+ return column_type.name
87
+
88
+ type_and_zone = TYPE_AND_ZONE.format(type_name=column_type.name, zone_name=time_zone.name)
89
+
90
+ return type_and_zone
91
+
92
+
93
+ def normalize_time_zone_column(column: Column) -> Column:
94
+ """
95
+ Return a datetime column with a time zone, counted in nanoseconds, its zone named by its IANA name.
96
+
97
+ .. versionadded:: 0.1.0
98
+
99
+ Parameters
100
+ ----------
101
+ column
102
+ A pandas column of datetimes with a time zone.
103
+
104
+ Returns
105
+ -------
106
+ pandas.Series
107
+ The same instants in the same zone.
108
+
109
+ Raises
110
+ ------
111
+ UnsupportedTypeError
112
+ When the zone is not an IANA time zone, such as a fixed offset of one hour.
113
+
114
+ Examples
115
+ --------
116
+ >>> import pandas
117
+ >>> from numdaplot.tables.frames.time_zones import normalize_time_zone_column
118
+ >>> times = pandas.Series(pandas.to_datetime(["2026-07-01 10:30"])).dt.tz_localize("UTC").dt.as_unit("s")
119
+ >>> str(normalize_time_zone_column(times).dtype)
120
+ 'datetime64[ns, UTC]'
121
+ """
122
+ column_dtype = cast(pandas.DatetimeTZDtype, column.dtype)
123
+ zone_name = str(column_dtype.tz)
124
+ try:
125
+ TimeZone(zone_name)
126
+ except UnknownTimeZoneError as error:
127
+ dtype_name = str(column_dtype)
128
+ raise UnsupportedTypeError(dtype_name, Library.PANDAS) from error
129
+
130
+ nanosecond_column = column.dt.as_unit(NANOSECOND_UNIT)
131
+ named_column = nanosecond_column.dt.tz_convert(zone_name)
132
+
133
+ return named_column
134
+
135
+
136
+ def remove_utc_zone(column: Column) -> Column:
137
+ """
138
+ Return the instants of a datetime column with a time zone as datetimes without zone, in UTC.
139
+
140
+ .. versionadded:: 0.1.0
141
+
142
+ Parameters
143
+ ----------
144
+ column
145
+ A Table column of datetimes with a time zone.
146
+
147
+ Returns
148
+ -------
149
+ pandas.Series
150
+ The UTC clock time of each instant, without time zone.
151
+
152
+ Examples
153
+ --------
154
+ >>> import pandas
155
+ >>> from numdaplot.tables.frames.time_zones import remove_utc_zone
156
+ >>> times = pandas.Series(pandas.to_datetime(["2026-07-01 10:30"])).dt.tz_localize("Europe/Paris")
157
+ >>> remove_utc_zone(times).tolist()
158
+ [Timestamp('2026-07-01 08:30:00')]
159
+ """
160
+ utc_column = column.dt.tz_convert(UTC_ZONE_NAME)
161
+ naive_column = utc_column.dt.tz_localize(None)
162
+
163
+ return naive_column
164
+
165
+
166
+ def attribute_utc_results(column: Column, time_zone: TimeZone) -> Column:
167
+ """
168
+ Return datetimes computed in UTC without zone as the same instants in a time zone.
169
+
170
+ .. versionadded:: 0.1.0
171
+
172
+ Parameters
173
+ ----------
174
+ column
175
+ A Table column of datetimes without time zone, holding UTC clock times.
176
+ time_zone
177
+ The zone to show the instants in.
178
+
179
+ Returns
180
+ -------
181
+ pandas.Series
182
+ The instants in the zone.
183
+
184
+ Examples
185
+ --------
186
+ >>> import pandas
187
+ >>> import numdaplot as ndp
188
+ >>> from numdaplot.tables.frames.time_zones import attribute_utc_results
189
+ >>> times = pandas.Series(pandas.to_datetime(["2026-07-01 08:30"]))
190
+ >>> attribute_utc_results(times, ndp.TimeZone("Europe/Paris")).tolist()
191
+ [Timestamp('2026-07-01 10:30:00+0200', tz='Europe/Paris')]
192
+ """
193
+ utc_column = column.dt.tz_localize(UTC_ZONE_NAME)
194
+ zone_column = utc_column.dt.tz_convert(time_zone.name)
195
+
196
+ return zone_column
197
+
198
+
199
+ def attribute_source_time_zone(results: Column, source: Column) -> Column:
200
+ """
201
+ Return statistics of a column in the time zone of that column, when they are datetimes and it has one.
202
+
203
+ .. versionadded:: 0.1.0
204
+
205
+ Parameters
206
+ ----------
207
+ results
208
+ Statistics computed on the UTC clock times of ``source``.
209
+ source
210
+ The column the statistics are computed on.
211
+
212
+ Returns
213
+ -------
214
+ pandas.Series
215
+ The datetime results in the zone of the source column, or the results themselves otherwise.
216
+
217
+ Examples
218
+ --------
219
+ >>> import pandas
220
+ >>> from numdaplot.tables.frames.time_zones import attribute_source_time_zone
221
+ >>> source = pandas.Series(pandas.to_datetime(["2026-07-01 10:30"])).dt.tz_localize("Europe/Paris")
222
+ >>> attribute_source_time_zone(pandas.Series(pandas.to_datetime(["2026-07-01 08:30"])), source).tolist()
223
+ [Timestamp('2026-07-01 10:30:00+0200', tz='Europe/Paris')]
224
+ """
225
+ time_zone = find_column_time_zone(source)
226
+ if time_zone is None:
227
+ return results
228
+
229
+ is_datetime_result = results.dtype.kind == DATETIME_KIND
230
+ if not is_datetime_result:
231
+ return results
232
+
233
+ zone_results = attribute_utc_results(results, time_zone)
234
+
235
+ return zone_results
236
+
237
+
238
+ def require_no_time_zone(frame: pandas.DataFrame, operation: str) -> None:
239
+ """
240
+ Check that no column of a Table holds datetimes with a time zone.
241
+
242
+ .. versionadded:: 0.1.0
243
+
244
+ Parameters
245
+ ----------
246
+ frame
247
+ The columns of a Table.
248
+ operation
249
+ What needs datetimes without zone, shown in the error message, such as ``"An Excel file"``.
250
+
251
+ Raises
252
+ ------
253
+ TimeZoneMismatchError
254
+ When a column holds datetimes with a time zone.
255
+
256
+ Examples
257
+ --------
258
+ >>> import pandas
259
+ >>> import numdaplot as ndp
260
+ >>> from numdaplot.tables.frames.time_zones import require_no_time_zone
261
+ >>> times = pandas.Series(pandas.to_datetime(["2026-07-01 10:30"])).dt.tz_localize("UTC")
262
+ >>> try:
263
+ ... require_no_time_zone(pandas.DataFrame({"time": times}), "An Excel file")
264
+ ... except ndp.TimeZoneMismatchError as error:
265
+ ... print(error)
266
+ An Excel file needs datetimes without a time zone.
267
+ """
268
+ for _, column in frame.items():
269
+ time_zone = find_column_time_zone(column)
270
+ if time_zone is not None:
271
+ raise TimeZoneMismatchError(operation, False)
@@ -0,0 +1,5 @@
1
+ """
2
+ Join of two Tables on the values of key columns.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
@@ -0,0 +1,110 @@
1
+ """
2
+ Settings of a join of two Tables.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from typing import TYPE_CHECKING
11
+
12
+ from numdaplot.errors.empty_list_error import EmptyListError
13
+ from numdaplot.settings.field_types import require_field_types
14
+ from numdaplot.tables.columns.column_names import require_column_names
15
+ from numdaplot.tables.joining.join_mode import JoinMode
16
+ from numdaplot.tables.joining.join_suffixes import JoinSuffixes
17
+ from numdaplot.tables.joining.key_types import KeyTypes
18
+ from numdaplot.tables.joining.relation import Relation
19
+
20
+ if TYPE_CHECKING:
21
+ from numdaplot.tables.table import Table
22
+
23
+ KEY_ITEM_NAME = "key"
24
+
25
+
26
+ # eq=False because comparing two settings would compare their Tables with ==, which they refuse.
27
+ @dataclass(frozen=True, eq=False)
28
+ class Join:
29
+ """
30
+ Settings of a join of two Tables on the values of key columns, used by ``Table.join``.
31
+
32
+ Each row of the joined Table meets each row of the other Table having the same keys. A missing key never matches,
33
+ as in SQL. The rows of the joined Table come first, in their order, then the unmatched rows of the other Table.
34
+ The columns are the keys, then the other columns of the joined Table, then those of the other Table.
35
+
36
+ .. versionadded:: 0.1.0
37
+
38
+ Parameters
39
+ ----------
40
+ other
41
+ The Table joined to this one.
42
+ keys
43
+ The names of the key columns, which both Tables have: rename them with ``Table.rename`` first when they differ.
44
+ Several keys match on their combination. The join keeps its own copy of the list.
45
+ mode
46
+ Which rows are kept, ``JoinMode.INNER`` by default: only the rows whose keys match.
47
+ suffixes
48
+ The suffixes of the columns both Tables have besides the keys. When omitted, such a column raises
49
+ ``DuplicateColumnError``.
50
+ key_types
51
+ How close the types of each key must be, ``KeyTypes.SAME_FAMILY`` by default: ``INT64`` matches ``FLOAT64``.
52
+ relation
53
+ How many times each key may appear in this Table, the left one, and in the other Table, the right one,
54
+ ``Relation.MANY_TO_MANY`` by default, which checks nothing.
55
+
56
+ Raises
57
+ ------
58
+ InvalidTypeError
59
+ When a setting is not of the type its annotation names.
60
+ EmptyListError
61
+ When ``keys`` is an empty list.
62
+ InvalidColumnNameError
63
+ When a name in ``keys`` is an empty text.
64
+ DuplicateColumnError
65
+ When a name appears twice in ``keys``.
66
+
67
+ Examples
68
+ --------
69
+ >>> import numdaplot as ndp
70
+ >>> accidents = ndp.Table.from_arrays({"id": ndp.Array.from_list([1, 2]), "dep": ndp.Array.from_list(["75", "69"])})
71
+ >>> users = ndp.Table.from_arrays({"id": ndp.Array.from_list([1, 1]), "age": ndp.Array.from_list([34, 8])})
72
+ >>> accidents.join(ndp.Join(other=users, keys=["id"]))
73
+ id dep age
74
+ INT64 TEXT INT64
75
+ 0 1 75 34
76
+ 1 1 75 8
77
+ """
78
+
79
+ other: Table
80
+ keys: list[str]
81
+ mode: JoinMode = JoinMode.INNER
82
+ suffixes: JoinSuffixes | None = None
83
+ key_types: KeyTypes = KeyTypes.SAME_FAMILY
84
+ relation: Relation = Relation.MANY_TO_MANY
85
+
86
+ def __post_init__(self) -> None:
87
+ """
88
+ Check the keys and replace them with a copy that only the join holds.
89
+
90
+ .. versionadded:: 0.1.0
91
+
92
+ Raises
93
+ ------
94
+ InvalidTypeError
95
+ When a setting is not of the type its annotation names.
96
+ EmptyListError
97
+ When ``keys`` is an empty list.
98
+ InvalidColumnNameError
99
+ When a name in ``keys`` is an empty text.
100
+ DuplicateColumnError
101
+ When a name appears twice in ``keys``.
102
+ """
103
+ require_field_types(self)
104
+
105
+ if self.keys == []:
106
+ raise EmptyListError(KEY_ITEM_NAME)
107
+
108
+ require_column_names(self.keys)
109
+ keys_copy = list(self.keys)
110
+ object.__setattr__(self, "keys", keys_copy)