Subclassing for aind-behavior. How should we organize? #636
Replies: 3 comments 5 replies
-
WaterCalibration.json The meaning of each field is shown in the following code. |
Beta Was this translation helpful? Give feedback.
-
I actually would prefer we not subclass the Calibration class if possible. I think we want the calibration list to include a calibration of the reward spout, and how you put the data in there should follow a convention and so having something in the ingest script makes sense, but I think adding subclasses into the schema is going to cause bloat. |
Beta Was this translation helpful? Give feedback.
-
Here's the revised structure. Still needs to go through some testing, but let me know if it makes sense. from pydantic import PositiveFloat, PositiveInt, Field
from typing import List, Optional, Dict
import aind_data_schema
from aind_data_schema.base import AindModel
from aind_data_schema.models.devices import Calibration
import json
class WaterCalibrationInput(AindModel):
valve_open_interval: PositiveFloat = Field(..., description="Time between two consecutive valve openings (s)", title="Valve open interval", units="s")
valve_open_time: PositiveFloat = Field(..., description="Valve open interval (s)", title="Valve open time", units="s")
water_weight: List[PositiveFloat] = Field(..., description="Weight of water delivered (g)", title="Water weight", units="g", min_length=1)
repeat_count: int = Field(..., ge=0, description="Number of times the valve opened.", title="Repeat count")
class WaterCalibrationOutput(AindModel):
interval_average: Optional[Dict[PositiveFloat, PositiveFloat]] = Field(None, description="Dictionary keyed by measured valve interval and corresponding average single event volume.", title="Interval average", units="s")
slope: float = Field(..., description="Slope of the linear regression : Volume(g) = Slope(g/s) * time(s) + offset(g)", title="Regression slope", units="g/s")
offset: float = Field(..., description="Offset of the linear regression : Volume(g) = Slope(g/s) * time(s) + offset(g)", title="Regression offset", units="g")
r2: PositiveFloat = Field(..., description="R2 metric from the linear model.", title="R2", gt=0, le=1)
valid_domain: Optional[List[PositiveFloat]] = Field(None, description="The optional time-intervals the calibration curve was calculated on.", min_length=2, title="Valid domain", units="s")
class WaterCalibration(Calibration):
description: Literal["Calibration of the water valve delivery system"] = "Calibration of the water valve delivery system"
input: List[WaterCalibrationInput] = Field([], title="Input of the calibration") #see note 1.
output: Optional[WaterCalibrationOutput] = Field(None, title="Output of the calibration.") Note 1. This assumes the type of the Calibration parent class is changed to |
Beta Was this translation helpful? Give feedback.
-
Some generic objects would be nice to subclass in order to more precisely define data structures that we might want to reuse across several different behavioral tasks. An obvious one would be the water calibration.
Under the current data-schema spec, this would be implemented using the
Calibration
class:aind-data-schema/src/aind_data_schema/device.py
Line 110 in e92f039
Since this is a generic class aimed at having the flexibility to express several different calibration procedures, it codes both the inputs and the outputs of the calibration as
Optional[Dict[str, Any]]
.For water calibration, we could come up with a much more precise structure (e.g. inputs would perhaps code
delay
andwater_weight
, while outputs could store some sort of model (e.g. just a linear with aslope
andoffset
) or a lookup table. Both could be easily expressed in a schema format.This raises several questions:
Calibration
class with generic inputs asOptional[Any]
. On the other hand, I believe that any object we set as the input to the children class, should be able to be deserialized back to aDict[str,Any]
, since that's what a json object is anyway. So this might not be an issue (?)WaterCalibration
object look like? Can you add the JSON you are currently using in the dynamic foraging (@XX-Yin or @hanhou) to store this data?Beta Was this translation helpful? Give feedback.
All reactions