solarwindpy 0.0.1.dev0__py3-none-any.whl → 0.1.1__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.1.dist-info/METADATA +181 -0
- solarwindpy-0.1.1.dist-info/RECORD +409 -0
- {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.1.dist-info}/WHEEL +1 -1
- solarwindpy-0.1.1.dist-info/licenses/LICENSE.rst +32 -0
- solarwindpy-0.1.1.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,1845 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
r"""Convenience accessors for histogram style plotters.
|
|
3
|
+
|
|
4
|
+
This module re-exports :class:`AggPlot`, :class:`Hist1D`, and
|
|
5
|
+
:class:`Hist2D` so they can be imported directly from
|
|
6
|
+
:mod:`solarwindpy.plotting.histograms`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import agg_plot
|
|
10
|
+
from . import hist1d
|
|
11
|
+
from . import hist2d
|
|
12
|
+
|
|
13
|
+
AggPlot = agg_plot.AggPlot
|
|
14
|
+
Hist1D = hist1d.Hist1D
|
|
15
|
+
Hist2D = hist2d.Hist2D
|
|
16
|
+
|
|
17
|
+
# import pdb # noqa: F401
|
|
18
|
+
# import logging
|
|
19
|
+
|
|
20
|
+
# import numpy as np
|
|
21
|
+
# import pandas as pd
|
|
22
|
+
# import matplotlib as mpl
|
|
23
|
+
|
|
24
|
+
# from types import FunctionType
|
|
25
|
+
# from numbers import Number
|
|
26
|
+
# from matplotlib import pyplot as plt
|
|
27
|
+
# from abc import abstractproperty, abstractmethod
|
|
28
|
+
# from collections import namedtuple
|
|
29
|
+
# from scipy.signal import savgol_filter
|
|
30
|
+
|
|
31
|
+
# try:
|
|
32
|
+
# from astropy.stats import knuth_bin_width
|
|
33
|
+
# except ModuleNotFoundError:
|
|
34
|
+
# pass
|
|
35
|
+
|
|
36
|
+
# from . import tools
|
|
37
|
+
# from . import base
|
|
38
|
+
# from . import labels as labels_module
|
|
39
|
+
|
|
40
|
+
# # import os
|
|
41
|
+
# # import psutil
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# # def log_mem_usage():
|
|
45
|
+
# # usage = psutil.Process(os.getpid()).memory_info()
|
|
46
|
+
# # usage = "\n".join(
|
|
47
|
+
# # ["{} {:.3f} GB".format(k, v * 1e-9) for k, v in usage._asdict().items()]
|
|
48
|
+
# # )
|
|
49
|
+
# # logging.getLogger("main").warning("Memory usage\n%s", usage)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# class AggPlot(base.Base):
|
|
53
|
+
# r"""ABC for aggregating data in 1D and 2D.
|
|
54
|
+
|
|
55
|
+
# Properties
|
|
56
|
+
# ----------
|
|
57
|
+
# logger, data, bins, clip, cut, logx, labels.x, labels.y, clim, agg_axes
|
|
58
|
+
|
|
59
|
+
# Methods
|
|
60
|
+
# -------
|
|
61
|
+
# set_<>:
|
|
62
|
+
# Set property <>.
|
|
63
|
+
|
|
64
|
+
# calc_bins, make_cut, agg, clip_data, make_plot
|
|
65
|
+
|
|
66
|
+
# Abstract Properties
|
|
67
|
+
# -------------------
|
|
68
|
+
# path, _gb_axes
|
|
69
|
+
|
|
70
|
+
# Abstract Methods
|
|
71
|
+
# ----------------
|
|
72
|
+
# __init__, set_labels.y, set_path, set_data, _format_axis, make_plot
|
|
73
|
+
# """
|
|
74
|
+
|
|
75
|
+
# @property
|
|
76
|
+
# def edges(self):
|
|
77
|
+
# return {k: v.left.union(v.right) for k, v in self.intervals.items()}
|
|
78
|
+
|
|
79
|
+
# @property
|
|
80
|
+
# def categoricals(self):
|
|
81
|
+
# return dict(self._categoricals)
|
|
82
|
+
|
|
83
|
+
# @property
|
|
84
|
+
# def intervals(self):
|
|
85
|
+
# # return dict(self._intervals)
|
|
86
|
+
# return {k: pd.IntervalIndex(v) for k, v in self.categoricals.items()}
|
|
87
|
+
|
|
88
|
+
# @property
|
|
89
|
+
# def cut(self):
|
|
90
|
+
# return self._cut
|
|
91
|
+
|
|
92
|
+
# @property
|
|
93
|
+
# def clim(self):
|
|
94
|
+
# return self._clim
|
|
95
|
+
|
|
96
|
+
# @property
|
|
97
|
+
# def agg_axes(self):
|
|
98
|
+
# r"""The axis to aggregate into, e.g. the z variable in an (x, y, z) heatmap.
|
|
99
|
+
# """
|
|
100
|
+
# tko = [c for c in self.data.columns if c not in self._gb_axes]
|
|
101
|
+
# assert len(tko) == 1
|
|
102
|
+
# tko = tko[0]
|
|
103
|
+
# return tko
|
|
104
|
+
|
|
105
|
+
# @property
|
|
106
|
+
# def joint(self):
|
|
107
|
+
# r"""A combination of the categorical and continuous data for use in `Groupby`.
|
|
108
|
+
# """
|
|
109
|
+
# # cut = self.cut
|
|
110
|
+
# # tko = self.agg_axes
|
|
111
|
+
|
|
112
|
+
# # self.logger.debug(f"Joining data ({tko}) with cat ({cut.columns.values})")
|
|
113
|
+
|
|
114
|
+
# # other = self.data.loc[cut.index, tko]
|
|
115
|
+
|
|
116
|
+
# # # joint = pd.concat([cut, other.to_frame(name=tko)], axis=1, sort=True)
|
|
117
|
+
# # joint = cut.copy(deep=True)
|
|
118
|
+
# # joint.loc[:, tko] = other
|
|
119
|
+
# # joint.sort_index(axis=1, inplace=True)
|
|
120
|
+
# # return joint
|
|
121
|
+
|
|
122
|
+
# cut = self.cut
|
|
123
|
+
# tk_target = self.agg_axes
|
|
124
|
+
# target = self.data.loc[cut.index, tk_target]
|
|
125
|
+
|
|
126
|
+
# mi = pd.MultiIndex.from_frame(cut)
|
|
127
|
+
# target.index = mi
|
|
128
|
+
|
|
129
|
+
# return target
|
|
130
|
+
|
|
131
|
+
# @property
|
|
132
|
+
# def grouped(self):
|
|
133
|
+
# r"""`joint.groupby` with appropriate axes passes.
|
|
134
|
+
# """
|
|
135
|
+
# # tko = self.agg_axes
|
|
136
|
+
# # gb = self.data.loc[:, tko].groupby([v for k, v in self.cut.items()], observed=False)
|
|
137
|
+
# # gb = self.joint.groupby(list(self._gb_axes))
|
|
138
|
+
|
|
139
|
+
# # cut = self.cut
|
|
140
|
+
# # tk_target = self.agg_axes
|
|
141
|
+
# # target = self.data.loc[cut.index, tk_target]
|
|
142
|
+
|
|
143
|
+
# # mi = pd.MultiIndex.from_frame(cut)
|
|
144
|
+
# # target.index = mi
|
|
145
|
+
|
|
146
|
+
# target = self.joint
|
|
147
|
+
# gb_axes = list(self._gb_axes)
|
|
148
|
+
# gb = target.groupby(gb_axes, axis=0, observed=True)
|
|
149
|
+
|
|
150
|
+
# # agg_axes = self.agg_axes
|
|
151
|
+
# # gb = (
|
|
152
|
+
# # self.joint.set_index(gb_axes)
|
|
153
|
+
# # .loc[:, agg_axes]
|
|
154
|
+
# # .groupby(gb_axes, axis=0, observed=False)
|
|
155
|
+
# # )
|
|
156
|
+
# return gb
|
|
157
|
+
|
|
158
|
+
# @property
|
|
159
|
+
# def axnorm(self):
|
|
160
|
+
# r"""Data normalization in plot.
|
|
161
|
+
|
|
162
|
+
# Not `mpl.colors.Normalize` instance. That is passed as a `kwarg` to
|
|
163
|
+
# `make_plot`.
|
|
164
|
+
# """
|
|
165
|
+
# return self._axnorm
|
|
166
|
+
|
|
167
|
+
# # Old version that cuts at percentiles.
|
|
168
|
+
# @staticmethod
|
|
169
|
+
# def clip_data(data, clip):
|
|
170
|
+
# q0 = 0.0001
|
|
171
|
+
# q1 = 0.9999
|
|
172
|
+
# pct = data.quantile([q0, q1])
|
|
173
|
+
# lo = pct.loc[q0]
|
|
174
|
+
# up = pct.loc[q1]
|
|
175
|
+
|
|
176
|
+
# if isinstance(data, pd.Series):
|
|
177
|
+
# ax = 0
|
|
178
|
+
# elif isinstance(data, pd.DataFrame):
|
|
179
|
+
# ax = 1
|
|
180
|
+
# else:
|
|
181
|
+
# raise TypeError("Unexpected object %s" % type(data))
|
|
182
|
+
|
|
183
|
+
# if isinstance(clip, str) and clip.lower()[0] == "l":
|
|
184
|
+
# data = data.clip_lower(lo, axis=ax)
|
|
185
|
+
# elif isinstance(clip, str) and clip.lower()[0] == "u":
|
|
186
|
+
# data = data.clip_upper(up, axis=ax)
|
|
187
|
+
# else:
|
|
188
|
+
# data = data.clip(lo, up, axis=ax)
|
|
189
|
+
# return data
|
|
190
|
+
|
|
191
|
+
# # New version that uses binning to cut.
|
|
192
|
+
# # @staticmethod
|
|
193
|
+
# # def clip_data(data, bins, clip):
|
|
194
|
+
# # q0 = 0.001
|
|
195
|
+
# # q1 = 0.999
|
|
196
|
+
# # pct = data.quantile([q0, q1])
|
|
197
|
+
# # lo = pct.loc[q0]
|
|
198
|
+
# # up = pct.loc[q1]
|
|
199
|
+
# # lo = bins.iloc[0]
|
|
200
|
+
# # up = bins.iloc[-1]
|
|
201
|
+
# # if isinstance(clip, str) and clip.lower()[0] == "l":
|
|
202
|
+
# # data = data.clip_lower(lo)
|
|
203
|
+
# # elif isinstance(clip, str) and clip.lower()[0] == "u":
|
|
204
|
+
# # data = data.clip_upper(up)
|
|
205
|
+
# # else:
|
|
206
|
+
# # data = data.clip(lo, up)
|
|
207
|
+
# # return data
|
|
208
|
+
|
|
209
|
+
# def set_clim(self, lower=None, upper=None):
|
|
210
|
+
# f"""Set the minimum (lower) and maximum (upper) alowed number of
|
|
211
|
+
# counts/bin to return aftter calling :py:meth:`{self.__class__.__name__}.add()`.
|
|
212
|
+
# """
|
|
213
|
+
# assert isinstance(lower, Number) or lower is None
|
|
214
|
+
# assert isinstance(upper, Number) or upper is None
|
|
215
|
+
# self._clim = (lower, upper)
|
|
216
|
+
|
|
217
|
+
# def calc_bins_intervals(self, nbins=101, precision=None):
|
|
218
|
+
# r"""
|
|
219
|
+
# Calculate histogram bins.
|
|
220
|
+
|
|
221
|
+
# nbins: int, str, array-like
|
|
222
|
+
# If int, use np.histogram to calculate the bin edges.
|
|
223
|
+
# If str and nbins == "knuth", use `astropy.stats.knuth_bin_width`
|
|
224
|
+
# to calculate optimal bin widths.
|
|
225
|
+
# If str and nbins != "knuth", use `np.histogram(data, bins=nbins)`
|
|
226
|
+
# to calculate bins.
|
|
227
|
+
# If array-like, treat as bins.
|
|
228
|
+
|
|
229
|
+
# precision: int or None
|
|
230
|
+
# Precision at which to store intervals. If None, default to 3.
|
|
231
|
+
# """
|
|
232
|
+
# data = self.data
|
|
233
|
+
# bins = {}
|
|
234
|
+
# intervals = {}
|
|
235
|
+
|
|
236
|
+
# if precision is None:
|
|
237
|
+
# precision = 5
|
|
238
|
+
|
|
239
|
+
# gb_axes = self._gb_axes
|
|
240
|
+
|
|
241
|
+
# if isinstance(nbins, (str, int)) or (
|
|
242
|
+
# hasattr(nbins, "__iter__") and len(nbins) != len(gb_axes)
|
|
243
|
+
# ):
|
|
244
|
+
# # Single paramter for `nbins`.
|
|
245
|
+
# nbins = {k: nbins for k in gb_axes}
|
|
246
|
+
|
|
247
|
+
# elif len(nbins) == len(gb_axes):
|
|
248
|
+
# # Passed one bin spec per axis
|
|
249
|
+
# nbins = {k: v for k, v in zip(gb_axes, nbins)}
|
|
250
|
+
|
|
251
|
+
# else:
|
|
252
|
+
# msg = f"Unrecognized `nbins`\ntype: {type(nbins)}\n bins:{nbins}"
|
|
253
|
+
# raise ValueError(msg)
|
|
254
|
+
|
|
255
|
+
# for k in self._gb_axes:
|
|
256
|
+
# b = nbins[k]
|
|
257
|
+
# # Numpy and Astropy don't like NaNs when calculating bins.
|
|
258
|
+
# # Infinities in bins (typically from log10(0)) also create problems.
|
|
259
|
+
# d = data.loc[:, k].replace([-np.inf, np.inf], np.nan).dropna()
|
|
260
|
+
|
|
261
|
+
# if isinstance(b, str):
|
|
262
|
+
# b = b.lower()
|
|
263
|
+
|
|
264
|
+
# if isinstance(b, str) and b == "knuth":
|
|
265
|
+
# try:
|
|
266
|
+
# assert knuth_bin_width
|
|
267
|
+
# except NameError:
|
|
268
|
+
# raise NameError("Astropy is unavailable.")
|
|
269
|
+
|
|
270
|
+
# dx, b = knuth_bin_width(d, return_bins=True)
|
|
271
|
+
|
|
272
|
+
# else:
|
|
273
|
+
# try:
|
|
274
|
+
# b = np.histogram_bin_edges(d, b)
|
|
275
|
+
# except MemoryError:
|
|
276
|
+
# # Clip the extremely large values and extremely small outliers.
|
|
277
|
+
# lo, up = d.quantile([0.0005, 0.9995])
|
|
278
|
+
# b = np.histogram_bin_edges(d.clip(lo, up), b)
|
|
279
|
+
# except AttributeError:
|
|
280
|
+
# c, b = np.histogram(d, b)
|
|
281
|
+
|
|
282
|
+
# assert np.unique(b).size == b.size
|
|
283
|
+
# try:
|
|
284
|
+
# assert not np.isnan(b).any()
|
|
285
|
+
# except TypeError:
|
|
286
|
+
# assert not b.isna().any()
|
|
287
|
+
|
|
288
|
+
# b = b.round(precision)
|
|
289
|
+
|
|
290
|
+
# zipped = zip(b[:-1], b[1:])
|
|
291
|
+
# i = [pd.Interval(*b0b1, closed="right") for b0b1 in zipped]
|
|
292
|
+
|
|
293
|
+
# bins[k] = b
|
|
294
|
+
# # intervals[k] = pd.IntervalIndex(i)
|
|
295
|
+
# intervals[k] = pd.CategoricalIndex(i)
|
|
296
|
+
|
|
297
|
+
# bins = tuple(bins.items())
|
|
298
|
+
# intervals = tuple(intervals.items())
|
|
299
|
+
# # self._intervals = intervals
|
|
300
|
+
# self._categoricals = intervals
|
|
301
|
+
|
|
302
|
+
# def make_cut(self):
|
|
303
|
+
# r"""Calculate the `Categorical` quantities for the aggregation axes.
|
|
304
|
+
# """
|
|
305
|
+
# intervals = self.intervals
|
|
306
|
+
# data = self.data
|
|
307
|
+
|
|
308
|
+
# cut = {}
|
|
309
|
+
# for k in self._gb_axes:
|
|
310
|
+
# d = data.loc[:, k]
|
|
311
|
+
# i = intervals[k]
|
|
312
|
+
|
|
313
|
+
# if self.clip:
|
|
314
|
+
# d = self.clip_data(d, self.clip)
|
|
315
|
+
|
|
316
|
+
# c = pd.cut(d, i)
|
|
317
|
+
# cut[k] = c
|
|
318
|
+
|
|
319
|
+
# cut = pd.DataFrame.from_dict(cut, orient="columns")
|
|
320
|
+
# self._cut = cut
|
|
321
|
+
|
|
322
|
+
# def _agg_runner(self, cut, tko, gb, fcn, **kwargs):
|
|
323
|
+
# r"""Refactored out the actual doing of the aggregation so that :py:class:`OrbitPlot`
|
|
324
|
+
# can aggregate (Inbound, Outbound, and Both).
|
|
325
|
+
# """
|
|
326
|
+
# self.logger.debug(f"aggregating {tko} data along {cut.columns.values}")
|
|
327
|
+
|
|
328
|
+
# if fcn is None:
|
|
329
|
+
# other = self.data.loc[cut.index, tko]
|
|
330
|
+
# if other.dropna().unique().size == 1:
|
|
331
|
+
# fcn = "count"
|
|
332
|
+
# else:
|
|
333
|
+
# fcn = "mean"
|
|
334
|
+
|
|
335
|
+
# agg = gb.agg(fcn, **kwargs) # .loc[:, tko]
|
|
336
|
+
|
|
337
|
+
# c0, c1 = self.clim
|
|
338
|
+
# if c0 is not None or c1 is not None:
|
|
339
|
+
# cnt = gb.agg("count") # .loc[:, tko]
|
|
340
|
+
# tk = pd.Series(True, index=agg.index)
|
|
341
|
+
# # tk = pd.DataFrame(True,
|
|
342
|
+
# # index=agg.index,
|
|
343
|
+
# # columns=agg.columns
|
|
344
|
+
# # )
|
|
345
|
+
# if c0 is not None:
|
|
346
|
+
# tk = tk & (cnt >= c0)
|
|
347
|
+
# if c1 is not None:
|
|
348
|
+
# tk = tk & (cnt <= c1)
|
|
349
|
+
|
|
350
|
+
# agg = agg.where(tk)
|
|
351
|
+
|
|
352
|
+
# # # Using `observed=False` in `self.grouped` raised a TypeError because mixed Categoricals and np.nans. (20200229)
|
|
353
|
+
# # # Ensure all bins are represented in the data. (20190605)
|
|
354
|
+
# # # for k, v in self.intervals.items():
|
|
355
|
+
# # for k, v in self.categoricals.items():
|
|
356
|
+
# # # if > 1 intervals, pass level. Otherwise, don't as this raises a NotImplementedError. (20190619)
|
|
357
|
+
# # agg = agg.reindex(index=v, level=k if agg.index.nlevels > 1 else None)
|
|
358
|
+
|
|
359
|
+
# return agg
|
|
360
|
+
|
|
361
|
+
# def _agg_reindexer(self, agg):
|
|
362
|
+
# # Using `observed=False` in `self.grouped` raised a TypeError because mixed Categoricals and np.nans. (20200229)
|
|
363
|
+
# # Ensure all bins are represented in the data. (20190605)
|
|
364
|
+
# # for k, v in self.intervals.items():
|
|
365
|
+
# for k, v in self.categoricals.items():
|
|
366
|
+
# # if > 1 intervals, pass level. Otherwise, don't as this raises a NotImplementedError. (20190619)
|
|
367
|
+
# agg = agg.reindex(index=v, level=k if agg.index.nlevels > 1 else None)
|
|
368
|
+
|
|
369
|
+
# return agg
|
|
370
|
+
|
|
371
|
+
# def agg(self, fcn=None, **kwargs):
|
|
372
|
+
# r"""Perform the aggregation along the agg axes.
|
|
373
|
+
|
|
374
|
+
# If either of the count limits specified in `clim` are not None, apply them.
|
|
375
|
+
|
|
376
|
+
# `fcn` allows you to specify a specific function for aggregation. Otherwise,
|
|
377
|
+
# automatically choose "count" or "mean" based on the uniqueness of the aggregated
|
|
378
|
+
# values.
|
|
379
|
+
# """
|
|
380
|
+
# cut = self.cut
|
|
381
|
+
# tko = self.agg_axes
|
|
382
|
+
|
|
383
|
+
# self.logger.info(
|
|
384
|
+
# f"Starting {self.__class__.__name__!s} aggregation of ({tko}) in ({cut.columns.values})\n%s",
|
|
385
|
+
# "\n".join([f"{k!s}: {v!s}" for k, v in self.labels._asdict().items()]),
|
|
386
|
+
# )
|
|
387
|
+
|
|
388
|
+
# gb = self.grouped
|
|
389
|
+
|
|
390
|
+
# agg = self._agg_runner(cut, tko, gb, fcn, **kwargs)
|
|
391
|
+
|
|
392
|
+
# return agg
|
|
393
|
+
|
|
394
|
+
# def get_plotted_data_boolean_series(self):
|
|
395
|
+
# f"""A boolean `pd.Series` identifing each measurement that is plotted.
|
|
396
|
+
|
|
397
|
+
# Note: The Series is indexed identically to the data stored in the :py:class:`{self.__class__.__name__}`.
|
|
398
|
+
# To align with another index, you may want to use:
|
|
399
|
+
|
|
400
|
+
# tk = {self.__class__.__name__}.get_plotted_data_boolean_series()
|
|
401
|
+
# idx = tk.replace(False, np.nan).dropna().index
|
|
402
|
+
# """
|
|
403
|
+
# agg = self.agg().dropna()
|
|
404
|
+
# cut = self.cut
|
|
405
|
+
|
|
406
|
+
# tk = pd.Series(True, index=cut.index)
|
|
407
|
+
# for k, v in cut.items():
|
|
408
|
+
# chk = agg.index.get_level_values(k)
|
|
409
|
+
# # Use the codes directly because the categoricals are
|
|
410
|
+
# # failing with some Pandas numpy ufunc use. (20200611)
|
|
411
|
+
# chk = pd.CategoricalIndex(chk)
|
|
412
|
+
# tk_ax = v.cat.codes.isin(chk.codes)
|
|
413
|
+
# tk = tk & tk_ax
|
|
414
|
+
|
|
415
|
+
# self.logger.info(
|
|
416
|
+
# f"Taking {tk.sum()!s} ({100*tk.mean():.1f}%) {self.__class__.__name__} spectra"
|
|
417
|
+
# )
|
|
418
|
+
|
|
419
|
+
# return tk
|
|
420
|
+
|
|
421
|
+
# # Old version that cuts at percentiles.
|
|
422
|
+
# # @staticmethod
|
|
423
|
+
# # def clip_data(data, clip):
|
|
424
|
+
# # q0 = 0.0001
|
|
425
|
+
# # q1 = 0.9999
|
|
426
|
+
# # pct = data.quantile([q0, q1])
|
|
427
|
+
# # lo = pct.loc[q0]
|
|
428
|
+
# # up = pct.loc[q1]
|
|
429
|
+
# #
|
|
430
|
+
# # if isinstance(data, pd.Series):
|
|
431
|
+
# # ax = 0
|
|
432
|
+
# # elif isinstance(data, pd.DataFrame):
|
|
433
|
+
# # ax = 1
|
|
434
|
+
# # else:
|
|
435
|
+
# # raise TypeError("Unexpected object %s" % type(data))
|
|
436
|
+
# #
|
|
437
|
+
# # if isinstance(clip, str) and clip.lower()[0] == "l":
|
|
438
|
+
# # data = data.clip_lower(lo, axis=ax)
|
|
439
|
+
# # elif isinstance(clip, str) and clip.lower()[0] == "u":
|
|
440
|
+
# # data = data.clip_upper(up, axis=ax)
|
|
441
|
+
# # else:
|
|
442
|
+
# # data = data.clip(lo, up, axis=ax)
|
|
443
|
+
# # return data
|
|
444
|
+
# #
|
|
445
|
+
# # New version that uses binning to cut.
|
|
446
|
+
# # @staticmethod
|
|
447
|
+
# # def clip_data(data, bins, clip):
|
|
448
|
+
# # q0 = 0.001
|
|
449
|
+
# # q1 = 0.999
|
|
450
|
+
# # pct = data.quantile([q0, q1])
|
|
451
|
+
# # lo = pct.loc[q0]
|
|
452
|
+
# # up = pct.loc[q1]
|
|
453
|
+
# # lo = bins.iloc[0]
|
|
454
|
+
# # up = bins.iloc[-1]
|
|
455
|
+
# # if isinstance(clip, str) and clip.lower()[0] == "l":
|
|
456
|
+
# # data = data.clip_lower(lo)
|
|
457
|
+
# # elif isinstance(clip, str) and clip.lower()[0] == "u":
|
|
458
|
+
# # data = data.clip_upper(up)
|
|
459
|
+
# # else:
|
|
460
|
+
# # data = data.clip(lo, up)
|
|
461
|
+
# # return data
|
|
462
|
+
|
|
463
|
+
# @abstractproperty
|
|
464
|
+
# def _gb_axes(self):
|
|
465
|
+
# r"""The axes or columns over which the `groupby` aggregation takes place.
|
|
466
|
+
|
|
467
|
+
# 1D cases aggregate over `x`. 2D cases aggregate over `x` and `y`.
|
|
468
|
+
# """
|
|
469
|
+
# pass
|
|
470
|
+
|
|
471
|
+
# @abstractmethod
|
|
472
|
+
# def set_axnorm(self, new):
|
|
473
|
+
# r"""The method by which the gridded data is normalized.
|
|
474
|
+
# """
|
|
475
|
+
# pass
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# class Hist1D(AggPlot):
|
|
479
|
+
# r"""Create 1D plot of `x`, optionally aggregating `y` in bins of `x`.
|
|
480
|
+
|
|
481
|
+
# Properties
|
|
482
|
+
# ----------
|
|
483
|
+
# _gb_axes, path
|
|
484
|
+
|
|
485
|
+
# Methods
|
|
486
|
+
# -------
|
|
487
|
+
# set_path, set_data, agg, _format_axis, make_plot
|
|
488
|
+
# """
|
|
489
|
+
|
|
490
|
+
# def __init__(
|
|
491
|
+
# self,
|
|
492
|
+
# x,
|
|
493
|
+
# y=None,
|
|
494
|
+
# logx=False,
|
|
495
|
+
# axnorm=None,
|
|
496
|
+
# clip_data=False,
|
|
497
|
+
# nbins=101,
|
|
498
|
+
# bin_precision=None,
|
|
499
|
+
# ):
|
|
500
|
+
# r"""
|
|
501
|
+
# Parameters
|
|
502
|
+
# ----------
|
|
503
|
+
# x: pd.Series
|
|
504
|
+
# Data from which to create bins.
|
|
505
|
+
# y: pd.Series, None
|
|
506
|
+
# If not None, the values to aggregate in bins of `x`. If None,
|
|
507
|
+
# aggregate counts of `x`.
|
|
508
|
+
# logx: bool
|
|
509
|
+
# If True, compute bins in log-space.
|
|
510
|
+
# axnorm: None, str
|
|
511
|
+
# Normalize the histogram.
|
|
512
|
+
# key normalization
|
|
513
|
+
# --- -------------
|
|
514
|
+
# t total
|
|
515
|
+
# d density
|
|
516
|
+
# clip_data: bool
|
|
517
|
+
# If True, remove the extreme values at 0.001 and 0.999 percentiles
|
|
518
|
+
# before calculating bins or aggregating.
|
|
519
|
+
# nbins: int, str, array-like
|
|
520
|
+
# Dispatched to `np.histogram_bin_edges` or `pd.cut` depending on
|
|
521
|
+
# input type and value.
|
|
522
|
+
# """
|
|
523
|
+
# super(Hist1D, self).__init__()
|
|
524
|
+
# self.set_log(x=logx)
|
|
525
|
+
# self.set_axnorm(axnorm)
|
|
526
|
+
# self.set_data(x, y, clip_data)
|
|
527
|
+
# self.set_labels(x="x", y=labels_module.Count(norm=axnorm) if y is None else "y")
|
|
528
|
+
# self.calc_bins_intervals(nbins=nbins, precision=bin_precision)
|
|
529
|
+
# self.make_cut()
|
|
530
|
+
# self.set_clim(None, None)
|
|
531
|
+
|
|
532
|
+
# @property
|
|
533
|
+
# def _gb_axes(self):
|
|
534
|
+
# return ("x",)
|
|
535
|
+
|
|
536
|
+
# def set_path(self, new, add_scale=True):
|
|
537
|
+
# path, x, y, z, scale_info = super(Hist1D, self).set_path(new, add_scale)
|
|
538
|
+
|
|
539
|
+
# if new == "auto":
|
|
540
|
+
# path = path / x / y
|
|
541
|
+
|
|
542
|
+
# else:
|
|
543
|
+
# assert x is None
|
|
544
|
+
# assert y is None
|
|
545
|
+
|
|
546
|
+
# if add_scale:
|
|
547
|
+
# assert scale_info is not None
|
|
548
|
+
# scale_info = scale_info[0]
|
|
549
|
+
# path = path / scale_info
|
|
550
|
+
|
|
551
|
+
# self._path = path
|
|
552
|
+
|
|
553
|
+
# set_path.__doc__ = base.Base.set_path.__doc__
|
|
554
|
+
|
|
555
|
+
# def set_data(self, x, y, clip):
|
|
556
|
+
# data = pd.DataFrame({"x": np.log10(np.abs(x)) if self.log.x else x})
|
|
557
|
+
|
|
558
|
+
# if y is None:
|
|
559
|
+
# y = pd.Series(1, index=x.index)
|
|
560
|
+
# data.loc[:, "y"] = y
|
|
561
|
+
|
|
562
|
+
# self._data = data
|
|
563
|
+
# self._clip = clip
|
|
564
|
+
|
|
565
|
+
# def set_axnorm(self, new):
|
|
566
|
+
# r"""The method by which the gridded data is normalized.
|
|
567
|
+
|
|
568
|
+
# ===== =============================================================
|
|
569
|
+
# key description
|
|
570
|
+
# ===== =============================================================
|
|
571
|
+
# d Density normalize
|
|
572
|
+
# t Total normalize
|
|
573
|
+
# ===== =============================================================
|
|
574
|
+
# """
|
|
575
|
+
# if new is not None:
|
|
576
|
+
# new = new.lower()[0]
|
|
577
|
+
# assert new == "d"
|
|
578
|
+
|
|
579
|
+
# ylbl = self.labels.y
|
|
580
|
+
# if isinstance(ylbl, labels_module.Count):
|
|
581
|
+
# ylbl.set_axnorm(new)
|
|
582
|
+
# ylbl.build_label()
|
|
583
|
+
|
|
584
|
+
# self._axnorm = new
|
|
585
|
+
|
|
586
|
+
# def construct_cdf(self, only_plotted=True):
|
|
587
|
+
# r"""Convert the obsered measuremets.
|
|
588
|
+
|
|
589
|
+
# Returns
|
|
590
|
+
# -------
|
|
591
|
+
# cdf: pd.DataFrame
|
|
592
|
+
# "x" column is the value of the measuremnt.
|
|
593
|
+
# "position" column is the normalized position in the cdf.
|
|
594
|
+
# To plot the cdf:
|
|
595
|
+
|
|
596
|
+
# cdf.plot(x="x", y="cdf")
|
|
597
|
+
# """
|
|
598
|
+
# data = self.data
|
|
599
|
+
# if not data.loc[:, "y"].unique().size <= 2:
|
|
600
|
+
# raise ValueError("Only able to convert data to a cdf if it is a histogram.")
|
|
601
|
+
|
|
602
|
+
# tk = self.cut.loc[:, "x"].notna()
|
|
603
|
+
# if only_plotted:
|
|
604
|
+
# tk = tk & self.get_plotted_data_boolean_series()
|
|
605
|
+
|
|
606
|
+
# x = data.loc[tk, "x"]
|
|
607
|
+
# cdf = x.sort_values().reset_index(drop=True)
|
|
608
|
+
|
|
609
|
+
# if self.log.x:
|
|
610
|
+
# cdf = 10.0 ** cdf
|
|
611
|
+
|
|
612
|
+
# cdf = cdf.to_frame()
|
|
613
|
+
# cdf.loc[:, "position"] = cdf.index / cdf.index.max()
|
|
614
|
+
|
|
615
|
+
# return cdf
|
|
616
|
+
|
|
617
|
+
# def _axis_normalizer(self, agg):
|
|
618
|
+
# r"""Takes care of row, column, total, and density normaliation.
|
|
619
|
+
|
|
620
|
+
# Written basically as `staticmethod` so that can be called in `OrbitHist2D`, but
|
|
621
|
+
# as actual method with `self` passed so we have access to `self.log` for density
|
|
622
|
+
# normalization.
|
|
623
|
+
# """
|
|
624
|
+
|
|
625
|
+
# axnorm = self.axnorm
|
|
626
|
+
# if axnorm is None:
|
|
627
|
+
# pass
|
|
628
|
+
# elif axnorm == "d":
|
|
629
|
+
# n = agg.sum()
|
|
630
|
+
# dx = pd.Series(pd.IntervalIndex(agg.index).length, index=agg.index)
|
|
631
|
+
# if self.log.x:
|
|
632
|
+
# dx = 10.0 ** dx
|
|
633
|
+
# agg = agg.divide(dx.multiply(n))
|
|
634
|
+
|
|
635
|
+
# elif axnorm == "t":
|
|
636
|
+
# agg = agg.divide(agg.max())
|
|
637
|
+
|
|
638
|
+
# else:
|
|
639
|
+
# raise ValueError("Unrecognized axnorm: %s" % axnorm)
|
|
640
|
+
|
|
641
|
+
# return agg
|
|
642
|
+
|
|
643
|
+
# def agg(self, **kwargs):
|
|
644
|
+
# if self.axnorm == "d":
|
|
645
|
+
# fcn = kwargs.get("fcn", None)
|
|
646
|
+
# if (fcn != "count") & (fcn is not None):
|
|
647
|
+
# raise ValueError("Unable to calculate a PDF with non-count aggregation")
|
|
648
|
+
|
|
649
|
+
# agg = super(Hist1D, self).agg(**kwargs)
|
|
650
|
+
# agg = self._axis_normalizer(agg)
|
|
651
|
+
# agg = self._agg_reindexer(agg)
|
|
652
|
+
|
|
653
|
+
# return agg
|
|
654
|
+
|
|
655
|
+
# def set_labels(self, **kwargs):
|
|
656
|
+
|
|
657
|
+
# if "z" in kwargs:
|
|
658
|
+
# raise ValueError(r"{} doesn't have a z-label".format(self))
|
|
659
|
+
|
|
660
|
+
# y = kwargs.pop("y", self.labels.y)
|
|
661
|
+
# if isinstance(y, labels_module.Count):
|
|
662
|
+
# y.set_axnorm(self.axnorm)
|
|
663
|
+
# y.build_label()
|
|
664
|
+
|
|
665
|
+
# super(Hist1D, self).set_labels(y=y, **kwargs)
|
|
666
|
+
|
|
667
|
+
# def make_plot(self, ax=None, fcn=None, **kwargs):
|
|
668
|
+
# f"""Make a plot.
|
|
669
|
+
|
|
670
|
+
# Parameters
|
|
671
|
+
# ----------
|
|
672
|
+
# ax: None, mpl.axis.Axis
|
|
673
|
+
# If `None`, create a subplot axis.
|
|
674
|
+
# fcn: None, str, aggregative function, or 2-tuple of strings
|
|
675
|
+
# Passed directly to `{self.__class__.__name__}.agg`. If
|
|
676
|
+
# None, use the default aggregation function. If str or a
|
|
677
|
+
# single aggregative function, use it.
|
|
678
|
+
# kwargs:
|
|
679
|
+
# Passed directly to `ax.plot`.
|
|
680
|
+
# """
|
|
681
|
+
# agg = self.agg(fcn=fcn)
|
|
682
|
+
# x = pd.IntervalIndex(agg.index).mid
|
|
683
|
+
|
|
684
|
+
# if fcn is None or isinstance(fcn, str):
|
|
685
|
+
# y = agg
|
|
686
|
+
# dy = None
|
|
687
|
+
|
|
688
|
+
# elif len(fcn) == 2:
|
|
689
|
+
|
|
690
|
+
# f0, f1 = fcn
|
|
691
|
+
# if isinstance(f0, FunctionType):
|
|
692
|
+
# f0 = f0.__name__
|
|
693
|
+
# if isinstance(f1, FunctionType):
|
|
694
|
+
# f1 = f1.__name__
|
|
695
|
+
|
|
696
|
+
# y = agg.loc[:, f0]
|
|
697
|
+
# dy = agg.loc[:, f1]
|
|
698
|
+
|
|
699
|
+
# else:
|
|
700
|
+
# raise ValueError(f"Unrecognized `fcn` ({fcn})")
|
|
701
|
+
|
|
702
|
+
# if ax is None:
|
|
703
|
+
# fig, ax = plt.subplots()
|
|
704
|
+
|
|
705
|
+
# if self.log.x:
|
|
706
|
+
# x = 10.0 ** x
|
|
707
|
+
|
|
708
|
+
# drawstyle = kwargs.pop("drawstyle", "steps-mid")
|
|
709
|
+
# pl, cl, bl = ax.errorbar(x, y, yerr=dy, drawstyle=drawstyle, **kwargs)
|
|
710
|
+
|
|
711
|
+
# self._format_axis(ax)
|
|
712
|
+
|
|
713
|
+
# return ax
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
# class Hist2D(base.Plot2D, AggPlot):
|
|
717
|
+
# r"""Create a 2D histogram with an optional z-value using an equal number
|
|
718
|
+
# of bins along the x and y axis.
|
|
719
|
+
|
|
720
|
+
# Parameters
|
|
721
|
+
# ----------
|
|
722
|
+
# x, y: pd.Series
|
|
723
|
+
# x and y data to aggregate
|
|
724
|
+
# z: None, pd.Series
|
|
725
|
+
# If not None, the z-value to aggregate.
|
|
726
|
+
# axnorm: str
|
|
727
|
+
# Normalize the histogram.
|
|
728
|
+
# key normalization
|
|
729
|
+
# --- -------------
|
|
730
|
+
# c column
|
|
731
|
+
# r row
|
|
732
|
+
# t total
|
|
733
|
+
# d density
|
|
734
|
+
# logx, logy: bool
|
|
735
|
+
# If True, log10 scale the axis.
|
|
736
|
+
|
|
737
|
+
# Properties
|
|
738
|
+
# ----------
|
|
739
|
+
# data:
|
|
740
|
+
# bins:
|
|
741
|
+
# cut:
|
|
742
|
+
# axnorm:
|
|
743
|
+
# log<x,y>:
|
|
744
|
+
# <x,y,z>label:
|
|
745
|
+
# path: None, Path
|
|
746
|
+
|
|
747
|
+
# Methods
|
|
748
|
+
# -------
|
|
749
|
+
# calc_bins:
|
|
750
|
+
# calculate the x, y bins.
|
|
751
|
+
# make_cut:
|
|
752
|
+
# Utilize the calculated bins to convert (x, y) into pd.Categoral
|
|
753
|
+
# or pd.Interval values used in aggregation.
|
|
754
|
+
# set_[x,y,z]label:
|
|
755
|
+
# Set the x, y, or z label.
|
|
756
|
+
# agg:
|
|
757
|
+
# Aggregate the data in the bins.
|
|
758
|
+
# If z-value is None, count the number of points in each bin.
|
|
759
|
+
# If z-value is not None, calculate the mean for each bin.
|
|
760
|
+
# make_plot:
|
|
761
|
+
# Make a 2D plot of the data with an optional color bar.
|
|
762
|
+
# """
|
|
763
|
+
|
|
764
|
+
# def __init__(
|
|
765
|
+
# self,
|
|
766
|
+
# x,
|
|
767
|
+
# y,
|
|
768
|
+
# z=None,
|
|
769
|
+
# axnorm=None,
|
|
770
|
+
# logx=False,
|
|
771
|
+
# logy=False,
|
|
772
|
+
# clip_data=False,
|
|
773
|
+
# nbins=101,
|
|
774
|
+
# bin_precision=None,
|
|
775
|
+
# ):
|
|
776
|
+
# super(Hist2D, self).__init__()
|
|
777
|
+
# self.set_log(x=logx, y=logy)
|
|
778
|
+
# self.set_data(x, y, z, clip_data)
|
|
779
|
+
# self.set_labels(
|
|
780
|
+
# x="x", y="y", z=labels_module.Count(norm=axnorm) if z is None else "z"
|
|
781
|
+
# )
|
|
782
|
+
|
|
783
|
+
# self.set_axnorm(axnorm)
|
|
784
|
+
# self.calc_bins_intervals(nbins=nbins, precision=bin_precision)
|
|
785
|
+
# self.make_cut()
|
|
786
|
+
# self.set_clim(None, None)
|
|
787
|
+
|
|
788
|
+
# @property
|
|
789
|
+
# def _gb_axes(self):
|
|
790
|
+
# return ("x", "y")
|
|
791
|
+
|
|
792
|
+
# def _maybe_convert_to_log_scale(self, x, y):
|
|
793
|
+
# if self.log.x:
|
|
794
|
+
# x = 10.0 ** x
|
|
795
|
+
# if self.log.y:
|
|
796
|
+
# y = 10.0 ** y
|
|
797
|
+
|
|
798
|
+
# return x, y
|
|
799
|
+
|
|
800
|
+
# # def set_path(self, new, add_scale=True):
|
|
801
|
+
# # # Bug: path doesn't auto-set log information.
|
|
802
|
+
# # path, x, y, z, scale_info = super(Hist2D, self).set_path(new, add_scale)
|
|
803
|
+
|
|
804
|
+
# # if new == "auto":
|
|
805
|
+
# # path = path / x / y / z
|
|
806
|
+
|
|
807
|
+
# # else:
|
|
808
|
+
# # assert x is None
|
|
809
|
+
# # assert y is None
|
|
810
|
+
# # assert z is None
|
|
811
|
+
|
|
812
|
+
# # if add_scale:
|
|
813
|
+
# # assert scale_info is not None
|
|
814
|
+
|
|
815
|
+
# # scale_info = "-".join(scale_info)
|
|
816
|
+
|
|
817
|
+
# # if bool(len(path.parts)) and path.parts[-1].endswith("norm"):
|
|
818
|
+
# # # Insert <norm> at end of path so scale order is (x, y, z).
|
|
819
|
+
# # path = path.parts
|
|
820
|
+
# # path = path[:-1] + (scale_info + "-" + path[-1],)
|
|
821
|
+
# # path = Path(*path)
|
|
822
|
+
# # else:
|
|
823
|
+
# # path = path / scale_info
|
|
824
|
+
|
|
825
|
+
# # self._path = path
|
|
826
|
+
|
|
827
|
+
# # set_path.__doc__ = base.Base.set_path.__doc__
|
|
828
|
+
|
|
829
|
+
# def set_labels(self, **kwargs):
|
|
830
|
+
|
|
831
|
+
# z = kwargs.pop("z", self.labels.z)
|
|
832
|
+
# if isinstance(z, labels_module.Count):
|
|
833
|
+
# try:
|
|
834
|
+
# z.set_axnorm(self.axnorm)
|
|
835
|
+
# except AttributeError:
|
|
836
|
+
# pass
|
|
837
|
+
|
|
838
|
+
# z.build_label()
|
|
839
|
+
|
|
840
|
+
# super(Hist2D, self).set_labels(z=z, **kwargs)
|
|
841
|
+
|
|
842
|
+
# # def set_data(self, x, y, z, clip):
|
|
843
|
+
# # data = pd.DataFrame(
|
|
844
|
+
# # {
|
|
845
|
+
# # "x": np.log10(np.abs(x)) if self.log.x else x,
|
|
846
|
+
# # "y": np.log10(np.abs(y)) if self.log.y else y,
|
|
847
|
+
# # }
|
|
848
|
+
# # )
|
|
849
|
+
# #
|
|
850
|
+
# #
|
|
851
|
+
# # if z is None:
|
|
852
|
+
# # z = pd.Series(1, index=x.index)
|
|
853
|
+
# #
|
|
854
|
+
# # data.loc[:, "z"] = z
|
|
855
|
+
# # data = data.dropna()
|
|
856
|
+
# # if not data.shape[0]:
|
|
857
|
+
# # raise ValueError(
|
|
858
|
+
# # "You can't build a %s with data that is exclusively NaNs"
|
|
859
|
+
# # % self.__class__.__name__
|
|
860
|
+
# # )
|
|
861
|
+
# #
|
|
862
|
+
# # self._data = data
|
|
863
|
+
# # self._clip = clip
|
|
864
|
+
|
|
865
|
+
# def set_data(self, x, y, z, clip):
|
|
866
|
+
# super(Hist2D, self).set_data(x, y, z, clip)
|
|
867
|
+
# data = self.data
|
|
868
|
+
# if self.log.x:
|
|
869
|
+
# data.loc[:, "x"] = np.log10(np.abs(data.loc[:, "x"]))
|
|
870
|
+
# if self.log.y:
|
|
871
|
+
# data.loc[:, "y"] = np.log10(np.abs(data.loc[:, "y"]))
|
|
872
|
+
# self._data = data
|
|
873
|
+
|
|
874
|
+
# def set_axnorm(self, new):
|
|
875
|
+
# r"""The method by which the gridded data is normalized.
|
|
876
|
+
|
|
877
|
+
# ===== =============================================================
|
|
878
|
+
# key description
|
|
879
|
+
# ===== =============================================================
|
|
880
|
+
# c Column normalize
|
|
881
|
+
# d Density normalize
|
|
882
|
+
# r Row normalize
|
|
883
|
+
# t Total normalize
|
|
884
|
+
# ===== =============================================================
|
|
885
|
+
# """
|
|
886
|
+
# if new is not None:
|
|
887
|
+
# new = new.lower()[0]
|
|
888
|
+
# assert new in ("c", "r", "t", "d")
|
|
889
|
+
|
|
890
|
+
# zlbl = self.labels.z
|
|
891
|
+
# if isinstance(zlbl, labels_module.Count):
|
|
892
|
+
# zlbl.set_axnorm(new)
|
|
893
|
+
# zlbl.build_label()
|
|
894
|
+
|
|
895
|
+
# self._axnorm = new
|
|
896
|
+
|
|
897
|
+
# def _axis_normalizer(self, agg):
|
|
898
|
+
# r"""Takes care of row, column, total, and density normaliation.
|
|
899
|
+
|
|
900
|
+
# Written basically as `staticmethod` so that can be called in `OrbitHist2D`, but
|
|
901
|
+
# as actual method with `self` passed so we have access to `self.log` for density
|
|
902
|
+
# normalization.
|
|
903
|
+
# """
|
|
904
|
+
|
|
905
|
+
# axnorm = self.axnorm
|
|
906
|
+
# if axnorm is None:
|
|
907
|
+
# pass
|
|
908
|
+
# elif axnorm == "c":
|
|
909
|
+
# agg = agg.divide(agg.max(level="x"), level="x")
|
|
910
|
+
# elif axnorm == "r":
|
|
911
|
+
# agg = agg.divide(agg.max(level="y"), level="y")
|
|
912
|
+
# elif axnorm == "t":
|
|
913
|
+
# agg = agg.divide(agg.max())
|
|
914
|
+
# elif axnorm == "d":
|
|
915
|
+
# N = agg.sum().sum()
|
|
916
|
+
# x = pd.IntervalIndex(agg.index.get_level_values("x").unique())
|
|
917
|
+
# y = pd.IntervalIndex(agg.index.get_level_values("y").unique())
|
|
918
|
+
# dx = pd.Series(
|
|
919
|
+
# x.length, index=x
|
|
920
|
+
# ) # dx = pd.Series(x.right - x.left, index=x)
|
|
921
|
+
# dy = pd.Series(
|
|
922
|
+
# y.length, index=y
|
|
923
|
+
# ) # dy = pd.Series(y.right - y.left, index=y)
|
|
924
|
+
|
|
925
|
+
# if self.log.x:
|
|
926
|
+
# dx = 10.0 ** dx
|
|
927
|
+
# if self.log.y:
|
|
928
|
+
# dy = 10.0 ** dy
|
|
929
|
+
|
|
930
|
+
# agg = agg.divide(dx, level="x").divide(dy, level="y").divide(N)
|
|
931
|
+
|
|
932
|
+
# elif hasattr(axnorm, "__iter__"):
|
|
933
|
+
# kind, fcn = axnorm
|
|
934
|
+
# if kind == "c":
|
|
935
|
+
# agg = agg.divide(agg.agg(fcn, level="x"), level="x")
|
|
936
|
+
# elif kind == "r":
|
|
937
|
+
# agg = agg.divide(agg.agg(fcn, level="y"), level="y")
|
|
938
|
+
# else:
|
|
939
|
+
# raise ValueError(f"Unrecognized axnorm with function ({kind}, {fcn})")
|
|
940
|
+
# else:
|
|
941
|
+
# raise ValueError(f"Unrecognized axnorm ({axnorm})")
|
|
942
|
+
|
|
943
|
+
# return agg
|
|
944
|
+
|
|
945
|
+
# def agg(self, **kwargs):
|
|
946
|
+
# agg = super(Hist2D, self).agg(**kwargs)
|
|
947
|
+
# agg = self._axis_normalizer(agg)
|
|
948
|
+
# agg = self._agg_reindexer(agg)
|
|
949
|
+
|
|
950
|
+
# return agg
|
|
951
|
+
|
|
952
|
+
# def _make_cbar(self, mappable, **kwargs):
|
|
953
|
+
# ticks = kwargs.pop(
|
|
954
|
+
# "ticks",
|
|
955
|
+
# mpl.ticker.MultipleLocator(0.1) if self.axnorm in ("c", "r") else None,
|
|
956
|
+
# )
|
|
957
|
+
# return super(Hist2D, self)._make_cbar(mappable, ticks=ticks, **kwargs)
|
|
958
|
+
|
|
959
|
+
# def _limit_color_norm(self, norm):
|
|
960
|
+
# if self.axnorm in ("c", "r"):
|
|
961
|
+
# # Don't limit us to (1%, 99%) interval.
|
|
962
|
+
# return None
|
|
963
|
+
|
|
964
|
+
# pct = self.data.loc[:, "z"].quantile([0.01, 0.99])
|
|
965
|
+
# v0 = pct.loc[0.01]
|
|
966
|
+
# v1 = pct.loc[0.99]
|
|
967
|
+
# if norm.vmin is None:
|
|
968
|
+
# norm.vmin = v0
|
|
969
|
+
# if norm.vmax is None:
|
|
970
|
+
# norm.vmax = v1
|
|
971
|
+
# norm.clip = True
|
|
972
|
+
|
|
973
|
+
# def make_plot(
|
|
974
|
+
# self,
|
|
975
|
+
# ax=None,
|
|
976
|
+
# cbar=True,
|
|
977
|
+
# limit_color_norm=False,
|
|
978
|
+
# cbar_kwargs=None,
|
|
979
|
+
# fcn=None,
|
|
980
|
+
# alpha_fcn=None,
|
|
981
|
+
# **kwargs,
|
|
982
|
+
# ):
|
|
983
|
+
# r"""
|
|
984
|
+
# Make a 2D plot on `ax` using `ax.pcolormesh`.
|
|
985
|
+
|
|
986
|
+
# Paremeters
|
|
987
|
+
# ----------
|
|
988
|
+
# ax: mpl.axes.Axes, None
|
|
989
|
+
# If None, create an `Axes` instance from `plt.subplots`.
|
|
990
|
+
# cbar: bool
|
|
991
|
+
# If True, create color bar with `labels.z`.
|
|
992
|
+
# limit_color_norm: bool
|
|
993
|
+
# If True, limit the color range to 0.001 and 0.999 percentile range
|
|
994
|
+
# of the z-value, count or otherwise.
|
|
995
|
+
# cbar_kwargs: dict, None
|
|
996
|
+
# If not None, kwargs passed to `self._make_cbar`.
|
|
997
|
+
# fcn: FunctionType, None
|
|
998
|
+
# Aggregation function. If None, automatically select in :py:meth:`agg`.
|
|
999
|
+
# alpha_fcn: None, str
|
|
1000
|
+
# If not None, the function used to aggregate the data for setting alpha
|
|
1001
|
+
# value.
|
|
1002
|
+
# kwargs:
|
|
1003
|
+
# Passed to `ax.pcolormesh`.
|
|
1004
|
+
# If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
|
|
1005
|
+
|
|
1006
|
+
# Returns
|
|
1007
|
+
# -------
|
|
1008
|
+
# ax: mpl.axes.Axes
|
|
1009
|
+
# Axes upon which plot was made.
|
|
1010
|
+
# cbar_or_mappable: colorbar.Colorbar, mpl.collections.QuadMesh
|
|
1011
|
+
# If `cbar` is True, return the colorbar. Otherwise, return the `Quadmesh` used
|
|
1012
|
+
# to create the colorbar.
|
|
1013
|
+
# """
|
|
1014
|
+
# agg = self.agg(fcn=fcn).unstack("x")
|
|
1015
|
+
# x = self.edges["x"]
|
|
1016
|
+
# y = self.edges["y"]
|
|
1017
|
+
|
|
1018
|
+
# # assert x.size == agg.shape[1] + 1
|
|
1019
|
+
# # assert y.size == agg.shape[0] + 1
|
|
1020
|
+
|
|
1021
|
+
# # HACK: Works around `gb.agg(observed=False)` pandas bug. (GH32381)
|
|
1022
|
+
# if x.size != agg.shape[1] + 1:
|
|
1023
|
+
# # agg = agg.reindex(columns=self.intervals["x"])
|
|
1024
|
+
# agg = agg.reindex(columns=self.categoricals["x"])
|
|
1025
|
+
# if y.size != agg.shape[0] + 1:
|
|
1026
|
+
# # agg = agg.reindex(index=self.intervals["y"])
|
|
1027
|
+
# agg = agg.reindex(index=self.categoricals["y"])
|
|
1028
|
+
|
|
1029
|
+
# if ax is None:
|
|
1030
|
+
# fig, ax = plt.subplots()
|
|
1031
|
+
|
|
1032
|
+
# # if self.log.x:
|
|
1033
|
+
# # x = 10.0 ** x
|
|
1034
|
+
# # if self.log.y:
|
|
1035
|
+
# # y = 10.0 ** y
|
|
1036
|
+
# x, y = self._maybe_convert_to_log_scale(x, y)
|
|
1037
|
+
|
|
1038
|
+
# axnorm = self.axnorm
|
|
1039
|
+
# norm = kwargs.pop(
|
|
1040
|
+
# "norm",
|
|
1041
|
+
# mpl.colors.BoundaryNorm(np.linspace(0, 1, 11), 256, clip=True)
|
|
1042
|
+
# if axnorm in ("c", "r")
|
|
1043
|
+
# else None,
|
|
1044
|
+
# )
|
|
1045
|
+
|
|
1046
|
+
# if limit_color_norm:
|
|
1047
|
+
# self._limit_color_norm(norm)
|
|
1048
|
+
|
|
1049
|
+
# C = np.ma.masked_invalid(agg.values)
|
|
1050
|
+
# XX, YY = np.meshgrid(x, y)
|
|
1051
|
+
# pc = ax.pcolormesh(XX, YY, C, norm=norm, **kwargs)
|
|
1052
|
+
|
|
1053
|
+
# cbar_or_mappable = pc
|
|
1054
|
+
# if cbar:
|
|
1055
|
+
# if cbar_kwargs is None:
|
|
1056
|
+
# cbar_kwargs = dict()
|
|
1057
|
+
|
|
1058
|
+
# if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
1059
|
+
# cbar_kwargs["ax"] = ax
|
|
1060
|
+
|
|
1061
|
+
# # Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
|
|
1062
|
+
# cbar = self._make_cbar(pc, norm=norm, **cbar_kwargs)
|
|
1063
|
+
# cbar_or_mappable = cbar
|
|
1064
|
+
|
|
1065
|
+
# self._format_axis(ax)
|
|
1066
|
+
|
|
1067
|
+
# color_plot = self.data.loc[:, self.agg_axes].dropna().unique().size > 1
|
|
1068
|
+
# if (alpha_fcn is not None) and color_plot:
|
|
1069
|
+
# self.logger.warning(
|
|
1070
|
+
# "Make sure you verify alpha actually set. I don't yet trust this."
|
|
1071
|
+
# )
|
|
1072
|
+
# alpha_agg = self.agg(fcn=alpha_fcn)
|
|
1073
|
+
# alpha_agg = alpha_agg.unstack("x")
|
|
1074
|
+
# alpha_agg = np.ma.masked_invalid(alpha_agg.values.ravel())
|
|
1075
|
+
# # Feature scale then invert so smallest STD
|
|
1076
|
+
# # is most opaque.
|
|
1077
|
+
# alpha = 1 - mpl.colors.Normalize()(alpha_agg)
|
|
1078
|
+
# self.logger.warning("Scaling alpha filter as alpha**0.25")
|
|
1079
|
+
# alpha = alpha ** 0.25
|
|
1080
|
+
|
|
1081
|
+
# # Set masked values to zero. Otherwise, masked
|
|
1082
|
+
# # values are rendered as black.
|
|
1083
|
+
# alpha = alpha.filled(0)
|
|
1084
|
+
# # Must draw to initialize `facecolor`s
|
|
1085
|
+
# plt.draw()
|
|
1086
|
+
# # Remove `pc` from axis so we can redraw with std
|
|
1087
|
+
# # pc.remove()
|
|
1088
|
+
# colors = pc.get_facecolors()
|
|
1089
|
+
# colors[:, 3] = alpha
|
|
1090
|
+
# pc.set_facecolor(colors)
|
|
1091
|
+
# # ax.add_collection(pc)
|
|
1092
|
+
|
|
1093
|
+
# elif alpha_fcn is not None:
|
|
1094
|
+
# self.logger.warning("Ignoring `alpha_fcn` because plotting counts")
|
|
1095
|
+
|
|
1096
|
+
# return ax, cbar_or_mappable
|
|
1097
|
+
|
|
1098
|
+
# def get_border(self):
|
|
1099
|
+
# r"""Get the top and bottom edges of the plot.
|
|
1100
|
+
|
|
1101
|
+
# Returns
|
|
1102
|
+
# -------
|
|
1103
|
+
# border: namedtuple
|
|
1104
|
+
# Contains "top" and "bottom" fields, each with a :py:class:`pd.Series`.
|
|
1105
|
+
# """
|
|
1106
|
+
|
|
1107
|
+
# Border = namedtuple("Border", "top,bottom")
|
|
1108
|
+
# top = {}
|
|
1109
|
+
# bottom = {}
|
|
1110
|
+
# for x, v in self.agg().unstack("x").items():
|
|
1111
|
+
# yt = v.last_valid_index()
|
|
1112
|
+
# if yt is not None:
|
|
1113
|
+
# z = v.loc[yt]
|
|
1114
|
+
# top[(yt, x)] = z
|
|
1115
|
+
|
|
1116
|
+
# yb = v.first_valid_index()
|
|
1117
|
+
# if yb is not None:
|
|
1118
|
+
# z = v.loc[yb]
|
|
1119
|
+
# bottom[(yb, x)] = z
|
|
1120
|
+
|
|
1121
|
+
# top = pd.Series(top)
|
|
1122
|
+
# bottom = pd.Series(bottom)
|
|
1123
|
+
# for edge in (top, bottom):
|
|
1124
|
+
# edge.index.names = ["y", "x"]
|
|
1125
|
+
|
|
1126
|
+
# border = Border(top, bottom)
|
|
1127
|
+
# return border
|
|
1128
|
+
|
|
1129
|
+
# def _plot_one_edge(
|
|
1130
|
+
# self,
|
|
1131
|
+
# ax,
|
|
1132
|
+
# edge,
|
|
1133
|
+
# smooth=False,
|
|
1134
|
+
# sg_kwargs=None,
|
|
1135
|
+
# xlim=(None, None),
|
|
1136
|
+
# ylim=(None, None),
|
|
1137
|
+
# **kwargs,
|
|
1138
|
+
# ):
|
|
1139
|
+
# x = edge.index.get_level_values("x").mid
|
|
1140
|
+
# y = edge.index.get_level_values("y").mid
|
|
1141
|
+
|
|
1142
|
+
# if sg_kwargs is None:
|
|
1143
|
+
# sg_kwargs = dict()
|
|
1144
|
+
|
|
1145
|
+
# if smooth:
|
|
1146
|
+
# wlength = sg_kwargs.pop("window_length", int(np.floor(y.shape[0] / 10)))
|
|
1147
|
+
# polyorder = sg_kwargs.pop("polyorder", 3)
|
|
1148
|
+
|
|
1149
|
+
# if not wlength % 2:
|
|
1150
|
+
# wlength -= 1
|
|
1151
|
+
|
|
1152
|
+
# y = savgol_filter(y, wlength, polyorder, **sg_kwargs)
|
|
1153
|
+
|
|
1154
|
+
# if self.log.x:
|
|
1155
|
+
# x = 10.0 ** x
|
|
1156
|
+
# if self.log.y:
|
|
1157
|
+
# y = 10.0 ** y
|
|
1158
|
+
|
|
1159
|
+
# x0, x1 = xlim
|
|
1160
|
+
# y0, y1 = ylim
|
|
1161
|
+
|
|
1162
|
+
# tk = np.full_like(x, True, dtype=bool)
|
|
1163
|
+
# if x0 is not None:
|
|
1164
|
+
# tk = tk & (x0 <= x)
|
|
1165
|
+
# if x1 is not None:
|
|
1166
|
+
# tk = tk & (x <= x1)
|
|
1167
|
+
# if y0 is not None:
|
|
1168
|
+
# tk = tk & (y0 <= y)
|
|
1169
|
+
# if y1 is not None:
|
|
1170
|
+
# tk = tk & (y <= y1)
|
|
1171
|
+
|
|
1172
|
+
# # if (~tk).any():
|
|
1173
|
+
# x = x[tk]
|
|
1174
|
+
# y = y[tk]
|
|
1175
|
+
|
|
1176
|
+
# return ax.plot(x, y, **kwargs)
|
|
1177
|
+
|
|
1178
|
+
# def plot_edges(self, ax, smooth=True, sg_kwargs=None, **kwargs):
|
|
1179
|
+
# r"""Overplot the edges.
|
|
1180
|
+
|
|
1181
|
+
# Parameters
|
|
1182
|
+
# ----------
|
|
1183
|
+
# ax:
|
|
1184
|
+
# Axis on which to plot.
|
|
1185
|
+
# smooth: bool
|
|
1186
|
+
# If True, apply a Savitzky-Golay filter (:py:func:`scipy.signal.savgol_filter`)
|
|
1187
|
+
# to the y-values before plotting to smooth the curve.
|
|
1188
|
+
# sg_kwargs: dict, None
|
|
1189
|
+
# If not None, dict of kwargs passed to Savitzky-Golay filter. Also allows
|
|
1190
|
+
# for setting of `window_length` and `polyorder` as kwargs. They default to
|
|
1191
|
+
# 10\% of the number of observations (`window_length`) and 3 (`polyorder`).
|
|
1192
|
+
# Note that because `window_length` must be odd, if the 10\% value is even, we
|
|
1193
|
+
# take 1-window_length.
|
|
1194
|
+
# kwargs:
|
|
1195
|
+
# Passed to `ax.plot`
|
|
1196
|
+
# """
|
|
1197
|
+
|
|
1198
|
+
# top, bottom = self.get_border()
|
|
1199
|
+
|
|
1200
|
+
# color = kwargs.pop("color", "cyan")
|
|
1201
|
+
# label = kwargs.pop("label", None)
|
|
1202
|
+
# etop = self._plot_one_edge(
|
|
1203
|
+
# ax, top, smooth, sg_kwargs, color=color, label=label, **kwargs
|
|
1204
|
+
# )
|
|
1205
|
+
# ebottom = self._plot_one_edge(
|
|
1206
|
+
# ax, bottom, smooth, sg_kwargs, color=color, **kwargs
|
|
1207
|
+
# )
|
|
1208
|
+
|
|
1209
|
+
# return etop, ebottom
|
|
1210
|
+
|
|
1211
|
+
# def _get_contour_levels(self, levels):
|
|
1212
|
+
# if (levels is not None) or (self.axnorm is None):
|
|
1213
|
+
# pass
|
|
1214
|
+
|
|
1215
|
+
# elif (levels is None) and (self.axnorm == "t"):
|
|
1216
|
+
# levels = [0.01, 0.1, 0.3, 0.7, 0.99]
|
|
1217
|
+
|
|
1218
|
+
# elif (levels is None) and (self.axnorm == "d"):
|
|
1219
|
+
# levels = [3e-5, 1e-4, 3e-4, 1e-3, 1.7e-3, 2.3e-3]
|
|
1220
|
+
|
|
1221
|
+
# elif (levels is None) and (self.axnorm in ["r", "c"]):
|
|
1222
|
+
# levels = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
|
1223
|
+
|
|
1224
|
+
# else:
|
|
1225
|
+
# raise ValueError(
|
|
1226
|
+
# f"Unrecognized axis normalization {self.axnorm} for default levels."
|
|
1227
|
+
# )
|
|
1228
|
+
|
|
1229
|
+
# return levels
|
|
1230
|
+
|
|
1231
|
+
# def _verify_contour_passthrough_kwargs(
|
|
1232
|
+
# self, ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
1233
|
+
# ):
|
|
1234
|
+
# if clabel_kwargs is None:
|
|
1235
|
+
# clabel_kwargs = dict()
|
|
1236
|
+
# if edges_kwargs is None:
|
|
1237
|
+
# edges_kwargs = dict()
|
|
1238
|
+
# if cbar_kwargs is None:
|
|
1239
|
+
# cbar_kwargs = dict()
|
|
1240
|
+
# if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
1241
|
+
# cbar_kwargs["ax"] = ax
|
|
1242
|
+
|
|
1243
|
+
# return clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
1244
|
+
|
|
1245
|
+
# def plot_contours(
|
|
1246
|
+
# self,
|
|
1247
|
+
# ax=None,
|
|
1248
|
+
# label_levels=True,
|
|
1249
|
+
# cbar=True,
|
|
1250
|
+
# limit_color_norm=False,
|
|
1251
|
+
# cbar_kwargs=None,
|
|
1252
|
+
# fcn=None,
|
|
1253
|
+
# plot_edges=True,
|
|
1254
|
+
# edges_kwargs=None,
|
|
1255
|
+
# clabel_kwargs=None,
|
|
1256
|
+
# skip_max_clbl=True,
|
|
1257
|
+
# use_contourf=False,
|
|
1258
|
+
# gaussian_filter_std=0,
|
|
1259
|
+
# gaussian_filter_kwargs=None,
|
|
1260
|
+
# **kwargs,
|
|
1261
|
+
# ):
|
|
1262
|
+
# f"""Make a contour plot on `ax` using `ax.contour`.
|
|
1263
|
+
|
|
1264
|
+
# Paremeters
|
|
1265
|
+
# ----------
|
|
1266
|
+
# ax: mpl.axes.Axes, None
|
|
1267
|
+
# If None, create an `Axes` instance from `plt.subplots`.
|
|
1268
|
+
# label_levels: bool
|
|
1269
|
+
# If True, add labels to contours with `ax.clabel`.
|
|
1270
|
+
# cbar: bool
|
|
1271
|
+
# If True, create color bar with `labels.z`.
|
|
1272
|
+
# limit_color_norm: bool
|
|
1273
|
+
# If True, limit the color range to 0.001 and 0.999 percentile range
|
|
1274
|
+
# of the z-value, count or otherwise.
|
|
1275
|
+
# cbar_kwargs: dict, None
|
|
1276
|
+
# If not None, kwargs passed to `self._make_cbar`.
|
|
1277
|
+
# fcn: FunctionType, None
|
|
1278
|
+
# Aggregation function. If None, automatically select in :py:meth:`agg`.
|
|
1279
|
+
# plot_edges: bool
|
|
1280
|
+
# If True, plot the smoothed, extreme edges of the 2D histogram.
|
|
1281
|
+
# edges_kwargs: None, dict
|
|
1282
|
+
# Passed to {self.plot_edges!s}.
|
|
1283
|
+
# clabel_kwargs: None, dict
|
|
1284
|
+
# If not None, dictionary of kwargs passed to `ax.clabel`.
|
|
1285
|
+
# skip_max_clbl: bool
|
|
1286
|
+
# If True, don't label the maximum contour. Primarily used when the maximum
|
|
1287
|
+
# contour is, effectively, a point.
|
|
1288
|
+
# maximum_color:
|
|
1289
|
+
# The color for the maximum of the PDF.
|
|
1290
|
+
# use_contourf: bool
|
|
1291
|
+
# If True, use `ax.contourf`. Else use `ax.contour`.
|
|
1292
|
+
# gaussian_filter_std: int
|
|
1293
|
+
# If > 0, apply `scipy.ndimage.gaussian_filter` to the z-values using the
|
|
1294
|
+
# standard deviation specified by `gaussian_filter_std`.
|
|
1295
|
+
# gaussian_filter_kwargs: None, dict
|
|
1296
|
+
# If not None and gaussian_filter_std > 0, passed to :py:meth:`scipy.ndimage.gaussian_filter`
|
|
1297
|
+
# kwargs:
|
|
1298
|
+
# Passed to :py:meth:`ax.pcolormesh`.
|
|
1299
|
+
# If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
|
|
1300
|
+
# """
|
|
1301
|
+
# levels = kwargs.pop("levels", None)
|
|
1302
|
+
# cmap = kwargs.pop("cmap", None)
|
|
1303
|
+
# norm = kwargs.pop(
|
|
1304
|
+
# "norm",
|
|
1305
|
+
# mpl.colors.BoundaryNorm(np.linspace(0, 1, 11), 256, clip=True)
|
|
1306
|
+
# if self.axnorm in ("c", "r")
|
|
1307
|
+
# else None,
|
|
1308
|
+
# )
|
|
1309
|
+
# linestyles = kwargs.pop(
|
|
1310
|
+
# "linestyles",
|
|
1311
|
+
# [
|
|
1312
|
+
# "-",
|
|
1313
|
+
# ":",
|
|
1314
|
+
# "--",
|
|
1315
|
+
# (0, (7, 3, 1, 3, 1, 3, 1, 3, 1, 3)),
|
|
1316
|
+
# "--",
|
|
1317
|
+
# ":",
|
|
1318
|
+
# "-",
|
|
1319
|
+
# (0, (7, 3, 1, 3, 1, 3)),
|
|
1320
|
+
# ],
|
|
1321
|
+
# )
|
|
1322
|
+
|
|
1323
|
+
# if ax is None:
|
|
1324
|
+
# fig, ax = plt.subplots()
|
|
1325
|
+
|
|
1326
|
+
# clabel_kwargs, edges_kwargs, cbar_kwargs = self._verify_contour_passthrough_kwargs(
|
|
1327
|
+
# ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
1328
|
+
# )
|
|
1329
|
+
|
|
1330
|
+
# inline = clabel_kwargs.pop("inline", True)
|
|
1331
|
+
# inline_spacing = clabel_kwargs.pop("inline_spacing", -3)
|
|
1332
|
+
# fmt = clabel_kwargs.pop("fmt", "%s")
|
|
1333
|
+
|
|
1334
|
+
# agg = self.agg(fcn=fcn).unstack("x")
|
|
1335
|
+
# x = self.intervals["x"].mid
|
|
1336
|
+
# y = self.intervals["y"].mid
|
|
1337
|
+
|
|
1338
|
+
# # assert x.size == agg.shape[1]
|
|
1339
|
+
# # assert y.size == agg.shape[0]
|
|
1340
|
+
|
|
1341
|
+
# # HACK: Works around `gb.agg(observed=False)` pandas bug. (GH32381)
|
|
1342
|
+
# if x.size != agg.shape[1]:
|
|
1343
|
+
# # agg = agg.reindex(columns=self.intervals["x"])
|
|
1344
|
+
# agg = agg.reindex(columns=self.categoricals["x"])
|
|
1345
|
+
# if y.size != agg.shape[0]:
|
|
1346
|
+
# # agg = agg.reindex(index=self.intervals["y"])
|
|
1347
|
+
# agg = agg.reindex(index=self.categoricals["y"])
|
|
1348
|
+
|
|
1349
|
+
# x, y = self._maybe_convert_to_log_scale(x, y)
|
|
1350
|
+
|
|
1351
|
+
# XX, YY = np.meshgrid(x, y)
|
|
1352
|
+
|
|
1353
|
+
# C = agg.values
|
|
1354
|
+
# if gaussian_filter_std:
|
|
1355
|
+
# from scipy.ndimage import gaussian_filter
|
|
1356
|
+
|
|
1357
|
+
# if gaussian_filter_kwargs is None:
|
|
1358
|
+
# gaussian_filter_kwargs = dict()
|
|
1359
|
+
|
|
1360
|
+
# C = gaussian_filter(C, gaussian_filter_std, **gaussian_filter_kwargs)
|
|
1361
|
+
|
|
1362
|
+
# C = np.ma.masked_invalid(C)
|
|
1363
|
+
|
|
1364
|
+
# assert XX.shape == C.shape
|
|
1365
|
+
# assert YY.shape == C.shape
|
|
1366
|
+
|
|
1367
|
+
# class nf(float):
|
|
1368
|
+
# # Source: https://matplotlib.org/3.1.0/gallery/images_contours_and_fields/contour_label_demo.html
|
|
1369
|
+
# # Define a class that forces representation of float to look a certain way
|
|
1370
|
+
# # This remove trailing zero so '1.0' becomes '1'
|
|
1371
|
+
# def __repr__(self):
|
|
1372
|
+
# return str(self).rstrip("0")
|
|
1373
|
+
|
|
1374
|
+
# levels = self._get_contour_levels(levels)
|
|
1375
|
+
|
|
1376
|
+
# contour_fcn = ax.contour
|
|
1377
|
+
# if use_contourf:
|
|
1378
|
+
# contour_fcn = ax.contourf
|
|
1379
|
+
|
|
1380
|
+
# if levels is None:
|
|
1381
|
+
# args = [XX, YY, C]
|
|
1382
|
+
# else:
|
|
1383
|
+
# args = [XX, YY, C, levels]
|
|
1384
|
+
|
|
1385
|
+
# qset = contour_fcn(*args, linestyles=linestyles, cmap=cmap, norm=norm, **kwargs)
|
|
1386
|
+
|
|
1387
|
+
# try:
|
|
1388
|
+
# args = (qset, levels[:-1] if skip_max_clbl else levels)
|
|
1389
|
+
# except TypeError:
|
|
1390
|
+
# # None can't be subscripted.
|
|
1391
|
+
# args = (qset,)
|
|
1392
|
+
|
|
1393
|
+
# lbls = None
|
|
1394
|
+
# if label_levels:
|
|
1395
|
+
# qset.levels = [nf(level) for level in qset.levels]
|
|
1396
|
+
# lbls = ax.clabel(
|
|
1397
|
+
# *args, inline=inline, inline_spacing=inline_spacing, fmt=fmt
|
|
1398
|
+
# )
|
|
1399
|
+
|
|
1400
|
+
# if plot_edges:
|
|
1401
|
+
# etop, ebottom = self.plot_edges(ax, **edges_kwargs)
|
|
1402
|
+
|
|
1403
|
+
# cbar_or_mappable = qset
|
|
1404
|
+
# if cbar:
|
|
1405
|
+
# # Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
|
|
1406
|
+
# cbar = self._make_cbar(qset, norm=norm, **cbar_kwargs)
|
|
1407
|
+
# cbar_or_mappable = cbar
|
|
1408
|
+
|
|
1409
|
+
# self._format_axis(ax)
|
|
1410
|
+
|
|
1411
|
+
# return ax, lbls, cbar_or_mappable, qset
|
|
1412
|
+
|
|
1413
|
+
# def project_1d(self, axis, only_plotted=True, project_counts=False, **kwargs):
|
|
1414
|
+
# f"""Make a `Hist1D` from the data stored in this `His2D`.
|
|
1415
|
+
|
|
1416
|
+
# Parameters
|
|
1417
|
+
# ----------
|
|
1418
|
+
# axis: str
|
|
1419
|
+
# "x" or "y", specifying the axis to project into 1D.
|
|
1420
|
+
# only_plotted: bool
|
|
1421
|
+
# If True, only pass data that appears in the {self.__class__.__name__} plot
|
|
1422
|
+
# to the :py:class:`Hist1D`.
|
|
1423
|
+
# project_counts: bool
|
|
1424
|
+
# If True, only send the variable plotted along `axis` to :py:class:`Hist1D`.
|
|
1425
|
+
# Otherwise, send both axes (but not z-values).
|
|
1426
|
+
# kwargs:
|
|
1427
|
+
# Passed to `Hist1D`. Primarily to allow specifying `bin_precision`.
|
|
1428
|
+
|
|
1429
|
+
# Returns
|
|
1430
|
+
# -------
|
|
1431
|
+
# h1: :py:class:`Hist1D`
|
|
1432
|
+
# """
|
|
1433
|
+
# axis = axis.lower()
|
|
1434
|
+
# assert axis in ("x", "y")
|
|
1435
|
+
|
|
1436
|
+
# data = self.data
|
|
1437
|
+
|
|
1438
|
+
# if data.loc[:, "z"].unique().size >= 2:
|
|
1439
|
+
# # Either all 1 or 1 and NaN.
|
|
1440
|
+
# other = "z"
|
|
1441
|
+
# else:
|
|
1442
|
+
# possible_axes = {"x", "y"}
|
|
1443
|
+
# possible_axes.remove(axis)
|
|
1444
|
+
# other = possible_axes.pop()
|
|
1445
|
+
|
|
1446
|
+
# logx = self.log._asdict()[axis]
|
|
1447
|
+
# x = self.data.loc[:, axis]
|
|
1448
|
+
# if logx:
|
|
1449
|
+
# # Need to convert back to regular from log-space for data setting.
|
|
1450
|
+
# x = 10.0 ** x
|
|
1451
|
+
|
|
1452
|
+
# y = self.data.loc[:, other] if not project_counts else None
|
|
1453
|
+
# logy = False # Defined b/c project_counts option.
|
|
1454
|
+
# if y is not None:
|
|
1455
|
+
# # Only select y-values plotted.
|
|
1456
|
+
# logy = self.log._asdict()[other]
|
|
1457
|
+
# yedges = self.edges[other].values
|
|
1458
|
+
# y = y.where((yedges[0] <= y) & (y <= yedges[-1]))
|
|
1459
|
+
# if logy:
|
|
1460
|
+
# y = 10.0 ** y
|
|
1461
|
+
|
|
1462
|
+
# if only_plotted:
|
|
1463
|
+
# tk = self.get_plotted_data_boolean_series()
|
|
1464
|
+
# x = x.loc[tk]
|
|
1465
|
+
# if y is not None:
|
|
1466
|
+
# y = y.loc[tk]
|
|
1467
|
+
|
|
1468
|
+
# h1 = Hist1D(
|
|
1469
|
+
# x,
|
|
1470
|
+
# y=y,
|
|
1471
|
+
# logx=logx,
|
|
1472
|
+
# clip_data=False, # Any clipping will be addressed by bins.
|
|
1473
|
+
# nbins=self.edges[axis].values,
|
|
1474
|
+
# **kwargs,
|
|
1475
|
+
# )
|
|
1476
|
+
|
|
1477
|
+
# h1.set_log(y=logy) # Need to propagate logy.
|
|
1478
|
+
# h1.set_labels(x=self.labels._asdict()[axis])
|
|
1479
|
+
# if not project_counts:
|
|
1480
|
+
# h1.set_labels(y=self.labels._asdict()[other])
|
|
1481
|
+
|
|
1482
|
+
# h1.set_path("auto")
|
|
1483
|
+
|
|
1484
|
+
# return h1
|
|
1485
|
+
|
|
1486
|
+
|
|
1487
|
+
# class GridHist2D(object):
|
|
1488
|
+
# r"""A grid of 2D heatmaps separating the data based on a categorical value.
|
|
1489
|
+
|
|
1490
|
+
# Properties
|
|
1491
|
+
# ----------
|
|
1492
|
+
# data: pd.DataFrame
|
|
1493
|
+
|
|
1494
|
+
# axnorm: str or None
|
|
1495
|
+
# Specify if column, row, total, or density normalization should be used.
|
|
1496
|
+
# log: namedtuple
|
|
1497
|
+
# Contains booleans identifying axes to log-scale.
|
|
1498
|
+
# nbins: int or str
|
|
1499
|
+
# Pass to `np.histogram_bin_edges` or `astropy.stats.knuth_bin_width`
|
|
1500
|
+
# depending on the input.
|
|
1501
|
+
# labels: namedtuple
|
|
1502
|
+
# Contains axis labels. Recomend using `labels.TeXlabel` so
|
|
1503
|
+
# grouped: pd.Groupeby
|
|
1504
|
+
# The data grouped by the categorical.
|
|
1505
|
+
# hist2ds: pd.Series
|
|
1506
|
+
# The `Hist2D` objects created for each axis. Index is the unique
|
|
1507
|
+
# categorical values.
|
|
1508
|
+
# fig: mpl.figure.Figure
|
|
1509
|
+
# The figure upon which the axes are placed.
|
|
1510
|
+
# axes: pd.Series
|
|
1511
|
+
# Contains the mpl axes upon which plots are drawn. Index should be
|
|
1512
|
+
# identical to `hist2ds`.
|
|
1513
|
+
# cbars: pd.Series
|
|
1514
|
+
# Contains the colorbar instances. Similar to `hist2ds` and `axes`.
|
|
1515
|
+
# cnorms: mpl.color.Normalize or pd.Series
|
|
1516
|
+
# mpl.colors.Normalize instance or a pd.Series of them with one for
|
|
1517
|
+
# each unique categorical value.
|
|
1518
|
+
# use_gs: bool
|
|
1519
|
+
# An attempt at the code is written, but not implemented because some
|
|
1520
|
+
# minor details need to be worked out. Ideally, if True, use a single
|
|
1521
|
+
# colorbar for the entire grid.
|
|
1522
|
+
|
|
1523
|
+
# Methods
|
|
1524
|
+
# -------
|
|
1525
|
+
# set_<>: setters
|
|
1526
|
+
# For data, nbins, axnorm, log, labels, cnorms.
|
|
1527
|
+
# make_h2ds:
|
|
1528
|
+
# Make the `Hist2D` objects.
|
|
1529
|
+
# make_plots:
|
|
1530
|
+
# Make the `Hist2D` plots.
|
|
1531
|
+
# """
|
|
1532
|
+
|
|
1533
|
+
# def __init__(self, x, y, cat, z=None):
|
|
1534
|
+
# r"""Create 2D heatmaps of x, y, and optional z data in a grid for which
|
|
1535
|
+
# each unique element in `cat` specifies one plot.
|
|
1536
|
+
|
|
1537
|
+
# Parameters
|
|
1538
|
+
# ----------
|
|
1539
|
+
# x, y, z: pd.Series or np.array
|
|
1540
|
+
# The data to aggregate. pd.Series is prefered.
|
|
1541
|
+
# cat: pd.Categorial
|
|
1542
|
+
# The categorial series used to create subsets of the data for each
|
|
1543
|
+
# grid element.
|
|
1544
|
+
|
|
1545
|
+
# """
|
|
1546
|
+
# self.set_nbins(101)
|
|
1547
|
+
# self.set_axnorm(None)
|
|
1548
|
+
# self.set_log(x=False, y=False)
|
|
1549
|
+
# self.set_data(x, y, cat, z)
|
|
1550
|
+
# self._labels = base.AxesLabels("x", "y") # Unsure how else to set defaults.
|
|
1551
|
+
# self.set_cnorms(None)
|
|
1552
|
+
|
|
1553
|
+
# @property
|
|
1554
|
+
# def data(self):
|
|
1555
|
+
# return self._data
|
|
1556
|
+
|
|
1557
|
+
# @property
|
|
1558
|
+
# def axnorm(self):
|
|
1559
|
+
# r"""Axis normalization."""
|
|
1560
|
+
# return self._axnorm
|
|
1561
|
+
|
|
1562
|
+
# @property
|
|
1563
|
+
# def logger(self):
|
|
1564
|
+
# return self._log
|
|
1565
|
+
|
|
1566
|
+
# @property
|
|
1567
|
+
# def nbins(self):
|
|
1568
|
+
# return self._nbins
|
|
1569
|
+
|
|
1570
|
+
# @property
|
|
1571
|
+
# def log(self):
|
|
1572
|
+
# r"""LogAxes booleans.
|
|
1573
|
+
# """
|
|
1574
|
+
# return self._log
|
|
1575
|
+
|
|
1576
|
+
# @property
|
|
1577
|
+
# def labels(self):
|
|
1578
|
+
# return self._labels
|
|
1579
|
+
|
|
1580
|
+
# @property
|
|
1581
|
+
# def grouped(self):
|
|
1582
|
+
# return self.data.groupby("cat")
|
|
1583
|
+
|
|
1584
|
+
# @property
|
|
1585
|
+
# def hist2ds(self):
|
|
1586
|
+
# try:
|
|
1587
|
+
# return self._h2ds
|
|
1588
|
+
# except AttributeError:
|
|
1589
|
+
# return self.make_h2ds()
|
|
1590
|
+
|
|
1591
|
+
# @property
|
|
1592
|
+
# def fig(self):
|
|
1593
|
+
# try:
|
|
1594
|
+
# return self._fig
|
|
1595
|
+
# except AttributeError:
|
|
1596
|
+
# return self.init_fig()[0]
|
|
1597
|
+
|
|
1598
|
+
# @property
|
|
1599
|
+
# def axes(self):
|
|
1600
|
+
# try:
|
|
1601
|
+
# return self._axes
|
|
1602
|
+
# except AttributeError:
|
|
1603
|
+
# return self.init_fig()[1]
|
|
1604
|
+
|
|
1605
|
+
# @property
|
|
1606
|
+
# def cbars(self):
|
|
1607
|
+
# return self._cbars
|
|
1608
|
+
|
|
1609
|
+
# @property
|
|
1610
|
+
# def cnorms(self):
|
|
1611
|
+
# r"""Color normalization (mpl.colors.Normalize instance)."""
|
|
1612
|
+
# return self._cnorms
|
|
1613
|
+
|
|
1614
|
+
# @property
|
|
1615
|
+
# def use_gs(self):
|
|
1616
|
+
# return self._use_gs
|
|
1617
|
+
|
|
1618
|
+
# @property
|
|
1619
|
+
# def path(self):
|
|
1620
|
+
# raise NotImplementedError("Just haven't sat down to write this.")
|
|
1621
|
+
|
|
1622
|
+
# def _init_logger(self):
|
|
1623
|
+
# self._logger = logging.getLogger(
|
|
1624
|
+
# "{}.{}".format(__name__, self.__class__.__name__)
|
|
1625
|
+
# )
|
|
1626
|
+
|
|
1627
|
+
# def set_nbins(self, new):
|
|
1628
|
+
# self._nbins = new
|
|
1629
|
+
|
|
1630
|
+
# def set_axnorm(self, new):
|
|
1631
|
+
# self._axnorm = new
|
|
1632
|
+
|
|
1633
|
+
# def set_cnorms(self, new):
|
|
1634
|
+
# self._cnorms = new
|
|
1635
|
+
|
|
1636
|
+
# def set_log(self, x=None, y=None):
|
|
1637
|
+
# if x is None:
|
|
1638
|
+
# x = self.log.x
|
|
1639
|
+
# if y is None:
|
|
1640
|
+
# y = self.log.y
|
|
1641
|
+
# log = base.LogAxes(x, y)
|
|
1642
|
+
# self._log = log
|
|
1643
|
+
|
|
1644
|
+
# def set_data(self, x, y, cat, z):
|
|
1645
|
+
# data = {"x": x, "y": y, "cat": cat}
|
|
1646
|
+
# if z is not None:
|
|
1647
|
+
# data["z"] = z
|
|
1648
|
+
# data = pd.concat(data, axis=1)
|
|
1649
|
+
# self._data = data
|
|
1650
|
+
|
|
1651
|
+
# def set_labels(self, **kwargs):
|
|
1652
|
+
# r"""Set or update x, y, or z labels. Any label not specified in kwargs
|
|
1653
|
+
# is propagated from `self.labels.<x, y, or z>`.
|
|
1654
|
+
# """
|
|
1655
|
+
|
|
1656
|
+
# x = kwargs.pop("x", self.labels.x)
|
|
1657
|
+
# y = kwargs.pop("y", self.labels.y)
|
|
1658
|
+
# z = kwargs.pop("z", self.labels.z)
|
|
1659
|
+
|
|
1660
|
+
# if len(kwargs.keys()):
|
|
1661
|
+
# raise KeyError("Unexpected kwarg: {}".format(kwargs.keys()))
|
|
1662
|
+
|
|
1663
|
+
# self._labels = base.AxesLabels(x, y, z)
|
|
1664
|
+
|
|
1665
|
+
# def set_fig_axes(self, fig, axes, use_gs=False):
|
|
1666
|
+
# self._set_fig(fig)
|
|
1667
|
+
# self._set_axes(axes)
|
|
1668
|
+
# self._use_gs = bool(use_gs)
|
|
1669
|
+
|
|
1670
|
+
# def _set_fig(self, new):
|
|
1671
|
+
# self._fig = new
|
|
1672
|
+
|
|
1673
|
+
# def _set_axes(self, new):
|
|
1674
|
+
# if new.size != len(self.grouped.groups.keys()) + 1:
|
|
1675
|
+
# msg = "Number of axes must match number of Categoricals + 1 for All."
|
|
1676
|
+
# raise ValueError(msg)
|
|
1677
|
+
|
|
1678
|
+
# keys = ["All"] + sorted(self.grouped.groups.keys())
|
|
1679
|
+
# axes = pd.Series(new.ravel(), index=pd.CategoricalIndex(keys))
|
|
1680
|
+
|
|
1681
|
+
# self._axes = axes
|
|
1682
|
+
|
|
1683
|
+
# def init_fig(self, use_gs=False, layout="auto", scale=1.5):
|
|
1684
|
+
|
|
1685
|
+
# if layout == "auto":
|
|
1686
|
+
# raise NotImplementedError(
|
|
1687
|
+
# """Need some densest packing algorithm I haven't
|
|
1688
|
+
# found yet"""
|
|
1689
|
+
# )
|
|
1690
|
+
|
|
1691
|
+
# assert len(layout) == 2
|
|
1692
|
+
# nrows, ncols = layout
|
|
1693
|
+
|
|
1694
|
+
# if use_gs:
|
|
1695
|
+
# raise NotImplementedError(
|
|
1696
|
+
# """Unsure how to consistently store single cax or
|
|
1697
|
+
# deal with variable layouts."""
|
|
1698
|
+
# )
|
|
1699
|
+
# fig = plt.figure(figsize=np.array([8, 6]) * scale)
|
|
1700
|
+
|
|
1701
|
+
# gs = mpl.gridspec.GridSpec(
|
|
1702
|
+
# 3,
|
|
1703
|
+
# 5,
|
|
1704
|
+
# width_ratios=[1, 1, 1, 1, 0.1],
|
|
1705
|
+
# height_ratios=[1, 1, 1],
|
|
1706
|
+
# hspace=0,
|
|
1707
|
+
# wspace=0,
|
|
1708
|
+
# figure=fig,
|
|
1709
|
+
# )
|
|
1710
|
+
|
|
1711
|
+
# axes = np.array(12 * [np.nan], dtype=object).reshape(3, 4)
|
|
1712
|
+
# sharer = None
|
|
1713
|
+
# for i in np.arange(0, 3):
|
|
1714
|
+
# for j in np.arange(0, 4):
|
|
1715
|
+
# if i and j:
|
|
1716
|
+
# a = plt.subplot(gs[i, j], sharex=sharer, sharey=sharer)
|
|
1717
|
+
# else:
|
|
1718
|
+
# a = plt.subplot(gs[i, j])
|
|
1719
|
+
# sharer = a
|
|
1720
|
+
# axes[i, j] = a
|
|
1721
|
+
|
|
1722
|
+
# others = axes.ravel().tolist()
|
|
1723
|
+
# a0 = others.pop(8)
|
|
1724
|
+
# a0.get_shared_x_axes().join(a0, *others)
|
|
1725
|
+
# a0.get_shared_y_axes().join(a0, *others)
|
|
1726
|
+
|
|
1727
|
+
# for ax in axes[:-1, 1:].ravel():
|
|
1728
|
+
# # All off
|
|
1729
|
+
# ax.tick_params(labelbottom=False, labelleft=False)
|
|
1730
|
+
# ax.xaxis.label.set_visible(False)
|
|
1731
|
+
# ax.yaxis.label.set_visible(False)
|
|
1732
|
+
# for ax in axes[:-1, 0].ravel():
|
|
1733
|
+
# # 0th column x-labels off.
|
|
1734
|
+
# ax.tick_params(which="x", labelbottom=False)
|
|
1735
|
+
# ax.xaxis.label.set_visible(False)
|
|
1736
|
+
# for ax in axes[-1, 1:].ravel():
|
|
1737
|
+
# # Nth row y-labels off.
|
|
1738
|
+
# ax.tick_params(which="y", labelleft=False)
|
|
1739
|
+
# ax.yaxis.label.set_visible(False)
|
|
1740
|
+
|
|
1741
|
+
# # cax = plt.subplot(gs[:, -1])
|
|
1742
|
+
|
|
1743
|
+
# else:
|
|
1744
|
+
# fig, axes = tools.subplots(
|
|
1745
|
+
# nrows=nrows, ncols=ncols, scale_width=scale, scale_height=scale
|
|
1746
|
+
# )
|
|
1747
|
+
# # cax = None
|
|
1748
|
+
|
|
1749
|
+
# self.set_fig_axes(fig, axes, use_gs)
|
|
1750
|
+
# return fig, axes
|
|
1751
|
+
|
|
1752
|
+
# def _build_one_hist2d(self, x, y, z):
|
|
1753
|
+
# h2d = Hist2D(
|
|
1754
|
+
# x,
|
|
1755
|
+
# y,
|
|
1756
|
+
# z=z,
|
|
1757
|
+
# logx=self.log.x,
|
|
1758
|
+
# logy=self.log.y,
|
|
1759
|
+
# clip_data=False,
|
|
1760
|
+
# nbins=self.nbins,
|
|
1761
|
+
# )
|
|
1762
|
+
# h2d.set_axnorm(self.axnorm)
|
|
1763
|
+
|
|
1764
|
+
# xlbl, ylbl, zlbl = self.labels.x, self.labels.y, self.labels.z
|
|
1765
|
+
# h2d.set_labels(x=xlbl, y=ylbl, z=zlbl)
|
|
1766
|
+
|
|
1767
|
+
# return h2d
|
|
1768
|
+
|
|
1769
|
+
# def make_h2ds(self):
|
|
1770
|
+
# grouped = self.grouped
|
|
1771
|
+
|
|
1772
|
+
# # Build case that doesn't include subgroups.
|
|
1773
|
+
|
|
1774
|
+
# x = self.data.loc[:, "x"]
|
|
1775
|
+
# y = self.data.loc[:, "y"]
|
|
1776
|
+
# try:
|
|
1777
|
+
# z = self.data.loc[:, "z"]
|
|
1778
|
+
# except KeyError:
|
|
1779
|
+
# z = None
|
|
1780
|
+
|
|
1781
|
+
# hall = self._build_one_hist2d(x, y, z)
|
|
1782
|
+
|
|
1783
|
+
# h2ds = {"All": hall}
|
|
1784
|
+
# for k, g in grouped:
|
|
1785
|
+
# x = g.loc[:, "x"]
|
|
1786
|
+
# y = g.loc[:, "y"]
|
|
1787
|
+
# try:
|
|
1788
|
+
# z = g.loc[:, "z"]
|
|
1789
|
+
# except KeyError:
|
|
1790
|
+
# z = None
|
|
1791
|
+
|
|
1792
|
+
# h2ds[k] = self._build_one_hist2d(x, y, z)
|
|
1793
|
+
|
|
1794
|
+
# h2ds = pd.Series(h2ds)
|
|
1795
|
+
# self._h2ds = h2ds
|
|
1796
|
+
# return h2ds
|
|
1797
|
+
|
|
1798
|
+
# @staticmethod
|
|
1799
|
+
# def _make_axis_text_label(key):
|
|
1800
|
+
# r"""Format the `key` identifying the Categorial group for this axis. To modify,
|
|
1801
|
+
# sublcass `GridHist2D` and redefine this staticmethod.
|
|
1802
|
+
# """
|
|
1803
|
+
# return key
|
|
1804
|
+
|
|
1805
|
+
# def _format_axes(self):
|
|
1806
|
+
# axes = self.axes
|
|
1807
|
+
# for k, ax in axes.items():
|
|
1808
|
+
# lbl = self._make_axis_text_label(k)
|
|
1809
|
+
# ax.text(
|
|
1810
|
+
# 0.025,
|
|
1811
|
+
# 0.95,
|
|
1812
|
+
# lbl,
|
|
1813
|
+
# transform=ax.transAxes,
|
|
1814
|
+
# va="top",
|
|
1815
|
+
# fontdict={"color": "k"},
|
|
1816
|
+
# bbox={"color": "wheat"},
|
|
1817
|
+
# )
|
|
1818
|
+
|
|
1819
|
+
# # ax.set_xlim(-1, 1)
|
|
1820
|
+
# # ax.set_ylim(-1, 1)
|
|
1821
|
+
|
|
1822
|
+
# def make_plots(self, **kwargs):
|
|
1823
|
+
# h2ds = self.hist2ds
|
|
1824
|
+
# axes = self.axes
|
|
1825
|
+
|
|
1826
|
+
# cbars = {}
|
|
1827
|
+
# cnorms = self.cnorms
|
|
1828
|
+
# for k, h2d in h2ds.items():
|
|
1829
|
+
# if isinstance(cnorms, mpl.colors.Normalize) or cnorms is None:
|
|
1830
|
+
# cnorm = cnorms
|
|
1831
|
+
# else:
|
|
1832
|
+
# cnorm = cnorms.loc[k]
|
|
1833
|
+
|
|
1834
|
+
# ax = axes.loc[k]
|
|
1835
|
+
# ax, cbar = h2d.make_plot(ax=ax, norm=cnorm, **kwargs)
|
|
1836
|
+
# if not self.use_gs:
|
|
1837
|
+
# cbars[k] = cbar
|
|
1838
|
+
# else:
|
|
1839
|
+
# raise NotImplementedError(
|
|
1840
|
+
# "Unsure how to handle `use_gs == True` for color bars."
|
|
1841
|
+
# )
|
|
1842
|
+
# cbars = pd.Series(cbars)
|
|
1843
|
+
|
|
1844
|
+
# self._format_axes()
|
|
1845
|
+
# self._cbars = cbars
|