---
url: https://jer-k.github.io/effect-prophet/examples/trend.md
description: >-
  Linear trends and changepoints: automatic and explicit changepoints, and how
  to make the trend more or less flexible.
---

# Trends and changepoints

The trend is the long-term direction of your data. Real trends rarely stay perfectly straight:
a launch, a price change or a new competitor can make growth speed up or slow down. The points
where the trend changes direction are called **changepoints**.

::: tip Verified against Python Prophet
Linear trends match Python Prophet 1.4.0. The [linear growth benchmarks](/benchmarks/linear) show
every case side by side.
:::

## The default

With no options, the model places 25 possible changepoints evenly across the first 80% of your
history and uses only the ones the data supports. Most changepoints end up with almost no effect.

```ts
fit(history);
```

## Making the trend more or less flexible

`changepointPriorScale` controls how easily the trend can bend:

* **Smaller** (e.g. `0.01`): a smoother trend that ignores short bumps.
* **Larger** (e.g. `0.5`): a trend that follows changes closely, but may overreact to noise.

The default is `0.05`. [Choosing settings](./tuning) shows how to compare values on your own data.

## Example

```ts
import { Effect } from "effect";
import { fit, predict, prophetFittingBackendLayer } from "effect-prophet";

const date = (day: number) => new Date(Date.UTC(2025, 0, 1 + day)).toISOString();

// Sales grow slowly, then speed up after a product launch on day 60.
const history = Array.from({ length: 120 }, (_, day) => ({
  timestamp: date(day),
  value: day < 60 ? 50 + day * 0.2 : 62 + (day - 60) * 1.5,
}));

const nextWeek = Array.from({ length: 7 }, (_, offset) => date(120 + offset));

// Let the model look for changes on its own, with more freedom to bend.
const automatic = fit(history, {
  map: {
    changepoints: { mode: "auto", count: 25, range: 0.9 },
    changepointPriorScale: 0.5,
  },
});

// Or tell it exactly when the trend changed.
const explicit = fit(history, {
  map: {
    changepoints: { mode: "explicit", timestamps: [date(60)] },
  },
});

const forecasts = await Effect.runPromise(
  Effect.gen(function* () {
    const automaticForecast = yield* predict(yield* automatic, nextWeek);
    const explicitForecast = yield* predict(yield* explicit, nextWeek);

    return { automaticForecast, explicitForecast };
  }).pipe(Effect.provide(prophetFittingBackendLayer)),
);

console.log(
  "automatic:",
  forecasts.automaticForecast.map((row) => row.value.toFixed(1)).join(", "),
);

console.log("explicit: ", forecasts.explicitForecast.map((row) => row.value.toFixed(1)).join(", "));

```

```txt [Output]
automatic: 152.0, 153.5, 155.0, 156.5, 158.0, 159.5, 161.0
explicit:  152.0, 153.5, 155.0, 156.5, 158.0, 159.5, 161.0
```

Both models pick up the faster growth after day 60.

## Automatic changepoints

`{ mode: "auto" }` takes two optional settings:

| Setting | Default | Meaning                                                          |
| ------- | ------- | ---------------------------------------------------------------- |
| `count` | `25`    | How many candidate changepoints to place                         |
| `range` | `0.8`   | How far into the history they may go. `0.9` means the first 90%. |

Keeping `range` below `1` stops the trend from overreacting to the last few days of data.

## Changepoints you already know about

If you know when something changed, list the dates with `{ mode: "explicit", timestamps: [...] }`.
Each date must fall within your history.

## Other trend shapes

* [Growth with a ceiling](./saturating-growth), for values that level off at a maximum.
* [Flat trends](./flat-trend), for values that don't grow at all.
