ml-time-graph 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,76 +1,75 @@
1
- # Dokumentation: Kinetische Temperaturaggregation (`aggregateBySlot`)
1
+ # Documentation: Kinetic Temperature Aggregation (`aggregateBySlot`)
2
2
 
3
- Dieses Modul bietet eine mathematische Kernfunktion zur zeitlichen Aggregation von Temperatur-Loggerdaten. Neben klassischen statistischen Werten (`min`, `max`, `avg`, `stdDev`) berechnet die Funktion die **Mean Kinetic Temperature (MKT)** sowie die präzise Dauer von Grenzwertverletzungen in Minuten.
3
+ This module provides a core mathematical function for the temporal aggregation of temperature logger data. In addition to classic statistical values (`min`, `max`, `avg`, `stdDev`), the function computes the **Mean Kinetic Temperature (MKT)** as well as the precise duration of limit violations in minutes.
4
4
 
5
- Die Funktion ist vollständig **entkoppelt**, besitzt kein inhärentes Wissen über spezifische Produkte (Blut, Pharma, Lebensmittel) und wird rein über mathematische Parameter gesteuert.
5
+ The function is fully **decoupled**, has no inherent knowledge about specific products (blood, pharma, food) and is driven purely by mathematical parameters.
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Was ist die Mean Kinetic Temperature (MKT)?
9
+ ## 1. What is the Mean Kinetic Temperature (MKT)?
10
10
 
11
- Die MKT ist eine Methode zur Bewertung von Temperaturschwankungen bei der Lagerung und dem Transport von temperaturempfindlichen Gütern. Im Gegensatz zum arithmetischen Mittelwert (`avg`) gewichtet die MKT **höhere Temperaturen exponentiell stärker**. Dies bildet das reale biologische und chemische Degradationsverhalten (Verderb) von Produkten ab, da chemische Reaktionen bei Wärme beschleunigt ablaufen (Arrhenius-Gleichung).
11
+ MKT is a method for assessing temperature excursions during storage and transport of temperature-sensitive goods. In contrast to the arithmetic mean (`avg`), MKT **weights higher temperatures exponentially stronger**. This mirrors the real biological and chemical degradation behavior (spoiling) of products, since chemical reactions run faster at elevated temperatures (Arrhenius equation).
12
12
 
13
- ### Mathematische Formel (nach USP <1079.2>)
13
+ ### Mathematical Formula (per USP <1079.2>)
14
14
 
15
15
  $$T_{K} = \frac{\frac{\Delta H}{R}}{-\ln\left(\frac{e^{-\frac{\Delta H}{R \cdot T_1}} + e^{-\frac{\Delta H}{R \cdot T_2}} + \dots + e^{-\frac{\Delta H}{R \cdot T_n}}}{n}\right)}$$
16
16
 
17
- * **$T_K$**: Mittlere kinetische Temperatur in Kelvin.
18
- * **$\Delta H$**: Aktivierungsenergie. Der globale Standard für Pharma- und GDP-Audits beträgt **$83.144\text{ kJ/mol}$** ($83144\text{ J/mol}$).
19
- * **$R$**: Universelle Gaskonstante ($8.31446\text{ J/(mol}\cdot\text{K)}$).
20
- * **$T_n$**: Gemessene Temperatur zum Zeitpunkt $n$ in Kelvin.
21
- * **$n$**: Anzahl der gültigen Messwerte im Intervall.
17
+ * **$T_K$**: Mean kinetic temperature in Kelvin.
18
+ * **$\Delta H$**: Activation energy. The global standard for pharma and GDP audits is **$83.144\text{ kJ/mol}$** ($83144\text{ J/mol}$).
19
+ * **$R$**: Universal gas constant ($8.31446\text{ J/(mol}\cdot\text{K)}$).
20
+ * **$T_n$**: Measured temperature at time point $n$, in Kelvin.
21
+ * **$n$**: Number of valid measurements in the interval.
22
22
 
23
23
  ---
24
24
 
25
- ## 2. Datenstrukturen & Interfaces
25
+ ## 2. Data Structures & Interfaces
26
26
 
