---
title: "Time-Series Analysis: What Is It and How to Use It"
published: 2024-03-12T07:34:00.000-04:00
updated: 2026-10-15T14:17:07.000-04:00
excerpt: "Learn what time-series analysis is and how to use it: check trend, seasonality, and noise with SQL, and know when Python or R is the better tool. "
tags: General, Time Series Data
authors: Team Tiger Data
---

> **TimescaleDB is now Tiger Data.**

_Updated October 2026_

Many statistical methods assume independent observations. Time-series analysis explicitly considers temporal order and possible dependence between observations. 

The relationship between neighboring data points can be the signal worth examining, because today's value often relates to yesterday's.

Tiger Data builds TimescaleDB, a PostgreSQL extension for time-series workloads, so this article has a clear point of view. It also shows where SQL is the practical choice and where Python or R is still the better tool. Both have a real role.

For a broader introduction to what a time series is and how it gets used, see [What Is a Time Series and How Is It Used?](https://www.tigerdata.com/blog/time-series-introduction)

## What is time-series analysis?

Time-series analysis examines a sequence of timestamped observations in order. It looks for patterns in the data, such as trend, seasonality, cycles, and noise. Forecasting is a separate task. Analysis explains what the data has done, while forecasting projects what it will do next.

Time-series analysis differs from general data analysis because temporal ordering and the dependence between neighboring observations are central to the data. A temperature reading at 3 PM is often correlated with earlier readings. If you strip the timestamps and shuffle the rows, the analysis falls apart.

Time series appear wherever something is measured repeatedly over time. Examples include server metrics sampled every second, electricity demand recorded every half-hour, and patient vitals logged throughout a hospital stay. Time-series analysis examines these sequences to find what is trending, what repeats, and what spikes without warning.

## Time-series analysis vs. time-series forecasting

Analysis examines and explains patterns in historical data. Forecasting uses those patterns to project future values. Analysis is frequently the necessary first step before forecasting, but the two answer different questions and require different methods.

If you came here looking specifically for forecasting, see Tiger Data's [Time-Series Forecasting with PostgreSQL](https://www.tigerdata.com/learn/time-series-forecasting) guide and the companion [Time-Series Forecasting, Definition, Methods, and Applications](https://www.tigerdata.com/blog/what-is-time-series-forecasting) post.

### Time-series analysis vs. anomaly detection vs. storage and query layers

People often mix up these three terms. Time-series analysis is the broad practice of examining patterns in time-ordered data. Anomaly detection is one specific task within that practice. It flags points that look unusual compared to the expected pattern. The storage and query layer holds the data and runs the analysis against it.

For a deeper look at anomaly detection (z-score, MAD, real-time alerting pipelines), see Tiger Data's dedicated [Time Series Anomaly Detection, Methods, SQL, and Real-Time Implementation](https://www.tigerdata.com/learn/time-series-anomaly-detection-methods-sql-real-time-implementation) guide. This article covers anomaly detection only lightly, on purpose.

## Checklist for time-series analysis

Start with three exploratory checks: identify the trend, look for seasonality, and examine variation. 

The checklist examples use the single-series `sensor_readings` table below. Load your own observations before querying it. 

The plain PostgreSQL window query needs no extensions; the hyperfunction examples require TimescaleDB and the companion `timescaledb_toolkit` extension. On [Tiger Cloud](https://www.tigerdata.com/cloud), both are enabled by default. On self-hosted TimescaleDB, [install the toolkit](https://www.tigerdata.com/docs/deploy/self-hosted/tooling/install-toolkit) and run `CREATE EXTENSION timescaledb_toolkit`; in your database. Later use cases specify additional columns or tables.

```SQL
-- sensor_readings table structure
CREATE TABLE sensor_readings (
  time   TIMESTAMPTZ NOT NULL,
  value  DOUBLE PRECISION
);
```

### Step 1: Trend

A plain window function gives a quick trend check on any Postgres instance:

```SQL
-- Plain Postgres: 7-period moving average
SELECT
  time,
  value,
  AVG(value) OVER (
    ORDER BY time
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) AS moving_avg_7
FROM sensor_readings
ORDER BY time;
```

This query runs on vanilla PostgreSQL with no extensions. It reads the selected raw observations each time it runs. The cost depends on the number of rows, their ordering, and the execution plan; benchmark it against your workload rather than assuming a fixed scaling threshold.

The `timescaledb_toolkit` extension offers a two-step approach with `stats_agg()` and `rolling()`. This example changes the analysis from seven observations to daily summaries, then rolls a window over those summaries. It still reads the raw rows to build the summaries each time the query runs:

```SQL
-- Two-step aggregation: bucket -> stats_agg -> rolling
-- The third argument to time_bucket() sets the timezone, so each bucket
-- covers one local calendar day. Omit it to get UTC days.
SELECT
  time_bucket('1 day', time, 'America/New_York') AS day,
  average(
    rolling(stats_agg(value))
    OVER (ORDER BY time_bucket('1 day', time, 'America/New_York')
          ROWS 6 PRECEDING)
  ) AS rolling_avg_7day
FROM sensor_readings
GROUP BY day
ORDER BY day;
```

The `rolling()` function combines multiple `StatsSummary1D` objects produced by `stats_agg()`, so the window step works on summaries rather than individual readings. Avoiding raw-data reads across repeated queries requires storing those summaries, for example in a continuous aggregate.

Here and in the rolling examples below, `ROWS 6 PRECEDING` means the current bucket plus six preceding buckets. This covers seven calendar days only when every day has data; the first six results use shorter windows. The average weights individual readings equally, not each day equally.

**Timezone note:** A `TIMESTAMPTZ` column stores an absolute instant. For this input type, default daily `time_bucket()` boundaries are midnight UTC, regardless of the session timezone; the session timezone still affects how the returned timestamps are displayed. If you need local calendar days, pass a timezone as the third argument to `time_bucket()`. Around daylight-saving changes, a local day can span 23 or 25 elapsed hours.

### Step 2: Seasonality

You can surface seasonality without a full statistical decomposition. Group the readings by hour of day, then compare the average and spread of values across those hours:

```SQL
-- Group readings by local hour of day to surface a daily pattern
-- Replace the time zone with your sensor's local time zone
SELECT
  EXTRACT(HOUR FROM time AT TIME ZONE 'America/New_York') AS hour_of_day,
  average(stats_agg(value))   AS avg_value,
  stddev(stats_agg(value))    AS stddev_value
FROM sensor_readings
GROUP BY hour_of_day
ORDER BY hour_of_day;
```

Suppose `avg_value` shows a clear peak between hours 8 and 18. That points to a daily seasonal pattern. If `stddev_value` is also high in those hours and low overnight, readings vary more during the active hours. To judge how stable the pattern is, compare the gap between hourly averages with the spread within each hour.

This is a repeatable exploratory check; its cost depends on the data and query plan. Compare results across bucket widths to see which patterns persist. These grouped summaries do not perform a full statistical decomposition. Python and R provide established tools for decomposition, including STL.

### Step 3: Noise

Use the `stddev()` and `kurtosis()` accessors from `stats_agg()` over a rolling window to characterize variation in the readings. These statistics include any trend and seasonality; they do not isolate residual noise:

```SQL
-- Characterize variation with rolling stddev and kurtosis
-- Replace the time zone with your sensor's local time zone
SELECT
  time_bucket('1 day', time, 'America/New_York') AS day,
  stddev(
    rolling(stats_agg(value))
    OVER (ORDER BY time_bucket('1 day', time, 'America/New_York')
          ROWS 6 PRECEDING),
    'population'
  ) AS rolling_stddev,
  kurtosis(
    rolling(stats_agg(value))
    OVER (ORDER BY time_bucket('1 day', time, 'America/New_York')
          ROWS 6 PRECEDING),
    'population'
  ) AS rolling_kurtosis
FROM sensor_readings
GROUP BY day
ORDER BY day;
```

This query returns Pearson kurtosis, for which a normal distribution has a value of 3. A value above 3 suggests a greater contribution from extreme deviations, but it is not a formal test for heavy tails. Interpret it alongside the window size and underlying pattern before setting anomaly thresholds. A constant window has zero variance, so its kurtosis is undefined.

PostgreSQL has no built-in ADF or KPSS stationarity test; these tests are commonly run in Python or R. See Tiger Data's [Stationary Time-Series Analysis](https://www.tigerdata.com/learn/stationary-time-series-analysis) guide for more.

### SQL or Python/R?

Use SQL when your data already lives in Postgres and you want repeatable checks that run close to the data. The examples above use TimescaleDB and `timescaledb_toolkit` for rolling trend statistics (`stats_agg()` and `rolling()`), hour-of-day seasonality checks, and variation checks (`stddev()` and `kurtosis()`).

Use Python or R when you need established tools for formal statistical tests or a full decomposition. For language-specific workflows, see Tiger Data's [Time-Series Analysis in R](https://www.tigerdata.com/learn/time-series-analysis-in-r) and [Time-Series Analysis and Forecasting With Python](https://www.tigerdata.com/learn/time-series-analysis-and-forecasting-with-python) guides.

## Scale considerations

For repeated queries over a growing dataset, store per-bucket `stats_agg()` summaries in a [continuous aggregate](https://www.tigerdata.com/docs/learn/continuous-aggregates), then apply `rolling()` when querying those summaries. 

A refresh policy updates eligible buckets on a schedule within a configured time window; it does not refresh on each insert. This can reduce repeated aggregation work, but the benefit depends on the workload, bucket size, and refresh policy. Choose a bucket width that preserves the detail you need: daily summaries cannot reconstruct hour-of-day patterns.

[Real-time aggregation](https://www.tigerdata.com/docs/learn/continuous-aggregates/real-time-aggregates) can combine materialized results with newer raw rows at query time. It is disabled by default in TimescaleDB 2.13 and later. Historical backfills still need a [scheduled or manual refresh](https://www.tigerdata.com/docs/build/continuous-aggregates/refresh-policies) that covers the affected buckets.

For more on time bucketing and gap-filling, see [Mind the Gap, Using SQL Functions for Time-Series Analysis](https://www.tigerdata.com/blog/sql-functions-for-time-series-analysis). For the statistical-summary functions used above, see the [`stats_agg()` reference](https://www.tigerdata.com/docs/reference/toolkit/statistical-and-regression-analysis/stats_agg-one-variable/stats_agg).

## Use cases for time-series analysis

Time-series analysis applies wherever data accumulates over time and its patterns matter operationally. The use cases below cover several common domains. Four include SQL examples.

### IoT and sensor monitoring

Device readings often arrive at irregular intervals because of network hiccups, batch uploads, or sensor sleep modes. `time_bucket_gapfill()` creates regular buckets, and `locf()` (last observation carried forward) can fill values when a previous value is available. This example assumes a `device_id` column has been added and populated so readings can be grouped by device:

```SQL
-- Fill hourly gaps with the last non-null hourly average
-- Assumes sensor_readings also has a device_id column
SELECT
  time_bucket_gapfill('1 hour', time, 
    start => '2026-01-01 00:00:00+00',
    finish => '2026-01-08 00:00:00+00'
  ) AS hour,
  device_id,
  locf(AVG(value), treat_null_as_missing => true) AS locf_value
FROM sensor_readings
WHERE time >= TIMESTAMPTZ '2026-01-01 00:00:00+00'
  AND time < TIMESTAMPTZ '2026-01-08 00:00:00+00'
GROUP BY hour, device_id
ORDER BY device_id, hour;
```

Here, `locf()` carries the last non-null hourly average forward, and `treat_null_as_missing => true` also skips existing NULL averages. Leading gaps remain NULL until a value is available; provide a `prev` lookup if you need to seed them from before the selected range. Carry values forward only when holding the prior value is appropriate for the signal. The query fills gaps for devices represented in the selected data, not devices with no readings in that range.

### Energy demand

Energy load often has seasonal patterns. The rolling average from the checklist, combined with a `LAG()` comparison, can highlight changes in demand. It is an exploratory comparison, not a complete monitoring or anomaly-detection system:

```SQL
-- Compare rolling demand averages across seven daily buckets
  SELECT
  day,
  rolling_avg_7day,
  LAG(rolling_avg_7day, 7) OVER (ORDER BY day) AS avg_7_days_ago,
  rolling_avg_7day - LAG(rolling_avg_7day, 7) OVER (ORDER BY day) AS delta
FROM ( 
-- UTC days by default; add a timezone argument for local days
  SELECT
    time_bucket('1 day', time) AS day,
    average(
      rolling(stats_agg(value))
      OVER (ORDER BY time_bucket('1 day', time)
            ROWS 6 PRECEDING)
    ) AS rolling_avg_7day
  FROM sensor_readings
  GROUP BY day 
) sub
ORDER BY day;
```

Rows where delta exceeds a chosen threshold flag a rise in the rolling average. This compares with the previous week's window only when every day has a bucket: `LAG(..., 7)` looks back seven rows, not seven calendar days. Gapfill at daily resolution first if dates are missing, and choose whether missing values should remain NULL or be imputed.

### Manufacturing

High-frequency production-line sensors produce noisy readings that hide the underlying trend. Plotting thousands of points per second in a monitoring dashboard is slow and hard to read. `asap_smooth()` from the `timescaledb_toolkit` extension reduces the series to a manageable number of points and preserves the trend shape:

```SQL
-- Smooth a high-frequency sensor feed for dashboard display
SELECT *
FROM unnest(
  (SELECT asap_smooth(time, value, 250)
   FROM sensor_readings
   WHERE time >= NOW() - INTERVAL '7 days')
);
```

The `resolution` parameter (250 here) sets the approximate number of output points. `asap_smooth()` computes new smoothed values instead of selecting existing ones. It minimizes local variance and preserves the larger trend shape.

### Finance and trading

Raw tick data from exchanges arrives as individual price-volume pairs. `candlestick_agg()` from `timescaledb_toolkit` builds OHLC (open/high/low/close) bars in SQL. This example assumes `crypto_ticks` contains one asset's raw trades, with `ts TIMESTAMPTZ, price DOUBLE PRECISION`, and per-trade `volume DOUBLE PRECISION`. If the table contains multiple assets, include the symbol in both the grouping and the output:

```SQL
-- Build 1-minute OHLCV bars from raw tick data
SELECT
  bucket,
  open(agg)   AS open,
  high(agg)   AS high,
  low(agg)    AS low,
  close(agg)  AS close,
  volume(agg) AS volume,
  vwap(agg)   AS vwap
FROM (
  SELECT
    time_bucket('1 minute', ts) AS bucket,
    candlestick_agg(ts, price, volume) AS agg
  FROM crypto_ticks
  GROUP BY bucket
) sub
ORDER BY bucket;
```

`candlestick_agg(ts, price, volume)` takes a timestamp, price, and volume for each tick and produces an intermediate `Candlestick` object. The accessor functions (`open()`, `high()`, `low()`, `close()`, `volume()`, `vwap()`) read specific values from that aggregate. This follows the same two-step pattern as `stats_agg()`: aggregate once, then read multiple metrics from the same object.

### Weather, e-commerce, healthcare, and web traffic

Weather data analysis focuses on detecting long-term trends (warming baselines, shifting seasonal windows) and identifying extreme events against historical distributions. 

E-commerce teams use it to separate genuine growth from seasonal effects in purchase data, so promotions get compared with the right baseline. 

Healthcare organizations analyze patient vital signs over time to look for signs of deterioration. Web and application traffic monitoring uses trend and seasonality analysis to distinguish organic growth from anomalies. 

A traffic spike that looks alarming against today's volume may be routine if it matches last year's same-week pattern.

## An illustrative example of time-series analysis

Consider a hypothetical fitness app that tracks daily step counts. The raw data is a time series with one count per user per day. Analyzing it can reveal several layers at once.

At the individual level, trend analysis shows whether a user is becoming more or less active over weeks and months. Seasonality analysis shows yearly and weekly rhythms. For example, users might walk more during warmer months, depending on their location, and weekday and weekend patterns often differ clearly. Noise characterization separates users with erratic patterns from users with steady ones. Erratic patterns can point to a data quality issue (the app wasn't running) or to a genuine behavior change worth flagging.

At the population level, decomposing the time series into trend, seasonality, and residual components helps the product team answer questions that simple aggregate statistics cannot answer well. Is overall engagement actually growing after accounting for the seasonal boost? Which user segments show a genuine trend change versus a seasonal effect? Where are the anomalies in device reporting?

Examining what the data has already done gives any later forecast (if needed) a firmer basis.

## Why use time-series analysis

Time-series analysis reveals patterns that aggregate statistics obscure.

For example, a system averaging 200 ms response time over a quarter tells you little about changes within that period. Time-series analysis can show whether response time trends up week over week or spikes every Monday morning. Correlating those patterns with scheduled jobs, hardware events, and traffic data can help investigate possible causes.

Spotting a trend early, before it crosses an operational threshold, gives teams time to act. Understanding seasonal effects lets you compare performance against the right baseline instead of an average that blends peak and off-peak behavior. Early anomaly detection can give teams more time to investigate an issue.

Trend detection, seasonal adjustment, and anomaly flagging apply anywhere data accumulates over time and patterns in that data have operational consequences.

## The four components of a time series

### Trend

Trend describes the long-term direction of a series. It can be steady growth, gradual decline, or a structural shift at a specific point in time. For example, an e-commerce site's weekly order volume that climbs over 18 months shows a trend. The rolling average queries in the checklist section above are a practical tool for exposing it.

### Seasonality

Seasonality is a pattern that repeats at a fixed, calendar-driven interval. Retail sales peak every November and December. API call volume drops every weekend. Electricity demand rises every summer. Seasonal patterns recur at a known frequency, though their strength can change over time.

### Cyclicity

Cyclicity is a repeating pattern without a fixed calendar period. Business cycles expand and contract over years, and the length of each cycle varies. No specific calendar date anchors a cycle, which makes cycles harder to identify and measure than seasonality.

### Irregularity

Irregularity appears in two forms. The first is a property of the data’s values. The second is a data quality issue.

The first form is unexpected spikes and noise. This residual variation is not explained by the modeled trend, seasonality, or cycle; not every residual is an outlier. A `LAG()` window function gives a lightweight, introductory flag for them. It compares each period's value with the value from N periods back and flags rows where the delta crosses a threshold:

```SQL
-- Delta-based spike flag: compare each day's value to 7 days prior
-- UTC days by default; add a timezone argument for local days 
SELECT
  time_bucket('1 day', time) AS day,
  AVG(value) AS avg_value,
  AVG(value) - LAG(AVG(value), 7)
    OVER (ORDER BY time_bucket('1 day', time)) AS delta_vs_7_days_ago
FROM sensor_readings
GROUP BY day
ORDER BY day;
```

**Note:** `LAG(..., 7)` looks back 7 rows. It matches 7 days only when every day has data. If a day is missing, the comparison uses the wrong day. Gapfill at daily resolution first (see the second form of irregularity below) to keep the comparison aligned; the IoT example uses hourly buckets instead.

Rows where `delta_vs_7_days_ago` exceeds your threshold are candidates for investigation. Treat this as a first-pass filter. For a deeper treatment with z-score and MAD-based methods, see the [Time Series Anomaly Detection](https://www.tigerdata.com/learn/time-series-anomaly-detection-methods-sql-real-time-implementation) guide.

A separate issue is irregular or gapped sampling. A sensor might go offline, a batch job might fail, or a device might disconnect. `time_bucket_gapfill()` creates regular intervals, and `locf()` can carry a previous value into gaps when that assumption fits the signal. In the IoT example above, it carries hourly averages; leading gaps can still remain NULL. Otherwise, consider interpolation or leave missing values explicit.

Gaps differ from the other components. Trend, seasonality, cyclicity, and noise describe the values in a series. Gapped sampling describes how the data was collected. Assess missingness before analysis and choose a handling method that fits the signal; a complete series of imputed values is not always the right goal.

## Types of time-series analysis and related tasks

### Exploratory analysis

Exploratory analysis is the first pass. Plot the data, compute summary statistics by time period, and look for obvious patterns before you commit to a specific method. The three steps in the checklist section belong here. They are repeatable, quick to run, and show you what to investigate further.

### Curve fitting and downsampling

Curve fitting estimates a mathematical function from observed data. Smoothing and downsampling are related but distinct tools for reducing noise or the number of plotted points. A close historical fit does not necessarily make a curve suitable for forecasting.

For visualization, `asap_smooth()` and `lttb()` (Largest Triangle Three Buckets) from the `timescaledb_toolkit` extension are SQL-native downsampling tools. `asap_smooth()` computes new smoothed values that preserve the overall trend shape while minimizing local variance. This helps when you want to see the signal through the noise. `lttb()` selects original data points that preserve visual similarity. 

It tries to retain the visual shape, but it does not guarantee that every peak or valley survives. This helps when point identity matters, such as in labeled Grafana panels. The visualization examples section below demonstrates `lttb()` on a high-frequency series.

### Classification and segmentation

Classification assigns a label to a series or time window. Segmentation divides a series into meaningful intervals. Clustering groups series with similar behavior. For example, a fleet of 10,000 IoT devices produces 10,000 time series. 

Clustering those devices by behavioral pattern can separate devices that spike on weekday mornings from devices that run flat. Python or R algorithms can work with features derived from the series; ECG heartbeat cycles provide a separate example of temporal segmentation.

### Forecasting

Forecasting is a related task that builds on analysis. Analysis explains what the data has done, and forecasting projects what it will do next. It is a distinct discipline with its own methods (ARIMA, Prophet, exponential smoothing, neural networks) and its own assumptions. Analysis often comes first, because you need to understand a series' trend, seasonality, and stationarity before you fit a forecasting model to it. For more on forecasting, see the [Time Series Forecasting with PostgreSQL](https://www.tigerdata.com/learn/time-series-forecasting) guide.

## Visualization examples

The first three examples come from published sources. Follow each link to see the chart. 

### Trend over time for Australian electricity demand

In _Forecasting: Principles and Practice_, Rob Hyndman and George Athanasopoulos plot [monthly Australian electricity demand from 1980 to 1995](https://otexts.com/fpp2/autocorrelation.html). The series shows both trend and seasonality, and its autocorrelation plot helps reveal the repeating annual pattern. That monthly series is separate from the half-hourly dataset used in the illustrative downsampling query below. 

### Curve fitting for Boston Marathon winning times

The same book compares [fitted curves for men's Boston Marathon winning times from 1924 onward](https://otexts.com/fpp2/nonlinear-regression.html), after the course was lengthened. It illustrates an important distinction: a curve that fits historical data closely may extrapolate poorly. In that comparison, the cubic spline fits the historical observations closely but produces less plausible forecasts than the piecewise linear fit.

### Segmentation of ECG-style physiological signals

Electrocardiogram (ECG) signals record the heart's electrical activity over time. A [2021 _Sensors_ study on ECG classification](https://www.mdpi.com/1424-8220/21/24/8174) uses R-peak detection to locate each heartbeat in a recording, so the signals can then be classified by their characteristics. Rolling `stddev()` and `kurtosis()` can summarize variation, but those statistics alone cannot distinguish physiological anomalies from sensor noise or validate a clinical diagnosis.

### Downsampling for dashboards with electricity demand and `lttb()`

For a separate dashboard example, assume electricity\_demand contains your half-hourly readings in `time TIMESTAMPTZ` and `demand_mw DOUBLE PRECISION` columns. The query below selects 2024 data and uses `lttb()` to reduce the number of plotted points. It is illustrative SQL, not a query against the monthly historical dataset above:

```SQL
-- Downsample a high-frequency electricity demand series for charting
SELECT *
FROM unnest(
  (SELECT lttb(time, demand_mw, 500)
   FROM electricity_demand
   WHERE time >= '2024-01-01'
     AND time < '2025-01-01')
);
```

The function targets approximately 500 points and tries to preserve the selected series' visual shape. It can reduce the data sent to a dashboard, but it does not guarantee preservation of every peak or valley.

## Where to go next

Time-series analysis is the foundation. Once you can identify trend, seasonality, and noise in your data, you're ready to build forecasting models, set anomaly thresholds grounded in actual signal characteristics, and design monitoring pipelines intended to surface problems earlier.

TimescaleDB and the companion `timescaledb_toolkit` extension make these analytical steps (rolling statistics, gap-filling, downsampling, and OHLC aggregation) available in SQL, so you can run them against your existing data without exporting to a separate environment. Start with the checklist queries above against your own table, then follow the links throughout this article to go deeper on whichever technique your use case needs most.

* * *

## FAQ on time-series analysis

### What is time-series analysis?

Time-series analysis is the practice of examining a sequence of time-ordered, timestamped observations to identify patterns in that data, specifically trend (the long-term direction), seasonality (repeating calendar-driven patterns), cycles (longer repeating patterns without a fixed period), and noise (irregularity and outliers). It explains what historical data has done, as distinct from forecasting, which projects what the data will do next.

### What is the difference between time-series analysis and time-series forecasting?

Analysis examines and explains patterns in historical data. Forecasting uses those patterns to project future values. Analysis is often a necessary first step before forecasting, because you need to understand trend, seasonality, and stationarity before fitting a forecasting model, but the two tasks answer different questions and use different methods.

### Is time-series analysis the same as anomaly detection?

Anomaly detection is one specific task within the broader practice of time-series analysis. It flags unusual observations relative to an expected pattern. For a deep treatment of anomaly detection methods (z-score, MAD, real-time alerting), see Tiger Data’s dedicated [anomaly detection guide](https://www.tigerdata.com/learn/time-series-anomaly-detection-methods-sql-real-time-implementation).

### What are the four components of a time series?

Trend (the long-term direction of the series), seasonality (repeating patterns tied to a calendar period, such as weekly or annual), cyclicity (repeating patterns without a fixed calendar period, such as business cycles), and irregularity (noise and unexpected spikes). Gaps in the data, such as missing readings from an offline sensor, are a separate data quality issue. Assess missingness before analysis and choose a handling method that fits the signal.

### What is time-series decomposition?

Time-series decomposition breaks a time series into its component parts, including trend, seasonality, and residual noise, to understand what’s driving the overall observed pattern. The `time_bucket()` and `stats_agg()` examples here provide exploratory summaries, not a full separation of trend, seasonality, and residual noise. Python’s `statsmodels` library provides a full statistical STL decomposition for cases where the formal separation of components is required.

### What does it mean for a time series to be stationary?

A stationary time series has statistical properties (mean, variance, autocorrelation structure) that don’t change over time. Many classical forecasting and modeling methods assume stationarity, which is why checking for it is a standard early step in analysis. Formal stationarity tests (ADF, KPSS) are typically run in Python or R. See Tiger Data’s [Stationary Time-Series Analysis](https://www.tigerdata.com/learn/stationary-time-series-analysis) guide for the full treatment.

### Can you do time-series analysis directly in SQL, without exporting to Python?

Yes, for a meaningful set of techniques. Rolling trend statistics with `stats_agg()` and `rolling()`, lightweight seasonality checks that group by time period (such as hour of day), variation checks with `stddev()` and `kurtosis()`, gap-filling with `time_bucket_gapfill()` and `locf()`, delta-based anomaly flagging with `LAG()`, downsampling for visualization with `lttb()` and `asap_smooth()`, and OHLC aggregation with `candlestick_agg()` all run in Postgres. `LAG()` is standard SQL, gap-filling comes with TimescaleDB, and the statistical, downsampling, and OHLC functions come with the `timescaledb_toolkit` extension. Formal statistical tests (ADF, KPSS) and full decomposition are better handled in Python or R.

### What is a rolling statistic, and how is it used in time-series analysis?

A rolling statistic is computed over a moving window of recent observations rather than the full dataset, so it tracks how a metric is evolving over time rather than summarizing it globally. A seven-day rolling average can smooth short-term variation. The `ROWS 6 PRECEDING` examples here cover seven days only when there is one bucket for every day. The Checklist section of this article shows how to compute rolling statistics in Postgres using `stats_agg()` and `rolling()` from the `timescaledb_toolkit` extension.

### How do you handle missing or irregular timestamps in a time series?

Bucket the data into regular time intervals with `time_bucket_gapfill()`, and pair it with a gap-filling function in the same `query. locf()` (last observation carried forward) holds the last known value forward into gaps, which is appropriate when a held reading better captures the signal than interpolation. `interpolate()` fits a linear value between surrounding known points when that assumption fits the signal. LOCF cannot fill leading gaps without a previous value or a `prev` lookup. Set `treat_null_as_missing => true` when existing `NULL` values should also be skipped.

### What is a continuous aggregate, and how does it help with time-series analysis?

A continuous aggregate is an incrementally maintained materialized view. A refresh policy updates eligible time buckets on a schedule within a configured window, or you can refresh a window manually. Stored statistical summaries can reduce repeated aggregation work for rolling-statistics queries. Freshness depends on the refresh settings. Optional real-time aggregation adds newer raw rows at query time but does not refresh historical backfills; it is disabled by default in TimescaleDB 2.13 and later.

### How do you define time windows or periods when analyzing time-series data?

There’s no universal rule. The right bucket size (hourly, daily, a fixed N-day window) depends on the question being asked and the natural cadence of the underlying process. The key is to define it explicitly and apply it consistently. Comparing results across different bucket definitions without controlling for that variable is a common source of confusion, particularly when multiple teams analyze the same dataset independently.

### What industries rely most on time-series analysis?

IoT and sensor monitoring, energy and utilities, manufacturing, finance and trading, healthcare, and web and application traffic monitoring are common users. The common thread is continuous data collection over time where patterns in that data have operational or business consequences, whether that’s detecting equipment failure early, managing energy load, flagging cardiac anomalies, or distinguishing organic traffic growth from an attack.