tdigest 0.1.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 631385c7b0fabf18b5809c0c97d96064b04b8318
4
+ data.tar.gz: 986ee363e12dab9e38ff06b4e831cbe82d41f9f9
5
+ SHA512:
6
+ metadata.gz: 6a326891245b4b929a917b57b774c7a7f9bc5b1d5769ab1880a5b57604f2d779e27800145ac1e46b1b1e835bec54bb7c6e7582d2d3bc0225efc8b4ca0ee563c7
7
+ data.tar.gz: fde8ed4cc7e9bb8c1a7910806c888934a46eec9d083f463575f45149c9f95076b77e3408f3105052b0d4c79ce5e12eb36968b875ff50111dbe26c6acc78898b6
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.3.1
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.2.4
6
+ - 2.3.1
7
+ - jruby-9.1.3.0
8
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tdigest.gemspec
4
+ gemspec
5
+
6
+ platform :jruby do
7
+ gem 'rbtree-jruby'
8
+ end
9
+
10
+ platform :mri, :rbx do
11
+ gem 'rbtree'
12
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Sebastian Wallin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # Tdigest
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/tdigest.svg)](https://badge.fury.io/rb/tdigest)
4
+ [![Build Status](https://travis-ci.org/castle/tdigest.svg?branch=master)](https://travis-ci.org/castle/tdigest)
5
+ [![Coverage Status](https://coveralls.io/repos/castle/tdigest/badge.svg?branch=master&service=github)](https://coveralls.io/github/castle/tdigest?branch=master)
6
+
7
+ Ruby implementation of Ted Dunning's [t-digest](https://github.com/tdunning/t-digest) data structure.
8
+
9
+ Inspired by the [Javascript implementation](https://github.com/welch/tdigest) by [Will Welch](https://github.com/welch)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'tdigest'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install tdigest
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ td = ::TDigest::TDigest.new
31
+ 1_000.times { td.push(rand) }
32
+ td.compress!
33
+
34
+ puts td.percentile(0.5)
35
+ puts td.p_rank(0.95)
36
+ ```
37
+
38
+ #### Serialization
39
+
40
+ This gem offers the same serialization options as the original [Java implementation](https://github.com/tdunning/t-digest). You can read more about T-digest persistance in [Chapter 3 in the paper](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf).
41
+
42
+ **Standard encoding**
43
+
44
+ This encoding uses 8-byte Double for the means and a 4-byte integers for counts.
45
+ Size per centroid is a fixed 12-bytes.
46
+
47
+ ```ruby
48
+ bytes = tdigest.as_bytes
49
+ ```
50
+
51
+ **Compressed encoding**
52
+
53
+ This encoding uses delta encoding with 4-byte floats for the means and variable
54
+ length encoding for the counts. Size per centroid is between 5-12 bytes.
55
+
56
+ ```ruby
57
+ bytes = tdigest.as_small_bytes
58
+ ```
59
+
60
+ **Deserializing**
61
+
62
+ Deserialization will automatically detect compression format
63
+
64
+ ```ruby
65
+ tdigest = TDigest::TDigest.from_bytes(bytes)
66
+ ```
67
+
68
+ ## Development
69
+
70
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
71
+
72
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
73
+
74
+ ## Contributing
75
+
76
+ Bug reports and pull requests are welcome on GitHub at https://github.com/castle/tdigest.
77
+
78
+
79
+ ## License
80
+
81
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
82
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "tdigest"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'rubygems/command.rb'
3
+ require 'rubygems/dependency_installer.rb'
4
+
5
+ begin
6
+ Gem::Command.build_args = ARGV
7
+ rescue NoMethodError
8
+ end
9
+ inst = Gem::DependencyInstaller.new
10
+ begin
11
+ if RUBY_PLATFORM =~ /java/
12
+ inst.install "rbtree-jruby"
13
+ else
14
+ inst.install "rbtree"
15
+ end
16
+ rescue
17
+ exit(1)
18
+ end
19
+
20
+ f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
21
+ f.write("task :default\n")
22
+ f.close
@@ -0,0 +1,6 @@
1
+ require "tdigest/version"
2
+ require "tdigest/tdigest"
3
+
4
+ module TDigest
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,15 @@
1
+ module TDigest
2
+ class Centroid
3
+ attr_accessor :mean, :n, :cumn, :mean_cumn
4
+ def initialize(mean, n, cumn, mean_cumn = nil)
5
+ @mean = mean
6
+ @n = n
7
+ @cumn = cumn
8
+ @mean_cumn = mean_cumn
9
+ end
10
+
11
+ def as_json(_ = nil)
12
+ { m: mean, n: n }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,347 @@
1
+ require 'rbtree'
2
+ require 'tdigest/centroid'
3
+
4
+ module TDigest
5
+ class TDigest
6
+ VERBOSE_ENCODING = 1
7
+ SMALL_ENCODING = 2
8
+
9
+ attr_accessor :centroids
10
+ def initialize(delta = 0.01, k = 25, cx = 1.1)
11
+ @delta = delta
12
+ @k = k
13
+ @cx = cx
14
+ @centroids = RBTree.new
15
+ @nreset = 0
16
+ @n = 0
17
+ reset!
18
+ end
19
+
20
+ def +(other)
21
+ # Uses delta, k and cx from the caller
22
+ t = self.class.new(@delta, @k, @cx)
23
+ data = self.centroids.values + other.centroids.values
24
+ while data.length > 0
25
+ t.push_centroid(data.delete_at(rand(data.length)))
26
+ end
27
+ t
28
+ end
29
+
30
+ def as_bytes
31
+ # compression as defined by Java implementation
32
+ size = @centroids.size
33
+ output = [VERBOSE_ENCODING, compression, size]
34
+ output += @centroids.map { |_, c| c.mean }
35
+ output += @centroids.map { |_, c| c.n }
36
+ output.pack("LdLd#{size}L#{size}")
37
+ end
38
+
39
+ def as_small_bytes
40
+ size = @centroids.size
41
+ output = [SMALL_ENCODING, compression, size]
42
+ x = 0
43
+ # delta encoding allows saving 4-bytes floats
44
+ mean_arr = @centroids.map do |_, c|
45
+ val = c.mean - x
46
+ x = c.mean
47
+ val
48
+ end
49
+ output += mean_arr
50
+ # Variable length encoding of numbers
51
+ c_arr = @centroids.each_with_object([]) do |(_, c), arr|
52
+ k = 0
53
+ n = c.n
54
+ while n < 0 || n > 0x7f
55
+ b = 0x80 | (0x7f & n)
56
+ arr << b
57
+ n = n >> 7
58
+ k += 1
59
+ fail 'Unreasonable large number' if k > 6
60
+ end
61
+ arr << n
62
+ end
63
+ output += c_arr
64
+ output.pack("LdLf#{mean_arr.size}C#{c_arr.size}")
65
+ end
66
+
67
+ def as_json(_ = nil)
68
+ @centroids.map { |_, c| c.as_json }
69
+ end
70
+
71
+ def bound_mean(x)
72
+ upper = @centroids.upper_bound(x)
73
+ lower = @centroids.lower_bound(x)
74
+ [lower[1], upper[1]]
75
+ end
76
+
77
+ def bound_mean_cumn(cumn)
78
+ last_c = nil
79
+ bounds = []
80
+ matches = @centroids.each do |k, v|
81
+ if v.mean_cumn == cumn
82
+ bounds << v
83
+ break
84
+ elsif v.mean_cumn > cumn
85
+ bounds << last_c
86
+ bounds << v
87
+ break
88
+ else
89
+ last_c = v
90
+ end
91
+ end
92
+ # If still no results, pick lagging value if any
93
+ bounds << last_c if bounds.empty? && !last_c.nil?
94
+
95
+ bounds
96
+ end
97
+
98
+ def compress!
99
+ points = to_a
100
+ reset!
101
+ push_centroid(points.shuffle)
102
+ _cumulate(true, true)
103
+ nil
104
+ end
105
+
106
+ def compression
107
+ 1 / @delta
108
+ end
109
+
110
+ def find_nearest(x)
111
+ return nil if size == 0
112
+
113
+ ceil = @centroids.upper_bound(x)
114
+ floor = @centroids.lower_bound(x)
115
+
116
+ return floor[1] if ceil.nil?
117
+ return ceil[1] if floor.nil?
118
+
119
+ ceil_key = ceil[0]
120
+ floor_key = floor[0]
121
+
122
+ if (floor_key - x).abs < (ceil_key - x).abs
123
+ floor[1]
124
+ else
125
+ ceil[1]
126
+ end
127
+ end
128
+
129
+ def merge!(other)
130
+ push_centroid(other.centroids.values.shuffle)
131
+ self
132
+ end
133
+
134
+ def p_rank(x)
135
+ is_array = x.is_a? Array
136
+ x = [x] unless is_array
137
+
138
+ min = @centroids.first
139
+ max = @centroids.last
140
+
141
+ x.map! do |item|
142
+ if size == 0
143
+ nil
144
+ elsif item < min[1].mean
145
+ 0.0
146
+ elsif item > max[1].mean
147
+ 1.0
148
+ else
149
+ _cumulate(true)
150
+ bound = bound_mean(item)
151
+ lower, upper = bound
152
+ mean_cumn = lower.mean_cumn
153
+ if lower != upper
154
+ mean_cumn += (item - lower.mean) * (upper.mean_cumn - lower.mean_cumn) / (upper.mean - lower.mean)
155
+ end
156
+ mean_cumn / @n
157
+ end
158
+ end
159
+ is_array ? x : x.first
160
+ end
161
+
162
+ def percentile(p)
163
+ is_array = p.is_a? Array
164
+ p = [p] unless is_array
165
+ p.map! do |item|
166
+ unless (0..1).include? item
167
+ fail ArgumentError, "p should be in [0,1], got #{item}"
168
+ end
169
+ if size == 0
170
+ nil
171
+ else
172
+ _cumulate(true)
173
+ h = @n * item
174
+ lower, upper = bound_mean_cumn(h)
175
+ if lower.nil? && upper.nil?
176
+ nil
177
+ elsif upper == lower || lower.nil? || upper.nil?
178
+ (lower || upper).mean
179
+ elsif h == lower.mean_cumn
180
+ lower.mean
181
+ else
182
+ upper.mean
183
+ end
184
+ end
185
+ end
186
+ is_array ? p : p.first
187
+ end
188
+
189
+ def push(x, n = 1)
190
+ x = [x] unless x.is_a? Array
191
+ x.each { |value| _digest(value, n) }
192
+ end
193
+
194
+ def push_centroid(c)
195
+ c = [c] unless c.is_a? Array
196
+ c.each { |centroid| _digest(centroid.mean, centroid.n) }
197
+ end
198
+
199
+ def reset!
200
+ @centroids.clear
201
+ @n = 0
202
+ @nreset += 1
203
+ @last_cumulate = 0
204
+ end
205
+
206
+ def size
207
+ @n || 0
208
+ end
209
+
210
+ def to_a
211
+ @centroids.map { |_, c| c }
212
+ end
213
+
214
+ def self.from_bytes(bytes)
215
+ format, compression, size = bytes.unpack('LdL')
216
+ tdigest = new(1 / compression)
217
+
218
+ start_idx = 16 # after header
219
+ case format
220
+ when VERBOSE_ENCODING
221
+ array = bytes[start_idx..-1].unpack("d#{size}L#{size}")
222
+ means, counts = array.each_slice(size).to_a if array.size > 0
223
+ when SMALL_ENCODING
224
+ means = bytes[start_idx..(start_idx + 4 * size)].unpack("f#{size}")
225
+ # Decode delta encoding of means
226
+ x = 0
227
+ means.map! do |m|
228
+ m += x
229
+ x = m
230
+ m
231
+ end
232
+ counts_bytes = bytes[(start_idx + 4 * size)..-1].unpack('C*')
233
+ counts = []
234
+ # Decode variable length integer bytes
235
+ size.times do
236
+ v = counts_bytes.shift
237
+ z = 0x7f & v
238
+ shift = 7
239
+ while (v & 0x80) != 0
240
+ fail 'Shift too large in decode' if shift > 28
241
+ v = counts_bytes.shift || 0
242
+ z += (v & 0x7f) << shift
243
+ shift += 7
244
+ end
245
+ counts << z
246
+ end
247
+ # This shouldn't happen
248
+ fail 'Mismatch' unless counts.size == means.size
249
+ else
250
+ fail 'Unknown compression format'
251
+ end
252
+ if means && counts
253
+ means.zip(counts).each { |val| tdigest.push(val[0], val[1]) }
254
+ end
255
+ tdigest
256
+ end
257
+
258
+ def self.from_json(array)
259
+ tdigest = new
260
+ # Handle both string and symbol keys
261
+ array.each { |a| tdigest.push(a['m'] || a[:m], a['n'] || a[:n]) }
262
+ tdigest
263
+ end
264
+
265
+ private
266
+
267
+ def _add_weight(nearest, x, n)
268
+ unless x == nearest.mean
269
+ nearest.mean += n * (x - nearest.mean) / (nearest.n + n)
270
+ end
271
+
272
+ _cumulate(false, true) if nearest.mean_cumn.nil?
273
+
274
+ nearest.cumn += n
275
+ nearest.mean_cumn += n / 2.0
276
+ nearest.n += n
277
+
278
+ nil
279
+ end
280
+
281
+ def _cumulate(exact = false, force = false)
282
+ unless force
283
+ factor = if @last_cumulate == 0
284
+ Float::INFINITY
285
+ else
286
+ (@n.to_f / @last_cumulate)
287
+ end
288
+ return if @n == @last_cumulate || (!exact && @cx && @cx > (factor))
289
+ end
290
+
291
+ cumn = 0
292
+ @centroids.each do |_, c|
293
+ c.mean_cumn = cumn + c.n / 2.0
294
+ cumn = c.cumn = cumn + c.n
295
+ end
296
+ @n = @last_cumulate = cumn
297
+ nil
298
+ end
299
+
300
+ def _digest(x, n)
301
+ # Use 'first' and 'last' instead of min/max because of performance reasons
302
+ # This works because RBTree is sorted
303
+ min = @centroids.first
304
+ max = @centroids.last
305
+
306
+ min = min.nil? ? nil : min[1]
307
+ max = max.nil? ? nil : max[1]
308
+ nearest = find_nearest(x)
309
+
310
+ @n += n
311
+
312
+ if nearest && nearest.mean == x
313
+ _add_weight(nearest, x, n)
314
+ elsif nearest == min
315
+ _new_centroid(x, n, 0)
316
+ elsif nearest == max
317
+ _new_centroid(x, n, @n)
318
+ else
319
+ p = nearest.mean_cumn.to_f / @n
320
+ max_n = (4 * @n * @delta * p * (1 - p)).floor
321
+ if (max_n - nearest.n >= n)
322
+ _add_weight(nearest, x, n)
323
+ else
324
+ _new_centroid(x, n, nearest.cumn)
325
+ end
326
+ end
327
+
328
+ _cumulate(false)
329
+
330
+ # If the number of centroids has grown to a very large size,
331
+ # it may be due to values being inserted in sorted order.
332
+ # We combat that by replaying the centroids in random order,
333
+ # which is what compress! does
334
+ if @centroids.size > (@k / @delta)
335
+ compress!
336
+ end
337
+
338
+ nil
339
+ end
340
+
341
+ def _new_centroid(x, n, cumn)
342
+ c = Centroid.new(x, n, cumn)
343
+ @centroids[x] = c
344
+ c
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,3 @@
1
+ module TDigest
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tdigest/version'
5
+
6
+ java = (ENV['RUBY_PLATFORM'] == 'java')
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "tdigest"
10
+ spec.version = TDigest::VERSION
11
+ spec.authors = ["Sebastian Wallin"]
12
+ spec.email = ["sebastian.wallin@gmail.com"]
13
+
14
+ spec.summary = %q{TDigest for Ruby}
15
+ spec.description = %q{Ruby implementation of Dunning's T-Digest for streaming quantile approximation}
16
+ spec.homepage = "https://github.com/castle/tdigest"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+ spec.platform = java ? 'java' : 'ruby'
24
+
25
+
26
+ if java
27
+ spec.add_runtime_dependency 'rbtree-jruby', '~> 0.2.1'
28
+ else
29
+ spec.add_runtime_dependency 'rbtree', '~> 0.4.2'
30
+ end
31
+
32
+ spec.extensions << 'ext/mkrf_conf.rb'
33
+
34
+ spec.add_development_dependency 'bundler', '~> 1.10'
35
+ spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'minitest', '~> 5.8'
37
+ spec.add_development_dependency 'coveralls', '~> 0.8.10'
38
+ spec.add_development_dependency 'simplecov', '~> 0.11.1'
39
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tdigest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: java
6
+ authors:
7
+ - Sebastian Wallin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rbtree-jruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.8.10
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.8.10
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.11.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.11.1
97
+ description: Ruby implementation of Dunning's T-Digest for streaming quantile approximation
98
+ email:
99
+ - sebastian.wallin@gmail.com
100
+ executables: []
101
+ extensions:
102
+ - ext/mkrf_conf.rb
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".ruby-version"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/console
113
+ - bin/setup
114
+ - ext/mkrf_conf.rb
115
+ - lib/tdigest.rb
116
+ - lib/tdigest/centroid.rb
117
+ - lib/tdigest/tdigest.rb
118
+ - lib/tdigest/version.rb
119
+ - tdigest.gemspec
120
+ homepage: https://github.com/castle/tdigest
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.5.1
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: TDigest for Ruby
144
+ test_files: []