@@ -371,6 +371,31 @@ def knn_impute(
371371 return edata if copy else None
372372
373373
374+ @singledispatch
375+ def _knn_impute_function (arr , var_indices : list [int ], numerical_indices : list [int ], imputer ) -> np .ndarray :
376+ raise _raise_array_type_not_implemented (_knn_impute_function , type (arr ))
377+
378+
379+ @_knn_impute_function .register (np .ndarray )
380+ @_apply_over_time_axis
381+ def _ (arr : np .ndarray , var_indices : list [int ], numerical_indices : list [int ], imputer ) -> np .ndarray :
382+ input_dtype = arr .dtype if np .issubdtype (arr .dtype , np .floating ) else np .float64
383+ numerical_arr = arr [:, numerical_indices ].astype (input_dtype , copy = True )
384+
385+ # complete columns to be used as anchors
386+ complete_numerical_columns = np .array (numerical_indices )[~ np .isnan (numerical_arr ).any (axis = 0 )].tolist ()
387+
388+ imputer_data_indices = var_indices + [
389+ column for column in complete_numerical_columns if column not in var_indices
390+ ] # columns to impute
391+ imputer_x = arr [:, imputer_data_indices ].astype (input_dtype , copy = True )
392+ X_imputed = imputer .fit_transform (imputer_x )
393+
394+ result = arr .copy ()
395+ result [:, imputer_data_indices ] = X_imputed
396+ return result
397+
398+
374399def _knn_impute (
375400 edata : EHRData ,
376401 var_names : Iterable [str ] | None ,
@@ -400,43 +425,13 @@ def _knn_impute(
400425 "var_names parameter or perform an encoding of your data."
401426 )
402427 mtx = edata .X if layer is None else edata .layers [layer ]
403- var_indices_original = var_indices
404- is_3d = False
405- input_dtype = mtx .dtype if np .issubdtype (mtx .dtype , np .floating ) else np .float64
406-
407- # if input data is 3D, flatten along axis 0 before passing it to the imputer: each timepoint becomes a row
408- if mtx .ndim == 3 :
409- is_3d = True
410- n_obs , n_vars , n_t = mtx .shape
411- mtx = (
412- mtx [:, var_indices , :]
413- .astype (input_dtype , copy = True )
414- .transpose (0 , 2 , 1 )
415- .reshape (n_obs * n_t , len (var_indices ))
416- )
417- numerical_indices = list (range (len (var_indices )))
418- var_indices = numerical_indices
419428
420- # complete columns to be used as anchors
421- complete_numerical_columns = np .array (numerical_indices )[~ np .isnan (mtx [:, numerical_indices ]).any (axis = 0 )].tolist ()
422-
423- imputer_data_indices = var_indices + [
424- column for column in complete_numerical_columns if column not in var_indices
425- ] # columns to impute
426- imputer_x = mtx [:, imputer_data_indices ].astype (input_dtype , copy = True )
427- X_imputed = imputer .fit_transform (imputer_x )
429+ X_imputed = _knn_impute_function (mtx , var_indices , numerical_indices , imputer )
428430
429- if is_3d :
430- # slice back to only requested columns and transpose back to n_obs, n_var, n_t
431- X_imputed = (
432- X_imputed [:, : len (var_indices_original )].reshape (n_obs , n_t , len (var_indices_original )).transpose (0 , 2 , 1 )
433- )
434- edata .layers [layer ][:, var_indices_original , :] = X_imputed
431+ if layer is None :
432+ edata .X [:, var_indices ] = X_imputed [:, var_indices ]
435433 else :
436- if layer is None :
437- edata .X [:, imputer_data_indices ] = X_imputed
438- else :
439- edata .layers [layer ][:, imputer_data_indices ] = X_imputed
434+ edata .layers [layer ][:, var_indices ] = X_imputed [:, var_indices ]
440435
441436
442437@singledispatch
0 commit comments