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