@@ -8123,5 +8123,129 @@ def test08_round_trip(self):
81238123 np .testing .assert_array_equal (w .flatten (), np .full (20 , 0.5 ))
81248124
81258125
8126+ class DataLoaderFromFiles (unittest .TestCase ):
8127+ file_name1 = "dataloader_from_files1.root"
8128+ file_name2 = "dataloader_from_files2.root"
8129+ tree_name = "mytree"
8130+
8131+ # Helpers
8132+ def create_file (self , file_name , num_of_entries = 10 , offset = 0 , output_format = "ttree" ):
8133+ opts = ROOT .RDF .RSnapshotOptions ()
8134+ if output_format == "rntuple" :
8135+ opts .fOutputFormat = ROOT .RDF .ESnapshotOutputFormat .kRNTuple
8136+ (
8137+ ROOT
8138+ .RDataFrame (num_of_entries )
8139+ .Define ("b1" , f"(int) rdfentry_ + { offset } " )
8140+ .Define ("b2" , "(double) b1 * b1" )
8141+ .Snapshot (self .tree_name , file_name , ["b1" , "b2" ], opts )
8142+ )
8143+
8144+ def tearDown (self ):
8145+ for file in (self .file_name1 , self .file_name2 ):
8146+ if os .path .exists (file ):
8147+ os .remove (file )
8148+
8149+ def load_all (self , inputs , ** kwargs ):
8150+ """Concatenate the features and the target of all the batches of an unshuffled loader."""
8151+ dl = ROOT .Experimental .ML .RDataLoader (
8152+ inputs , batch_size = 3 , batches_in_memory = 2 , target = "b2" , shuffle = False , drop_remainder = False , ** kwargs
8153+ )
8154+ batches = list (dl .as_numpy ())
8155+ x = np .concatenate ([x for x , _ in batches ]).flatten ()
8156+ y = np .concatenate ([y for _ , y in batches ]).flatten ()
8157+ return x , y
8158+
8159+ def test01_single_path (self ):
8160+ self .create_file (self .file_name1 )
8161+
8162+ for load_eager in (False , True ):
8163+ with self .subTest (load_eager = load_eager ):
8164+ x , y = self .load_all ((self .tree_name , self .file_name1 ), load_eager = load_eager )
8165+ np .testing .assert_array_equal (x , np .arange (10 ))
8166+ np .testing .assert_array_equal (y , np .arange (10 ) ** 2 )
8167+
8168+ def test02_pathlike (self ):
8169+ from pathlib import Path
8170+
8171+ self .create_file (self .file_name1 )
8172+
8173+ x , y = self .load_all ((self .tree_name , Path (self .file_name1 )))
8174+ np .testing .assert_array_equal (x , np .arange (10 ))
8175+ np .testing .assert_array_equal (y , np .arange (10 ) ** 2 )
8176+
8177+ def test03_list_of_paths (self ):
8178+ from pathlib import Path
8179+
8180+ self .create_file (self .file_name1 , num_of_entries = 9 )
8181+ self .create_file (self .file_name2 , num_of_entries = 6 , offset = 9 )
8182+
8183+ # the files are read as a single dataset, in the given order
8184+ x , y = self .load_all ((self .tree_name , [self .file_name1 , Path (self .file_name2 )]))
8185+ np .testing .assert_array_equal (x , np .arange (15 ))
8186+ np .testing .assert_array_equal (y , np .arange (15 ) ** 2 )
8187+
8188+ def test04_glob (self ):
8189+ self .create_file (self .file_name1 )
8190+ self .create_file (self .file_name2 , num_of_entries = 5 , offset = 10 )
8191+
8192+ x , _ = self .load_all ((self .tree_name , "dataloader_from_files*.root" ))
8193+ np .testing .assert_array_equal (np .sort (x ), np .arange (15 ))
8194+
8195+ def test05_rntuple (self ):
8196+ self .create_file (self .file_name1 , output_format = "rntuple" )
8197+
8198+ x , y = self .load_all ((self .tree_name , self .file_name1 ))
8199+ np .testing .assert_array_equal (x , np .arange (10 ))
8200+ np .testing .assert_array_equal (y , np .arange (10 ) ** 2 )
8201+
8202+ def test06_train_test_split (self ):
8203+ self .create_file (self .file_name1 )
8204+
8205+ dl = ROOT .Experimental .ML .RDataLoader (
8206+ (self .tree_name , self .file_name1 ),
8207+ batch_size = 2 ,
8208+ columns = ["b1" ],
8209+ shuffle = False ,
8210+ drop_remainder = False ,
8211+ )
8212+ gen_train , gen_validation = dl .train_test_split (0.4 )
8213+
8214+ train_x = np .concatenate (list (gen_train .as_numpy ())).flatten ()
8215+ val_x = np .concatenate (list (gen_validation .as_numpy ())).flatten ()
8216+ np .testing .assert_array_equal (train_x , np .arange (6 ))
8217+ np .testing .assert_array_equal (val_x , np .arange (6 , 10 ))
8218+
8219+ def test07_invalid_inputs (self ):
8220+ self .create_file (self .file_name1 )
8221+ df = ROOT .RDataFrame (self .tree_name , self .file_name1 )
8222+
8223+ # file paths need the name of the dataset to read
8224+ with self .assertRaisesRegex (TypeError , "dataset_name, files" ):
8225+ self .load_all (self .file_name1 )
8226+
8227+ with self .assertRaisesRegex (ValueError , "At least one RDataFrame or file path is required" ):
8228+ self .load_all ([])
8229+
8230+ # all the arguments but the inputs are keyword-only
8231+ with self .assertRaises (TypeError ):
8232+ ROOT .Experimental .ML .RDataLoader (df , 3 )
8233+
8234+ def test08_several_datasets (self ):
8235+ self .create_file (self .file_name1 , num_of_entries = 9 )
8236+ self .create_file (self .file_name2 , num_of_entries = 6 , offset = 9 )
8237+
8238+ # one RDataFrame per tuple, which can be mixed with RDataFrames
8239+ df = ROOT .RDataFrame (self .tree_name , self .file_name2 )
8240+ for inputs in (
8241+ [(self .tree_name , self .file_name1 ), (self .tree_name , self .file_name2 )],
8242+ [(self .tree_name , self .file_name1 ), df ],
8243+ ):
8244+ with self .subTest (inputs = inputs ):
8245+ x , y = self .load_all (inputs )
8246+ np .testing .assert_array_equal (x , np .arange (15 ))
8247+ np .testing .assert_array_equal (y , np .arange (15 ) ** 2 )
8248+
8249+
81268250if __name__ == "__main__" :
81278251 unittest .main ()
0 commit comments