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,526 @@
1
+ """
2
+ Conversion of Table columns between texts, dates, datetimes and durations.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from typing import Any, cast
8
+
9
+ import numpy
10
+ import pandas
11
+
12
+ from numdaplot.arrays.conversion.time_text import format_durations
13
+ from numdaplot.data_types.type import Type
14
+ from numdaplot.dates.custom_date_format import DATE_CODES, TIME_CODES, CustomDateFormat
15
+ from numdaplot.dates.date_format import DateFormat
16
+ from numdaplot.errors.time_overflow_error import TimeOverflowError
17
+ from numdaplot.errors.value_conversion_error import ValueConversionError
18
+ from numdaplot.tables.columns.date_range import is_text_out_of_range
19
+ from numdaplot.tables.frames.column import Column
20
+ from numdaplot.tables.frames.column_values import find_column_missing, find_column_type
21
+ from numdaplot.tables.frames.frame_normalization import convert_to_type
22
+ from numdaplot.tables.frames.time_zones import find_column_time_zone
23
+
24
+ DAY_FIRST = r"(?P<day>\d{1,2})(?P<separator>[/.-])(?P<month>\d{1,2})(?P=separator)(?P<year>\d{4})"
25
+ MONTH_FIRST = r"(?P<month>\d{1,2})(?P<separator>[/.-])(?P<day>\d{1,2})(?P=separator)(?P<year>\d{4})"
26
+ YEAR_FIRST = r"(?P<year>\d{4})(?P<separator>[/.-])(?P<month>\d{1,2})(?P=separator)(?P<day>\d{1,2})"
27
+ CLOCK = r"(?P<hour>\d{1,2}):(?P<minute>\d{2})(?::(?P<second>\d{2}))?"
28
+ FULL_TEXT = "^{pattern}$"
29
+ READ_PATTERNS = {
30
+ DateFormat.DAY_MONTH_YEAR: DAY_FIRST,
31
+ DateFormat.MONTH_DAY_YEAR: MONTH_FIRST,
32
+ DateFormat.YEAR_MONTH_DAY: YEAR_FIRST,
33
+ DateFormat.DAY_MONTH_YEAR_TIME: DAY_FIRST + " " + CLOCK,
34
+ DateFormat.MONTH_DAY_YEAR_TIME: MONTH_FIRST + " " + CLOCK,
35
+ DateFormat.YEAR_MONTH_DAY_TIME: YEAR_FIRST + "[ T]" + CLOCK,
36
+ }
37
+ WRITE_PATTERNS = {
38
+ DateFormat.DAY_MONTH_YEAR: "%d/%m/%Y",
39
+ DateFormat.MONTH_DAY_YEAR: "%m/%d/%Y",
40
+ DateFormat.YEAR_MONTH_DAY: "%Y-%m-%d",
41
+ DateFormat.DAY_MONTH_YEAR_TIME: "%d/%m/%Y %H:%M:%S",
42
+ DateFormat.MONTH_DAY_YEAR_TIME: "%m/%d/%Y %H:%M:%S",
43
+ DateFormat.YEAR_MONTH_DAY_TIME: "%Y-%m-%d %H:%M:%S",
44
+ }
45
+ # Without a format, a datetime takes its time when it has one: to_csv leaves out a time of midnight on every row.
46
+ DEFAULT_READ_PATTERNS = {
47
+ Type.DATE: YEAR_FIRST,
48
+ Type.DATETIME: YEAR_FIRST + "(?:[ T]" + CLOCK + ")?",
49
+ }
50
+ TIME_FORMATS = frozenset(
51
+ {DateFormat.DAY_MONTH_YEAR_TIME, DateFormat.MONTH_DAY_YEAR_TIME, DateFormat.YEAR_MONTH_DAY_TIME}
52
+ )
53
+ DEFAULT_FORMATS = {
54
+ Type.DATE: DateFormat.YEAR_MONTH_DAY,
55
+ Type.DATETIME: DateFormat.YEAR_MONTH_DAY_TIME,
56
+ }
57
+ YEAR_PART = "year"
58
+ DATE_PARTS = [YEAR_PART, "month", "day", "hour", "minute", "second"]
59
+ NO_CLOCK_PART = "0"
60
+ # pandas carries an hour of 25 over to the next day, so impossible clock parts are refused first.
61
+ CLOCK_LIMITS = {"hour": 24, "minute": 60, "second": 60}
62
+ COLUMN_AXIS = "columns"
63
+ OFFSET_CODE = "%z"
64
+ COMPACT_OFFSET = r"([+-]\d{2})(\d{2})$"
65
+ SEPARATED_OFFSET = r"\1:\2"
66
+ NANOSECOND_DTYPE = "timedelta64[ns]"
67
+ CONVERT_OPERATION = "convert"
68
+
69
+
70
+ def has_date(date_format: DateFormat | CustomDateFormat) -> bool:
71
+ """
72
+ Return whether a format reads or writes a date: every common format does, and a pattern with a date code.
73
+
74
+ .. versionadded:: 0.1.0
75
+
76
+ Parameters
77
+ ----------
78
+ date_format
79
+ A common format or a custom pattern.
80
+
81
+ Returns
82
+ -------
83
+ bool
84
+ False only for a custom pattern of a time without date.
85
+
86
+ Examples
87
+ --------
88
+ >>> import numdaplot as ndp
89
+ >>> from numdaplot.tables.columns.date_conversion import has_date
90
+ >>> has_date(ndp.DateFormat.DAY_MONTH_YEAR), has_date(ndp.CustomDateFormat("%H:%M"))
91
+ (True, False)
92
+ """
93
+ if not isinstance(date_format, CustomDateFormat):
94
+ return True
95
+
96
+ pattern_has_date = any(date_code in date_format.pattern for date_code in DATE_CODES)
97
+
98
+ return pattern_has_date
99
+
100
+
101
+ def has_time(date_format: DateFormat | CustomDateFormat) -> bool:
102
+ """
103
+ Return whether a date format reads or writes a time.
104
+
105
+ .. versionadded:: 0.1.0
106
+
107
+ Parameters
108
+ ----------
109
+ date_format
110
+ A common format or a custom pattern.
111
+
112
+ Returns
113
+ -------
114
+ bool
115
+ True for the ``_TIME`` members of ``DateFormat``, and the patterns with an hour, minute, second or
116
+ AM/PM code.
117
+
118
+ Examples
119
+ --------
120
+ >>> import numdaplot as ndp
121
+ >>> from numdaplot.tables.columns.date_conversion import has_time
122
+ >>> has_time(ndp.DateFormat.DAY_MONTH_YEAR)
123
+ False
124
+ """
125
+ if isinstance(date_format, CustomDateFormat):
126
+ pattern_has_time = any(time_code in date_format.pattern for time_code in TIME_CODES)
127
+ return pattern_has_time
128
+
129
+ format_has_time = date_format in TIME_FORMATS
130
+
131
+ return format_has_time
132
+
133
+
134
+ def parse_dates(column: Column, target_type: Type, date_format: DateFormat | CustomDateFormat | None) -> Column:
135
+ """
136
+ Return a text column read as dates or datetimes.
137
+
138
+ .. versionadded:: 0.1.0
139
+
140
+ Parameters
141
+ ----------
142
+ column
143
+ A text or category column.
144
+ target_type
145
+ ``DATE`` or ``DATETIME``.
146
+ date_format
147
+ How the texts are written. A format without time gives datetimes at midnight. When omitted, texts are read
148
+ as year, month and day, and for datetimes each text may also have a time.
149
+
150
+ Returns
151
+ -------
152
+ Column
153
+ The dates or datetimes, missing texts staying missing.
154
+
155
+ Raises
156
+ ------
157
+ ValueConversionError
158
+ At the first text that does not follow the format or is not a real date, such as ``"31/02/2026"``.
159
+ TimeOverflowError
160
+ When the version of pandas in use reads no date for a text holding a real date out of range.
161
+
162
+ Examples
163
+ --------
164
+ >>> import pandas
165
+ >>> import numdaplot as ndp
166
+ >>> from numdaplot.tables.columns.date_conversion import parse_dates
167
+ >>> texts = pandas.Series(["31/12/2026", None], dtype="string")
168
+ >>> parse_dates(texts, ndp.Type.DATE, ndp.DateFormat.DAY_MONTH_YEAR).tolist()
169
+ [Timestamp('2026-12-31 00:00:00'), NaT]
170
+ >>> times = pandas.Series(["2026-12-31", "2026-12-31 14:30"], dtype="string")
171
+ >>> parse_dates(times, ndp.Type.DATETIME, None).tolist()
172
+ [Timestamp('2026-12-31 00:00:00'), Timestamp('2026-12-31 14:30:00')]
173
+ """
174
+ source_type = find_column_type(column)
175
+ text_column = convert_to_type(column, Type.TEXT)
176
+ stripped_texts = text_column.str.strip()
177
+ moments = read_moments(stripped_texts, target_type, date_format)
178
+ missing = find_column_missing(column)
179
+ unread_moments = moments.isna()
180
+ unread = unread_moments.to_numpy(dtype=numpy.bool_)
181
+ rejected = unread & ~missing
182
+ has_rejected = bool(rejected.any())
183
+ if has_rejected:
184
+ rejected_texts = text_column[rejected]
185
+ first_rejected_text = rejected_texts.iloc[0]
186
+ read_format = find_read_format(target_type, date_format)
187
+ is_out_of_range = is_text_out_of_range(first_rejected_text, read_format)
188
+ if is_out_of_range:
189
+ raise TimeOverflowError(CONVERT_OPERATION, target_type.name)
190
+
191
+ format_name = find_format_name(target_type, date_format)
192
+ raise ValueConversionError(source_type.name, target_type.name, first_rejected_text, format_name)
193
+
194
+ dates = convert_to_type(moments, target_type)
195
+
196
+ return dates
197
+
198
+
199
+ def read_moments(texts: Column, target_type: Type, date_format: DateFormat | CustomDateFormat | None) -> Column:
200
+ """
201
+ Return texts read as datetimes, NaT where a text does not follow the format.
202
+
203
+ .. versionadded:: 0.1.0
204
+
205
+ Parameters
206
+ ----------
207
+ texts
208
+ A text column, without spaces around the texts.
209
+ target_type
210
+ ``DATE`` or ``DATETIME``, which sets how texts are read without a format.
211
+ date_format
212
+ How the texts are written, the default of the type when omitted.
213
+
214
+ Returns
215
+ -------
216
+ Column
217
+ The datetimes, NaT for a missing text or a text that does not follow the format.
218
+
219
+ Examples
220
+ --------
221
+ >>> import pandas
222
+ >>> import numdaplot as ndp
223
+ >>> from numdaplot.tables.columns.date_conversion import read_moments
224
+ >>> texts = pandas.Series(["2026-12-31 14:30"], dtype="string")
225
+ >>> read_moments(texts, ndp.Type.DATE, None).tolist()
226
+ [NaT]
227
+ >>> read_moments(texts, ndp.Type.DATETIME, None).tolist()
228
+ [Timestamp('2026-12-31 14:30:00')]
229
+ """
230
+ if isinstance(date_format, CustomDateFormat):
231
+ custom_moments = pandas.to_datetime(texts, format=date_format.pattern, errors="coerce")
232
+ return custom_moments
233
+
234
+ if date_format is None:
235
+ default_pattern = DEFAULT_READ_PATTERNS[target_type]
236
+ default_moments = read_date_pattern(texts, default_pattern)
237
+ return default_moments
238
+
239
+ common_moments = read_common_format(texts, date_format)
240
+
241
+ return common_moments
242
+
243
+
244
+ def find_read_format(target_type: Type, date_format: DateFormat | CustomDateFormat | None) -> str | CustomDateFormat:
245
+ """
246
+ Return what texts are read with: a custom format, or the regular expression of a common or default format.
247
+
248
+ .. versionadded:: 0.1.0
249
+
250
+ Parameters
251
+ ----------
252
+ target_type
253
+ ``DATE`` or ``DATETIME``, which sets how texts are read without a format.
254
+ date_format
255
+ How the texts are written, the default of the type when omitted.
256
+
257
+ Returns
258
+ -------
259
+ str or CustomDateFormat
260
+ The custom format, or the regular expression.
261
+
262
+ Examples
263
+ --------
264
+ >>> import numdaplot as ndp
265
+ >>> from numdaplot.tables.columns.date_conversion import YEAR_FIRST, find_read_format
266
+ >>> find_read_format(ndp.Type.DATE, None) == YEAR_FIRST
267
+ True
268
+ """
269
+ if isinstance(date_format, CustomDateFormat):
270
+ return date_format
271
+
272
+ if date_format is None:
273
+ default_pattern = DEFAULT_READ_PATTERNS[target_type]
274
+ return default_pattern
275
+
276
+ common_pattern = READ_PATTERNS[date_format]
277
+
278
+ return common_pattern
279
+
280
+
281
+ def find_format_name(target_type: Type, date_format: DateFormat | CustomDateFormat | None) -> str:
282
+ """
283
+ Return how an error names the format texts were read with.
284
+
285
+ .. versionadded:: 0.1.0
286
+
287
+ Parameters
288
+ ----------
289
+ target_type
290
+ ``DATE`` or ``DATETIME``, which sets the default format.
291
+ date_format
292
+ The format, the default of the type when omitted.
293
+
294
+ Returns
295
+ -------
296
+ str
297
+ The pattern of a custom format, or the name of the ``DateFormat`` member.
298
+
299
+ Examples
300
+ --------
301
+ >>> import numdaplot as ndp
302
+ >>> from numdaplot.tables.columns.date_conversion import find_format_name
303
+ >>> find_format_name(ndp.Type.DATETIME, None)
304
+ 'YEAR_MONTH_DAY_TIME'
305
+ >>> find_format_name(ndp.Type.DATE, ndp.CustomDateFormat("%d %b %Y"))
306
+ '%d %b %Y'
307
+ """
308
+ if isinstance(date_format, CustomDateFormat):
309
+ return date_format.pattern
310
+
311
+ common_format = date_format
312
+ if common_format is None:
313
+ common_format = DEFAULT_FORMATS[target_type]
314
+
315
+ return common_format.name
316
+
317
+
318
+ def read_common_format(texts: Column, date_format: DateFormat) -> Column:
319
+ """
320
+ Return texts read with a common date format, NaT where a text does not follow it.
321
+
322
+ .. versionadded:: 0.1.0
323
+
324
+ Parameters
325
+ ----------
326
+ texts
327
+ A text column, without spaces around the texts.
328
+ date_format
329
+ The order of the day, the month and the year, and whether a time follows.
330
+
331
+ Returns
332
+ -------
333
+ Column
334
+ The datetimes, NaT for a missing text, a text that does not follow the format, or an impossible date
335
+ or time such as ``"31/02/2026"`` or ``"25:00"``.
336
+
337
+ Examples
338
+ --------
339
+ >>> import pandas
340
+ >>> import numdaplot as ndp
341
+ >>> from numdaplot.tables.columns.date_conversion import read_common_format
342
+ >>> texts = pandas.Series(["2026-12-31T14:30", "31/12/2026"], dtype="string")
343
+ >>> read_common_format(texts, ndp.DateFormat.YEAR_MONTH_DAY_TIME).tolist()
344
+ [Timestamp('2026-12-31 14:30:00'), NaT]
345
+ """
346
+ read_pattern = READ_PATTERNS[date_format]
347
+ moments = read_date_pattern(texts, read_pattern)
348
+
349
+ return moments
350
+
351
+
352
+ def read_date_pattern(texts: Column, read_pattern: str) -> Column:
353
+ """
354
+ Return texts read with a regular expression naming the parts of a date, NaT where a text does not follow it.
355
+
356
+ .. versionadded:: 0.1.0
357
+
358
+ Parameters
359
+ ----------
360
+ texts
361
+ A text column, without spaces around the texts.
362
+ read_pattern
363
+ The regular expression, whose groups are named after ``DATE_PARTS``. A part it leaves out counts as 0.
364
+
365
+ Returns
366
+ -------
367
+ Column
368
+ The datetimes, NaT for a missing text, a text that does not follow the pattern, or an impossible date
369
+ or time such as ``"31/02/2026"`` or ``"25:00"``.
370
+
371
+ Examples
372
+ --------
373
+ >>> import pandas
374
+ >>> from numdaplot.tables.columns.date_conversion import YEAR_FIRST, read_date_pattern
375
+ >>> texts = pandas.Series(["2026-12-31", "31/12/2026"], dtype="string")
376
+ >>> read_date_pattern(texts, YEAR_FIRST).tolist()
377
+ [Timestamp('2026-12-31 00:00:00'), NaT]
378
+ """
379
+ full_pattern = FULL_TEXT.format(pattern=read_pattern)
380
+ # Some versions of the pandas stubs leave the columns of the extracted parts unknown.
381
+ extracted_parts = cast(Any, texts.str.extract(full_pattern))
382
+ date_parts = extracted_parts.reindex(columns=DATE_PARTS, fill_value=NO_CLOCK_PART)
383
+ filled_parts = date_parts.fillna(NO_CLOCK_PART)
384
+ years = extracted_parts[YEAR_PART]
385
+ has_date = years.notna()
386
+ present_parts = filled_parts.where(has_date)
387
+ number_parts = present_parts.astype(numpy.float64)
388
+ clock_parts = number_parts[CLOCK_LIMITS.keys()]
389
+ clock_limits = pandas.Series(CLOCK_LIMITS)
390
+ out_of_range = clock_parts.ge(clock_limits)
391
+ has_impossible_time = out_of_range.any(axis=COLUMN_AXIS)
392
+ possible_parts = cast(pandas.DataFrame, number_parts.mask(has_impossible_time))
393
+ moments = pandas.to_datetime(possible_parts, errors="coerce")
394
+
395
+ return moments
396
+
397
+
398
+ def write_dates(column: Column, date_format: DateFormat | CustomDateFormat | None) -> Column:
399
+ """
400
+ Return a date or datetime column written as texts.
401
+
402
+ .. versionadded:: 0.1.0
403
+
404
+ Parameters
405
+ ----------
406
+ column
407
+ A date or datetime column.
408
+ date_format
409
+ How to write the texts: year, month and day separated by ``"-"``, then the time for datetimes, when
410
+ omitted. A datetime with a time zone is written with its offset, such as ``"+02:00"``, except with
411
+ a custom pattern.
412
+
413
+ Returns
414
+ -------
415
+ Column
416
+ The texts, missing values staying missing.
417
+
418
+ Examples
419
+ --------
420
+ >>> import pandas
421
+ >>> import numdaplot as ndp
422
+ >>> from numdaplot.tables.columns.date_conversion import write_dates
423
+ >>> days = pandas.Series(pandas.to_datetime(["2026-12-31", None])).astype("datetime64[s]")
424
+ >>> write_dates(days, ndp.DateFormat.DAY_MONTH_YEAR).tolist()
425
+ ['31/12/2026', <NA>]
426
+ """
427
+ source_type = find_column_type(column)
428
+ chosen_format = DEFAULT_FORMATS[source_type]
429
+ if date_format is not None:
430
+ chosen_format = date_format
431
+
432
+ if isinstance(chosen_format, CustomDateFormat):
433
+ custom_texts = column.dt.strftime(chosen_format.pattern)
434
+ custom_column = convert_to_type(custom_texts, Type.TEXT)
435
+ return custom_column
436
+
437
+ write_pattern = WRITE_PATTERNS[chosen_format]
438
+ time_zone = find_column_time_zone(column)
439
+ writes_offset = time_zone is not None and chosen_format in TIME_FORMATS
440
+ if not writes_offset:
441
+ plain_texts = column.dt.strftime(write_pattern)
442
+ plain_column = convert_to_type(plain_texts, Type.TEXT)
443
+ return plain_column
444
+
445
+ offset_pattern = write_pattern + OFFSET_CODE
446
+ compact_texts = column.dt.strftime(offset_pattern)
447
+ offset_texts = compact_texts.str.replace(COMPACT_OFFSET, SEPARATED_OFFSET, regex=True)
448
+ offset_column = convert_to_type(offset_texts, Type.TEXT)
449
+
450
+ return offset_column
451
+
452
+
453
+ def write_durations(column: Column) -> Column:
454
+ """
455
+ Return a duration column written as texts, as pandas writes durations.
456
+
457
+ .. versionadded:: 0.1.0
458
+
459
+ Parameters
460
+ ----------
461
+ column
462
+ A duration column.
463
+
464
+ Returns
465
+ -------
466
+ Column
467
+ Texts such as ``"1 days 12:00:00"``, missing values staying missing.
468
+
469
+ Examples
470
+ --------
471
+ >>> import pandas
472
+ >>> from numdaplot.tables.columns.date_conversion import write_durations
473
+ >>> write_durations(pandas.Series(pandas.to_timedelta(["36h", None]))).tolist()
474
+ ['1 days 12:00:00', <NA>]
475
+ """
476
+ durations = column.to_numpy(dtype=NANOSECOND_DTYPE)
477
+ duration_texts = format_durations(durations)
478
+ # Some versions of the pandas stubs leave the type of a Series built from numpy texts unknown.
479
+ text_column = cast(Any, pandas.Series(duration_texts))
480
+ missing = find_column_missing(column)
481
+ present_texts = text_column.mask(missing)
482
+ texts = convert_to_type(present_texts, Type.TEXT)
483
+
484
+ return texts
485
+
486
+
487
+ def convert_moments(column: Column, target_type: Type) -> Column:
488
+ """
489
+ Return dates as datetimes at midnight, or datetimes as their dates.
490
+
491
+ .. versionadded:: 0.1.0
492
+
493
+ Parameters
494
+ ----------
495
+ column
496
+ A date or datetime column.
497
+ target_type
498
+ ``DATE`` or ``DATETIME``.
499
+
500
+ Returns
501
+ -------
502
+ Column
503
+ The converted column. A datetime with a time zone gives the date of its clock time in that zone.
504
+
505
+ Examples
506
+ --------
507
+ >>> import pandas
508
+ >>> import numdaplot as ndp
509
+ >>> from numdaplot.tables.columns.date_conversion import convert_moments
510
+ >>> times = pandas.Series(pandas.to_datetime(["2026-12-31 14:30"]))
511
+ >>> convert_moments(times, ndp.Type.DATE).tolist()
512
+ [Timestamp('2026-12-31 00:00:00')]
513
+ """
514
+ if target_type == Type.DATETIME:
515
+ datetimes = convert_to_type(column, Type.DATETIME)
516
+ return datetimes
517
+
518
+ clock_times = column
519
+ time_zone = find_column_time_zone(column)
520
+ if time_zone is not None:
521
+ clock_times = column.dt.tz_localize(None)
522
+
523
+ midnights = clock_times.dt.normalize()
524
+ dates = convert_to_type(midnights, Type.DATE)
525
+
526
+ return dates
@@ -0,0 +1,135 @@
1
+ """
2
+ Detection of texts holding a real date beyond what a count of nanoseconds can hold.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ import re
8
+ from datetime import datetime
9
+
10
+ from numdaplot.arrays.operations.time_bounds import is_python_time_in_range
11
+ from numdaplot.dates.custom_date_format import CustomDateFormat
12
+
13
+ NO_CLOCK_PART = "0"
14
+ DATE_PART_NAMES = ("year", "month", "day", "hour", "minute", "second")
15
+
16
+
17
+ def read_moment_text(text: str, read_pattern: str) -> datetime | None:
18
+ r"""
19
+ Return a text read as a datetime with a regular expression naming the parts of a date.
20
+
21
+ .. versionadded:: 0.1.0
22
+
23
+ Parameters
24
+ ----------
25
+ text
26
+ A text without spaces around it.
27
+ read_pattern
28
+ The regular expression, whose groups are named after the parts of a date. A part it leaves out counts as 0.
29
+
30
+ Returns
31
+ -------
32
+ datetime or None
33
+ The datetime, or ``None`` for a text that does not follow the pattern or is not a real date.
34
+
35
+ Examples
36
+ --------
37
+ >>> from numdaplot.tables.columns.date_range import read_moment_text
38
+ >>> read_moment_text("3000-12-31", r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})")
39
+ datetime.datetime(3000, 12, 31, 0, 0)
40
+ >>> read_moment_text("3000-02-31", r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})") is None
41
+ True
42
+ """
43
+ match = re.fullmatch(read_pattern, text)
44
+ if match is None:
45
+ return None
46
+
47
+ found_parts = match.groupdict()
48
+ part_values = list[int]()
49
+ for part_name in DATE_PART_NAMES:
50
+ part_text = found_parts.get(part_name) or NO_CLOCK_PART
51
+ part_value = int(part_text)
52
+ part_values.append(part_value)
53
+
54
+ year, month, day, hour, minute, second = part_values
55
+ try:
56
+ moment = datetime(year, month, day, hour, minute, second)
57
+ except ValueError:
58
+ return None
59
+
60
+ return moment
61
+
62
+
63
+ def read_custom_text(text: str, date_format: CustomDateFormat) -> datetime | None:
64
+ """
65
+ Return a text read as a datetime with a custom date format.
66
+
67
+ .. versionadded:: 0.1.0
68
+
69
+ Parameters
70
+ ----------
71
+ text
72
+ A text without spaces around it.
73
+ date_format
74
+ The custom pattern.
75
+
76
+ Returns
77
+ -------
78
+ datetime or None
79
+ The datetime, or ``None`` for a text that does not follow the pattern or is not a real date.
80
+
81
+ Examples
82
+ --------
83
+ >>> import numdaplot as ndp
84
+ >>> from numdaplot.tables.columns.date_range import read_custom_text
85
+ >>> read_custom_text("31 Dec 3000", ndp.CustomDateFormat("%d %b %Y"))
86
+ datetime.datetime(3000, 12, 31, 0, 0)
87
+ """
88
+ try:
89
+ moment = datetime.strptime(text, date_format.pattern)
90
+ except ValueError:
91
+ return None
92
+
93
+ return moment
94
+
95
+
96
+ def is_text_out_of_range(text: str, read_format: str | CustomDateFormat) -> bool:
97
+ """
98
+ Return whether a text holds a real date that a count of nanoseconds cannot hold.
99
+
100
+ .. versionadded:: 0.1.0
101
+
102
+ Parameters
103
+ ----------
104
+ text
105
+ A text, with or without spaces around it.
106
+ read_format
107
+ A regular expression naming the parts of a date, or a custom pattern.
108
+
109
+ Returns
110
+ -------
111
+ bool
112
+ True for a real date out of range, False for an accepted date or for a text that is not a date.
113
+
114
+ Examples
115
+ --------
116
+ >>> import numdaplot as ndp
117
+ >>> from numdaplot.tables.columns.date_range import is_text_out_of_range
118
+ >>> is_text_out_of_range("31 Dec 3000", ndp.CustomDateFormat("%d %b %Y"))
119
+ True
120
+ >>> is_text_out_of_range("31 Dec 2026", ndp.CustomDateFormat("%d %b %Y"))
121
+ False
122
+ """
123
+ stripped_text = text.strip()
124
+ moment = None
125
+ if isinstance(read_format, CustomDateFormat):
126
+ moment = read_custom_text(stripped_text, read_format)
127
+ else:
128
+ moment = read_moment_text(stripped_text, read_format)
129
+
130
+ if moment is None:
131
+ return False
132
+
133
+ moment_in_range = is_python_time_in_range(moment)
134
+
135
+ return not moment_in_range