27
- ### Eingangsdaten (`DataPoint`)
28
- Die Rohdaten erlauben explizit `null`-Werte (z. B. bei temporärem Sensorausfall).
27
+ ### Input Data (`DataPoint`)
28
+ Raw data explicitly allows `null` values (e.g. for temporary sensor outages).
29
29
 
30
30
  ```typescript
31
31
  export interface DataPoint {
32
- time: number; // Unix-Zeitstempel in Millisekunden
33
- value: number | null; // Temperaturwert in °C oder null
32
+ time: number; // Unix timestamp in milliseconds
33
+ value: number | null; // Temperature value in °C or null
34
34
  }
35
35
  ```
36
36
 
37
- ### Konfiguration (`AggregationThresholds`)
38
- Ermöglicht die Übergabe dynamischer Grenzwerte zur Bestimmung der Abweichungsminuten.
37
+ ### Configuration (`AggregationThresholds`)
38
+ Allows passing dynamic limits to determine the violation minutes.
39
39
 
40
40
  ```typescript
41
41
  export interface AggregationThresholds {
42
- limitLow: number; // Unterer Grenzwert in °C
43
- limitHigh: number; // Oberer Grenzwert in °C
44
- activationEnergy?: number; // Optional: Aktivierungsenergie in J/mol (Standard: 83144.0)
42
+ limitLow: number; // Lower limit in °C
43
+ limitHigh: number; // Upper limit in °C
44
+ activationEnergy?: number; // Optional: activation energy in J/mol (default: 83144.0)
45
45
  }
46
46
  ```
47
47
 
48
- ### Ausgangsdaten (`StatsAggregatedPoint`)
48
+ ### Output Data (`StatsAggregatedPoint`)
49
49
 
50
- `aggregateBySlot()` mit `thresholds`-Parameter liefert
51
- `StatsAggregatedPoint[]` — eine Intersection aus `MktPoint`,
52
- `StdDevPoint` und `LimitStatsPoint`. Die Basis-`AggregatedPoint` trägt
53
- nur die statistischen Pflichtfelder; alles MKT/σ/Limit-spezifische lebt
54
- auf den spezialisierten Untertypen, sodass User unfallfrei zwischen
55
- "reine min/max/avg"-Aggregation und "voll dekoriertem" Slot
56
- unterscheiden können.
50
+ `aggregateBySlot()` with a `thresholds` parameter returns
51
+ `StatsAggregatedPoint[]` — an intersection of `MktPoint`,
52
+ `StdDevPoint` and `LimitStatsPoint`. The base `AggregatedPoint` carries
53
+ only the required statistical fields; everything MKT/σ/limit-specific lives
54
+ on the specialized subtypes, so users can safely distinguish between a
55
+ "pure min/max/avg" aggregation and a "fully decorated" slot.
57
56
 
