nutramilo-nis 1.1.5__tar.gz
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.
- nutramilo_nis-1.1.5/LICENSE +47 -0
- nutramilo_nis-1.1.5/NOTICE +27 -0
- nutramilo_nis-1.1.5/PKG-INFO +275 -0
- nutramilo_nis-1.1.5/README.md +237 -0
- nutramilo_nis-1.1.5/nutramilo_nis/__init__.py +44 -0
- nutramilo_nis-1.1.5/nutramilo_nis/classify.py +194 -0
- nutramilo_nis-1.1.5/nutramilo_nis/nis.py +387 -0
- nutramilo_nis-1.1.5/nutramilo_nis.egg-info/PKG-INFO +275 -0
- nutramilo_nis-1.1.5/nutramilo_nis.egg-info/SOURCES.txt +15 -0
- nutramilo_nis-1.1.5/nutramilo_nis.egg-info/dependency_links.txt +1 -0
- nutramilo_nis-1.1.5/nutramilo_nis.egg-info/requires.txt +10 -0
- nutramilo_nis-1.1.5/nutramilo_nis.egg-info/top_level.txt +1 -0
- nutramilo_nis-1.1.5/pyproject.toml +52 -0
- nutramilo_nis-1.1.5/setup.cfg +4 -0
- nutramilo_nis-1.1.5/tests/test_nis.py +137 -0
- nutramilo_nis-1.1.5/tests/test_v1_0_1_validation.py +71 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 Nutramilo Ltd. (EIK 207793581, Sofia, Bulgaria)
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
18
|
+
|
|
19
|
+
────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
ATTRIBUTION REQUIREMENT (Apache License §4(c) — required preservation of
|
|
21
|
+
NOTICE file):
|
|
22
|
+
|
|
23
|
+
When using this package in research, clinical software, mobile apps, or any
|
|
24
|
+
derivative work, you MUST cite:
|
|
25
|
+
|
|
26
|
+
Inkov, I. et al. (2026). "Nutramilo Insulin Score (NIS): An Open,
|
|
27
|
+
Macronutrient-Derived Algorithm for Predicting Postprandial
|
|
28
|
+
Insulinaemic Response — Development and Validation Against the Holt
|
|
29
|
+
Food Insulin Index." International Journal of Medical Reviews and
|
|
30
|
+
Case Reports, Vol. X(Y), pp. ZZ-ZZ. DOI: 10.XXXX/ijmrcr.2026.NIS
|
|
31
|
+
|
|
32
|
+
────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
TRADEMARK NOTICE:
|
|
34
|
+
|
|
35
|
+
"NIS™", "Nutramilo Insulin Score™", and "Nutramilo™" are trademarks of
|
|
36
|
+
Nutramilo Ltd. The trademark rights are NOT granted under this Apache 2.0
|
|
37
|
+
license. Use of these marks in commercial products, marketing, or branding
|
|
38
|
+
requires written permission from Nutramilo Ltd. (legal@nutramilo.com).
|
|
39
|
+
|
|
40
|
+
You ARE permitted to use the marks in:
|
|
41
|
+
- Academic citations and references to this algorithm
|
|
42
|
+
- Bug reports and forks (with clear disclaimer)
|
|
43
|
+
- Educational material describing the algorithm
|
|
44
|
+
|
|
45
|
+
You are NOT permitted to use the marks to:
|
|
46
|
+
- Brand a competing or derivative product as "NIS-powered" without licence
|
|
47
|
+
- Imply endorsement by Nutramilo Ltd.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Nutramilo Insulin Score (NIS) — Python Reference Implementation
|
|
2
|
+
Copyright 2026 Nutramilo Ltd. (Sofia, Bulgaria, EIK 207793581)
|
|
3
|
+
|
|
4
|
+
This product includes software developed at Nutramilo Ltd.
|
|
5
|
+
Licensed under the Apache License, Version 2.0.
|
|
6
|
+
|
|
7
|
+
Required citation when used in research or derivative work:
|
|
8
|
+
|
|
9
|
+
Inkov, I. et al. (2026). "Nutramilo Insulin Score (NIS): An Open,
|
|
10
|
+
Macronutrient-Derived Algorithm for Predicting Postprandial
|
|
11
|
+
Insulinaemic Response — Development and Validation Against the
|
|
12
|
+
Holt Food Insulin Index." Int J Med Rev Case Rep.
|
|
13
|
+
DOI: 10.XXXX/ijmrcr.2026.NIS
|
|
14
|
+
|
|
15
|
+
Methodological lineage:
|
|
16
|
+
- Holt SHA, Brand-Miller JC, Petocz P (1997). An insulin index of foods:
|
|
17
|
+
the insulin demand generated by 1000-kJ portions of common foods.
|
|
18
|
+
Am J Clin Nutr 66:1264-76. PMID 9356547.
|
|
19
|
+
- Bell KJ (2014). Algorithms to improve the prediction of postprandial
|
|
20
|
+
insulinaemia in response to common foods (PhD thesis, U. Sydney).
|
|
21
|
+
- Bao J et al. (2009). Prediction of postprandial glycemia and
|
|
22
|
+
insulinemia in lean, young, healthy adults: glycemic load compared
|
|
23
|
+
with carbohydrate content alone. Am J Clin Nutr 90:986-92. PMID 19710196.
|
|
24
|
+
|
|
25
|
+
Trademark: "NIS™", "Nutramilo Insulin Score™" and "Nutramilo™" are
|
|
26
|
+
trademarks of Nutramilo Ltd., applied for at EUIPO. Use of marks restricted
|
|
27
|
+
under separate licence (see LICENSE).
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nutramilo-nis
|
|
3
|
+
Version: 1.1.5
|
|
4
|
+
Summary: Nutramilo Insulin Score (NIS) — open algorithm for predicting postprandial insulin response from macronutrient composition.
|
|
5
|
+
Author-email: Ivan Inkov <inkov@nutramilo.com>, Nutramilo Research <research@nutramilo.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://nutramilo.com/science
|
|
8
|
+
Project-URL: Repository, https://github.com/nutramilo/nutramilo-nis
|
|
9
|
+
Project-URL: Documentation, https://nutramilo.com/science/nis
|
|
10
|
+
Project-URL: Paper, https://doi.org/10.XXXX/ijmrcr.2026.NIS
|
|
11
|
+
Project-URL: Issues, https://github.com/nutramilo/nutramilo-nis/issues
|
|
12
|
+
Keywords: insulin,postprandial,glycemic,nutrition,metabolism,food insulin index,NIS,diabetes,nutrigenomics,biomedical
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
License-File: NOTICE
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
33
|
+
Provides-Extra: validation
|
|
34
|
+
Requires-Dist: numpy>=1.24; extra == "validation"
|
|
35
|
+
Requires-Dist: pandas>=2.0; extra == "validation"
|
|
36
|
+
Requires-Dist: scipy>=1.10; extra == "validation"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# nutramilo-nis
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/nutramilo-nis/)
|
|
42
|
+
[](https://python.org)
|
|
43
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
44
|
+
[](https://doi.org/10.XXXX/ijmrcr.2026.NIS)
|
|
45
|
+
[](https://pypi.org/project/nutramilo-nis/)
|
|
46
|
+
[](#)
|
|
47
|
+
[](https://github.com/astral-sh/ruff)
|
|
48
|
+
|
|
49
|
+
> **Reference implementation of the Nutramilo Insulin Score (NIS™)** —
|
|
50
|
+
> an open, **exploratory** macronutrient-derived insulinogenic surrogate
|
|
51
|
+
> computable from any meal composition. Trained on the Holt–Bell–Bao
|
|
52
|
+
> Food Insulin Index cohort (n = 147) and evaluated on a strictly
|
|
53
|
+
> out-of-sample subset (n = 25).
|
|
54
|
+
>
|
|
55
|
+
> ⚠️ **Positioning.** NIS v1.1.5 is an **exploratory methodology study**,
|
|
56
|
+
> not an externally validated predictive model. A prospective CGM trial
|
|
57
|
+
> is the planned confirmatory step (see manuscript §4.2 + Part II).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## ✨ Why NIS?
|
|
62
|
+
|
|
63
|
+
| Property | Holt Food Insulin Index (FII) | **NIS v1.1.5** |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| In-vivo insulin AUC measurement required | ✅ Yes | ❌ No — **composition only** |
|
|
66
|
+
| Suitable for mobile apps & CGM-less use | ❌ No | ✅ Yes |
|
|
67
|
+
| Frozen, versioned coefficients | ❌ No | ✅ Yes (Apache 2.0, since v1.0) |
|
|
68
|
+
| Mandatory citation in derivatives | ❌ No | ✅ Yes (NOTICE clause) |
|
|
69
|
+
| Open dataset (SHA-256-verified) | ❌ No | ✅ Yes (Zenodo on release) |
|
|
70
|
+
| Out-of-sample evaluation reported | Partial | ✅ Yes (n = 25 hold-out) |
|
|
71
|
+
| Externally validated on fresh CGM cohort | n/a | ❌ **Not yet** — see Part II |
|
|
72
|
+
|
|
73
|
+
**Headline metrics (v1.1.5 on n = 25 strictly out-of-sample subset):**
|
|
74
|
+
|
|
75
|
+
| Pipeline | OOS MAE | 95 % bootstrap CI | Pearson r |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| v1.0.0 baseline | 17.49 | [14.22, 21.12] | 0.778 |
|
|
78
|
+
| **v1.1.5 full** | **10.20** | **[7.06, 13.70]** | **0.826** |
|
|
79
|
+
|
|
80
|
+
Paired Wilcoxon (n = 25 OOS): **W = 55.5, z = −2.879, p = 0.00399**.
|
|
81
|
+
Bootstrap 95 % CIs do not overlap. Bland–Altman LoA on the OOS subset
|
|
82
|
+
is **±37.6 %-points** — wide enough that NIS is **best used for
|
|
83
|
+
relative meal comparison within the same subject**, not for absolute
|
|
84
|
+
insulin AUC prediction.
|
|
85
|
+
|
|
86
|
+
## 📦 Install
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install nutramilo-nis
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Optional extras:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install "nutramilo-nis[validation]" # numpy / pandas / scipy for re-running the regression notebook
|
|
96
|
+
pip install "nutramilo-nis[dev]" # pytest, ruff, coverage
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 🚀 Quick start — 30 seconds
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from nutramilo_nis import compute_nis
|
|
103
|
+
|
|
104
|
+
# A balanced lunch: salmon + quinoa + broccoli
|
|
105
|
+
result = compute_nis(
|
|
106
|
+
carbs_g=35,
|
|
107
|
+
protein_g=30,
|
|
108
|
+
fat_g=15,
|
|
109
|
+
fiber_g=8,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
print(result.nis_percent) # → 65.0
|
|
113
|
+
print(result.tier) # → "Medium"
|
|
114
|
+
print(result.tier_color) # → "#F59E0B"
|
|
115
|
+
print(result.contributions) # → {"carbs": 9.65, "protein": 3.36, ...}
|
|
116
|
+
print(result.citation) # → "Inkov I, et al. (2026). NIS … IJMRCR …"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
JSON-serialisable for APIs:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
import json
|
|
123
|
+
print(json.dumps(result.to_dict(), indent=2))
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 🧪 Worked examples (from the paper)
|
|
127
|
+
|
|
128
|
+
> Computed with default `mixed` source-type and calibration **on**
|
|
129
|
+
> (slope = 0.7275, intercept = 8.111). See `tests/test_nis_v1_1_*.py`
|
|
130
|
+
> for the full pinned numerical regression suite.
|
|
131
|
+
|
|
132
|
+
| Plate | C / P / F / Fb (g) | NIS % | Tier |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| White bread, lean cheese, butter | 50 / 12 / 8 / 1 | **77** | High |
|
|
135
|
+
| Salmon + quinoa + broccoli | 35 / 30 / 15 / 8 | **65** | Medium |
|
|
136
|
+
| Plain Greek yogurt + walnuts | 8 / 18 / 14 / 2 | **40** | Moderate |
|
|
137
|
+
| Black bean & spinach bowl | 30 / 14 / 6 / 13 | **48** | Moderate |
|
|
138
|
+
| Avocado-omelette + side salad | 6 / 18 / 22 / 5 | **27** | Low |
|
|
139
|
+
|
|
140
|
+
## 📐 Algorithm (5-step pipeline, v1.1.5)
|
|
141
|
+
|
|
142
|
+
1. **Per-1000-kJ normalisation** using Atwater factors (17·C + 17·P + 37·F kJ/g).
|
|
143
|
+
2. **Linear regression layer** — independent OLS on the frozen
|
|
144
|
+
`HoltBellBao_v1_frozen_2026.csv` cohort (n = 147 foods).
|
|
145
|
+
Coefficients exposed as `NIS_COEFFICIENTS`:
|
|
146
|
+
`carbs = +1.61 · protein = +0.66 · fat = +1.20 · fiber = −1.14` (per 1000 kJ).
|
|
147
|
+
Protein and fat scaled by **source-type multipliers** (plant/mixed/animal:
|
|
148
|
+
protein 0.55/1.00/1.30, fat 0.45/1.00/1.25).
|
|
149
|
+
3. **Pure-fat extrapolation guard** — when `(net-carb + protein) / 1000 kJ < 5 g`
|
|
150
|
+
the regression output is scaled by `0.15×` (energy-normalised, serving-
|
|
151
|
+
invariant since v1.1.1).
|
|
152
|
+
4. **Cross-cohort linear calibration** — `obs ≈ 0.7275 × pct + 8.111`
|
|
153
|
+
(fit on the n = 63 cross-cohort validation set; opt-out via
|
|
154
|
+
`apply_calibration=False`).
|
|
155
|
+
5. **Optional Holt cross-track + clinical-tier floor**, then
|
|
156
|
+
**`Final NIS = max(regression_cal, holt, floor)`**, clamped [0, 100].
|
|
157
|
+
|
|
158
|
+
> **Note on validation framing.** The n = 147 training cohort and the
|
|
159
|
+
> n = 63 cross-cohort calibration set share Holt 1997 foods (derivational
|
|
160
|
+
> overlap, see manuscript §4.3 L1). All inferential claims in v1.1.5 are
|
|
161
|
+
> therefore restricted to the **n = 25 strictly out-of-sample subset**
|
|
162
|
+
> (Bao 2011, Nilsson 2004, Boirie 1997, Trichopoulou 2003, Sahyoun 2008).
|
|
163
|
+
|
|
164
|
+
## 📘 API reference
|
|
165
|
+
|
|
166
|
+
### `compute_nis(carbs_g, protein_g, fat_g, fiber_g=0.0, *, insulin_impact_tier="low", il_total_g=None) → NisResult`
|
|
167
|
+
|
|
168
|
+
| Argument | Type | Default | Description |
|
|
169
|
+
|---|---|---|---|
|
|
170
|
+
| `carbs_g` | `float` | — | Net carbohydrate grams |
|
|
171
|
+
| `protein_g` | `float` | — | Protein grams |
|
|
172
|
+
| `fat_g` | `float` | — | Total fat grams |
|
|
173
|
+
| `fiber_g` | `float` | `0.0` | Dietary fibre grams (blunts response) |
|
|
174
|
+
| `insulin_impact_tier` | `Literal["low","medium","high"]` | `"low"` | Clinical floor band |
|
|
175
|
+
| `il_total_g` | `Optional[float]` | `None` | Pre-computed Insulin Load (overrides Holt track) |
|
|
176
|
+
|
|
177
|
+
### `NisResult` (frozen `dataclass`)
|
|
178
|
+
|
|
179
|
+
| Field | Type | Description |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `nis_percent` | `float` | Final NIS, 0–100 |
|
|
182
|
+
| `tier` | `str` | `"Low"` / `"Moderate"` / `"Medium"` / `"High"` / `"Very high"` |
|
|
183
|
+
| `tier_color` | `str` | Hex colour for UI (Tailwind-compatible) |
|
|
184
|
+
| `piru` | `float` | Predicted Insulin Response Unit (regression layer raw) |
|
|
185
|
+
| `regression_pct` | `float` | Pure regression output (0–100) |
|
|
186
|
+
| `holt_pct` | `float` | Holt cross-track output (0–100) |
|
|
187
|
+
| `tier_floor_pct` | `float` | Clinical-tier floor applied |
|
|
188
|
+
| `contributions` | `dict[str, float]` | Per-macro absolute contribution to PIRU |
|
|
189
|
+
| `nis_version` | `str` | Frozen version (e.g. `"1.0.0"`) |
|
|
190
|
+
| `coefficients_date` | `str` | Date coefficients were regressed (ISO) |
|
|
191
|
+
| `citation` | `str` | Required academic citation |
|
|
192
|
+
|
|
193
|
+
Use `.to_dict()` → JSON-serialisable.
|
|
194
|
+
|
|
195
|
+
## 🔬 Reproducibility
|
|
196
|
+
|
|
197
|
+
| Artifact | Location |
|
|
198
|
+
|---|---|
|
|
199
|
+
| Frozen training dataset | Zenodo DOI [10.5281/zenodo.XXXXXX](https://zenodo.org/) |
|
|
200
|
+
| Regression notebook | [`notebooks/independent_regression_v2.ipynb`](https://github.com/nutramilo/nutramilo-nis) |
|
|
201
|
+
| SHA-256 of v1.0 dataset | `b41eb157661b2d8ad0…` (paper § 2.2) |
|
|
202
|
+
| Unit tests | `pytest -q` — 13 tests, all should pass |
|
|
203
|
+
| Figures (PNG + TIFF 300dpi) | Paper supplement, Figs 1–6 |
|
|
204
|
+
|
|
205
|
+
## 📖 Paper
|
|
206
|
+
|
|
207
|
+
> **Inkov, I., et al.** (2026). "Nutramilo Insulin Score (NIS): An Open,
|
|
208
|
+
> Macronutrient-Derived Algorithm for Predicting Postprandial Insulinaemic
|
|
209
|
+
> Response — Development and Validation Against the Holt Food Insulin Index."
|
|
210
|
+
> *International Journal of Medical Reviews and Case Reports*, in press.
|
|
211
|
+
> DOI: [10.XXXX/ijmrcr.2026.NIS](https://doi.org/10.XXXX/ijmrcr.2026.NIS)
|
|
212
|
+
|
|
213
|
+
**Pre-print:** [medRxiv 2026.02.XXXXXX](https://medrxiv.org/)
|
|
214
|
+
|
|
215
|
+
If you use NIS in research, **please cite the paper** (Apache 2.0 + NOTICE).
|
|
216
|
+
|
|
217
|
+
## ™ Trademark notice
|
|
218
|
+
|
|
219
|
+
`NIS™`, `Nutramilo Insulin Score™`, and `Nutramilo™` are trademarks of
|
|
220
|
+
**International Sci Ink Press Ltd EOOD** (EIK 205414288, Sofia, Bulgaria),
|
|
221
|
+
filed at the EUIPO.
|
|
222
|
+
|
|
223
|
+
The Apache 2.0 licence covers the **code** — *not* the trademarks. For
|
|
224
|
+
commercial use of the marks in branding/marketing, contact
|
|
225
|
+
[legal@nutramilo.com](mailto:legal@nutramilo.com).
|
|
226
|
+
|
|
227
|
+
## ⚖️ Disclaimer & regulatory framing
|
|
228
|
+
|
|
229
|
+
NIS is intended for **educational and research** purposes.
|
|
230
|
+
It is **not** a medical device, does **not** diagnose any disease, and should
|
|
231
|
+
**not** replace professional clinical judgement.
|
|
232
|
+
|
|
233
|
+
| Framework | Classification |
|
|
234
|
+
|---|---|
|
|
235
|
+
| EU MDR 2017/745 | **Not a medical device** |
|
|
236
|
+
| EU AI Act 2024/1689 | **Limited risk** (Art. 50 — transparency only) |
|
|
237
|
+
|
|
238
|
+
## 🏗️ Roadmap (semver-frozen)
|
|
239
|
+
|
|
240
|
+
- **v1.x** — patch fixes only; coefficients **never** change.
|
|
241
|
+
- **v2.0.x** — prospective CGM trial re-regression (manuscript Part II,
|
|
242
|
+
pre-registered at OSF before data collection).
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
| GDPR | No personal data processed by the library itself |
|
|
246
|
+
|
|
247
|
+
## 🤝 NIS vs DIL — different questions, different units
|
|
248
|
+
|
|
249
|
+
NIS and DIL are **complementary, not interchangeable**:
|
|
250
|
+
|
|
251
|
+
| | **NIS** (per-meal) | **DIL** (per-meal & daily) |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| Question | *"How insulinogenic is THIS meal compared to pure glucose?"* | *"How many grams of carb-equivalent insulin load did I eat today?"* |
|
|
254
|
+
| Output | 0–100 % (intensity score) | grams of carb-equivalent (load) |
|
|
255
|
+
| Use-case | Compare two meals; rank foods on a phone; coach single-meal swaps | Daily budget tracking; weekly trend monitoring; clinical dosing-adjacent reasoning |
|
|
256
|
+
| Formula | 5-step pipeline with source-aware multipliers, pure-fat guard, cross-cohort calibration | `0.69 × protein + max(carbs − fiber, 0)` per food, summed across the day |
|
|
257
|
+
| Validation | n = 25 OOS Pearson r = 0.83, MAE = 10.2 | OLS-stable across Ridge/LASSO; food-level coefficient frozen at 0.69 since 2026-02-10 |
|
|
258
|
+
| In this SDK | `compute_nis(...)` | not bundled — see `nutramilo-health` package or `services/dil_formula.py` in the Nutramilo platform |
|
|
259
|
+
|
|
260
|
+
If you need both layers (per-meal NIS for ranking + daily DIL for budget),
|
|
261
|
+
combine `compute_nis` here with the open `compute_dil` formula:
|
|
262
|
+
`DIL_g = max(0, 0.69·protein + max(0, carbs − fiber))`.
|
|
263
|
+
|
|
264
|
+
## 📜 Licence
|
|
265
|
+
|
|
266
|
+
[Apache 2.0](LICENSE) — see [`NOTICE`](NOTICE) for the mandatory citation clause.
|
|
267
|
+
|
|
268
|
+
## 💬 Contact
|
|
269
|
+
|
|
270
|
+
| For | Email |
|
|
271
|
+
|---|---|
|
|
272
|
+
| Research collaboration | research@nutramilo.com |
|
|
273
|
+
| Trademark / commercial licensing | legal@nutramilo.com |
|
|
274
|
+
| Bug reports & feature requests | [github.com/nutramilo/nutramilo-nis/issues](https://github.com/nutramilo/nutramilo-nis/issues) |
|
|
275
|
+
| General product info | https://nutramilo.com/science |
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# nutramilo-nis
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/nutramilo-nis/)
|
|
4
|
+
[](https://python.org)
|
|
5
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
6
|
+
[](https://doi.org/10.XXXX/ijmrcr.2026.NIS)
|
|
7
|
+
[](https://pypi.org/project/nutramilo-nis/)
|
|
8
|
+
[](#)
|
|
9
|
+
[](https://github.com/astral-sh/ruff)
|
|
10
|
+
|
|
11
|
+
> **Reference implementation of the Nutramilo Insulin Score (NIS™)** —
|
|
12
|
+
> an open, **exploratory** macronutrient-derived insulinogenic surrogate
|
|
13
|
+
> computable from any meal composition. Trained on the Holt–Bell–Bao
|
|
14
|
+
> Food Insulin Index cohort (n = 147) and evaluated on a strictly
|
|
15
|
+
> out-of-sample subset (n = 25).
|
|
16
|
+
>
|
|
17
|
+
> ⚠️ **Positioning.** NIS v1.1.5 is an **exploratory methodology study**,
|
|
18
|
+
> not an externally validated predictive model. A prospective CGM trial
|
|
19
|
+
> is the planned confirmatory step (see manuscript §4.2 + Part II).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## ✨ Why NIS?
|
|
24
|
+
|
|
25
|
+
| Property | Holt Food Insulin Index (FII) | **NIS v1.1.5** |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| In-vivo insulin AUC measurement required | ✅ Yes | ❌ No — **composition only** |
|
|
28
|
+
| Suitable for mobile apps & CGM-less use | ❌ No | ✅ Yes |
|
|
29
|
+
| Frozen, versioned coefficients | ❌ No | ✅ Yes (Apache 2.0, since v1.0) |
|
|
30
|
+
| Mandatory citation in derivatives | ❌ No | ✅ Yes (NOTICE clause) |
|
|
31
|
+
| Open dataset (SHA-256-verified) | ❌ No | ✅ Yes (Zenodo on release) |
|
|
32
|
+
| Out-of-sample evaluation reported | Partial | ✅ Yes (n = 25 hold-out) |
|
|
33
|
+
| Externally validated on fresh CGM cohort | n/a | ❌ **Not yet** — see Part II |
|
|
34
|
+
|
|
35
|
+
**Headline metrics (v1.1.5 on n = 25 strictly out-of-sample subset):**
|
|
36
|
+
|
|
37
|
+
| Pipeline | OOS MAE | 95 % bootstrap CI | Pearson r |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| v1.0.0 baseline | 17.49 | [14.22, 21.12] | 0.778 |
|
|
40
|
+
| **v1.1.5 full** | **10.20** | **[7.06, 13.70]** | **0.826** |
|
|
41
|
+
|
|
42
|
+
Paired Wilcoxon (n = 25 OOS): **W = 55.5, z = −2.879, p = 0.00399**.
|
|
43
|
+
Bootstrap 95 % CIs do not overlap. Bland–Altman LoA on the OOS subset
|
|
44
|
+
is **±37.6 %-points** — wide enough that NIS is **best used for
|
|
45
|
+
relative meal comparison within the same subject**, not for absolute
|
|
46
|
+
insulin AUC prediction.
|
|
47
|
+
|
|
48
|
+
## 📦 Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install nutramilo-nis
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Optional extras:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install "nutramilo-nis[validation]" # numpy / pandas / scipy for re-running the regression notebook
|
|
58
|
+
pip install "nutramilo-nis[dev]" # pytest, ruff, coverage
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 🚀 Quick start — 30 seconds
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from nutramilo_nis import compute_nis
|
|
65
|
+
|
|
66
|
+
# A balanced lunch: salmon + quinoa + broccoli
|
|
67
|
+
result = compute_nis(
|
|
68
|
+
carbs_g=35,
|
|
69
|
+
protein_g=30,
|
|
70
|
+
fat_g=15,
|
|
71
|
+
fiber_g=8,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
print(result.nis_percent) # → 65.0
|
|
75
|
+
print(result.tier) # → "Medium"
|
|
76
|
+
print(result.tier_color) # → "#F59E0B"
|
|
77
|
+
print(result.contributions) # → {"carbs": 9.65, "protein": 3.36, ...}
|
|
78
|
+
print(result.citation) # → "Inkov I, et al. (2026). NIS … IJMRCR …"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
JSON-serialisable for APIs:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import json
|
|
85
|
+
print(json.dumps(result.to_dict(), indent=2))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🧪 Worked examples (from the paper)
|
|
89
|
+
|
|
90
|
+
> Computed with default `mixed` source-type and calibration **on**
|
|
91
|
+
> (slope = 0.7275, intercept = 8.111). See `tests/test_nis_v1_1_*.py`
|
|
92
|
+
> for the full pinned numerical regression suite.
|
|
93
|
+
|
|
94
|
+
| Plate | C / P / F / Fb (g) | NIS % | Tier |
|
|
95
|
+
|---|---|---|---|
|
|
96
|
+
| White bread, lean cheese, butter | 50 / 12 / 8 / 1 | **77** | High |
|
|
97
|
+
| Salmon + quinoa + broccoli | 35 / 30 / 15 / 8 | **65** | Medium |
|
|
98
|
+
| Plain Greek yogurt + walnuts | 8 / 18 / 14 / 2 | **40** | Moderate |
|
|
99
|
+
| Black bean & spinach bowl | 30 / 14 / 6 / 13 | **48** | Moderate |
|
|
100
|
+
| Avocado-omelette + side salad | 6 / 18 / 22 / 5 | **27** | Low |
|
|
101
|
+
|
|
102
|
+
## 📐 Algorithm (5-step pipeline, v1.1.5)
|
|
103
|
+
|
|
104
|
+
1. **Per-1000-kJ normalisation** using Atwater factors (17·C + 17·P + 37·F kJ/g).
|
|
105
|
+
2. **Linear regression layer** — independent OLS on the frozen
|
|
106
|
+
`HoltBellBao_v1_frozen_2026.csv` cohort (n = 147 foods).
|
|
107
|
+
Coefficients exposed as `NIS_COEFFICIENTS`:
|
|
108
|
+
`carbs = +1.61 · protein = +0.66 · fat = +1.20 · fiber = −1.14` (per 1000 kJ).
|
|
109
|
+
Protein and fat scaled by **source-type multipliers** (plant/mixed/animal:
|
|
110
|
+
protein 0.55/1.00/1.30, fat 0.45/1.00/1.25).
|
|
111
|
+
3. **Pure-fat extrapolation guard** — when `(net-carb + protein) / 1000 kJ < 5 g`
|
|
112
|
+
the regression output is scaled by `0.15×` (energy-normalised, serving-
|
|
113
|
+
invariant since v1.1.1).
|
|
114
|
+
4. **Cross-cohort linear calibration** — `obs ≈ 0.7275 × pct + 8.111`
|
|
115
|
+
(fit on the n = 63 cross-cohort validation set; opt-out via
|
|
116
|
+
`apply_calibration=False`).
|
|
117
|
+
5. **Optional Holt cross-track + clinical-tier floor**, then
|
|
118
|
+
**`Final NIS = max(regression_cal, holt, floor)`**, clamped [0, 100].
|
|
119
|
+
|
|
120
|
+
> **Note on validation framing.** The n = 147 training cohort and the
|
|
121
|
+
> n = 63 cross-cohort calibration set share Holt 1997 foods (derivational
|
|
122
|
+
> overlap, see manuscript §4.3 L1). All inferential claims in v1.1.5 are
|
|
123
|
+
> therefore restricted to the **n = 25 strictly out-of-sample subset**
|
|
124
|
+
> (Bao 2011, Nilsson 2004, Boirie 1997, Trichopoulou 2003, Sahyoun 2008).
|
|
125
|
+
|
|
126
|
+
## 📘 API reference
|
|
127
|
+
|
|
128
|
+
### `compute_nis(carbs_g, protein_g, fat_g, fiber_g=0.0, *, insulin_impact_tier="low", il_total_g=None) → NisResult`
|
|
129
|
+
|
|
130
|
+
| Argument | Type | Default | Description |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `carbs_g` | `float` | — | Net carbohydrate grams |
|
|
133
|
+
| `protein_g` | `float` | — | Protein grams |
|
|
134
|
+
| `fat_g` | `float` | — | Total fat grams |
|
|
135
|
+
| `fiber_g` | `float` | `0.0` | Dietary fibre grams (blunts response) |
|
|
136
|
+
| `insulin_impact_tier` | `Literal["low","medium","high"]` | `"low"` | Clinical floor band |
|
|
137
|
+
| `il_total_g` | `Optional[float]` | `None` | Pre-computed Insulin Load (overrides Holt track) |
|
|
138
|
+
|
|
139
|
+
### `NisResult` (frozen `dataclass`)
|
|
140
|
+
|
|
141
|
+
| Field | Type | Description |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `nis_percent` | `float` | Final NIS, 0–100 |
|
|
144
|
+
| `tier` | `str` | `"Low"` / `"Moderate"` / `"Medium"` / `"High"` / `"Very high"` |
|
|
145
|
+
| `tier_color` | `str` | Hex colour for UI (Tailwind-compatible) |
|
|
146
|
+
| `piru` | `float` | Predicted Insulin Response Unit (regression layer raw) |
|
|
147
|
+
| `regression_pct` | `float` | Pure regression output (0–100) |
|
|
148
|
+
| `holt_pct` | `float` | Holt cross-track output (0–100) |
|
|
149
|
+
| `tier_floor_pct` | `float` | Clinical-tier floor applied |
|
|
150
|
+
| `contributions` | `dict[str, float]` | Per-macro absolute contribution to PIRU |
|
|
151
|
+
| `nis_version` | `str` | Frozen version (e.g. `"1.0.0"`) |
|
|
152
|
+
| `coefficients_date` | `str` | Date coefficients were regressed (ISO) |
|
|
153
|
+
| `citation` | `str` | Required academic citation |
|
|
154
|
+
|
|
155
|
+
Use `.to_dict()` → JSON-serialisable.
|
|
156
|
+
|
|
157
|
+
## 🔬 Reproducibility
|
|
158
|
+
|
|
159
|
+
| Artifact | Location |
|
|
160
|
+
|---|---|
|
|
161
|
+
| Frozen training dataset | Zenodo DOI [10.5281/zenodo.XXXXXX](https://zenodo.org/) |
|
|
162
|
+
| Regression notebook | [`notebooks/independent_regression_v2.ipynb`](https://github.com/nutramilo/nutramilo-nis) |
|
|
163
|
+
| SHA-256 of v1.0 dataset | `b41eb157661b2d8ad0…` (paper § 2.2) |
|
|
164
|
+
| Unit tests | `pytest -q` — 13 tests, all should pass |
|
|
165
|
+
| Figures (PNG + TIFF 300dpi) | Paper supplement, Figs 1–6 |
|
|
166
|
+
|
|
167
|
+
## 📖 Paper
|
|
168
|
+
|
|
169
|
+
> **Inkov, I., et al.** (2026). "Nutramilo Insulin Score (NIS): An Open,
|
|
170
|
+
> Macronutrient-Derived Algorithm for Predicting Postprandial Insulinaemic
|
|
171
|
+
> Response — Development and Validation Against the Holt Food Insulin Index."
|
|
172
|
+
> *International Journal of Medical Reviews and Case Reports*, in press.
|
|
173
|
+
> DOI: [10.XXXX/ijmrcr.2026.NIS](https://doi.org/10.XXXX/ijmrcr.2026.NIS)
|
|
174
|
+
|
|
175
|
+
**Pre-print:** [medRxiv 2026.02.XXXXXX](https://medrxiv.org/)
|
|
176
|
+
|
|
177
|
+
If you use NIS in research, **please cite the paper** (Apache 2.0 + NOTICE).
|
|
178
|
+
|
|
179
|
+
## ™ Trademark notice
|
|
180
|
+
|
|
181
|
+
`NIS™`, `Nutramilo Insulin Score™`, and `Nutramilo™` are trademarks of
|
|
182
|
+
**International Sci Ink Press Ltd EOOD** (EIK 205414288, Sofia, Bulgaria),
|
|
183
|
+
filed at the EUIPO.
|
|
184
|
+
|
|
185
|
+
The Apache 2.0 licence covers the **code** — *not* the trademarks. For
|
|
186
|
+
commercial use of the marks in branding/marketing, contact
|
|
187
|
+
[legal@nutramilo.com](mailto:legal@nutramilo.com).
|
|
188
|
+
|
|
189
|
+
## ⚖️ Disclaimer & regulatory framing
|
|
190
|
+
|
|
191
|
+
NIS is intended for **educational and research** purposes.
|
|
192
|
+
It is **not** a medical device, does **not** diagnose any disease, and should
|
|
193
|
+
**not** replace professional clinical judgement.
|
|
194
|
+
|
|
195
|
+
| Framework | Classification |
|
|
196
|
+
|---|---|
|
|
197
|
+
| EU MDR 2017/745 | **Not a medical device** |
|
|
198
|
+
| EU AI Act 2024/1689 | **Limited risk** (Art. 50 — transparency only) |
|
|
199
|
+
|
|
200
|
+
## 🏗️ Roadmap (semver-frozen)
|
|
201
|
+
|
|
202
|
+
- **v1.x** — patch fixes only; coefficients **never** change.
|
|
203
|
+
- **v2.0.x** — prospective CGM trial re-regression (manuscript Part II,
|
|
204
|
+
pre-registered at OSF before data collection).
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
| GDPR | No personal data processed by the library itself |
|
|
208
|
+
|
|
209
|
+
## 🤝 NIS vs DIL — different questions, different units
|
|
210
|
+
|
|
211
|
+
NIS and DIL are **complementary, not interchangeable**:
|
|
212
|
+
|
|
213
|
+
| | **NIS** (per-meal) | **DIL** (per-meal & daily) |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| Question | *"How insulinogenic is THIS meal compared to pure glucose?"* | *"How many grams of carb-equivalent insulin load did I eat today?"* |
|
|
216
|
+
| Output | 0–100 % (intensity score) | grams of carb-equivalent (load) |
|
|
217
|
+
| Use-case | Compare two meals; rank foods on a phone; coach single-meal swaps | Daily budget tracking; weekly trend monitoring; clinical dosing-adjacent reasoning |
|
|
218
|
+
| Formula | 5-step pipeline with source-aware multipliers, pure-fat guard, cross-cohort calibration | `0.69 × protein + max(carbs − fiber, 0)` per food, summed across the day |
|
|
219
|
+
| Validation | n = 25 OOS Pearson r = 0.83, MAE = 10.2 | OLS-stable across Ridge/LASSO; food-level coefficient frozen at 0.69 since 2026-02-10 |
|
|
220
|
+
| In this SDK | `compute_nis(...)` | not bundled — see `nutramilo-health` package or `services/dil_formula.py` in the Nutramilo platform |
|
|
221
|
+
|
|
222
|
+
If you need both layers (per-meal NIS for ranking + daily DIL for budget),
|
|
223
|
+
combine `compute_nis` here with the open `compute_dil` formula:
|
|
224
|
+
`DIL_g = max(0, 0.69·protein + max(0, carbs − fiber))`.
|
|
225
|
+
|
|
226
|
+
## 📜 Licence
|
|
227
|
+
|
|
228
|
+
[Apache 2.0](LICENSE) — see [`NOTICE`](NOTICE) for the mandatory citation clause.
|
|
229
|
+
|
|
230
|
+
## 💬 Contact
|
|
231
|
+
|
|
232
|
+
| For | Email |
|
|
233
|
+
|---|---|
|
|
234
|
+
| Research collaboration | research@nutramilo.com |
|
|
235
|
+
| Trademark / commercial licensing | legal@nutramilo.com |
|
|
236
|
+
| Bug reports & feature requests | [github.com/nutramilo/nutramilo-nis/issues](https://github.com/nutramilo/nutramilo-nis/issues) |
|
|
237
|
+
| General product info | https://nutramilo.com/science |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Nutramilo Insulin Score (NIS) — open, macronutrient-derived postprandial insulin response algorithm.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
>>> from nutramilo_nis import compute_nis
|
|
5
|
+
>>> result = compute_nis(carbs_g=50, protein_g=20, fat_g=10, fiber_g=5)
|
|
6
|
+
>>> result["nis_percent"]
|
|
7
|
+
37.4
|
|
8
|
+
|
|
9
|
+
Citation (required by NOTICE file):
|
|
10
|
+
Inkov, I. et al. (2026). "Nutramilo Insulin Score (NIS): An Open,
|
|
11
|
+
Macronutrient-Derived Algorithm for Predicting Postprandial
|
|
12
|
+
Insulinaemic Response..." Int J Med Rev Case Rep. DOI: 10.XXXX/ijmrcr.2026.NIS
|
|
13
|
+
|
|
14
|
+
Trademarks: NIS™, Nutramilo Insulin Score™, Nutramilo™ — Nutramilo Ltd., EUIPO.
|
|
15
|
+
"""
|
|
16
|
+
from .nis import (
|
|
17
|
+
NIS_VERSION,
|
|
18
|
+
NIS_COEFFICIENTS,
|
|
19
|
+
NIS_COEFFICIENTS_DATE,
|
|
20
|
+
NIS_CALIBRATION,
|
|
21
|
+
PURE_FAT_DENSITY_THRESHOLD,
|
|
22
|
+
PURE_FAT_ATTENUATION,
|
|
23
|
+
FRUCTOSE_OF_SUGAR,
|
|
24
|
+
FRUCTOSE_MULT,
|
|
25
|
+
compute_nis,
|
|
26
|
+
NisResult,
|
|
27
|
+
)
|
|
28
|
+
from .classify import classify
|
|
29
|
+
|
|
30
|
+
__version__ = "1.1.5"
|
|
31
|
+
__all__ = [
|
|
32
|
+
"compute_nis",
|
|
33
|
+
"classify",
|
|
34
|
+
"NIS_VERSION",
|
|
35
|
+
"NIS_COEFFICIENTS",
|
|
36
|
+
"NIS_COEFFICIENTS_DATE",
|
|
37
|
+
"NIS_CALIBRATION",
|
|
38
|
+
"PURE_FAT_DENSITY_THRESHOLD",
|
|
39
|
+
"PURE_FAT_ATTENUATION",
|
|
40
|
+
"FRUCTOSE_OF_SUGAR",
|
|
41
|
+
"FRUCTOSE_MULT",
|
|
42
|
+
"NisResult",
|
|
43
|
+
"__version__",
|
|
44
|
+
]
|