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.
@@ -0,0 +1,7 @@
1
+ # growthcharts/__init__.py
2
+
3
+ from .patient import Patient
4
+ from .infant import Infant
5
+ from .child import Child
6
+
7
+ __all__ = ["Patient", "Infant", "Child"]
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
+ }