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,177 @@
1
+ """
2
+ Colors of the series of a chart.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from collections.abc import Sequence
8
+ from itertools import cycle, islice
9
+
10
+ from numdaplot.errors.invalid_color_count_error import InvalidColorCountError
11
+ from numdaplot.plots.style.color import Color
12
+ from numdaplot.plots.style.custom_color import CustomColor
13
+
14
+ COLOR_CODES = {
15
+ Color.BLUE: "#1f77b4",
16
+ Color.ORANGE: "#ff7f0e",
17
+ Color.GREEN: "#2ca02c",
18
+ Color.RED: "#d62728",
19
+ Color.PURPLE: "#9467bd",
20
+ Color.BROWN: "#8c564b",
21
+ Color.PINK: "#e377c2",
22
+ Color.GRAY: "#7f7f7f",
23
+ Color.OLIVE: "#bcbd22",
24
+ Color.CYAN: "#17becf",
25
+ Color.BLACK: "#000000",
26
+ }
27
+ DEFAULT_CYCLE = (
28
+ Color.BLUE,
29
+ Color.ORANGE,
30
+ Color.GREEN,
31
+ Color.RED,
32
+ Color.PURPLE,
33
+ Color.BROWN,
34
+ Color.PINK,
35
+ Color.GRAY,
36
+ Color.OLIVE,
37
+ Color.CYAN,
38
+ )
39
+
40
+
41
+ def find_color_code(color: Color | CustomColor) -> str:
42
+ """
43
+ Return the ``#rrggbb`` code of a color.
44
+
45
+ .. versionadded:: 0.1.0
46
+
47
+ Parameters
48
+ ----------
49
+ color
50
+ A named or a custom color.
51
+
52
+ Returns
53
+ -------
54
+ str
55
+ The code matplotlib draws.
56
+
57
+ Examples
58
+ --------
59
+ >>> import numdaplot as ndp
60
+ >>> from numdaplot.plots.drawing.series_colors import find_color_code
61
+ >>> find_color_code(ndp.Color.RED)
62
+ '#d62728'
63
+ """
64
+ if isinstance(color, CustomColor):
65
+ return color.code
66
+
67
+ color_code = COLOR_CODES[color]
68
+
69
+ return color_code
70
+
71
+
72
+ def choose_series_colors(colors: Sequence[Color | CustomColor] | None, series_count: int) -> list[str]:
73
+ """
74
+ Return the color code of each series of a chart.
75
+
76
+ .. versionadded:: 0.1.0
77
+
78
+ Parameters
79
+ ----------
80
+ colors
81
+ One color per series, or None for the colors of the default cycle, which starts again after ten series.
82
+ series_count
83
+ The number of series drawn.
84
+
85
+ Returns
86
+ -------
87
+ list[str]
88
+ One ``#rrggbb`` code per series.
89
+
90
+ Raises
91
+ ------
92
+ InvalidColorCountError
93
+ When the colors are not exactly one per series.
94
+
95
+ Examples
96
+ --------
97
+ >>> from numdaplot.plots.drawing.series_colors import choose_series_colors
98
+ >>> choose_series_colors(None, 2)
99
+ ['#1f77b4', '#ff7f0e']
100
+ """
101
+ chosen_colors = colors
102
+ if chosen_colors is None:
103
+ cycled_colors = take_cycle_colors(0, series_count)
104
+ chosen_colors = list[Color | CustomColor](cycled_colors)
105
+
106
+ require_color_count(chosen_colors, series_count)
107
+ color_codes = [find_color_code(color) for color in chosen_colors]
108
+
109
+ return color_codes
110
+
111
+
112
+ def require_color_count(colors: Sequence[Color | CustomColor] | None, series_count: int) -> None:
113
+ """
114
+ Check that a chart has one color per series, or no colors at all.
115
+
116
+ .. versionadded:: 0.1.0
117
+
118
+ Parameters
119
+ ----------
120
+ colors
121
+ The colors of the chart, or None for the colors of the default cycle.
122
+ series_count
123
+ The number of series drawn.
124
+
125
+ Raises
126
+ ------
127
+ InvalidColorCountError
128
+ When the colors are not exactly one per series.
129
+
130
+ Examples
131
+ --------
132
+ >>> import numdaplot as ndp
133
+ >>> from numdaplot.plots.drawing.series_colors import require_color_count
134
+ >>> try:
135
+ ... require_color_count([ndp.Color.RED], 2)
136
+ ... except ndp.InvalidColorCountError as error:
137
+ ... print(error)
138
+ Expected one color per series, 2 series and 1 color(s) given.
139
+ """
140
+ if colors is None:
141
+ return
142
+
143
+ color_count = len(colors)
144
+ if color_count != series_count:
145
+ raise InvalidColorCountError(color_count, series_count)
146
+
147
+
148
+ def take_cycle_colors(start: int, color_count: int) -> list[Color]:
149
+ """
150
+ Return colors of the default cycle, which starts again after ten colors.
151
+
152
+ .. versionadded:: 0.1.0
153
+
154
+ Parameters
155
+ ----------
156
+ start
157
+ The number of colors of the cycle already used, from 0.
158
+ color_count
159
+ The number of colors taken.
160
+
161
+ Returns
162
+ -------
163
+ list[Color]
164
+ The colors following the ones already used.
165
+
166
+ Examples
167
+ --------
168
+ >>> from numdaplot.plots.drawing.series_colors import take_cycle_colors
169
+ >>> take_cycle_colors(9, 2)
170
+ [<Color.CYAN: 10>, <Color.BLUE: 1>]
171
+ """
172
+ end = start + color_count
173
+ default_colors = cycle(DEFAULT_CYCLE)
174
+ taken_colors = islice(default_colors, start, end)
175
+ color_list = list(taken_colors)
176
+
177
+ return color_list
@@ -0,0 +1,45 @@
1
+ """
2
+ Width and height of a Plot in inches, as matplotlib counts sizes.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from numdaplot.plots.style.size import Size
8
+ from numdaplot.plots.style.size_unit import SizeUnit
9
+
10
+ UNITS_PER_INCH = {
11
+ SizeUnit.INCH: 1.0,
12
+ SizeUnit.CENTIMETER: 2.54,
13
+ SizeUnit.PIXEL: 100.0,
14
+ }
15
+
16
+
17
+ def convert_size_to_inches(size: Size) -> tuple[float, float]:
18
+ """
19
+ Return the width and the height of a size in inches, as matplotlib counts sizes.
20
+
21
+ .. versionadded:: 0.1.0
22
+
23
+ Parameters
24
+ ----------
25
+ size
26
+ The size, in any ``SizeUnit``.
27
+
28
+ Returns
29
+ -------
30
+ tuple[float, float]
31
+ The width and the height, a pixel counting for a hundredth of an inch.
32
+
33
+ Examples
34
+ --------
35
+ >>> import numdaplot as ndp
36
+ >>> from numdaplot.plots.drawing.size_inches import convert_size_to_inches
37
+ >>> convert_size_to_inches(ndp.Size(width=600, height=300, unit=ndp.SizeUnit.PIXEL))
38
+ (6.0, 3.0)
39
+ """
40
+ units_per_inch = UNITS_PER_INCH[size.unit]
41
+ width_inches = size.width / units_per_inch
42
+ height_inches = size.height / units_per_inch
43
+ inches = (width_inches, height_inches)
44
+
45
+ return inches
@@ -0,0 +1,410 @@
1
+ """
2
+ Drawing of the PlotStyle of a Plot: title, axis labels, rotation of the values, grid lines and legend.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ # The matplotlib stubs leave the keyword arguments of most drawing methods untyped.
8
+ # pyright: reportUnknownMemberType=false
9
+
10
+ from operator import itemgetter
11
+ from typing import Any, Literal, cast
12
+
13
+ from matplotlib.artist import Artist
14
+ from matplotlib.axes import Axes
15
+ from matplotlib.collections import PathCollection
16
+ from matplotlib.container import Container
17
+ from matplotlib.figure import Figure
18
+ from matplotlib.transforms import ScaledTranslation, Transform
19
+
20
+ from numdaplot.plots.drawing.chart_texts import choose_label, choose_title
21
+ from numdaplot.plots.drawing.drawn_chart import DrawnChart
22
+ from numdaplot.plots.drawing.grid_axes import GridAxes
23
+ from numdaplot.plots.drawing.plot_axes import PlotAxes
24
+ from numdaplot.plots.drawing.tick_rotation import rotate_x_labels, rotate_y_labels
25
+ from numdaplot.plots.style.grid_lines import GridLines
26
+ from numdaplot.plots.style.legend_position import LegendPosition
27
+ from numdaplot.plots.style.plot_style import PlotStyle
28
+
29
+ type LegendLocation = Literal["best", "upper left", "upper right", "lower left", "lower right", "center left"]
30
+ """A legend location matplotlib accepts.
31
+
32
+ .. versionadded:: 0.1.0
33
+ """
34
+
35
+ LEGEND_LOCATIONS = dict[LegendPosition, LegendLocation](
36
+ {
37
+ LegendPosition.BEST: "best",
38
+ LegendPosition.UPPER_LEFT: "upper left",
39
+ LegendPosition.UPPER_RIGHT: "upper right",
40
+ LegendPosition.LOWER_LEFT: "lower left",
41
+ LegendPosition.LOWER_RIGHT: "lower right",
42
+ LegendPosition.OUTSIDE_RIGHT: "center left",
43
+ }
44
+ )
45
+ OUTSIDE_ANCHOR = (1.02, 0.5)
46
+ # The grid lines of bars only cross the axis of their values, which depends on their direction.
47
+ VALUE_GRID_AXES = dict[bool, Literal["x", "y"]]({True: "x", False: "y"})
48
+ MINIMUM_LEGEND_ENTRIES = 2
49
+ LEGEND_POINT_AREA = 36.0
50
+ LEGEND_POINT_OPACITY = 1.0
51
+
52
+
53
+ def apply_style(plot_axes: PlotAxes, drawn_chart: DrawnChart, style: PlotStyle) -> None:
54
+ """
55
+ Draw the title, the axis labels, the rotation of the values, the grid lines and the legend of a Plot.
56
+
57
+ .. versionadded:: 0.1.0
58
+
59
+ Parameters
60
+ ----------
61
+ plot_axes
62
+ The matplotlib axes of the Plot, with the ones on the right if any.
63
+ drawn_chart
64
+ The default labels and axis directions of its first chart.
65
+ style
66
+ The look of the Plot.
67
+
68
+ Examples
69
+ --------
70
+ >>> import numdaplot as ndp
71
+ >>> from matplotlib.figure import Figure
72
+ >>> from numdaplot.plots.drawing.drawn_chart import DrawnChart
73
+ >>> from numdaplot.plots.drawing.grid_axes import GridAxes
74
+ >>> from numdaplot.plots.drawing.plot_axes import PlotAxes
75
+ >>> from numdaplot.plots.drawing.style_drawing import apply_style
76
+ >>> axes = Figure().add_subplot()
77
+ >>> drawn_chart = DrawnChart("day", "price", False, GridAxes.BOTH, None)
78
+ >>> apply_style(PlotAxes(axes, None, None), drawn_chart, ndp.PlotStyle(title="Sales"))
79
+ >>> axes.get_title(), axes.get_xlabel(), axes.get_ylabel()
80
+ ('Sales', 'day', 'price')
81
+ """
82
+ axes = plot_axes.left
83
+ title = choose_title(drawn_chart, style)
84
+ axes.set_title(title)
85
+ write_axis_labels(axes, drawn_chart, style)
86
+ write_right_label(plot_axes, style)
87
+ rotate_x_labels(axes, drawn_chart, style.x_tick_rotation)
88
+ rotate_y_labels(axes, drawn_chart, style.y_tick_rotation)
89
+ if style.grid_lines == GridLines.SHOWN:
90
+ draw_grid_lines(axes, drawn_chart)
91
+
92
+ if style.legend != LegendPosition.HIDDEN:
93
+ place_legend(plot_axes, drawn_chart, style.legend)
94
+
95
+
96
+ def write_axis_labels(axes: Axes, drawn_chart: DrawnChart, style: PlotStyle) -> None:
97
+ """
98
+ Write the labels of the axes, the style ones or else the defaults of the chart.
99
+
100
+ .. versionadded:: 0.1.0
101
+
102
+ Parameters
103
+ ----------
104
+ axes
105
+ The matplotlib axes of the Plot.
106
+ drawn_chart
107
+ The default labels and where the ``x`` values are.
108
+ style
109
+ The labels chosen, None for the defaults.
110
+
111
+ Examples
112
+ --------
113
+ >>> import numdaplot as ndp
114
+ >>> from matplotlib.figure import Figure
115
+ >>> from numdaplot.plots.drawing.drawn_chart import DrawnChart
116
+ >>> from numdaplot.plots.drawing.grid_axes import GridAxes
117
+ >>> from numdaplot.plots.drawing.style_drawing import write_axis_labels
118
+ >>> axes = Figure().add_subplot()
119
+ >>> drawn_chart = DrawnChart("city", "price", True, GridAxes.VALUES, None)
120
+ >>> write_axis_labels(axes, drawn_chart, ndp.PlotStyle(y_label="Euros"))
121
+ >>> axes.get_xlabel(), axes.get_ylabel()
122
+ ('Euros', 'city')
123
+ """
124
+ x_label = choose_label(style.x_label, drawn_chart.x_label)
125
+ y_label = choose_label(style.y_label, drawn_chart.y_label)
126
+ if drawn_chart.x_on_vertical_axis:
127
+ axes.set_xlabel(y_label)
128
+ axes.set_ylabel(x_label)
129
+ return
130
+
131
+ axes.set_xlabel(x_label)
132
+ axes.set_ylabel(y_label)
133
+
134
+
135
+ def draw_grid_lines(axes: Axes, drawn_chart: DrawnChart) -> None:
136
+ """
137
+ Draw light grid lines across the axes the chart asks for: both, the axis of the values only, or none.
138
+
139
+ .. versionadded:: 0.1.0
140
+
141
+ Parameters
142
+ ----------
143
+ axes
144
+ The matplotlib axes of the Plot.
145
+ drawn_chart
146
+ The axes crossed by the grid lines, and where the ``x`` values are.
147
+
148
+ Examples
149
+ --------
150
+ >>> from matplotlib.figure import Figure
151
+ >>> from numdaplot.plots.drawing.drawn_chart import DrawnChart
152
+ >>> from numdaplot.plots.drawing.grid_axes import GridAxes
153
+ >>> from numdaplot.plots.drawing.style_drawing import draw_grid_lines
154
+ >>> axes = Figure().add_subplot()
155
+ >>> draw_grid_lines(axes, DrawnChart(None, None, False, GridAxes.VALUES, None))
156
+ >>> axes.yaxis.get_gridlines()[0].get_visible(), axes.xaxis.get_gridlines()[0].get_visible()
157
+ (True, False)
158
+ """
159
+ if drawn_chart.grid_axes == GridAxes.NONE:
160
+ return
161
+
162
+ if drawn_chart.grid_axes == GridAxes.BOTH:
163
+ axes.grid(True)
164
+ return
165
+
166
+ value_axis = VALUE_GRID_AXES[drawn_chart.x_on_vertical_axis]
167
+ axes.grid(True, axis=value_axis)
168
+
169
+
170
+ def write_right_label(plot_axes: PlotAxes, style: PlotStyle) -> None:
171
+ """
172
+ Write the label of the axis on the right, the style one or else the default one, when the Plot has that axis.
173
+
174
+ .. versionadded:: 0.1.0
175
+
176
+ Parameters
177
+ ----------
178
+ plot_axes
179
+ The matplotlib axes of the Plot.
180
+ style
181
+ The label chosen, None for the default.
182
+
183
+ Examples
184
+ --------
185
+ >>> import numdaplot as ndp
186
+ >>> from matplotlib.figure import Figure
187
+ >>> from numdaplot.plots.drawing.plot_axes import PlotAxes
188
+ >>> from numdaplot.plots.drawing.style_drawing import write_right_label
189
+ >>> axes = Figure().add_subplot()
190
+ >>> right_axes = axes.twinx()
191
+ >>> write_right_label(PlotAxes(axes, right_axes, "price"), ndp.PlotStyle())
192
+ >>> right_axes.get_ylabel()
193
+ 'price'
194
+ """
195
+ if plot_axes.right is None:
196
+ return
197
+
198
+ right_label = choose_label(style.right_label, plot_axes.right_label)
199
+ plot_axes.right.set_ylabel(right_label)
200
+
201
+
202
+ def place_legend(plot_axes: PlotAxes, drawn_chart: DrawnChart, legend_position: LegendPosition) -> None:
203
+ """
204
+ Draw the legend naming the series of every chart, when there are at least two of them.
205
+
206
+ The series are named in the order they are drawn, the ones of the left axes first. The legend is drawn over
207
+ the axes on the right if any, so their charts never hide it. With these axes, ``BEST`` and ``OUTSIDE_RIGHT`` place
208
+ it past their axis: ``BEST`` would only avoid their charts, not the ones of the left axes. ``BEST`` also places
209
+ it past the axes when the chart asks for it, as a chart filling them does. Points sized by
210
+ ``size_by`` are all shown with the same size in the legend, which names their colors.
211
+
212
+ .. versionadded:: 0.1.0
213
+
214
+ Parameters
215
+ ----------
216
+ plot_axes
217
+ The matplotlib axes of the Plot.
218
+ drawn_chart
219
+ The title of the legend, if any, and whether ``BEST`` places it past the axes.
220
+ legend_position
221
+ Where the legend is placed, other than ``HIDDEN``.
222
+
223
+ Examples
224
+ --------
225
+ >>> import numdaplot as ndp
226
+ >>> from matplotlib.figure import Figure
227
+ >>> from numdaplot.plots.drawing.drawn_chart import DrawnChart
228
+ >>> from numdaplot.plots.drawing.grid_axes import GridAxes
229
+ >>> from numdaplot.plots.drawing.plot_axes import PlotAxes
230
+ >>> from numdaplot.plots.drawing.style_drawing import place_legend
231
+ >>> axes = Figure().add_subplot()
232
+ >>> price_lines = axes.plot([1.0, 2.0], label="price")
233
+ >>> right_axes = axes.twinx()
234
+ >>> cost_lines = right_axes.plot([3.0, 1.0], label="cost")
235
+ >>> drawn_chart = DrawnChart(None, None, False, GridAxes.BOTH, None)
236
+ >>> place_legend(PlotAxes(axes, right_axes, None), drawn_chart, ndp.LegendPosition.UPPER_LEFT)
237
+ >>> [text.get_text() for text in right_axes.get_legend().get_texts()]
238
+ ['price', 'cost']
239
+ """
240
+ handles, labels = list_legend_entries(plot_axes.left)
241
+ legend_axes = plot_axes.left
242
+ if plot_axes.right is not None:
243
+ right_handles, right_labels = list_legend_entries(plot_axes.right)
244
+ handles = [*handles, *right_handles]
245
+ labels = [*labels, *right_labels]
246
+ legend_axes = plot_axes.right
247
+
248
+ label_count = len(labels)
249
+ if label_count < MINIMUM_LEGEND_ENTRIES:
250
+ return
251
+
252
+ location = LEGEND_LOCATIONS[legend_position]
253
+ anchor = None
254
+ anchor_transform = None
255
+ has_right_axes = plot_axes.right is not None
256
+ needs_outside = has_right_axes or drawn_chart.legend_outside
257
+ best_goes_outside = needs_outside and legend_position == LegendPosition.BEST
258
+ is_outside = best_goes_outside or legend_position == LegendPosition.OUTSIDE_RIGHT
259
+ if is_outside:
260
+ location = LEGEND_LOCATIONS[LegendPosition.OUTSIDE_RIGHT]
261
+ anchor = OUTSIDE_ANCHOR
262
+
263
+ if is_outside and plot_axes.right is not None:
264
+ anchor_transform = find_transform_past_right_axis(plot_axes.right)
265
+
266
+ legend = legend_axes.legend(
267
+ handles,
268
+ labels,
269
+ loc=location,
270
+ bbox_to_anchor=anchor,
271
+ bbox_transform=anchor_transform,
272
+ title=drawn_chart.legend_title,
273
+ )
274
+
275
+ for handle in legend.legend_handles:
276
+ if isinstance(handle, PathCollection):
277
+ handle.set_sizes([LEGEND_POINT_AREA])
278
+ handle.set_alpha(LEGEND_POINT_OPACITY)
279
+
280
+
281
+ def find_transform_past_right_axis(right_axes: Axes) -> Transform:
282
+ """
283
+ Return the coordinates of the axes shifted past the values and the label of their axis on the right.
284
+
285
+ The shift is a fixed distance on the page, which stays right when the layout then narrows the axes.
286
+
287
+ .. versionadded:: 0.1.0
288
+
289
+ Parameters
290
+ ----------
291
+ right_axes
292
+ The axes on the right, with their label and their ticks.
293
+
294
+ Returns
295
+ -------
296
+ matplotlib.transforms.Transform
297
+ The coordinates to anchor the legend with.
298
+
299
+ Examples
300
+ --------
301
+ >>> from matplotlib.figure import Figure
302
+ >>> from numdaplot.plots.drawing.style_drawing import find_transform_past_right_axis
303
+ >>> axes = Figure().add_subplot()
304
+ >>> right_axes = axes.twinx()
305
+ >>> transform = find_transform_past_right_axis(right_axes)
306
+ >>> bool(transform.transform((1.0, 0.0))[0] > right_axes.transAxes.transform((1.0, 0.0))[0])
307
+ True
308
+ """
309
+ axes_box = right_axes.get_window_extent()
310
+ axis_box = right_axes.yaxis.get_tightbbox()
311
+ axis_width = 0.0
312
+ if axis_box is not None:
313
+ axis_width = axis_box.x1 - axes_box.x1
314
+
315
+ figure = cast(Figure, right_axes.get_figure(root=True))
316
+ shift_inches = axis_width / figure.dpi
317
+ shift = ScaledTranslation(shift_inches, 0.0, figure.dpi_scale_trans)
318
+ transform = right_axes.transAxes + shift
319
+
320
+ return transform
321
+
322
+
323
+ def list_legend_entries(axes: Axes) -> tuple[list[Any], list[str]]:
324
+ """
325
+ Return the legend entries of matplotlib axes in the order their series were drawn.
326
+
327
+ matplotlib lists bars and boxes after lines and points, whatever their order: each entry is placed back at
328
+ the position of its first drawn shape.
329
+
330
+ .. versionadded:: 0.1.0
331
+
332
+ Parameters
333
+ ----------
334
+ axes
335
+ The matplotlib axes.
336
+
337
+ Returns
338
+ -------
339
+ tuple[list[Any], list[str]]
340
+ The handles drawn in the legend, and the label of each one.
341
+
342
+ Examples
343
+ --------
344
+ >>> from matplotlib.figure import Figure
345
+ >>> from numdaplot.plots.drawing.style_drawing import list_legend_entries
346
+ >>> axes = Figure().add_subplot()
347
+ >>> bars = axes.bar([0.0, 1.0], [3.0, 4.0], label="sales")
348
+ >>> lines = axes.plot([0.0, 1.0], [2.0, 5.0], label="target")
349
+ >>> list_legend_entries(axes)[1]
350
+ ['sales', 'target']
351
+ """
352
+ handles, labels = axes.get_legend_handles_labels()
353
+ children = axes.get_children()
354
+ child_positions = {id(child): i for i, child in enumerate(children)}
355
+ child_count = len(children)
356
+ entry_positions = list[int]()
357
+
358
+ for handle in handles:
359
+ shape_id = find_first_shape_id(handle)
360
+ entry_position = child_positions.get(shape_id, child_count)
361
+ entry_positions.append(entry_position)
362
+
363
+ entries = zip(entry_positions, handles, labels, strict=True)
364
+ ordered_entries = sorted(entries, key=itemgetter(0))
365
+ ordered_handles = [handle for _, handle, _ in ordered_entries]
366
+ ordered_labels = [label for _, _, label in ordered_entries]
367
+
368
+ return ordered_handles, ordered_labels
369
+
370
+
371
+ def find_first_shape_id(handle: Any) -> int:
372
+ """
373
+ Return the identity of the first shape drawn for a legend entry.
374
+
375
+ .. versionadded:: 0.1.0
376
+
377
+ Parameters
378
+ ----------
379
+ handle
380
+ A legend handle: a drawn shape, or a container of shapes such as the bars of a series.
381
+
382
+ Returns
383
+ -------
384
+ int
385
+ The ``id`` of the shape, or of the first shape of a container that holds one, else of the handle.
386
+
387
+ Examples
388
+ --------
389
+ >>> from matplotlib.figure import Figure
390
+ >>> from numdaplot.plots.drawing.style_drawing import find_first_shape_id
391
+ >>> axes = Figure().add_subplot()
392
+ >>> bars = axes.bar([0.0, 1.0], [3.0, 4.0])
393
+ >>> find_first_shape_id(bars) == id(bars.patches[0])
394
+ True
395
+ """
396
+ if not isinstance(handle, Container):
397
+ handle_id = id(handle)
398
+ return handle_id
399
+
400
+ # The matplotlib stubs leave the shapes of a container untyped.
401
+ shapes = cast(tuple[Artist, ...], handle)
402
+ shape_count = len(shapes)
403
+ if shape_count == 0:
404
+ container_id = id(handle)
405
+ return container_id
406
+
407
+ first_shape = shapes[0]
408
+ shape_id = id(first_shape)
409
+
410
+ return shape_id
@@ -0,0 +1,44 @@
1
+ """
2
+ Color of a text written over a colored area.
3
+
4
+ .. versionadded:: 0.1.0
5
+ """
6
+
7
+ from collections.abc import Sequence
8
+
9
+ DARK_TEXT_COLOR = "#000000"
10
+ LIGHT_TEXT_COLOR = "#ffffff"
11
+ # Weights of the red, green and blue parts in the lightness the eye perceives.
12
+ LIGHTNESS_WEIGHTS = (0.2126, 0.7152, 0.0722)
13
+ LIGHT_BACKGROUND_THRESHOLD = 0.5
14
+
15
+
16
+ def choose_text_color(background_rgba: Sequence[float]) -> str:
17
+ """
18
+ Return the color of a text that stays readable over a background: black over a light one, white over a dark one.
19
+
20
+ .. versionadded:: 0.1.0
21
+
22
+ Parameters
23
+ ----------
24
+ background_rgba
25
+ The red, green, blue and opacity parts of the background, each from 0 to 1.
26
+
27
+ Returns
28
+ -------
29
+ str
30
+ ``"#000000"`` or ``"#ffffff"``.
31
+
32
+ Examples
33
+ --------
34
+ >>> from numdaplot.plots.drawing.text_contrast import choose_text_color
35
+ >>> choose_text_color((1.0, 1.0, 0.6, 1.0)), choose_text_color((0.1, 0.1, 0.4, 1.0))
36
+ ('#000000', '#ffffff')
37
+ """
38
+ red, green, blue = background_rgba[:3]
39
+ red_weight, green_weight, blue_weight = LIGHTNESS_WEIGHTS
40
+ lightness = red * red_weight + green * green_weight + blue * blue_weight
41
+ if lightness > LIGHT_BACKGROUND_THRESHOLD:
42
+ return DARK_TEXT_COLOR
43
+
44
+ return LIGHT_TEXT_COLOR