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.
- package/README.md +2 -2
- package/USAGE.md +44 -2
- package/dist/analyze/index.d.ts +15 -1
- package/dist/analyze/index.js +2 -2
- package/dist/index.d.ts +63 -5
- package/dist/index.js +20 -20
- package/dist/interaction/index.d.ts +1 -1
- package/dist/internals.d.ts +7 -7
- package/dist/internals.js +4 -4
- package/dist/{layout-DDdMtPrB.d.ts → layout-C_t250fG.d.ts} +5 -5
- package/dist/{scale-DcFBNLdU.d.ts → scale-DFyECKZq.d.ts} +46 -46
- package/docs/MKT_AGGREGATE.md +66 -68
- package/package.json +65 -65
package/docs/MKT_AGGREGATE.md
CHANGED
|
@@ -1,76 +1,75 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Documentation: Kinetic Temperature Aggregation (`aggregateBySlot`)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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.
|
|
9
|
+
## 1. What is the Mean Kinetic Temperature (MKT)?
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
###
|
|
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$**:
|
|
18
|
-
* **$\Delta H$**:
|
|
19
|
-
* **$R$**:
|
|
20
|
-
* **$T_n$**:
|
|
21
|
-
* **$n$**:
|
|
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.
|
|
25
|
+
## 2. Data Structures & Interfaces
|
|
26
26
|
|
|
27
|
-
###
|
|
28
|
-
|
|
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
|
|
33
|
-
value: number | 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
|
-
###
|
|
38
|
-
|
|
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; //
|
|
43
|
-
limitHigh: number; //
|
|
44
|
-
activationEnergy?: number; // Optional:
|
|
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
|
-
###
|
|
48
|
+
### Output Data (`StatsAggregatedPoint`)
|
|
49
49
|
|
|
50
|
-
`aggregateBySlot()`
|
|
51
|
-
`StatsAggregatedPoint[]` —
|
|
52
|
-
`StdDevPoint`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
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; //
|
|
61
|
-
min: number | null; //
|
|
62
|
-
max: number | null; //
|
|
63
|
-
avg: number | null; //
|
|
64
|
-
count: number; //
|
|
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
|
|
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; //
|
|
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.
|
|
85
|
+
## 3. Core Algorithm Features
|
|
87
86
|
|
|
88
|
-
1. **
|
|
89
|
-
* `null
|
|
90
|
-
*
|
|
91
|
-
2. **
|
|
92
|
-
*
|
|
93
|
-
3. **MKT
|
|
94
|
-
*
|
|
95
|
-
4. **
|
|
96
|
-
*
|
|
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 &
|
|
99
|
+
## 4. Integration & Usage in the System
|
|
101
100
|
|
|
102
|
-
|
|
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
|
-
###
|
|
103
|
+
### Typical Product Limits at a Glance
|
|
105
104
|
|
|
106
105
|
|
|
107
|
-
|
|
|
106
|
+
| Product category | Lower limit (`limitLow`) | Upper limit (`limitHigh`) | Activation energy ($\Delta H$) |
|
|
108
107
|
| :--- | :--- | :--- | :--- |
|
|
109
|
-
| **Pharma (
|
|
110
|
-
| **Pharma (
|
|
111
|
-
| **
|
|
112
|
-
| **
|
|
113
|
-
| **
|
|
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
|
|
114
|
+
### Code Examples
|
|
116
115
|
|
|
117
|
-
####
|
|
118
|
-
|
|
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
|
-
####
|
|
126
|
-
|
|
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
|
|
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
|
|
138
|
+
12 // only compute MKT when at least 12 valid values exist per day
|
|
140
139
|
);
|
|
141
140
|
```
|
|
142
141
|
|
|
143
|
-
####
|
|
144
|
-
|
|
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
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
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
|
}
|