growthcharts 0.1.0__py3-none-any.whl
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.
- growthcharts/__init__.py +7 -0
- growthcharts/child.py +269 -0
- growthcharts/data/bmiage.csv +439 -0
- growthcharts/data/hcageinf.csv +77 -0
- growthcharts/data/lenage.csv +437 -0
- growthcharts/data/lenageinf.csv +75 -0
- growthcharts/data/wtage.csv +437 -0
- growthcharts/data/wtageinf.csv +77 -0
- growthcharts/data/wtleninf.csv +121 -0
- growthcharts/infant.py +388 -0
- growthcharts/patient.py +83 -0
- growthcharts-0.1.0.dist-info/METADATA +119 -0
- growthcharts-0.1.0.dist-info/RECORD +16 -0
- growthcharts-0.1.0.dist-info/WHEEL +5 -0
- growthcharts-0.1.0.dist-info/licenses/LICENSE.txt +12 -0
- growthcharts-0.1.0.dist-info/top_level.txt +1 -0
growthcharts/__init__.py
ADDED
growthcharts/child.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import math
|
|
4
|
+
from scipy.stats import norm
|
|
5
|
+
|
|
6
|
+
from .patient import Patient
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DATA_PATH = Path(__file__).parent / "data"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Child(Patient):
|
|
14
|
+
"""
|
|
15
|
+
Represents a child patient (2–20 years) and provides growth chart analysis
|
|
16
|
+
and visualization based on CDC growth standards.
|
|
17
|
+
|
|
18
|
+
This class extends the Patient class and adds functionality for:
|
|
19
|
+
- Weight-for-age
|
|
20
|
+
- Length-for-age
|
|
21
|
+
- BMI-for-age
|
|
22
|
+
|
|
23
|
+
Supports plotting percentile curves and calculating Z-scores and percentiles
|
|
24
|
+
using the LMS method.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Child 2 to 20 database
|
|
28
|
+
df_wtage = pd.read_csv(DATA_PATH /'wtage.csv', dtype={'Sex': str})
|
|
29
|
+
df_lenage = pd.read_csv(DATA_PATH /'lenage.csv', dtype={'Sex': str})
|
|
30
|
+
df_bmiage = pd.read_csv(DATA_PATH /'bmiage.csv', dtype={'Sex': str})
|
|
31
|
+
|
|
32
|
+
def __init__(self, sex: str, age: int, wt: float = None, length: float = None):
|
|
33
|
+
"""
|
|
34
|
+
Initialize a Child object.
|
|
35
|
+
|
|
36
|
+
At least one anthropometric measurement must be provided.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
sex (str): Child sex.
|
|
40
|
+
age (int): Age in months.
|
|
41
|
+
wt (float, optional): Weight in kilograms.
|
|
42
|
+
length (float, optional): Length/height in centimeters.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ValueError: If both wt and length are None.
|
|
46
|
+
"""
|
|
47
|
+
if wt is None and length is None:
|
|
48
|
+
raise ValueError("At least wt or length must be provided")
|
|
49
|
+
|
|
50
|
+
super().__init__(sex, age, wt, length)
|
|
51
|
+
|
|
52
|
+
def plot_wtage(self, title=None):
|
|
53
|
+
"""
|
|
54
|
+
Plot weight-for-age growth chart with patient's measurement.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
title (str, optional): Plot title.
|
|
58
|
+
"""
|
|
59
|
+
df = self.df_wtage
|
|
60
|
+
|
|
61
|
+
self.sex = str(self.sex).lower()
|
|
62
|
+
if self.sex in ['m', '1']:
|
|
63
|
+
self.sex = '1'
|
|
64
|
+
elif self.sex in ['f', '2']:
|
|
65
|
+
self.sex = '2'
|
|
66
|
+
else:
|
|
67
|
+
raise TypeError('define sex as m or f')
|
|
68
|
+
|
|
69
|
+
data = df[df['Sex'] == self.sex]
|
|
70
|
+
|
|
71
|
+
plt.figure(figsize=(12, 7))
|
|
72
|
+
for i, col in enumerate(self.agecolumns[1:]):
|
|
73
|
+
if i in self.highlight_indices:
|
|
74
|
+
plt.plot(data['Agemos'], data[col],
|
|
75
|
+
color='gray', linewidth=2, label=f'{title} {col}')
|
|
76
|
+
else:
|
|
77
|
+
plt.plot(data['Agemos'], data[col],
|
|
78
|
+
color='lightgray', linewidth=1)
|
|
79
|
+
|
|
80
|
+
if self.wt is not None:
|
|
81
|
+
plt.scatter(self.age, self.wt, color='red', s=100, zorder=5)
|
|
82
|
+
|
|
83
|
+
plt.title(title)
|
|
84
|
+
plt.xlabel("age(month)")
|
|
85
|
+
plt.ylabel("Weight(Kg)")
|
|
86
|
+
plt.grid(True)
|
|
87
|
+
plt.legend()
|
|
88
|
+
plt.show()
|
|
89
|
+
|
|
90
|
+
def plot_lenage(self, title=None):
|
|
91
|
+
"""
|
|
92
|
+
Plot length-for-age growth chart with patient's measurement.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
title (str, optional): Plot title.
|
|
96
|
+
"""
|
|
97
|
+
df = self.df_lenage
|
|
98
|
+
|
|
99
|
+
self.sex = str(self.sex).lower()
|
|
100
|
+
if self.sex in ['m', '1']:
|
|
101
|
+
self.sex = '1'
|
|
102
|
+
elif self.sex in ['f', '2']:
|
|
103
|
+
self.sex = '2'
|
|
104
|
+
else:
|
|
105
|
+
raise TypeError('define sex as m or f')
|
|
106
|
+
|
|
107
|
+
data = df[df['Sex'] == self.sex]
|
|
108
|
+
|
|
109
|
+
plt.figure(figsize=(14, 10))
|
|
110
|
+
for i, col in enumerate(self.agecolumns[1:]):
|
|
111
|
+
if i in self.highlight_indices:
|
|
112
|
+
plt.plot(data['Agemos'], data[col],
|
|
113
|
+
color='gray', linewidth=2, label=f'{title} {col}')
|
|
114
|
+
else:
|
|
115
|
+
plt.plot(data['Agemos'], data[col],
|
|
116
|
+
color='lightgray', linewidth=1)
|
|
117
|
+
|
|
118
|
+
if self.length is not None:
|
|
119
|
+
plt.scatter(self.age, self.length, color='red', s=100, zorder=5)
|
|
120
|
+
|
|
121
|
+
plt.title(title)
|
|
122
|
+
plt.xlabel("age(month)")
|
|
123
|
+
plt.ylabel("Length(cm)")
|
|
124
|
+
plt.grid(True)
|
|
125
|
+
plt.legend()
|
|
126
|
+
plt.show()
|
|
127
|
+
|
|
128
|
+
def plot_bmiage(self, title=None):
|
|
129
|
+
"""
|
|
130
|
+
Plot BMI-for-age growth chart with patient's measurement.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
title (str, optional): Plot title.
|
|
134
|
+
"""
|
|
135
|
+
df = self.df_bmiage
|
|
136
|
+
|
|
137
|
+
self.sex = str(self.sex).lower()
|
|
138
|
+
if self.sex in ['m', '1']:
|
|
139
|
+
self.sex = '1'
|
|
140
|
+
elif self.sex in ['f', '2']:
|
|
141
|
+
self.sex = '2'
|
|
142
|
+
else:
|
|
143
|
+
raise TypeError('define sex as m or f')
|
|
144
|
+
|
|
145
|
+
data = df[df['Sex'] == self.sex]
|
|
146
|
+
|
|
147
|
+
plt.figure(figsize=(12, 7))
|
|
148
|
+
for i, col in enumerate(self.agecolumns[1:]):
|
|
149
|
+
if i in self.highlight_indices:
|
|
150
|
+
plt.plot(data['Agemos'], data[col],
|
|
151
|
+
color='gray', linewidth=2, label=f'{title} {col}')
|
|
152
|
+
else:
|
|
153
|
+
plt.plot(data['Agemos'], data[col],
|
|
154
|
+
color='lightgray', linewidth=1)
|
|
155
|
+
|
|
156
|
+
if self.wt is not None and self.length is not None and self.length > 0:
|
|
157
|
+
plt.scatter(self.age, self.bmi, color='red', s=100, zorder=5)
|
|
158
|
+
|
|
159
|
+
plt.title(title)
|
|
160
|
+
plt.xlabel("age(month)")
|
|
161
|
+
plt.ylabel("BMI")
|
|
162
|
+
plt.grid(True)
|
|
163
|
+
plt.legend()
|
|
164
|
+
plt.show()
|
|
165
|
+
|
|
166
|
+
def analyze_wtage(self):
|
|
167
|
+
"""
|
|
168
|
+
Analyze weight-for-age using LMS method.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
dict: Z-score and percentile.
|
|
172
|
+
"""
|
|
173
|
+
df = self.df_wtage
|
|
174
|
+
self.sex = str(self.sex).lower()
|
|
175
|
+
if self.sex in ['m', '1']:
|
|
176
|
+
self.sex = '1'
|
|
177
|
+
elif self.sex in ['f', '2']:
|
|
178
|
+
self.sex = '2'
|
|
179
|
+
else:
|
|
180
|
+
raise TypeError('define sex as m or f')
|
|
181
|
+
|
|
182
|
+
df2 = df[df['Sex'] == self.sex]
|
|
183
|
+
row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]
|
|
184
|
+
|
|
185
|
+
L = float(row['L'].values[0])
|
|
186
|
+
M = float(row['M'].values[0])
|
|
187
|
+
S = float(row['S'].values[0])
|
|
188
|
+
|
|
189
|
+
if L != 0:
|
|
190
|
+
z = ((self.wt / M) ** L - 1) / (L * S)
|
|
191
|
+
else:
|
|
192
|
+
z = math.log(self.wt / M) / S
|
|
193
|
+
|
|
194
|
+
percentile = norm.cdf(z) * 100
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
"Z": round(z, 2),
|
|
198
|
+
"Percentile": round(percentile, 2)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
def analyze_lenage(self):
|
|
202
|
+
"""
|
|
203
|
+
Analyze length-for-age using LMS method.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
dict: Z-score and percentile.
|
|
207
|
+
"""
|
|
208
|
+
df = self.df_lenage
|
|
209
|
+
self.sex = str(self.sex).lower()
|
|
210
|
+
if self.sex in ['m', '1']:
|
|
211
|
+
self.sex = '1'
|
|
212
|
+
elif self.sex in ['f', '2']:
|
|
213
|
+
self.sex = '2'
|
|
214
|
+
else:
|
|
215
|
+
raise TypeError('define sex as m or f')
|
|
216
|
+
|
|
217
|
+
df2 = df[df['Sex'] == self.sex]
|
|
218
|
+
row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]
|
|
219
|
+
|
|
220
|
+
L = float(row['L'].values[0])
|
|
221
|
+
M = float(row['M'].values[0])
|
|
222
|
+
S = float(row['S'].values[0])
|
|
223
|
+
|
|
224
|
+
if L != 0:
|
|
225
|
+
z = ((self.length / M) ** L - 1) / (L * S)
|
|
226
|
+
else:
|
|
227
|
+
z = math.log(self.length / M) / S
|
|
228
|
+
|
|
229
|
+
percentile = norm.cdf(z) * 100
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
"Z": round(z, 2),
|
|
233
|
+
"Percentile": round(percentile, 2)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
def analyze_bmiage(self):
|
|
237
|
+
"""
|
|
238
|
+
Analyze BMI-for-age using LMS method.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
dict: Z-score and percentile.
|
|
242
|
+
"""
|
|
243
|
+
df = self.df_bmiage
|
|
244
|
+
self.sex = str(self.sex).lower()
|
|
245
|
+
if self.sex in ['m', '1']:
|
|
246
|
+
self.sex = '1'
|
|
247
|
+
elif self.sex in ['f', '2']:
|
|
248
|
+
self.sex = '2'
|
|
249
|
+
else:
|
|
250
|
+
raise TypeError('define sex as m or f')
|
|
251
|
+
|
|
252
|
+
df2 = df[df['Sex'] == self.sex]
|
|
253
|
+
row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]
|
|
254
|
+
|
|
255
|
+
L = float(row['L'].values[0])
|
|
256
|
+
M = float(row['M'].values[0])
|
|
257
|
+
S = float(row['S'].values[0])
|
|
258
|
+
|
|
259
|
+
if L != 0:
|
|
260
|
+
z = ((self.bmi / M) ** L - 1) / (L * S)
|
|
261
|
+
else:
|
|
262
|
+
z = math.log(self.bmi / M) / S
|
|
263
|
+
|
|
264
|
+
percentile = norm.cdf(z) * 100
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"Z": round(z, 2),
|
|
268
|
+
"Percentile": round(percentile, 2)
|
|
269
|
+
}
|