1- from typing import Dict , List , Optional
1+ from typing import Dict , List , Optional , Union , Type
2+ import inspect
23
34from torch .utils .data import Dataset
45from tqdm import tqdm
56
67from ..processors import get_processor
8+ from ..processors .base_processor import FeatureProcessor
79
810
911class SampleDataset (Dataset ):
1012 """Sample dataset class for handling and processing data samples.
1113
1214 Attributes:
1315 samples (List[Dict]): List of data samples.
14- input_schema (Dict[str, str]): Schema for input data.
15- output_schema (Dict[str, str]): Schema for output data.
16+ input_schema (Dict[str, Union[str, Type[FeatureProcessor]]]):
17+ Schema for input data.
18+ output_schema (Dict[str, Union[str, Type[FeatureProcessor]]]):
19+ Schema for output data.
1620 dataset_name (Optional[str]): Name of the dataset.
1721 task_name (Optional[str]): Name of the task.
1822 """
1923
2024 def __init__ (
2125 self ,
2226 samples : List [Dict ],
23- input_schema : Dict [str , str ],
24- output_schema : Dict [str , str ],
27+ input_schema : Dict [str , Union [ str , Type [ FeatureProcessor ]] ],
28+ output_schema : Dict [str , Union [ str , Type [ FeatureProcessor ]] ],
2529 dataset_name : Optional [str ] = None ,
2630 task_name : Optional [str ] = None ,
2731 ) -> None :
2832 """Initializes the SampleDataset with samples and schemas.
2933
3034 Args:
3135 samples (List[Dict]): List of data samples.
32- input_schema (Dict[str, str]): Schema for input data.
33- output_schema (Dict[str, str]): Schema for output data.
34- dataset_name (Optional[str], optional): Name of the dataset. Defaults to None.
35- task_name (Optional[str], optional): Name of the task. Defaults to None.
36+ input_schema (Dict[str, Union[str, Type[FeatureProcessor]]]):
37+ Schema for input data. Values can be string aliases or
38+ processor classes.
39+ output_schema (Dict[str, Union[str, Type[FeatureProcessor]]]):
40+ Schema for output data. Values can be string aliases or
41+ processor classes.
42+ dataset_name (Optional[str], optional): Name of the dataset.
43+ Defaults to None.
44+ task_name (Optional[str], optional): Name of the task.
45+ Defaults to None.
3646 """
3747 if dataset_name is None :
3848 dataset_name = ""
@@ -48,43 +58,66 @@ def __init__(
4858 # Create patient_to_index and record_to_index mappings
4959 self .patient_to_index = {}
5060 self .record_to_index = {}
51-
61+
5262 for i , sample in enumerate (samples ):
5363 # Create patient_to_index mapping
54- patient_id = sample .get (' patient_id' )
64+ patient_id = sample .get (" patient_id" )
5565 if patient_id is not None :
5666 if patient_id not in self .patient_to_index :
5767 self .patient_to_index [patient_id ] = []
5868 self .patient_to_index [patient_id ].append (i )
59-
69+
6070 # Create record_to_index mapping (optional)
61- record_id = sample .get (' record_id' , sample .get (' visit_id' ))
71+ record_id = sample .get (" record_id" , sample .get (" visit_id" ))
6272 if record_id is not None :
6373 if record_id not in self .record_to_index :
6474 self .record_to_index [record_id ] = []
6575 self .record_to_index [record_id ].append (i )
66-
76+
6777 self .validate ()
6878 self .build ()
6979
80+ def _get_processor_instance (self , processor_spec ):
81+ """Get processor instance from either string alias or class reference.
82+
83+ Args:
84+ processor_spec: Either a string alias or a processor class
85+
86+ Returns:
87+ Instance of the processor
88+ """
89+ if isinstance (processor_spec , str ):
90+ # Use existing registry system for string aliases
91+ return get_processor (processor_spec )()
92+ elif inspect .isclass (processor_spec ) and issubclass (
93+ processor_spec , FeatureProcessor
94+ ):
95+ # Direct class reference
96+ return processor_spec ()
97+ else :
98+ raise ValueError (
99+ f"Processor spec must be either a string alias or a "
100+ f"FeatureProcessor class, got { type (processor_spec )} "
101+ )
102+
70103 def validate (self ) -> None :
71104 """Validates that the samples match the input and output schemas."""
72105 input_keys = set (self .input_schema .keys ())
73106 output_keys = set (self .output_schema .keys ())
74107 for s in self .samples :
75- assert input_keys .issubset (s .keys ()), \
76- "Input schema does not match samples."
77- assert output_keys . issubset ( s .keys ()), \
78- "Output schema does not match samples."
108+ assert input_keys .issubset (s .keys ()), "Input schema does not match samples."
109+ assert output_keys . issubset (
110+ s .keys ()
111+ ), "Output schema does not match samples."
79112 return
80113
81114 def build (self ) -> None :
82115 """Builds the processors for input and output data based on schemas."""
83116 for k , v in self .input_schema .items ():
84- self .input_processors [k ] = get_processor ( v )( )
117+ self .input_processors [k ] = self . _get_processor_instance ( v )
85118 self .input_processors [k ].fit (self .samples , k )
86119 for k , v in self .output_schema .items ():
87- self .output_processors [k ] = get_processor ( v )( )
120+ self .output_processors [k ] = self . _get_processor_instance ( v )
88121 self .output_processors [k ].fit (self .samples , k )
89122 for sample in tqdm (self .samples , desc = "Processing samples" ):
90123 for k , v in sample .items ():
@@ -101,9 +134,9 @@ def __getitem__(self, index: int) -> Dict:
101134 index (int): Index of the sample to retrieve.
102135
103136 Returns:
104- Dict: A dict with patient_id, visit_id/record_id, and other task-specific
105- attributes as key. Conversion to index/tensor will be done
106- in the model.
137+ Dict: A dict with patient_id, visit_id/record_id, and other
138+ task-specific attributes as key. Conversion to index/tensor
139+ will be done in the model.
107140 """
108141 return self .samples [index ]
109142
0 commit comments