data_utils.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """
  2. Utility functions for data extraction and processing
  3. Authors: Martin Horvat, January 2026
  4. """
  5. import numpy as np
  6. import pandas as pd
  7. """
  8. Extract data fom dictionaries for specific organ
  9. Input:
  10. organ: string in ["lung", "bowel", "thyroid"]
  11. perc: int, percentiles [1, ... , 100]
  12. suv_dict: dict containing percentiles of suv
  13. flags_dict: dict containing states
  14. """
  15. def get_data(organ, perc, suv_dict, flags_dict, nr_patient = 58):
  16. fname = "get_data"
  17. # get concrete data set
  18. suv = suv_dict[organ + '_SUVperc_COMBINED'][:nr_patient,:,:] # suv percentiles
  19. # index of percentile
  20. perc_idx = perc -1
  21. # computing max SUV percentile per patient, ignoring nans
  22. x = np.nanmax(suv[:,:,perc_idx], axis = 1)
  23. # determining index in the flags based on organ
  24. match organ:
  25. case "lung":
  26. flags_idx = 3
  27. case "bowel":
  28. flags_idx = 1
  29. case "thyroid":
  30. flags_idx = 5
  31. case _:
  32. assert False, f"{fname}::this organ {organ = } is not supported"
  33. # getting state of patients: 0 == NC, 1 == AE
  34. y = flags_dict['flags'][:nr_patient, flags_idx]
  35. return x, y
  36. """
  37. Check if a vector lies within the specified bounds for each dimension.
  38. Parameters:
  39. - vector (np.ndarray): 1D array representing the point to check. Shape: (n,)
  40. - bounds (np.ndarray): 2D array of shape (n, 2), where each row is (min, max) for a dimension.
  41. Returns:
  42. - bool: True if the vector is within bounds in all dimensions, False otherwise.
  43. """
  44. def within_bounds(vector: np.ndarray, bounds: np.ndarray) -> bool:
  45. if vector.shape[-1] != bounds.shape[0]:
  46. raise ValueError("Dimension mismatch: vector length and bounds rows must be equal.")
  47. return np.apply_along_axis(lambda x: np.all((x >= bounds[:, 0]) & (x <= bounds[:, 1])), -1, vector)
  48. def prepare_data(x0, y0, drop_mask):
  49. """
  50. Prepare data for plotting and fitting by creating a DataFrame with different subsets and scales.
  51. Parameters:
  52. - x0: Original feature vector (e.g., SUV percentiles).
  53. - y0: Original target vector (e.g., patient states).
  54. - drop_mask: Boolean array indicating which samples to drop for the "TRIM" dataset.
  55. Returns:
  56. dataframe, scales, datasets:
  57. - df_data: A pandas DataFrame containing the prepared data with columns ['X', 'Y', 'scale', 'dataset'].
  58. - scales: List of scale names.
  59. - datasets: List of dataset names.
  60. """
  61. datasets = ["FULL", "TRIM"]
  62. scales = ["plain", "log"]
  63. lst = []
  64. for dataset in datasets:
  65. for scale in scales:
  66. if dataset == "TRIM":
  67. y_tmp = y0[~drop_mask]
  68. x_tmp = x0[~drop_mask]
  69. else:
  70. y_tmp = y0[:]
  71. x_tmp = x0[:]
  72. if scale == "log":
  73. # remove non-positive values for log scale
  74. valid_mask = (x_tmp > 0)
  75. y_tmp = y_tmp[valid_mask]
  76. x_tmp = x_tmp[valid_mask]
  77. x_tmp = np.log(x_tmp)
  78. df_tmp = pd.DataFrame({
  79. "X" : x_tmp,
  80. "Y" : y_tmp ,
  81. "scale" : scale,
  82. "dataset": dataset})
  83. lst.append(df_tmp)
  84. return pd.concat(lst, ignore_index=True)