|
4 | 4 |
|
5 | 5 | import logging |
6 | 6 | from abc import ABC, abstractmethod |
7 | | -from collections import OrderedDict |
| 7 | +from collections import Counter, OrderedDict |
8 | 8 | from collections.abc import Set |
9 | 9 | from dataclasses import dataclass, field |
10 | 10 | from enum import IntEnum |
@@ -496,62 +496,44 @@ class CheckExperimentTable(ValidationTask): |
496 | 496 | """A task to validate the experiment table of a PEtab problem.""" |
497 | 497 |
|
498 | 498 | def run(self, problem: Problem) -> ValidationIssue | None: |
499 | | - if problem.experiment_df is None: |
500 | | - return |
501 | | - |
502 | | - df = problem.experiment_df |
503 | | - |
504 | | - try: |
505 | | - _check_df(df, EXPERIMENT_DF_REQUIRED_COLS, "experiment") |
506 | | - except AssertionError as e: |
507 | | - return ValidationError(str(e)) |
| 499 | + messages = [] |
| 500 | + for experiment in problem.experiments_table.experiments: |
| 501 | + # Check that there are no duplicate timepoints |
| 502 | + counter = Counter(period.time for period in experiment.periods) |
| 503 | + duplicates = {time for time, count in counter.items() if count > 1} |
| 504 | + if duplicates: |
| 505 | + messages.append( |
| 506 | + f"Experiment {experiment.id} contains duplicate " |
| 507 | + f"timepoints: {duplicates}" |
| 508 | + ) |
508 | 509 |
|
509 | | - # valid timepoints |
510 | | - invalid = [] |
511 | | - for time in df[TIME].values: |
512 | | - try: |
513 | | - time = float(time) |
514 | | - if not np.isfinite(time) and time != -np.inf: |
515 | | - invalid.append(time) |
516 | | - except ValueError: |
517 | | - invalid.append(time) |
518 | | - if invalid: |
519 | | - return ValidationError( |
520 | | - f"Invalid timepoints in experiment table: {invalid}" |
521 | | - ) |
| 510 | + if messages: |
| 511 | + return ValidationError("\n".join(messages)) |
522 | 512 |
|
523 | 513 |
|
524 | 514 | class CheckExperimentConditionsExist(ValidationTask): |
525 | 515 | """A task to validate that all conditions in the experiment table exist |
526 | 516 | in the condition table.""" |
527 | 517 |
|
528 | 518 | def run(self, problem: Problem) -> ValidationIssue | None: |
529 | | - if problem.experiment_df is None: |
530 | | - return |
531 | | - |
532 | | - if ( |
533 | | - problem.condition_df is None |
534 | | - and problem.experiment_df is not None |
535 | | - and not problem.experiment_df.empty |
536 | | - ): |
537 | | - return ValidationError( |
538 | | - "Experiment table is non-empty, " |
539 | | - "but condition table is missing." |
540 | | - ) |
541 | | - |
542 | | - required_conditions = problem.experiment_df[CONDITION_ID].unique() |
543 | | - existing_conditions = problem.condition_df[CONDITION_ID].unique() |
| 519 | + messages = [] |
| 520 | + available_conditions = { |
| 521 | + c.id |
| 522 | + for c in problem.conditions_table.conditions |
| 523 | + if not pd.isna(c.id) |
| 524 | + } |
| 525 | + for experiment in problem.experiments_table.experiments: |
| 526 | + missing_conditions = { |
| 527 | + period.condition for period in experiment.periods |
| 528 | + } - available_conditions |
| 529 | + if missing_conditions: |
| 530 | + messages.append( |
| 531 | + f"Experiment {experiment.id} requires conditions that are " |
| 532 | + f"not present in the condition table: {missing_conditions}" |
| 533 | + ) |
544 | 534 |
|
545 | | - missing_conditions = set(required_conditions) - set( |
546 | | - existing_conditions |
547 | | - ) |
548 | | - # TODO NA allowed? |
549 | | - missing_conditions = {x for x in missing_conditions if not pd.isna(x)} |
550 | | - if missing_conditions: |
551 | | - return ValidationError( |
552 | | - f"Experiment table contains conditions that are not present " |
553 | | - f"in the condition table: {missing_conditions}" |
554 | | - ) |
| 535 | + if messages: |
| 536 | + return ValidationError("\n".join(messages)) |
555 | 537 |
|
556 | 538 |
|
557 | 539 | class CheckAllParametersPresentInParameterTable(ValidationTask): |
|
0 commit comments