solarwindpy 0.0.1.dev0__py3-none-any.whl → 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.
Potentially problematic release.
This version of solarwindpy might be problematic. Click here for more details.
- plans/.velocity/metrics.json +96 -0
- plans/0-overview-template.md +268 -0
- plans/N-phase-template.md +106 -0
- plans/PLAN_AUDIT_SUMMARY.md +173 -0
- plans/TEMPLATE-USAGE-GUIDE.md +198 -0
- plans/__init__.py +1 -0
- plans/abandoned/compaction-agent-system/0-Overview.md +123 -0
- plans/abandoned/compaction-agent-system/agents-index-update-plan.md +109 -0
- plans/abandoned/compaction-agent-system/compacted_state.md +85 -0
- plans/abandoned/compaction-agent-system/implementation-plan.md +107 -0
- plans/abandoned/compaction-agent-system/system-validation-report.md +159 -0
- plans/abandoned/compaction-agent-system/usage-guide.md +210 -0
- plans/abandoned/hook-system-enhancement/0-Overview.md +214 -0
- plans/abandoned/hook-system-enhancement/1-Phase1-Core-Infrastructure.md +313 -0
- plans/abandoned/hook-system-enhancement/2-Phase2-Intelligent-Testing.md +385 -0
- plans/abandoned/hook-system-enhancement/3-Phase3-Physics-Validation.md +444 -0
- plans/abandoned/hook-system-enhancement/4-Phase4-Performance-Monitoring.md +458 -0
- plans/abandoned/hook-system-enhancement/5-Phase5-Developer-Experience.md +532 -0
- plans/abandoned/hook-system-enhancement/6-Implementation-Timeline.md +274 -0
- plans/abandoned/hook-system-enhancement/7-Risk-Management.md +376 -0
- plans/abandoned/hook-system-enhancement/8-Testing-Strategy.md +579 -0
- plans/abandoned/readthedocs-automation/0-Overview.md +247 -0
- plans/abandoned/readthedocs-automation/1-Emergency-Documentation-Fixes.md +270 -0
- plans/abandoned/readthedocs-automation/2-Template-System-Enhancement.md +811 -0
- plans/abandoned/readthedocs-automation/3-Quality-Audit-ReadTheDocs-Integration.md +844 -0
- plans/abandoned/readthedocs-automation/4-Plan-Consolidation-Cleanup.md +632 -0
- plans/abandoned/readthedocs-automation/9-Closeout.md +207 -0
- plans/abandoned/readthedocs-automation/ABANDONMENT_REASON.md +72 -0
- plans/cicd-architecture-redesign/0-Overview.md +193 -0
- plans/cicd-architecture-redesign/1-Workflow-Creation.md +103 -0
- plans/cicd-architecture-redesign/2-Version-Detection.md +123 -0
- plans/cicd-architecture-redesign/3-Deployment-Gates.md +169 -0
- plans/cicd-architecture-redesign/4-RC-Testing.md +194 -0
- plans/cicd-architecture-redesign/5-TestPyPI-Validation.md +264 -0
- plans/cicd-architecture-redesign/6-Production-Release.md +263 -0
- plans/cicd-architecture-redesign/7-Cleanup.md +243 -0
- plans/cicd-architecture-redesign/8-Documentation.md +285 -0
- plans/cicd-architecture-redesign/Closeout.md +225 -0
- plans/closeout-template.md +259 -0
- plans/completed/circular-import-audit/0-Overview.md +152 -0
- plans/completed/circular-import-audit/1-Static-Dependency-Analysis.md +62 -0
- plans/completed/circular-import-audit/2-Dynamic-Import-Testing.md +56 -0
- plans/completed/circular-import-audit/3-Performance-Impact-Assessment.md +56 -0
- plans/completed/circular-import-audit/4-Issue-Remediation.md +78 -0
- plans/completed/circular-import-audit/5-Preventive-Infrastructure.md +89 -0
- plans/completed/claude-settings-ecosystem-alignment/0-Overview.md +162 -0
- plans/completed/claude-settings-ecosystem-alignment/1-Security-Foundation.md +148 -0
- plans/completed/claude-settings-ecosystem-alignment/2-Hook-Integration.md +158 -0
- plans/completed/claude-settings-ecosystem-alignment/3-Agent-System-Integration.md +177 -0
- plans/completed/claude-settings-ecosystem-alignment/4-Enhanced-Workflow-Automation.md +159 -0
- plans/completed/claude-settings-ecosystem-alignment/5-Validation-Monitoring.md +181 -0
- plans/completed/claude-settings-ecosystem-alignment/compacted_session_state.md +290 -0
- plans/completed/combined_plan_with_checklist_documentation/1-Overview-and-Goals.md +51 -0
- plans/completed/combined_plan_with_checklist_documentation/2-Toolchain-and-Hosting.md +69 -0
- plans/completed/combined_plan_with_checklist_documentation/3-Repository-Structure.md +61 -0
- plans/completed/combined_plan_with_checklist_documentation/4-Configuration-and-Standards.md +70 -0
- plans/completed/combined_plan_with_checklist_documentation/5-Documentation-Content.md +62 -0
- plans/completed/combined_plan_with_checklist_documentation/6-CI-CD-and-Validation.md +58 -0
- plans/completed/combined_plan_with_checklist_documentation/7-Maintenance.md +55 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/0-Overview.md +135 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/1-Common-fixtures.md +59 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/10-power_laws.md +56 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/2-core.py-FitFunction.md +118 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/3-gaussians.py-Gaussian-GaussianNormalized-GaussianLn.md +69 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/4-trend_fits.py-TrendFit.md +99 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/5-plots.py-FFPlot.md +98 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/6-tex_info.py-TeXinfo.md +79 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/7-Justification.md +49 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/8-exponentials.md +64 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/9-lines.md +58 -0
- plans/completed/combined_test_plan_with_checklist_plotting/0-Overview.md +142 -0
- plans/completed/combined_test_plan_with_checklist_plotting/1-base.py.md +90 -0
- plans/completed/combined_test_plan_with_checklist_plotting/10-labels-special.py.md +102 -0
- plans/completed/combined_test_plan_with_checklist_plotting/11-labels-chemistry.py.md +212 -0
- plans/completed/combined_test_plan_with_checklist_plotting/12-labels-composition.py.md +242 -0
- plans/completed/combined_test_plan_with_checklist_plotting/13-labels-datetime.py.md +247 -0
- plans/completed/combined_test_plan_with_checklist_plotting/14-labels-elemental_abundance.py.md +274 -0
- plans/completed/combined_test_plan_with_checklist_plotting/15-visual-validation.md +256 -0
- plans/completed/combined_test_plan_with_checklist_plotting/16-integration-testing.md +266 -0
- plans/completed/combined_test_plan_with_checklist_plotting/17-performance-benchmarks.md +267 -0
- plans/completed/combined_test_plan_with_checklist_plotting/18-Fixtures-and-Utilities.md +86 -0
- plans/completed/combined_test_plan_with_checklist_plotting/2-agg_plot.py.md +90 -0
- plans/completed/combined_test_plan_with_checklist_plotting/3-histograms.py.md +201 -0
- plans/completed/combined_test_plan_with_checklist_plotting/4-scatter.py.md +167 -0
- plans/completed/combined_test_plan_with_checklist_plotting/5-spiral.py.md +216 -0
- plans/completed/combined_test_plan_with_checklist_plotting/6-orbits.py.md +108 -0
- plans/completed/combined_test_plan_with_checklist_plotting/7-tools.py.md +86 -0
- plans/completed/combined_test_plan_with_checklist_plotting/8-select_data_from_figure.py.md +97 -0
- plans/completed/combined_test_plan_with_checklist_plotting/9-labels-base.py.md +88 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/.gitkeep +0 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/0-Overview.md +170 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/1-Package-Entry-Point-__init__.py.md +121 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/2-Core-Base-Classes-base.py.md +142 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/3-Plotting-Helpers-plots.py.md +123 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/4-LISIRD-Sub-package.md +119 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/5-Extrema-Calculator.md +103 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/6-Sunspot-Number-Sub-package.md +163 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/7-Sunspot-Number-Init.py.md +217 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/compacted_state.md +52 -0
- plans/completed/compaction-agent-modernization/0-Overview.md +156 -0
- plans/completed/compaction-agent-modernization/1-Architecture-Audit-Gap-Analysis.md +132 -0
- plans/completed/compaction-agent-modernization/2-Token-Baseline-Recalibration.md +153 -0
- plans/completed/compaction-agent-modernization/3-Agent-Reference-Updates.md +184 -0
- plans/completed/compaction-agent-modernization/4-Compression-Algorithm-Modernization.md +238 -0
- plans/completed/compaction-agent-modernization/5-Workflow-Integration-Streamlining.md +252 -0
- plans/completed/compaction-agent-modernization/6-Template-Structure-Optimization.md +240 -0
- plans/completed/compaction-agent-modernization/7-Integration-Testing-Validation.md +292 -0
- plans/completed/compaction-hook-enhancement/0-Overview.md +150 -0
- plans/completed/compaction-hook-enhancement/1-Token-Estimation-Enhancement.md +179 -0
- plans/completed/compaction-hook-enhancement/2-Compression-Intelligence.md +294 -0
- plans/completed/compaction-hook-enhancement/3-Git-Integration-Metadata.md +310 -0
- plans/completed/compaction-hook-enhancement/4-Session-Continuity-Features.md +358 -0
- plans/completed/compaction-hook-enhancement/5-Testing-Strategy.md +404 -0
- plans/completed/compaction-hook-enhancement/6-Integration-Roadmap.md +319 -0
- plans/completed/compaction-hook-enhancement/compacted_state.md +142 -0
- plans/completed/docstring-audit-enhancement/0-Overview.md +274 -0
- plans/completed/docstring-audit-enhancement/1-Infrastructure-Setup-and-Validation-Tools.md +206 -0
- plans/completed/docstring-audit-enhancement/2-Core-Physics-Modules-Enhancement.md +237 -0
- plans/completed/docstring-audit-enhancement/3-Fitfunctions-Mathematical-Modules-Enhancement.md +188 -0
- plans/completed/docstring-audit-enhancement/4-Plotting-Visualization-Modules-Enhancement.md +243 -0
- plans/completed/docstring-audit-enhancement/5-Specialized-Modules-Enhancement.md +216 -0
- plans/completed/docstring-audit-enhancement/6-Validation-and-Integration.md +216 -0
- plans/completed/fitfunctions-testing-implementation/0-Overview.md +130 -0
- plans/completed/fitfunctions-testing-implementation/1-Test-Infrastructure-Setup.md +79 -0
- plans/completed/fitfunctions-testing-implementation/2-Common-Fixtures-Test-Utilities.md +104 -0
- plans/completed/fitfunctions-testing-implementation/3-Core-FitFunction-Testing.md +168 -0
- plans/completed/fitfunctions-testing-implementation/4-Specialized-Function-Classes.md +210 -0
- plans/completed/fitfunctions-testing-implementation/5-Advanced-Classes-Testing.md +214 -0
- plans/completed/fitfunctions-testing-implementation/6-Plotting-Integration-Testing.md +231 -0
- plans/completed/fitfunctions-testing-implementation/7-Extended-Coverage-BONUS.md +184 -0
- plans/completed/numpy-docstring-conversion-plan/numpy-docstring-conversion-plan.md +118 -0
- plans/completed/pr-review-remediation/0-Overview.md +138 -0
- plans/completed/pr-review-remediation/1-Critical-Safety-Improvements.md +179 -0
- plans/completed/pr-review-remediation/2-Smart-Timeouts-Validation.md +399 -0
- plans/completed/pr-review-remediation/3-Enhanced-GitHub-Integration.md +258 -0
- plans/completed/pr-review-remediation/compacted_state.md +66 -0
- plans/completed/python-310-migration/0-Overview.md +390 -0
- plans/completed/python-310-migration/1-Planning-Setup.md +164 -0
- plans/completed/python-310-migration/2-Implementation.md +256 -0
- plans/completed/python-310-migration/3-Testing-Validation.md +335 -0
- plans/completed/python-310-migration/4-Documentation-Release.md +274 -0
- plans/completed/python-310-migration/5-Closeout.md +252 -0
- plans/completed/requirements-management-consolidation/0-Overview.md +118 -0
- plans/completed/requirements-management-consolidation/1-Documentation-Validation-Environment-Setup.md +116 -0
- plans/completed/requirements-management-consolidation/2-Requirements-Consolidation.md +161 -0
- plans/completed/requirements-management-consolidation/3-Workflow-Automation-Final-Integration.md +196 -0
- plans/completed/single-ecosystem-plan-implementation/0-Overview.md +83 -0
- plans/completed/single-ecosystem-plan-implementation/1-Plan-Preservation-Session-Management.md +38 -0
- plans/completed/single-ecosystem-plan-implementation/2-File-Structure-Optimization.md +43 -0
- plans/completed/single-ecosystem-plan-implementation/3-Plan-Migration-Archive-Setup.md +82 -0
- plans/completed/single-ecosystem-plan-implementation/4-Agent-System-Transformation.md +108 -0
- plans/completed/single-ecosystem-plan-implementation/5-Template-System-Enhancement.md +131 -0
- plans/completed/single-ecosystem-plan-implementation/6-Final-Validation-Testing.md +120 -0
- plans/completed/test-directory-consolidation/0-Overview.md +51 -0
- plans/completed/test-directory-consolidation/1-Structure-Preparation.md +82 -0
- plans/completed/test-directory-consolidation/2-File-Migration.md +100 -0
- plans/completed/test-directory-consolidation/3-Import-Transformation.md +117 -0
- plans/completed/test-directory-consolidation/4-Configuration-Consolidation.md +140 -0
- plans/completed/test-directory-consolidation/5-Validation.md +152 -0
- plans/completed/test-directory-consolidation/6-Cleanup.md +156 -0
- plans/completed/test-planning-agents-architecture/0-Overview.md +79 -0
- plans/completed/test-planning-agents-architecture/1-Branch-Isolation-Testing.md +49 -0
- plans/completed/test-planning-agents-architecture/2-Cross-Branch-Coordination.md +51 -0
- plans/completed/test-planning-agents-architecture/3-Merge-Workflow-Testing.md +48 -0
- plans/deployment-semver-pypi-rtd/0-Overview.md +463 -0
- plans/deployment-semver-pypi-rtd/1-Semantic-Versioning-Foundation.md +136 -0
- plans/deployment-semver-pypi-rtd/2-PyPI-Deployment-Infrastructure.md +168 -0
- plans/deployment-semver-pypi-rtd/3-Release-Automation.md +214 -0
- plans/deployment-semver-pypi-rtd/4-Plan-Closeout.md +543 -0
- plans/deployment-semver-pypi-rtd/compacted_session_state.md +172 -0
- plans/deployment-semver-pypi-rtd/compacted_state.md +131 -0
- plans/documentation-code-audit/0-Overview.md +393 -0
- plans/documentation-code-audit/1-Discovery-Inventory.md +183 -0
- plans/documentation-code-audit/2-Execution-Environment-Setup.md +263 -0
- plans/documentation-code-audit/3-Systematic-Validation.md +322 -0
- plans/documentation-code-audit/4-Code-Example-Remediation.md +358 -0
- plans/documentation-code-audit/5-Physics-MultiIndex-Compliance.md +464 -0
- plans/documentation-code-audit/6-Doctest-Integration.md +523 -0
- plans/documentation-code-audit/7-Reporting-Documentation.md +498 -0
- plans/documentation-code-audit/8-Closeout.md +456 -0
- plans/documentation-rebuild-session/compacted_state.md +109 -0
- plans/documentation-rendering-fixes/0-Overview.md +104 -0
- plans/documentation-rendering-fixes/1-Sphinx-Build-Diagnostics-Warning-Audit.md +101 -0
- plans/documentation-rendering-fixes/2-Configuration-Infrastructure-Fixes.md +113 -0
- plans/documentation-rendering-fixes/3-Docstring-Syntax-Audit-Repair.md +131 -0
- plans/documentation-rendering-fixes/4-HTML-Page-Rendering-Verification.md +113 -0
- plans/documentation-rendering-fixes/5-Advanced-Documentation-Quality-Assurance.md +119 -0
- plans/documentation-rendering-fixes/6-Documentation-Build-Optimization-Testing.md +129 -0
- plans/documentation-rendering-fixes/compacted_state.md +132 -0
- plans/documentation-template-fix/0-Overview.md +197 -0
- plans/documentation-template-fix/1-Template-System-Analysis.md +269 -0
- plans/documentation-template-fix/2-Template-Modification.md +609 -0
- plans/documentation-template-fix/3-Build-System-Integration.md +766 -0
- plans/documentation-template-fix/4-Testing-Validation.md +1399 -0
- plans/documentation-template-fix/5-Documentation-Training.md +602 -0
- plans/documentation-workflow-fix/0-Overview.md +222 -0
- plans/documentation-workflow-fix/1-Immediate-Fixes.md +238 -0
- plans/documentation-workflow-fix/2-Configuration-Setup.md +298 -0
- plans/documentation-workflow-fix/3-Pre-commit-Integration.md +382 -0
- plans/documentation-workflow-fix/4-Workflow-Improvements.md +446 -0
- plans/documentation-workflow-fix/5-Documentation-and-Training.md +527 -0
- plans/duplicate-object-warnings-fix-plan.md +130 -0
- plans/github-issues-migration/0-Overview.md +510 -0
- plans/github-issues-migration/1-Foundation-Label-System.md +180 -0
- plans/github-issues-migration/2-Migration-Tool-Rewrite.md +235 -0
- plans/github-issues-migration/3-CLI-Integration-Automation.md +169 -0
- plans/github-issues-migration/4-Validated-Migration.md +252 -0
- plans/github-issues-migration/5-Documentation-Training.md +171 -0
- plans/github-issues-migration/6-Closeout.md +179 -0
- plans/github-workflows-repair/repair-plan.md +299 -0
- plans/issues_from_plans.py +342 -0
- plans/pr-270-doc-validation-fixes/0-Overview.md +354 -0
- plans/pr-270-doc-validation-fixes/1-Critical-PR-Fixes.md +117 -0
- plans/pr-270-doc-validation-fixes/2-Framework-Right-Sizing.md +129 -0
- plans/pr-270-doc-validation-fixes/3-Sustainable-Documentation.md +126 -0
- plans/pr-270-doc-validation-fixes/4-Closeout-Migration.md +143 -0
- plans/pr-270-doc-validation-fixes/PLAN_COMPLETED.md +149 -0
- plans/python-310-migration/0-Overview.md +390 -0
- plans/python-310-migration/1-Planning-Setup.md +164 -0
- plans/python-310-migration/2-Implementation.md +256 -0
- plans/python-310-migration/3-Testing-Validation.md +335 -0
- plans/python-310-migration/4-Documentation-Release.md +274 -0
- plans/python-310-migration/5-Closeout.md +252 -0
- plans/readthedocs-simplified/0-Overview.md +243 -0
- plans/readthedocs-simplified/1-Immediate-Fixes.md +216 -0
- plans/readthedocs-simplified/2-Template-Simplification.md +278 -0
- plans/readthedocs-simplified/3-ReadTheDocs-Setup.md +298 -0
- plans/readthedocs-simplified/4-Testing-Validation.md +328 -0
- plans/readthedocs-simplified/5-Closeout.md +231 -0
- plans/readthedocs-simplified/compacted_state.md +127 -0
- plans/session-compaction-2025-08-12/compacted_state.md +114 -0
- plans/session-compaction-2025-08-13/compacted_state.md +145 -0
- plans/session-continuity-protocol/0-Overview.md +35 -0
- plans/session-continuity-protocol/1-Core-Principles-Framework.md +40 -0
- plans/session-continuity-protocol/2-Pre-Session-Validation-System.md +79 -0
- plans/session-continuity-protocol/3-Context-Switching-Prevention.md +87 -0
- plans/session-continuity-protocol/4-Progress-Tracking-Recovery.md +100 -0
- plans/sphinx-warnings-analysis.md +222 -0
- plans/systemprompt-optimization/0-Overview.md +447 -0
- plans/systemprompt-optimization/1-Deploy-SystemPrompt.md +114 -0
- plans/systemprompt-optimization/2-Documentation-Alignment.md +198 -0
- plans/systemprompt-optimization/3-Monitoring-Infrastructure.md +396 -0
- plans/systemprompt-optimization/4-Implementation-Script.md +450 -0
- plans/systemprompt-optimization/9-Closeout.md +165 -0
- plans/systemprompt-optimization/compacted_state.md +143 -0
- plans/template-value-propositions/0-Overview.md +357 -0
- plans/template-value-propositions/1-Value-Proposition-Framework-Design.md +144 -0
- plans/template-value-propositions/2-Plan-Template-Enhancement.md +178 -0
- plans/template-value-propositions/3-Value-Generator-Hook-Implementation.md +291 -0
- plans/template-value-propositions/4-Value-Validator-Hook-Implementation.md +274 -0
- plans/template-value-propositions/5-Documentation-Agent-Updates.md +219 -0
- plans/template-value-propositions/6-Integration-Testing-Validation.md +247 -0
- plans/tests-audit/0-Overview.md +410 -0
- plans/tests-audit/1-Discovery-Inventory.md +170 -0
- plans/tests-audit/2-Physics-Validation-Audit.md +195 -0
- plans/tests-audit/3-Architecture-Compliance.md +195 -0
- plans/tests-audit/4-Numerical-Stability-Analysis.md +203 -0
- plans/tests-audit/5-Documentation-Enhancement.md +220 -0
- plans/tests-audit/6-Audit-Deliverables.md +220 -0
- plans/tests-audit/7-Closeout.md +252 -0
- plans/tests-audit/artifacts/ARCHITECTURE_COMPLIANCE_REPORT.md +315 -0
- plans/tests-audit/artifacts/ARCHITECTURE_RECOMMENDATIONS.md +943 -0
- plans/tests-audit/artifacts/COMPREHENSIVE_AUDIT_REPORT.md +356 -0
- plans/tests-audit/artifacts/CONTRIBUTING_ENHANCED_TEMPLATE.md +419 -0
- plans/tests-audit/artifacts/COVERAGE_GAP_ANALYSIS.md +152 -0
- plans/tests-audit/artifacts/DOCUMENTATION_ENHANCEMENT_REPORT.md +502 -0
- plans/tests-audit/artifacts/EXECUTIVE_AUDIT_SUMMARY.md +129 -0
- plans/tests-audit/artifacts/IMPLEMENTATION_ROADMAP.md +647 -0
- plans/tests-audit/artifacts/NUMERICAL_RECOMMENDATIONS.md +739 -0
- plans/tests-audit/artifacts/NUMERICAL_STABILITY_GUIDE_TEMPLATE.rst +451 -0
- plans/tests-audit/artifacts/NUMERICAL_STABILITY_REPORT.md +301 -0
- plans/tests-audit/artifacts/PHASE_3_SUMMARY.md +280 -0
- plans/tests-audit/artifacts/PHASE_4_SUMMARY.md +229 -0
- plans/tests-audit/artifacts/PHASE_5_SUMMARY.md +292 -0
- plans/tests-audit/artifacts/PHASE_6_CLOSEOUT.md +278 -0
- plans/tests-audit/artifacts/PHYSICS_GUIDE_TEMPLATE.rst +268 -0
- plans/tests-audit/artifacts/PHYSICS_VALIDATION_REPORT.md +235 -0
- plans/tests-audit/artifacts/TECHNICAL_DELIVERABLES_PACKAGE.md +2502 -0
- plans/tests-audit/artifacts/TEST_INVENTORY.csv +1204 -0
- plans/tests-audit/artifacts/TEST_INVENTORY.md +135 -0
- plans/tests-audit/artifacts/test_discovery_analysis.py +231 -0
- plans/tests-audit/artifacts/test_parser.py +395 -0
- solarwindpy/README.md +3 -0
- solarwindpy/Untitled.ipynb +54 -0
- solarwindpy/__init__.py +74 -0
- solarwindpy/core/__init__.py +23 -0
- solarwindpy/core/alfvenic_turbulence.py +804 -0
- solarwindpy/core/base.py +267 -0
- solarwindpy/core/ions.py +309 -0
- solarwindpy/core/plasma.py +2133 -0
- solarwindpy/core/spacecraft.py +256 -0
- solarwindpy/core/tensor.py +90 -0
- solarwindpy/core/units_constants.py +199 -0
- solarwindpy/core/vector.py +328 -0
- solarwindpy/fitfunctions/__init__.py +20 -0
- solarwindpy/fitfunctions/core.py +734 -0
- solarwindpy/fitfunctions/exponentials.py +188 -0
- solarwindpy/fitfunctions/gaussians.py +264 -0
- solarwindpy/fitfunctions/lines.py +116 -0
- solarwindpy/fitfunctions/moyal.py +71 -0
- solarwindpy/fitfunctions/plots.py +751 -0
- solarwindpy/fitfunctions/power_laws.py +209 -0
- solarwindpy/fitfunctions/tex_info.py +568 -0
- solarwindpy/fitfunctions/trend_fits.py +482 -0
- solarwindpy/instabilities/__init__.py +16 -0
- solarwindpy/instabilities/beta_ani.py +82 -0
- solarwindpy/instabilities/verscharen2016.py +631 -0
- solarwindpy/plotting/__init__.py +33 -0
- solarwindpy/plotting/agg_plot.py +489 -0
- solarwindpy/plotting/base.py +465 -0
- solarwindpy/plotting/hist1d.py +405 -0
- solarwindpy/plotting/hist2d.py +1035 -0
- solarwindpy/plotting/histograms.py +1845 -0
- solarwindpy/plotting/labels/__init__.py +104 -0
- solarwindpy/plotting/labels/base.py +686 -0
- solarwindpy/plotting/labels/chemistry.py +19 -0
- solarwindpy/plotting/labels/composition.py +100 -0
- solarwindpy/plotting/labels/datetime.py +235 -0
- solarwindpy/plotting/labels/elemental_abundance.py +73 -0
- solarwindpy/plotting/labels/special.py +794 -0
- solarwindpy/plotting/orbits.py +515 -0
- solarwindpy/plotting/scatter.py +99 -0
- solarwindpy/plotting/select_data_from_figure.py +329 -0
- solarwindpy/plotting/spiral.py +980 -0
- solarwindpy/plotting/tools.py +434 -0
- solarwindpy/scripts/__init__.py +1 -0
- solarwindpy/scripts/logs/.gitignore +1 -0
- solarwindpy/solar_activity/__init__.py +53 -0
- solarwindpy/solar_activity/base.py +605 -0
- solarwindpy/solar_activity/lisird/__init__.py +3 -0
- solarwindpy/solar_activity/lisird/extrema_calculator.py +394 -0
- solarwindpy/solar_activity/lisird/lisird.py +319 -0
- solarwindpy/solar_activity/plots.py +116 -0
- solarwindpy/solar_activity/sunspot_number/.DS_Store +0 -0
- solarwindpy/solar_activity/sunspot_number/__init__.py +3 -0
- solarwindpy/solar_activity/sunspot_number/sidc.py +556 -0
- solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv +72 -0
- solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv.silso +72 -0
- solarwindpy/tools/__init__.py +162 -0
- solarwindpy-0.1.0.dist-info/METADATA +181 -0
- solarwindpy-0.1.0.dist-info/RECORD +409 -0
- {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.0.dist-info}/WHEEL +1 -1
- solarwindpy-0.1.0.dist-info/licenses/LICENSE.rst +32 -0
- solarwindpy-0.1.0.dist-info/top_level.txt +3 -0
- tests/__init__.py +1 -0
- tests/conftest.py +10 -0
- tests/core/__init__.py +1 -0
- tests/core/test_alfvenic_turbulence.py +544 -0
- tests/core/test_base.py +112 -0
- tests/core/test_base_head_tail.py +29 -0
- tests/core/test_base_mi_tuples.py +11 -0
- tests/core/test_core_verify_datetimeindex.py +32 -0
- tests/core/test_ions.py +325 -0
- tests/core/test_plasma.py +2581 -0
- tests/core/test_plasma_io.py +12 -0
- tests/core/test_quantities.py +507 -0
- tests/core/test_spacecraft.py +210 -0
- tests/core/test_units_constants.py +22 -0
- tests/data/epoch.csv +4 -0
- tests/data/plasma.csv +4 -0
- tests/data/spacecraft.csv +4 -0
- tests/fitfunctions/conftest.py +60 -0
- tests/fitfunctions/test_core.py +193 -0
- tests/fitfunctions/test_exponentials.py +342 -0
- tests/fitfunctions/test_gaussians.py +142 -0
- tests/fitfunctions/test_lines.py +349 -0
- tests/fitfunctions/test_moyal.py +258 -0
- tests/fitfunctions/test_plots.py +258 -0
- tests/fitfunctions/test_power_laws.py +365 -0
- tests/fitfunctions/test_tex_info.py +183 -0
- tests/fitfunctions/test_trend_fit_properties.py +31 -0
- tests/fitfunctions/test_trend_fits.py +244 -0
- tests/plotting/__init__.py +1 -0
- tests/plotting/labels/__init__.py +1 -0
- tests/plotting/labels/test_chemistry.py +243 -0
- tests/plotting/labels/test_composition.py +345 -0
- tests/plotting/labels/test_datetime.py +445 -0
- tests/plotting/labels/test_elemental_abundance.py +366 -0
- tests/plotting/labels/test_init.py +66 -0
- tests/plotting/labels/test_labels_base.py +347 -0
- tests/plotting/labels/test_special.py +550 -0
- tests/plotting/test_agg_plot.py +602 -0
- tests/plotting/test_base.py +752 -0
- tests/plotting/test_fixtures_utilities.py +775 -0
- tests/plotting/test_histograms.py +546 -0
- tests/plotting/test_integration.py +675 -0
- tests/plotting/test_orbits.py +435 -0
- tests/plotting/test_performance.py +708 -0
- tests/plotting/test_scatter.py +752 -0
- tests/plotting/test_select_data_from_figure.py +1209 -0
- tests/plotting/test_spiral.py +573 -0
- tests/plotting/test_tools.py +607 -0
- tests/plotting/test_visual_validation.py +465 -0
- tests/solar_activity/__init__.py +1 -0
- tests/solar_activity/lisird/__init__.py +1 -0
- tests/solar_activity/lisird/test_extrema_calculator.py +593 -0
- tests/solar_activity/lisird/test_lisird_id.py +187 -0
- tests/solar_activity/sunspot_number/__init__.py +1 -0
- tests/solar_activity/sunspot_number/test_init.py +399 -0
- tests/solar_activity/sunspot_number/test_sidc.py +465 -0
- tests/solar_activity/sunspot_number/test_sidc_id.py +223 -0
- tests/solar_activity/sunspot_number/test_sidc_loader.py +275 -0
- tests/solar_activity/sunspot_number/test_ssn_extrema.py +406 -0
- tests/solar_activity/test_base.py +656 -0
- tests/solar_activity/test_init.py +396 -0
- tests/solar_activity/test_plots.py +371 -0
- tests/test_circular_imports.py +408 -0
- tests/test_issue_titles.py +25 -0
- tests/test_statusline.py +298 -0
- solarwindpy-0.0.1.dev0.dist-info/METADATA +0 -14
- solarwindpy-0.0.1.dev0.dist-info/RECORD +0 -4
- solarwindpy-0.0.1.dev0.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,1035 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
r"""Two-dimensional histogram and heatmap plotting utilities."""
|
|
3
|
+
|
|
4
|
+
import pdb # noqa: F401
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import matplotlib as mpl
|
|
9
|
+
|
|
10
|
+
from matplotlib import pyplot as plt
|
|
11
|
+
from collections import namedtuple
|
|
12
|
+
from scipy.signal import savgol_filter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from . import base
|
|
16
|
+
from . import labels as labels_module
|
|
17
|
+
|
|
18
|
+
# from .agg_plot import AggPlot
|
|
19
|
+
# from .hist1d import Hist1D
|
|
20
|
+
|
|
21
|
+
from . import agg_plot
|
|
22
|
+
from . import hist1d
|
|
23
|
+
|
|
24
|
+
AggPlot = agg_plot.AggPlot
|
|
25
|
+
Hist1D = hist1d.Hist1D
|
|
26
|
+
|
|
27
|
+
# import os
|
|
28
|
+
# import psutil
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# def log_mem_usage():
|
|
32
|
+
# usage = psutil.Process(os.getpid()).memory_info()
|
|
33
|
+
# usage = "\n".join(
|
|
34
|
+
# ["{} {:.3f} GB".format(k, v * 1e-9) for k, v in usage._asdict().items()]
|
|
35
|
+
# )
|
|
36
|
+
# logging.getLogger("main").warning("Memory usage\n%s", usage)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# class Hist2D(base.Plot2D, AggPlot):
|
|
40
|
+
class Hist2D(base.PlotWithZdata, base.CbarMaker, AggPlot):
|
|
41
|
+
r"""Create a 2D histogram with an optional z-value using an equal number.
|
|
42
|
+
|
|
43
|
+
of bins along the x and y axis.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
x, y: pd.Series
|
|
48
|
+
x and y data to aggregate
|
|
49
|
+
z: None, pd.Series
|
|
50
|
+
If not None, the z-value to aggregate.
|
|
51
|
+
axnorm: str
|
|
52
|
+
Normalize the histogram.
|
|
53
|
+
key normalization
|
|
54
|
+
--- -------------
|
|
55
|
+
c column
|
|
56
|
+
r row
|
|
57
|
+
t total
|
|
58
|
+
d density
|
|
59
|
+
logx, logy: bool
|
|
60
|
+
If True, log10 scale the axis.
|
|
61
|
+
|
|
62
|
+
Attributes
|
|
63
|
+
----------
|
|
64
|
+
data:
|
|
65
|
+
bins:
|
|
66
|
+
cut:
|
|
67
|
+
axnorm:
|
|
68
|
+
log<x,y>:
|
|
69
|
+
<x,y,z>label:
|
|
70
|
+
path: None, Path
|
|
71
|
+
|
|
72
|
+
Methods
|
|
73
|
+
-------
|
|
74
|
+
calc_bins:
|
|
75
|
+
calculate the x, y bins.
|
|
76
|
+
make_cut:
|
|
77
|
+
Utilize the calculated bins to convert (x, y) into pd.Categoral
|
|
78
|
+
or pd.Interval values used in aggregation.
|
|
79
|
+
set_[x,y,z]label:
|
|
80
|
+
Set the x, y, or z label.
|
|
81
|
+
agg:
|
|
82
|
+
Aggregate the data in the bins.
|
|
83
|
+
If z-value is None, count the number of points in each bin.
|
|
84
|
+
If z-value is not None, calculate the mean for each bin.
|
|
85
|
+
make_plot:
|
|
86
|
+
Make a 2D plot of the data with an optional color bar.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
x,
|
|
92
|
+
y,
|
|
93
|
+
z=None,
|
|
94
|
+
axnorm=None,
|
|
95
|
+
logx=False,
|
|
96
|
+
logy=False,
|
|
97
|
+
clip_data=False,
|
|
98
|
+
nbins=101,
|
|
99
|
+
bin_precision=None,
|
|
100
|
+
):
|
|
101
|
+
super().__init__()
|
|
102
|
+
self.set_log(x=logx, y=logy)
|
|
103
|
+
self.set_data(x, y, z, clip_data)
|
|
104
|
+
self.set_labels(
|
|
105
|
+
x="x", y="y", z=labels_module.Count(norm=axnorm) if z is None else "z"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
self.set_axnorm(axnorm)
|
|
109
|
+
self.calc_bins_intervals(nbins=nbins, precision=bin_precision)
|
|
110
|
+
self.make_cut()
|
|
111
|
+
self.set_clim(None, None)
|
|
112
|
+
self.set_alim(None, None)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def _gb_axes(self):
|
|
116
|
+
return ("x", "y")
|
|
117
|
+
|
|
118
|
+
def _maybe_convert_to_log_scale(self, x, y):
|
|
119
|
+
if self.log.x:
|
|
120
|
+
x = 10.0**x
|
|
121
|
+
if self.log.y:
|
|
122
|
+
y = 10.0**y
|
|
123
|
+
|
|
124
|
+
return x, y
|
|
125
|
+
|
|
126
|
+
# def set_path(self, new, add_scale=True):
|
|
127
|
+
# # Bug: path doesn't auto-set log information.
|
|
128
|
+
# path, x, y, z, scale_info = super().set_path(new, add_scale)
|
|
129
|
+
|
|
130
|
+
# if new == "auto":
|
|
131
|
+
# path = path / x / y / z
|
|
132
|
+
|
|
133
|
+
# else:
|
|
134
|
+
# assert x is None
|
|
135
|
+
# assert y is None
|
|
136
|
+
# assert z is None
|
|
137
|
+
|
|
138
|
+
# if add_scale:
|
|
139
|
+
# assert scale_info is not None
|
|
140
|
+
|
|
141
|
+
# scale_info = "-".join(scale_info)
|
|
142
|
+
|
|
143
|
+
# if bool(len(path.parts)) and path.parts[-1].endswith("norm"):
|
|
144
|
+
# # Insert <norm> at end of path so scale order is (x, y, z).
|
|
145
|
+
# path = path.parts
|
|
146
|
+
# path = path[:-1] + (scale_info + "-" + path[-1],)
|
|
147
|
+
# path = Path(*path)
|
|
148
|
+
# else:
|
|
149
|
+
# path = path / scale_info
|
|
150
|
+
|
|
151
|
+
# self._path = path
|
|
152
|
+
|
|
153
|
+
# set_path.__doc__ = base.Base.set_path.__doc__
|
|
154
|
+
|
|
155
|
+
def set_labels(self, **kwargs):
|
|
156
|
+
|
|
157
|
+
z = kwargs.pop("z", self.labels.z)
|
|
158
|
+
if isinstance(z, labels_module.Count):
|
|
159
|
+
try:
|
|
160
|
+
z.set_axnorm(self.axnorm)
|
|
161
|
+
except AttributeError:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
z.build_label()
|
|
165
|
+
|
|
166
|
+
super().set_labels(z=z, **kwargs)
|
|
167
|
+
|
|
168
|
+
# def set_data(self, x, y, z, clip):
|
|
169
|
+
# data = pd.DataFrame(
|
|
170
|
+
# {
|
|
171
|
+
# "x": np.log10(np.abs(x)) if self.log.x else x,
|
|
172
|
+
# "y": np.log10(np.abs(y)) if self.log.y else y,
|
|
173
|
+
# }
|
|
174
|
+
# )
|
|
175
|
+
#
|
|
176
|
+
#
|
|
177
|
+
# if z is None:
|
|
178
|
+
# z = pd.Series(1, index=x.index)
|
|
179
|
+
#
|
|
180
|
+
# data.loc[:, "z"] = z
|
|
181
|
+
# data = data.dropna()
|
|
182
|
+
# if not data.shape[0]:
|
|
183
|
+
# raise ValueError(
|
|
184
|
+
# "You can't build a %s with data that is exclusively NaNs"
|
|
185
|
+
# % self.__class__.__name__
|
|
186
|
+
# )
|
|
187
|
+
#
|
|
188
|
+
# self._data = data
|
|
189
|
+
# self._clip = clip
|
|
190
|
+
|
|
191
|
+
def set_data(self, x, y, z, clip):
|
|
192
|
+
super().set_data(x, y, z, clip)
|
|
193
|
+
data = self.data
|
|
194
|
+
if self.log.x:
|
|
195
|
+
data.loc[:, "x"] = np.log10(np.abs(data.loc[:, "x"]))
|
|
196
|
+
if self.log.y:
|
|
197
|
+
data.loc[:, "y"] = np.log10(np.abs(data.loc[:, "y"]))
|
|
198
|
+
self._data = data
|
|
199
|
+
|
|
200
|
+
def set_axnorm(self, new):
|
|
201
|
+
r"""The method by which the gridded data is normalized.
|
|
202
|
+
|
|
203
|
+
===== =============================================================
|
|
204
|
+
key description
|
|
205
|
+
===== =============================================================
|
|
206
|
+
c Column normalize
|
|
207
|
+
d Density normalize
|
|
208
|
+
r Row normalize
|
|
209
|
+
t Total normalize
|
|
210
|
+
cd PDFs in each column
|
|
211
|
+
rd PDFs in each row
|
|
212
|
+
===== ============================================================="""
|
|
213
|
+
if new is not None:
|
|
214
|
+
new = new.lower()
|
|
215
|
+
assert new in (
|
|
216
|
+
"c",
|
|
217
|
+
"r",
|
|
218
|
+
"t",
|
|
219
|
+
"d",
|
|
220
|
+
"cd",
|
|
221
|
+
"rd",
|
|
222
|
+
), f"Unrecgonized axnorm `{new}`"
|
|
223
|
+
|
|
224
|
+
zlbl = self.labels.z
|
|
225
|
+
if isinstance(zlbl, labels_module.Count):
|
|
226
|
+
zlbl.set_axnorm(new)
|
|
227
|
+
zlbl.build_label()
|
|
228
|
+
|
|
229
|
+
self._axnorm = new
|
|
230
|
+
|
|
231
|
+
def _axis_normalizer(self, agg):
|
|
232
|
+
r"""Takes care of row, column, total, and density normaliation.
|
|
233
|
+
|
|
234
|
+
Written basically as `staticmethod` so that can be called in `OrbitHist2D`, but
|
|
235
|
+
as actual method with `self` passed so we have access to `self.log` for density
|
|
236
|
+
normalization.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
axnorm = self.axnorm
|
|
240
|
+
if axnorm is None:
|
|
241
|
+
pass
|
|
242
|
+
elif axnorm == "c":
|
|
243
|
+
agg = agg.divide(agg.max(level="x"), level="x")
|
|
244
|
+
elif axnorm == "r":
|
|
245
|
+
agg = agg.divide(agg.max(level="y"), level="y")
|
|
246
|
+
elif axnorm == "t":
|
|
247
|
+
agg = agg.divide(agg.max())
|
|
248
|
+
elif axnorm == "d":
|
|
249
|
+
N = agg.sum().sum()
|
|
250
|
+
x = pd.IntervalIndex(agg.index.get_level_values("x").unique())
|
|
251
|
+
y = pd.IntervalIndex(agg.index.get_level_values("y").unique())
|
|
252
|
+
dx = pd.Series(
|
|
253
|
+
x.length, index=x
|
|
254
|
+
) # dx = pd.Series(x.right - x.left, index=x)
|
|
255
|
+
dy = pd.Series(
|
|
256
|
+
y.length, index=y
|
|
257
|
+
) # dy = pd.Series(y.right - y.left, index=y)
|
|
258
|
+
|
|
259
|
+
if self.log.x:
|
|
260
|
+
dx = 10.0**dx
|
|
261
|
+
if self.log.y:
|
|
262
|
+
dy = 10.0**dy
|
|
263
|
+
|
|
264
|
+
agg = agg.divide(dx, level="x").divide(dy, level="y").divide(N)
|
|
265
|
+
|
|
266
|
+
elif axnorm == "cd":
|
|
267
|
+
# raise NotImplementedError("Need to verify data alignment, especially `dx` values and index")
|
|
268
|
+
N = agg.sum(level="x")
|
|
269
|
+
dy = pd.IntervalIndex(
|
|
270
|
+
agg.index.get_level_values("y").unique()
|
|
271
|
+
).sort_values()
|
|
272
|
+
dy = pd.Series(dy.length, index=dy).sort_index()
|
|
273
|
+
# Divide by total in each column and each row's width
|
|
274
|
+
agg = agg.divide(N, level="x").divide(dy, level="y")
|
|
275
|
+
|
|
276
|
+
elif axnorm == "rd":
|
|
277
|
+
# raise NotImplementedError("Need to verify data alignment, especially `dx` values and index")
|
|
278
|
+
N = agg.sum(level="y")
|
|
279
|
+
dx = pd.IntervalIndex(
|
|
280
|
+
agg.index.get_level_values("x").unique()
|
|
281
|
+
).sort_values()
|
|
282
|
+
dx = pd.Series(dx.length, index=dx).sort_index()
|
|
283
|
+
# Divide by total in each column and each row's width
|
|
284
|
+
agg = agg.divide(N, level="y").divide(dx, level="x")
|
|
285
|
+
|
|
286
|
+
elif hasattr(axnorm, "__iter__"):
|
|
287
|
+
kind, fcn = axnorm
|
|
288
|
+
if kind == "c":
|
|
289
|
+
agg = agg.divide(agg.agg(fcn, level="x"), level="x")
|
|
290
|
+
elif kind == "r":
|
|
291
|
+
agg = agg.divide(agg.agg(fcn, level="y"), level="y")
|
|
292
|
+
else:
|
|
293
|
+
raise ValueError(f"Unrecognized axnorm with function ({kind}, {fcn})")
|
|
294
|
+
else:
|
|
295
|
+
raise ValueError(f"Unrecognized axnorm ({axnorm})")
|
|
296
|
+
|
|
297
|
+
return agg
|
|
298
|
+
|
|
299
|
+
def agg(self, **kwargs):
|
|
300
|
+
agg = super().agg(**kwargs)
|
|
301
|
+
agg = self._axis_normalizer(agg)
|
|
302
|
+
agg = self._agg_reindexer(agg)
|
|
303
|
+
|
|
304
|
+
a0, a1 = self.alim
|
|
305
|
+
if a0 is not None or a1 is not None:
|
|
306
|
+
tk = pd.Series(True, index=agg.index)
|
|
307
|
+
# tk = pd.DataFrame(True,
|
|
308
|
+
# index=agg.index,
|
|
309
|
+
# columns=agg.columns
|
|
310
|
+
# )
|
|
311
|
+
if a0 is not None:
|
|
312
|
+
tk = tk & (agg >= a0)
|
|
313
|
+
if a1 is not None:
|
|
314
|
+
tk = tk & (agg <= a1)
|
|
315
|
+
|
|
316
|
+
agg = agg.where(tk)
|
|
317
|
+
|
|
318
|
+
return agg
|
|
319
|
+
|
|
320
|
+
def _make_cbar(self, mappable, **kwargs):
|
|
321
|
+
ticks = kwargs.pop(
|
|
322
|
+
"ticks",
|
|
323
|
+
mpl.ticker.MultipleLocator(0.1) if self.axnorm in ("c", "r") else None,
|
|
324
|
+
)
|
|
325
|
+
return super()._make_cbar(mappable, ticks=ticks, **kwargs)
|
|
326
|
+
|
|
327
|
+
def _limit_color_norm(self, norm):
|
|
328
|
+
if self.axnorm in ("c", "r"):
|
|
329
|
+
# Don't limit us to (1%, 99%) interval.
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
pct = self.data.loc[:, "z"].quantile([0.01, 0.99])
|
|
333
|
+
v0 = pct.loc[0.01]
|
|
334
|
+
v1 = pct.loc[0.99]
|
|
335
|
+
if norm.vmin is None:
|
|
336
|
+
norm.vmin = v0
|
|
337
|
+
if norm.vmax is None:
|
|
338
|
+
norm.vmax = v1
|
|
339
|
+
norm.clip = True
|
|
340
|
+
|
|
341
|
+
def make_plot(
|
|
342
|
+
self,
|
|
343
|
+
ax=None,
|
|
344
|
+
cbar=True,
|
|
345
|
+
limit_color_norm=False,
|
|
346
|
+
cbar_kwargs=None,
|
|
347
|
+
fcn=None,
|
|
348
|
+
alpha_fcn=None,
|
|
349
|
+
**kwargs,
|
|
350
|
+
):
|
|
351
|
+
r"""Make a 2D plot on `ax` using `ax.pcolormesh`.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
ax: mpl.axes.Axes, None
|
|
356
|
+
If None, create an `Axes` instance from `plt.subplots`.
|
|
357
|
+
cbar: bool
|
|
358
|
+
If True, create color bar with `labels.z`.
|
|
359
|
+
limit_color_norm: bool
|
|
360
|
+
If True, limit the color range to 0.001 and 0.999 percentile range
|
|
361
|
+
of the z-value, count or otherwise.
|
|
362
|
+
cbar_kwargs: dict, None
|
|
363
|
+
If not None, kwargs passed to `self._make_cbar`.
|
|
364
|
+
fcn: FunctionType, None
|
|
365
|
+
Aggregation function. If None, automatically select in :py:meth:`agg`.
|
|
366
|
+
alpha_fcn: None, str
|
|
367
|
+
If not None, the function used to aggregate the data for setting alpha
|
|
368
|
+
value.
|
|
369
|
+
kwargs:
|
|
370
|
+
Passed to `ax.pcolormesh`.
|
|
371
|
+
If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
ax: mpl.axes.Axes
|
|
376
|
+
Axes upon which plot was made.
|
|
377
|
+
cbar_or_mappable: colorbar.Colorbar, mpl.collections.QuadMesh
|
|
378
|
+
If `cbar` is True, return the colorbar. Otherwise, return the `Quadmesh` used
|
|
379
|
+
to create the colorbar.
|
|
380
|
+
"""
|
|
381
|
+
agg = self.agg(fcn=fcn).unstack("x")
|
|
382
|
+
x = self.edges["x"]
|
|
383
|
+
y = self.edges["y"]
|
|
384
|
+
|
|
385
|
+
# assert x.size == agg.shape[1] + 1
|
|
386
|
+
# assert y.size == agg.shape[0] + 1
|
|
387
|
+
|
|
388
|
+
# HACK: Works around `gb.agg(observed=False)` pandas bug. (GH32381)
|
|
389
|
+
if x.size != agg.shape[1] + 1:
|
|
390
|
+
# agg = agg.reindex(columns=self.intervals["x"])
|
|
391
|
+
agg = agg.reindex(columns=self.categoricals["x"])
|
|
392
|
+
if y.size != agg.shape[0] + 1:
|
|
393
|
+
# agg = agg.reindex(index=self.intervals["y"])
|
|
394
|
+
agg = agg.reindex(index=self.categoricals["y"])
|
|
395
|
+
|
|
396
|
+
if ax is None:
|
|
397
|
+
fig, ax = plt.subplots()
|
|
398
|
+
|
|
399
|
+
# if self.log.x:
|
|
400
|
+
# x = 10.0 ** x
|
|
401
|
+
# if self.log.y:
|
|
402
|
+
# y = 10.0 ** y
|
|
403
|
+
x, y = self._maybe_convert_to_log_scale(x, y)
|
|
404
|
+
|
|
405
|
+
axnorm = self.axnorm
|
|
406
|
+
default_norm = None
|
|
407
|
+
if axnorm in ("c", "r"):
|
|
408
|
+
default_norm = mpl.colors.BoundaryNorm(
|
|
409
|
+
np.linspace(0, 1, 11), 256, clip=True
|
|
410
|
+
)
|
|
411
|
+
elif axnorm in ("d", "cd", "rd"):
|
|
412
|
+
default_norm = mpl.colors.LogNorm(clip=True)
|
|
413
|
+
norm = kwargs.pop("norm", default_norm)
|
|
414
|
+
|
|
415
|
+
if limit_color_norm:
|
|
416
|
+
self._limit_color_norm(norm)
|
|
417
|
+
|
|
418
|
+
C = np.ma.masked_invalid(agg.values)
|
|
419
|
+
XX, YY = np.meshgrid(x, y)
|
|
420
|
+
pc = ax.pcolormesh(XX, YY, C, norm=norm, **kwargs)
|
|
421
|
+
|
|
422
|
+
cbar_or_mappable = pc
|
|
423
|
+
if cbar:
|
|
424
|
+
if cbar_kwargs is None:
|
|
425
|
+
cbar_kwargs = dict()
|
|
426
|
+
|
|
427
|
+
if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
428
|
+
cbar_kwargs["ax"] = ax
|
|
429
|
+
|
|
430
|
+
# Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
|
|
431
|
+
cbar = self._make_cbar(pc, **cbar_kwargs)
|
|
432
|
+
cbar_or_mappable = cbar
|
|
433
|
+
|
|
434
|
+
self._format_axis(ax)
|
|
435
|
+
|
|
436
|
+
color_plot = self.data.loc[:, self.agg_axes].dropna().unique().size > 1
|
|
437
|
+
if (alpha_fcn is not None) and color_plot:
|
|
438
|
+
self.logger.warning(
|
|
439
|
+
"Make sure you verify alpha actually set. I don't yet trust this."
|
|
440
|
+
)
|
|
441
|
+
alpha_agg = self.agg(fcn=alpha_fcn)
|
|
442
|
+
alpha_agg = alpha_agg.unstack("x")
|
|
443
|
+
alpha_agg = np.ma.masked_invalid(alpha_agg.values.ravel())
|
|
444
|
+
# Feature scale then invert so smallest STD
|
|
445
|
+
# is most opaque.
|
|
446
|
+
alpha = 1 - mpl.colors.Normalize()(alpha_agg)
|
|
447
|
+
self.logger.warning("Scaling alpha filter as alpha**0.25")
|
|
448
|
+
alpha = alpha**0.25
|
|
449
|
+
|
|
450
|
+
# Set masked values to zero. Otherwise, masked
|
|
451
|
+
# values are rendered as black.
|
|
452
|
+
alpha = alpha.filled(0)
|
|
453
|
+
# Must draw to initialize `facecolor`s
|
|
454
|
+
plt.draw()
|
|
455
|
+
# Remove `pc` from axis so we can redraw with std
|
|
456
|
+
# pc.remove()
|
|
457
|
+
colors = pc.get_facecolors()
|
|
458
|
+
colors[:, 3] = alpha
|
|
459
|
+
pc.set_facecolor(colors)
|
|
460
|
+
# ax.add_collection(pc)
|
|
461
|
+
|
|
462
|
+
elif alpha_fcn is not None:
|
|
463
|
+
self.logger.warning("Ignoring `alpha_fcn` because plotting counts")
|
|
464
|
+
|
|
465
|
+
return ax, cbar_or_mappable
|
|
466
|
+
|
|
467
|
+
def get_border(self):
|
|
468
|
+
r"""Get the top and bottom edges of the plot.
|
|
469
|
+
|
|
470
|
+
Returns
|
|
471
|
+
-------
|
|
472
|
+
border: namedtuple
|
|
473
|
+
Contains "top" and "bottom" fields, each with a :py:class:`pd.Series`.
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
Border = namedtuple("Border", "top,bottom")
|
|
477
|
+
top = {}
|
|
478
|
+
bottom = {}
|
|
479
|
+
for x, v in self.agg().unstack("x").items():
|
|
480
|
+
yt = v.last_valid_index()
|
|
481
|
+
if yt is not None:
|
|
482
|
+
z = v.loc[yt]
|
|
483
|
+
top[(yt, x)] = z
|
|
484
|
+
|
|
485
|
+
yb = v.first_valid_index()
|
|
486
|
+
if yb is not None:
|
|
487
|
+
z = v.loc[yb]
|
|
488
|
+
bottom[(yb, x)] = z
|
|
489
|
+
|
|
490
|
+
top = pd.Series(top)
|
|
491
|
+
bottom = pd.Series(bottom)
|
|
492
|
+
for edge in (top, bottom):
|
|
493
|
+
edge.index.names = ["y", "x"]
|
|
494
|
+
|
|
495
|
+
border = Border(top, bottom)
|
|
496
|
+
return border
|
|
497
|
+
|
|
498
|
+
def _plot_one_edge(
|
|
499
|
+
self,
|
|
500
|
+
ax,
|
|
501
|
+
edge,
|
|
502
|
+
smooth=False,
|
|
503
|
+
sg_kwargs=None,
|
|
504
|
+
xlim=(None, None),
|
|
505
|
+
ylim=(None, None),
|
|
506
|
+
**kwargs,
|
|
507
|
+
):
|
|
508
|
+
x = edge.index.get_level_values("x").mid
|
|
509
|
+
y = edge.index.get_level_values("y").mid
|
|
510
|
+
|
|
511
|
+
if sg_kwargs is None:
|
|
512
|
+
sg_kwargs = dict()
|
|
513
|
+
|
|
514
|
+
if smooth:
|
|
515
|
+
wlength = sg_kwargs.pop("window_length", int(np.floor(y.shape[0] / 10)))
|
|
516
|
+
polyorder = sg_kwargs.pop("polyorder", 3)
|
|
517
|
+
|
|
518
|
+
if not wlength % 2:
|
|
519
|
+
wlength -= 1
|
|
520
|
+
|
|
521
|
+
y = savgol_filter(y, wlength, polyorder, **sg_kwargs)
|
|
522
|
+
|
|
523
|
+
if self.log.x:
|
|
524
|
+
x = 10.0**x
|
|
525
|
+
if self.log.y:
|
|
526
|
+
y = 10.0**y
|
|
527
|
+
|
|
528
|
+
x0, x1 = xlim
|
|
529
|
+
y0, y1 = ylim
|
|
530
|
+
|
|
531
|
+
tk = np.full_like(x, True, dtype=bool)
|
|
532
|
+
if x0 is not None:
|
|
533
|
+
tk = tk & (x0 <= x)
|
|
534
|
+
if x1 is not None:
|
|
535
|
+
tk = tk & (x <= x1)
|
|
536
|
+
if y0 is not None:
|
|
537
|
+
tk = tk & (y0 <= y)
|
|
538
|
+
if y1 is not None:
|
|
539
|
+
tk = tk & (y <= y1)
|
|
540
|
+
|
|
541
|
+
# if (~tk).any():
|
|
542
|
+
x = x[tk]
|
|
543
|
+
y = y[tk]
|
|
544
|
+
|
|
545
|
+
return ax.plot(x, y, **kwargs)
|
|
546
|
+
|
|
547
|
+
def plot_edges(self, ax, smooth=True, sg_kwargs=None, **kwargs):
|
|
548
|
+
"""Overplot the edges.
|
|
549
|
+
|
|
550
|
+
Parameters
|
|
551
|
+
----------
|
|
552
|
+
ax:
|
|
553
|
+
Axis on which to plot.
|
|
554
|
+
smooth: bool
|
|
555
|
+
If True, apply a Savitzky-Golay filter (:py:func:`scipy.signal.savgol_filter`)
|
|
556
|
+
to the y-values before plotting to smooth the curve.
|
|
557
|
+
sg_kwargs: dict, None
|
|
558
|
+
If not None, dict of kwargs passed to Savitzky-Golay filter. Also allows
|
|
559
|
+
for setting of `window_length` and `polyorder` as kwargs. They default to
|
|
560
|
+
10% of the number of observations (`window_length`) and 3 (`polyorder`).
|
|
561
|
+
Note that because `window_length` must be odd, if the 10% value is even, we
|
|
562
|
+
take 1-window_length.
|
|
563
|
+
kwargs:
|
|
564
|
+
Passed to `ax.plot`
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
top, bottom = self.get_border()
|
|
568
|
+
|
|
569
|
+
color = kwargs.pop("color", "cyan")
|
|
570
|
+
label = kwargs.pop("label", None)
|
|
571
|
+
etop = self._plot_one_edge(
|
|
572
|
+
ax, top, smooth, sg_kwargs, color=color, label=label, **kwargs
|
|
573
|
+
)
|
|
574
|
+
ebottom = self._plot_one_edge(
|
|
575
|
+
ax, bottom, smooth, sg_kwargs, color=color, **kwargs
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
return etop, ebottom
|
|
579
|
+
|
|
580
|
+
def _get_contour_levels(self, levels):
|
|
581
|
+
if (levels is not None) or (self.axnorm is None):
|
|
582
|
+
pass
|
|
583
|
+
|
|
584
|
+
elif (levels is None) and (self.axnorm == "t"):
|
|
585
|
+
levels = [0.01, 0.1, 0.3, 0.7, 0.99]
|
|
586
|
+
|
|
587
|
+
elif (levels is None) and (self.axnorm == "d"):
|
|
588
|
+
levels = [3e-5, 1e-4, 3e-4, 1e-3, 1.7e-3, 2.3e-3]
|
|
589
|
+
|
|
590
|
+
elif (levels is None) and (self.axnorm in ["r", "c"]):
|
|
591
|
+
levels = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
|
592
|
+
|
|
593
|
+
elif (levels is None) and (self.axnorm in ["cd", "rd"]):
|
|
594
|
+
levels = None
|
|
595
|
+
|
|
596
|
+
else:
|
|
597
|
+
raise ValueError(
|
|
598
|
+
f"Unrecognized axis normalization {self.axnorm} for default levels."
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
return levels
|
|
602
|
+
|
|
603
|
+
def _verify_contour_passthrough_kwargs(
|
|
604
|
+
self, ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
605
|
+
):
|
|
606
|
+
if clabel_kwargs is None:
|
|
607
|
+
clabel_kwargs = dict()
|
|
608
|
+
if edges_kwargs is None:
|
|
609
|
+
edges_kwargs = dict()
|
|
610
|
+
if cbar_kwargs is None:
|
|
611
|
+
cbar_kwargs = dict()
|
|
612
|
+
if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
613
|
+
cbar_kwargs["ax"] = ax
|
|
614
|
+
|
|
615
|
+
return clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
616
|
+
|
|
617
|
+
def plot_contours(
|
|
618
|
+
self,
|
|
619
|
+
ax=None,
|
|
620
|
+
label_levels=True,
|
|
621
|
+
cbar=True,
|
|
622
|
+
limit_color_norm=False,
|
|
623
|
+
cbar_kwargs=None,
|
|
624
|
+
fcn=None,
|
|
625
|
+
plot_edges=False,
|
|
626
|
+
edges_kwargs=None,
|
|
627
|
+
clabel_kwargs=None,
|
|
628
|
+
skip_max_clbl=True,
|
|
629
|
+
use_contourf=False,
|
|
630
|
+
gaussian_filter_std=0,
|
|
631
|
+
gaussian_filter_kwargs=None,
|
|
632
|
+
**kwargs,
|
|
633
|
+
):
|
|
634
|
+
"""Make a contour plot on `ax` using `ax.contour`.
|
|
635
|
+
|
|
636
|
+
Parameters
|
|
637
|
+
----------
|
|
638
|
+
ax: mpl.axes.Axes, None
|
|
639
|
+
If None, create an `Axes` instance from `plt.subplots`.
|
|
640
|
+
label_levels: bool
|
|
641
|
+
If True, add labels to contours with `ax.clabel`.
|
|
642
|
+
cbar: bool
|
|
643
|
+
If True, create color bar with `labels.z`.
|
|
644
|
+
limit_color_norm: bool
|
|
645
|
+
If True, limit the color range to 0.001 and 0.999 percentile range
|
|
646
|
+
of the z-value, count or otherwise.
|
|
647
|
+
cbar_kwargs: dict, None
|
|
648
|
+
If not None, kwargs passed to `self._make_cbar`.
|
|
649
|
+
fcn: FunctionType, None
|
|
650
|
+
Aggregation function. If None, automatically select in :py:meth:`agg`.
|
|
651
|
+
plot_edges: bool
|
|
652
|
+
If True, plot the smoothed, extreme edges of the 2D histogram.
|
|
653
|
+
edges_kwargs: None, dict
|
|
654
|
+
Passed to {self.plot_edges!s}.
|
|
655
|
+
clabel_kwargs: None, dict
|
|
656
|
+
If not None, dictionary of kwargs passed to `ax.clabel`.
|
|
657
|
+
skip_max_clbl: bool
|
|
658
|
+
If True, don't label the maximum contour. Primarily used when the maximum
|
|
659
|
+
contour is, effectively, a point.
|
|
660
|
+
maximum_color:
|
|
661
|
+
The color for the maximum of the PDF.
|
|
662
|
+
use_contourf: bool
|
|
663
|
+
If True, use `ax.contourf`. Else use `ax.contour`.
|
|
664
|
+
gaussian_filter_std: int
|
|
665
|
+
If > 0, apply `scipy.ndimage.gaussian_filter` to the z-values using the
|
|
666
|
+
standard deviation specified by `gaussian_filter_std`.
|
|
667
|
+
gaussian_filter_kwargs: None, dict
|
|
668
|
+
If not None and gaussian_filter_std > 0, passed to :py:meth:`scipy.ndimage.gaussian_filter`
|
|
669
|
+
kwargs:
|
|
670
|
+
Passed to :py:meth:`ax.pcolormesh`.
|
|
671
|
+
If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
|
|
672
|
+
"""
|
|
673
|
+
levels = kwargs.pop("levels", None)
|
|
674
|
+
cmap = kwargs.pop("cmap", None)
|
|
675
|
+
norm = kwargs.pop(
|
|
676
|
+
"norm",
|
|
677
|
+
(
|
|
678
|
+
mpl.colors.BoundaryNorm(np.linspace(0, 1, 11), 256, clip=True)
|
|
679
|
+
if self.axnorm in ("c", "r")
|
|
680
|
+
else None
|
|
681
|
+
),
|
|
682
|
+
)
|
|
683
|
+
linestyles = kwargs.pop(
|
|
684
|
+
"linestyles",
|
|
685
|
+
[
|
|
686
|
+
"-",
|
|
687
|
+
":",
|
|
688
|
+
"--",
|
|
689
|
+
(0, (7, 3, 1, 3, 1, 3, 1, 3, 1, 3)),
|
|
690
|
+
"--",
|
|
691
|
+
":",
|
|
692
|
+
"-",
|
|
693
|
+
(0, (7, 3, 1, 3, 1, 3)),
|
|
694
|
+
],
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
if ax is None:
|
|
698
|
+
fig, ax = plt.subplots()
|
|
699
|
+
|
|
700
|
+
(
|
|
701
|
+
clabel_kwargs,
|
|
702
|
+
edges_kwargs,
|
|
703
|
+
cbar_kwargs,
|
|
704
|
+
) = self._verify_contour_passthrough_kwargs(
|
|
705
|
+
ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
inline = clabel_kwargs.pop("inline", True)
|
|
709
|
+
inline_spacing = clabel_kwargs.pop("inline_spacing", -3)
|
|
710
|
+
fmt = clabel_kwargs.pop("fmt", "%s")
|
|
711
|
+
|
|
712
|
+
agg = self.agg(fcn=fcn).unstack("x")
|
|
713
|
+
x = self.intervals["x"].mid
|
|
714
|
+
y = self.intervals["y"].mid
|
|
715
|
+
|
|
716
|
+
# assert x.size == agg.shape[1]
|
|
717
|
+
# assert y.size == agg.shape[0]
|
|
718
|
+
|
|
719
|
+
# HACK: Works around `gb.agg(observed=False)` pandas bug. (GH32381)
|
|
720
|
+
if x.size != agg.shape[1]:
|
|
721
|
+
# agg = agg.reindex(columns=self.intervals["x"])
|
|
722
|
+
agg = agg.reindex(columns=self.categoricals["x"])
|
|
723
|
+
if y.size != agg.shape[0]:
|
|
724
|
+
# agg = agg.reindex(index=self.intervals["y"])
|
|
725
|
+
agg = agg.reindex(index=self.categoricals["y"])
|
|
726
|
+
|
|
727
|
+
x, y = self._maybe_convert_to_log_scale(x, y)
|
|
728
|
+
|
|
729
|
+
XX, YY = np.meshgrid(x, y)
|
|
730
|
+
|
|
731
|
+
C = agg.values
|
|
732
|
+
if gaussian_filter_std:
|
|
733
|
+
from scipy.ndimage import gaussian_filter
|
|
734
|
+
|
|
735
|
+
if gaussian_filter_kwargs is None:
|
|
736
|
+
gaussian_filter_kwargs = dict()
|
|
737
|
+
|
|
738
|
+
C = gaussian_filter(C, gaussian_filter_std, **gaussian_filter_kwargs)
|
|
739
|
+
|
|
740
|
+
C = np.ma.masked_invalid(C)
|
|
741
|
+
|
|
742
|
+
assert XX.shape == C.shape
|
|
743
|
+
assert YY.shape == C.shape
|
|
744
|
+
|
|
745
|
+
class nf(float):
|
|
746
|
+
# Source: https://matplotlib.org/3.1.0/gallery/images_contours_and_fields/contour_label_demo.html
|
|
747
|
+
# Define a class that forces representation of float to look a certain way
|
|
748
|
+
# This remove trailing zero so '1.0' becomes '1'
|
|
749
|
+
def __repr__(self):
|
|
750
|
+
return str(self).rstrip("0")
|
|
751
|
+
|
|
752
|
+
levels = self._get_contour_levels(levels)
|
|
753
|
+
|
|
754
|
+
if (norm is None) and (levels is not None):
|
|
755
|
+
norm = mpl.colors.BoundaryNorm(levels, 256, clip=True)
|
|
756
|
+
|
|
757
|
+
contour_fcn = ax.contour
|
|
758
|
+
if use_contourf:
|
|
759
|
+
contour_fcn = ax.contourf
|
|
760
|
+
|
|
761
|
+
if levels is None:
|
|
762
|
+
args = [XX, YY, C]
|
|
763
|
+
else:
|
|
764
|
+
args = [XX, YY, C, levels]
|
|
765
|
+
|
|
766
|
+
qset = contour_fcn(*args, linestyles=linestyles, cmap=cmap, norm=norm, **kwargs)
|
|
767
|
+
|
|
768
|
+
try:
|
|
769
|
+
args = (qset, levels[:-1] if skip_max_clbl else levels)
|
|
770
|
+
except TypeError:
|
|
771
|
+
# None can't be subscripted.
|
|
772
|
+
args = (qset,)
|
|
773
|
+
|
|
774
|
+
lbls = None
|
|
775
|
+
if label_levels:
|
|
776
|
+
qset.levels = [nf(level) for level in qset.levels]
|
|
777
|
+
lbls = ax.clabel(
|
|
778
|
+
*args,
|
|
779
|
+
inline=inline,
|
|
780
|
+
inline_spacing=inline_spacing,
|
|
781
|
+
fmt=fmt,
|
|
782
|
+
**clabel_kwargs,
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
if plot_edges:
|
|
786
|
+
etop, ebottom = self.plot_edges(ax, **edges_kwargs)
|
|
787
|
+
|
|
788
|
+
cbar_or_mappable = qset
|
|
789
|
+
if cbar:
|
|
790
|
+
# Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
|
|
791
|
+
cbar = self._make_cbar(qset, norm=norm, **cbar_kwargs)
|
|
792
|
+
cbar_or_mappable = cbar
|
|
793
|
+
|
|
794
|
+
self._format_axis(ax)
|
|
795
|
+
|
|
796
|
+
return ax, lbls, cbar_or_mappable, qset
|
|
797
|
+
|
|
798
|
+
def project_1d(self, axis, only_plotted=True, project_counts=False, **kwargs):
|
|
799
|
+
"""Make a `Hist1D` from the data stored in this `His2D`.
|
|
800
|
+
|
|
801
|
+
Parameters
|
|
802
|
+
----------
|
|
803
|
+
axis: str
|
|
804
|
+
"x" or "y", specifying the axis to project into 1D.
|
|
805
|
+
only_plotted: bool
|
|
806
|
+
If True, only pass data that appears in the {self.__class__.__name__} plot
|
|
807
|
+
to the :py:class:`Hist1D`.
|
|
808
|
+
project_counts: bool
|
|
809
|
+
If True, only send the variable plotted along `axis` to :py:class:`Hist1D`.
|
|
810
|
+
Otherwise, send both axes (but not z-values).
|
|
811
|
+
kwargs:
|
|
812
|
+
Passed to `Hist1D`. Primarily to allow specifying `bin_precision`.
|
|
813
|
+
|
|
814
|
+
Returns
|
|
815
|
+
-------
|
|
816
|
+
h1: :py:class:`Hist1D`
|
|
817
|
+
"""
|
|
818
|
+
axis = axis.lower()
|
|
819
|
+
assert axis in ("x", "y")
|
|
820
|
+
|
|
821
|
+
data = self.data
|
|
822
|
+
|
|
823
|
+
if data.loc[:, "z"].unique().size >= 2:
|
|
824
|
+
# Either all 1 or 1 and NaN.
|
|
825
|
+
other = "z"
|
|
826
|
+
else:
|
|
827
|
+
possible_axes = {"x", "y"}
|
|
828
|
+
possible_axes.remove(axis)
|
|
829
|
+
other = possible_axes.pop()
|
|
830
|
+
|
|
831
|
+
logx = self.log._asdict()[axis]
|
|
832
|
+
x = self.data.loc[:, axis]
|
|
833
|
+
if logx:
|
|
834
|
+
# Need to convert back to regular from log-space for data setting.
|
|
835
|
+
x = 10.0**x
|
|
836
|
+
|
|
837
|
+
y = self.data.loc[:, other] if not project_counts else None
|
|
838
|
+
logy = False # Defined b/c project_counts option.
|
|
839
|
+
if y is not None and (other == "y"):
|
|
840
|
+
# Only select y-values plotted.
|
|
841
|
+
logy = self.log._asdict()[other]
|
|
842
|
+
yedges = self.edges[other].values
|
|
843
|
+
y = y.where((yedges[0] <= y) & (y <= yedges[-1]))
|
|
844
|
+
if logy:
|
|
845
|
+
y = 10.0**y
|
|
846
|
+
|
|
847
|
+
if only_plotted:
|
|
848
|
+
tk = self.get_plotted_data_boolean_series()
|
|
849
|
+
x = x.loc[tk]
|
|
850
|
+
if y is not None:
|
|
851
|
+
y = y.loc[tk]
|
|
852
|
+
|
|
853
|
+
h1 = Hist1D(
|
|
854
|
+
x,
|
|
855
|
+
y=y,
|
|
856
|
+
logx=logx,
|
|
857
|
+
clip_data=False, # Any clipping will be addressed by bins.
|
|
858
|
+
nbins=self.edges[axis].values,
|
|
859
|
+
**kwargs,
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
h1.set_log(y=logy) # Need to propagate logy.
|
|
863
|
+
h1.set_labels(x=self.labels._asdict()[axis])
|
|
864
|
+
if not project_counts:
|
|
865
|
+
h1.set_labels(y=self.labels._asdict()[other])
|
|
866
|
+
|
|
867
|
+
return h1
|
|
868
|
+
|
|
869
|
+
def make_joint_h2_h1_plot(
|
|
870
|
+
self, project_counts=True, kwargs_1d=None, fig_axes=None, **kwargs
|
|
871
|
+
):
|
|
872
|
+
figsize = kwargs.pop("figsize", (5, 6))
|
|
873
|
+
height_ratios = kwargs.pop("height_ratios", [0.25, 1, 0.2, 0.1])
|
|
874
|
+
width_ratios = kwargs.pop("width_ratios", [1, 0.25])
|
|
875
|
+
hspace = kwargs.pop("hspace", 0)
|
|
876
|
+
wspace = kwargs.pop("wspace", 0)
|
|
877
|
+
|
|
878
|
+
# if fig_axes is not None:
|
|
879
|
+
# fig, axes = fig_axes
|
|
880
|
+
# hax, xax, yax, cax = axes
|
|
881
|
+
# else:
|
|
882
|
+
fig = plt.figure(figsize=figsize)
|
|
883
|
+
gs = mpl.gridspec.GridSpec(
|
|
884
|
+
4,
|
|
885
|
+
2,
|
|
886
|
+
height_ratios=height_ratios,
|
|
887
|
+
width_ratios=width_ratios,
|
|
888
|
+
hspace=hspace,
|
|
889
|
+
wspace=wspace,
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
hax = fig.add_subplot(gs[1, 0])
|
|
893
|
+
xax = fig.add_subplot(gs[0, 0], sharex=hax)
|
|
894
|
+
yax = fig.add_subplot(gs[1, 1], sharey=hax)
|
|
895
|
+
cax = fig.add_subplot(gs[3, 0])
|
|
896
|
+
|
|
897
|
+
cbar_kwargs = kwargs.pop("cbar_kwargs", dict())
|
|
898
|
+
cax = cbar_kwargs.pop("cax", cax)
|
|
899
|
+
orientation = cbar_kwargs.pop("orientation", "horizontal")
|
|
900
|
+
_, cbar = self.make_plot(
|
|
901
|
+
ax=hax,
|
|
902
|
+
cbar_kwargs=dict(cax=cax, orientation=orientation, **cbar_kwargs),
|
|
903
|
+
**kwargs,
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
if kwargs_1d is None:
|
|
907
|
+
kwargs_1d = dict()
|
|
908
|
+
|
|
909
|
+
self.project_1d("x", project_counts=project_counts).make_plot(
|
|
910
|
+
ax=xax, **kwargs_1d
|
|
911
|
+
)
|
|
912
|
+
self.project_1d("y", project_counts=project_counts).make_plot(
|
|
913
|
+
ax=yax, **kwargs_1d, transpose_axes=True
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
xax.label_outer()
|
|
917
|
+
# Mimic `ax.label_outer` for `yax`.
|
|
918
|
+
for label in yax.get_yticklabels(which="both"):
|
|
919
|
+
label.set_visible(False)
|
|
920
|
+
yax.get_yaxis().get_offset_text().set_visible(False)
|
|
921
|
+
yax.set_ylabel("")
|
|
922
|
+
|
|
923
|
+
log = self.log
|
|
924
|
+
if not log.x:
|
|
925
|
+
hax.xaxis.set_major_locator(
|
|
926
|
+
mpl.ticker.MaxNLocator(
|
|
927
|
+
nbins=hax.xaxis.get_ticklocs().size - 1, prune="upper"
|
|
928
|
+
)
|
|
929
|
+
)
|
|
930
|
+
if not log.y:
|
|
931
|
+
hax.yaxis.set_major_locator(
|
|
932
|
+
mpl.ticker.MaxNLocator(
|
|
933
|
+
nbins=hax.yaxis.get_ticklocs().size - 1, prune="upper"
|
|
934
|
+
)
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
return hax, xax, yax, cbar
|
|
938
|
+
|
|
939
|
+
def id_data_above_contour(self, level):
|
|
940
|
+
r"""Gets data above the `level`.
|
|
941
|
+
|
|
942
|
+
Parameters
|
|
943
|
+
----------
|
|
944
|
+
level: scalar
|
|
945
|
+
The z-value above which to select data. Data is aggregated according
|
|
946
|
+
to `ax_norm`.
|
|
947
|
+
|
|
948
|
+
Returns
|
|
949
|
+
-------
|
|
950
|
+
above_contour: pd.Series
|
|
951
|
+
For data in a bin above `level`, indicates the x-`pd.Interval` within
|
|
952
|
+
which the observation falls. `NaN` are observations that are below
|
|
953
|
+
`level`. This object is purposely the same length as the data stored by
|
|
954
|
+
Hist2D and can be used in groupby operations.
|
|
955
|
+
"""
|
|
956
|
+
x = self.data.x
|
|
957
|
+
y = self.data.y
|
|
958
|
+
above_contour = pd.Series(np.nan, self.data.index)
|
|
959
|
+
for k, v in self.agg().unstack("x").items():
|
|
960
|
+
tk = v >= level
|
|
961
|
+
left, right = k.left, k.right
|
|
962
|
+
bottom, top = v[tk].index.min().left, v[tk].index.max().right
|
|
963
|
+
above_contour_at_x = (left < x) & (x <= right) & (bottom < y) & (y <= top)
|
|
964
|
+
above_contour[above_contour_at_x] = k
|
|
965
|
+
|
|
966
|
+
above_contour = pd.Series(
|
|
967
|
+
pd.Categorical(above_contour), index=above_contour.index
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
return above_contour
|
|
971
|
+
|
|
972
|
+
def take_data_in_yrange_across_x(
|
|
973
|
+
self,
|
|
974
|
+
ranges_by_x,
|
|
975
|
+
get_x_bounds,
|
|
976
|
+
get_y_bounds,
|
|
977
|
+
):
|
|
978
|
+
r"""Take data within y-ranges across x-values.
|
|
979
|
+
|
|
980
|
+
Parameters
|
|
981
|
+
----------
|
|
982
|
+
ranges_by_x: iterable
|
|
983
|
+
An iterable with keys used to get the left and right bounds for the data
|
|
984
|
+
and values used to get the top and bottom bounds for the data.
|
|
985
|
+
|
|
986
|
+
get_x_bounds: function
|
|
987
|
+
First argument is one key of `ranges_by_x` and returns `left, right`.
|
|
988
|
+
Second argument is a kwarg (`expected_logx`) boolean to transform the returned values according
|
|
989
|
+
to whether or not the keys are :math:`log(x)` or :math:`x` in a manner
|
|
990
|
+
that matches data stored in Hist2D.
|
|
991
|
+
|
|
992
|
+
get_y_bounds: functions
|
|
993
|
+
Takes on value of `ranges_by_x` and returns `top, bottom`. Second argument
|
|
994
|
+
Second argument is a kwarg (`expected_logx`) boolean to transform the returned values according
|
|
995
|
+
to whether or not the keys are :math:`log(y)` or :math:`y` in a manner
|
|
996
|
+
that matches data stored in Hist2D.
|
|
997
|
+
|
|
998
|
+
Returns
|
|
999
|
+
-------
|
|
1000
|
+
taken: np.ndarray 1D
|
|
1001
|
+
Array of indices for selecting data in interval.
|
|
1002
|
+
"""
|
|
1003
|
+
|
|
1004
|
+
available_x = self.agg().unstack("x").columns
|
|
1005
|
+
if ranges_by_x.index.symmetric_difference(available_x).size:
|
|
1006
|
+
drop = ranges_by_x.index.symmetric_difference(available_x)
|
|
1007
|
+
if not drop.isin(available_x).all():
|
|
1008
|
+
raise ValueError(
|
|
1009
|
+
"Need a way to drop values in selector that aren't available."
|
|
1010
|
+
)
|
|
1011
|
+
else:
|
|
1012
|
+
self.logger.warning(
|
|
1013
|
+
f"Dropping {drop.size} intervals from available for selecting."
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
data = self.data
|
|
1017
|
+
logx = self.log.x
|
|
1018
|
+
logy = self.log.y
|
|
1019
|
+
|
|
1020
|
+
taken = []
|
|
1021
|
+
for x, at_x in ranges_by_x.iterrows():
|
|
1022
|
+
l, r = get_x_bounds(x, expected_logx=logx)
|
|
1023
|
+
b, t = get_y_bounds(at_x, expected_logy=logy)
|
|
1024
|
+
|
|
1025
|
+
assert l < r
|
|
1026
|
+
assert b < t
|
|
1027
|
+
|
|
1028
|
+
tkx = (l < data.x) & (data.x <= r)
|
|
1029
|
+
tky = (b < data.y) & (data.y <= t)
|
|
1030
|
+
tk = tkx & tky
|
|
1031
|
+
tk = tk.loc[tk].index
|
|
1032
|
+
taken.append(tk)
|
|
1033
|
+
|
|
1034
|
+
taken = np.sort(np.concatenate(taken))
|
|
1035
|
+
return taken
|