58
57
  ```typescript
59
58
  export interface AggregatedPoint {
60
- time: number; // Startzeitpunkt des Slots in ms
61
- min: number | null; // Niedrigste Temperatur im Slot
62
- max: number | null; // Höchste Temperatur im Slot
63
- avg: number | null; // Arithmetischer Mittelwert
64
- count: number; // Anzahl valider Messwerte
59
+ time: number; // Start time of the slot in ms
60
+ min: number | null; // Lowest temperature in the slot
61
+ max: number | null; // Highest temperature in the slot
62
+ avg: number | null; // Arithmetic mean
63
+ count: number; // Number of valid measurements
65
64
  }
66
65
 
67
66
  export interface MktPoint extends AggregatedPoint {
68
67
  mkt: number | null; // Mean Kinetic Temperature in °C
69
- deltaMkt?: number | null; // Delta zur Vor-Slot-MKT (optional)
68
+ deltaMkt?: number | null; // Delta vs. previous slot's MKT (optional)
70
69
  }
71
70
 
72
71
  export interface StdDevPoint extends AggregatedPoint {
73
- stdDev: number | null; // Standardabweichung
72
+ stdDev: number | null; // Standard deviation
74
73
  }
75
74
 
76
75
  export interface LimitStatsPoint extends AggregatedPoint {
@@ -83,72 +82,71 @@ export type StatsAggregatedPoint = MktPoint & StdDevPoint & LimitStatsPoint;
83
82
 
84
83
  ---
85
84
 
86
- ## 3. Kern-Features des Algorithmus
85
+ ## 3. Core Algorithm Features
87
86
 
88
- 1. **Umgang mit Lücken & Ausfällen (`null`-Werte):**
89
- * `null`-Werte in den Rohdaten werden vor der mathematischen Berechnung strikt herausgefiltert. Sie verfälschen weder Minimum, Maximum noch die MKT-Exponentialsumme.
90
- * Ein Slot, der *ausschließlich* aus `null`-Daten besteht, bleibt im Ausgabe-Array als Platzhalter erhalten (`count: 0`, alle Werte sind `null`), um Zeitleisten-Lücken in Diagrammen zu verhindern.
91
- 2. **Präzise Zeitsynchronität (Feste Slots):**
92
- * Die Funktion verwendet ein festes Intervallgitter. Unabhängig davon, wann Datenpunkte eintreffen, springt das Raster präzise um die exakte Intervall-Länge (z. B. stündlich).
93
- 3. **MKT-Stabilitätsschutz (`minCountForMkt`):**
94
- * Die MKT benötigt eine statistisch relevante Anzahl an Messwerten. Über einen Parameter kann definiert werden, ab wie vielen validen Punkten im Slot die MKT berechnet wird. Wird die Schwelle unterschritten, wird `mkt` sicher auf `null` gesetzt, anstatt mathematische Artefakte zu erzeugen.
95
- 4. **Präzise Abweichungsdauer (Dauer in Minuten):**
96
- * Anstatt nur Datenpunkte zu zählen, berechnet die Funktion die reale zeitliche Differenz zwischen aufeinanderfolgenden Loggereinträgen. Dadurch werden unregelmäßige Datenaufzeichnungen (z. B. Event-basierte Logger) exakt in Minuten erfasst.
87
+ 1. **Handling of gaps & outages (`null` values):**
88
+ * `null` values in the raw data are strictly filtered out before any mathematical computation. They do not distort the minimum, maximum, or the MKT exponential sum.
89
+ * A slot consisting *exclusively* of `null` data is kept as a placeholder in the output array (`count: 0`, all values `null`) to prevent timeline holes in charts.
90
+ 2. **Precise time synchronization (fixed slots):**
91
+ * The function uses a fixed interval grid. Regardless of when data points arrive, the grid steps by exactly the interval length (e.g. hourly).
92
+ 3. **MKT stability guard (`minCountForMkt`):**
93
+ * MKT requires a statistically relevant number of measurements. A parameter defines from how many valid points per slot the MKT is computed. Below that threshold, `mkt` is safely set to `null` instead of producing mathematical artifacts.
94
+ 4. **Precise deviation duration (duration in minutes):**
95
+ * Instead of merely counting data points, the function computes the actual time difference between consecutive logger entries. Irregular recordings (e.g. event-based loggers) are captured exactly in minutes this way.
97
96
 
98
97
  ---
99
98
 
100
- ## 4. Integration & Nutzung im System
99
+ ## 4. Integration & Usage in the System
101
100
 
102
- Da die Kernfunktion mathematisch dumm gehalten ist, werden die branchenspezifischen Vorgaben (Pharma, Blut, Lebensmittel) **außerhalb** über die Konfigurationsschicht verwaltet.
101
+ Since the core function is kept mathematically dumb, the industry-specific requirements (pharma, blood, food) are managed **outside**, via the configuration layer.
103
102
 
104
- ### Typische Produkt-Grenzwerte im Überblick
103
+ ### Typical Product Limits at a Glance
105
104
 
106
105
 
107
- | Produktkategorie | Unteres Limit (`limitLow`) | Oberes Limit (`limitHigh`) | Aktivierungsenergie ($\Delta H$) |
106
+ | Product category | Lower limit (`limitLow`) | Upper limit (`limitHigh`) | Activation energy ($\Delta H$) |
108
107
  | :--- | :--- | :--- | :--- |
109
- | **Pharma (Kühlkette)** | $+2.0^\circ\text{C}$ | $+8.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP Standard) |
110
- | **Pharma (Raumtemperatur)** | $+15.0^\circ\text{C}$ | $+25.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP Standard) |
111
- | **Blutkonserven** | $+2.0^\circ\text{C}$ | $+6.0^\circ\text{C}$ | $83144\text{ J/mol}$ (Standard-Anwendung) |
112
- | **Lebensmittel (Frische)** | $0.0^\circ\text{C}$ | $+4.0^\circ\text{C}$ | *MKT irrelevant / entfällt meist* |
113
- | **Tiefkühlkost** | $-40.0^\circ\text{C}$ | $-18.0^\circ\text{C}$ | *MKT irrelevant / entfällt meist* |
108
+ | **Pharma (cold chain)** | $+2.0^\circ\text{C}$ | $+8.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP standard) |
109
+ | **Pharma (room temperature)** | $+15.0^\circ\text{C}$ | $+25.0^\circ\text{C}$ | $83144\text{ J/mol}$ (USP standard) |
110
+ | **Blood products** | $+2.0^\circ\text{C}$ | $+6.0^\circ\text{C}$ | $83144\text{ J/mol}$ (standard application) |
111
+ | **Food (fresh)** | $0.0^\circ\text{C}$ | $+4.0^\circ\text{C}$ | *MKT irrelevant / usually omitted* |
112
+ | **Frozen food** | $-40.0^\circ\text{C}$ | $-18.0^\circ\text{C}$ | *MKT irrelevant / usually omitted* |
114
113
 
115
- ### Code-Beispiele zur Verwendung
114
+ ### Code Examples
116
115
 
117
- #### Beispiel 1: Standard-Aggregation stündlich ohne Limits
118
- Es werden nur die Basis-Statistiken und die MKT berechnet. Die Alarmminuten bleiben `null`.
116
+ #### Example 1: Standard hourly aggregation without limits
117
+ Only the base statistics and the MKT are computed. The violation minutes remain `null`.
119
118
  ```typescript
