{"id":13411824,"url":"https://github.com/ankane/prophet-ruby","last_synced_at":"2025-11-17T14:17:28.793Z","repository":{"id":43784533,"uuid":"254335080","full_name":"ankane/prophet-ruby","owner":"ankane","description":"Time series forecasting for Ruby","archived":false,"fork":false,"pushed_at":"2025-06-03T19:26:45.000Z","size":1934,"stargazers_count":426,"open_issues_count":1,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-10-08T02:58:19.659Z","etag":null,"topics":["anomaly-detection","forecasting"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ankane.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-09T10:01:47.000Z","updated_at":"2025-09-24T04:27:48.000Z","dependencies_parsed_at":"2023-12-26T20:40:22.121Z","dependency_job_id":"d43bffc7-00e2-486e-b630-faec6de89871","html_url":"https://github.com/ankane/prophet-ruby","commit_stats":{"total_commits":171,"total_committers":4,"mean_commits":42.75,"dds":0.4736842105263158,"last_synced_commit":"b8f0b1f40233316180929bae197db3638f666616"},"previous_names":["ankane/prophet"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/ankane/prophet-ruby","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fprophet-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fprophet-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fprophet-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fprophet-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ankane","download_url":"https://codeload.github.com/ankane/prophet-ruby/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fprophet-ruby/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284894658,"owners_count":27080744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-17T02:00:06.431Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anomaly-detection","forecasting"],"created_at":"2024-07-30T20:01:17.284Z","updated_at":"2025-11-17T14:17:28.781Z","avatar_url":"https://github.com/ankane.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Prophet.rb\n\nTime series forecasting for Ruby, ported from [Prophet](https://github.com/facebook/prophet)\n\nSupports:\n\n- Multiple seasonalities\n- Linear and non-linear growth\n- Holidays and special events\n\nAnd gracefully handles missing data\n\n[![Build Status](https://github.com/ankane/prophet-ruby/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/prophet-ruby/actions)\n\n## Installation\n\nAdd this line to your application’s Gemfile:\n\n```ruby\ngem \"prophet-rb\"\n```\n\n## Simple API\n\n### Forecasting\n\nGet future predictions for a time series\n\n```ruby\nseries = {\n  Date.parse(\"2020-01-01\") =\u003e 100,\n  Date.parse(\"2020-01-02\") =\u003e 150,\n  Date.parse(\"2020-01-03\") =\u003e 136,\n  # ...\n}\n\nProphet.forecast(series)\n```\n\nSpecify the number of predictions to return\n\n```ruby\nProphet.forecast(series, count: 3)\n```\n\nWorks great with [Groupdate](https://github.com/ankane/groupdate)\n\n```ruby\nseries = User.group_by_day(:created_at).count\nProphet.forecast(series)\n```\n\nAnd supports [advanced API](#advanced-api) options\n\n```ruby\nProphet.forecast(series, growth: \"logistic\", weekly_seasonality: false)\n```\n\n### Anomaly Detection\n\nDetect anomalies in a time series\n\n```ruby\nProphet.anomalies(series)\n```\n\nSpecify the width of uncertainty intervals (decrease for more anomalies)\n\n```ruby\nProphet.anomalies(series, interval_width: 0.99)\n```\n\nAlso supports [advanced API](#advanced-api) options\n\n```ruby\nProphet.anomalies(series, growth: \"logistic\", weekly_seasonality: false)\n```\n\n## Advanced API\n\nCheck 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.\n\n- [Quick Start](#advanced-quick-start)\n- [Plots](#plots)\n- [Saturating Forecasts](#saturating-forecasts)\n- [Trend Changepoints](#trend-changepoints)\n- [Holidays and Special Events](#holidays-and-special-events)\n- [Multiplicative Seasonality](#multiplicative-seasonality)\n- [Uncertainty Intervals](#uncertainty-intervals)\n- [Outliers](#outliers)\n- [Non-Daily Data](#non-daily-data)\n- [Diagnostics](#diagnostics)\n- [Additional Topics](#additional-topics)\n\n## Advanced Quick Start\n\n[Explanation](https://facebook.github.io/prophet/docs/quick_start.html)\n\nCreate a data frame with `ds` and `y` columns - here’s [an example](examples/example_wp_log_peyton_manning.csv) you can use\n\n```ruby\ndf = Rover.read_csv(\"example_wp_log_peyton_manning.csv\")\ndf.head\n```\n\nds | y\n--- | ---\n2007-12-10 | 9.59076113\n2007-12-11 | 8.51959031\n2007-12-12 | 8.18367658\n2007-12-13 | 8.07246736\n2007-12-14 | 7.89357207\n\nFit a model\n\n```ruby\nm = Prophet.new\nm.fit(df)\n```\n\nMake a data frame with a `ds` column for future predictions\n\n```ruby\nfuture = m.make_future_dataframe(periods: 365)\nfuture.tail\n```\n\nds |\n--- |\n2017-01-15 |\n2017-01-16 |\n2017-01-17 |\n2017-01-18 |\n2017-01-19 |\n\nMake predictions\n\n```ruby\nforecast = m.predict(future)\nforecast[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].tail\n```\n\nds | yhat | yhat_lower | yhat_upper\n--- | --- | --- | ---\n2017-01-15 | 8.21192840 | 7.52526442 | 8.92389960\n2017-01-16 | 8.53696359 | 7.79124970 | 9.22620028\n2017-01-17 | 8.32439891 | 7.62482699 | 9.04719328\n2017-01-18 | 8.15702395 | 7.40079968 | 8.91301650\n2017-01-19 | 8.16900433 | 7.45673678 | 8.83486188\n\n## Plots\n\nFor plots, install the [matplotlib](https://github.com/mrkn/matplotlib.rb) gem.\n\nPlot the forecast\n\n```ruby\nm.plot(forecast).savefig(\"forecast.png\")\n```\n\n![Forecast](https://blazer.dokkuapp.com/assets/prophet/forecast-77cf453fda67d1b462c6c22aee3a02572203b71c4517fedecc1f438cd374a876.png)\n\nPlot components\n\n```ruby\nm.plot_components(forecast).savefig(\"components.png\")\n```\n\n![Components](https://blazer.dokkuapp.com/assets/prophet/components-2cdd260e23bc89824ecca25f6bfe394deb5821d60b7e0e551469c90d204acd67.png)\n\n## Saturating Forecasts\n\n[Explanation](https://facebook.github.io/prophet/docs/saturating_forecasts.html)\n\nForecast logistic growth instead of linear\n\n```ruby\ndf = Rover.read_csv(\"example_wp_log_R.csv\")\ndf[\"cap\"] = 8.5\nm = Prophet.new(growth: \"logistic\")\nm.fit(df)\nfuture = m.make_future_dataframe(periods: 1826)\nfuture[\"cap\"] = 8.5\nforecast = m.predict(future)\n```\n\nSaturating minimum\n\n```ruby\ndf[\"y\"] = 10 - df[\"y\"]\ndf[\"cap\"] = 6\ndf[\"floor\"] = 1.5\nfuture[\"cap\"] = 6\nfuture[\"floor\"] = 1.5\nm = Prophet.new(growth: \"logistic\")\nm.fit(df)\nforecast = m.predict(future)\n```\n\n## Trend Changepoints\n\n[Explanation](https://facebook.github.io/prophet/docs/trend_changepoints.html)\n\nPlot changepoints\n\n```ruby\nfig = m.plot(forecast)\nm.add_changepoints_to_plot(fig.gca, forecast)\n```\n\nAdjust trend flexibility\n\n```ruby\nm = Prophet.new(changepoint_prior_scale: 0.5)\n```\n\nSpecify the location of changepoints\n\n```ruby\nm = Prophet.new(changepoints: [\"2014-01-01\"])\n```\n\n## Holidays and Special Events\n\n[Explanation](https://facebook.github.io/prophet/docs/seasonality,_holiday_effects,_and_regressors.html)\n\nCreate a data frame with `holiday` and `ds` columns. Include all occurrences in your past data and future occurrences you’d like to forecast.\n\n```ruby\nplayoffs = Rover::DataFrame.new({\n  \"holiday\" =\u003e \"playoff\",\n  \"ds\" =\u003e [\n    \"2008-01-13\", \"2009-01-03\", \"2010-01-16\",\n    \"2010-01-24\", \"2010-02-07\", \"2011-01-08\",\n    \"2013-01-12\", \"2014-01-12\", \"2014-01-19\",\n    \"2014-02-02\", \"2015-01-11\", \"2016-01-17\",\n    \"2016-01-24\", \"2016-02-07\"\n  ],\n  \"lower_window\" =\u003e 0,\n  \"upper_window\" =\u003e 1\n})\nsuperbowls = Rover::DataFrame.new({\n  \"holiday\" =\u003e \"superbowl\",\n  \"ds\" =\u003e [\"2010-02-07\", \"2014-02-02\", \"2016-02-07\"],\n  \"lower_window\" =\u003e 0,\n  \"upper_window\" =\u003e 1\n})\nholidays = playoffs.concat(superbowls)\n\nm = Prophet.new(holidays: holidays)\nm.fit(df)\n```\n\nAdd country-specific holidays\n\n```ruby\nm = Prophet.new\nm.add_country_holidays(\"US\")\nm.fit(df)\n```\n\nSpecify custom seasonalities\n\n```ruby\nm = Prophet.new(weekly_seasonality: false)\nm.add_seasonality(name: \"monthly\", period: 30.5, fourier_order: 5)\nforecast = m.fit(df).predict(future)\n```\n\nSpecify additional regressors\n\n```ruby\nnfl_sunday = lambda do |ds|\n  date = ds.respond_to?(:to_date) ? ds.to_date : Date.parse(ds)\n  date.wday == 0 \u0026\u0026 (date.month \u003e 8 || date.month \u003c 2) ? 1 : 0\nend\n\ndf[\"nfl_sunday\"] = df[\"ds\"].map(\u0026nfl_sunday)\n\nm = Prophet.new\nm.add_regressor(\"nfl_sunday\")\nm.fit(df)\n\nfuture[\"nfl_sunday\"] = future[\"ds\"].map(\u0026nfl_sunday)\n\nforecast = m.predict(future)\n```\n\n## Multiplicative Seasonality\n\n[Explanation](https://facebook.github.io/prophet/docs/multiplicative_seasonality.html)\n\nSpecify multiplicative seasonality\n\n```ruby\ndf = Rover.read_csv(\"example_air_passengers.csv\")\nm = Prophet.new(seasonality_mode: \"multiplicative\")\nm.fit(df)\nfuture = m.make_future_dataframe(periods: 50, freq: \"MS\")\nforecast = m.predict(future)\n```\n\nSpecify mode when adding seasonality and regressors\n\n```ruby\nm = Prophet.new(seasonality_mode: \"multiplicative\")\nm.add_seasonality(name: \"quarterly\", period: 91.25, fourier_order: 8, mode: \"additive\")\nm.add_regressor(\"regressor\", mode: \"additive\")\n```\n\n## Uncertainty Intervals\n\n[Explanation](https://facebook.github.io/prophet/docs/uncertainty_intervals.html)\n\nSpecify the width of uncertainty intervals (80% by default)\n\n```ruby\nProphet.new(interval_width: 0.95)\n```\n\nGet uncertainty in seasonality\n\n```ruby\nProphet.new(mcmc_samples: 300)\n```\n\n## Outliers\n\n[Explanation](https://facebook.github.io/prophet/docs/outliers.html)\n\nRemove outliers\n\n```ruby\ndf = Rover.read_csv(\"example_wp_log_R_outliers1.csv\")\ndf[\"y\"][(df[\"ds\"] \u003e \"2010-01-01\") \u0026 (df[\"ds\"] \u003c \"2011-01-01\")] = Float::NAN\nm = Prophet.new.fit(df)\n```\n\n## Non-Daily Data\n\n[Explanation](https://facebook.github.io/prophet/docs/non-daily_data.html)\n\nSub-daily data\n\n```ruby\ndf = Rover.read_csv(\"example_yosemite_temps.csv\")\nm = Prophet.new(changepoint_prior_scale: 0.01).fit(df)\nfuture = m.make_future_dataframe(periods: 300, freq: \"H\")\nforecast = m.predict(future)\n```\n\n## Diagnostics\n\n[Explanation](https://facebook.github.io/prophet/docs/diagnostics.html)\n\nCross validation\n\n```ruby\ndf_cv = Prophet::Diagnostics.cross_validation(m, initial: \"730 days\", period: \"180 days\", horizon: \"365 days\")\n```\n\nCustom cutoffs\n\n```ruby\ncutoffs = [\"2013-02-15\", \"2013-08-15\", \"2014-02-15\"].map { |v| Time.parse(\"#{v} 00:00:00 UTC\") }\ndf_cv2 = Prophet::Diagnostics.cross_validation(m, cutoffs: cutoffs, horizon: \"365 days\")\n```\n\nGet performance metrics\n\n```ruby\ndf_p = Prophet::Diagnostics.performance_metrics(df_cv)\n```\n\nPlot cross validation metrics\n\n```ruby\nProphet::Plot.plot_cross_validation_metric(df_cv, metric: \"mape\")\n```\n\nHyperparameter tuning\n\n```ruby\nparam_grid = {\n  changepoint_prior_scale: [0.001, 0.01, 0.1, 0.5],\n  seasonality_prior_scale: [0.01, 0.1, 1.0, 10.0]\n}\n\n# Generate all combinations of parameters\nall_params = param_grid.values[0].product(*param_grid.values[1..-1]).map { |v| param_grid.keys.zip(v).to_h }\nrmses = [] # Store the RMSEs for each params here\n\n# Use cross validation to evaluate all parameters\nall_params.each do |params|\n  m = Prophet.new(**params).fit(df) # Fit model with given params\n  df_cv = Prophet::Diagnostics.cross_validation(m, cutoffs: cutoffs, horizon: \"30 days\")\n  df_p = Prophet::Diagnostics.performance_metrics(df_cv, rolling_window: 1)\n  rmses \u003c\u003c df_p[\"rmse\"][0]\nend\n\n# Find the best parameters\ntuning_results = Rover::DataFrame.new(all_params)\ntuning_results[\"rmse\"] = rmses\np tuning_results\n```\n\n## Additional Topics\n\n[Explanation](https://facebook.github.io/prophet/docs/additional_topics.html)\n\nSave a model\n\n```ruby\nFile.write(\"model.json\", m.to_json)\n```\n\nLoad a model\n\n```ruby\nm = Prophet.from_json(File.read(\"model.json\"))\n```\n\nUses the same format as Python, so models can be saved and loaded in either language\n\nFlat trend\n\n```ruby\nm = Prophet.new(growth: \"flat\")\n```\n\nUpdating fitted models\n\n```ruby\ndef stan_init(m)\n  res = {}\n  [\"k\", \"m\", \"sigma_obs\"].each do |pname|\n    res[pname] = m.params[pname][0, true][0]\n  end\n  [\"delta\", \"beta\"].each do |pname|\n    res[pname] = m.params[pname][0, true]\n  end\n  res\nend\n\ndf = Rover.read_csv(\"example_wp_log_peyton_manning.csv\")\ndf1 = df[df[\"ds\"] \u003c= \"2016-01-19\"] # All data except the last day\nm1 = Prophet.new.fit(df1) # A model fit to all data except the last day\n\nm2 = Prophet.new.fit(df) # Adding the last day, fitting from scratch\nm2 = Prophet.new.fit(df, init: stan_init(m1)) # Adding the last day, warm-starting from m1\n```\n\n## Resources\n\n- [Forecasting at Scale](https://peerj.com/preprints/3190.pdf)\n\n## Credits\n\nThis library was ported from the [Prophet Python library](https://github.com/facebook/prophet) and is available under the same license.\n\n## History\n\nView the [changelog](https://github.com/ankane/prophet-ruby/blob/master/CHANGELOG.md)\n\n## Contributing\n\nEveryone is encouraged to help improve this project. Here are a few ways you can help:\n\n- [Report bugs](https://github.com/ankane/prophet-ruby/issues)\n- Fix bugs and [submit pull requests](https://github.com/ankane/prophet-ruby/pulls)\n- Write, clarify, or fix documentation\n- Suggest or add new features\n\nTo get started with development:\n\n```sh\ngit clone https://github.com/ankane/prophet-ruby.git\ncd prophet-ruby\nbundle install\nbundle exec rake vendor:all\nbundle exec rake test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankane%2Fprophet-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fankane%2Fprophet-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankane%2Fprophet-ruby/lists"}