prophet-rb 0.3.1 → 0.3.2
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 +4 -4
- data/CHANGELOG.md +6 -2
- data/README.md +23 -1
- data/lib/prophet/forecaster.rb +6 -0
- data/lib/prophet/holidays.rb +6 -10
- data/lib/prophet/version.rb +1 -1
- data/lib/prophet.rb +18 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e24dad0631694318db703e420f657e964911071b73e03cd73a6fc4c2d6e16fc
|
4
|
+
data.tar.gz: bcf2a53b3bcacf5461cd0b881bba0d026c7adee88200646f915074c9946711d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdad3069aeaadaa9ac8d339fb6e877942470fd5c8e11652bce50029b0a6de13d0fe383f6a97a343e29138396dec4b1c159e5ee87d229073d2f3eb5c084aa5af2
|
7
|
+
data.tar.gz: 1d54e502621ed4d34c818fe2e0e064578eae948957305300222892f4ee4750418e2471261596568d05795aea31bb4fe4639925da20a1f5db5bd20a0f0b35a66f
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
## 0.3.
|
1
|
+
## 0.3.2 (2022-05-15)
|
2
|
+
|
3
|
+
- Added advanced API options to `forecast` and `anomalies` methods
|
4
|
+
|
5
|
+
## 0.3.1 (2022-04-28)
|
2
6
|
|
3
7
|
- Improved error message for missing columns
|
4
8
|
|
5
|
-
## 0.3.0 (
|
9
|
+
## 0.3.0 (2022-04-24)
|
6
10
|
|
7
11
|
- Switched to precompiled models
|
8
12
|
- Dropped support for Ruby < 2.7
|
data/README.md
CHANGED
@@ -22,6 +22,8 @@ gem "prophet-rb"
|
|
22
22
|
|
23
23
|
## Simple API
|
24
24
|
|
25
|
+
### Forecasting
|
26
|
+
|
25
27
|
Get future predictions for a time series
|
26
28
|
|
27
29
|
```ruby
|
@@ -48,12 +50,32 @@ series = User.group_by_day(:created_at).count
|
|
48
50
|
Prophet.forecast(series)
|
49
51
|
```
|
50
52
|
|
53
|
+
And supports [advanced API](#advanced-api) options
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Prophet.forecast(series, growth: "logistic", weekly_seasonality: false)
|
57
|
+
```
|
58
|
+
|
59
|
+
### Anomaly Detection
|
60
|
+
|
51
61
|
Detect anomalies in a time series
|
52
62
|
|
53
63
|
```ruby
|
54
64
|
Prophet.anomalies(series)
|
55
65
|
```
|
56
66
|
|
67
|
+
Specify the width of uncertainty intervals (decrease for more anomalies)
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
Prophet.anomalies(series, interval_width: 0.99)
|
71
|
+
```
|
72
|
+
|
73
|
+
Also supports [advanced API](#advanced-api) options
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
Prophet.anomalies(series, growth: "logistic", weekly_seasonality: false)
|
77
|
+
```
|
78
|
+
|
57
79
|
## Advanced API
|
58
80
|
|
59
81
|
Check out the [Prophet documentation](https://facebook.github.io/prophet/docs/quick_start.html) for a great explanation of all of the features. The advanced API follows the Python API and supports the same features. It uses [Rover](https://github.com/ankane/rover) for data frames.
|
@@ -206,7 +228,7 @@ Add country-specific holidays
|
|
206
228
|
|
207
229
|
```ruby
|
208
230
|
m = Prophet.new
|
209
|
-
m.add_country_holidays(
|
231
|
+
m.add_country_holidays("US")
|
210
232
|
m.fit(df)
|
211
233
|
```
|
212
234
|
|
data/lib/prophet/forecaster.rb
CHANGED
@@ -386,6 +386,12 @@ module Prophet
|
|
386
386
|
|
387
387
|
def add_country_holidays(country_name)
|
388
388
|
raise Error, "Country holidays must be added prior to model fitting." if @history
|
389
|
+
|
390
|
+
# Fix for previously documented keyword argument
|
391
|
+
if country_name.is_a?(Hash) && country_name[:country_name]
|
392
|
+
country_name = country_name[:country_name]
|
393
|
+
end
|
394
|
+
|
389
395
|
# Validate names.
|
390
396
|
get_holiday_names(country_name).each do |name|
|
391
397
|
# Allow merging with existing holidays
|
data/lib/prophet/holidays.rb
CHANGED
@@ -2,25 +2,21 @@ module Prophet
|
|
2
2
|
module Holidays
|
3
3
|
def get_holiday_names(country)
|
4
4
|
years = (1995..2045).to_a
|
5
|
-
make_holidays_df(years, country)["holiday"].uniq
|
5
|
+
holiday_names = make_holidays_df(years, country)["holiday"].uniq
|
6
|
+
# TODO raise error in 0.4.0
|
7
|
+
logger.warn "Holidays in #{country} are not currently supported"
|
8
|
+
holiday_names
|
6
9
|
end
|
7
10
|
|
8
11
|
def make_holidays_df(year_list, country)
|
9
12
|
holidays_df[(holidays_df["country"] == country) & (holidays_df["year"].in?(year_list))][["ds", "holiday"]]
|
10
13
|
end
|
11
14
|
|
12
|
-
# TODO
|
15
|
+
# TODO improve performance
|
13
16
|
def holidays_df
|
14
17
|
@holidays_df ||= begin
|
15
|
-
holidays = {"ds" => [], "holiday" => [], "country" => [], "year" => []}
|
16
18
|
holidays_file = File.expand_path("../../data-raw/generated_holidays.csv", __dir__)
|
17
|
-
|
18
|
-
holidays["ds"] << row["ds"]
|
19
|
-
holidays["holiday"] << row["holiday"]
|
20
|
-
holidays["country"] << row["country"]
|
21
|
-
holidays["year"] << row["year"]
|
22
|
-
end
|
23
|
-
Rover::DataFrame.new(holidays)
|
19
|
+
Rover.read_csv(holidays_file, converters: [:date, :numeric])
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
data/lib/prophet/version.rb
CHANGED
data/lib/prophet.rb
CHANGED
@@ -21,9 +21,12 @@ module Prophet
|
|
21
21
|
Forecaster.new(**kwargs)
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.forecast(series, count: 10)
|
24
|
+
def self.forecast(series, count: 10, country_holidays: nil, cap: nil, verbose: false, **options)
|
25
25
|
raise ArgumentError, "Series must have at least 10 data points" if series.size < 10
|
26
26
|
|
27
|
+
# error early on unknown keywords
|
28
|
+
m = Prophet.new(**options)
|
29
|
+
|
27
30
|
# check type to determine output format
|
28
31
|
# check for before converting to time
|
29
32
|
keys = series.keys
|
@@ -62,12 +65,14 @@ module Prophet
|
|
62
65
|
|
63
66
|
# use series, not times, so dates are handled correctly
|
64
67
|
df = Rover::DataFrame.new({"ds" => series.keys, "y" => series.values})
|
68
|
+
df["cap"] = cap if cap
|
65
69
|
|
66
|
-
m =
|
67
|
-
m.
|
70
|
+
m.logger.level = ::Logger::FATAL unless verbose
|
71
|
+
m.add_country_holidays(country_holidays) if country_holidays
|
68
72
|
m.fit(df)
|
69
73
|
|
70
74
|
future = m.make_future_dataframe(periods: count, include_history: false, freq: freq)
|
75
|
+
future["cap"] = cap if cap
|
71
76
|
forecast = m.predict(future)
|
72
77
|
result = forecast[["ds", "yhat"]].to_a
|
73
78
|
|
@@ -84,11 +89,17 @@ module Prophet
|
|
84
89
|
result.map { |v| [v["ds"], v["yhat"]] }.to_h
|
85
90
|
end
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
92
|
+
# TODO better name for interval_width
|
93
|
+
# TODO DRY with forecast method
|
94
|
+
def self.anomalies(series, interval_width: 0.99, country_holidays: nil, cap: nil, verbose: false, **options)
|
95
|
+
df = Rover::DataFrame.new({"ds" => series.keys, "y" => series.values})
|
96
|
+
df["cap"] = cap if cap
|
97
|
+
|
98
|
+
m = Prophet.new(interval_width: interval_width, **options)
|
99
|
+
m.logger.level = ::Logger::FATAL unless verbose
|
100
|
+
m.add_country_holidays(country_holidays) if country_holidays
|
91
101
|
m.fit(df)
|
102
|
+
|
92
103
|
forecast = m.predict(df)
|
93
104
|
# filter df["ds"] to ensure dates/times in same format as input
|
94
105
|
df["ds"][(df["y"] < forecast["yhat_lower"]) | (df["y"] > forecast["yhat_upper"])].to_a
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prophet-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdstan
|