Skip to content

Commit 0933b7e

Browse files
authored
Merge pull request #79 from JuBiotech/improve-BLData-type-hints
improve `BLData` type hints
2 parents b5614ef + 02cf9f0 commit 0933b7e

File tree

2 files changed

+86
-67
lines changed

2 files changed

+86
-67
lines changed

bletl/types.py

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,73 @@ class FluidicsSource(enum.IntEnum):
3232
"""Additions from pipetting."""
3333

3434

35-
class BLData(dict):
35+
class FilterTimeSeries:
36+
"""Generalizable data type for calibrated timeseries."""
37+
38+
@property
39+
def wells(self) -> typing.Tuple[str, ...]:
40+
"""Well IDs that were measured."""
41+
return tuple(self.time.columns)
42+
43+
def __init__(self, time_df: pandas.DataFrame, value_df: pandas.DataFrame):
44+
self.time = time_df
45+
self.value = value_df
46+
47+
def get_timeseries(
48+
self, well: str, *, last_cycle: Optional[int] = None
49+
) -> Tuple[numpy.ndarray, numpy.ndarray]:
50+
"""Retrieves (time, value) for a specific well.
51+
52+
Parameters
53+
----------
54+
well : str
55+
Well id to retrieve.
56+
last_cycle : int, optional
57+
Cycle number of the last cycle to be included (defaults to all cycles).
58+
59+
Returns
60+
-------
61+
x : numpy.ndarray
62+
Timepoints of measurements.
63+
y : numpy.ndarray
64+
Measured values.
65+
"""
66+
if last_cycle is not None and last_cycle <= 0:
67+
raise ValueError(f"last_cycle must be > 0")
68+
x = numpy.array(self.time[well])[:last_cycle]
69+
y = numpy.array(self.value[well])[:last_cycle]
70+
return x, y
71+
72+
def get_unified_dataframe(self, well: Optional[str] = None) -> pandas.DataFrame:
73+
"""Retrieves a DataFrame with unified time on index.
74+
75+
Parameters
76+
----------
77+
well : str, optional
78+
Well id from which time is taken.
79+
If `None`, the first well is used.
80+
81+
Returns
82+
-------
83+
unified_df : pandas.DataFrame
84+
Dataframe with unified time on index.
85+
"""
86+
if not well is None:
87+
if not well in self.time.columns:
88+
raise KeyError("Could not find well id")
89+
time = self.time.loc[:, well]
90+
else:
91+
time = self.time.iloc[:, 0]
92+
93+
new_index = pandas.Index(time, name="time in h")
94+
unified_df = self.value.set_index(new_index)
95+
return unified_df
96+
97+
def __repr__(self):
98+
return f"FilterTimeSeries({len(self.time)} cycles, {len(self.time.columns)} wells)"
99+
100+
101+
class BLData(Dict[str, FilterTimeSeries]):
36102
"""Standardized data type for BioLector data."""
37103

38104
def __init__(
@@ -223,72 +289,6 @@ def __repr__(self):
223289
)
224290

225291

226-
class FilterTimeSeries:
227-
"""Generalizable data type for calibrated timeseries."""
228-
229-
@property
230-
def wells(self) -> typing.Tuple[str, ...]:
231-
"""Well IDs that were measured."""
232-
return tuple(self.time.columns)
233-
234-
def __init__(self, time_df: pandas.DataFrame, value_df: pandas.DataFrame):
235-
self.time = time_df
236-
self.value = value_df
237-
238-
def get_timeseries(
239-
self, well: str, *, last_cycle: Optional[int] = None
240-
) -> Tuple[numpy.ndarray, numpy.ndarray]:
241-
"""Retrieves (time, value) for a specific well.
242-
243-
Parameters
244-
----------
245-
well : str
246-
Well id to retrieve.
247-
last_cycle : int, optional
248-
Cycle number of the last cycle to be included (defaults to all cycles).
249-
250-
Returns
251-
-------
252-
x : numpy.ndarray
253-
Timepoints of measurements.
254-
y : numpy.ndarray
255-
Measured values.
256-
"""
257-
if last_cycle is not None and last_cycle <= 0:
258-
raise ValueError(f"last_cycle must be > 0")
259-
x = numpy.array(self.time[well])[:last_cycle]
260-
y = numpy.array(self.value[well])[:last_cycle]
261-
return x, y
262-
263-
def get_unified_dataframe(self, well: Optional[str] = None) -> pandas.DataFrame:
264-
"""Retrieves a DataFrame with unified time on index.
265-
266-
Parameters
267-
----------
268-
well : str, optional
269-
Well id from which time is taken.
270-
If `None`, the first well is used.
271-
272-
Returns
273-
-------
274-
unified_df : pandas.DataFrame
275-
Dataframe with unified time on index.
276-
"""
277-
if not well is None:
278-
if not well in self.time.columns:
279-
raise KeyError("Could not find well id")
280-
time = self.time.loc[:, well]
281-
else:
282-
time = self.time.iloc[:, 0]
283-
284-
new_index = pandas.Index(time, name="time in h")
285-
unified_df = self.value.set_index(new_index)
286-
return unified_df
287-
288-
def __repr__(self):
289-
return f"FilterTimeSeries({len(self.time)} cycles, {len(self.time.columns)} wells)"
290-
291-
292292
class BLDParser:
293293
"""Abstract type for parsers that read BioLector CSV files."""
294294

tests/test_core.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,25 @@ def test_NoMeasurements_Warning(self):
319319
with pytest.warns(NoMeasurementData):
320320
bletl.parse(file_with_no_measurements)
321321

322+
def test_get_unified_dataframe_well_selection(self):
323+
# Create test data
324+
time_df = pandas.DataFrame({"A01": [0.0, 1.0, 2.0], "A02": [0.0, 1.0, 2.0]})
325+
value_df = pandas.DataFrame({"A01": [1.0, 2.0, 3.0], "A02": [1.5, 2.5, 3.5]})
326+
327+
fts = bletl.FilterTimeSeries(time_df, value_df)
328+
329+
# Test with valid well ID - should return DataFrame with correct time values
330+
df = fts.get_unified_dataframe(well="A01")
331+
numpy.testing.assert_array_equal(df.index.values, [0.0, 1.0, 2.0])
332+
333+
# Test with non-existent well ID - should raise KeyError
334+
with pytest.raises(KeyError, match="Could not find well id"):
335+
fts.get_unified_dataframe(well="X99")
336+
337+
# Test with None
338+
df_default = fts.get_unified_dataframe(well=None)
339+
numpy.testing.assert_array_equal(df_default.index.values, [0.0, 1.0, 2.0])
340+
322341

323342
class TestBL1Calibration:
324343
def test_calibration_data_type(self):

0 commit comments

Comments
 (0)