Check the time axis and units of a test CSV first
Check units, missing values, finite numbers, and duplicate or reversed timestamps in synthetic demonstration data, then save the check results and a chart.
Convert synthetic vehicle speed and acceleration data into consistent SI units, then convert the results back to check the arithmetic. The script preserves the original CSV and writes the converted values to a new output folder.
Who this is forThis guide is for people who work with vehicle or test data containing mixed speed and acceleration units and want simple conversion checks before analysis.
This example standardizes speed to metres per second and acceleration to metres per second squared. Speed conversion is based on 1 kilometre = 1000 metres and 1 hour = 3600 seconds, so km/h is divided by 3.6 to obtain m/s. The reverse conversion multiplies m/s by 3.6.
For acceleration, this tutorial uses the conventional standard gravity value g0 = 9.80665 m/s². A value expressed in g is multiplied by 9.80665 to obtain m/s². The reverse conversion divides m/s² by 9.80665.
| Conversion | Rule |
|---|---|
| km/h to m/s | km/h ÷ 3.6 |
| m/s to km/h | m/s × 3.6 |
| g to m/s² | g × 9.80665 |
| m/s² to g | m/s² ÷ 9.80665 |
The following dataset is synthetic and was written specifically for this article. Save it as unit_test_data.csv. The values are chosen so that the speed conversions are easy to verify by hand while the acceleration examples include positive, negative, and fractional g values.
sample_id,speed_kmh,accel_g
T001,0,0
T002,36,1
T003,72,-0.5
T004,90,0.25
T005,108,-1.25
There are 5 records. Negative acceleration values are preserved as negative values; the conversion changes the unit, not the sign. This dataset is only a numerical example and is not intended to represent a specific vehicle manoeuvre or sensor.
The speed conversions are direct. For example, 72 km/h ÷ 3.6 = 20 m/s, and 90 km/h ÷ 3.6 = 25 m/s. Converting back gives 20 × 3.6 = 72 km/h and 25 × 3.6 = 90 km/h.
For acceleration, 1 g becomes 9.80665 m/s². A value of -0.5 g becomes -4.903325 m/s². A value of 0.25 g becomes 2.4516625 m/s². A value of -1.25 g becomes -12.2583125 m/s².
| sample_id | speed_kmh | Expected speed_m_s | accel_g | Expected accel_m_s2 |
|---|---|---|---|---|
| T001 | 0 | 0.000000 | 0 | 0.000000 |
| T002 | 36 | 10.000000 | 1 | 9.806650 |
| T003 | 72 | 20.000000 | -0.5 | -4.903325 |
| T004 | 90 | 25.000000 | 0.25 | 2.451662 |
| T005 | 108 | 30.000000 | -1.25 | -12.258312 |
The displayed acceleration values are rounded to six decimal places. The calculation itself should use the full 9.80665 factor rather than repeatedly using already rounded output text.
Save the following script as unit_conversion_check.py. It reads the original values, performs both forward conversions, converts the results back to the original units, and checks the round trip with a small numerical tolerance. The original CSV is never rewritten.
import csv
import math
from pathlib import Path
SOURCE = Path("unit_test_data.csv")
OUTPUT_DIR = Path("outputs") / "unit_conversion_result"
OUTPUT = OUTPUT_DIR / "converted_test_data.csv"
STANDARD_GRAVITY = 9.80665
TOLERANCE = 1e-12
def kmh_to_ms(value: float) -> float:
return value / 3.6
def ms_to_kmh(value: float) -> float:
return value * 3.6
def g_to_ms2(value: float) -> float:
return value * STANDARD_GRAVITY
def ms2_to_g(value: float) -> float:
return value / STANDARD_GRAVITY
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
output_rows = []
with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream)
required = {"sample_id", "speed_kmh", "accel_g"}
if reader.fieldnames is None or not required.issubset(reader.fieldnames):
raise ValueError("CSV is missing a required column.")
seen_ids = set()
for row in reader:
sample_id = row["sample_id"].strip()
if not sample_id:
raise ValueError("sample_id must not be blank.")
if sample_id in seen_ids:
raise ValueError(f"Duplicate sample_id: {sample_id}")
seen_ids.add(sample_id)
speed_kmh = float(row["speed_kmh"])
accel_g = float(row["accel_g"])
speed_ms = kmh_to_ms(speed_kmh)
accel_ms2 = g_to_ms2(accel_g)
speed_roundtrip = ms_to_kmh(speed_ms)
accel_roundtrip = ms2_to_g(accel_ms2)
speed_ok = math.isclose(
speed_kmh, speed_roundtrip,
rel_tol=TOLERANCE, abs_tol=TOLERANCE
)
accel_ok = math.isclose(
accel_g, accel_roundtrip,
rel_tol=TOLERANCE, abs_tol=TOLERANCE
)
if not speed_ok or not accel_ok:
raise RuntimeError(f"Round-trip check failed: {sample_id}")
output_rows.append({
"sample_id": sample_id,
"speed_kmh": f"{speed_kmh:g}",
"speed_m_s": f"{speed_ms:.6f}",
"speed_roundtrip_kmh": f"{speed_roundtrip:.6f}",
"accel_g": f"{accel_g:g}",
"accel_m_s2": f"{accel_ms2:.6f}",
"accel_roundtrip_g": f"{accel_roundtrip:.6f}",
"roundtrip_check": "PASS",
})
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
fields = [
"sample_id", "speed_kmh", "speed_m_s", "speed_roundtrip_kmh",
"accel_g", "accel_m_s2", "accel_roundtrip_g", "roundtrip_check"
]
with OUTPUT.open("x", encoding="utf-8", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=fields)
writer.writeheader()
writer.writerows(output_rows)
print(f"Converted records: {len(output_rows)}.")
print(f"Round-trip checks passed: {len(output_rows)}.")
print(f"Standard gravity used: {STANDARD_GRAVITY} m/s^2.")
print(f"Output: {OUTPUT.as_posix()}")
if __name__ == "__main__":
main()
Every row should return to its original speed and acceleration after the reverse conversion. The six-decimal output is shown below. The internal calculations use floating-point values before formatting.
sample_id,speed_kmh,speed_m_s,speed_roundtrip_kmh,accel_g,accel_m_s2,accel_roundtrip_g,roundtrip_check
T001,0,0.000000,0.000000,0,0.000000,0.000000,PASS
T002,36,10.000000,36.000000,1,9.806650,1.000000,PASS
T003,72,20.000000,72.000000,-0.5,-4.903325,-0.500000,PASS
T004,90,25.000000,90.000000,0.25,2.451662,0.250000,PASS
T005,108,30.000000,108.000000,-1.25,-12.258312,-1.250000,PASS
The expected console text below was derived by hand from the synthetic data and script. It is not a captured execution log.
Converted records: 5.
Round-trip checks passed: 5.
Standard gravity used: 9.80665 m/s^2.
Output: outputs/unit_conversion_result/converted_test_data.csv| Problem | What to check |
|---|---|
| Speed differs by a factor of 3.6 | Check whether a value labeled m/s was mistakenly treated as km/h or the reverse. |
| Acceleration differs by about a factor of 9.8 | Check whether g and m/s² were confused. |
| Negative acceleration becomes positive | Do not apply absolute value unless the analysis specifically requires magnitude. |
| Unexpected rounding differences | Keep full precision for calculations and round only when displaying or exporting values. |
| Round trip passes but source units were mislabeled | A mathematically reversible conversion cannot detect an incorrect unit label in the original data. |
A successful unit conversion only shows that the arithmetic and declared units are internally consistent. It does not prove that the original sensor channel was calibrated correctly, that the unit label was correct, or that the recorded value represents the physical quantity you think it does.
The g conversion here uses conventional standard gravity, 9.80665 m/s². Local gravitational acceleration can differ from that conventional reference. In many engineering datasets, g is used as a normalized acceleration unit, so the declared convention should be documented.
Real vehicle test datasets may also mix wheel speed, vehicle speed, rotational speed, force, pressure, and temperature units. Build a data dictionary and convert each channel according to its physical dimension. Never infer a unit solely from the magnitude of the numbers.
ChatGPT Python tool · Python 3.13.5 · standard library: csv, math, pathlib
The explanations and examples were written for this site. See the official sources below for the related behavior and concepts.