Skip to content

Commit fca15e4

Browse files
committed
mapping,parameters
1 parent 427088b commit fca15e4

File tree

2 files changed

+135
-1
lines changed

2 files changed

+135
-1
lines changed

petab/v2/core.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ class ObservableTransformation(str, Enum):
3030
LOG10 = C.LOG10
3131

3232

33+
class ParameterScale(str, Enum):
34+
"""Parameter scales.
35+
36+
Parameter scales as used in the PEtab parameters table.
37+
"""
38+
39+
LIN = C.LIN
40+
LOG = C.LOG
41+
LOG10 = C.LOG10
42+
43+
3344
class NoiseDistribution(str, Enum):
3445
"""Noise distribution types.
3546
@@ -411,3 +422,118 @@ def from_tsv(cls, file_path: str | Path) -> MeasurementTable:
411422
def to_tsv(self, file_path: str | Path) -> None:
412423
df = self.to_dataframe()
413424
df.to_csv(file_path, sep="\t", index=False)
425+
426+
427+
class Mapping(BaseModel):
428+
"""Mapping PEtab entities to model entities."""
429+
430+
petab_id: str = Field(alias=C.PETAB_ENTITY_ID)
431+
model_id: str = Field(alias=C.MODEL_ENTITY_ID)
432+
433+
class Config:
434+
populate_by_name = True
435+
436+
@field_validator(
437+
"petab_id",
438+
)
439+
@classmethod
440+
def validate_id(cls, v):
441+
if not v:
442+
raise ValueError("ID must not be empty.")
443+
if not is_valid_identifier(v):
444+
raise ValueError(f"Invalid ID: {v}")
445+
return v
446+
447+
448+
class MappingTable(BaseModel):
449+
"""PEtab mapping table."""
450+
451+
mappings: list[Mapping]
452+
453+
@classmethod
454+
def from_dataframe(cls, df: pd.DataFrame) -> MappingTable:
455+
if df is None:
456+
return cls(mappings=[])
457+
458+
mappings = [
459+
Mapping(**row.to_dict()) for _, row in df.reset_index().iterrows()
460+
]
461+
462+
return cls(mappings=mappings)
463+
464+
def to_dataframe(self) -> pd.DataFrame:
465+
return pd.DataFrame(self.model_dump()["mappings"])
466+
467+
@classmethod
468+
def from_tsv(cls, file_path: str | Path) -> MappingTable:
469+
df = pd.read_csv(file_path, sep="\t")
470+
return cls.from_dataframe(df)
471+
472+
def to_tsv(self, file_path: str | Path) -> None:
473+
df = self.to_dataframe()
474+
df.to_csv(file_path, sep="\t", index=False)
475+
476+
477+
class Parameter(BaseModel):
478+
"""Parameter definition."""
479+
480+
id: str = Field(alias=C.PARAMETER_ID)
481+
lb: float | None = Field(alias=C.LOWER_BOUND, default=None)
482+
ub: float | None = Field(alias=C.UPPER_BOUND, default=None)
483+
nominal_value: float | None = Field(alias=C.NOMINAL_VALUE, default=None)
484+
scale: ParameterScale = Field(
485+
alias=C.PARAMETER_SCALE, default=ParameterScale.LIN
486+
)
487+
estimate: bool = Field(alias=C.ESTIMATE, default=True)
488+
# TODO priors
489+
490+
class Config:
491+
populate_by_name = True
492+
arbitrary_types_allowed = True
493+
use_enum_values = True
494+
495+
@field_validator("id")
496+
@classmethod
497+
def validate_id(cls, v):
498+
if not v:
499+
raise ValueError("ID must not be empty.")
500+
if not is_valid_identifier(v):
501+
raise ValueError(f"Invalid ID: {v}")
502+
return v
503+
504+
@field_validator("lb", "ub", "nominal_value")
505+
@classmethod
506+
def convert_nan_to_none(cls, v):
507+
if isinstance(v, float) and np.isnan(v):
508+
return None
509+
return v
510+
511+
512+
class ParameterTable(BaseModel):
513+
"""PEtab parameter table."""
514+
515+
parameters: list[Parameter]
516+
517+
@classmethod
518+
def from_dataframe(cls, df: pd.DataFrame) -> ParameterTable:
519+
if df is None:
520+
return cls(parameters=[])
521+
522+
parameters = [
523+
Parameter(**row.to_dict())
524+
for _, row in df.reset_index().iterrows()
525+
]
526+
527+
return cls(parameters=parameters)
528+
529+
def to_dataframe(self) -> pd.DataFrame:
530+
return pd.DataFrame(self.model_dump()["parameters"])
531+
532+
@classmethod
533+
def from_tsv(cls, file_path: str | Path) -> ParameterTable:
534+
df = pd.read_csv(file_path, sep="\t")
535+
return cls.from_dataframe(df)
536+
537+
def to_tsv(self, file_path: str | Path) -> None:
538+
df = self.to_dataframe()
539+
df.to_csv(file_path, sep="\t", index=False)

petab/v2/problem.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ def __init__(
9898
ConditionsTable,
9999
Experiment,
100100
ExperimentsTable,
101+
MappingTable,
101102
MeasurementTable,
102103
Observable,
103104
ObservablesTable,
105+
ParameterTable,
104106
)
105107

106108
self.observables_table: ObservablesTable = (
@@ -128,7 +130,13 @@ def __init__(
128130
)
129131
)
130132

131-
# TODO: measurements, parameters, visualization, mapping
133+
self.mapping_table: MappingTable = MappingTable.from_dataframe(
134+
self.mapping_df
135+
)
136+
self.parameter_table: ParameterTable = ParameterTable.from_dataframe(
137+
self.parameter_df
138+
)
139+
# TODO: visualization table
132140

133141
def __str__(self):
134142
model = f"with model ({self.model})" if self.model else "without model"

0 commit comments

Comments
 (0)