120
119
  import { aggregateBySlot } from "ml-time-graph/analyze";
121
120
 
122
121
  const hourlyStats = aggregateBySlot(rawLoggerData, "hourly");
123
122
  ```
124
123
 
125
- #### Beispiel 2: Aggregation für Blutkonserven (Tägliches Intervall)
126
- Hier werden die Grenzwerte injiziert. Die Funktion berechnet automatisch, wie viele Minuten die Blutkonserven außerhalb der erlaubten $2^\circ\text{C} - 6^\circ\text{C}$ lagen.
124
+ #### Example 2: Aggregation for blood products (daily interval)
125
+ Here the limits are injected. The function automatically computes how many minutes the blood products were outside the allowed $2^\circ\text{C} - 6^\circ\text{C}$ range.
127
126
  ```typescript
128
127
  const bloodThresholds = {
129
128
  limitLow: 2.0,
130
129
  limitHigh: 6.0
131
- // activationEnergy wird weggelassen -> nutzt automatisch internen 83144 J/mol Fallback
130
+ // activationEnergy is omitted -> automatically uses the internal 83144 J/mol fallback
132
131
  };
133
132
 
134
133
  const dailyBloodReport = aggregateBySlot(
135
- rawLoggerData,
136
- "daily",
134
+ rawLoggerData,
135
+ "daily",
137
136
  undefined, // customInterval
138
137
  bloodThresholds,
139
- 12 // MKT erst berechnen, wenn mindestens 12 gültige Werte im Tag liegen
138
+ 12 // only compute MKT when at least 12 valid values exist per day
140
139
  );
141
140
  ```
142
141
 
143
- #### Beispiel 3: Spezial-Laborwert (Custom Aktivierungsenergie)
144
- Sollte ein Audit für ein bestimmtes biologisches Plasma-Protein eine abweichende Aktivierungsenergie vorschreiben, kann diese direkt mitgegeben werden:
142
+ #### Example 3: Special lab value (custom activation energy)
143
+ If an audit for a specific biological plasma protein prescribes a different activation energy, it can be passed directly:
145
144
  ```typescript
146
145
  const customLabThresholds = {
147
146
  limitLow: 1.0,
148
147
  limitHigh: 4.5,
149
- activationEnergy: 65000.0 // 65 kJ/mol statt 83.144 kJ/mol
148
+ activationEnergy: 65000.0 // 65 kJ/mol instead of 83.144 kJ/mol
150
149
  };
