tabalyst 0.7.3__tar.gz → 0.7.4__tar.gz
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.
- {tabalyst-0.7.3/src/tabalyst.egg-info → tabalyst-0.7.4}/PKG-INFO +1 -1
- {tabalyst-0.7.3 → tabalyst-0.7.4}/pyproject.toml +1 -1
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/sample.py +45 -3
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/sampling.py +154 -26
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/sampling_service.py +9 -2
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/render.py +2 -2
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/stratification.py +5 -8
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/stratmodels.py +2 -2
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/stratify.py +40 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4/src/tabalyst.egg-info}/PKG-INFO +1 -1
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst.egg-info/SOURCES.txt +1 -0
- tabalyst-0.7.4/tests/test_sampling_stratified_balanced.py +292 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/LICENSE +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/MANIFEST.in +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/README.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/README.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/config.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/datasets/basic.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/datasets/insurance-customers.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/README.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/cohorts-anomalies/catalog.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/cohorts-anomalies/dataset.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/cohorts-anomalies/schemas/accounts.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/cohorts-anomalies/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/common-references/catalog.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/common-references/dataset.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/common-references/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/examples/generate/generate_examples.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/setup.cfg +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/__main__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/_version.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/batch.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cache_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/app.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/cache.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/confirm.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/dedup.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/generate.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/inspect.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/report.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/scan.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/cli/terminal.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/config.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/csv_records.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/analysis.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/batch_report.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/cache.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/calculation.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/count_only.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/discovery.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/diskstore.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/failures.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/groups.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/identity.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/pipeline.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/publish.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/report.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/settings.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/store.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/tiered.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup/writer.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/dedup_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/deep_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/deep_store.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/errors.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/execution_log.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_anomalies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_columns_package.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_computer.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_computer_output.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_computer_parallel.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_csv.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_definition.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/SOURCES.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/catalog.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/brand.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/color.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/computer_type.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/computer_type_form_factor.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/computer_type_product_segment.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/cpu.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/cpu_memory_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/cpu_operating_system.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/display.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/extended_specs.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/form_factor.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_color.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_cpu.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_display.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_gpu.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_memory.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_operating_system.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_profile.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_profile_computer_type.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_profile_segment.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/generation_storage.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/gpu.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/gpu_display.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/gpu_memory_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/gpu_storage_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/memory.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/model_form_factor.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/model_generation.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/operating_system.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_category.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_family.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_family_computer_type.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_family_product_segment.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_model.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_segment.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_segment_brand.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_segment_display_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_segment_memory_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_segment_storage_weight.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/product_subcategory.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/profile_exclusions.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/promotion_status.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/promotion_type.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/seller.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/seller_type.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/shipping_availability.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/standard_specs.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/storage.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/data/warehouse.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/dataset.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/computer_type.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/computer_type_form_factor.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/computer_type_product_segment.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/computers.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/cpu.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/cpu_memory_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/cpu_operating_system.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/display.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/extended_specs.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_color.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_cpu.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_display.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_gpu.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_memory.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_operating_system.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_profile.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_profile_computer_type.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_profile_segment.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/generation_storage.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/gpu.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/gpu_display.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/gpu_memory_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/gpu_storage_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/localized.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/memory.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/model_form_factor.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/model_generation.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/operating_system.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_family.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_family_computer_type.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_family_product_segment.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_model.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_segment.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_segment_brand.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_segment_display_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_segment_memory_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_segment_storage_weight.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/product_subcategory.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/profile_exclusions.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/seller.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/standard_specs.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/storage.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/computer/schemas/warehouse.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/crm/catalog.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/crm/dataset.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/crm/schemas/accounts.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/crm/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/SOURCES.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/catalog.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/agencies.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/branch_contract_weights.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/claim_statuses.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/contract_statuses.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/contract_types.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/payment_frequencies.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/data/payment_methods.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/dataset.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/agencies.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/branch_contract_weights.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/claim_statuses.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/contract_statuses.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/contract_types.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/customers.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/payment_frequencies.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_examples/insurance/schemas/payment_methods.yaml +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_insurance.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_package.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_references.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/SOURCES.md +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/addresses/street_directions_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/addresses/street_names_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/addresses/street_types_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/addresses/unit_types_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/contact/email_patterns.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/contact/email_providers_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/contact/phone_area_codes_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/geography/cities_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/geography/postal_prefixes_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/geography/provinces_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/people/family_names_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/people/first_names_en_ca.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_resources/people/first_names_fr_qc.csv +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_selection.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/generate_values.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/facts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/near_unique.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/proof.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/identifier_uniqueness/rule.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspect_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/choices.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/excel_inspect/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/excel_inspect/build.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/excel_inspect/detect.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/excel_inspect/parameters.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/json_inspect/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/json_inspect/build.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/json_inspect/detect.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/json_inspect/parameters.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/persistence.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/inspector/resolution.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/json_text.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/option_notices.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/plan_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/progress.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/project_open_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/project_scan_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_bounded_values.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_cache.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_codec.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_generation.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_locks.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_publication.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_queries.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_query_budget.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_recovery.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_retention.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_schema.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_session.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_staging.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_sync.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/_validation.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/catalog.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/freshness.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/identity.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/index.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/location.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/projects/store.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_chain.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_classification.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_column_page.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_config.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_deep.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_details.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_finding_details.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_findings.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_insights.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_labels.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_measures.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_overview.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_plan.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_preview.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_profile.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_relations.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_schema.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_sections.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_signals.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_summary.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_table.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_uniqueness.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/report_views.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/reporting.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/result_model.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/sample_page.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scan_document.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scan_page.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scan_reuse.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scan_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/api.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/bottomk.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/cases.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/charactermap.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/characters.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/candidates.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/catalog.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/identifiers.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/names.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/classify/rules.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/config.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/convention_rule.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/correlation_ratio.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/crosstab.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/amount.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/base.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/boolean.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/currency.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/email.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/enumeration.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/ip.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/keys.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/names.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/number.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/pattern.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/percentage.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/phone.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/postal.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/quantity.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/registry.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/shape.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/temporal.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/url.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/detectors/uuid.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/diagnostics.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/engine.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/exposure.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/features.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/field.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/identity.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/lengthsample.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/longtext.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/measures.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/memory.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/normalization.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/observations.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/paths.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/pattern_eligibility.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/patternfacets.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/patterninfer.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/patterntree.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/pearson.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzer.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/analytical.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/characters.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/common.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/copies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/crosstab.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/date_order.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/distinct.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/drift.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/duplicates.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/forms.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/frequencies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/histogram.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/lengths.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/matrix.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/measures.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/missing_copies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/parts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/patterns.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/pearson.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/residuals.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/shape.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/stats.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/structure.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/temporal.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/analyzers/texts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/categorystats.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/costmodel.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/crosstabs.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/facts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/identifiers.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/orders.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/planner.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/reasons.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/registry.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/relations.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/samplerows.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/planner/secondpass.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/process.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/quality.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/rankcorr.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/base.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/csv_reader.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/excel_common.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/excel_reader.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/excel_table.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/factory.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/json_common.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/json_reader.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/readers/jsonl_reader.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/records.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/rowsample.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/schema.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/accumulators.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/analytical.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/bins.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/boundaries.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/characters.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/copies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/crosstab.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/document.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/drift.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/duplicates.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/exactset.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/executor.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/forms.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/frequencies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/matrix.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/measures.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/missing_copies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/orders.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/parallel.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/parts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/patternreader.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/patterns.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/pearson.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/periods.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/ranks.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/replay.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/residuals.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/routing.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/schema.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/shape.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/stats.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/structure.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/secondpass/texts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/__init__.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/bins.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/estimate.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/evaluate.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/execute.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/near.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/plan.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/reasons.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/render.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/result.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/select.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/targets.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/smart/weights.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/structure.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/technical.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/validation.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/values.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/valuesample.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/scanner/workers.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.dedup.batch.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.dedup.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.report.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.sample.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.scan.deep.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/schemas/tabalyst.scan.schema.json +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/shared_scan_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/smart_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/source_guard.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/static/report.js +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/static/theme.css +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/summary_models.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/characters.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/column.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/insights.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/patterns.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/report.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst/templates/sample.html +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst.egg-info/dependency_links.txt +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst.egg-info/entry_points.txt +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst.egg-info/requires.txt +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/src/tabalyst.egg-info/top_level.txt +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_all_collections.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_cli.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_csv_records.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_examples_index.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_examples_layout.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_anomalies.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_anomalies_regression.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_catalog.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_components.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_derivations.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_hierarchy.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_output.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_parallel.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_computer_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_crm.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_crm_package.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_insurance.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_insurance_stress.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_lot4.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_package.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_references.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_generate_selection.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_json_text.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_license_headers.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_make_datasets.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_option_notices.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_cache.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_database.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_generations.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_identity.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_index.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_location.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_open_service.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_queries.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_rescan.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_scan.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_scan_model.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_sessions.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_project_store.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_public_api.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_report_contrast.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_reporting.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_sampling.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scan_command.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_amounts.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_batches.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_config.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_csv.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_email_url.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_exposure.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_paths.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_phone.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_postal.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_scanner_uuid_ip.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_shared_scan_cli.py +0 -0
- {tabalyst-0.7.3 → tabalyst-0.7.4}/tests/test_source_freshness.py +0 -0
|
@@ -32,6 +32,8 @@ def _print_result(result: SampleResult) -> None:
|
|
|
32
32
|
typer.echo(f"Source rows: {result.source_rows:,}", err=True)
|
|
33
33
|
typer.echo(f"Sample rows: {result.sample_rows:,}", err=True)
|
|
34
34
|
typer.echo(f"Sample rate: {result.sample_rate:.2f}%", err=True)
|
|
35
|
+
if result.per_value is not None:
|
|
36
|
+
typer.echo(f"Per value: up to {result.per_value:,} rows", err=True)
|
|
35
37
|
if result.strata_known is not None:
|
|
36
38
|
typer.echo(
|
|
37
39
|
f"Strata: {result.strata_known:,} known, "
|
|
@@ -39,9 +41,23 @@ def _print_result(result: SampleResult) -> None:
|
|
|
39
41
|
f"{result.strata_uncovered:,} without rows",
|
|
40
42
|
err=True,
|
|
41
43
|
)
|
|
44
|
+
if result.strata_capped is not None:
|
|
45
|
+
typer.echo(
|
|
46
|
+
f"Drawn from: {result.strata_capped:,} strata with more rows than "
|
|
47
|
+
f"the quota; the other {result.strata_known - result.strata_capped:,} "
|
|
48
|
+
"are kept whole",
|
|
49
|
+
err=True,
|
|
50
|
+
)
|
|
51
|
+
if result.source_rows and result.sample_rows * 2 >= result.source_rows:
|
|
52
|
+
typer.echo(
|
|
53
|
+
"Note: the sample holds half of the file or more: the "
|
|
54
|
+
"strata are many or the quota is large.",
|
|
55
|
+
err=True,
|
|
56
|
+
)
|
|
42
57
|
if result.seed is not None and result.method in {
|
|
43
58
|
SampleMethod.RANDOM,
|
|
44
59
|
SampleMethod.STRATIFIED,
|
|
60
|
+
SampleMethod.STRATIFIED_BALANCED,
|
|
45
61
|
}:
|
|
46
62
|
typer.echo(f"Seed: {result.seed}", err=True)
|
|
47
63
|
smart = result.smart
|
|
@@ -144,18 +160,32 @@ def _reject_unused_options(
|
|
|
144
160
|
seed: int | None,
|
|
145
161
|
yes: bool,
|
|
146
162
|
no_html: bool,
|
|
163
|
+
per_value: int | None,
|
|
164
|
+
rows: int | None,
|
|
165
|
+
percent: float | None,
|
|
147
166
|
) -> None:
|
|
148
167
|
"""Refuse an option the method does not read: none is ignored without an error."""
|
|
149
168
|
smart = method is SampleMethod.SMART
|
|
150
169
|
for name, given in (("--yes", yes), ("--no-html", no_html)):
|
|
151
170
|
if given and not smart:
|
|
152
171
|
raise typer.BadParameter(f"{name} only goes with the smart method.")
|
|
153
|
-
|
|
172
|
+
balanced = method is SampleMethod.STRATIFIED_BALANCED
|
|
173
|
+
stratified = balanced or method is SampleMethod.STRATIFIED
|
|
154
174
|
for name, given in (("--field", bool(field)), ("--max-strata", max_strata is not None)):
|
|
155
175
|
if given and not stratified:
|
|
156
176
|
raise typer.BadParameter(
|
|
157
|
-
f"{name} only goes with --sample-method stratified
|
|
177
|
+
f"{name} only goes with --sample-method stratified or "
|
|
178
|
+
f"stratified-balanced, not {method.value}."
|
|
158
179
|
)
|
|
180
|
+
if per_value is not None and not balanced:
|
|
181
|
+
raise typer.BadParameter(
|
|
182
|
+
f"--per-value only goes with --sample-method stratified-balanced, not {method.value}."
|
|
183
|
+
)
|
|
184
|
+
if balanced and (rows is not None or percent is not None):
|
|
185
|
+
raise typer.BadParameter(
|
|
186
|
+
"--rows and --percent do not go with stratified-balanced: use --per-value "
|
|
187
|
+
"for the quota of each stratum."
|
|
188
|
+
)
|
|
159
189
|
if seed is not None and method in {SampleMethod.FIRST, SampleMethod.LAST}:
|
|
160
190
|
raise typer.BadParameter(
|
|
161
191
|
f"--seed has no effect with {method.value}: it keeps the rows in file order."
|
|
@@ -197,10 +227,18 @@ def sample_command(
|
|
|
197
227
|
list[str] | None,
|
|
198
228
|
typer.Option(
|
|
199
229
|
"--field",
|
|
200
|
-
help="Field used for stratified sampling; repeat it to stratify "
|
|
230
|
+
help="Field used for stratified sampling (stratified, stratified-balanced); repeat it to stratify "
|
|
201
231
|
"by the combinations of several fields.",
|
|
202
232
|
),
|
|
203
233
|
] = None,
|
|
234
|
+
per_value: Annotated[
|
|
235
|
+
int | None,
|
|
236
|
+
typer.Option(
|
|
237
|
+
"--per-value",
|
|
238
|
+
help="With stratified-balanced, the most rows kept from each stratum "
|
|
239
|
+
"(all of a smaller one). The size of the sample follows from the data.",
|
|
240
|
+
),
|
|
241
|
+
] = None,
|
|
204
242
|
max_strata: Annotated[
|
|
205
243
|
int | None,
|
|
206
244
|
typer.Option(
|
|
@@ -273,6 +311,9 @@ def sample_command(
|
|
|
273
311
|
seed=seed,
|
|
274
312
|
yes=yes,
|
|
275
313
|
no_html=no_html,
|
|
314
|
+
per_value=per_value,
|
|
315
|
+
rows=rows,
|
|
316
|
+
percent=percent,
|
|
276
317
|
)
|
|
277
318
|
try:
|
|
278
319
|
if method is SampleMethod.SMART:
|
|
@@ -305,6 +346,7 @@ def sample_command(
|
|
|
305
346
|
encoding=encoding,
|
|
306
347
|
force=force,
|
|
307
348
|
max_strata=max_strata,
|
|
349
|
+
per_value=per_value,
|
|
308
350
|
)
|
|
309
351
|
except TabalystError as exc:
|
|
310
352
|
typer.echo(f"Error: {exc}", err=True)
|
|
@@ -21,7 +21,7 @@ from tabalyst.batch import apply_default_file_mode
|
|
|
21
21
|
from tabalyst.config import CsvConfig
|
|
22
22
|
from tabalyst.csv_records import open_records, output_encoding
|
|
23
23
|
from tabalyst.errors import InputError, ReportError
|
|
24
|
-
from tabalyst.scanner.stratify import
|
|
24
|
+
from tabalyst.scanner.stratify import allocate_strata_with_minimum
|
|
25
25
|
|
|
26
26
|
# The most strata a stratified sample keeps at once (STRAT-08): one counter and
|
|
27
27
|
# one reservoir per stratum are held in memory while the file streams.
|
|
@@ -35,9 +35,13 @@ class SampleMethod(StrEnum):
|
|
|
35
35
|
LAST = "last"
|
|
36
36
|
RANDOM = "random"
|
|
37
37
|
STRATIFIED = "stratified"
|
|
38
|
+
STRATIFIED_BALANCED = "stratified-balanced"
|
|
38
39
|
SMART = "smart"
|
|
39
40
|
|
|
40
41
|
|
|
42
|
+
_STRATIFIED_METHODS = frozenset({"stratified", "stratified-balanced"})
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
@dataclass(frozen=True)
|
|
42
46
|
class SmartSummary:
|
|
43
47
|
"""What a smart sample covers of the catalog of targets of its plan (design
|
|
@@ -72,11 +76,15 @@ class SampleResult:
|
|
|
72
76
|
delimiter: str
|
|
73
77
|
# The columns of a stratified sample (``field`` joins them with a comma) and
|
|
74
78
|
# its balance: the strata the file has, the ones that got rows, and the
|
|
75
|
-
# ones
|
|
79
|
+
# ones without a row (STRAT-02). ``None`` for the other methods.
|
|
76
80
|
fields: tuple[str, ...] = ()
|
|
77
81
|
strata_known: int | None = None
|
|
78
82
|
strata_selected: int | None = None
|
|
79
83
|
strata_uncovered: int | None = None
|
|
84
|
+
# ``stratified-balanced`` only: the quota asked per stratum, and the strata
|
|
85
|
+
# that have more rows than it (so that were drawn from, not kept whole).
|
|
86
|
+
per_value: int | None = None
|
|
87
|
+
strata_capped: int | None = None
|
|
80
88
|
# A smart sample writes a report beside its CSV file and sums it up here.
|
|
81
89
|
report: Path | None = None
|
|
82
90
|
page: Path | None = None
|
|
@@ -107,6 +115,26 @@ def _validate_paths(source: Path, output: Path, *, force: bool) -> None:
|
|
|
107
115
|
raise ReportError(f"Output path is not a file: {output}")
|
|
108
116
|
|
|
109
117
|
|
|
118
|
+
def _field_indexes(headers: Sequence[str], fields: Sequence[str]) -> tuple[int, ...]:
|
|
119
|
+
indexes: list[int] = []
|
|
120
|
+
for field in fields:
|
|
121
|
+
matches = [index for index, name in enumerate(headers) if name == field]
|
|
122
|
+
if not matches:
|
|
123
|
+
raise InputError(f"Stratification field does not exist: {field}")
|
|
124
|
+
if len(matches) > 1:
|
|
125
|
+
raise InputError(f"Stratification field is ambiguous: {field}")
|
|
126
|
+
indexes.append(matches[0])
|
|
127
|
+
return tuple(indexes)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _too_many_strata(fields: Sequence[str], max_strata: int, record: int) -> InputError:
|
|
131
|
+
return InputError(
|
|
132
|
+
f"Stratification by {', '.join(fields)} found more than "
|
|
133
|
+
f"{max_strata:,} strata at data record {record}. Use "
|
|
134
|
+
"fewer or less detailed fields, or raise --max-strata."
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
110
138
|
def _scan(
|
|
111
139
|
source: Path,
|
|
112
140
|
config: CsvConfig,
|
|
@@ -116,14 +144,7 @@ def _scan(
|
|
|
116
144
|
strata: Counter[Hashable] = Counter()
|
|
117
145
|
with open_records(source, config) as records:
|
|
118
146
|
headers = records.header
|
|
119
|
-
field_indexes
|
|
120
|
-
for field in fields:
|
|
121
|
-
matches = [index for index, name in enumerate(headers) if name == field]
|
|
122
|
-
if not matches:
|
|
123
|
-
raise InputError(f"Stratification field does not exist: {field}")
|
|
124
|
-
if len(matches) > 1:
|
|
125
|
-
raise InputError(f"Stratification field is ambiguous: {field}")
|
|
126
|
-
field_indexes.append(matches[0])
|
|
147
|
+
field_indexes = list(_field_indexes(headers, fields))
|
|
127
148
|
|
|
128
149
|
row_count = 0
|
|
129
150
|
for row_count, row in enumerate(records, start=1):
|
|
@@ -131,11 +152,7 @@ def _scan(
|
|
|
131
152
|
key = _stratum_key(row, field_indexes)
|
|
132
153
|
strata[key] += 1
|
|
133
154
|
if len(strata) > max_strata:
|
|
134
|
-
raise
|
|
135
|
-
f"Stratification by {', '.join(fields)} found more than "
|
|
136
|
-
f"{max_strata:,} strata at data record {row_count}. Use "
|
|
137
|
-
"fewer or less detailed fields, or raise --max-strata."
|
|
138
|
-
)
|
|
155
|
+
raise _too_many_strata(fields, max_strata, row_count)
|
|
139
156
|
return _ScanResult(headers, row_count, strata, tuple(field_indexes))
|
|
140
157
|
|
|
141
158
|
|
|
@@ -174,6 +191,17 @@ def _sample_size(row_count: int, rows: int | None, percent: float | None) -> int
|
|
|
174
191
|
return size
|
|
175
192
|
|
|
176
193
|
|
|
194
|
+
def _check_every_stratum_fits(size: int, strata: int, fields: Sequence[str]) -> None:
|
|
195
|
+
"""A proportional sample keeps one row of every stratum or does not run: it
|
|
196
|
+
never drops categories silently."""
|
|
197
|
+
if size < strata:
|
|
198
|
+
raise InputError(
|
|
199
|
+
f"A stratified sample of {size:,} rows cannot keep one row of each of the "
|
|
200
|
+
f"{strata:,} strata of {', '.join(fields)}: use --rows {strata:,} or more, "
|
|
201
|
+
"or fewer or less detailed fields."
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
177
205
|
def _reservoir_add(
|
|
178
206
|
reservoir: list[tuple[int, list[str]]],
|
|
179
207
|
item: tuple[int, list[str]],
|
|
@@ -230,6 +258,52 @@ def _select_rows(
|
|
|
230
258
|
return [row for _, row in selected]
|
|
231
259
|
|
|
232
260
|
|
|
261
|
+
@dataclass(frozen=True)
|
|
262
|
+
class _BalancedSelection:
|
|
263
|
+
headers: list[str]
|
|
264
|
+
row_count: int
|
|
265
|
+
rows: list[list[str]]
|
|
266
|
+
strata: int
|
|
267
|
+
capped: int
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _select_balanced(
|
|
271
|
+
source: Path,
|
|
272
|
+
config: CsvConfig,
|
|
273
|
+
fields: Sequence[str],
|
|
274
|
+
per_value: int,
|
|
275
|
+
seed: int | None,
|
|
276
|
+
max_strata: int,
|
|
277
|
+
) -> _BalancedSelection:
|
|
278
|
+
"""Draw up to ``per_value`` rows of each stratum in one reading.
|
|
279
|
+
|
|
280
|
+
One reservoir of ``per_value`` rows per stratum, filled independently: the
|
|
281
|
+
places a small stratum leaves unused are never given to another one. The
|
|
282
|
+
rows come back in file order.
|
|
283
|
+
"""
|
|
284
|
+
rng = random.Random(seed)
|
|
285
|
+
reservoirs: dict[Hashable, list[tuple[int, list[str]]]] = {}
|
|
286
|
+
seen_by_stratum: Counter[Hashable] = Counter()
|
|
287
|
+
row_count = 0
|
|
288
|
+
with open_records(source, config) as reader:
|
|
289
|
+
headers = reader.header
|
|
290
|
+
indexes = _field_indexes(headers, fields)
|
|
291
|
+
for index, row in enumerate(reader):
|
|
292
|
+
row_count = index + 1
|
|
293
|
+
key = _stratum_key(row, indexes)
|
|
294
|
+
if key not in reservoirs:
|
|
295
|
+
if len(reservoirs) >= max_strata:
|
|
296
|
+
raise _too_many_strata(fields, max_strata, row_count)
|
|
297
|
+
reservoirs[key] = []
|
|
298
|
+
seen_by_stratum[key] += 1
|
|
299
|
+
_reservoir_add(reservoirs[key], (index, row), seen_by_stratum[key], per_value, rng)
|
|
300
|
+
chosen = sorted(item for items in reservoirs.values() for item in items)
|
|
301
|
+
capped = sum(1 for count in seen_by_stratum.values() if count > per_value)
|
|
302
|
+
return _BalancedSelection(
|
|
303
|
+
headers, row_count, [row for _, row in chosen], len(reservoirs), capped
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
233
307
|
def write_sample_csv(
|
|
234
308
|
output: Path,
|
|
235
309
|
header: Sequence[str],
|
|
@@ -291,6 +365,7 @@ def validate_sample_options(
|
|
|
291
365
|
percent: float | None,
|
|
292
366
|
field: str | Sequence[str] | None,
|
|
293
367
|
max_strata: int = DEFAULT_MAX_STRATA,
|
|
368
|
+
per_value: int | None = None,
|
|
294
369
|
) -> SampleMethod:
|
|
295
370
|
"""Validate options shared by single-file and batch adapters."""
|
|
296
371
|
|
|
@@ -300,12 +375,28 @@ def validate_sample_options(
|
|
|
300
375
|
choices = ", ".join(item.value for item in SampleMethod)
|
|
301
376
|
raise InputError(f"Unknown sample method {method!r}; choose from {choices}.") from exc
|
|
302
377
|
fields = stratification_fields(field)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
378
|
+
stratified = selected_method in _STRATIFIED_METHODS
|
|
379
|
+
if stratified and not fields:
|
|
380
|
+
raise InputError(f"--field is required with the {selected_method.value} method.")
|
|
381
|
+
if not stratified and fields:
|
|
382
|
+
raise InputError(
|
|
383
|
+
"--field can only be used with the stratified and stratified-balanced methods."
|
|
384
|
+
)
|
|
307
385
|
if max_strata < 1:
|
|
308
386
|
raise InputError("--max-strata must be at least 1.")
|
|
387
|
+
if selected_method is SampleMethod.STRATIFIED_BALANCED:
|
|
388
|
+
if rows is not None or percent is not None:
|
|
389
|
+
raise InputError(
|
|
390
|
+
"--rows and --percent do not go with stratified-balanced: it keeps "
|
|
391
|
+
"up to --per-value rows of each stratum, so its size follows from the data."
|
|
392
|
+
)
|
|
393
|
+
if per_value is None:
|
|
394
|
+
raise InputError("--per-value is required with the stratified-balanced method.")
|
|
395
|
+
if isinstance(per_value, bool) or not isinstance(per_value, int) or per_value < 1:
|
|
396
|
+
raise InputError("--per-value must be a whole number greater than 0.")
|
|
397
|
+
return selected_method
|
|
398
|
+
if per_value is not None:
|
|
399
|
+
raise InputError("--per-value can only be used with the stratified-balanced method.")
|
|
309
400
|
_validate_size_options(rows, percent)
|
|
310
401
|
return selected_method
|
|
311
402
|
|
|
@@ -323,18 +414,28 @@ def sample_csv(
|
|
|
323
414
|
encoding: str = "utf-8-sig",
|
|
324
415
|
force: bool = False,
|
|
325
416
|
max_strata: int = DEFAULT_MAX_STRATA,
|
|
417
|
+
per_value: int | None = None,
|
|
326
418
|
) -> SampleResult:
|
|
327
419
|
"""Create a bounded-memory sample of a CSV file.
|
|
328
420
|
|
|
329
|
-
A stratified sample allocates its rows to the strata in proportion
|
|
330
|
-
size, with
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
421
|
+
A ``stratified`` sample allocates its ``rows`` to the strata in proportion
|
|
422
|
+
to their size, with at least one row of each: when ``rows`` is under the
|
|
423
|
+
number of strata it stops with an ``InputError`` instead of dropping some.
|
|
424
|
+
A ``stratified-balanced`` sample keeps up to ``per_value`` random rows of
|
|
425
|
+
each stratum (all of a smaller stratum, nothing moved to another), so its
|
|
426
|
+
size is the sum of ``min(per_value, rows of the stratum)``. ``field`` may
|
|
427
|
+
name several columns; a stratum is then a combination of their values. More
|
|
428
|
+
than ``max_strata`` strata stop the operation with an ``InputError`` rather
|
|
429
|
+
than grow without bound.
|
|
334
430
|
"""
|
|
335
431
|
|
|
336
432
|
selected_method = validate_sample_options(
|
|
337
|
-
method,
|
|
433
|
+
method,
|
|
434
|
+
rows=rows,
|
|
435
|
+
percent=percent,
|
|
436
|
+
field=field,
|
|
437
|
+
max_strata=max_strata,
|
|
438
|
+
per_value=per_value,
|
|
338
439
|
)
|
|
339
440
|
if selected_method is SampleMethod.SMART:
|
|
340
441
|
raise InputError(
|
|
@@ -352,10 +453,37 @@ def sample_csv(
|
|
|
352
453
|
Path(output) if output is not None else default_sample_output(source_path)
|
|
353
454
|
)
|
|
354
455
|
_validate_paths(source_path, output_path, force=force)
|
|
456
|
+
if selected_method is SampleMethod.STRATIFIED_BALANCED:
|
|
457
|
+
assert per_value is not None
|
|
458
|
+
balanced = _select_balanced(
|
|
459
|
+
source_path, config, fields, per_value, seed, max_strata
|
|
460
|
+
)
|
|
461
|
+
write_sample_csv(output_path, balanced.headers, balanced.rows, source_path, config)
|
|
462
|
+
total = balanced.row_count
|
|
463
|
+
return SampleResult(
|
|
464
|
+
source=source_path,
|
|
465
|
+
output=output_path,
|
|
466
|
+
method=selected_method,
|
|
467
|
+
source_rows=total,
|
|
468
|
+
sample_rows=len(balanced.rows),
|
|
469
|
+
sample_rate=(len(balanced.rows) / total * 100) if total else 0.0,
|
|
470
|
+
field=", ".join(fields),
|
|
471
|
+
seed=seed,
|
|
472
|
+
encoding=config.encoding,
|
|
473
|
+
delimiter=config.delimiter,
|
|
474
|
+
fields=fields,
|
|
475
|
+
strata_known=balanced.strata,
|
|
476
|
+
strata_selected=balanced.strata,
|
|
477
|
+
strata_uncovered=0,
|
|
478
|
+
per_value=per_value,
|
|
479
|
+
strata_capped=balanced.capped,
|
|
480
|
+
)
|
|
355
481
|
scan = _scan(source_path, config, fields, max_strata)
|
|
356
482
|
size = _sample_size(scan.row_count, rows, percent)
|
|
357
483
|
stratified = selected_method is SampleMethod.STRATIFIED
|
|
358
|
-
|
|
484
|
+
if stratified:
|
|
485
|
+
_check_every_stratum_fits(size, len(scan.strata), fields)
|
|
486
|
+
allocations = allocate_strata_with_minimum(scan.strata, size) if stratified else None
|
|
359
487
|
selected = _select_rows(
|
|
360
488
|
source_path, config, scan, selected_method, size, seed, allocations
|
|
361
489
|
)
|
|
@@ -157,6 +157,7 @@ def generate_samples(
|
|
|
157
157
|
config_path: ConfigPath | None = None,
|
|
158
158
|
force: bool = False,
|
|
159
159
|
max_strata: int | None = None,
|
|
160
|
+
per_value: int | None = None,
|
|
160
161
|
confirmed: bool = False,
|
|
161
162
|
html: bool = True,
|
|
162
163
|
on_progress: ProgressCallback | None = None,
|
|
@@ -173,7 +174,12 @@ def generate_samples(
|
|
|
173
174
|
explicit_limit = max_strata is not None
|
|
174
175
|
max_strata = DEFAULT_MAX_STRATA if max_strata is None else max_strata
|
|
175
176
|
selected_method = validate_sample_options(
|
|
176
|
-
method,
|
|
177
|
+
method,
|
|
178
|
+
rows=rows,
|
|
179
|
+
percent=percent,
|
|
180
|
+
field=field,
|
|
181
|
+
max_strata=max_strata,
|
|
182
|
+
per_value=per_value,
|
|
177
183
|
)
|
|
178
184
|
if selected_method is SampleMethod.SMART:
|
|
179
185
|
from tabalyst.smart_service import execute_smart_samples, prepare_smart_samples
|
|
@@ -201,7 +207,7 @@ def generate_samples(
|
|
|
201
207
|
plan = build_sample_plan(
|
|
202
208
|
input_specs, output=output, output_dir=output_dir, force=force
|
|
203
209
|
)
|
|
204
|
-
if selected_method
|
|
210
|
+
if selected_method in {SampleMethod.STRATIFIED, SampleMethod.STRATIFIED_BALANCED} and not explicit_limit:
|
|
205
211
|
_refuse_excessive_stratification(
|
|
206
212
|
[job.source for job in plan.jobs],
|
|
207
213
|
stratification_fields(field),
|
|
@@ -225,6 +231,7 @@ def generate_samples(
|
|
|
225
231
|
encoding=config.csv.encoding,
|
|
226
232
|
force=force,
|
|
227
233
|
max_strata=max_strata,
|
|
234
|
+
per_value=per_value,
|
|
228
235
|
)
|
|
229
236
|
except TabalystError as exc:
|
|
230
237
|
failures.append(SampleFailure(job=job, error=exc))
|
|
@@ -91,13 +91,13 @@ def stratification_lines(advice: Stratification) -> list[str]:
|
|
|
91
91
|
rare = (
|
|
92
92
|
""
|
|
93
93
|
if column.smallest_share is None
|
|
94
|
-
else f", rarest {column.smallest_share:.2%},
|
|
94
|
+
else f", rarest {column.smallest_share:.2%}, one row of each stratum from "
|
|
95
95
|
f"{column.size_for_every_stratum:,} rows"
|
|
96
96
|
)
|
|
97
97
|
absent = (
|
|
98
98
|
""
|
|
99
99
|
if column.uncovered_at_size is None
|
|
100
|
-
else f", {column.uncovered_at_size}
|
|
100
|
+
else f", {column.uncovered_at_size} strata too many for {advice.sample_size:,} rows"
|
|
101
101
|
)
|
|
102
102
|
lines.append(
|
|
103
103
|
f" {column.dataset} / {column.display}: "
|
|
@@ -34,11 +34,7 @@ from tabalyst.scanner.planner.stratmodels import (
|
|
|
34
34
|
Stratification,
|
|
35
35
|
StratificationLimits,
|
|
36
36
|
)
|
|
37
|
-
from tabalyst.scanner.stratify import
|
|
38
|
-
allocate_strata,
|
|
39
|
-
saturating_product,
|
|
40
|
-
size_for_every_stratum,
|
|
41
|
-
)
|
|
37
|
+
from tabalyst.scanner.stratify import saturating_product
|
|
42
38
|
|
|
43
39
|
STRATIFICATION_VERSION = 1
|
|
44
40
|
|
|
@@ -157,10 +153,11 @@ def _column(
|
|
|
157
153
|
counts.append(field.missing.count)
|
|
158
154
|
total = sum(counts)
|
|
159
155
|
smallest, largest = _round(min(counts) / total), _round(max(counts) / total)
|
|
160
|
-
size_all =
|
|
156
|
+
size_all = len(counts)
|
|
161
157
|
if sample_size is not None:
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
# ``stratified`` keeps one row of each stratum or refuses to run,
|
|
159
|
+
# so a size under the strata is the only way to leave some out.
|
|
160
|
+
uncovered = max(0, len(counts) - sample_size)
|
|
164
161
|
if smallest <= RARE_SHARE or uncovered:
|
|
165
162
|
reasons.append("rare_strata")
|
|
166
163
|
else:
|
|
@@ -61,9 +61,9 @@ class ColumnAdvice(ScanModel):
|
|
|
61
61
|
# frequencies (analytical domain, so a little coarser than the original).
|
|
62
62
|
smallest_share: float | None = None
|
|
63
63
|
largest_share: float | None = None
|
|
64
|
-
# The
|
|
64
|
+
# The smallest size of a ``stratified`` sample: one row of each stratum.
|
|
65
65
|
size_for_every_stratum: int | None = None
|
|
66
|
-
# With a sample size
|
|
66
|
+
# With a sample size under the strata: how many it cannot hold (the sample is refused).
|
|
67
67
|
uncovered_at_size: int | None = None
|
|
68
68
|
|
|
69
69
|
@model_serializer(mode="wrap")
|
|
@@ -67,6 +67,46 @@ def allocate_strata(counts: Mapping[K, int], size: int) -> dict[K, int]:
|
|
|
67
67
|
return allocated
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def allocate_strata_with_minimum(counts: Mapping[K, int], size: int) -> dict[K, int]:
|
|
71
|
+
"""Proportional allocation of ``size`` rows with at least one row per stratum.
|
|
72
|
+
|
|
73
|
+
A stratum whose exact quota is under one row is held at one, and the rows
|
|
74
|
+
that costs are taken from the others in proportion: this repeats until no
|
|
75
|
+
free stratum falls under one. The rest is shared by largest remainder (ties:
|
|
76
|
+
the key as text). Only integers are compared, so the result is exact and
|
|
77
|
+
identical on every machine. No stratum gets more rows than it has, and the
|
|
78
|
+
total is ``size``.
|
|
79
|
+
|
|
80
|
+
Raises ``ValueError`` when ``size`` is under the number of strata (the
|
|
81
|
+
minimum cannot be kept) or over the rows counted (nothing to draw).
|
|
82
|
+
"""
|
|
83
|
+
total = sum(counts.values())
|
|
84
|
+
if size < len(counts):
|
|
85
|
+
raise ValueError("the size is under the number of strata")
|
|
86
|
+
if size > total:
|
|
87
|
+
raise ValueError("the size is over the rows counted")
|
|
88
|
+
allocated: dict[K, int] = {}
|
|
89
|
+
free = dict(counts)
|
|
90
|
+
budget = size
|
|
91
|
+
while free:
|
|
92
|
+
weight = sum(free.values())
|
|
93
|
+
held = [value for value, count in free.items() if count * budget < weight]
|
|
94
|
+
if not held:
|
|
95
|
+
break
|
|
96
|
+
for value in held:
|
|
97
|
+
allocated[value] = 1
|
|
98
|
+
budget -= 1
|
|
99
|
+
del free[value]
|
|
100
|
+
weight = sum(free.values())
|
|
101
|
+
remainders: dict[K, int] = {}
|
|
102
|
+
for value, count in free.items():
|
|
103
|
+
allocated[value], remainders[value] = divmod(count * budget, weight)
|
|
104
|
+
left = size - sum(allocated.values())
|
|
105
|
+
for value in sorted(remainders, key=lambda item: (-remainders[item], str(item)))[:left]:
|
|
106
|
+
allocated[value] += 1
|
|
107
|
+
return allocated
|
|
108
|
+
|
|
109
|
+
|
|
70
110
|
def size_for_every_stratum(smallest: int, total: int) -> int:
|
|
71
111
|
"""The smallest sample size at which the exact quota of the rarest stratum
|
|
72
112
|
reaches one row, so that its allocation cannot be zero.
|
|
@@ -525,6 +525,7 @@ tests/test_public_api.py
|
|
|
525
525
|
tests/test_report_contrast.py
|
|
526
526
|
tests/test_reporting.py
|
|
527
527
|
tests/test_sampling.py
|
|
528
|
+
tests/test_sampling_stratified_balanced.py
|
|
528
529
|
tests/test_scan_command.py
|
|
529
530
|
tests/test_scanner_amounts.py
|
|
530
531
|
tests/test_scanner_batches.py
|