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,286 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Refusal of dates, datetimes and durations that a count of nanoseconds cannot hold.
|
|
3
|
+
|
|
4
|
+
.. versionadded:: 0.1.0
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import date, datetime, time, timedelta
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import numpy
|
|
11
|
+
from numpy.typing import NDArray
|
|
12
|
+
|
|
13
|
+
from numdaplot.arrays.operations.integer_bounds import find_value_bounds
|
|
14
|
+
from numdaplot.arrays.operations.operand import Operand
|
|
15
|
+
from numdaplot.data_types.type import Type
|
|
16
|
+
from numdaplot.errors.time_overflow_error import TimeOverflowError
|
|
17
|
+
|
|
18
|
+
NANOSECOND_TYPES = frozenset({Type.DATETIME, Type.DURATION})
|
|
19
|
+
TIME_KINDS = frozenset({"M", "m"})
|
|
20
|
+
NANOSECOND_DTYPES = frozenset({numpy.dtype("datetime64[ns]"), numpy.dtype("timedelta64[ns]")})
|
|
21
|
+
SECOND_DTYPE_FORMAT = "{kind}8[s]"
|
|
22
|
+
COUNT_DTYPE = numpy.dtype(numpy.int64)
|
|
23
|
+
COUNT_LIMITS = numpy.iinfo(COUNT_DTYPE)
|
|
24
|
+
# numpy keeps the lowest count for NaT, so the range is the same on both sides of zero.
|
|
25
|
+
HIGHEST_NANOSECONDS = int(COUNT_LIMITS.max)
|
|
26
|
+
LOWEST_NANOSECONDS = -HIGHEST_NANOSECONDS
|
|
27
|
+
FIRST_COUNT_TOO_LARGE = 2.0**63
|
|
28
|
+
NANOSECONDS_PER_SECOND = 10**9
|
|
29
|
+
HIGHEST_SECONDS = HIGHEST_NANOSECONDS // NANOSECONDS_PER_SECOND
|
|
30
|
+
LOWEST_SECONDS = -HIGHEST_SECONDS
|
|
31
|
+
LOWEST_DATETIME = datetime(1677, 9, 21, 0, 12, 43, 145225)
|
|
32
|
+
HIGHEST_DATETIME = datetime(2262, 4, 11, 23, 47, 16, 854775)
|
|
33
|
+
LONGEST_DURATION = timedelta(days=106751, seconds=85636, microseconds=854775)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def is_python_time_in_range(value: object) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Return whether a Python value is not a date, a datetime or a duration beyond a count of nanoseconds.
|
|
39
|
+
|
|
40
|
+
.. versionadded:: 0.1.0
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
value
|
|
45
|
+
A single Python value.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
bool
|
|
50
|
+
False for a date, a datetime or a duration out of range, True for any other value.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
--------
|
|
54
|
+
>>> from datetime import datetime
|
|
55
|
+
>>> from numdaplot.arrays.operations.time_bounds import is_python_time_in_range
|
|
56
|
+
>>> is_python_time_in_range(datetime(2026, 1, 31))
|
|
57
|
+
True
|
|
58
|
+
>>> is_python_time_in_range(datetime(3000, 1, 31))
|
|
59
|
+
False
|
|
60
|
+
"""
|
|
61
|
+
if isinstance(value, timedelta):
|
|
62
|
+
duration_in_range = -LONGEST_DURATION <= value <= LONGEST_DURATION
|
|
63
|
+
return duration_in_range
|
|
64
|
+
|
|
65
|
+
if isinstance(value, datetime):
|
|
66
|
+
clock_time = value.replace(tzinfo=None)
|
|
67
|
+
datetime_in_range = LOWEST_DATETIME <= clock_time <= HIGHEST_DATETIME
|
|
68
|
+
return datetime_in_range
|
|
69
|
+
|
|
70
|
+
if isinstance(value, date):
|
|
71
|
+
midnight = datetime.combine(value, time.min)
|
|
72
|
+
date_in_range = LOWEST_DATETIME <= midnight <= HIGHEST_DATETIME
|
|
73
|
+
return date_in_range
|
|
74
|
+
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def require_stored_times_fit(single_values: list[object], stored_type: Type, operation_name: str) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Check that Python values stored as datetimes or durations fit a count of nanoseconds.
|
|
81
|
+
|
|
82
|
+
.. versionadded:: 0.1.0
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
single_values
|
|
87
|
+
Single Python values.
|
|
88
|
+
stored_type
|
|
89
|
+
The type the values are stored as. Other types than ``DATETIME`` and ``DURATION`` are not checked.
|
|
90
|
+
operation_name
|
|
91
|
+
The operation shown in the error message.
|
|
92
|
+
|
|
93
|
+
Raises
|
|
94
|
+
------
|
|
95
|
+
TimeOverflowError
|
|
96
|
+
When a value is out of range.
|
|
97
|
+
|
|
98
|
+
Examples
|
|
99
|
+
--------
|
|
100
|
+
>>> from datetime import datetime
|
|
101
|
+
>>> import numdaplot as ndp
|
|
102
|
+
>>> from numdaplot.arrays.operations.time_bounds import require_stored_times_fit
|
|
103
|
+
>>> try:
|
|
104
|
+
... require_stored_times_fit([datetime(3000, 1, 31)], ndp.Type.DATETIME, "from_list")
|
|
105
|
+
... except ndp.TimeOverflowError as error:
|
|
106
|
+
... print(error)
|
|
107
|
+
The operation from_list gives values outside the range of DATETIME, from 1677 to 2262.
|
|
108
|
+
"""
|
|
109
|
+
if stored_type not in NANOSECOND_TYPES:
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
for value in single_values:
|
|
113
|
+
value_in_range = is_python_time_in_range(value)
|
|
114
|
+
if not value_in_range:
|
|
115
|
+
raise TimeOverflowError(operation_name, stored_type.name)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def require_nanoseconds_fit(operand: Operand, data_type: Type, operation_name: str) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Check that the dates, datetimes or durations of an operand can be counted in nanoseconds.
|
|
121
|
+
|
|
122
|
+
.. versionadded:: 0.1.0
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
operand
|
|
127
|
+
The values of an Array, or a single value. Other values than times are not checked.
|
|
128
|
+
data_type
|
|
129
|
+
The type named by the error message.
|
|
130
|
+
operation_name
|
|
131
|
+
The operation shown in the error message.
|
|
132
|
+
|
|
133
|
+
Raises
|
|
134
|
+
------
|
|
135
|
+
TimeOverflowError
|
|
136
|
+
When a value is out of range. An Array in a coarser unit is compared to the second.
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
>>> import numpy
|
|
141
|
+
>>> import numdaplot as ndp
|
|
142
|
+
>>> from numdaplot.arrays.operations.time_bounds import require_nanoseconds_fit
|
|
143
|
+
>>> try:
|
|
144
|
+
... require_nanoseconds_fit(numpy.array(["3000-01-31"], dtype="datetime64[D]"), ndp.Type.DATE, "+")
|
|
145
|
+
... except ndp.TimeOverflowError as error:
|
|
146
|
+
... print(error)
|
|
147
|
+
The operation + gives values outside the range of DATE, from 1677 to 2262 in a calculation.
|
|
148
|
+
"""
|
|
149
|
+
if not isinstance(operand, numpy.ndarray):
|
|
150
|
+
operand_in_range = is_python_time_in_range(operand)
|
|
151
|
+
if not operand_in_range:
|
|
152
|
+
raise TimeOverflowError(operation_name, data_type.name)
|
|
153
|
+
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
if operand.dtype.kind not in TIME_KINDS or operand.dtype in NANOSECOND_DTYPES:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
count_bounds = find_present_bounds(operand)
|
|
160
|
+
if count_bounds is None:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
second_dtype = SECOND_DTYPE_FORMAT.format(kind=operand.dtype.kind)
|
|
164
|
+
bound_counts = numpy.array(count_bounds, dtype=COUNT_DTYPE)
|
|
165
|
+
bound_times = bound_counts.astype(operand.dtype)
|
|
166
|
+
bound_seconds = bound_times.astype(second_dtype)
|
|
167
|
+
second_counts = bound_seconds.astype(COUNT_DTYPE)
|
|
168
|
+
low_seconds, high_seconds = find_value_bounds(second_counts)
|
|
169
|
+
if low_seconds < LOWEST_SECONDS or high_seconds > HIGHEST_SECONDS:
|
|
170
|
+
raise TimeOverflowError(operation_name, data_type.name)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def find_present_bounds(values: Any) -> tuple[int, int] | None:
|
|
174
|
+
"""
|
|
175
|
+
Return the lowest and the highest values of a calculation, times being counted in their unit.
|
|
176
|
+
|
|
177
|
+
.. versionadded:: 0.1.0
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
values
|
|
182
|
+
Times or integers, an array or a single value.
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
tuple[int, int] or None
|
|
187
|
+
The bounds of the present values, or ``None`` when no value is present.
|
|
188
|
+
|
|
189
|
+
Examples
|
|
190
|
+
--------
|
|
191
|
+
>>> import numpy
|
|
192
|
+
>>> from numdaplot.arrays.operations.time_bounds import find_present_bounds
|
|
193
|
+
>>> find_present_bounds(numpy.array([5, "NaT", 2], dtype="timedelta64[ns]"))
|
|
194
|
+
(2, 5)
|
|
195
|
+
>>> find_present_bounds(3)
|
|
196
|
+
(3, 3)
|
|
197
|
+
"""
|
|
198
|
+
array = numpy.asarray(values)
|
|
199
|
+
if array.size == 0:
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
if array.dtype.kind not in TIME_KINDS:
|
|
203
|
+
number_bounds = find_value_bounds(array)
|
|
204
|
+
return number_bounds
|
|
205
|
+
|
|
206
|
+
counts = array.view(COUNT_DTYPE)
|
|
207
|
+
low_count, high_count = find_value_bounds(counts)
|
|
208
|
+
if low_count != COUNT_LIMITS.min:
|
|
209
|
+
return low_count, high_count
|
|
210
|
+
|
|
211
|
+
is_present = counts != COUNT_LIMITS.min
|
|
212
|
+
present_counts = counts[is_present]
|
|
213
|
+
if present_counts.size == 0:
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
present_bounds = find_value_bounds(present_counts)
|
|
217
|
+
|
|
218
|
+
return present_bounds
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def require_whole_durations_fit(values: NDArray[Any], nanoseconds_per_unit: int, function_name: str) -> None:
|
|
222
|
+
"""
|
|
223
|
+
Check that integers read as durations in a unit fit a count of nanoseconds.
|
|
224
|
+
|
|
225
|
+
.. versionadded:: 0.1.0
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
values
|
|
230
|
+
Integers.
|
|
231
|
+
nanoseconds_per_unit
|
|
232
|
+
The number of nanoseconds in the unit of the integers.
|
|
233
|
+
function_name
|
|
234
|
+
The function shown in the error message.
|
|
235
|
+
|
|
236
|
+
Raises
|
|
237
|
+
------
|
|
238
|
+
TimeOverflowError
|
|
239
|
+
When a duration is out of range.
|
|
240
|
+
|
|
241
|
+
Examples
|
|
242
|
+
--------
|
|
243
|
+
>>> import numpy
|
|
244
|
+
>>> from numdaplot.arrays.operations.time_bounds import require_whole_durations_fit
|
|
245
|
+
>>> require_whole_durations_fit(numpy.array([1, 2]), 1000, "ToDuration")
|
|
246
|
+
"""
|
|
247
|
+
if values.size == 0:
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
low_value, high_value = find_value_bounds(values)
|
|
251
|
+
low_count = low_value * nanoseconds_per_unit
|
|
252
|
+
high_count = high_value * nanoseconds_per_unit
|
|
253
|
+
if low_count < LOWEST_NANOSECONDS or high_count > HIGHEST_NANOSECONDS:
|
|
254
|
+
raise TimeOverflowError(function_name, Type.DURATION.name)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def require_decimal_durations_fit(nanoseconds: NDArray[Any], function_name: str) -> None:
|
|
258
|
+
"""
|
|
259
|
+
Check that decimal numbers of nanoseconds fit a count of nanoseconds, an infinite number being out of range.
|
|
260
|
+
|
|
261
|
+
.. versionadded:: 0.1.0
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
nanoseconds
|
|
266
|
+
Decimal numbers of nanoseconds, NaN for a missing value.
|
|
267
|
+
function_name
|
|
268
|
+
The function shown in the error message.
|
|
269
|
+
|
|
270
|
+
Raises
|
|
271
|
+
------
|
|
272
|
+
TimeOverflowError
|
|
273
|
+
When a duration is out of range.
|
|
274
|
+
|
|
275
|
+
Examples
|
|
276
|
+
--------
|
|
277
|
+
>>> import numpy
|
|
278
|
+
>>> from numdaplot.arrays.operations.time_bounds import require_decimal_durations_fit
|
|
279
|
+
>>> require_decimal_durations_fit(numpy.array([1.5, numpy.nan]), "ToDuration")
|
|
280
|
+
"""
|
|
281
|
+
magnitudes = numpy.abs(nanoseconds)
|
|
282
|
+
# A comparison with NaN is False, so a missing value is never out of range.
|
|
283
|
+
is_too_large = magnitudes >= FIRST_COUNT_TOO_LARGE
|
|
284
|
+
has_too_large = bool(numpy.any(is_too_large))
|
|
285
|
+
if has_too_large:
|
|
286
|
+
raise TimeOverflowError(function_name, Type.DURATION.name)
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Refusal of the results of calculations on times that a count of nanoseconds cannot hold.
|
|
3
|
+
|
|
4
|
+
.. versionadded:: 0.1.0
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, cast
|
|
8
|
+
|
|
9
|
+
import numpy
|
|
10
|
+
from numpy.typing import NDArray
|
|
11
|
+
|
|
12
|
+
from numdaplot.arrays.operations.integer_bounds import find_result_bounds, find_value_bounds
|
|
13
|
+
from numdaplot.arrays.operations.integer_rules import require_combination_fits
|
|
14
|
+
from numdaplot.arrays.operations.operand import Operand
|
|
15
|
+
from numdaplot.arrays.operations.time_bounds import (
|
|
16
|
+
COUNT_DTYPE,
|
|
17
|
+
COUNT_LIMITS,
|
|
18
|
+
FIRST_COUNT_TOO_LARGE,
|
|
19
|
+
HIGHEST_NANOSECONDS,
|
|
20
|
+
LOWEST_NANOSECONDS,
|
|
21
|
+
TIME_KINDS,
|
|
22
|
+
find_present_bounds,
|
|
23
|
+
)
|
|
24
|
+
from numdaplot.data_types.type import Type
|
|
25
|
+
from numdaplot.errors.integer_overflow_error import IntegerOverflowError
|
|
26
|
+
from numdaplot.errors.time_overflow_error import TimeOverflowError
|
|
27
|
+
|
|
28
|
+
DECIMAL_KIND = "f"
|
|
29
|
+
UNSIGNED_KIND = "u"
|
|
30
|
+
IGNORE_MODE = "ignore"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def widen_large_integers(values: Any) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
Return numbers beside a duration as decimals when they do not fit a signed count of nanoseconds.
|
|
36
|
+
|
|
37
|
+
.. versionadded:: 0.1.0
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
values
|
|
42
|
+
Times in nanoseconds or numbers, an array or a single value.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
Any
|
|
47
|
+
Decimals for integers too large, signed integers for ``UINT64`` that fit, the values themselves otherwise.
|
|
48
|
+
|
|
49
|
+
Raises
|
|
50
|
+
------
|
|
51
|
+
OverflowError
|
|
52
|
+
When a Python integer is too large for a decimal.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> import numpy
|
|
57
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import widen_large_integers
|
|
58
|
+
>>> widen_large_integers(2**70)
|
|
59
|
+
1.1805916207174113e+21
|
|
60
|
+
>>> widen_large_integers(numpy.array([2], dtype="uint64"))
|
|
61
|
+
array([2])
|
|
62
|
+
"""
|
|
63
|
+
if isinstance(values, int):
|
|
64
|
+
if LOWEST_NANOSECONDS <= values <= HIGHEST_NANOSECONDS:
|
|
65
|
+
return values
|
|
66
|
+
|
|
67
|
+
return float(values)
|
|
68
|
+
|
|
69
|
+
if not isinstance(values, numpy.ndarray):
|
|
70
|
+
return values
|
|
71
|
+
|
|
72
|
+
# isinstance leaves the array type unknown, because the values are typed Any.
|
|
73
|
+
array = cast(NDArray[Any], values)
|
|
74
|
+
if array.dtype.kind != UNSIGNED_KIND or array.size == 0:
|
|
75
|
+
return array
|
|
76
|
+
|
|
77
|
+
_, highest_value = find_value_bounds(array)
|
|
78
|
+
if highest_value > HIGHEST_NANOSECONDS:
|
|
79
|
+
large_decimals = array.astype(numpy.float64)
|
|
80
|
+
return large_decimals
|
|
81
|
+
|
|
82
|
+
signed_values = array.astype(COUNT_DTYPE)
|
|
83
|
+
|
|
84
|
+
return signed_values
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def can_integers_overflow(operands: list[Any], function: numpy.ufunc) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Return whether the bounds of a calculation on times and integers leave the range of nanoseconds.
|
|
90
|
+
|
|
91
|
+
.. versionadded:: 0.1.0
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
operands
|
|
96
|
+
The left and the right operands, with their times in nanoseconds.
|
|
97
|
+
function
|
|
98
|
+
``numpy.add``, ``numpy.subtract`` or ``numpy.multiply``.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
bool
|
|
103
|
+
False when every result is in range, True when each result must be looked at.
|
|
104
|
+
|
|
105
|
+
Examples
|
|
106
|
+
--------
|
|
107
|
+
>>> import numpy
|
|
108
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import can_integers_overflow
|
|
109
|
+
>>> durations = numpy.array([1000], dtype="timedelta64[ns]")
|
|
110
|
+
>>> can_integers_overflow([durations, 2], numpy.multiply)
|
|
111
|
+
False
|
|
112
|
+
>>> can_integers_overflow([durations, 2**62], numpy.multiply)
|
|
113
|
+
True
|
|
114
|
+
"""
|
|
115
|
+
left_operand, right_operand = operands
|
|
116
|
+
left_bounds = find_present_bounds(left_operand)
|
|
117
|
+
right_bounds = find_present_bounds(right_operand)
|
|
118
|
+
if left_bounds is None or right_bounds is None:
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
low_result, high_result = find_result_bounds(left_bounds, right_bounds, function)
|
|
122
|
+
bounds_fit = LOWEST_NANOSECONDS <= low_result and high_result <= HIGHEST_NANOSECONDS
|
|
123
|
+
|
|
124
|
+
return not bounds_fit
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def find_strongest_factor(factors: NDArray[Any], function: numpy.ufunc) -> float | None:
|
|
128
|
+
"""
|
|
129
|
+
Return what most moves a duration away from zero among decimal factors or divisors.
|
|
130
|
+
|
|
131
|
+
A factor that is NaN or infinite and a divisor of 0 have no result to refuse, so they are not counted.
|
|
132
|
+
|
|
133
|
+
.. versionadded:: 0.1.0
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
factors
|
|
138
|
+
The decimals.
|
|
139
|
+
function
|
|
140
|
+
``numpy.multiply`` or ``numpy.true_divide``.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
float or None
|
|
145
|
+
The number a duration is multiplied by at most, or ``None`` when no decimal gives a result.
|
|
146
|
+
|
|
147
|
+
Examples
|
|
148
|
+
--------
|
|
149
|
+
>>> import numpy
|
|
150
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import find_strongest_factor
|
|
151
|
+
>>> find_strongest_factor(numpy.array([2.0, -4.0, numpy.nan]), numpy.multiply)
|
|
152
|
+
4.0
|
|
153
|
+
>>> find_strongest_factor(numpy.array([2.0, 0.5, 0.0]), numpy.true_divide)
|
|
154
|
+
2.0
|
|
155
|
+
"""
|
|
156
|
+
decimals = factors.astype(numpy.float64)
|
|
157
|
+
magnitudes = numpy.abs(decimals)
|
|
158
|
+
if magnitudes.size == 0:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
multiplies = function == numpy.multiply
|
|
162
|
+
if multiplies:
|
|
163
|
+
largest_magnitude = float(magnitudes.max())
|
|
164
|
+
is_finite = bool(numpy.isfinite(largest_magnitude))
|
|
165
|
+
if is_finite:
|
|
166
|
+
return largest_magnitude
|
|
167
|
+
|
|
168
|
+
is_usable = numpy.isfinite(magnitudes) & (magnitudes != 0)
|
|
169
|
+
usable_magnitudes = magnitudes[is_usable]
|
|
170
|
+
if usable_magnitudes.size == 0:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
if multiplies:
|
|
174
|
+
largest_usable = float(usable_magnitudes.max())
|
|
175
|
+
return largest_usable
|
|
176
|
+
|
|
177
|
+
smallest_divisor = float(usable_magnitudes.min())
|
|
178
|
+
with numpy.errstate(all=IGNORE_MODE):
|
|
179
|
+
strongest_factor = 1 / numpy.float64(smallest_divisor)
|
|
180
|
+
|
|
181
|
+
return float(strongest_factor)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def can_decimal_factor_overflow(left_counts: Any, right_counts: Any, function: numpy.ufunc) -> bool:
|
|
185
|
+
"""
|
|
186
|
+
Return whether the largest duration and the strongest decimal factor leave the range of nanoseconds.
|
|
187
|
+
|
|
188
|
+
.. versionadded:: 0.1.0
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
left_counts
|
|
193
|
+
The left operand: durations in nanoseconds, or decimals.
|
|
194
|
+
right_counts
|
|
195
|
+
The right operand: durations in nanoseconds, or decimals.
|
|
196
|
+
function
|
|
197
|
+
``numpy.multiply`` or ``numpy.true_divide``.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
bool
|
|
202
|
+
False when every result is in range, True when each result must be looked at.
|
|
203
|
+
|
|
204
|
+
Examples
|
|
205
|
+
--------
|
|
206
|
+
>>> import numpy
|
|
207
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import can_decimal_factor_overflow
|
|
208
|
+
>>> durations = numpy.array([1000], dtype="timedelta64[ns]")
|
|
209
|
+
>>> can_decimal_factor_overflow(durations, 2.5, numpy.multiply)
|
|
210
|
+
False
|
|
211
|
+
>>> can_decimal_factor_overflow(durations, 1e-300, numpy.true_divide)
|
|
212
|
+
True
|
|
213
|
+
"""
|
|
214
|
+
left_array = numpy.asarray(left_counts)
|
|
215
|
+
right_array = numpy.asarray(right_counts)
|
|
216
|
+
durations, factors = left_array, right_array
|
|
217
|
+
if left_array.dtype.kind == DECIMAL_KIND:
|
|
218
|
+
durations, factors = right_array, left_array
|
|
219
|
+
|
|
220
|
+
duration_bounds = find_present_bounds(durations)
|
|
221
|
+
if duration_bounds is None:
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
strongest_factor = find_strongest_factor(factors, function)
|
|
225
|
+
if strongest_factor is None:
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
low_duration, high_duration = duration_bounds
|
|
229
|
+
low_magnitude = abs(low_duration)
|
|
230
|
+
high_magnitude = abs(high_duration)
|
|
231
|
+
longest_count = max(low_magnitude, high_magnitude)
|
|
232
|
+
longest_duration = float(longest_count)
|
|
233
|
+
with numpy.errstate(all=IGNORE_MODE):
|
|
234
|
+
largest_estimate = longest_duration * strongest_factor
|
|
235
|
+
|
|
236
|
+
return bool(largest_estimate >= FIRST_COUNT_TOO_LARGE)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def to_counts(values: Any) -> NDArray[Any]:
|
|
240
|
+
"""
|
|
241
|
+
Return values of a calculation with their times as integers, a missing time counting as 0.
|
|
242
|
+
|
|
243
|
+
.. versionadded:: 0.1.0
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
values
|
|
248
|
+
Times in nanoseconds or numbers, an array or a single value.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
NDArray
|
|
253
|
+
The counts of nanoseconds, or the numbers.
|
|
254
|
+
|
|
255
|
+
Examples
|
|
256
|
+
--------
|
|
257
|
+
>>> import numpy
|
|
258
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import to_counts
|
|
259
|
+
>>> to_counts(numpy.array([5, "NaT"], dtype="timedelta64[ns]"))
|
|
260
|
+
array([5, 0])
|
|
261
|
+
"""
|
|
262
|
+
array = numpy.asarray(values)
|
|
263
|
+
if array.dtype.kind not in TIME_KINDS:
|
|
264
|
+
return array
|
|
265
|
+
|
|
266
|
+
counts = array.astype(COUNT_DTYPE)
|
|
267
|
+
missing = numpy.isnat(array)
|
|
268
|
+
present_counts = numpy.where(missing, 0, counts)
|
|
269
|
+
|
|
270
|
+
return present_counts
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def require_decimal_factor_fits(operands: list[Any], function: numpy.ufunc, result_type: Type, symbol: str) -> None:
|
|
274
|
+
"""
|
|
275
|
+
Check that a duration multiplied or divided by decimals fits a count of nanoseconds.
|
|
276
|
+
|
|
277
|
+
A factor that is NaN or infinite and a divisor of 0 have no result to refuse: they give missing values.
|
|
278
|
+
|
|
279
|
+
.. versionadded:: 0.1.0
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
operands
|
|
284
|
+
The counts of the left and the right operands, one of them holding the decimals.
|
|
285
|
+
function
|
|
286
|
+
``numpy.multiply`` or ``numpy.true_divide``.
|
|
287
|
+
result_type
|
|
288
|
+
The type named by the error message.
|
|
289
|
+
symbol
|
|
290
|
+
The operator shown in the error message.
|
|
291
|
+
|
|
292
|
+
Raises
|
|
293
|
+
------
|
|
294
|
+
TimeOverflowError
|
|
295
|
+
When a result is out of range.
|
|
296
|
+
|
|
297
|
+
Examples
|
|
298
|
+
--------
|
|
299
|
+
>>> import numpy
|
|
300
|
+
>>> import numdaplot as ndp
|
|
301
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import require_decimal_factor_fits
|
|
302
|
+
>>> operands = [numpy.array([2**62]), numpy.array([4.0])]
|
|
303
|
+
>>> try:
|
|
304
|
+
... require_decimal_factor_fits(operands, numpy.multiply, ndp.Type.DURATION, "*")
|
|
305
|
+
... except ndp.TimeOverflowError as error:
|
|
306
|
+
... print(error)
|
|
307
|
+
The operation * gives values outside the range of DURATION, about 292 years before or after zero.
|
|
308
|
+
"""
|
|
309
|
+
left_counts, right_counts = operands
|
|
310
|
+
factors = right_counts
|
|
311
|
+
if left_counts.dtype.kind == DECIMAL_KIND:
|
|
312
|
+
factors = left_counts
|
|
313
|
+
|
|
314
|
+
left_decimals = left_counts.astype(numpy.float64)
|
|
315
|
+
right_decimals = right_counts.astype(numpy.float64)
|
|
316
|
+
with numpy.errstate(all=IGNORE_MODE):
|
|
317
|
+
estimates = function(left_decimals, right_decimals)
|
|
318
|
+
|
|
319
|
+
has_result = numpy.isfinite(factors)
|
|
320
|
+
if function == numpy.true_divide:
|
|
321
|
+
has_result = has_result & (factors != 0)
|
|
322
|
+
|
|
323
|
+
magnitudes = numpy.abs(estimates)
|
|
324
|
+
is_too_large = (magnitudes >= FIRST_COUNT_TOO_LARGE) & has_result
|
|
325
|
+
has_too_large = bool(numpy.any(is_too_large))
|
|
326
|
+
if has_too_large:
|
|
327
|
+
raise TimeOverflowError(symbol, result_type.name)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def require_time_result_fits(operands: list[Any], function: numpy.ufunc, result_type: Type, symbol: str) -> None:
|
|
331
|
+
"""
|
|
332
|
+
Check that the results of a calculation on times fit a count of nanoseconds, before computing them.
|
|
333
|
+
|
|
334
|
+
.. versionadded:: 0.1.0
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
operands
|
|
339
|
+
The left and the right operands, with their times in nanoseconds.
|
|
340
|
+
function
|
|
341
|
+
The numpy function about to be applied.
|
|
342
|
+
result_type
|
|
343
|
+
The type named by the error message.
|
|
344
|
+
symbol
|
|
345
|
+
The operator shown in the error message.
|
|
346
|
+
|
|
347
|
+
Raises
|
|
348
|
+
------
|
|
349
|
+
TimeOverflowError
|
|
350
|
+
When a result is out of range.
|
|
351
|
+
|
|
352
|
+
Examples
|
|
353
|
+
--------
|
|
354
|
+
>>> import numpy
|
|
355
|
+
>>> import numdaplot as ndp
|
|
356
|
+
>>> from numdaplot.arrays.operations.time_result_bounds import require_time_result_fits
|
|
357
|
+
>>> durations = numpy.array([1], dtype="timedelta64[D]").astype("timedelta64[ns]")
|
|
358
|
+
>>> try:
|
|
359
|
+
... require_time_result_fits([durations, 200000], numpy.multiply, ndp.Type.DURATION, "*")
|
|
360
|
+
... except ndp.TimeOverflowError as error:
|
|
361
|
+
... print(error)
|
|
362
|
+
The operation * gives values outside the range of DURATION, about 292 years before or after zero.
|
|
363
|
+
"""
|
|
364
|
+
left_operand, right_operand = operands
|
|
365
|
+
left_kind = numpy.asarray(left_operand).dtype.kind
|
|
366
|
+
right_kind = numpy.asarray(right_operand).dtype.kind
|
|
367
|
+
has_decimal = DECIMAL_KIND in (left_kind, right_kind)
|
|
368
|
+
if has_decimal:
|
|
369
|
+
can_overflow = can_decimal_factor_overflow(left_operand, right_operand, function)
|
|
370
|
+
if can_overflow:
|
|
371
|
+
left_counts = to_counts(left_operand)
|
|
372
|
+
right_counts = to_counts(right_operand)
|
|
373
|
+
require_decimal_factor_fits([left_counts, right_counts], function, result_type, symbol)
|
|
374
|
+
|
|
375
|
+
return
|
|
376
|
+
|
|
377
|
+
# Dividing by an integer never moves a count away from zero.
|
|
378
|
+
if function == numpy.true_divide:
|
|
379
|
+
return
|
|
380
|
+
|
|
381
|
+
can_overflow = can_integers_overflow(operands, function)
|
|
382
|
+
if not can_overflow:
|
|
383
|
+
return
|
|
384
|
+
|
|
385
|
+
left_counts = to_counts(left_operand)
|
|
386
|
+
right_counts = to_counts(right_operand)
|
|
387
|
+
count_operands = list[Operand]([left_counts, right_counts])
|
|
388
|
+
wrapped_counts = function(left_counts, right_counts)
|
|
389
|
+
try:
|
|
390
|
+
require_combination_fits(count_operands, function, wrapped_counts, symbol)
|
|
391
|
+
except IntegerOverflowError as error:
|
|
392
|
+
raise TimeOverflowError(symbol, result_type.name) from error
|
|
393
|
+
|
|
394
|
+
reaches_missing_marker = wrapped_counts == COUNT_LIMITS.min
|
|
395
|
+
has_marker = bool(numpy.any(reaches_missing_marker))
|
|
396
|
+
if has_marker:
|
|
397
|
+
raise TimeOverflowError(symbol, result_type.name)
|