151
150
 
152
151
  const labReport = aggregateBySlot(rawLoggerData, "hourly", undefined, customLabThresholds);
153
152
  ```
154
-
package/package.json CHANGED
@@ -1,71 +1,71 @@
1
1
  {
2
- "name": "ml-time-graph",
3
- "version": "1.1.1",
4
- "description": "TypeScript library for time-series chart rendering — DOM-free SVG output (works in the browser and on the server).",
5
- "type": "module",
6
- "module": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "sideEffects": false,
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js"
13
- },
14
- "./internals": {
15
- "types": "./dist/internals.d.ts",
16
- "import": "./dist/internals.js"
17
- },
18
- "./analyze": {
19
- "types": "./dist/analyze/index.d.ts",
20
- "import": "./dist/analyze/index.js"
21
- },
22
- "./interaction": {
23
- "types": "./dist/interaction/index.d.ts",
24
- "import": "./dist/interaction/index.js"
25
- }
2
+ "name": "ml-time-graph",
3
+ "version": "1.2.0",
4
+ "description": "TypeScript library for time-series chart rendering — DOM-free SVG output (works in the browser and on the server).",
5
+ "type": "module",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
26
13
  },
27
- "files": [
28
- "dist",
29
- "docs",
30
- "README.md",
31
- "README.de.md",
32
- "USAGE.md",
33
- "API_DESIGN.md",
34
- "LICENSE"
35
- ],
36
- "scripts": {
37
- "build": "tsup",
38
- "test": "vitest run",
39
- "typecheck": "tsc --noEmit",
40
- "prepublishOnly": "tsup"
14
+ "./internals": {
15
+ "types": "./dist/internals.d.ts",
16
+ "import": "./dist/internals.js"
41
17
  },
42
- "keywords": [
43
- "chart",
44
- "time-series",
45
- "typescript",
46
- "svg",
47
- "renderer",
48
- "ssr",
49
- "sensor",
50
- "monitoring",
51
- "incident-analysis",
52
- "mkt"
53
- ],
54
- "devDependencies": {
55
- "ml-time-analyze": "workspace:*"
18
+ "./analyze": {
19
+ "types": "./dist/analyze/index.d.ts",
20
+ "import": "./dist/analyze/index.js"
56
21
  },
57
- "author": "Michael Lechner",
58
- "license": "SEE LICENSE IN LICENSE",
59
- "homepage": "https://github.com/mlc0911/mlctimegraph",
60
- "repository": {
61
- "type": "git",
62
- "url": "git+https://gitlab.com/mlc0911/mlctimegraph.git",
63
- "directory": "packages/ml-time-graph"
64
- },
65
- "bugs": {
66
- "url": "https://gitlab.com/mlc0911/mlctimegraph/-/issues"
67
- },
68
- "engines": {
69
- "node": ">=18"
22
+ "./interaction": {
23
+ "types": "./dist/interaction/index.d.ts",
24
+ "import": "./dist/interaction/index.js"
70
25
  }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "docs",
30
+ "README.md",
31
+ "README.de.md",
32
+ "USAGE.md",
33
+ "API_DESIGN.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "test": "vitest run",
39
+ "typecheck": "tsc --noEmit",
40
+ "prepublishOnly": "tsup"
41
+ },
42
+ "keywords": [
43
+ "chart",
44
+ "time-series",
45
+ "typescript",
46
+ "svg",
47
+ "renderer",
48
+ "ssr",
49
+ "sensor",
50
+ "monitoring",
51
+ "incident-analysis",
52
+ "mkt"
53
+ ],
54
+ "devDependencies": {
55
+ "ml-time-analyze": "workspace:*"
56
+ },
57
+ "author": "Michael Lechner",
58
+ "license": "SEE LICENSE IN LICENSE",
59
+ "homepage": "https://github.com/mlc0911/mlctimegraph",
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://gitlab.com/mlc0911/mlctimegraph.git",
63
+ "directory": "packages/ml-time-graph"
64
+ },
65
+ "bugs": {
66
+ "url": "https://gitlab.com/mlc0911/mlctimegraph/-/issues"
67
+ },
68
+ "engines": {
69
+ "node": ">=18"
70
+ }
71
71
  }