Sample dataset · CC0
Telemetry, including the readings that never arrived.
Forty devices across five sites reporting through a thirty-day window. Battery level falls the whole way and never recovers, status is derived from the readings rather than drawn beside them, and about one temperature in two hundred is simply absent.
7 columns · 100 or 1,000 rows as a file · up to 1,000,000 rows generated here · CSV, JSON, JSONL, Excel, Parquet
Take a file
A thousand rows is twenty-five readings per device. The interesting sizes for this dataset are the generated ones, because downsampling is the exercise it is built for.
| Format | Small | Standard |
|---|---|---|
| CSV | 100 rows (5.5 KB) | 1,000 rows (54.4 KB) |
| JSON | 100 rows (15.1 KB) | 1,000 rows (150.6 KB) |
| JSONL | 100 rows (14.8 KB) | 1,000 rows (147.7 KB) |
| Excel | 100 rows (17.4 KB) | 1,000 rows (102.2 KB) |
| Parquet | 100 rows (3.2 KB) | 1,000 rows (14.8 KB) |
The sizes that make this dataset interesting
A hundred thousand readings across forty devices over thirty days is roughly one every seventeen minutes per device, which is a realistic cadence for building sensors. This is the dataset where the million-row option earns its place.
The first eight rows
The first eight rows of the 1,000-row file. The forty devices rotate in a fixed order.
| reading_ts | device_id | site | temperature_c | humidity_pct | battery_pct | status |
|---|---|---|---|---|---|---|
| 2026-03-01T00:00:00Z | DEV-001 | Plant A | 17.9 | 41.3 | 98 | ok |
| 2026-03-01T00:43:12Z | DEV-002 | Plant B | 20.6 | 44.0 | 100 | ok |
| 2026-03-01T01:26:24Z | DEV-003 | Warehouse North | 21.6 | 44.2 | 98 | ok |
| 2026-03-01T02:09:36Z | DEV-004 | Warehouse South | 24.4 | 40.4 | 99 | ok |
| 2026-03-01T02:52:48Z | DEV-005 | Lab | 24.2 | 42.0 | 98 | ok |
| 2026-03-01T03:36:00Z | DEV-006 | Plant A | 19.1 | 39.0 | 98 | ok |
| 2026-03-01T04:19:12Z | DEV-007 | Plant B | 18.7 | 43.4 | 99 | ok |
| 2026-03-01T05:02:24Z | DEV-008 | Warehouse North | 20.2 | 41.0 | 98 | ok |
Seven columns, and what each one holds
| Column | Type | What it holds | Example |
|---|---|---|---|
| reading_ts | timestamp | UTC timestamp with a Z suffix, ascending through the file. | 2026-03-01T00:00:00Z |
| device_id | text | DEV-001 to DEV-040, cycling in a fixed order. | DEV-001 |
| site | text | Building the device is installed in. Fixed per device. | Plant A |
| temperature_c | decimal | Celsius, one decimal. Blank on roughly one reading in 200, where the sensor dropped a sample. | 17.9 |
| humidity_pct | decimal | Relative humidity, one decimal. | 41.3 |
| battery_pct | integer | Battery remaining. Falls monotonically across the file for each device. | 98 |
| status | text | ok, warn or fault. Derived from battery and temperature, never contradicting them. | ok |
What it models
Building telemetry: forty battery-powered sensors installed across five sites, each reporting temperature, humidity and its own battery level on a fixed cadence. The timestamp column is a full UTC instant with a Z suffix rather than a date, which is the format these systems actually emit and the one that trips up importers expecting a date.
The readings ascend strictly in time and the devices rotate in a fixed order, so consecutive rows are different devices at nearly the same instant. That is what a real ingestion stream looks like, and it means anything you build has to group by device before it can do anything per device.
The battery column tells a story
Battery starts near 100 and falls across the window for every device, at a rate that depends on the device, so some are nearly flat after thirty days and others still hold most of their charge. Nothing recharges, which is a property you can rely on: for any device, a later reading always has a battery level at or below an earlier one.
That makes it a good file for window functions. Compute the difference between consecutive readings per device and the value should never be positive. Compute a per-device slope and you get a drain rate you can rank the fleet by, which is exactly the query a real operations team runs to decide which sites to send somebody to.
Missing readings and derived status
About one row in two hundred has an empty temperature. The row still exists, with its timestamp, device and battery, because the sensor reported and the temperature sample was lost. That is a different failure from a missing row, and telling the two apart is most of what monitoring a fleet consists of.
status is not drawn independently. It is fault when the battery is under twelve percent, warn when the temperature is above 27.5 degrees or the battery is under twenty-five, and ok otherwise. So the alert column always agrees with the numbers beside it, which means you can test alerting logic against a known-correct baseline instead of guessing.
What people use it for
- Downsampling: turning a reading every few minutes into an hourly or daily mean.
- Window functions per device, on a column with a guaranteed direction.
- Gap and anomaly detection, with a small, known rate of missing values.
- Testing timestamp parsing on a full UTC instant with a Z suffix.
- Alerting logic, checked against a status column that is derived rather than drawn.
Open it somewhere useful
Downsampling and gap detection are the two things this file is for, and both want the full workspace rather than a single-purpose converter.
License, and the people in it
This dataset is dedicated to the public domain under CC0 1.0. Put it in a course, a paid product, a test suite, a bug report, a screenshot, a conference talk or a book. There is nothing to ask for, nothing to sign and no attribution required. A link back is welcome and is not a condition.
No people, no locations that exist and no real equipment. Device identifiers are sequential strings, sites are generic building names, and every reading comes from a deterministic function rather than from a real installation.
Questions people ask about this file
Does the battery level ever go up?
Never. For each device the level is a decreasing function of how far through the window the reading is, so any later reading for a device has a battery at or below the earlier one. That guarantee is what makes the file usable for teaching window functions: the difference between consecutive readings per device should never be positive, and if your query says otherwise the query is wrong.
Why are some temperature values empty?
Because sensors drop samples, and about one row in two hundred here does. The row still exists with its timestamp, device and battery, which is the important distinction: a lost reading is not a lost row. Handling the two differently is most of the work in fleet monitoring, and a file with no missing values at all cannot teach it.
Is the status column trustworthy?
It is derived from the numbers on the same row, so yes. Under twelve percent battery is fault, above 27.5 degrees or under twenty-five percent battery is warn, and everything else is ok. That makes it a reference answer: implement the same rules in your own tool and the two should agree on every row, and any disagreement is a bug in the implementation.
What sampling rate should I generate?
A hundred thousand rows over forty devices and thirty days works out to a reading roughly every seventeen minutes per device, which is realistic for building sensors. Ten thousand rows is about one reading every three hours, which is too sparse to demonstrate downsampling. This is the dataset where the larger generated sizes are the point rather than a stress test.
More sample data
All twenty datasets · Messy files and every other format · Build your own schema