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.

Files changed (412) hide show
  1. plans/.velocity/metrics.json +96 -0
  2. plans/0-overview-template.md +268 -0
  3. plans/N-phase-template.md +106 -0
  4. plans/PLAN_AUDIT_SUMMARY.md +173 -0
  5. plans/TEMPLATE-USAGE-GUIDE.md +198 -0
  6. plans/__init__.py +1 -0
  7. plans/abandoned/compaction-agent-system/0-Overview.md +123 -0
  8. plans/abandoned/compaction-agent-system/agents-index-update-plan.md +109 -0
  9. plans/abandoned/compaction-agent-system/compacted_state.md +85 -0
  10. plans/abandoned/compaction-agent-system/implementation-plan.md +107 -0
  11. plans/abandoned/compaction-agent-system/system-validation-report.md +159 -0
  12. plans/abandoned/compaction-agent-system/usage-guide.md +210 -0
  13. plans/abandoned/hook-system-enhancement/0-Overview.md +214 -0
  14. plans/abandoned/hook-system-enhancement/1-Phase1-Core-Infrastructure.md +313 -0
  15. plans/abandoned/hook-system-enhancement/2-Phase2-Intelligent-Testing.md +385 -0
  16. plans/abandoned/hook-system-enhancement/3-Phase3-Physics-Validation.md +444 -0
  17. plans/abandoned/hook-system-enhancement/4-Phase4-Performance-Monitoring.md +458 -0
  18. plans/abandoned/hook-system-enhancement/5-Phase5-Developer-Experience.md +532 -0
  19. plans/abandoned/hook-system-enhancement/6-Implementation-Timeline.md +274 -0
  20. plans/abandoned/hook-system-enhancement/7-Risk-Management.md +376 -0
  21. plans/abandoned/hook-system-enhancement/8-Testing-Strategy.md +579 -0
  22. plans/abandoned/readthedocs-automation/0-Overview.md +247 -0
  23. plans/abandoned/readthedocs-automation/1-Emergency-Documentation-Fixes.md +270 -0
  24. plans/abandoned/readthedocs-automation/2-Template-System-Enhancement.md +811 -0
  25. plans/abandoned/readthedocs-automation/3-Quality-Audit-ReadTheDocs-Integration.md +844 -0
  26. plans/abandoned/readthedocs-automation/4-Plan-Consolidation-Cleanup.md +632 -0
  27. plans/abandoned/readthedocs-automation/9-Closeout.md +207 -0
  28. plans/abandoned/readthedocs-automation/ABANDONMENT_REASON.md +72 -0
  29. plans/cicd-architecture-redesign/0-Overview.md +193 -0
  30. plans/cicd-architecture-redesign/1-Workflow-Creation.md +103 -0
  31. plans/cicd-architecture-redesign/2-Version-Detection.md +123 -0
  32. plans/cicd-architecture-redesign/3-Deployment-Gates.md +169 -0
  33. plans/cicd-architecture-redesign/4-RC-Testing.md +194 -0
  34. plans/cicd-architecture-redesign/5-TestPyPI-Validation.md +264 -0
  35. plans/cicd-architecture-redesign/6-Production-Release.md +263 -0
  36. plans/cicd-architecture-redesign/7-Cleanup.md +243 -0
  37. plans/cicd-architecture-redesign/8-Documentation.md +285 -0
  38. plans/cicd-architecture-redesign/Closeout.md +225 -0
  39. plans/closeout-template.md +259 -0
  40. plans/completed/circular-import-audit/0-Overview.md +152 -0
  41. plans/completed/circular-import-audit/1-Static-Dependency-Analysis.md +62 -0
  42. plans/completed/circular-import-audit/2-Dynamic-Import-Testing.md +56 -0
  43. plans/completed/circular-import-audit/3-Performance-Impact-Assessment.md +56 -0
  44. plans/completed/circular-import-audit/4-Issue-Remediation.md +78 -0
  45. plans/completed/circular-import-audit/5-Preventive-Infrastructure.md +89 -0
  46. plans/completed/claude-settings-ecosystem-alignment/0-Overview.md +162 -0
  47. plans/completed/claude-settings-ecosystem-alignment/1-Security-Foundation.md +148 -0
  48. plans/completed/claude-settings-ecosystem-alignment/2-Hook-Integration.md +158 -0
  49. plans/completed/claude-settings-ecosystem-alignment/3-Agent-System-Integration.md +177 -0
  50. plans/completed/claude-settings-ecosystem-alignment/4-Enhanced-Workflow-Automation.md +159 -0
  51. plans/completed/claude-settings-ecosystem-alignment/5-Validation-Monitoring.md +181 -0
  52. plans/completed/claude-settings-ecosystem-alignment/compacted_session_state.md +290 -0
  53. plans/completed/combined_plan_with_checklist_documentation/1-Overview-and-Goals.md +51 -0
  54. plans/completed/combined_plan_with_checklist_documentation/2-Toolchain-and-Hosting.md +69 -0
  55. plans/completed/combined_plan_with_checklist_documentation/3-Repository-Structure.md +61 -0
  56. plans/completed/combined_plan_with_checklist_documentation/4-Configuration-and-Standards.md +70 -0
  57. plans/completed/combined_plan_with_checklist_documentation/5-Documentation-Content.md +62 -0
  58. plans/completed/combined_plan_with_checklist_documentation/6-CI-CD-and-Validation.md +58 -0
  59. plans/completed/combined_plan_with_checklist_documentation/7-Maintenance.md +55 -0
  60. plans/completed/combined_test_plan_with_checklist_fitfunctions/0-Overview.md +135 -0
  61. plans/completed/combined_test_plan_with_checklist_fitfunctions/1-Common-fixtures.md +59 -0
  62. plans/completed/combined_test_plan_with_checklist_fitfunctions/10-power_laws.md +56 -0
  63. plans/completed/combined_test_plan_with_checklist_fitfunctions/2-core.py-FitFunction.md +118 -0
  64. plans/completed/combined_test_plan_with_checklist_fitfunctions/3-gaussians.py-Gaussian-GaussianNormalized-GaussianLn.md +69 -0
  65. plans/completed/combined_test_plan_with_checklist_fitfunctions/4-trend_fits.py-TrendFit.md +99 -0
  66. plans/completed/combined_test_plan_with_checklist_fitfunctions/5-plots.py-FFPlot.md +98 -0
  67. plans/completed/combined_test_plan_with_checklist_fitfunctions/6-tex_info.py-TeXinfo.md +79 -0
  68. plans/completed/combined_test_plan_with_checklist_fitfunctions/7-Justification.md +49 -0
  69. plans/completed/combined_test_plan_with_checklist_fitfunctions/8-exponentials.md +64 -0
  70. plans/completed/combined_test_plan_with_checklist_fitfunctions/9-lines.md +58 -0
  71. plans/completed/combined_test_plan_with_checklist_plotting/0-Overview.md +142 -0
  72. plans/completed/combined_test_plan_with_checklist_plotting/1-base.py.md +90 -0
  73. plans/completed/combined_test_plan_with_checklist_plotting/10-labels-special.py.md +102 -0
  74. plans/completed/combined_test_plan_with_checklist_plotting/11-labels-chemistry.py.md +212 -0
  75. plans/completed/combined_test_plan_with_checklist_plotting/12-labels-composition.py.md +242 -0
  76. plans/completed/combined_test_plan_with_checklist_plotting/13-labels-datetime.py.md +247 -0
  77. plans/completed/combined_test_plan_with_checklist_plotting/14-labels-elemental_abundance.py.md +274 -0
  78. plans/completed/combined_test_plan_with_checklist_plotting/15-visual-validation.md +256 -0
  79. plans/completed/combined_test_plan_with_checklist_plotting/16-integration-testing.md +266 -0
  80. plans/completed/combined_test_plan_with_checklist_plotting/17-performance-benchmarks.md +267 -0
  81. plans/completed/combined_test_plan_with_checklist_plotting/18-Fixtures-and-Utilities.md +86 -0
  82. plans/completed/combined_test_plan_with_checklist_plotting/2-agg_plot.py.md +90 -0
  83. plans/completed/combined_test_plan_with_checklist_plotting/3-histograms.py.md +201 -0
  84. plans/completed/combined_test_plan_with_checklist_plotting/4-scatter.py.md +167 -0
  85. plans/completed/combined_test_plan_with_checklist_plotting/5-spiral.py.md +216 -0
  86. plans/completed/combined_test_plan_with_checklist_plotting/6-orbits.py.md +108 -0
  87. plans/completed/combined_test_plan_with_checklist_plotting/7-tools.py.md +86 -0
  88. plans/completed/combined_test_plan_with_checklist_plotting/8-select_data_from_figure.py.md +97 -0
  89. plans/completed/combined_test_plan_with_checklist_plotting/9-labels-base.py.md +88 -0
  90. plans/completed/combined_test_plan_with_checklist_solar_activity/.gitkeep +0 -0
  91. plans/completed/combined_test_plan_with_checklist_solar_activity/0-Overview.md +170 -0
  92. plans/completed/combined_test_plan_with_checklist_solar_activity/1-Package-Entry-Point-__init__.py.md +121 -0
  93. plans/completed/combined_test_plan_with_checklist_solar_activity/2-Core-Base-Classes-base.py.md +142 -0
  94. plans/completed/combined_test_plan_with_checklist_solar_activity/3-Plotting-Helpers-plots.py.md +123 -0
  95. plans/completed/combined_test_plan_with_checklist_solar_activity/4-LISIRD-Sub-package.md +119 -0
  96. plans/completed/combined_test_plan_with_checklist_solar_activity/5-Extrema-Calculator.md +103 -0
  97. plans/completed/combined_test_plan_with_checklist_solar_activity/6-Sunspot-Number-Sub-package.md +163 -0
  98. plans/completed/combined_test_plan_with_checklist_solar_activity/7-Sunspot-Number-Init.py.md +217 -0
  99. plans/completed/combined_test_plan_with_checklist_solar_activity/compacted_state.md +52 -0
  100. plans/completed/compaction-agent-modernization/0-Overview.md +156 -0
  101. plans/completed/compaction-agent-modernization/1-Architecture-Audit-Gap-Analysis.md +132 -0
  102. plans/completed/compaction-agent-modernization/2-Token-Baseline-Recalibration.md +153 -0
  103. plans/completed/compaction-agent-modernization/3-Agent-Reference-Updates.md +184 -0
  104. plans/completed/compaction-agent-modernization/4-Compression-Algorithm-Modernization.md +238 -0
  105. plans/completed/compaction-agent-modernization/5-Workflow-Integration-Streamlining.md +252 -0
  106. plans/completed/compaction-agent-modernization/6-Template-Structure-Optimization.md +240 -0
  107. plans/completed/compaction-agent-modernization/7-Integration-Testing-Validation.md +292 -0
  108. plans/completed/compaction-hook-enhancement/0-Overview.md +150 -0
  109. plans/completed/compaction-hook-enhancement/1-Token-Estimation-Enhancement.md +179 -0
  110. plans/completed/compaction-hook-enhancement/2-Compression-Intelligence.md +294 -0
  111. plans/completed/compaction-hook-enhancement/3-Git-Integration-Metadata.md +310 -0
  112. plans/completed/compaction-hook-enhancement/4-Session-Continuity-Features.md +358 -0
  113. plans/completed/compaction-hook-enhancement/5-Testing-Strategy.md +404 -0
  114. plans/completed/compaction-hook-enhancement/6-Integration-Roadmap.md +319 -0
  115. plans/completed/compaction-hook-enhancement/compacted_state.md +142 -0
  116. plans/completed/docstring-audit-enhancement/0-Overview.md +274 -0
  117. plans/completed/docstring-audit-enhancement/1-Infrastructure-Setup-and-Validation-Tools.md +206 -0
  118. plans/completed/docstring-audit-enhancement/2-Core-Physics-Modules-Enhancement.md +237 -0
  119. plans/completed/docstring-audit-enhancement/3-Fitfunctions-Mathematical-Modules-Enhancement.md +188 -0
  120. plans/completed/docstring-audit-enhancement/4-Plotting-Visualization-Modules-Enhancement.md +243 -0
  121. plans/completed/docstring-audit-enhancement/5-Specialized-Modules-Enhancement.md +216 -0
  122. plans/completed/docstring-audit-enhancement/6-Validation-and-Integration.md +216 -0
  123. plans/completed/fitfunctions-testing-implementation/0-Overview.md +130 -0
  124. plans/completed/fitfunctions-testing-implementation/1-Test-Infrastructure-Setup.md +79 -0
  125. plans/completed/fitfunctions-testing-implementation/2-Common-Fixtures-Test-Utilities.md +104 -0
  126. plans/completed/fitfunctions-testing-implementation/3-Core-FitFunction-Testing.md +168 -0
  127. plans/completed/fitfunctions-testing-implementation/4-Specialized-Function-Classes.md +210 -0
  128. plans/completed/fitfunctions-testing-implementation/5-Advanced-Classes-Testing.md +214 -0
  129. plans/completed/fitfunctions-testing-implementation/6-Plotting-Integration-Testing.md +231 -0
  130. plans/completed/fitfunctions-testing-implementation/7-Extended-Coverage-BONUS.md +184 -0
  131. plans/completed/numpy-docstring-conversion-plan/numpy-docstring-conversion-plan.md +118 -0
  132. plans/completed/pr-review-remediation/0-Overview.md +138 -0
  133. plans/completed/pr-review-remediation/1-Critical-Safety-Improvements.md +179 -0
  134. plans/completed/pr-review-remediation/2-Smart-Timeouts-Validation.md +399 -0
  135. plans/completed/pr-review-remediation/3-Enhanced-GitHub-Integration.md +258 -0
  136. plans/completed/pr-review-remediation/compacted_state.md +66 -0
  137. plans/completed/python-310-migration/0-Overview.md +390 -0
  138. plans/completed/python-310-migration/1-Planning-Setup.md +164 -0
  139. plans/completed/python-310-migration/2-Implementation.md +256 -0
  140. plans/completed/python-310-migration/3-Testing-Validation.md +335 -0
  141. plans/completed/python-310-migration/4-Documentation-Release.md +274 -0
  142. plans/completed/python-310-migration/5-Closeout.md +252 -0
  143. plans/completed/requirements-management-consolidation/0-Overview.md +118 -0
  144. plans/completed/requirements-management-consolidation/1-Documentation-Validation-Environment-Setup.md +116 -0
  145. plans/completed/requirements-management-consolidation/2-Requirements-Consolidation.md +161 -0
  146. plans/completed/requirements-management-consolidation/3-Workflow-Automation-Final-Integration.md +196 -0
  147. plans/completed/single-ecosystem-plan-implementation/0-Overview.md +83 -0
  148. plans/completed/single-ecosystem-plan-implementation/1-Plan-Preservation-Session-Management.md +38 -0
  149. plans/completed/single-ecosystem-plan-implementation/2-File-Structure-Optimization.md +43 -0
  150. plans/completed/single-ecosystem-plan-implementation/3-Plan-Migration-Archive-Setup.md +82 -0
  151. plans/completed/single-ecosystem-plan-implementation/4-Agent-System-Transformation.md +108 -0
  152. plans/completed/single-ecosystem-plan-implementation/5-Template-System-Enhancement.md +131 -0
  153. plans/completed/single-ecosystem-plan-implementation/6-Final-Validation-Testing.md +120 -0
  154. plans/completed/test-directory-consolidation/0-Overview.md +51 -0
  155. plans/completed/test-directory-consolidation/1-Structure-Preparation.md +82 -0
  156. plans/completed/test-directory-consolidation/2-File-Migration.md +100 -0
  157. plans/completed/test-directory-consolidation/3-Import-Transformation.md +117 -0
  158. plans/completed/test-directory-consolidation/4-Configuration-Consolidation.md +140 -0
  159. plans/completed/test-directory-consolidation/5-Validation.md +152 -0
  160. plans/completed/test-directory-consolidation/6-Cleanup.md +156 -0
  161. plans/completed/test-planning-agents-architecture/0-Overview.md +79 -0
  162. plans/completed/test-planning-agents-architecture/1-Branch-Isolation-Testing.md +49 -0
  163. plans/completed/test-planning-agents-architecture/2-Cross-Branch-Coordination.md +51 -0
  164. plans/completed/test-planning-agents-architecture/3-Merge-Workflow-Testing.md +48 -0
  165. plans/deployment-semver-pypi-rtd/0-Overview.md +463 -0
  166. plans/deployment-semver-pypi-rtd/1-Semantic-Versioning-Foundation.md +136 -0
  167. plans/deployment-semver-pypi-rtd/2-PyPI-Deployment-Infrastructure.md +168 -0
  168. plans/deployment-semver-pypi-rtd/3-Release-Automation.md +214 -0
  169. plans/deployment-semver-pypi-rtd/4-Plan-Closeout.md +543 -0
  170. plans/deployment-semver-pypi-rtd/compacted_session_state.md +172 -0
  171. plans/deployment-semver-pypi-rtd/compacted_state.md +131 -0
  172. plans/documentation-code-audit/0-Overview.md +393 -0
  173. plans/documentation-code-audit/1-Discovery-Inventory.md +183 -0
  174. plans/documentation-code-audit/2-Execution-Environment-Setup.md +263 -0
  175. plans/documentation-code-audit/3-Systematic-Validation.md +322 -0
  176. plans/documentation-code-audit/4-Code-Example-Remediation.md +358 -0
  177. plans/documentation-code-audit/5-Physics-MultiIndex-Compliance.md +464 -0
  178. plans/documentation-code-audit/6-Doctest-Integration.md +523 -0
  179. plans/documentation-code-audit/7-Reporting-Documentation.md +498 -0
  180. plans/documentation-code-audit/8-Closeout.md +456 -0
  181. plans/documentation-rebuild-session/compacted_state.md +109 -0
  182. plans/documentation-rendering-fixes/0-Overview.md +104 -0
  183. plans/documentation-rendering-fixes/1-Sphinx-Build-Diagnostics-Warning-Audit.md +101 -0
  184. plans/documentation-rendering-fixes/2-Configuration-Infrastructure-Fixes.md +113 -0
  185. plans/documentation-rendering-fixes/3-Docstring-Syntax-Audit-Repair.md +131 -0
  186. plans/documentation-rendering-fixes/4-HTML-Page-Rendering-Verification.md +113 -0
  187. plans/documentation-rendering-fixes/5-Advanced-Documentation-Quality-Assurance.md +119 -0
  188. plans/documentation-rendering-fixes/6-Documentation-Build-Optimization-Testing.md +129 -0
  189. plans/documentation-rendering-fixes/compacted_state.md +132 -0
  190. plans/documentation-template-fix/0-Overview.md +197 -0
  191. plans/documentation-template-fix/1-Template-System-Analysis.md +269 -0
  192. plans/documentation-template-fix/2-Template-Modification.md +609 -0
  193. plans/documentation-template-fix/3-Build-System-Integration.md +766 -0
  194. plans/documentation-template-fix/4-Testing-Validation.md +1399 -0
  195. plans/documentation-template-fix/5-Documentation-Training.md +602 -0
  196. plans/documentation-workflow-fix/0-Overview.md +222 -0
  197. plans/documentation-workflow-fix/1-Immediate-Fixes.md +238 -0
  198. plans/documentation-workflow-fix/2-Configuration-Setup.md +298 -0
  199. plans/documentation-workflow-fix/3-Pre-commit-Integration.md +382 -0
  200. plans/documentation-workflow-fix/4-Workflow-Improvements.md +446 -0
  201. plans/documentation-workflow-fix/5-Documentation-and-Training.md +527 -0
  202. plans/duplicate-object-warnings-fix-plan.md +130 -0
  203. plans/github-issues-migration/0-Overview.md +510 -0
  204. plans/github-issues-migration/1-Foundation-Label-System.md +180 -0
  205. plans/github-issues-migration/2-Migration-Tool-Rewrite.md +235 -0
  206. plans/github-issues-migration/3-CLI-Integration-Automation.md +169 -0
  207. plans/github-issues-migration/4-Validated-Migration.md +252 -0
  208. plans/github-issues-migration/5-Documentation-Training.md +171 -0
  209. plans/github-issues-migration/6-Closeout.md +179 -0
  210. plans/github-workflows-repair/repair-plan.md +299 -0
  211. plans/issues_from_plans.py +342 -0
  212. plans/pr-270-doc-validation-fixes/0-Overview.md +354 -0
  213. plans/pr-270-doc-validation-fixes/1-Critical-PR-Fixes.md +117 -0
  214. plans/pr-270-doc-validation-fixes/2-Framework-Right-Sizing.md +129 -0
  215. plans/pr-270-doc-validation-fixes/3-Sustainable-Documentation.md +126 -0
  216. plans/pr-270-doc-validation-fixes/4-Closeout-Migration.md +143 -0
  217. plans/pr-270-doc-validation-fixes/PLAN_COMPLETED.md +149 -0
  218. plans/python-310-migration/0-Overview.md +390 -0
  219. plans/python-310-migration/1-Planning-Setup.md +164 -0
  220. plans/python-310-migration/2-Implementation.md +256 -0
  221. plans/python-310-migration/3-Testing-Validation.md +335 -0
  222. plans/python-310-migration/4-Documentation-Release.md +274 -0
  223. plans/python-310-migration/5-Closeout.md +252 -0
  224. plans/readthedocs-simplified/0-Overview.md +243 -0
  225. plans/readthedocs-simplified/1-Immediate-Fixes.md +216 -0
  226. plans/readthedocs-simplified/2-Template-Simplification.md +278 -0
  227. plans/readthedocs-simplified/3-ReadTheDocs-Setup.md +298 -0
  228. plans/readthedocs-simplified/4-Testing-Validation.md +328 -0
  229. plans/readthedocs-simplified/5-Closeout.md +231 -0
  230. plans/readthedocs-simplified/compacted_state.md +127 -0
  231. plans/session-compaction-2025-08-12/compacted_state.md +114 -0
  232. plans/session-compaction-2025-08-13/compacted_state.md +145 -0
  233. plans/session-continuity-protocol/0-Overview.md +35 -0
  234. plans/session-continuity-protocol/1-Core-Principles-Framework.md +40 -0
  235. plans/session-continuity-protocol/2-Pre-Session-Validation-System.md +79 -0
  236. plans/session-continuity-protocol/3-Context-Switching-Prevention.md +87 -0
  237. plans/session-continuity-protocol/4-Progress-Tracking-Recovery.md +100 -0
  238. plans/sphinx-warnings-analysis.md +222 -0
  239. plans/systemprompt-optimization/0-Overview.md +447 -0
  240. plans/systemprompt-optimization/1-Deploy-SystemPrompt.md +114 -0
  241. plans/systemprompt-optimization/2-Documentation-Alignment.md +198 -0
  242. plans/systemprompt-optimization/3-Monitoring-Infrastructure.md +396 -0
  243. plans/systemprompt-optimization/4-Implementation-Script.md +450 -0
  244. plans/systemprompt-optimization/9-Closeout.md +165 -0
  245. plans/systemprompt-optimization/compacted_state.md +143 -0
  246. plans/template-value-propositions/0-Overview.md +357 -0
  247. plans/template-value-propositions/1-Value-Proposition-Framework-Design.md +144 -0
  248. plans/template-value-propositions/2-Plan-Template-Enhancement.md +178 -0
  249. plans/template-value-propositions/3-Value-Generator-Hook-Implementation.md +291 -0
  250. plans/template-value-propositions/4-Value-Validator-Hook-Implementation.md +274 -0
  251. plans/template-value-propositions/5-Documentation-Agent-Updates.md +219 -0
  252. plans/template-value-propositions/6-Integration-Testing-Validation.md +247 -0
  253. plans/tests-audit/0-Overview.md +410 -0
  254. plans/tests-audit/1-Discovery-Inventory.md +170 -0
  255. plans/tests-audit/2-Physics-Validation-Audit.md +195 -0
  256. plans/tests-audit/3-Architecture-Compliance.md +195 -0
  257. plans/tests-audit/4-Numerical-Stability-Analysis.md +203 -0
  258. plans/tests-audit/5-Documentation-Enhancement.md +220 -0
  259. plans/tests-audit/6-Audit-Deliverables.md +220 -0
  260. plans/tests-audit/7-Closeout.md +252 -0
  261. plans/tests-audit/artifacts/ARCHITECTURE_COMPLIANCE_REPORT.md +315 -0
  262. plans/tests-audit/artifacts/ARCHITECTURE_RECOMMENDATIONS.md +943 -0
  263. plans/tests-audit/artifacts/COMPREHENSIVE_AUDIT_REPORT.md +356 -0
  264. plans/tests-audit/artifacts/CONTRIBUTING_ENHANCED_TEMPLATE.md +419 -0
  265. plans/tests-audit/artifacts/COVERAGE_GAP_ANALYSIS.md +152 -0
  266. plans/tests-audit/artifacts/DOCUMENTATION_ENHANCEMENT_REPORT.md +502 -0
  267. plans/tests-audit/artifacts/EXECUTIVE_AUDIT_SUMMARY.md +129 -0
  268. plans/tests-audit/artifacts/IMPLEMENTATION_ROADMAP.md +647 -0
  269. plans/tests-audit/artifacts/NUMERICAL_RECOMMENDATIONS.md +739 -0
  270. plans/tests-audit/artifacts/NUMERICAL_STABILITY_GUIDE_TEMPLATE.rst +451 -0
  271. plans/tests-audit/artifacts/NUMERICAL_STABILITY_REPORT.md +301 -0
  272. plans/tests-audit/artifacts/PHASE_3_SUMMARY.md +280 -0
  273. plans/tests-audit/artifacts/PHASE_4_SUMMARY.md +229 -0
  274. plans/tests-audit/artifacts/PHASE_5_SUMMARY.md +292 -0
  275. plans/tests-audit/artifacts/PHASE_6_CLOSEOUT.md +278 -0
  276. plans/tests-audit/artifacts/PHYSICS_GUIDE_TEMPLATE.rst +268 -0
  277. plans/tests-audit/artifacts/PHYSICS_VALIDATION_REPORT.md +235 -0
  278. plans/tests-audit/artifacts/TECHNICAL_DELIVERABLES_PACKAGE.md +2502 -0
  279. plans/tests-audit/artifacts/TEST_INVENTORY.csv +1204 -0
  280. plans/tests-audit/artifacts/TEST_INVENTORY.md +135 -0
  281. plans/tests-audit/artifacts/test_discovery_analysis.py +231 -0
  282. plans/tests-audit/artifacts/test_parser.py +395 -0
  283. solarwindpy/README.md +3 -0
  284. solarwindpy/Untitled.ipynb +54 -0
  285. solarwindpy/__init__.py +74 -0
  286. solarwindpy/core/__init__.py +23 -0
  287. solarwindpy/core/alfvenic_turbulence.py +804 -0
  288. solarwindpy/core/base.py +267 -0
  289. solarwindpy/core/ions.py +309 -0
  290. solarwindpy/core/plasma.py +2133 -0
  291. solarwindpy/core/spacecraft.py +256 -0
  292. solarwindpy/core/tensor.py +90 -0
  293. solarwindpy/core/units_constants.py +199 -0
  294. solarwindpy/core/vector.py +328 -0
  295. solarwindpy/fitfunctions/__init__.py +20 -0
  296. solarwindpy/fitfunctions/core.py +734 -0
  297. solarwindpy/fitfunctions/exponentials.py +188 -0
  298. solarwindpy/fitfunctions/gaussians.py +264 -0
  299. solarwindpy/fitfunctions/lines.py +116 -0
  300. solarwindpy/fitfunctions/moyal.py +71 -0
  301. solarwindpy/fitfunctions/plots.py +751 -0
  302. solarwindpy/fitfunctions/power_laws.py +209 -0
  303. solarwindpy/fitfunctions/tex_info.py +568 -0
  304. solarwindpy/fitfunctions/trend_fits.py +482 -0
  305. solarwindpy/instabilities/__init__.py +16 -0
  306. solarwindpy/instabilities/beta_ani.py +82 -0
  307. solarwindpy/instabilities/verscharen2016.py +631 -0
  308. solarwindpy/plotting/__init__.py +33 -0
  309. solarwindpy/plotting/agg_plot.py +489 -0
  310. solarwindpy/plotting/base.py +465 -0
  311. solarwindpy/plotting/hist1d.py +405 -0
  312. solarwindpy/plotting/hist2d.py +1035 -0
  313. solarwindpy/plotting/histograms.py +1845 -0
  314. solarwindpy/plotting/labels/__init__.py +104 -0
  315. solarwindpy/plotting/labels/base.py +686 -0
  316. solarwindpy/plotting/labels/chemistry.py +19 -0
  317. solarwindpy/plotting/labels/composition.py +100 -0
  318. solarwindpy/plotting/labels/datetime.py +235 -0
  319. solarwindpy/plotting/labels/elemental_abundance.py +73 -0
  320. solarwindpy/plotting/labels/special.py +794 -0
  321. solarwindpy/plotting/orbits.py +515 -0
  322. solarwindpy/plotting/scatter.py +99 -0
  323. solarwindpy/plotting/select_data_from_figure.py +329 -0
  324. solarwindpy/plotting/spiral.py +980 -0
  325. solarwindpy/plotting/tools.py +434 -0
  326. solarwindpy/scripts/__init__.py +1 -0
  327. solarwindpy/scripts/logs/.gitignore +1 -0
  328. solarwindpy/solar_activity/__init__.py +53 -0
  329. solarwindpy/solar_activity/base.py +605 -0
  330. solarwindpy/solar_activity/lisird/__init__.py +3 -0
  331. solarwindpy/solar_activity/lisird/extrema_calculator.py +394 -0
  332. solarwindpy/solar_activity/lisird/lisird.py +319 -0
  333. solarwindpy/solar_activity/plots.py +116 -0
  334. solarwindpy/solar_activity/sunspot_number/.DS_Store +0 -0
  335. solarwindpy/solar_activity/sunspot_number/__init__.py +3 -0
  336. solarwindpy/solar_activity/sunspot_number/sidc.py +556 -0
  337. solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv +72 -0
  338. solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv.silso +72 -0
  339. solarwindpy/tools/__init__.py +162 -0
  340. solarwindpy-0.1.1.dist-info/METADATA +181 -0
  341. solarwindpy-0.1.1.dist-info/RECORD +409 -0
  342. {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.1.dist-info}/WHEEL +1 -1
  343. solarwindpy-0.1.1.dist-info/licenses/LICENSE.rst +32 -0
  344. solarwindpy-0.1.1.dist-info/top_level.txt +3 -0
  345. tests/__init__.py +1 -0
  346. tests/conftest.py +10 -0
  347. tests/core/__init__.py +1 -0
  348. tests/core/test_alfvenic_turbulence.py +544 -0
  349. tests/core/test_base.py +112 -0
  350. tests/core/test_base_head_tail.py +29 -0
  351. tests/core/test_base_mi_tuples.py +11 -0
  352. tests/core/test_core_verify_datetimeindex.py +32 -0
  353. tests/core/test_ions.py +325 -0
  354. tests/core/test_plasma.py +2581 -0
  355. tests/core/test_plasma_io.py +12 -0
  356. tests/core/test_quantities.py +507 -0
  357. tests/core/test_spacecraft.py +210 -0
  358. tests/core/test_units_constants.py +22 -0
  359. tests/data/epoch.csv +4 -0
  360. tests/data/plasma.csv +4 -0
  361. tests/data/spacecraft.csv +4 -0
  362. tests/fitfunctions/conftest.py +60 -0
  363. tests/fitfunctions/test_core.py +193 -0
  364. tests/fitfunctions/test_exponentials.py +342 -0
  365. tests/fitfunctions/test_gaussians.py +142 -0
  366. tests/fitfunctions/test_lines.py +349 -0
  367. tests/fitfunctions/test_moyal.py +258 -0
  368. tests/fitfunctions/test_plots.py +258 -0
  369. tests/fitfunctions/test_power_laws.py +365 -0
  370. tests/fitfunctions/test_tex_info.py +183 -0
  371. tests/fitfunctions/test_trend_fit_properties.py +31 -0
  372. tests/fitfunctions/test_trend_fits.py +244 -0
  373. tests/plotting/__init__.py +1 -0
  374. tests/plotting/labels/__init__.py +1 -0
  375. tests/plotting/labels/test_chemistry.py +243 -0
  376. tests/plotting/labels/test_composition.py +345 -0
  377. tests/plotting/labels/test_datetime.py +445 -0
  378. tests/plotting/labels/test_elemental_abundance.py +366 -0
  379. tests/plotting/labels/test_init.py +66 -0
  380. tests/plotting/labels/test_labels_base.py +347 -0
  381. tests/plotting/labels/test_special.py +550 -0
  382. tests/plotting/test_agg_plot.py +602 -0
  383. tests/plotting/test_base.py +752 -0
  384. tests/plotting/test_fixtures_utilities.py +775 -0
  385. tests/plotting/test_histograms.py +546 -0
  386. tests/plotting/test_integration.py +675 -0
  387. tests/plotting/test_orbits.py +435 -0
  388. tests/plotting/test_performance.py +708 -0
  389. tests/plotting/test_scatter.py +752 -0
  390. tests/plotting/test_select_data_from_figure.py +1209 -0
  391. tests/plotting/test_spiral.py +573 -0
  392. tests/plotting/test_tools.py +607 -0
  393. tests/plotting/test_visual_validation.py +465 -0
  394. tests/solar_activity/__init__.py +1 -0
  395. tests/solar_activity/lisird/__init__.py +1 -0
  396. tests/solar_activity/lisird/test_extrema_calculator.py +593 -0
  397. tests/solar_activity/lisird/test_lisird_id.py +187 -0
  398. tests/solar_activity/sunspot_number/__init__.py +1 -0
  399. tests/solar_activity/sunspot_number/test_init.py +399 -0
  400. tests/solar_activity/sunspot_number/test_sidc.py +465 -0
  401. tests/solar_activity/sunspot_number/test_sidc_id.py +223 -0
  402. tests/solar_activity/sunspot_number/test_sidc_loader.py +275 -0
  403. tests/solar_activity/sunspot_number/test_ssn_extrema.py +406 -0
  404. tests/solar_activity/test_base.py +656 -0
  405. tests/solar_activity/test_init.py +396 -0
  406. tests/solar_activity/test_plots.py +371 -0
  407. tests/test_circular_imports.py +408 -0
  408. tests/test_issue_titles.py +25 -0
  409. tests/test_statusline.py +298 -0
  410. solarwindpy-0.0.1.dev0.dist-info/METADATA +0 -14
  411. solarwindpy-0.0.1.dev0.dist-info/RECORD +0 -4
  412. 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