Skip to content

Commit 48ce7fb

Browse files
committed
[Python][ML] Add tests for the different inputs
Assisted-by: Claude Opus 5.5
1 parent f0cd2b9 commit 48ce7fb

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

‎bindings/pyroot/pythonizations/test/ml_dataloader.py‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
81268250
if __name__ == "__main__":
81278251
unittest.main()

0 commit comments

Comments
 (0)