├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bokeh ├── deploy-scala-2.10.xml ├── deploy-scala-2.11.xml ├── pom.xml └── src │ └── main │ └── scala │ └── org │ └── dianahep │ └── histogrammar │ └── bokeh.scala ├── core ├── deploy-scala-2.10.xml ├── deploy-scala-2.11.xml ├── deploy-scala-2.12.xml ├── deploy-scala-2.13.xml ├── pom.xml └── src │ ├── main │ └── scala │ │ └── org │ │ └── dianahep │ │ └── histogrammar │ │ ├── ascii.scala │ │ ├── defs.scala │ │ ├── json.scala │ │ ├── primitives │ │ ├── average.scala │ │ ├── bag.scala │ │ ├── bin.scala │ │ ├── categorize.scala │ │ ├── centrallybin.scala │ │ ├── collection.scala │ │ ├── count.scala │ │ ├── deviate.scala │ │ ├── fraction.scala │ │ ├── irregularlybin.scala │ │ ├── minmax.scala │ │ ├── select.scala │ │ ├── sparselybin.scala │ │ ├── stack.scala │ │ └── sum.scala │ │ ├── tutorial │ │ └── cmsdata.scala │ │ └── util.scala │ └── test │ └── scala │ ├── basic.scala_ │ └── specification.scala_ ├── make-poms.py ├── pom.xml └── sparksql ├── deploy-scala-2.10.xml ├── deploy-scala-2.11.xml ├── deploy-scala-2.12.xml ├── deploy-scala-2.13.xml ├── pom.xml └── src └── main └── scala └── org └── dianahep └── histogrammar └── sparksql.scala /.gitignore: -------------------------------------------------------------------------------- 1 | # Emacs 2 | *~ 3 | \#*\# 4 | 5 | # JVM 6 | *.class 7 | *.log 8 | 9 | # sbt specific 10 | .cache 11 | .history 12 | .lib/ 13 | dist/* 14 | target/ 15 | lib_managed/ 16 | src_managed/ 17 | project/boot/ 18 | project/plugins/project/ 19 | 20 | # Scala-IDE specific 21 | .scala_dependencies 22 | .worksheet 23 | 24 | # Mobile Tools for Java (J2ME) 25 | .mtj.tmp/ 26 | 27 | # Package Files # 28 | *.jar 29 | *.war 30 | *.ear 31 | 32 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 33 | hs_err_pid* 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java # because we use Maven 2 | 3 | os: 4 | - linux 5 | 6 | jdk: 7 | - oraclejdk7 8 | - oraclejdk8 9 | - openjdk7 10 | 11 | env: 12 | matrix: 13 | - SCALA_VERSION=2.10 14 | - SCALA_VERSION=2.11 15 | 16 | install: mvn install -P scala-${SCALA_VERSION} -DskipTests=true -Dmaven.javadoc.skip=true -B -V 17 | 18 | script: mvn test -P scala-${SCALA_VERSION} 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scala implementation of Histogrammar 2 | ==================================== 3 | 4 | [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.61344.svg)](http://dx.doi.org/10.5281/zenodo.61344) 5 | 6 | See [https://histogrammar.github.io/histogrammar-docs](https://histogrammar.github.io/histogrammar-docs) for a complete introduction to Histogrammar. 7 | 8 | This is a Scala implementation for Scala versions 2.10, 2.11, 2.12 and 2.13. 9 | 10 | Latest Scala release: v1.0.30 (Dec 2023). 11 | 12 | Announcements 13 | ============= 14 | 15 | Spark 3.X 16 | --------- 17 | 18 | With Spark 3.X, based on Scala 2.12 or 2.13, make sure to pick up the correct histogrammar jar file: 19 | 20 | ``` 21 | spark = SparkSession.builder.config("spark.jars.packages", "io.github.histogrammar:histogrammar_2.12:1.0.30,io.github.histogrammar:histogrammar-sparksql_2.12:1.0.30").getOrCreate() 22 | ``` 23 | 24 | For Scala 2.13, in the string above simply replace "2.12" with "2.13". 25 | 26 | December, 2023 27 | 28 | 29 | Installation 30 | ============ 31 | 32 | Histogrammar has a standard Maven POM. With Maven 3+, run 33 | 34 | ```bash 35 | mvn install -P scala-2.XX 36 | ``` 37 | 38 | in the base directory (to compile everything) or one of the subdirectories, where XX selects the scala version (2.10, 2.11, 2.12, 2.13). 39 | All subdirectories depend on `core`, so this must be installed first. 40 | 41 | Status 42 | ====== 43 | 44 | ![Build status](https://travis-ci.org/histogrammar/histogrammar-scala.svg) 45 | 46 | The Scala implementation is verified against the Python implementation by running exactly the same tests on both. They agree numerically to one part in a trillion, with the same NaN/infinity handling, and exchange the same JSON. 47 | 48 | All primitives except `UntypedLabel` preserve type information in the Scala REPL, so you can extract values or tab-complete without casting. 49 | 50 | In the future, JIT-compilation will be available, similar to the ROOT/Cling interface in Python, but compiling to Java bytecode with [Janino](http://janino-compiler.github.io/janino/) instead of native bytecode with LLVM. 51 | 52 | | Primitive | Scala | JVM JIT | 53 | |:------------------|:------|:--------| 54 | | Count | done | | 55 | | Sum | done | | 56 | | Average | done | | 57 | | Deviate | done | | 58 | | Minimize | done | | 59 | | Maximize | done | | 60 | | Bag | done | | 61 | | Bin | done | | 62 | | SparselyBin | done | | 63 | | CentrallyBin | done | | 64 | | IrregularlyBin | done | | 65 | | Categorize | done | | 66 | | Fraction | done | | 67 | | Stack | done | | 68 | | Select | done | | 69 | | Label | done | | 70 | | UntypedLabel | done | | 71 | | Index | done | | 72 | | Branch | done | | 73 | -------------------------------------------------------------------------------- /bokeh/deploy-scala-2.10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-bokeh 19 | Adapter for using Histogrammar to generate Bokeh plots. 20 | http://histogrammar.org 21 | 2016 22 | 23 | org.diana-hep 24 | histogrammar-bokeh_2.10 25 | 1.0.4 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.7 65 | 1.7 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.10.6 73 | 74 | 75 | 76 | org.diana-hep 77 | histogrammar_2.10 78 | 1.0.4 79 | 80 | 81 | 82 | io.continuum.bokeh 83 | bokeh_2.10 84 | 0.7 85 | 86 | 87 | 88 | 89 | 90 | 91 | central 92 | Central Repository 93 | http://repo1.maven.org/maven2 94 | default 95 | 96 | false 97 | 98 | 99 | 100 | 101 | 102 | 103 | central 104 | Maven Plugin Repository 105 | http://repo1.maven.org/maven2 106 | default 107 | 108 | false 109 | 110 | 111 | never 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | net.alchim31.maven 122 | scala-maven-plugin 123 | 3.2.2 124 | 125 | 126 | 127 | compile 128 | testCompile 129 | 130 | 131 | 132 | -Dscalac.patmat.analysisBudget=512 133 | -deprecation 134 | -feature 135 | -unchecked 136 | -dependencyfile 137 | ${project.build.directory}/.scala_dependencies 138 | 139 | incremental 140 | 141 | 142 | 143 | 144 | attach-sources 145 | 146 | add-source 147 | 148 | 149 | 150 | attach-javadocs 151 | 152 | doc-jar 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | maven-install-plugin 162 | 2.5.2 163 | 164 | true 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-source-plugin 171 | 2.2.1 172 | 173 | 174 | attach-sources 175 | 176 | jar-no-fork 177 | 178 | 179 | 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-gpg-plugin 185 | 1.6 186 | 187 | 188 | sign-artifacts 189 | verify 190 | 191 | sign 192 | 193 | 194 | 195 | 196 | 197 | 198 | org.sonatype.plugins 199 | nexus-staging-maven-plugin 200 | 1.6.7 201 | true 202 | 203 | ossrh 204 | https://oss.sonatype.org/ 205 | true 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | org.apache.maven.plugins 215 | maven-release-plugin 216 | 2.5 217 | 218 | false 219 | false 220 | true 221 | deploy 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | ossrh 237 | https://oss.sonatype.org/content/repositories/snapshots 238 | 239 | 240 | ossrh 241 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /bokeh/deploy-scala-2.11.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-bokeh 19 | Adapter for using Histogrammar to generate Bokeh plots. 20 | http://histogrammar.org 21 | 2016 22 | 23 | org.diana-hep 24 | histogrammar-bokeh_2.11 25 | 1.0.4 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.11.8 73 | 74 | 75 | 76 | org.diana-hep 77 | histogrammar_2.11 78 | 1.0.4 79 | 80 | 81 | 82 | io.continuum.bokeh 83 | bokeh_2.11 84 | 0.7 85 | 86 | 87 | 88 | 89 | 90 | 91 | central 92 | Central Repository 93 | http://repo1.maven.org/maven2 94 | default 95 | 96 | false 97 | 98 | 99 | 100 | 101 | 102 | 103 | central 104 | Maven Plugin Repository 105 | http://repo1.maven.org/maven2 106 | default 107 | 108 | false 109 | 110 | 111 | never 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | net.alchim31.maven 122 | scala-maven-plugin 123 | 3.2.2 124 | 125 | 126 | 127 | compile 128 | testCompile 129 | 130 | 131 | 132 | -Dscalac.patmat.analysisBudget=512 133 | -deprecation 134 | -feature 135 | -unchecked 136 | -dependencyfile 137 | ${project.build.directory}/.scala_dependencies 138 | 139 | incremental 140 | 141 | 142 | 143 | 144 | attach-sources 145 | 146 | add-source 147 | 148 | 149 | 150 | attach-javadocs 151 | 152 | doc-jar 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | maven-install-plugin 162 | 2.5.2 163 | 164 | true 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-source-plugin 171 | 2.2.1 172 | 173 | 174 | attach-sources 175 | 176 | jar-no-fork 177 | 178 | 179 | 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-gpg-plugin 185 | 1.6 186 | 187 | 188 | sign-artifacts 189 | verify 190 | 191 | sign 192 | 193 | 194 | 195 | 196 | 197 | 198 | org.sonatype.plugins 199 | nexus-staging-maven-plugin 200 | 1.6.7 201 | true 202 | 203 | ossrh 204 | https://oss.sonatype.org/ 205 | true 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | org.apache.maven.plugins 215 | maven-release-plugin 216 | 2.5 217 | 218 | false 219 | false 220 | true 221 | deploy 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | ossrh 237 | https://oss.sonatype.org/content/repositories/snapshots 238 | 239 | 240 | ossrh 241 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /bokeh/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-bokeh 19 | Adapter for using Histogrammar to generate Bokeh plots. 20 | http://histogrammar.org 21 | 2016 22 | 23 | org.diana-hep 24 | histogrammar-bokeh_${scala.binary.version} 25 | 1.0.4 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | scala-2.10 54 | 55 | true 56 | 57 | 58 | 2.10.6 59 | 2.10 60 | 1.7 61 | 1.7 62 | 1.6.2 63 | 64 | 65 | 66 | 67 | scala-2.11 68 | 69 | scala-2.11 70 | 71 | 72 | 2.11.8 73 | 2.11 74 | 1.8 75 | 1.8 76 | 2.0.0 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.scala-tools 86 | maven-scala-plugin 87 | 2.15.2 88 | 89 | 90 | 91 | 92 | 93 | UTF-8 94 | UTF-8 95 | 96 | 97 | 98 | 99 | org.scala-lang 100 | scala-library 101 | ${scala.version} 102 | 103 | 104 | 105 | org.diana-hep 106 | histogrammar_${scala.binary.version} 107 | 1.0.4 108 | 109 | 110 | 111 | io.continuum.bokeh 112 | bokeh_${scala.binary.version} 113 | 0.7 114 | 115 | 116 | 117 | 118 | 119 | 120 | central 121 | Central Repository 122 | http://repo1.maven.org/maven2 123 | default 124 | 125 | false 126 | 127 | 128 | 129 | 130 | 131 | 132 | central 133 | Maven Plugin Repository 134 | http://repo1.maven.org/maven2 135 | default 136 | 137 | false 138 | 139 | 140 | never 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | net.alchim31.maven 151 | scala-maven-plugin 152 | 3.2.2 153 | 154 | 155 | 156 | compile 157 | testCompile 158 | 159 | 160 | 161 | -Dscalac.patmat.analysisBudget=512 162 | -deprecation 163 | -feature 164 | -unchecked 165 | -dependencyfile 166 | ${project.build.directory}/.scala_dependencies 167 | 168 | incremental 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | org.apache.maven.plugins 178 | maven-dependency-plugin 179 | 2.10 180 | 181 | 182 | package 183 | 184 | copy-dependencies 185 | 186 | 187 | 188 | target/lib 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | maven-install-plugin 197 | 2.5.2 198 | 199 | true 200 | 201 | 202 | 203 | 204 | org.apache.maven.plugins 205 | maven-source-plugin 206 | 2.2.1 207 | 208 | 209 | attach-sources 210 | 211 | jar-no-fork 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /core/deploy-scala-2.10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar 19 | Histogram abstraction to simplify complex aggregations in distributed environments. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar_2.10 25 | 1.0.20 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.7 65 | 1.7 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.10.6 73 | 74 | 75 | 76 | org.scalatest 77 | scalatest_2.10 78 | 2.2.5 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | central 87 | Central Repository 88 | http://repo1.maven.org/maven2 89 | default 90 | 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | central 99 | Maven Plugin Repository 100 | http://repo1.maven.org/maven2 101 | default 102 | 103 | false 104 | 105 | 106 | never 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | net.alchim31.maven 117 | scala-maven-plugin 118 | 3.2.2 119 | 120 | 121 | 122 | compile 123 | testCompile 124 | 125 | 126 | 127 | -Dscalac.patmat.analysisBudget=512 128 | -deprecation 129 | -feature 130 | -unchecked 131 | -dependencyfile 132 | ${project.build.directory}/.scala_dependencies 133 | 134 | incremental 135 | 136 | 137 | 138 | 139 | attach-sources 140 | 141 | add-source 142 | 143 | 144 | 145 | attach-javadocs 146 | 147 | doc-jar 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.scalatest 156 | scalatest-maven-plugin 157 | 1.0 158 | 159 | ${project.build.directory}/surefire-reports 160 | . 161 | 162 | 163 | 164 | test 165 | 166 | test 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | maven-install-plugin 175 | 2.5.2 176 | 177 | true 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-source-plugin 184 | 2.2.1 185 | 186 | 187 | attach-sources 188 | 189 | jar-no-fork 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-gpg-plugin 198 | 1.6 199 | 200 | 201 | sign-artifacts 202 | verify 203 | 204 | sign 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.sonatype.plugins 212 | nexus-staging-maven-plugin 213 | 1.6.7 214 | true 215 | 216 | ossrh 217 | https://oss.sonatype.org/ 218 | true 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | org.apache.maven.plugins 228 | maven-release-plugin 229 | 2.5 230 | 231 | false 232 | false 233 | true 234 | deploy 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | ossrh 250 | https://oss.sonatype.org/content/repositories/snapshots 251 | 252 | 253 | ossrh 254 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /core/deploy-scala-2.11.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar 19 | Histogram abstraction to simplify complex aggregations in distributed environments. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar_2.11 25 | 1.0.20 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.11.12 73 | 74 | 75 | 76 | org.scalatest 77 | scalatest_2.11 78 | 3.2.3 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | central 87 | Central Repository 88 | http://repo1.maven.org/maven2 89 | default 90 | 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | central 99 | Maven Plugin Repository 100 | http://repo1.maven.org/maven2 101 | default 102 | 103 | false 104 | 105 | 106 | never 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | net.alchim31.maven 117 | scala-maven-plugin 118 | 4.4.0 119 | 120 | 121 | 122 | compile 123 | testCompile 124 | 125 | 126 | 127 | -Dscalac.patmat.analysisBudget=512 128 | -deprecation 129 | -feature 130 | -unchecked 131 | -dependencyfile 132 | ${project.build.directory}/.scala_dependencies 133 | 134 | incremental 135 | 136 | 137 | 138 | 139 | attach-sources 140 | 141 | add-source 142 | 143 | 144 | 145 | attach-javadocs 146 | 147 | doc-jar 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.scalatest 156 | scalatest-maven-plugin 157 | 1.0 158 | 159 | ${project.build.directory}/surefire-reports 160 | . 161 | 162 | 163 | 164 | test 165 | 166 | test 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | maven-install-plugin 175 | 2.5.2 176 | 177 | true 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-source-plugin 184 | 2.2.1 185 | 186 | 187 | attach-sources 188 | 189 | jar-no-fork 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-gpg-plugin 198 | 1.5 199 | 200 | 201 | sign-artifacts 202 | verify 203 | 204 | sign 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.sonatype.plugins 212 | nexus-staging-maven-plugin 213 | 1.6.7 214 | true 215 | 216 | ossrh 217 | https://oss.sonatype.org/ 218 | true 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | org.apache.maven.plugins 228 | maven-release-plugin 229 | 2.5 230 | 231 | false 232 | false 233 | true 234 | deploy 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | ossrh 250 | https://oss.sonatype.org/content/repositories/snapshots 251 | 252 | 253 | ossrh 254 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /core/deploy-scala-2.12.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar 19 | Histogram abstraction to simplify complex aggregations in distributed environments. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar_2.12 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.12.13 73 | 74 | 75 | 76 | org.scalatest 77 | scalatest_2.12 78 | 3.2.3 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | central 87 | Central Repository 88 | http://repo1.maven.org/maven2 89 | default 90 | 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | central 99 | Maven Plugin Repository 100 | http://repo1.maven.org/maven2 101 | default 102 | 103 | false 104 | 105 | 106 | never 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | net.alchim31.maven 117 | scala-maven-plugin 118 | 4.4.0 119 | 120 | 121 | 122 | compile 123 | testCompile 124 | 125 | 126 | 127 | -Dscalac.patmat.analysisBudget=512 128 | -deprecation 129 | -feature 130 | -unchecked 131 | -dependencyfile 132 | ${project.build.directory}/.scala_dependencies 133 | 134 | incremental 135 | 136 | 137 | 138 | 139 | attach-sources 140 | 141 | add-source 142 | 143 | 144 | 145 | attach-javadocs 146 | 147 | doc-jar 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.scalatest 156 | scalatest-maven-plugin 157 | 1.0 158 | 159 | ${project.build.directory}/surefire-reports 160 | . 161 | 162 | 163 | 164 | test 165 | 166 | test 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | maven-install-plugin 175 | 2.5.2 176 | 177 | true 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-source-plugin 184 | 2.2.1 185 | 186 | 187 | attach-sources 188 | 189 | jar-no-fork 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-gpg-plugin 198 | 1.5 199 | 200 | 201 | sign-artifacts 202 | verify 203 | 204 | sign 205 | 206 | 207 | 208 | 209 | 210 | org.sonatype.plugins 211 | nexus-staging-maven-plugin 212 | 1.6.13 213 | true 214 | 215 | ossrh 216 | https://oss.sonatype.org/ 217 | true 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-release-plugin 228 | 3.0.1 229 | 230 | false 231 | false 232 | true 233 | deploy 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | ossrh 249 | https://oss.sonatype.org/content/repositories/snapshots 250 | 251 | 252 | ossrh 253 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /core/deploy-scala-2.13.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar 19 | Histogram abstraction to simplify complex aggregations in distributed environments. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar_2.13 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.13.12 73 | 74 | 75 | 76 | org.scalatest 77 | scalatest_2.13 78 | 3.2.3 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | central 87 | Central Repository 88 | http://repo1.maven.org/maven2 89 | default 90 | 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | central 99 | Maven Plugin Repository 100 | http://repo1.maven.org/maven2 101 | default 102 | 103 | false 104 | 105 | 106 | never 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | net.alchim31.maven 117 | scala-maven-plugin 118 | 4.4.0 119 | 120 | 121 | 122 | compile 123 | testCompile 124 | 125 | 126 | 127 | -Dscalac.patmat.analysisBudget=512 128 | -deprecation 129 | -feature 130 | -unchecked 131 | -dependencyfile 132 | ${project.build.directory}/.scala_dependencies 133 | 134 | incremental 135 | 136 | 137 | 138 | 139 | attach-sources 140 | 141 | add-source 142 | 143 | 144 | 145 | attach-javadocs 146 | 147 | doc-jar 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.scalatest 156 | scalatest-maven-plugin 157 | 1.0 158 | 159 | ${project.build.directory}/surefire-reports 160 | . 161 | 162 | 163 | 164 | test 165 | 166 | test 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | maven-install-plugin 175 | 2.5.2 176 | 177 | true 178 | 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-source-plugin 184 | 2.2.1 185 | 186 | 187 | attach-sources 188 | 189 | jar-no-fork 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-gpg-plugin 198 | 1.5 199 | 200 | 201 | sign-artifacts 202 | verify 203 | 204 | sign 205 | 206 | 207 | 208 | 209 | 210 | org.sonatype.plugins 211 | nexus-staging-maven-plugin 212 | 1.6.13 213 | true 214 | 215 | ossrh 216 | https://oss.sonatype.org/ 217 | true 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-release-plugin 228 | 3.0.1 229 | 230 | false 231 | false 232 | true 233 | deploy 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | ossrh 249 | https://oss.sonatype.org/content/repositories/snapshots 250 | 251 | 252 | ossrh 253 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar 19 | Histogram abstraction to simplify complex aggregations in distributed environments. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar_${scala.binary.version} 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | scala-2.10 54 | 55 | true 56 | 57 | 58 | 2.10.6 59 | 2.10 60 | 1.7 61 | 1.7 62 | 1.6.2 63 | 8 64 | 65 | 66 | 67 | 68 | scala-2.11 69 | 70 | scala-2.11 71 | 72 | 73 | 2.11.12 74 | 2.11 75 | 1.8 76 | 1.8 77 | 2.0.0 78 | 8 79 | 80 | 81 | 82 | 83 | scala-2.12 84 | 85 | scala-2.12 86 | 87 | 88 | 2.12.13 89 | 2.12 90 | 1.9 91 | 1.9 92 | 3.0.1 93 | 11 94 | 95 | 96 | 97 | 98 | scala-2.13 99 | 100 | scala-2.13 101 | 102 | 103 | 2.13.0 104 | 2.13 105 | 11 106 | 11 107 | 3.2.0 108 | 11 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.scala-tools 118 | maven-scala-plugin 119 | 2.15.2 120 | 121 | 122 | 123 | 124 | 125 | UTF-8 126 | UTF-8 127 | 128 | 129 | 130 | 131 | org.scala-lang 132 | scala-library 133 | ${scala.version} 134 | 135 | 136 | 137 | org.scalatest 138 | scalatest_${scala.binary.version} 139 | 3.2.3 140 | test 141 | 142 | 143 | 144 | 145 | 146 | 147 | central 148 | Central Repository 149 | https://repo1.maven.org/maven2 150 | default 151 | 152 | false 153 | 154 | 155 | 156 | 157 | 158 | 159 | central 160 | Maven Plugin Repository 161 | https://repo1.maven.org/maven2 162 | default 163 | 164 | false 165 | 166 | 167 | never 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | maven-compiler-plugin 177 | 3.8.1 178 | 179 | 11 180 | 11 181 | 182 | 183 | 184 | 185 | 186 | net.alchim31.maven 187 | scala-maven-plugin 188 | 4.4.0 189 | 190 | 191 | 192 | compile 193 | testCompile 194 | 195 | 196 | 197 | -Dscalac.patmat.analysisBudget=512 198 | -deprecation 199 | -feature 200 | -unchecked 201 | -dependencyfile 202 | ${project.build.directory}/.scala_dependencies 203 | 204 | incremental 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | org.scalatest 214 | scalatest-maven-plugin 215 | 1.0 216 | 217 | ${project.build.directory}/surefire-reports 218 | . 219 | 220 | 221 | 222 | test 223 | 224 | test 225 | 226 | 227 | 228 | 229 | 230 | 231 | org.apache.maven.plugins 232 | maven-dependency-plugin 233 | 234 | 2.10 235 | 236 | 237 | package 238 | 239 | copy-dependencies 240 | 241 | 242 | 243 | target/lib 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | maven-install-plugin 252 | 2.5.2 253 | 254 | true 255 | 256 | 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-source-plugin 261 | 3.2.0 262 | 263 | 264 | attach-sources 265 | 266 | jar 267 | 268 | 269 | 270 | 271 | 272 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/average.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar.json._ 20 | import org.dianahep.histogrammar.util._ 21 | 22 | package histogrammar { 23 | //////////////////////////////////////////////////////////////// Average/Averaged/Averaging 24 | 25 | /** Accumulate the weighted mean of a given quantity. 26 | * 27 | * Uses the numerically stable weighted mean algorithm described in Tony Finch, [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance"]] ''Univeristy of Cambridge Computing Service, 2009''. 28 | * 29 | * Factory produces mutable [[org.dianahep.histogrammar.Averaging]] and immutable [[org.dianahep.histogrammar.Averaged]] objects. 30 | */ 31 | object Average extends Factory { 32 | val name = "Average" 33 | val help = "Accumulate the weighted mean of a given quantity." 34 | val detailedHelp = """Uses the numerically stable weighted mean algorithm described in Tony Finch, [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance"]] ''Univeristy of Cambridge Computing Service, 2009''.""" 35 | 36 | /** Create an immutable [[org.dianahep.histogrammar.Averaged]] from arguments (instead of JSON). 37 | * 38 | * @param entries Weighted number of entries (sum of all observed weights). 39 | * @param mean Weighted mean of the quantity. 40 | */ 41 | def ed(entries: Double, mean: Double) = new Averaged(entries, None, mean) 42 | 43 | /** Create an empty, mutable [[org.dianahep.histogrammar.Averaging]]. 44 | * 45 | * @param quantity Numerical function to track. 46 | */ 47 | def apply[DATUM](quantity: UserFcn[DATUM, Double]) = new Averaging(quantity, 0.0, java.lang.Double.NaN) 48 | 49 | /** Synonym for `apply`. */ 50 | def ing[DATUM](quantity: UserFcn[DATUM, Double]) = apply(quantity) 51 | 52 | /** Use [[org.dianahep.histogrammar.Averaged]] in Scala pattern-matching. */ 53 | def unapply(x: Averaged) = Some(x.mean) 54 | /** Use [[org.dianahep.histogrammar.Averaging]] in Scala pattern-matching. */ 55 | def unapply[DATUM](x: Averaging[DATUM]) = Some(x.mean) 56 | 57 | import KeySetComparisons._ 58 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 59 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "mean").maybe("name")) => 60 | val get = pairs.toMap 61 | 62 | val entries = get("entries") match { 63 | case JsonNumber(x) => x 64 | case x => throw new JsonFormatException(x, name + ".entries") 65 | } 66 | 67 | val quantityName = get.getOrElse("name", JsonNull) match { 68 | case JsonString(x) => Some(x) 69 | case JsonNull => None 70 | case x => throw new JsonFormatException(x, name + ".name") 71 | } 72 | 73 | val mean = get("mean") match { 74 | case JsonNumber(x) => x 75 | case x => throw new JsonFormatException(x, name + ".mean") 76 | } 77 | 78 | new Averaged(entries, (nameFromParent ++ quantityName).lastOption, mean) 79 | 80 | case _ => throw new JsonFormatException(json, name) 81 | } 82 | 83 | private[histogrammar] def plus(ca: Double, mua: Double, cb: Double, mub: Double) = 84 | if (ca == 0.0) 85 | (cb, mub) 86 | else if (cb == 0.0) 87 | (ca, mua) 88 | else 89 | (ca + cb, (ca*mua + cb*mub)/(ca + cb)) 90 | } 91 | 92 | /** An accumulated weighted mean of a given quantity. 93 | * 94 | * Use the factory [[org.dianahep.histogrammar.Average]] to construct an instance. 95 | * 96 | * @param entries Weighted number of entries (sum of all observed weights). 97 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 98 | * @param mean Weighted mean of the quantity. 99 | */ 100 | class Averaged private[histogrammar](val entries: Double, val quantityName: Option[String], val mean: Double) extends Container[Averaged] with NoAggregation with QuantityName { 101 | type Type = Averaged 102 | type EdType = Averaged 103 | def factory = Average 104 | 105 | if (entries < 0.0) 106 | throw new ContainerException(s"entries ($entries) cannot be negative") 107 | 108 | def zero = new Averaged(0.0, quantityName, java.lang.Double.NaN) 109 | def +(that: Averaged) = 110 | if (this.quantityName != that.quantityName) 111 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 112 | else { 113 | val (newentries, newmean) = Average.plus(this.entries, this.mean, that.entries, that.mean) 114 | new Averaged(newentries, this.quantityName, newmean) 115 | } 116 | def *(factor: Double) = 117 | if (factor.isNaN || factor <= 0.0) 118 | zero 119 | else 120 | new Averaged(factor * entries, quantityName, mean) 121 | 122 | def children = Nil 123 | 124 | def toJsonFragment(suppressName: Boolean) = JsonObject( 125 | "entries" -> JsonFloat(entries), 126 | "mean" -> JsonFloat(mean)). 127 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 128 | 129 | override def toString() = s"""""" 130 | override def equals(that: Any) = that match { 131 | case that: Averaged => this.entries === that.entries && this.quantityName == that.quantityName && this.mean === that.mean 132 | case _ => false 133 | } 134 | override def hashCode() = (entries, quantityName, mean).hashCode 135 | } 136 | 137 | /** Accumulating a weighted mean of a given quantity. 138 | * 139 | * Use the factory [[org.dianahep.histogrammar.Average]] to construct an instance. 140 | * 141 | * @param quantity Numerical function to track. 142 | * @param entries Weighted number of entries (sum of all weights). 143 | * @param mean Cumulative weighted mean (accrued with a numerically stable algorithm). 144 | */ 145 | class Averaging[DATUM] private[histogrammar](val quantity: UserFcn[DATUM, Double], var entries: Double, var mean: Double) extends Container[Averaging[DATUM]] with AggregationOnData with NumericalQuantity[DATUM] { 146 | type Type = Averaging[DATUM] 147 | type EdType = Averaged 148 | type Datum = DATUM 149 | def factory = Average 150 | 151 | if (entries < 0.0) 152 | throw new ContainerException(s"entries ($entries) cannot be negative") 153 | 154 | def zero = new Averaging[DATUM](quantity, 0.0, java.lang.Double.NaN) 155 | def +(that: Averaging[DATUM]) = 156 | if (this.quantity.name != that.quantity.name) 157 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 158 | else { 159 | val (newentries, newmean) = Average.plus(this.entries, this.mean, that.entries, that.mean) 160 | new Averaging(this.quantity, newentries, newmean) 161 | } 162 | def *(factor: Double) = 163 | if (factor.isNaN || factor <= 0.0) 164 | zero 165 | else 166 | new Averaging(quantity, factor * entries, mean) 167 | 168 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 169 | checkForCrossReferences() 170 | if (weight > 0.0) { 171 | val q = quantity(datum) 172 | 173 | // no possibility of exception from here on out (for rollback) 174 | if (entries == 0.0) 175 | mean = q 176 | entries += weight 177 | 178 | if (mean.isNaN || q.isNaN) 179 | mean = java.lang.Double.NaN 180 | 181 | else if (mean.isInfinite || q.isInfinite) { 182 | if (mean.isInfinite && q.isInfinite && mean * q < 0.0) 183 | mean = java.lang.Double.NaN 184 | else if (q.isInfinite) 185 | mean = q 186 | else 187 | { } 188 | if (entries.isInfinite || entries.isNaN) 189 | mean = java.lang.Double.NaN 190 | } 191 | 192 | else { 193 | val delta = q - mean 194 | val shift = delta * weight / entries 195 | mean += shift 196 | } 197 | } 198 | } 199 | 200 | def children = Nil 201 | 202 | def toJsonFragment(suppressName: Boolean) = JsonObject( 203 | "entries" -> JsonFloat(entries), 204 | "mean" -> JsonFloat(mean)). 205 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 206 | 207 | override def toString() = s"""""" 208 | override def equals(that: Any) = that match { 209 | case that: Averaging[DATUM] => this.quantity == that.quantity && this.entries === that.entries && this.mean === that.mean 210 | case _ => false 211 | } 212 | override def hashCode() = (quantity, entries, mean).hashCode 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/categorize.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.collection.mutable 18 | import scala.language.postfixOps 19 | import scala.language.existentials 20 | 21 | import org.dianahep.histogrammar.json._ 22 | import org.dianahep.histogrammar.util._ 23 | 24 | package histogrammar { 25 | //////////////////////////////////////////////////////////////// Categorize/Categorized/Categorizing 26 | 27 | /** Split a given quantity by its categorical value and fill only one category per datum. 28 | * 29 | * A bar chart may be thought of as a histogram with string-valued (categorical) bins, so this is the equivalent of [[org.dianahep.histogrammar.Bin]] for bar charts. The order of the strings is deferred to the visualization stage. 30 | * 31 | * Unlike [[org.dianahep.histogrammar.SparselyBin]], this aggregator has the potential to use unlimited memory. A large number of ''distinct'' categories can generate many unwanted bins. 32 | * 33 | * Factory produces mutable [[org.dianahep.histogrammar.Categorizing]] and immutable [[org.dianahep.histogrammar.Categorized]] objects. 34 | */ 35 | object Categorize extends Factory { 36 | val name = "Categorize" 37 | val help = "Split a given quantity by its categorical value and fill only one category per datum." 38 | val detailedHelp = """A bar chart may be thought of as a histogram with string-valued (categorical) bins, so this is the equivalent of [[org.dianahep.histogrammar.Bin]] for bar charts. The order of the strings is deferred to the visualization stage. 39 | 40 | Unlike [[org.dianahep.histogrammar.SparselyBin]], this aggregator has the potential to use unlimited memory. A large number of ''distinct'' categories can generate many unwanted bins.""" 41 | 42 | /** Create an immutable [[org.dianahep.histogrammar.Categorized]] from arguments (instead of JSON). 43 | * 44 | * @param entries Weighted number of entries (sum of all observed weights). 45 | * @param contentType Name of the intended content; used as a placeholder in cases with zero bins (due to no observed data). 46 | * @param bins String category and the associated container of values associated with it. 47 | */ 48 | def ed[V <: Container[V] with NoAggregation](entries: Double, contentType: String, bins: Map[String, V]) = new Categorized(entries, None, contentType, bins) 49 | 50 | /** Create an empty, mutable [[org.dianahep.histogrammar.Categorizing]]. 51 | * 52 | * @param quantity Numerical function to split into bins. 53 | * @param value New value (note the `=>`: expression is reevaluated every time a new value is needed). 54 | */ 55 | def apply[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}](quantity: UserFcn[DATUM, String], value: => V = Count()) = 56 | new Categorizing(quantity, 0.0, value, mutable.HashMap[String, V]()) 57 | 58 | /** Synonym for `apply`. */ 59 | def ing[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}](quantity: UserFcn[DATUM, String], value: => V = Count()) = 60 | apply(quantity, value) 61 | 62 | import KeySetComparisons._ 63 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 64 | case JsonObject(bins @ _*) if (bins.keySet has Set("entries", "bins:type", "bins").maybe("name").maybe("bins:name")) => 65 | val get = bins.toMap 66 | 67 | val entries = get("entries") match { 68 | case JsonNumber(x) => x 69 | case x => throw new JsonFormatException(x, name + ".entries") 70 | } 71 | 72 | val quantityName = get.getOrElse("name", JsonNull) match { 73 | case JsonString(x) => Some(x) 74 | case JsonNull => None 75 | case x => throw new JsonFormatException(x, name + ".name") 76 | } 77 | 78 | val (contentType, factory) = get("bins:type") match { 79 | case JsonString(name) => (name, Factory(name)) 80 | case x => throw new JsonFormatException(x, name + ".bins:type") 81 | } 82 | 83 | val dataName = get.getOrElse("bins:name", JsonNull) match { 84 | case JsonString(x) => Some(x) 85 | case JsonNull => None 86 | case x => throw new JsonFormatException(x, name + ".bins:name") 87 | } 88 | 89 | val thebins = 90 | get("bins") match { 91 | case JsonObject(categoryPairs @ _*) => 92 | categoryPairs map { 93 | case (JsonString(category), value) => 94 | category -> factory.fromJsonFragment(value, dataName) 95 | } toMap 96 | case x => throw new JsonFormatException(x, name + ".bins") 97 | } 98 | 99 | new Categorized(entries, (nameFromParent ++ quantityName).lastOption, contentType, thebins.asInstanceOf[Map[String, C] forSome {type C <: Container[C] with NoAggregation}]) 100 | 101 | case _ => throw new JsonFormatException(json, name) 102 | } 103 | } 104 | 105 | /** An accumulated quantity that was split by its categorical (string-based) values, filling only one category per datum. 106 | * 107 | * Use the factory [[org.dianahep.histogrammar.Categorize]] to construct an instance. 108 | * 109 | * @param entries Weighted number of entries (sum of all observed weights). 110 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 111 | * @param contentType Name of the intended content; used as a placeholder in cases with zero bins (due to no observed data). 112 | * @param bins String category and the associated container of values associated with it. 113 | */ 114 | class Categorized[V <: Container[V] with NoAggregation] private[histogrammar](val entries: Double, val quantityName: Option[String], contentType: String, val bins: Map[String, V]) extends Container[Categorized[V]] with NoAggregation with QuantityName { 115 | type Type = Categorized[V] 116 | type EdType = Categorized[V] 117 | def factory = Categorize 118 | 119 | if (entries < 0.0) 120 | throw new ContainerException(s"entries ($entries) cannot be negative") 121 | 122 | /** Number of `bins`. */ 123 | def size = bins.size 124 | /** Iterable over the keys of the `bins`. */ 125 | def keys: Iterable[String] = bins.toIterable.map(_._1) 126 | /** Iterable over the values of the `bins`. */ 127 | def values: Iterable[Container[V]] = bins.toIterable.map(_._2) 128 | /** Set of keys among the `bins`. */ 129 | def keySet: Set[String] = keys.toSet 130 | /** Attempt to get key `x`, throwing an exception if it does not exist. */ 131 | def apply(x: String) = bins(x) 132 | /** Attempt to get key `x`, returning `None` if it does not exist. */ 133 | def get(x: String) = bins.get(x) 134 | /** Attempt to get key `x`, returning an alternative if it does not exist. */ 135 | def getOrElse(x: String, default: => V) = bins.getOrElse(x, default) 136 | 137 | def zero = new Categorized[V](0.0, quantityName, contentType, Map[String, V]()) 138 | def +(that: Categorized[V]) = 139 | if (this.quantityName != that.quantityName) 140 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 141 | else 142 | new Categorized( 143 | this.entries + that.entries, 144 | this.quantityName, 145 | contentType, 146 | (this.keySet union that.keySet).toSeq map {key => 147 | if ((this.bins contains key) && (that.bins contains key)) 148 | (key, this.bins(key) + that.bins(key)) 149 | else if (this.bins contains key) 150 | (key, this.bins(key)) 151 | else 152 | (key, that.bins(key)) 153 | } toMap) 154 | def *(factor: Double) = 155 | if (factor.isNaN || factor <= 0.0) 156 | zero 157 | else 158 | new Categorized[V](factor * entries, quantityName, contentType, bins map {case (c, v) => (c, v * factor)}) 159 | 160 | def children = values.toList 161 | 162 | def toJsonFragment(suppressName: Boolean) = JsonObject( 163 | "entries" -> JsonFloat(entries), 164 | "bins:type" -> JsonString(contentType), 165 | "bins" -> JsonObject(bins.toSeq map {case (k, v) => (JsonString(k), v.toJsonFragment(true))}: _*)). 166 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))). 167 | maybe(JsonString("bins:name") -> (bins.headOption match {case Some((k, v: QuantityName)) => v.quantityName.map(JsonString(_)); case _ => None})) 168 | 169 | override def toString() = s"""""" 170 | override def equals(that: Any) = that match { 171 | case that: Categorized[V] => this.entries === that.entries && this.quantityName == that.quantityName && this.bins == that.bins 172 | case _ => false 173 | } 174 | override def hashCode() = (entries, quantityName, bins).hashCode() 175 | } 176 | 177 | /** Accumulating a quantity by splitting it by its categorical (string-based) value and filling only one category per datum. 178 | * 179 | * Use the factory [[org.dianahep.histogrammar.Categorize]] to construct an instance. 180 | * 181 | * @param quantity Numerical function to track. 182 | * @param entries Weighted number of entries (sum of all observed weights). 183 | * @param value New value (note the `=>`: expression is reevaluated every time a new value is needed). 184 | * @param bins Map of string category and the associated container of values associated with it. 185 | */ 186 | class Categorizing[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}] private[histogrammar](val quantity: UserFcn[DATUM, String], var entries: Double, value: => V, val bins: mutable.HashMap[String, V]) extends Container[Categorizing[DATUM, V]] with AggregationOnData with CategoricalQuantity[DATUM] { 187 | 188 | protected val v = value 189 | type Type = Categorizing[DATUM, V] 190 | type EdType = Categorized[v.EdType] 191 | type Datum = DATUM 192 | def factory = Categorize 193 | 194 | if (entries < 0.0) 195 | throw new ContainerException(s"entries ($entries) cannot be negative") 196 | 197 | /** Number of `bins`. */ 198 | def size = bins.size 199 | /** Iterable over the keys of the `bins`. */ 200 | def keys: Iterable[String] = bins.toIterable.map(_._1) 201 | /** Iterable over the values of the `bins`. */ 202 | def values: Iterable[V] = bins.toIterable.map(_._2) 203 | /** Set of keys among the `bins`. */ 204 | def keySet: Set[String] = keys.toSet 205 | /** Attempt to get key `x`, throwing an exception if it does not exist. */ 206 | def apply(x: String) = bins(x) 207 | /** Attempt to get key `x`, returning `None` if it does not exist. */ 208 | def get(x: String) = bins.get(x) 209 | /** Attempt to get key `x`, returning an alternative if it does not exist. */ 210 | def getOrElse(x: String, default: => V) = bins.getOrElse(x, default) 211 | 212 | def zero = new Categorizing[DATUM, V](quantity, 0.0, value, mutable.HashMap[String, V]()) 213 | def +(that: Categorizing[DATUM, V]) = 214 | if (this.quantity.name != that.quantity.name) 215 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 216 | else 217 | new Categorizing[DATUM, V]( 218 | this.quantity, 219 | this.entries + that.entries, 220 | this.value, 221 | mutable.HashMap[String, V]((this.keySet union that.keySet).toSeq map {key => 222 | if ((this.bins contains key) && (that.bins contains key)) 223 | (key, this.bins(key) + that.bins(key)) 224 | else if (this.bins contains key) 225 | (key, this.bins(key)) 226 | else 227 | (key, that.bins(key)) 228 | }: _*)) 229 | def *(factor: Double) = 230 | if (factor.isNaN || factor <= 0.0) 231 | zero 232 | else 233 | new Categorizing[DATUM, V](quantity, factor * entries, value, mutable.HashMap[String, V](bins.toSeq.map({case (c, v) => (c, v * factor)}): _*)) 234 | 235 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 236 | checkForCrossReferences() 237 | if (weight > 0.0) { 238 | val qd = quantity(datum) 239 | val q = if (qd == null) "NaN" else qd 240 | if (!(bins contains q)) 241 | bins(q) = value.zero 242 | bins(q).fill(datum, weight) 243 | 244 | // no possibility of exception from here on out (for rollback) 245 | entries += weight 246 | } 247 | } 248 | 249 | def children = value :: values.toList 250 | 251 | def toJsonFragment(suppressName: Boolean) = JsonObject( 252 | "entries" -> JsonFloat(entries), 253 | "bins:type" -> JsonString(value.factory.name), 254 | "bins" -> JsonObject(bins.toSeq map {case (k, v) => (JsonString(k), v.toJsonFragment(true))}: _*)). 255 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))). 256 | maybe(JsonString("bins:name") -> List(value).collect({case v: AnyQuantity[_, _] => v.quantity.name}).headOption.flatten.map(JsonString(_))) 257 | 258 | override def toString() = s"""""" 259 | override def equals(that: Any) = that match { 260 | case that: Categorizing[DATUM, V] => this.quantity == that.quantity && this.entries === that.entries && this.bins == that.bins 261 | case _ => false 262 | } 263 | override def hashCode() = (quantity, entries, bins).hashCode() 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/count.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar._ 20 | import org.dianahep.histogrammar.json._ 21 | import org.dianahep.histogrammar.util._ 22 | 23 | package histogrammar { 24 | //////////////////////////////////////////////////////////////// Count/Counted/Counting 25 | 26 | /** Count entries by accumulating the sum of all observed weights or a sum of transformed weights (e.g. sum of squares of weights). 27 | * 28 | * An optional `transform` function can be applied to the weights before summing. To accumulate the sum of squares of weights, use `{x: Double => x*x}`, for instance. This is unlike any other primitive's `quantity` function in that its domain is the ''weights'' (always double), not ''data'' (any type). 29 | * 30 | * Factory produces mutable [[org.dianahep.histogrammar.Counting]] and immutable [[org.dianahep.histogrammar.Counted]] objects. 31 | */ 32 | object Count extends Factory { 33 | val name = "Count" 34 | val help = "Count entries by accumulating the sum of all observed weights or a sum of transformed weights (e.g. sum of squares of weights)." 35 | val detailedHelp = """An optional `transform` function can be applied to the weights before summing. To accumulate the sum of squares of weights, use `{x: Double => x*x}`, for instance. This is unlike any other primitive's `quantity` function in that its domain is the ''weights'' (always double), not ''data'' (any type).""" 36 | 37 | object Identity extends UserFcn[Double, Double] { 38 | def name = None 39 | def hasCache = false 40 | def apply[SUB <: Double](x: SUB) = x 41 | } 42 | 43 | /** Create an immutable [[org.dianahep.histogrammar.Counted]] from arguments (instead of JSON). 44 | * 45 | * @param entries Weighted number of entries (sum of all observed weights). 46 | */ 47 | def ed(entries: Double) = new Counted(entries) 48 | 49 | /** Create an empty, mutable [[org.dianahep.histogrammar.Counting]]. 50 | * 51 | * @param transform Transform each weight before adding. For instance, to collect a sum of squared weights, pass {x: Double: x*x} as `transform`. 52 | */ 53 | def apply(transform: UserFcn[Double, Double] = Identity) = new Counting(0.0, transform) 54 | 55 | /** Synonym for `apply`. */ 56 | def ing(transform: UserFcn[Double, Double] = Identity) = apply(transform) 57 | 58 | /** Use [[org.dianahep.histogrammar.Counted]] in Scala pattern-matching. */ 59 | def unapply(x: Counted) = Some(x.entries) 60 | /** Use [[org.dianahep.histogrammar.Counting]] in Scala pattern-matching. */ 61 | def unapply(x: Counting) = Some(x.entries) 62 | 63 | import KeySetComparisons._ 64 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 65 | case JsonNumber(entries) => new Counted(entries) 66 | case _ => throw new JsonFormatException(json, name) 67 | } 68 | 69 | // // Confidence interval formulas for counting statistics (e.g. bin-by-bin). 70 | // // For more information, see https://www.ine.pt/revstat/pdf/rs120203.pdf 71 | 72 | // /** Compute the normal confidence interval on `number` (where `number` is a count of events in a Poisson process). 73 | // * 74 | // * The normal confidence interval is `number + z * sqrt(number)`. It has good properties for large numbers. 75 | // * 76 | // * @param z Informally, the "number of sigmas." Formally, `z` is the `1 - alpha/2` quantile of the standard normal distribution, where `alpha` is the error quantile. For example, for a 95% confidence level, the error (`alpha`) is 5%, so z = 1.96. Note that `z` is signed. `z = 0` yields the central value, `z = -1` yields "one sigma" below the central value, and `z = 1` yields "one sigma" above the central value. 77 | // */ 78 | // def normalConfidenceInterval(number: Double, z: Double): Double = normalConfidenceInterval(number, List(z): _*).head 79 | 80 | // /** Compute the normal confidence interval on `number` (where `number` is a count of events in a Poisson process). 81 | // * 82 | // * The normal confidence interval is `number + z * sqrt(number)`. It has good properties for large numbers. 83 | // * 84 | // * @param z Informally, the "number of sigmas." Formally, `z` is the `1 - alpha/2` quantile of the standard normal distribution, where `alpha` is the error quantile. For example, for a 95% confidence level, the error (`alpha`) is 5%, so z = 1.96. Note that `z` is signed. `z = 0` yields the central value, `z = -1` yields "one sigma" below the central value, and `z = 1` yields "one sigma" above the central value. 85 | // */ 86 | // def normalConfidenceInterval(number: Double, zs: Double*): Seq[Double] = 87 | // zs.map(z => number + z * Math.sqrt(number)) 88 | 89 | // /** Compute the Wilson confidence interval on `number` (where `number` is a count of events in a Poisson process). 90 | // * 91 | // * The Wilson confidence interval is `number * (1 - 1/(9*number) + z / (3 * sqrt(number)))**3`. It has good properties for small integers, but not for numbers below one. 92 | // * 93 | // * @param z Informally, the "number of sigmas." Formally, `z` is the `1 - alpha/2` quantile of the standard normal distribution, where `alpha` is the error quantile. For example, for a 95% confidence level, the error (`alpha`) is 5%, so z = 1.96. Note that `z` is signed. `z = 0` yields the central value, `z = -1` yields "one sigma" below the central value, and `z = 1` yields "one sigma" above the central value. 94 | // */ 95 | // def wilsonConfidenceInterval(number: Double, z: Double): Double = wilsonConfidenceInterval(number, List(z): _*).head 96 | 97 | // /** Compute the Wilson confidence interval on `number` (where `number` is a count of events in a Poisson process). 98 | // * 99 | // * The Wilson confidence interval is `number * (1 - 1/(9*number) + z / (3 * sqrt(number)))**3`. It has good properties for small integers, but not for numbers below one. 100 | // * 101 | // * @param z Informally, the "number of sigmas." Formally, `z` is the `1 - alpha/2` quantile of the standard normal distribution, where `alpha` is the error quantile. For example, for a 95% confidence level, the error (`alpha`) is 5%, so z = 1.96. Note that `z` is signed. `z = 0` yields the central value, `z = -1` yields "one sigma" below the central value, and `z = 1` yields "one sigma" above the central value. 102 | // */ 103 | // def wilsonConfidenceInterval(number: Double, zs: Double*): Seq[Double] = 104 | // zs.map(z => number * Math.pow(1.0 - 1.0/(9.0*number) + z / (3.0 * Math.sqrt(number)), 3)) 105 | 106 | } 107 | 108 | /** An accumulated count (sum of weights) of data, ignoring its content. 109 | * 110 | * Use the factory [[org.dianahep.histogrammar.Count]] to construct an instance. 111 | * 112 | * @param entries Weighted number of entries (sum of all weights). 113 | */ 114 | class Counted private[histogrammar](val entries: Double) extends Container[Counted] with NoAggregation { 115 | type Type = Counted 116 | type EdType = Counted 117 | def factory = Count 118 | 119 | if (entries < 0.0) 120 | throw new ContainerException(s"entries ($entries) cannot be negative") 121 | 122 | def zero = new Counted(0.0) 123 | def +(that: Counted): Counted = new Counted(this.entries + that.entries) 124 | def *(factor: Double) = 125 | if (factor.isNaN || factor <= 0.0) 126 | zero 127 | else 128 | new Counted(factor * entries) 129 | 130 | def children = Nil 131 | 132 | def toJsonFragment(suppressName: Boolean) = JsonFloat(entries) 133 | 134 | override def toString() = s"" 135 | override def equals(that: Any) = that match { 136 | case that: Counted => this.entries === that.entries 137 | case _ => false 138 | } 139 | override def hashCode() = entries.hashCode 140 | } 141 | 142 | /** Accumulating a count (sum of weights) of data, ignoring its content. 143 | * 144 | * Use the factory [[org.dianahep.histogrammar.Count]] to construct an instance. 145 | * 146 | * This is the only container with [[org.dianahep.histogrammar.Aggregation]] that doesn't have a configurable data type: its `Datum` is `Any`. It is primarily for the sake of this container that `Aggregation` is contravariant. 147 | * 148 | * @param entries Weighted number of entries (sum of all observed weights). 149 | */ 150 | class Counting private[histogrammar](var entries: Double, val transform: UserFcn[Double, Double]) extends Container[Counting] with Aggregation { 151 | type Type = Counting 152 | type EdType = Counted 153 | type Datum = Any 154 | def factory = Count 155 | 156 | if (entries < 0.0) 157 | throw new ContainerException(s"entries ($entries) cannot be negative") 158 | 159 | def zero = new Counting(0.0, transform) 160 | def +(that: Counting): Counting = new Counting(this.entries + that.entries, transform) 161 | def *(factor: Double) = 162 | if (!transform.isInstanceOf[Count.Identity.type]) 163 | throw new ContainerException("Cannot scalar-multiply Counting with a non-identity transform.") 164 | else if (factor.isNaN || factor <= 0.0) 165 | zero 166 | else 167 | new Counting(factor * entries, transform) 168 | 169 | def fill[SUB <: Any](datum: SUB, weight: Double = 1.0): Unit = { 170 | checkForCrossReferences() 171 | // no possibility of exception from here on out (for rollback) 172 | if (weight > 0.0) 173 | entries += transform(weight) 174 | } 175 | 176 | def children = Nil 177 | 178 | def toJsonFragment(suppressName: Boolean) = JsonFloat(entries) 179 | 180 | override def toString() = s"" 181 | override def equals(that: Any) = that match { 182 | case that: Counting => this.entries === that.entries && this.transform == that.transform 183 | case _ => false 184 | } 185 | override def hashCode() = (entries, transform).hashCode 186 | } 187 | } 188 | 189 | 190 | 191 | 192 | // Note on statistics: 193 | 194 | // Could follow http://statpages.info/confint.html to provide exact Clopper-Pearson bounds for Poisson (Count) and Binomial (Fraction) confidence levels. 195 | 196 | // Inefficient calculation (JavaScript) that assumes the number of entries is an integer (and scales with it!): 197 | 198 | // var vTL=2.5; var vTU=2.5; var vCL=95 199 | 200 | // function CalcCL(form) { 201 | // vTL = eval(form.TL.value) 202 | // vTU = eval(form.TU.value) 203 | // vCL = 100-(vTL+vTU) 204 | // form.CL.value = ''+vCL 205 | // } 206 | 207 | // function CalcTails(form) { 208 | // vCL = eval(form.CL.value) 209 | // vTU = (100-vCL)/2 210 | // vTL = vTU 211 | // form.TL.value = ''+vTL 212 | // form.TU.value = ''+vTU 213 | // } 214 | 215 | // function CalcBin(form) { 216 | // var vx = eval(form.x.value) 217 | // var vN = eval(form.N.value) 218 | // var vP = vx/vN 219 | // form.P.value = Fmt(vP) 220 | // if(vx==0) { 221 | // form.DL.value = "0.0000" 222 | // } 223 | // else { 224 | // var v=vP/2; 225 | // vsL=0; 226 | // vsH=vP; 227 | // var p=vTL/100; 228 | // while ((vsH-vsL)>1e-5) { 229 | // if(BinP(vN,v,vx,vN)>p) { 230 | // vsH=v; 231 | // v=(vsL+v)/2 232 | // } 233 | // else { 234 | // vsL=v; 235 | // v=(v+vsH)/2 236 | // } 237 | // } 238 | // form.DL.value = Fmt(v) 239 | // } 240 | // if(vx==vN) { 241 | // form.DU.value = "1.0000" 242 | // } 243 | // else { 244 | // var v=(1+vP)/2; 245 | // vsL=vP; 246 | // vsH=1; 247 | // var p=vTU/100 248 | // while ((vsH-vsL)>1e-5) { 249 | // if (BinP(vN,v,0,vx)=x1 & k<=x2) { 271 | // s=s+v 272 | // } 273 | // if (tot>1e30) { 274 | // s=s/1e30; 275 | // tot=tot/1e30; 276 | // v=v/1e30 277 | // } 278 | // k=k+1; 279 | // v=v*q*(N+1-k)/k 280 | // } 281 | // return s/tot 282 | // } 283 | 284 | // function CalcPois(form) { 285 | // var vZ = eval(form.Z.value) 286 | // if(vZ==0) { 287 | // form.QL.value = "0.0000" 288 | // } 289 | // else { 290 | // var v=0.5; 291 | // var dv=0.5; 292 | // var p=vTL/100 293 | // while (dv>1e-7) { 294 | // dv=dv/2; 295 | // if (PoisP((1+vZ)*v/(1-v),vZ,1e10)>p) { 296 | // v=v-dv 297 | // } 298 | // else { 299 | // v=v+dv 300 | // } 301 | // } 302 | // form.QL.value = Fmt((1+vZ)*v/(1-v)) 303 | // } 304 | // if(vTU==0) { 305 | // form.QU.value = "Infinity" 306 | // } 307 | // else { 308 | // var v=0.5; 309 | // var dv=0.5; 310 | // var p=vTU/100 311 | // while (dv>1e-7) { 312 | // dv=dv/2; 313 | // if (PoisP((1+vZ)*v/(1-v),0,vZ)(tot*1e-10)) { 327 | // tot=tot+q 328 | // if(k>=x1 & k<=x2) { 329 | // s=s+q 330 | // } 331 | // if (tot>1e30) { 332 | // s=s/1e30; 333 | // tot=tot/1e30; 334 | // q=q/1e30 335 | // } 336 | // k=k+1; 337 | // q=q*Z/k 338 | // } 339 | // return s/tot 340 | // } 341 | 342 | // function Fmt(x) { 343 | // var v 344 | // if (x>=0) { 345 | // v=''+(x+0.00005) 346 | // } 347 | // else { 348 | // v=''+(x-0.00005) 349 | // } 350 | // return v.substring(0,v.indexOf('.')+5) 351 | // } 352 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/deviate.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar.json._ 20 | import org.dianahep.histogrammar.util._ 21 | 22 | package histogrammar { 23 | //////////////////////////////////////////////////////////////// Deviate/Deviated/Deviating 24 | 25 | /** Accumulate the weighted mean and weighted variance of a given quantity. 26 | * 27 | * The variance is computed around the mean, not zero. 28 | * 29 | * Uses the numerically stable weighted mean and weighted variance algorithms described in Tony Finch, [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance"]] ''Univeristy of Cambridge Computing Service, 2009''. 30 | * 31 | * Factory produces mutable [[org.dianahep.histogrammar.Deviating]] and immutable [[org.dianahep.histogrammar.Deviated]] objects. 32 | */ 33 | object Deviate extends Factory { 34 | val name = "Deviate" 35 | val help = "Accumulate the weighted mean and weighted variance of a given quantity." 36 | val detailedHelp = """The variance is computed around the mean, not zero. 37 | 38 | Uses the numerically stable weighted mean and weighted variance algorithms described in Tony Finch, [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance"]] ''Univeristy of Cambridge Computing Service, 2009''.""" 39 | 40 | /** Create an immutable [[org.dianahep.histogrammar.Deviated]] from arguments (instead of JSON). 41 | * 42 | * @param entries Weighted number of entries (sum of all observed weights). 43 | * @param mean Weighted mean of the quantity. 44 | * @param variance Weighted variance of the quantity. 45 | */ 46 | def ed(entries: Double, mean: Double, variance: Double) = new Deviated(entries, None, mean, variance) 47 | 48 | /** Create an empty, mutable [[org.dianahep.histogrammar.Deviating]]. 49 | * 50 | * @param quantity Numerical function to track. 51 | */ 52 | def apply[DATUM](quantity: UserFcn[DATUM, Double]) = new Deviating(quantity, 0.0, java.lang.Double.NaN, java.lang.Double.NaN) 53 | 54 | /** Synonym for `apply`. */ 55 | def ing[DATUM](quantity: UserFcn[DATUM, Double]) = apply(quantity) 56 | 57 | /** Use [[org.dianahep.histogrammar.Deviated]] in Scala pattern-matching. */ 58 | def unapply(x: Deviated) = Some(x.variance) 59 | /** Use [[org.dianahep.histogrammar.Deviating]] in Scala pattern-matching. */ 60 | def unapply[DATUM](x: Deviating[DATUM]) = Some(x.variance) 61 | 62 | import KeySetComparisons._ 63 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 64 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "mean", "variance").maybe("name")) => 65 | val get = pairs.toMap 66 | 67 | val entries = get("entries") match { 68 | case JsonNumber(x) => x 69 | case x => throw new JsonFormatException(x, name + ".entries") 70 | } 71 | 72 | val quantityName = get.getOrElse("name", JsonNull) match { 73 | case JsonString(x) => Some(x) 74 | case JsonNull => None 75 | case x => throw new JsonFormatException(x, name + ".name") 76 | } 77 | 78 | val mean = get("mean") match { 79 | case JsonNumber(x) => x 80 | case x => throw new JsonFormatException(x, name + ".mean") 81 | } 82 | 83 | val variance = get("variance") match { 84 | case JsonNumber(x) => x 85 | case x => throw new JsonFormatException(x, name + ".variance") 86 | } 87 | 88 | new Deviated(entries, (nameFromParent ++ quantityName).lastOption, mean, variance) 89 | 90 | case _ => throw new JsonFormatException(json, name) 91 | } 92 | 93 | private[histogrammar] def plus(ca: Double, mua: Double, sa: Double, cb: Double, mub: Double, sb: Double) = 94 | if (ca == 0.0) 95 | (cb, mub, sb / cb) 96 | else if (cb == 0.0) 97 | (ca, mua, sa / ca) 98 | else { 99 | val muab = (ca*mua + cb*mub)/(ca + cb) 100 | val sab = sa + sb + ca*mua*mua + cb*mub*mub - 2.0*muab*(ca*mua + cb*mub) + muab*muab*(ca + cb) 101 | (ca + cb, muab, sab / (ca + cb)) 102 | } 103 | } 104 | 105 | /** An accumulated weighted variance (and mean) of a given quantity. 106 | * 107 | * Use the factory [[org.dianahep.histogrammar.Deviate]] to construct an instance. 108 | * 109 | * @param entries Weighted number of entries (sum of all observed weights). 110 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 111 | * @param mean Weighted mean of the quantity. 112 | * @param variance Weighted variance of the quantity. 113 | * 114 | * The implementation of this container uses a numerically stable variance as described by Tony Finch in [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance,"]] ''Univeristy of Cambridge Computing Service,'' 2009. 115 | */ 116 | class Deviated private[histogrammar](val entries: Double, val quantityName: Option[String], val mean: Double, val variance: Double) extends Container[Deviated] with NoAggregation with QuantityName { 117 | type Type = Deviated 118 | type EdType = Deviated 119 | def factory = Deviate 120 | 121 | if (entries < 0.0) 122 | throw new ContainerException(s"entries ($entries) cannot be negative") 123 | 124 | def zero = new Deviated(0.0, quantityName, java.lang.Double.NaN, java.lang.Double.NaN) 125 | def +(that: Deviated) = 126 | if (this.quantityName != that.quantityName) 127 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 128 | else { 129 | val (newentries, newmean, newvariance) = Deviate.plus(this.entries, this.mean, this.variance * this.entries, 130 | that.entries, that.mean, that.variance * that.entries) 131 | new Deviated(newentries, this.quantityName, newmean, newvariance) 132 | } 133 | def *(factor: Double) = 134 | if (factor.isNaN || factor <= 0.0) 135 | zero 136 | else 137 | new Deviated(factor * entries, quantityName, mean, variance) 138 | 139 | def children = Nil 140 | 141 | def toJsonFragment(suppressName: Boolean) = JsonObject( 142 | "entries" -> JsonFloat(entries), 143 | "mean" -> JsonFloat(mean), 144 | "variance" -> JsonFloat(variance)). 145 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 146 | 147 | override def toString() = s"" 148 | override def equals(that: Any) = that match { 149 | case that: Deviated => this.entries === that.entries && this.quantityName == that.quantityName && this.mean === that.mean && this.variance === that.variance 150 | case _ => false 151 | } 152 | override def hashCode() = (entries, quantityName, mean, variance).hashCode 153 | } 154 | 155 | /** Accumulating a weighted variance (and mean) of a given quantity. 156 | * 157 | * Use the factory [[org.dianahep.histogrammar.Deviate]] to construct an instance. 158 | * 159 | * @param quantity Numerical function to track. 160 | * @param entries Weighted number of entries (sum of all observed weights). 161 | * @param mean Weighted mean of the quantity. 162 | * @param _variance Weighted variance of the quantity. 163 | * 164 | * The implementation of this container uses a numerically stable variance as described by Tony Finch in [[http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/doc/antiforgery/stats.pdf "Incremental calculation of weighted mean and variance,"]] ''Univeristy of Cambridge Computing Service,'' 2009. 165 | */ 166 | class Deviating[DATUM] private[histogrammar](val quantity: UserFcn[DATUM, Double], var entries: Double, var mean: Double, _variance: Double) extends Container[Deviating[DATUM]] with AggregationOnData with NumericalQuantity[DATUM] { 167 | type Type = Deviating[DATUM] 168 | type EdType = Deviated 169 | type Datum = DATUM 170 | def factory = Deviate 171 | 172 | if (entries < 0.0) 173 | throw new ContainerException(s"entries ($entries) cannot be negative") 174 | 175 | private var varianceTimesEntries = entries * _variance 176 | 177 | /** weighted variance of the quantity */ 178 | def variance = 179 | if (entries == 0.0) 180 | _variance 181 | else 182 | varianceTimesEntries / entries 183 | 184 | def variance_=(_variance: Double): Unit = { 185 | varianceTimesEntries = entries * _variance 186 | } 187 | 188 | def zero = new Deviating[DATUM](quantity, 0.0, java.lang.Double.NaN, java.lang.Double.NaN) 189 | def +(that: Deviating[DATUM]) = 190 | if (this.quantity.name != that.quantity.name) 191 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 192 | else { 193 | val (newentries, newmean, newvariance) = Deviate.plus(this.entries, this.mean, this.variance * this.entries, 194 | that.entries, that.mean, that.variance * that.entries) 195 | new Deviating[DATUM](this.quantity, newentries, newmean, newvariance) 196 | } 197 | def *(factor: Double) = 198 | if (factor.isNaN || factor <= 0.0) 199 | zero 200 | else 201 | new Deviating[DATUM](quantity, factor * entries, mean, variance) 202 | 203 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 204 | checkForCrossReferences() 205 | if (weight > 0.0) { 206 | val q = quantity(datum) 207 | 208 | // no possibility of exception from here on out (for rollback) 209 | if (entries == 0.0) { 210 | mean = q 211 | varianceTimesEntries = 0.0 212 | } 213 | entries += weight 214 | 215 | if (mean.isNaN || q.isNaN) { 216 | mean = java.lang.Double.NaN 217 | varianceTimesEntries = java.lang.Double.NaN 218 | } 219 | 220 | else if (mean.isInfinite || q.isInfinite) { 221 | if (mean.isInfinite && q.isInfinite && mean * q < 0.0) 222 | mean = java.lang.Double.NaN 223 | else if (q.isInfinite) 224 | mean = q 225 | else 226 | { } 227 | if (entries.isInfinite || entries.isNaN) 228 | mean = java.lang.Double.NaN 229 | 230 | varianceTimesEntries = java.lang.Double.NaN 231 | } 232 | 233 | else { 234 | val delta = q - mean 235 | val shift = delta * weight / entries 236 | mean += shift 237 | varianceTimesEntries += weight * delta * (q - mean) // old delta times new delta 238 | } 239 | } 240 | } 241 | 242 | def children = Nil 243 | 244 | def toJsonFragment(suppressName: Boolean) = JsonObject( 245 | "entries" -> JsonFloat(entries), 246 | "mean" -> JsonFloat(mean), 247 | "variance" -> JsonFloat(variance)). 248 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 249 | 250 | override def toString() = s"" 251 | override def equals(that: Any) = that match { 252 | case that: Deviating[DATUM] => this.quantity == that.quantity && this.entries === that.entries && this.mean === that.mean && this.variance === that.variance 253 | case _ => false 254 | } 255 | override def hashCode() = (quantity, entries, mean, variance).hashCode 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/minmax.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar.json._ 20 | import org.dianahep.histogrammar.util._ 21 | 22 | package histogrammar { 23 | //////////////////////////////////////////////////////////////// Minimize/Minimized/Minimizing 24 | 25 | /** Find the minimum value of a given quantity. If no data are observed, the result is NaN. 26 | * 27 | * Factory produces mutable [[org.dianahep.histogrammar.Minimizing]] and immutable [[org.dianahep.histogrammar.Minimized]] objects. 28 | */ 29 | object Minimize extends Factory { 30 | val name = "Minimize" 31 | val help = "Find the minimum value of a given quantity. If no data are observed, the result is NaN." 32 | val detailedHelp = """""" 33 | 34 | /** Create an immutable [[org.dianahep.histogrammar.Minimized]] from arguments (instead of JSON). 35 | * 36 | * @param entries Weighted number of entries (sum of all observed weights). 37 | * @param min Lowest observed value. 38 | */ 39 | def ed(entries: Double, min: Double) = new Minimized(entries, None, min) 40 | 41 | /** Create an immutable [[org.dianahep.histogrammar.Minimizing]]. 42 | * 43 | * @param quantity Numerical function to track. 44 | */ 45 | def apply[DATUM](quantity: UserFcn[DATUM, Double]) = new Minimizing(quantity, 0.0, java.lang.Double.NaN) 46 | 47 | /** Synonym for `apply`. */ 48 | def ing[DATUM](quantity: UserFcn[DATUM, Double]) = apply(quantity) 49 | 50 | /** Use [[org.dianahep.histogrammar.Minimized]] in Scala pattern-matching. */ 51 | def unapply(x: Minimized) = Some(x.min) 52 | /** Use [[org.dianahep.histogrammar.Minimizing]] in Scala pattern-matching. */ 53 | def unapply[DATUM](x: Minimizing[DATUM]) = Some(x.min) 54 | 55 | import KeySetComparisons._ 56 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 57 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "min").maybe("name")) => 58 | val get = pairs.toMap 59 | 60 | val entries = get("entries") match { 61 | case JsonNumber(x) => x 62 | case x => throw new JsonFormatException(x, name + ".entries") 63 | } 64 | 65 | val quantityName = get.getOrElse("name", JsonNull) match { 66 | case JsonString(x) => Some(x) 67 | case JsonNull => None 68 | case x => throw new JsonFormatException(x, name + ".name") 69 | } 70 | 71 | get("min") match { 72 | case JsonNumber(x) => new Minimized(entries, (nameFromParent ++ quantityName).lastOption, x) 73 | case x => throw new JsonFormatException(x, name) 74 | } 75 | 76 | case _ => throw new JsonFormatException(json, name) 77 | } 78 | 79 | private[histogrammar] def plus(one: Double, two: Double) = 80 | if (one.isNaN) 81 | two 82 | else if (two.isNaN) 83 | one 84 | else if (one < two) 85 | one 86 | else 87 | two 88 | } 89 | 90 | /** An accumulated minimum of a given quantity. If no data are observed, the result is `NaN`. 91 | * 92 | * Use the factory [[org.dianahep.histogrammar.Minimize]] to construct an instance. 93 | * 94 | * @param entries Weighted number of entries (sum of all observed weights). 95 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 96 | * @param min Lowest observed value. 97 | */ 98 | class Minimized private[histogrammar](val entries: Double, val quantityName: Option[String], val min: Double) extends Container[Minimized] with NoAggregation with QuantityName { 99 | type Type = Minimized 100 | type EdType = Minimized 101 | def factory = Minimize 102 | 103 | if (entries < 0.0) 104 | throw new ContainerException(s"entries ($entries) cannot be negative") 105 | 106 | def zero = new Minimized(0.0, quantityName, java.lang.Double.NaN) 107 | def +(that: Minimized) = 108 | if (this.quantityName != that.quantityName) 109 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 110 | else 111 | new Minimized(this.entries + that.entries, quantityName, Minimize.plus(this.min, that.min)) 112 | def *(factor: Double) = 113 | if (factor.isNaN || factor <= 0.0) 114 | zero 115 | else 116 | new Minimized(factor * entries, quantityName, min) 117 | 118 | def children = Nil 119 | 120 | def toJsonFragment(suppressName: Boolean) = JsonObject( 121 | "entries" -> JsonFloat(entries), 122 | "min" -> JsonFloat(min)). 123 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 124 | 125 | override def toString() = s"""""" 126 | override def equals(that: Any) = that match { 127 | case that: Minimized => this.entries === that.entries && this.quantityName == that.quantityName && this.min === that.min 128 | case _ => false 129 | } 130 | override def hashCode() = (entries, quantityName, min).hashCode 131 | } 132 | 133 | /** Accumulating the minimum of a given quantity. If no data are observed, the result is `NaN`. 134 | * 135 | * Use the factory [[org.dianahep.histogrammar.Minimize]] to construct an instance. 136 | * 137 | * @param quantity Numerical function to track. 138 | * @param entries Weighted number of entries (sum of all observed weights). 139 | * @param min Lowest observed value. 140 | */ 141 | class Minimizing[DATUM] private[histogrammar](val quantity: UserFcn[DATUM, Double], var entries: Double, var min: Double) extends Container[Minimizing[DATUM]] with AggregationOnData with NumericalQuantity[DATUM] { 142 | type Type = Minimizing[DATUM] 143 | type EdType = Minimized 144 | type Datum = DATUM 145 | def factory = Minimize 146 | 147 | def zero = new Minimizing[DATUM](quantity, 0.0, java.lang.Double.NaN) 148 | def +(that: Minimizing[DATUM]) = 149 | if (this.quantity.name != that.quantity.name) 150 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 151 | else 152 | new Minimizing[DATUM](this.quantity, this.entries + that.entries, Minimize.plus(this.min, that.min)) 153 | def *(factor: Double) = 154 | if (factor.isNaN || factor <= 0.0) 155 | zero 156 | else 157 | new Minimizing[DATUM](quantity, factor * entries, min) 158 | 159 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 160 | checkForCrossReferences() 161 | if (weight > 0.0) { 162 | val q = quantity(datum) 163 | 164 | // no possibility of exception from here on out (for rollback) 165 | entries += weight 166 | if (min.isNaN || q < min) 167 | min = q 168 | } 169 | } 170 | 171 | def children = Nil 172 | 173 | def toJsonFragment(suppressName: Boolean) = JsonObject( 174 | "entries" -> JsonFloat(entries), 175 | "min" -> JsonFloat(min)). 176 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 177 | 178 | override def toString() = s"""""" 179 | override def equals(that: Any) = that match { 180 | case that: Minimizing[DATUM] => this.quantity == that.quantity && this.entries === that.entries && this.min === that.min 181 | case _ => false 182 | } 183 | override def hashCode() = (quantity, entries, min).hashCode 184 | } 185 | 186 | //////////////////////////////////////////////////////////////// Maximize/Maximized/Maximizing 187 | 188 | /** Find the maximum value of a given quantity. If no data are observed, the result is NaN. 189 | * 190 | * Factory produces mutable [[org.dianahep.histogrammar.Maximizing]] and immutable [[org.dianahep.histogrammar.Maximized]] objects. 191 | */ 192 | object Maximize extends Factory { 193 | val name = "Maximize" 194 | val help = "Find the maximum value of a given quantity. If no data are observed, the result is NaN." 195 | val detailedHelp = """""" 196 | 197 | /** Create an immutable [[org.dianahep.histogrammar.Maximized]] from arguments (instead of JSON). 198 | * 199 | * @param entries Weighted number of entries (sum of all observed weights). 200 | * @param max Highest observed value. 201 | */ 202 | def ed(entries: Double, max: Double) = new Maximized(entries, None, max) 203 | 204 | /** Create an immutable [[org.dianahep.histogrammar.Maximizing]]. 205 | * 206 | * @param quantity Numerical function to track. 207 | */ 208 | def apply[DATUM](quantity: UserFcn[DATUM, Double]) = new Maximizing(quantity, 0.0, java.lang.Double.NaN) 209 | 210 | /** Synonym for `apply`. */ 211 | def ing[DATUM](quantity: UserFcn[DATUM, Double]) = apply(quantity) 212 | 213 | /** Use [[org.dianahep.histogrammar.Maximized]] in Scala pattern-matching. */ 214 | def unapply(x: Maximized) = Some(x.max) 215 | /** Use [[org.dianahep.histogrammar.Maximizing]] in Scala pattern-matching. */ 216 | def unapply[DATUM](x: Maximizing[DATUM]) = Some(x.max) 217 | 218 | import KeySetComparisons._ 219 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 220 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "max").maybe("name")) => 221 | val get = pairs.toMap 222 | 223 | val entries = get("entries") match { 224 | case JsonNumber(x) => x 225 | case x => throw new JsonFormatException(x, name + ".entries") 226 | } 227 | 228 | val quantityName = get.getOrElse("name", JsonNull) match { 229 | case JsonString(x) => Some(x) 230 | case JsonNull => None 231 | case x => throw new JsonFormatException(x, name + ".name") 232 | } 233 | 234 | get("max") match { 235 | case JsonNumber(x) => new Maximized(entries, (nameFromParent ++ quantityName).lastOption, x) 236 | case x => throw new JsonFormatException(x, name) 237 | } 238 | 239 | case _ => throw new JsonFormatException(json, name) 240 | } 241 | 242 | private[histogrammar] def plus(one: Double, two: Double) = 243 | if (one.isNaN) 244 | two 245 | else if (two.isNaN) 246 | one 247 | else if (one > two) 248 | one 249 | else 250 | two 251 | } 252 | 253 | /** An accumulated maximum of a given quantity. If no data are observed, the result is `NaN`. 254 | * 255 | * Use the factory [[org.dianahep.histogrammar.Maximize]] to construct an instance. 256 | * 257 | * @param entries Weighted number of entries (sum of all observed weights). 258 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 259 | * @param max Highest observed value. 260 | */ 261 | class Maximized private[histogrammar](val entries: Double, val quantityName: Option[String], val max: Double) extends Container[Maximized] with NoAggregation with QuantityName { 262 | type Type = Maximized 263 | type EdType = Maximized 264 | def factory = Maximize 265 | 266 | def zero = new Maximized(0.0, quantityName, java.lang.Double.NaN) 267 | def +(that: Maximized) = 268 | if (this.quantityName != that.quantityName) 269 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 270 | else 271 | new Maximized(this.entries + that.entries, this.quantityName, Maximize.plus(this.max, that.max)) 272 | def *(factor: Double) = 273 | if (factor.isNaN || factor <= 0.0) 274 | zero 275 | else 276 | new Maximized(factor * entries, quantityName, max) 277 | 278 | def children = Nil 279 | 280 | def toJsonFragment(suppressName: Boolean) = JsonObject( 281 | "entries" -> JsonFloat(entries), 282 | "max" -> JsonFloat(max)). 283 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 284 | 285 | override def toString() = s"""""" 286 | override def equals(that: Any) = that match { 287 | case that: Maximized => this.entries === that.entries && this.quantityName == that.quantityName && this.max === that.max 288 | case _ => false 289 | } 290 | override def hashCode() = (entries, quantityName, max).hashCode 291 | } 292 | 293 | /** Accumulating the maximum of a given quantity. If no data are observed, the result is `NaN`. 294 | * 295 | * Use the factory [[org.dianahep.histogrammar.Maximize]] to construct an instance. 296 | * 297 | * @param quantity Numerical function to track. 298 | * @param entries Weighted number of entries (sum of all observed weights). 299 | * @param max Highest observed value. 300 | */ 301 | class Maximizing[DATUM] private[histogrammar](val quantity: UserFcn[DATUM, Double], var entries: Double, var max: Double) extends Container[Maximizing[DATUM]] with AggregationOnData with NumericalQuantity[DATUM] { 302 | type Type = Maximizing[DATUM] 303 | type EdType = Maximized 304 | type Datum = DATUM 305 | def factory = Maximize 306 | 307 | def zero = new Maximizing[DATUM](quantity, 0.0, java.lang.Double.NaN) 308 | def +(that: Maximizing[DATUM]) = 309 | if (this.quantity.name != that.quantity.name) 310 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 311 | else 312 | new Maximizing[DATUM](this.quantity, this.entries + that.entries, Maximize.plus(this.max, that.max)) 313 | def *(factor: Double) = 314 | if (factor.isNaN || factor <= 0.0) 315 | zero 316 | else 317 | new Maximizing[DATUM](quantity, factor * entries, max) 318 | 319 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 320 | checkForCrossReferences() 321 | if (weight > 0.0) { 322 | val q = quantity(datum) 323 | 324 | // no possibility of exception from here on out (for rollback) 325 | entries += weight 326 | if (max.isNaN || q > max) 327 | max = q 328 | } 329 | } 330 | 331 | def children = Nil 332 | 333 | def toJsonFragment(suppressName: Boolean) = JsonObject( 334 | "entries" -> JsonFloat(entries), 335 | "max" -> JsonFloat(max)). 336 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 337 | 338 | override def toString() = s"""""" 339 | override def equals(that: Any) = that match { 340 | case that: Maximizing[DATUM] => this.quantity == that.quantity && this.entries === that.entries && this.max === that.max 341 | case _ => false 342 | } 343 | override def hashCode() = (quantity, entries, max).hashCode 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/select.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar.json._ 20 | import org.dianahep.histogrammar.util._ 21 | 22 | package histogrammar { 23 | //////////////////////////////////////////////////////////////// Select/Selected/Selecting 24 | 25 | /** Filter or weight data according to a given selection. 26 | * 27 | * This primitive is a basic building block, intended to be used in conjunction with anything that needs a user-defined cut. In particular, a standard histogram often has a custom selection, and this can be built by nesting Select -> Bin -> Count. 28 | * 29 | * Select also resembles [[org.dianahep.histogrammar.Fraction]], but without the `denominator`. 30 | * 31 | * The efficiency of a cut in a Select aggregator named `x` is simply `x.cut.entries / x.entries` (because all aggregators have an `entries` member). 32 | * 33 | * Factory produces mutable [[org.dianahep.histogrammar.Selecting]] and immutable [[org.dianahep.histogrammar.Selected]] (sic) objects. 34 | */ 35 | object Select extends Factory { 36 | val name = "Select" 37 | val help = "Filter or weight data according to a given selection." 38 | val detailedHelp = """This primitive is a basic building block, intended to be used in conjunction with anything that needs a user-defined cut. In particular, a standard histogram often has a custom selection, and this can be built by nesting Select -> Bin -> Count. 39 | 40 | Select also resembles [[org.dianahep.histogrammar.Fraction]], but without the `denominator`. 41 | 42 | The efficiency of a cut in a Select aggregator named `x` is simply `x.cut.entries / x.entries` (because all aggregators have an `entries` member).""" 43 | 44 | /** Create an immutable [[org.dianahep.histogrammar.Selected]] from arguments (instead of JSON). 45 | * 46 | * @param entries Weighted number of entries (sum of all observed weights without the cut applied). 47 | * @param cut Aggregator that accumulated values that passed the cut. 48 | */ 49 | def ed[V <: Container[V] with NoAggregation](entries: Double, cut: V) = new Selected[V](entries, None, cut) 50 | 51 | /** Create an empty, mutable [[org.dianahep.histogrammar.Selecting]]. 52 | * 53 | * @param quantity Boolean or non-negative function that cuts or weights entries. 54 | * @param cut Aggregator to accumulate for values that pass the selection (`quantity`). 55 | */ 56 | def apply[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}](quantity: UserFcn[DATUM, Double], cut: V = Count()) = new Selecting[DATUM, V](0.0, quantity, cut) 57 | 58 | /** Synonym for `apply`. */ 59 | def ing[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}](quantity: UserFcn[DATUM, Double], cut: V = Count()) = apply(quantity, cut) 60 | 61 | /** Use [[org.dianahep.histogrammar.Selected]] in Scala pattern-matching. */ 62 | def unapply[V <: Container[V] with NoAggregation](x: Selected[V]) = Some(x.cut) 63 | /** Use [[org.dianahep.histogrammar.Selecting]] in Scala pattern-matching. */ 64 | def unapply[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}](x: Selecting[DATUM, V]) = Some(x.cut) 65 | 66 | import KeySetComparisons._ 67 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 68 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "sub:type", "data").maybe("name")) => 69 | val get = pairs.toMap 70 | 71 | val entries = get("entries") match { 72 | case JsonNumber(x) => x 73 | case x => throw new JsonFormatException(x, name + ".entries") 74 | } 75 | 76 | val quantityName = get.getOrElse("name", JsonNull) match { 77 | case JsonString(x) => Some(x) 78 | case JsonNull => None 79 | case x => throw new JsonFormatException(x, name + ".name") 80 | } 81 | 82 | val factory = get("sub:type") match { 83 | case JsonString(x) => Factory(x) 84 | case x => throw new JsonFormatException(x, name + ".type") 85 | } 86 | 87 | val cut = factory.fromJsonFragment(get("data"), None) 88 | 89 | new Selected(entries, (nameFromParent ++ quantityName).lastOption, cut.asInstanceOf[C forSome {type C <: Container[C] with NoAggregation}]) 90 | 91 | case _ => throw new JsonFormatException(json, name) 92 | } 93 | 94 | trait Methods { 95 | /** Fraction of weights that pass the quantity. */ 96 | def fractionPassing: Double 97 | } 98 | } 99 | 100 | /** An accumulated aggregator of data that passed the cut. 101 | * 102 | * @param entries Weighted number of entries (sum of all observed weights without the cut applied). 103 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 104 | * @param cut Aggregator that accumulated values that passed the cut. 105 | */ 106 | class Selected[V <: Container[V] with NoAggregation] private[histogrammar](val entries: Double, val quantityName: Option[String], val cut: V) extends Container[Selected[V]] with NoAggregation with QuantityName with Select.Methods { 107 | type Type = Selected[V] 108 | type EdType = Selected[V] 109 | def factory = Select 110 | 111 | if (entries < 0.0) 112 | throw new ContainerException(s"entries ($entries) cannot be negative") 113 | 114 | def fractionPassing = cut.entries / entries 115 | 116 | def zero = new Selected[V](0.0, quantityName, cut.zero) 117 | def +(that: Selected[V]) = 118 | if (this.quantityName != that.quantityName) 119 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 120 | else 121 | new Selected[V](this.entries + that.entries, this.quantityName, this.cut + that.cut) 122 | def *(factor: Double) = 123 | if (factor.isNaN || factor <= 0.0) 124 | zero 125 | else 126 | new Selected[V](factor * entries, quantityName, cut * factor) 127 | 128 | def children = cut :: Nil 129 | 130 | def toJsonFragment(suppressName: Boolean) = JsonObject( 131 | "entries" -> JsonFloat(entries), 132 | "sub:type" -> JsonString(cut.factory.name), 133 | "data" -> cut.toJsonFragment(false)). 134 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 135 | 136 | override def toString() = s"""""" 137 | override def equals(that: Any) = that match { 138 | case that: Selected[V] => this.entries === that.entries && this.quantityName == that.quantityName && this.cut == that.cut 139 | case _ => false 140 | } 141 | override def hashCode() = (entries, quantityName, cut).hashCode 142 | } 143 | 144 | /** Accumulating an aggregator of data that passes a cut. 145 | * 146 | * @param entries Weighted number of entries (sum of all observed weights without the cut applied). 147 | * @param quantity Boolean or non-negative function that cuts or weights entries. 148 | * @param cut Aggregator to accumulate values that pass the cut. 149 | */ 150 | class Selecting[DATUM, V <: Container[V] with Aggregation{type Datum >: DATUM}] private[histogrammar](var entries: Double, val quantity: UserFcn[DATUM, Double], val cut: V) extends Container[Selecting[DATUM, V]] with AggregationOnData with NumericalQuantity[DATUM] with Select.Methods { 151 | type Type = Selecting[DATUM, V] 152 | type EdType = Selected[cut.EdType] 153 | type Datum = DATUM 154 | def factory = Select 155 | 156 | if (entries < 0.0) 157 | throw new ContainerException(s"entries ($entries) cannot be negative") 158 | 159 | def fractionPassing = cut.entries / entries 160 | 161 | def zero = new Selecting[DATUM, V](0.0, quantity, cut.zero) 162 | def +(that: Selecting[DATUM, V]) = 163 | if (this.quantity.name != that.quantity.name) 164 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 165 | else 166 | new Selecting[DATUM, V](this.entries + that.entries, this.quantity, this.cut + that.cut) 167 | def *(factor: Double) = 168 | if (factor.isNaN || factor <= 0.0) 169 | zero 170 | else 171 | new Selecting[DATUM, V](factor * entries, quantity, cut * factor) 172 | 173 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 174 | checkForCrossReferences() 175 | if (weight > 0.0) { 176 | val w = weight * quantity(datum) 177 | if (w > 0.0) 178 | cut.fill(datum, w) 179 | 180 | // no possibility of exception from here on out (for rollback) 181 | entries += weight 182 | } 183 | } 184 | 185 | def children = cut :: Nil 186 | 187 | def toJsonFragment(suppressName: Boolean) = JsonObject( 188 | "entries" -> JsonFloat(entries), 189 | "sub:type" -> JsonString(cut.factory.name), 190 | "data" -> cut.toJsonFragment(false)). 191 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 192 | 193 | override def toString() = s"""""" 194 | override def equals(that: Any) = that match { 195 | case that: Selecting[DATUM, V] => this.entries === that.entries && this.quantity == that.quantity && this.cut == that.cut 196 | case _ => false 197 | } 198 | override def hashCode() = (entries, quantity, cut).hashCode 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/primitives/sum.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep 16 | 17 | import scala.language.existentials 18 | 19 | import org.dianahep.histogrammar.json._ 20 | import org.dianahep.histogrammar.util._ 21 | 22 | package histogrammar { 23 | //////////////////////////////////////////////////////////////// Sum/Summed/Summing 24 | 25 | /** Accumulate the (weighted) sum of a given quantity, calculated from the data. 26 | * 27 | * Sum differs from [[org.dianahep.histogrammar.Count]] in that it computes a quantity on the spot, rather than percolating a product of weight metadata from nested primitives. Also unlike weights, the sum can add both positive and negative quantities (weights are always non-negative). 28 | * 29 | * Factory produces mutable [[org.dianahep.histogrammar.Summing]] and immutable [[org.dianahep.histogrammar.Summed]] objects. 30 | */ 31 | object Sum extends Factory { 32 | val name = "Sum" 33 | val help = "Accumulate the (weighted) sum of a given quantity, calculated from the data." 34 | val detailedHelp = """Sum differs from [[org.dianahep.histogrammar.Count]] in that it computes a quantity on the spot, rather than percolating a product of weight metadata from nested primitives. Also unlike weights, the sum can add both positive and negative quantities (weights are always non-negative).""" 35 | 36 | /** Create an immutable [[org.dianahep.histogrammar.Summed]] from arguments (instead of JSON). 37 | * 38 | * @param entries Weighted number of entries (sum of all observed weights). 39 | * @param sum The sum of weight times quantity over all entries. 40 | */ 41 | def ed(entries: Double, sum: Double) = new Summed(entries, None, sum) 42 | 43 | /** Create an empty, mutable [[org.dianahep.histogrammar.Summing]]. 44 | * 45 | * @param quantity Numerical function to track. 46 | */ 47 | def apply[DATUM](quantity: UserFcn[DATUM, Double]) = new Summing[DATUM](quantity, 0.0, 0.0) 48 | 49 | /** Synonym for `apply`. */ 50 | def ing[DATUM](quantity: UserFcn[DATUM, Double]) = apply(quantity) 51 | 52 | /** Use [[org.dianahep.histogrammar.Summed]] in Scala pattern-matching. */ 53 | def unapply(x: Summed) = Some(x.sum) 54 | /** Use [[org.dianahep.histogrammar.Summing]] in Scala pattern-matching. */ 55 | def unapply(x: Summing[_]) = Some(x.sum) 56 | 57 | import KeySetComparisons._ 58 | def fromJsonFragment(json: Json, nameFromParent: Option[String]): Container[_] with NoAggregation = json match { 59 | case JsonObject(pairs @ _*) if (pairs.keySet has Set("entries", "sum").maybe("name")) => 60 | val get = pairs.toMap 61 | 62 | val entries = get("entries") match { 63 | case JsonNumber(x) => x 64 | case x => throw new JsonFormatException(x, name + ".entries") 65 | } 66 | 67 | val quantityName = get.getOrElse("name", JsonNull) match { 68 | case JsonString(x) => Some(x) 69 | case JsonNull => None 70 | case x => throw new JsonFormatException(x, name + ".name") 71 | } 72 | 73 | val sum = get("sum") match { 74 | case JsonNumber(x) => x 75 | case x => throw new JsonFormatException(x, name + ".sum") 76 | } 77 | 78 | new Summed(entries, (nameFromParent ++ quantityName).lastOption, sum) 79 | 80 | case _ => throw new JsonFormatException(json, name) 81 | } 82 | } 83 | 84 | /** An accumulated weighted sum of a given quantity. 85 | * 86 | * Use the factory [[org.dianahep.histogrammar.Sum]] to construct an instance. 87 | * 88 | * @param entries Weighted number of entries (sum of all observed weights). 89 | * @param quantityName Optional name given to the quantity function, passed for bookkeeping. 90 | * @param sum The sum of weight times quantity over all entries. 91 | */ 92 | class Summed private[histogrammar](val entries: Double, val quantityName: Option[String], val sum: Double) extends Container[Summed] with NoAggregation with QuantityName { 93 | type Type = Summed 94 | type EdType = Summed 95 | def factory = Sum 96 | 97 | def zero = new Summed(0.0, quantityName, 0.0) 98 | def +(that: Summed) = 99 | if (this.quantityName != that.quantityName) 100 | throw new ContainerException(s"cannot add ${getClass.getName} because quantityName differs (${this.quantityName} vs ${that.quantityName})") 101 | else 102 | new Summed(this.entries + that.entries, this.quantityName, this.sum + that.sum) 103 | def *(factor: Double) = 104 | if (factor.isNaN || factor <= 0.0) 105 | zero 106 | else 107 | new Summed(factor * entries, quantityName, factor * sum) 108 | 109 | def children = Nil 110 | 111 | def toJsonFragment(suppressName: Boolean) = JsonObject( 112 | "entries" -> JsonFloat(entries), 113 | "sum" -> JsonFloat(sum)). 114 | maybe(JsonString("name") -> (if (suppressName) None else quantityName.map(JsonString(_)))) 115 | 116 | override def toString() = s"" 117 | override def equals(that: Any) = that match { 118 | case that: Summed => this.entries === that.entries && this.quantityName == that.quantityName && this.sum === that.sum 119 | case _ => false 120 | } 121 | override def hashCode() = (entries, quantityName, sum).hashCode 122 | } 123 | 124 | /** Accumulating a weighted sum of a given quantity. 125 | * 126 | * Use the factory [[org.dianahep.histogrammar.Sum]] to construct an instance. 127 | * 128 | * @param quantity Numerical function to track. 129 | * @param entries Weighted number of entries (sum of all observed weights). 130 | * @param sum The sum of weight times quantity over all entries. 131 | */ 132 | class Summing[DATUM] private[histogrammar](val quantity: UserFcn[DATUM, Double], var entries: Double, var sum: Double) extends Container[Summing[DATUM]] with AggregationOnData with NumericalQuantity[DATUM] { 133 | type Type = Summing[DATUM] 134 | type EdType = Summed 135 | type Datum = DATUM 136 | def factory = Sum 137 | 138 | def zero = new Summing[DATUM](quantity, 0.0, 0.0) 139 | def +(that: Summing[DATUM]) = 140 | if (this.quantity.name != that.quantity.name) 141 | throw new ContainerException(s"cannot add ${getClass.getName} because quantity name differs (${this.quantity.name} vs ${that.quantity.name})") 142 | else 143 | new Summing(this.quantity, this.entries + that.entries, this.sum + that.sum) 144 | def *(factor: Double) = 145 | if (factor.isNaN || factor <= 0.0) 146 | zero 147 | else 148 | new Summing[DATUM](quantity, factor * entries, factor * sum) 149 | 150 | def fill[SUB <: Datum](datum: SUB, weight: Double = 1.0): Unit = { 151 | checkForCrossReferences() 152 | if (weight > 0.0) { 153 | val q = quantity(datum) 154 | 155 | // no possibility of exception from here on out (for rollback) 156 | entries += weight 157 | sum += q * weight 158 | } 159 | } 160 | 161 | def children = Nil 162 | 163 | def toJsonFragment(suppressName: Boolean) = JsonObject( 164 | "entries" -> JsonFloat(entries), 165 | "sum" -> JsonFloat(sum)). 166 | maybe(JsonString("name") -> (if (suppressName) None else quantity.name.map(JsonString(_)))) 167 | 168 | override def toString() = s"" 169 | override def equals(that: Any) = that match { 170 | case that: Summing[DATUM] => this.quantity == that.quantity && this.entries === that.entries && this.sum === that.sum 171 | case _ => false 172 | } 173 | override def hashCode() = (quantity, entries, sum).hashCode 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/tutorial/cmsdata.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep.histogrammar.tutorial 16 | 17 | /** 18 | * Most of the examples must use some dataset, so I prepared a sample of CMS public data to be read with no dependencies. The data came from the CERN Open Data portal; it is 50 fb-1 of highly processed particle physics data from the CMS experiment, with 469,384 events selected by the 24 GeV/c isolated muon trigger. 19 | * 20 | * For convenience, it has been converted to compressed JSON. The code that reads it into classes is provided below for you to copy-paste, rather than a JAR to load, because I want you to see how it’s done and you may want to modify it. 21 | */ 22 | package cmsdata { 23 | // class definitions 24 | 25 | trait LorentzVector extends Serializable { 26 | // abstract members; must be defined by subclasses 27 | def px: Double 28 | def py: Double 29 | def pz: Double 30 | def E: Double 31 | 32 | // methods common to all LorentzVectors 33 | def pt = Math.sqrt(px*px + py*py) 34 | def p = Math.sqrt(px*px + py*py + pz*pz) 35 | def mass = Math.sqrt(E*E - px*px - py*py - pz*pz) 36 | def eta = 0.5*Math.log((p + pz)/(p - pz)) 37 | def phi = Math.atan2(py, px) 38 | 39 | // addition operator is a method named "+" 40 | def +(two: LorentzVector) = { 41 | val one = this 42 | // create a subclass and an instance in one block 43 | new LorentzVector { 44 | def px = one.px + two.px 45 | def py = one.py + two.py 46 | def pz = one.pz + two.pz 47 | def E = one.E + two.E 48 | override def toString() = s"LorentzVector($px, $py, $pz, $E)" 49 | } 50 | } 51 | } 52 | 53 | // particle class definitions are now one-liners 54 | 55 | case class Jet(px: Double, py: Double, pz: Double, E: Double, btag: Double) extends LorentzVector 56 | 57 | case class Muon(px: Double, py: Double, pz: Double, E: Double, q: Int, iso: Double) extends LorentzVector 58 | 59 | case class Electron(px: Double, py: Double, pz: Double, E: Double, q: Int, iso: Double) extends LorentzVector 60 | 61 | case class Photon(px: Double, py: Double, pz: Double, E: Double, iso: Double) extends LorentzVector 62 | 63 | case class MET(px: Double, py: Double) { 64 | def pt = Math.sqrt(px*px + py*py) 65 | } 66 | 67 | case class Event(jets: Seq[Jet], muons: Seq[Muon], electrons: Seq[Electron], photons: Seq[Photon], met: MET, numPrimaryVertices: Long) 68 | 69 | // event data iterator 70 | case class EventIterator(location: String = "http://histogrammar.org/docs/data/triggerIsoMu24_50fb-1.json.gz") extends Iterator[Event] { 71 | import org.dianahep.histogrammar.json._ 72 | 73 | // use Java libraries to stream and decompress data on-the-fly 74 | @transient val scanner = new java.util.Scanner( 75 | new java.util.zip.GZIPInputStream( 76 | new java.net.URL(location).openStream)) 77 | 78 | // read one ahead so that hasNext can effectively "peek" 79 | private def getNext() = 80 | try { 81 | Json.parse(scanner.nextLine) collect { 82 | case event: JsonObject => eventFromJson(event) 83 | } 84 | } 85 | catch { 86 | case err: java.util.NoSuchElementException => None 87 | } 88 | 89 | private var theNext = getNext() 90 | 91 | // iterator interface 92 | def hasNext = !theNext.isEmpty 93 | def next() = { 94 | val out = theNext.get 95 | theNext = getNext() 96 | out 97 | } 98 | 99 | def jetFromJson(params: Map[String, JsonNumber]) = 100 | new Jet(params("px").toDouble, 101 | params("py").toDouble, 102 | params("pz").toDouble, 103 | params("E").toDouble, 104 | params("btag").toDouble) 105 | 106 | def muonFromJson(params: Map[String, JsonNumber]) = 107 | new Muon(params("px").toDouble, 108 | params("py").toDouble, 109 | params("pz").toDouble, 110 | params("E").toDouble, 111 | params("q").toInt, 112 | params("iso").toDouble) 113 | 114 | def electronFromJson(params: Map[String, JsonNumber]) = 115 | new Electron(params("px").toDouble, 116 | params("py").toDouble, 117 | params("pz").toDouble, 118 | params("E").toDouble, 119 | params("q").toInt, 120 | params("iso").toDouble) 121 | 122 | def photonFromJson(params: Map[String, JsonNumber]) = 123 | new Photon(params("px").toDouble, 124 | params("py").toDouble, 125 | params("pz").toDouble, 126 | params("E").toDouble, 127 | params("iso").toDouble) 128 | 129 | def metFromJson(params: Map[String, JsonNumber]): MET = 130 | new MET(params("px").toDouble, params("py").toDouble) 131 | 132 | def eventFromJson(params: JsonObject) = { 133 | val JsonArray(jets @ _*) = params("jets") 134 | val JsonArray(muons @ _*) = params("muons") 135 | val JsonArray(electrons @ _*) = params("electrons") 136 | val JsonArray(photons @ _*) = params("photons") 137 | val met = params("MET").asInstanceOf[JsonObject] 138 | val JsonInt(numPrimaryVertices) = params("numPrimaryVertices") 139 | new Event( 140 | jets collect {case j: JsonObject => jetFromJson(j.to[JsonNumber].toMap)}, 141 | muons collect {case j: JsonObject => muonFromJson(j.to[JsonNumber].toMap)}, 142 | electrons collect {case j: JsonObject => electronFromJson(j.to[JsonNumber].toMap)}, 143 | photons collect {case j: JsonObject => photonFromJson(j.to[JsonNumber].toMap)}, 144 | metFromJson(met.to[JsonNumber].toMap), 145 | numPrimaryVertices) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /core/src/main/scala/org/dianahep/histogrammar/util.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2016 DIANA-HEP 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package org.dianahep.histogrammar 16 | 17 | import scala.util.Random 18 | 19 | import scala.collection.SortedMap 20 | import scala.collection.SortedSet 21 | 22 | import org.dianahep.histogrammar._ 23 | 24 | /** Supporting functions, mostly called by those in [[org.dianahep.histogrammar]]. */ 25 | package object util { 26 | // /** The natural [[org.dianahep.histogrammar.util.MetricOrdering]] for `Double` precision numbers. */ 27 | // implicit val doubleOrdering: MetricOrdering[Double] = new MetricOrdering[Double] { 28 | // def difference(x: Double, y: Double) = x - y 29 | // } 30 | 31 | /** Relative tolerance for numerical equality. Only affects `equals` checks, not `fill` or `+`. */ 32 | var relativeTolerance: Double = 0.0 33 | /** Absolute tolerance for numerical equality. Only affects `equals` checks, not `fill` or `+`. */ 34 | var absoluteTolerance: Double = 0.0 35 | } 36 | 37 | package util { 38 | //////////////////////////////////////////////////////////////// type-level programming to test compatibility of user-defined functions 39 | 40 | private[histogrammar] trait Compatible[-X, -Y] 41 | private[histogrammar] object Compatible { 42 | implicit object BothAreCounting extends Compatible[Counting, Counting] 43 | implicit object XIsCounting extends Compatible[Counting, AggregationOnData] 44 | implicit object YIsCounting extends Compatible[AggregationOnData, Counting] 45 | implicit def dataAreCompatible[X <: AggregationOnData, Y <: AggregationOnData](implicit evidence: X#Datum =:= Y#Datum) = new Compatible[X, Y] {} 46 | } 47 | 48 | //////////////////////////////////////////////////////////////// handling key set comparisons with optional keys 49 | 50 | object KeySetComparisons { 51 | trait KeySet { 52 | def required: Set[String] 53 | def optional: Set[String] 54 | 55 | def maybe(string: String) = { 56 | val t = this 57 | new KeySet { 58 | def required = t.required 59 | def optional = t.optional ++ Set(string) 60 | } 61 | } 62 | } 63 | 64 | implicit class KeySetFromSet(test: Set[String]) extends KeySet { 65 | def required = test 66 | def optional = Set[String]() 67 | def has(that: KeySet) = (that.required subsetOf test) && (test subsetOf (that.required ++ that.optional)) 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | histogrammar-parent 8 | Histogrammar parent package. See org.diana-hep:histogrammar for the core functionality. 9 | https://histogrammar.github.io/histogrammar-docs 10 | 2016 11 | 12 | io.github.histogrammar 13 | histogrammar-parent 14 | pom 15 | 1.0.20 16 | 17 | 18 | 19 | Apache License, Version 2.0 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | repo 22 | 23 | 24 | 25 | 26 | 27 | Jim Pivarski 28 | jpivarski@gmail.com 29 | DIANA-HEP 30 | http://diana-hep.org 31 | 32 | 33 | 34 | 35 | 36 | scala-2.10 37 | 38 | true 39 | 40 | 41 | core 42 | sparksql 43 | bokeh 44 | 45 | 46 | 47 | scala-2.11 48 | 49 | scala-2.11 50 | 51 | 52 | core 53 | sparksql 54 | 55 | 56 | 57 | 58 | scala-2.12 59 | 60 | scala-2.12 61 | 62 | 63 | core 64 | sparksql 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ossrh 73 | https://oss.sonatype.org/content/repositories/snapshots 74 | 75 | 76 | ossrh 77 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 78 | 79 | 80 | 81 | 82 | 83 | central 84 | Central Repository 85 | http://repo1.maven.org/maven2 86 | default 87 | 88 | false 89 | 90 | 91 | 92 | 93 | 94 | 95 | central 96 | Maven Plugin Repository 97 | http://repo1.maven.org/maven2 98 | default 99 | 100 | false 101 | 102 | 103 | never 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /sparksql/deploy-scala-2.10.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-sparksql 19 | Adapter for using Histogrammar in SparkSQL. 20 | http://histogrammar.org 21 | 2016 22 | 23 | org.diana-hep 24 | histogrammar-sparksql_2.10 25 | 1.0.20 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.7 65 | 1.7 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.10.6 73 | 74 | 75 | 76 | org.diana-hep 77 | histogrammar_2.10 78 | 1.0.4 79 | 80 | 81 | 82 | org.apache.spark 83 | spark-sql_2.10 84 | 1.6.2 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | 92 | central 93 | Central Repository 94 | http://repo1.maven.org/maven2 95 | default 96 | 97 | false 98 | 99 | 100 | 101 | 102 | 103 | 104 | central 105 | Maven Plugin Repository 106 | http://repo1.maven.org/maven2 107 | default 108 | 109 | false 110 | 111 | 112 | never 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | net.alchim31.maven 123 | scala-maven-plugin 124 | 3.2.2 125 | 126 | 127 | 128 | compile 129 | testCompile 130 | 131 | 132 | 133 | -Dscalac.patmat.analysisBudget=512 134 | -deprecation 135 | -feature 136 | -unchecked 137 | -dependencyfile 138 | ${project.build.directory}/.scala_dependencies 139 | 140 | incremental 141 | 142 | 143 | 144 | 145 | attach-sources 146 | 147 | add-source 148 | 149 | 150 | 151 | attach-javadocs 152 | 153 | doc-jar 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-install-plugin 163 | 2.5.2 164 | 165 | true 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-source-plugin 172 | 2.2.1 173 | 174 | 175 | attach-sources 176 | 177 | jar-no-fork 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 1.6 187 | 188 | 189 | sign-artifacts 190 | verify 191 | 192 | sign 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.sonatype.plugins 200 | nexus-staging-maven-plugin 201 | 1.6.7 202 | true 203 | 204 | ossrh 205 | https://oss.sonatype.org/ 206 | true 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-release-plugin 217 | 2.5 218 | 219 | false 220 | false 221 | true 222 | deploy 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ossrh 238 | https://oss.sonatype.org/content/repositories/snapshots 239 | 240 | 241 | ossrh 242 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /sparksql/deploy-scala-2.11.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-sparksql 19 | Adapter for using Histogrammar in SparkSQL. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar-sparksql_2.11 25 | 1.0.20 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.11.12 73 | 74 | 75 | 76 | io.github.histogrammar 77 | histogrammar_2.11 78 | 1.0.11 79 | 80 | 81 | 82 | org.apache.spark 83 | spark-sql_2.11 84 | 2.0.0 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | 92 | central 93 | Central Repository 94 | http://repo1.maven.org/maven2 95 | default 96 | 97 | false 98 | 99 | 100 | 101 | 102 | 103 | 104 | central 105 | Maven Plugin Repository 106 | http://repo1.maven.org/maven2 107 | default 108 | 109 | false 110 | 111 | 112 | never 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | net.alchim31.maven 123 | scala-maven-plugin 124 | 4.4.0 125 | 126 | 127 | 128 | compile 129 | testCompile 130 | 131 | 132 | 133 | -Dscalac.patmat.analysisBudget=512 134 | -deprecation 135 | -feature 136 | -unchecked 137 | -dependencyfile 138 | ${project.build.directory}/.scala_dependencies 139 | 140 | incremental 141 | 142 | 143 | 144 | 145 | attach-sources 146 | 147 | add-source 148 | 149 | 150 | 151 | attach-javadocs 152 | 153 | doc-jar 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-install-plugin 163 | 2.5.2 164 | 165 | true 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-source-plugin 172 | 2.2.1 173 | 174 | 175 | attach-sources 176 | 177 | jar-no-fork 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 1.5 187 | 188 | 189 | sign-artifacts 190 | verify 191 | 192 | sign 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.sonatype.plugins 200 | nexus-staging-maven-plugin 201 | 1.6.7 202 | true 203 | 204 | ossrh 205 | https://oss.sonatype.org/ 206 | true 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-release-plugin 217 | 2.5 218 | 219 | false 220 | false 221 | true 222 | deploy 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ossrh 238 | https://oss.sonatype.org/content/repositories/snapshots 239 | 240 | 241 | ossrh 242 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /sparksql/deploy-scala-2.12.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-sparksql 19 | Adapter for using Histogrammar in SparkSQL. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar-sparksql_2.12 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.12.13 73 | 74 | 75 | 76 | io.github.histogrammar 77 | histogrammar_2.12 78 | 1.0.30 79 | 80 | 81 | 82 | org.apache.spark 83 | spark-sql_2.12 84 | 3.0.1 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | 92 | central 93 | Central Repository 94 | http://repo1.maven.org/maven2 95 | default 96 | 97 | false 98 | 99 | 100 | 101 | 102 | 103 | 104 | central 105 | Maven Plugin Repository 106 | http://repo1.maven.org/maven2 107 | default 108 | 109 | false 110 | 111 | 112 | never 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | net.alchim31.maven 123 | scala-maven-plugin 124 | 4.4.0 125 | 126 | 127 | 128 | compile 129 | testCompile 130 | 131 | 132 | 133 | -Dscalac.patmat.analysisBudget=512 134 | -deprecation 135 | -feature 136 | -unchecked 137 | -dependencyfile 138 | ${project.build.directory}/.scala_dependencies 139 | 140 | incremental 141 | 142 | 143 | 144 | 145 | attach-sources 146 | 147 | add-source 148 | 149 | 150 | 151 | attach-javadocs 152 | 153 | doc-jar 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-install-plugin 163 | 2.5.2 164 | 165 | true 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-source-plugin 172 | 2.2.1 173 | 174 | 175 | attach-sources 176 | 177 | jar-no-fork 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 1.5 187 | 188 | 189 | sign-artifacts 190 | verify 191 | 192 | sign 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.sonatype.plugins 200 | nexus-staging-maven-plugin 201 | 1.6.13 202 | true 203 | 204 | ossrh 205 | https://oss.sonatype.org/ 206 | true 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-release-plugin 217 | 3.0.1 218 | 219 | false 220 | false 221 | true 222 | deploy 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ossrh 238 | https://oss.sonatype.org/content/repositories/snapshots 239 | 240 | 241 | ossrh 242 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /sparksql/deploy-scala-2.13.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-sparksql 19 | Adapter for using Histogrammar in SparkSQL. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar-sparksql_2.13 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | 54 | org.scala-tools 55 | maven-scala-plugin 56 | 2.15.2 57 | 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | org.scala-lang 71 | scala-library 72 | 2.13.12 73 | 74 | 75 | 76 | io.github.histogrammar 77 | histogrammar_2.13 78 | 1.0.30 79 | 80 | 81 | 82 | org.apache.spark 83 | spark-sql_2.13 84 | 3.4.2 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | 92 | central 93 | Central Repository 94 | http://repo1.maven.org/maven2 95 | default 96 | 97 | false 98 | 99 | 100 | 101 | 102 | 103 | 104 | central 105 | Maven Plugin Repository 106 | http://repo1.maven.org/maven2 107 | default 108 | 109 | false 110 | 111 | 112 | never 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | net.alchim31.maven 123 | scala-maven-plugin 124 | 4.4.0 125 | 126 | 127 | 128 | compile 129 | testCompile 130 | 131 | 132 | 133 | -Dscalac.patmat.analysisBudget=512 134 | -deprecation 135 | -feature 136 | -unchecked 137 | -dependencyfile 138 | ${project.build.directory}/.scala_dependencies 139 | 140 | incremental 141 | 142 | 143 | 144 | 145 | attach-sources 146 | 147 | add-source 148 | 149 | 150 | 151 | attach-javadocs 152 | 153 | doc-jar 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-install-plugin 163 | 2.5.2 164 | 165 | true 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-source-plugin 172 | 2.2.1 173 | 174 | 175 | attach-sources 176 | 177 | jar-no-fork 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 1.5 187 | 188 | 189 | sign-artifacts 190 | verify 191 | 192 | sign 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.sonatype.plugins 200 | nexus-staging-maven-plugin 201 | 1.6.13 202 | true 203 | 204 | ossrh 205 | https://oss.sonatype.org/ 206 | true 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-release-plugin 217 | 3.0.1 218 | 219 | false 220 | false 221 | true 222 | deploy 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ossrh 238 | https://oss.sonatype.org/content/repositories/snapshots 239 | 240 | 241 | ossrh 242 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /sparksql/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | histogrammar-sparksql 19 | Adapter for using Histogrammar in SparkSQL. 20 | https://histogrammar.github.io/histogrammar-docs 21 | 2016 22 | 23 | io.github.histogrammar 24 | histogrammar-sparksql_${scala.binary.version} 25 | 1.0.30 26 | jar 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | repo 33 | 34 | 35 | 36 | 37 | 38 | Jim Pivarski 39 | jpivarski@gmail.com 40 | DIANA-HEP 41 | http://diana-hep.org 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 47 | scm:git:git@github.com:histogrammar/histogrammar-scala.git 48 | git@github.com:histogrammar/histogrammar-scala.git 49 | 50 | 51 | 52 | 53 | scala-2.10 54 | 55 | true 56 | 57 | 58 | 2.10.6 59 | 2.10 60 | 1.7 61 | 1.7 62 | 1.6.2 63 | 8 64 | 65 | 66 | 67 | 68 | scala-2.11 69 | 70 | scala-2.11 71 | 72 | 73 | 2.11.12 74 | 2.11 75 | 1.8 76 | 1.8 77 | 2.0.0 78 | 8 79 | 80 | 81 | 82 | 83 | scala-2.12 84 | 85 | scala-2.12 86 | 87 | 88 | 2.12.13 89 | 2.12 90 | 11 91 | 11 92 | 3.0.1 93 | 11 94 | 95 | 96 | 97 | 98 | scala-2.13 99 | 100 | scala-2.13 101 | 102 | 103 | 2.13.0 104 | 2.13 105 | 11 106 | 11 107 | 3.2.0 108 | 11 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.scala-tools 118 | maven-scala-plugin 119 | 2.15.2 120 | 121 | 122 | 123 | 124 | 125 | UTF-8 126 | UTF-8 127 | 128 | 129 | 130 | 131 | org.scala-lang 132 | scala-library 133 | ${scala.version} 134 | 135 | 136 | 137 | io.github.histogrammar 138 | histogrammar_${scala.binary.version} 139 | 1.0.20 140 | 141 | 142 | 143 | org.apache.spark 144 | spark-sql_${scala.binary.version} 145 | ${spark.version} 146 | provided 147 | 148 | 149 | 150 | 151 | 152 | 153 | central 154 | Central Repository 155 | https://repo1.maven.org/maven2 156 | default 157 | 158 | false 159 | 160 | 161 | 162 | 163 | 164 | 165 | central 166 | Maven Plugin Repository 167 | https://repo1.maven.org/maven2 168 | default 169 | 170 | false 171 | 172 | 173 | never 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | maven-compiler-plugin 183 | 3.8.1 184 | 185 | 11 186 | 11 187 | 188 | 189 | 190 | 191 | 192 | net.alchim31.maven 193 | scala-maven-plugin 194 | 4.4.0 195 | 196 | 197 | 198 | compile 199 | testCompile 200 | 201 | 202 | 203 | -Dscalac.patmat.analysisBudget=512 204 | -deprecation 205 | -feature 206 | -unchecked 207 | -dependencyfile 208 | ${project.build.directory}/.scala_dependencies 209 | 210 | incremental 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-dependency-plugin 221 | 2.10 222 | 223 | 224 | package 225 | 226 | copy-dependencies 227 | 228 | 229 | 230 | target/lib 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | maven-install-plugin 239 | 2.5.2 240 | 241 | true 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-source-plugin 248 | 3.2.0 249 | 250 | 251 | attach-sources 252 | 253 | jar 254 | 255 | 256 | 257 | 258 | 259 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | --------------------------------------------------------------------------------