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.
- numdaplot/__init__.py +506 -0
- numdaplot/arrays/__init__.py +5 -0
- numdaplot/arrays/array.py +92 -0
- numdaplot/arrays/array_arithmetic.py +404 -0
- numdaplot/arrays/array_calculation.py +143 -0
- numdaplot/arrays/array_conversion.py +150 -0
- numdaplot/arrays/array_creation.py +371 -0
- numdaplot/arrays/array_data.py +281 -0
- numdaplot/arrays/array_layout.py +179 -0
- numdaplot/arrays/array_logic.py +227 -0
- numdaplot/arrays/array_reflected_arithmetic.py +334 -0
- numdaplot/arrays/array_refusals.py +339 -0
- numdaplot/arrays/array_selection.py +195 -0
- numdaplot/arrays/array_sorting.py +59 -0
- numdaplot/arrays/axis_number.py +58 -0
- numdaplot/arrays/calculation/__init__.py +5 -0
- numdaplot/arrays/calculation/counting_statistics.py +325 -0
- numdaplot/arrays/calculation/location_statistics.py +268 -0
- numdaplot/arrays/calculation/math_function_values.py +183 -0
- numdaplot/arrays/calculation/statistic_values.py +209 -0
- numdaplot/arrays/calculation/time_function_values.py +419 -0
- numdaplot/arrays/conversion/__init__.py +5 -0
- numdaplot/arrays/conversion/array_text.py +62 -0
- numdaplot/arrays/conversion/equality_check.py +72 -0
- numdaplot/arrays/conversion/python_values.py +70 -0
- numdaplot/arrays/conversion/time_text.py +290 -0
- numdaplot/arrays/creation/__init__.py +5 -0
- numdaplot/arrays/creation/date_parts.py +89 -0
- numdaplot/arrays/creation/date_parts_values.py +437 -0
- numdaplot/arrays/creation/distribution.py +54 -0
- numdaplot/arrays/creation/filled_values.py +58 -0
- numdaplot/arrays/creation/list_conversion.py +311 -0
- numdaplot/arrays/creation/list_shape.py +97 -0
- numdaplot/arrays/creation/normal.py +65 -0
- numdaplot/arrays/creation/numpy_conversion.py +149 -0
- numdaplot/arrays/creation/random_integers.py +104 -0
- numdaplot/arrays/creation/random_values.py +97 -0
- numdaplot/arrays/creation/range_values.py +73 -0
- numdaplot/arrays/creation/spacing.py +84 -0
- numdaplot/arrays/creation/spacing_values.py +63 -0
- numdaplot/arrays/creation/uniform.py +63 -0
- numdaplot/arrays/equality.py +68 -0
- numdaplot/arrays/layout/__init__.py +5 -0
- numdaplot/arrays/layout/sorting_values.py +113 -0
- numdaplot/arrays/layout/unique_values.py +49 -0
- numdaplot/arrays/mapping/__init__.py +5 -0
- numdaplot/arrays/mapping/mapped_values.py +286 -0
- numdaplot/arrays/mapping/unmatched.py +50 -0
- numdaplot/arrays/mapping/value_change.py +55 -0
- numdaplot/arrays/mapping/value_mapping.py +161 -0
- numdaplot/arrays/math_functions/__init__.py +5 -0
- numdaplot/arrays/math_functions/absolute.py +27 -0
- numdaplot/arrays/math_functions/cosine.py +28 -0
- numdaplot/arrays/math_functions/exponential.py +27 -0
- numdaplot/arrays/math_functions/logarithm.py +72 -0
- numdaplot/arrays/math_functions/math_function.py +58 -0
- numdaplot/arrays/math_functions/round.py +58 -0
- numdaplot/arrays/math_functions/sine.py +28 -0
- numdaplot/arrays/math_functions/square_root.py +27 -0
- numdaplot/arrays/operations/__init__.py +5 -0
- numdaplot/arrays/operations/boolean_operands.py +48 -0
- numdaplot/arrays/operations/combination.py +157 -0
- numdaplot/arrays/operations/float_errors.py +170 -0
- numdaplot/arrays/operations/integer_bounds.py +175 -0
- numdaplot/arrays/operations/integer_rules.py +397 -0
- numdaplot/arrays/operations/operand.py +139 -0
- numdaplot/arrays/operations/time_arithmetic.py +479 -0
- numdaplot/arrays/operations/time_bounds.py +286 -0
- numdaplot/arrays/operations/time_result_bounds.py +397 -0
- numdaplot/arrays/operations/transformation.py +54 -0
- numdaplot/arrays/operations/type_requirements.py +94 -0
- numdaplot/arrays/replacement.py +66 -0
- numdaplot/arrays/selection/__init__.py +5 -0
- numdaplot/arrays/selection/comparison_masks.py +131 -0
- numdaplot/arrays/selection/membership_masks.py +118 -0
- numdaplot/arrays/selection/missing_mask.py +50 -0
- numdaplot/arrays/selection/region_index.py +178 -0
- numdaplot/arrays/selection/replaced_values.py +120 -0
- numdaplot/arrays/selection/rule_mask.py +83 -0
- numdaplot/arrays/selection/time_reference.py +172 -0
- numdaplot/arrays/shape.py +74 -0
- numdaplot/arrays/type_match.py +51 -0
- numdaplot/data_types/__init__.py +5 -0
- numdaplot/data_types/library.py +57 -0
- numdaplot/data_types/numpy_mapping.py +207 -0
- numdaplot/data_types/pandas_mapping.py +114 -0
- numdaplot/data_types/time_of_day.py +292 -0
- numdaplot/data_types/type.py +224 -0
- numdaplot/data_types/value_family.py +149 -0
- numdaplot/data_types/value_type.py +147 -0
- numdaplot/dates/__init__.py +5 -0
- numdaplot/dates/custom_date_format.py +83 -0
- numdaplot/dates/date_format.py +120 -0
- numdaplot/dates/date_part.py +59 -0
- numdaplot/dates/date_part_unit.py +153 -0
- numdaplot/dates/date_period.py +57 -0
- numdaplot/dates/duration_in.py +58 -0
- numdaplot/dates/duration_unit.py +90 -0
- numdaplot/dates/period_unit.py +101 -0
- numdaplot/dates/time_function.py +56 -0
- numdaplot/dates/time_zone.py +68 -0
- numdaplot/dates/time_zone_change.py +71 -0
- numdaplot/dates/time_zone_mode.py +81 -0
- numdaplot/dates/to_duration.py +58 -0
- numdaplot/errors/__init__.py +7 -0
- numdaplot/errors/broadcast_warning.py +36 -0
- numdaplot/errors/calculation_warning.py +35 -0
- numdaplot/errors/cell_out_of_range_error.py +77 -0
- numdaplot/errors/column_not_found_error.py +64 -0
- numdaplot/errors/conflicting_settings_error.py +60 -0
- numdaplot/errors/dimension_error.py +62 -0
- numdaplot/errors/display_warning.py +32 -0
- numdaplot/errors/duplicate_column_error.py +82 -0
- numdaplot/errors/duplicate_label_error.py +55 -0
- numdaplot/errors/duplicate_statistic_error.py +55 -0
- numdaplot/errors/duplicate_value_error.py +54 -0
- numdaplot/errors/empty_array_error.py +55 -0
- numdaplot/errors/empty_list_error.py +52 -0
- numdaplot/errors/empty_region_error.py +34 -0
- numdaplot/errors/empty_text_error.py +34 -0
- numdaplot/errors/empty_total_error.py +62 -0
- numdaplot/errors/empty_values_error.py +34 -0
- numdaplot/errors/file_access_error.py +70 -0
- numdaplot/errors/file_exists_error.py +62 -0
- numdaplot/errors/file_not_found_error.py +56 -0
- numdaplot/errors/incompatible_charts_error.py +80 -0
- numdaplot/errors/incompatible_types_error.py +104 -0
- numdaplot/errors/integer_overflow_error.py +61 -0
- numdaplot/errors/invalid_base_error.py +52 -0
- numdaplot/errors/invalid_bin_width_error.py +66 -0
- numdaplot/errors/invalid_bounds_error.py +58 -0
- numdaplot/errors/invalid_chart_data_error.py +81 -0
- numdaplot/errors/invalid_color_count_error.py +63 -0
- numdaplot/errors/invalid_color_error.py +51 -0
- numdaplot/errors/invalid_column_name_error.py +52 -0
- numdaplot/errors/invalid_conversion_error.py +119 -0
- numdaplot/errors/invalid_count_error.py +61 -0
- numdaplot/errors/invalid_date_format_error.py +51 -0
- numdaplot/errors/invalid_deviation_error.py +52 -0
- numdaplot/errors/invalid_fraction_error.py +51 -0
- numdaplot/errors/invalid_label_count_error.py +61 -0
- numdaplot/errors/invalid_limits_error.py +111 -0
- numdaplot/errors/invalid_opacity_error.py +52 -0
- numdaplot/errors/invalid_point_size_error.py +52 -0
- numdaplot/errors/invalid_position_error.py +106 -0
- numdaplot/errors/invalid_resolution_error.py +51 -0
- numdaplot/errors/invalid_rotation_error.py +53 -0
- numdaplot/errors/invalid_seed_error.py +53 -0
- numdaplot/errors/invalid_separator_error.py +52 -0
- numdaplot/errors/invalid_shape_error.py +52 -0
- numdaplot/errors/invalid_sheet_name_error.py +54 -0
- numdaplot/errors/invalid_size_error.py +58 -0
- numdaplot/errors/invalid_step_error.py +77 -0
- numdaplot/errors/invalid_suffixes_error.py +61 -0
- numdaplot/errors/invalid_time_zone_change_error.py +63 -0
- numdaplot/errors/invalid_type_error.py +79 -0
- numdaplot/errors/irregular_list_error.py +36 -0
- numdaplot/errors/join_relation_error.py +183 -0
- numdaplot/errors/missing_dependency_error.py +58 -0
- numdaplot/errors/missing_type_error.py +36 -0
- numdaplot/errors/missing_value_error.py +62 -0
- numdaplot/errors/not_single_value_error.py +53 -0
- numdaplot/errors/numdaplot_error.py +25 -0
- numdaplot/errors/numdaplot_warning.py +34 -0
- numdaplot/errors/occupied_cell_error.py +63 -0
- numdaplot/errors/out_of_domain_error.py +70 -0
- numdaplot/errors/readability_warning.py +35 -0
- numdaplot/errors/refused_operation_error.py +64 -0
- numdaplot/errors/refused_setting_error.py +60 -0
- numdaplot/errors/rejected_dates.py +60 -0
- numdaplot/errors/repeated_key.py +36 -0
- numdaplot/errors/shape_mismatch_error.py +67 -0
- numdaplot/errors/time_overflow_error.py +131 -0
- numdaplot/errors/time_zone_mismatch_error.py +83 -0
- numdaplot/errors/unknown_time_zone_error.py +51 -0
- numdaplot/errors/unmatched_value_error.py +81 -0
- numdaplot/errors/unsupported_image_format_error.py +56 -0
- numdaplot/errors/unsupported_index_error.py +38 -0
- numdaplot/errors/unsupported_missing_error.py +129 -0
- numdaplot/errors/unsupported_type_error.py +62 -0
- numdaplot/errors/value_conversion_error.py +215 -0
- numdaplot/ordering/__init__.py +5 -0
- numdaplot/ordering/missing_position.py +46 -0
- numdaplot/ordering/order.py +46 -0
- numdaplot/package_version.py +36 -0
- numdaplot/plots/__init__.py +5 -0
- numdaplot/plots/chart_layer.py +47 -0
- numdaplot/plots/charts/__init__.py +5 -0
- numdaplot/plots/charts/bar_chart.py +98 -0
- numdaplot/plots/charts/bar_layout.py +58 -0
- numdaplot/plots/charts/bin_count.py +61 -0
- numdaplot/plots/charts/bin_width.py +78 -0
- numdaplot/plots/charts/box_chart.py +97 -0
- numdaplot/plots/charts/cell_values.py +61 -0
- numdaplot/plots/charts/chart.py +59 -0
- numdaplot/plots/charts/chart_settings.py +105 -0
- numdaplot/plots/charts/color_scale.py +147 -0
- numdaplot/plots/charts/heatmap_chart.py +92 -0
- numdaplot/plots/charts/histogram_chart.py +93 -0
- numdaplot/plots/charts/line_chart.py +100 -0
- numdaplot/plots/charts/line_type.py +75 -0
- numdaplot/plots/charts/marker_shape.py +113 -0
- numdaplot/plots/charts/missing_chart.py +60 -0
- numdaplot/plots/charts/missing_map_chart.py +27 -0
- numdaplot/plots/charts/orientation.py +58 -0
- numdaplot/plots/charts/outliers.py +49 -0
- numdaplot/plots/charts/percentages.py +55 -0
- numdaplot/plots/charts/pie_chart.py +87 -0
- numdaplot/plots/charts/scatter_chart.py +159 -0
- numdaplot/plots/charts/value_axis.py +66 -0
- numdaplot/plots/data/__init__.py +5 -0
- numdaplot/plots/data/array_values.py +128 -0
- numdaplot/plots/data/chart_data.py +143 -0
- numdaplot/plots/data/chart_values.py +109 -0
- numdaplot/plots/data/column_reading.py +468 -0
- numdaplot/plots/data/heatmap_values.py +63 -0
- numdaplot/plots/data/missing_cells.py +165 -0
- numdaplot/plots/data/pie_values.py +215 -0
- numdaplot/plots/data/point_values.py +249 -0
- numdaplot/plots/data/position_kind.py +83 -0
- numdaplot/plots/data/renamed_layers.py +340 -0
- numdaplot/plots/data/sample_values.py +160 -0
- numdaplot/plots/data/shown_columns.py +248 -0
- numdaplot/plots/data/table_values.py +256 -0
- numdaplot/plots/data/unique_labels.py +45 -0
- numdaplot/plots/drawing/__init__.py +5 -0
- numdaplot/plots/drawing/bar_drawing.py +316 -0
- numdaplot/plots/drawing/box_drawing.py +344 -0
- numdaplot/plots/drawing/cell_merging.py +237 -0
- numdaplot/plots/drawing/chart_texts.py +124 -0
- numdaplot/plots/drawing/color_bar.py +57 -0
- numdaplot/plots/drawing/color_scale_values.py +120 -0
- numdaplot/plots/drawing/default_labels.py +41 -0
- numdaplot/plots/drawing/drawn_chart.py +50 -0
- numdaplot/plots/drawing/grid_axes.py +75 -0
- numdaplot/plots/drawing/heatmap_drawing.py +313 -0
- numdaplot/plots/drawing/histogram_drawing.py +350 -0
- numdaplot/plots/drawing/label_room.py +344 -0
- numdaplot/plots/drawing/label_thinning.py +216 -0
- numdaplot/plots/drawing/layer_alignment.py +319 -0
- numdaplot/plots/drawing/layer_colors.py +123 -0
- numdaplot/plots/drawing/line_drawing.py +85 -0
- numdaplot/plots/drawing/marker_codes.py +20 -0
- numdaplot/plots/drawing/missing_drawing.py +360 -0
- numdaplot/plots/drawing/moment_period_formatter.py +253 -0
- numdaplot/plots/drawing/pie_drawing.py +185 -0
- numdaplot/plots/drawing/plot_axes.py +38 -0
- numdaplot/plots/drawing/plot_drawing.py +288 -0
- numdaplot/plots/drawing/plot_theme.py +45 -0
- numdaplot/plots/drawing/position_axis.py +278 -0
- numdaplot/plots/drawing/scatter_drawing.py +364 -0
- numdaplot/plots/drawing/series_colors.py +177 -0
- numdaplot/plots/drawing/size_inches.py +45 -0
- numdaplot/plots/drawing/style_drawing.py +410 -0
- numdaplot/plots/drawing/text_contrast.py +44 -0
- numdaplot/plots/drawing/tick_rotation.py +354 -0
- numdaplot/plots/drawing/time_axis.py +212 -0
- numdaplot/plots/drawing/whole_numbers.py +72 -0
- numdaplot/plots/figure/__init__.py +5 -0
- numdaplot/plots/figure/cell.py +95 -0
- numdaplot/plots/figure/figure.py +256 -0
- numdaplot/plots/figure/figure_drawing.py +99 -0
- numdaplot/plots/figure/figure_style.py +60 -0
- numdaplot/plots/figure/layout.py +71 -0
- numdaplot/plots/figure/placement.py +150 -0
- numdaplot/plots/layer_reading.py +166 -0
- numdaplot/plots/output/__init__.py +5 -0
- numdaplot/plots/output/image_saving.py +187 -0
- numdaplot/plots/output/plot_display.py +181 -0
- numdaplot/plots/output/shown_object.py +45 -0
- numdaplot/plots/overlay_checks.py +245 -0
- numdaplot/plots/plot.py +31 -0
- numdaplot/plots/plot_data.py +140 -0
- numdaplot/plots/plot_distribution_creation.py +237 -0
- numdaplot/plots/plot_output.py +124 -0
- numdaplot/plots/plot_overlays.py +273 -0
- numdaplot/plots/plot_overview_creation.py +195 -0
- numdaplot/plots/plot_series_creation.py +213 -0
- numdaplot/plots/style/__init__.py +5 -0
- numdaplot/plots/style/axis_settings.py +60 -0
- numdaplot/plots/style/color.py +168 -0
- numdaplot/plots/style/custom_color.py +65 -0
- numdaplot/plots/style/grid_lines.py +49 -0
- numdaplot/plots/style/image_options.py +71 -0
- numdaplot/plots/style/legend_position.py +149 -0
- numdaplot/plots/style/plot_style.py +136 -0
- numdaplot/plots/style/size.py +70 -0
- numdaplot/plots/style/size_unit.py +62 -0
- numdaplot/positions/__init__.py +5 -0
- numdaplot/positions/axis.py +54 -0
- numdaplot/positions/index.py +51 -0
- numdaplot/positions/range.py +90 -0
- numdaplot/positions/region.py +92 -0
- numdaplot/py.typed +0 -0
- numdaplot/ratios/__init__.py +5 -0
- numdaplot/ratios/fraction.py +40 -0
- numdaplot/ratios/measure.py +48 -0
- numdaplot/rules/__init__.py +5 -0
- numdaplot/rules/between.py +78 -0
- numdaplot/rules/bounds.py +83 -0
- numdaplot/rules/case.py +48 -0
- numdaplot/rules/contains.py +63 -0
- numdaplot/rules/equal.py +43 -0
- numdaplot/rules/greater.py +39 -0
- numdaplot/rules/greater_or_equal.py +38 -0
- numdaplot/rules/is_in.py +67 -0
- numdaplot/rules/is_missing.py +25 -0
- numdaplot/rules/is_present.py +25 -0
- numdaplot/rules/less.py +39 -0
- numdaplot/rules/less_or_equal.py +38 -0
- numdaplot/rules/not_equal.py +45 -0
- numdaplot/rules/rule.py +62 -0
- numdaplot/rules/rule_values.py +44 -0
- numdaplot/rules/value_rule.py +110 -0
- numdaplot/settings/__init__.py +5 -0
- numdaplot/settings/argument_types.py +327 -0
- numdaplot/settings/field_types.py +593 -0
- numdaplot/statistics/__init__.py +5 -0
- numdaplot/statistics/count.py +71 -0
- numdaplot/statistics/count_distinct.py +27 -0
- numdaplot/statistics/counted_missing.py +46 -0
- numdaplot/statistics/maximum.py +27 -0
- numdaplot/statistics/mean.py +27 -0
- numdaplot/statistics/median.py +27 -0
- numdaplot/statistics/minimum.py +27 -0
- numdaplot/statistics/most_frequent.py +27 -0
- numdaplot/statistics/most_frequent_count.py +27 -0
- numdaplot/statistics/quantile.py +61 -0
- numdaplot/statistics/standard_deviation.py +29 -0
- numdaplot/statistics/statistic.py +63 -0
- numdaplot/statistics/sum.py +27 -0
- numdaplot/tables/__init__.py +5 -0
- numdaplot/tables/aggregation/__init__.py +5 -0
- numdaplot/tables/aggregation/aggregated_frame.py +189 -0
- numdaplot/tables/aggregation/aggregation.py +97 -0
- numdaplot/tables/aggregation/calculation.py +123 -0
- numdaplot/tables/aggregation/result_column.py +108 -0
- numdaplot/tables/aggregation/row_groups.py +96 -0
- numdaplot/tables/aggregation/statistic_name.py +122 -0
- numdaplot/tables/columns/__init__.py +5 -0
- numdaplot/tables/columns/added_column.py +89 -0
- numdaplot/tables/columns/boolean_conversion.py +144 -0
- numdaplot/tables/columns/boolean_rule.py +59 -0
- numdaplot/tables/columns/bracket_end.py +50 -0
- numdaplot/tables/columns/bracket_names.py +346 -0
- numdaplot/tables/columns/bracketed_column.py +179 -0
- numdaplot/tables/columns/brackets.py +234 -0
- numdaplot/tables/columns/chosen_columns.py +55 -0
- numdaplot/tables/columns/column_choice.py +84 -0
- numdaplot/tables/columns/column_mode.py +64 -0
- numdaplot/tables/columns/column_names.py +186 -0
- numdaplot/tables/columns/conversion.py +103 -0
- numdaplot/tables/columns/converted_column.py +669 -0
- numdaplot/tables/columns/date_conversion.py +526 -0
- numdaplot/tables/columns/date_range.py +135 -0
- numdaplot/tables/columns/name_change.py +67 -0
- numdaplot/tables/columns/new_column.py +79 -0
- numdaplot/tables/columns/number_conversion.py +281 -0
- numdaplot/tables/columns/renamed_columns.py +60 -0
- numdaplot/tables/columns/renaming.py +81 -0
- numdaplot/tables/columns/text_conversion.py +70 -0
- numdaplot/tables/columns/time_conversion.py +313 -0
- numdaplot/tables/columns/time_zone_conversion.py +128 -0
- numdaplot/tables/conflict.py +68 -0
- numdaplot/tables/description/__init__.py +5 -0
- numdaplot/tables/description/described_frame.py +335 -0
- numdaplot/tables/description/description.py +155 -0
- numdaplot/tables/duplicates/__init__.py +5 -0
- numdaplot/tables/duplicates/deduplicated_frame.py +66 -0
- numdaplot/tables/duplicates/deduplication.py +88 -0
- numdaplot/tables/duplicates/keep.py +81 -0
- numdaplot/tables/files/__init__.py +5 -0
- numdaplot/tables/files/column_names.py +60 -0
- numdaplot/tables/files/csv_file.py +265 -0
- numdaplot/tables/files/csv_read_options.py +89 -0
- numdaplot/tables/files/csv_separator.py +41 -0
- numdaplot/tables/files/csv_write_options.py +79 -0
- numdaplot/tables/files/decimal_count.py +42 -0
- numdaplot/tables/files/encoding.py +82 -0
- numdaplot/tables/files/excel_file.py +171 -0
- numdaplot/tables/files/excel_read_options.py +79 -0
- numdaplot/tables/files/excel_sheet.py +50 -0
- numdaplot/tables/files/excel_time_cells.py +146 -0
- numdaplot/tables/files/excel_write_options.py +63 -0
- numdaplot/tables/files/file_access.py +152 -0
- numdaplot/tables/files/file_paths.py +106 -0
- numdaplot/tables/files/json_decimals.py +387 -0
- numdaplot/tables/files/json_file.py +203 -0
- numdaplot/tables/files/json_layout.py +63 -0
- numdaplot/tables/files/json_read_options.py +64 -0
- numdaplot/tables/files/json_write_options.py +71 -0
- numdaplot/tables/files/missing_texts.py +63 -0
- numdaplot/tables/files/optional_package.py +42 -0
- numdaplot/tables/files/pandas_backend.py +11 -0
- numdaplot/tables/files/parquet_file.py +91 -0
- numdaplot/tables/files/parquet_read_options.py +56 -0
- numdaplot/tables/files/sas_file.py +94 -0
- numdaplot/tables/files/sas_read_options.py +67 -0
- numdaplot/tables/files/stata_file.py +83 -0
- numdaplot/tables/files/stata_read_options.py +56 -0
- numdaplot/tables/files/xpt_read_options.py +65 -0
- numdaplot/tables/filtering/__init__.py +5 -0
- numdaplot/tables/filtering/checked_frame.py +60 -0
- numdaplot/tables/filtering/column_mask.py +71 -0
- numdaplot/tables/filtering/column_match.py +62 -0
- numdaplot/tables/filtering/condition.py +94 -0
- numdaplot/tables/filtering/filter.py +74 -0
- numdaplot/tables/filtering/filter_mode.py +67 -0
- numdaplot/tables/filtering/row_mask.py +152 -0
- numdaplot/tables/filtering/rule_moments.py +164 -0
- numdaplot/tables/frames/__init__.py +5 -0
- numdaplot/tables/frames/array_frame.py +95 -0
- numdaplot/tables/frames/cleaned_names.py +132 -0
- numdaplot/tables/frames/column.py +15 -0
- numdaplot/tables/frames/column_values.py +273 -0
- numdaplot/tables/frames/frame_equality.py +130 -0
- numdaplot/tables/frames/frame_normalization.py +459 -0
- numdaplot/tables/frames/named_column_errors.py +85 -0
- numdaplot/tables/frames/pandas_decimals.py +113 -0
- numdaplot/tables/frames/region_positions.py +113 -0
- numdaplot/tables/frames/table_text.py +222 -0
- numdaplot/tables/frames/time_columns.py +345 -0
- numdaplot/tables/frames/time_range_errors.py +117 -0
- numdaplot/tables/frames/time_zones.py +271 -0
- numdaplot/tables/joining/__init__.py +5 -0
- numdaplot/tables/joining/join.py +110 -0
- numdaplot/tables/joining/join_keys.py +367 -0
- numdaplot/tables/joining/join_mode.py +93 -0
- numdaplot/tables/joining/join_relation.py +149 -0
- numdaplot/tables/joining/join_suffixes.py +71 -0
- numdaplot/tables/joining/joined_frame.py +166 -0
- numdaplot/tables/joining/joined_names.py +107 -0
- numdaplot/tables/joining/joined_rows.py +235 -0
- numdaplot/tables/joining/key_types.py +57 -0
- numdaplot/tables/joining/relation.py +78 -0
- numdaplot/tables/missing/__init__.py +5 -0
- numdaplot/tables/missing/missing_count.py +59 -0
- numdaplot/tables/missing/missing_counts.py +214 -0
- numdaplot/tables/missing/missing_drop.py +98 -0
- numdaplot/tables/missing/missing_mode.py +63 -0
- numdaplot/tables/missing/missing_ratio.py +71 -0
- numdaplot/tables/missing/missing_removal.py +257 -0
- numdaplot/tables/missing/missing_summary.py +174 -0
- numdaplot/tables/missing/ratio_comparison.py +70 -0
- numdaplot/tables/pivot/__init__.py +5 -0
- numdaplot/tables/pivot/pivot_table.py +104 -0
- numdaplot/tables/pivot/pivoted_frame.py +244 -0
- numdaplot/tables/preview/__init__.py +5 -0
- numdaplot/tables/preview/notebook_display.py +238 -0
- numdaplot/tables/preview/preview.py +100 -0
- numdaplot/tables/preview/preview_rows.py +55 -0
- numdaplot/tables/preview/shown_columns.py +62 -0
- numdaplot/tables/preview/side.py +82 -0
- numdaplot/tables/sorting/__init__.py +5 -0
- numdaplot/tables/sorting/sort_key.py +71 -0
- numdaplot/tables/sorting/sorted_frame.py +81 -0
- numdaplot/tables/sorting/sorting.py +82 -0
- numdaplot/tables/table.py +71 -0
- numdaplot/tables/table_calculation.py +172 -0
- numdaplot/tables/table_columns.py +214 -0
- numdaplot/tables/table_conversion.py +351 -0
- numdaplot/tables/table_data.py +321 -0
- numdaplot/tables/table_display.py +56 -0
- numdaplot/tables/table_joining.py +80 -0
- numdaplot/tables/table_layout.py +105 -0
- numdaplot/tables/table_missing.py +146 -0
- numdaplot/tables/table_reading.py +451 -0
- numdaplot/tables/table_refusals.py +295 -0
- numdaplot/tables/table_selection.py +163 -0
- numdaplot/tables/table_writing.py +269 -0
- numdaplot-0.1.0.dist-info/METADATA +176 -0
- numdaplot-0.1.0.dist-info/RECORD +474 -0
- numdaplot-0.1.0.dist-info/WHEEL +4 -0
- 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
|