@@ -136,11 +136,11 @@ def _calculate_sd_subtypes(gd2d: np.ndarray, dt0: int, subject_id: Any) -> Dict[
136136 # 1. SDw - vertical within days
137137 # Standard deviation within each day, then mean across days
138138 daily_sds = _safe_nanstd (gd2d , axis = 1 , ddof = 1 ) # ddof=1 for sample std
139- result ['SDw' ] = np . nanmean (daily_sds )
139+ result ['SDw' ] = _safe_nanmean (daily_sds )
140140
141141 # 2. SDhhmm - between time points
142142 # Mean at each time point across days, then SD of those means
143- timepoint_means = np . nanmean (gd2d , axis = 0 )
143+ timepoint_means = _safe_nanmean (gd2d , axis = 0 )
144144 result ['SDhhmm' ] = _safe_nanstd (timepoint_means , ddof = 1 )
145145
146146 # 3. SDwsh - within series (1-hour windows)
@@ -150,24 +150,24 @@ def _calculate_sd_subtypes(gd2d: np.ndarray, dt0: int, subject_id: Any) -> Dict[
150150
151151 # Calculate rolling standard deviation
152152 rolling_sds = _rolling_std (gs , window = win )
153- result ['SDwsh' ] = np . nanmean (rolling_sds )
153+ result ['SDwsh' ] = _safe_nanmean (rolling_sds )
154154
155155 # 4. SDdm - horizontal sd (between daily means)
156156 # Standard deviation of daily mean glucose values
157- daily_means = np . nanmean (gd2d , axis = 1 )
157+ daily_means = _safe_nanmean (gd2d , axis = 1 )
158158 result ['SDdm' ] = _safe_nanstd (daily_means , ddof = 1 )
159159
160160 # 5. SDb - between days, within timepoints
161161 # SD across days for each time point, then mean of those SDs
162162 timepoint_sds = _safe_nanstd (gd2d , axis = 0 , ddof = 1 )
163- result ['SDb' ] = np . nanmean (timepoint_sds )
163+ result ['SDb' ] = _safe_nanmean (timepoint_sds )
164164
165165 # 6. SDbdm - between days, within timepoints, corrected for daily means
166166 # Subtract daily mean from each value, then calculate SDb on corrected values
167167 daily_means_matrix = daily_means [:, np .newaxis ] # Convert to column vector
168168 corrected_gd2d = gd2d - daily_means_matrix
169169 corrected_timepoint_sds = _safe_nanstd (corrected_gd2d , axis = 0 , ddof = 1 )
170- result ['SDbdm' ] = np . nanmean (corrected_timepoint_sds )
170+ result ['SDbdm' ] = _safe_nanmean (corrected_timepoint_sds )
171171
172172 return result
173173
@@ -238,6 +238,36 @@ def _safe_nanstd(data: np.ndarray, axis: Optional[int] = None, ddof: int = 1) ->
238238 return np .nanstd (data , axis = axis , ddof = ddof )
239239
240240
241+ def _safe_nanmean (data : np .ndarray , axis : Optional [int ] = None ) -> float :
242+ """
243+ Safe version of np.nanmean that handles empty slices gracefully
244+
245+ Parameters
246+ ----------
247+ data : np.ndarray
248+ Input data
249+ axis : int, optional
250+ Axis along which the mean is computed
251+
252+ Returns
253+ -------
254+ float
255+ Mean or np.nan if no valid data
256+ """
257+ with warnings .catch_warnings ():
258+ warnings .simplefilter ("ignore" , category = RuntimeWarning )
259+
260+ if axis is None :
261+ # Check if we have any non-NaN values
262+ if np .isnan (data ).all ():
263+ return np .nan
264+ else :
265+ # For axis operations, suppress warnings and let numpy handle it
266+ pass
267+
268+ return np .nanmean (data , axis = axis )
269+
270+
241271# Alternative vectorized implementation for better performance
242272def sd_measures_vectorized (data : pd .DataFrame ,
243273 dt0 : Optional [int ] = None ,
@@ -270,11 +300,11 @@ def _calculate_sd_subtypes_vectorized(gd2d: np.ndarray, dt0: int, subject_id: An
270300
271301 return {
272302 'id' : subject_id ,
273- 'SDw' : np . nanmean (np .nanstd (gd2d , axis = 1 , ddof = 1 )),
274- 'SDhhmm' : np .nanstd (np . nanmean (gd2d , axis = 0 ), ddof = 1 ),
275- 'SDwsh' : np . nanmean (_rolling_std (gd2d .T .flatten (), round (60 / dt0 ))),
276- 'SDdm' : np .nanstd (np . nanmean (gd2d , axis = 1 ), ddof = 1 ),
277- 'SDb' : np . nanmean (np .nanstd (gd2d , axis = 0 , ddof = 1 )),
278- 'SDbdm' : np . nanmean (np .nanstd (gd2d - np . nanmean (gd2d , axis = 1 , keepdims = True ),
303+ 'SDw' : _safe_nanmean (np .nanstd (gd2d , axis = 1 , ddof = 1 )),
304+ 'SDhhmm' : np .nanstd (_safe_nanmean (gd2d , axis = 0 ), ddof = 1 ),
305+ 'SDwsh' : _safe_nanmean (_rolling_std (gd2d .T .flatten (), round (60 / dt0 ))),
306+ 'SDdm' : np .nanstd (_safe_nanmean (gd2d , axis = 1 ), ddof = 1 ),
307+ 'SDb' : _safe_nanmean (np .nanstd (gd2d , axis = 0 , ddof = 1 )),
308+ 'SDbdm' : _safe_nanmean (np .nanstd (gd2d - _safe_nanmean (gd2d , axis = 1 , keepdims = True ),
279309 axis = 0 , ddof = 1 ))
280310 }
0 commit comments