mc_forecast 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f0d344256cf08700cd6d8475d0ac19ec44079cae9699f7a037fc1b934ee5c4d
4
- data.tar.gz: dca662a52e4bc902ddf944e47e59882f43d86629cf5792aa19d737fa430f8d59
3
+ metadata.gz: 82e81b59b88ffa6eadba70aef437ecec5c040b890b19d7f64f8237eb59fc7c56
4
+ data.tar.gz: 729b9f9d931b338f5860427d1aaba73aea35c847eedb018d72c39a7486c7f551
5
5
  SHA512:
6
- metadata.gz: a066871adf87c23610e0f08938934f5e1abdb630e477aea65e86195446952e279df54028c9ea12017e37669371203d6f22c53d21c1433aa69cf88c51ccb7b6b1
7
- data.tar.gz: 3c294be97dc9040d7cde19ae90c0508d02fe2f6eb777cb38ff109ac7a7971ca94b25b1fbf47cd53190cdcb5eb2f2a3558198361ffa54d6d9fdd43b7004d63982
6
+ metadata.gz: 4ce13d962a1c9e82f52e101387f6c8627fdac52187000e5ceb5a25bc7513bc9929cd94d6fc6faaf5196f7bcc90336bb69be642c774abe93a205b43464bbc20f1
7
+ data.tar.gz: ea2d982df7f53a36558969c1d8361ea477d817895f92a4118cb23472eaa3066b85ce9efffcc12f804894facf1303f8dffe68c2b2bb9fd8865125d0ba516a41fd
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/DaanVanVugt/ruby-mc/ci.yml)](https://github.com/DaanVanVugt/ruby-mc/actions/workflows/ci.yml)
6
6
  [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/DaanVanVugt/ruby-mc)](https://codeclimate.com/github/DaanVanVugt/ruby-mc)
7
7
 
8
- TODO: Description of this gem goes here.
8
+ Use Monte-Carlo methods for business forecasting. Define transition methods (for example a month-based one) and keep track of events you are interested in. Automatically generates a 95% confidence interval and mean values.
9
9
 
10
10
  ---
11
11
 
@@ -23,6 +23,17 @@ gem install mc_forecast
23
23
 
24
24
  ```ruby
25
25
  require "mc_forecast"
26
+ # all arguments optional
27
+ e = McForecast::Simulation.new.run(init_state: nil, steps: 1, trials: 1_000) do |_state, _step, _trial|
28
+ events = {}
29
+ events[:coin] = rand > 0.5 ? 1 : 0
30
+
31
+ # block should return a new state and a hash of events
32
+ [nil, events]
33
+ end
34
+ # e[:coin][:mean][0] ~ 0.5
35
+ # e[:coin][:quantiles][0.025][0] ~ 0
36
+ # e[:coin][:quantiles][0.975][0] ~ 1
26
37
  ```
27
38
 
28
39
  ## Support
@@ -1,9 +1,11 @@
1
+ require "deep_dup"
2
+
1
3
  module McForecast
2
4
  class Simulation
3
- def run(init_state: nil, trials: 1_000, steps: 1, quantiles: [0.025, 0.975])
5
+ def run(init_state: nil, trials: 1_000, steps: 1, quantiles: [0.025, 0.16, 0.84, 0.975])
4
6
  events = {}
5
7
  (0..trials - 1).each do |trial|
6
- state = init_state.dup # this is not a deepcopy!
8
+ state = DeepDup.deep_dup(init_state)
7
9
  (0..steps - 1).each do |step|
8
10
  state, e = yield state, step, trial
9
11
  e.each_pair do |k, v|
@@ -22,7 +24,11 @@ module McForecast
22
24
 
23
25
  # Return an analysis of the events, containing:
24
26
  # { event_name:
25
- # { mean: [...], # per step
27
+ # {
28
+ # sum: { mean: ...,
29
+ # quantiles:
30
+ # { 0.025: ..., 0.975: ... }},
31
+ # mean: [...], # per step
26
32
  # quantiles:
27
33
  # { 0.025: [...],
28
34
  # 0.975: [...]
@@ -30,20 +36,39 @@ module McForecast
30
36
  def analyze(events, quantiles)
31
37
  events.transform_values do |steps| # array(steps) of arrays(trials)
32
38
  {
33
- mean: steps.map { |trials| Rational(trials.sum || 0, trials.length) },
34
- quantiles: Array.new(steps.length)
39
+ sum: sum(steps, quantiles),
40
+ # besides the total sum we may want to have a sum for multiples of our base period
41
+ # (or week/month/quarter/year but that gets a bit complicated)
42
+ mean: steps.map { |trials| (trials.sum || 0).to_f / trials.length },
43
+ quantiles: quantiles.zip(step_quantiles(quantiles, steps)).to_h
35
44
  }
45
+ end
46
+ end
47
+
48
+ def sum(steps, quantiles)
49
+ sums = steps.transpose.map(&:sum) # gives a sum of this event, per trial
50
+ {
51
+ mean: (sums.sum || 0).to_f / steps[0].length,
52
+ # sort all of the sums, and take the elements closest to the chosen quantiles, and then make a nice hash
53
+ quantiles: quantiles.zip(sums.sort.values_at(*quantile_indices(steps[0].length, quantiles))).to_h
54
+ }
55
+ end
36
56
 
37
- require 'byebug'
38
- puts(steps.map do |trials|
39
- [quantiles, trials.sort.values_at(*quantile_indices(trials.length, quantiles))]
40
- end)
57
+ def step_quantiles(quantiles, steps)
58
+ if quantiles.any?
59
+ # only need to sort if we request answers on any quantiles
60
+ steps.map do |trials|
61
+ # could avoid sorting with some creativity, but probably fine for our data lengths so far
62
+ trials.sort.values_at(*quantile_indices(trials.length, quantiles))
63
+ end.transpose # a[step][]
64
+ else
65
+ []
41
66
  end
42
67
  end
43
68
 
44
- def quantile_indices(n_trials, quantiles)
69
+ def quantile_indices(count, quantiles)
45
70
  quantiles.map do |q|
46
- (q * (n_trials - 1)).round.to_i.clamp(0, n_trials - 1)
71
+ (q * (count - 1)).round.to_i.clamp(0, count - 1)
47
72
  end
48
73
  end
49
74
  end
@@ -1,3 +1,3 @@
1
1
  module McForecast
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mc_forecast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daan van Vugt
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-08 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deep_dup
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description:
14
28
  email:
15
29
  - dvanvugt@ignitioncomputing.com