Repository: epic-open-source/seismometer

Issue:
available_cohort_groups not initialized on Seismogram
<!--- Issues must not contain any PHI, PII, or other confidential information. --->

<!--- Provide a general summary of the issue in the Title above -->

## Problem Summary
It is possible for a Seismogram to be populated with data but not have the attribute available_cohort_groups

## Impact
sm.ExplorSubgroups() errors
![image](https://github.com/user-attachments/assets/15ed19ba-65cf-48bb-b629-59e7f80e2486)

Was hit with a tiny tiny dataset, which had all cohorts dropped for censoring, so unlikely analysis would be useful regardless.

## Possible Solution
Initialize available_cohort_groups in the constructor.

Discussion:
@reproduce-bot: The following script is generated by AI Agent to help reproduce the issue:

```python
# seismometer/reproduce.py
import unittest
from seismometer.seismogram import Seismogram, ConfigProvider, SeismogramLoader
from unittest.mock import Mock

class TestSeismogramInitialization(unittest.TestCase):

    def test_available_cohort_groups_initialization(self):
        try:
            config = Mock(spec=ConfigProvider)
            dataloader = Mock(spec=SeismogramLoader)
            config.config_dir = '/mock/path'  # Adding mock attribute
            config.cohorts = []  # Adding mock attribute
            config.target = 'mock_event'  # Adding mock attribute
            seismogram = Seismogram(config, dataloader)
            # This should not raise an AttributeError if available_cohort_groups is properly initialized
            _ = seismogram.available_cohort_groups
        except AttributeError as e:
            raise AssertionError(f"Test failed as expected with error: {e}")
        
        print("Test passed successfully with no errors!")

if __name__ == "__main__":
    unittest.main()
```

How to run:
```shell
python3 seismometer/reproduce.py
```

Expected Result:
```
WARNING:seismometer:No cohort columns were configured; tool may behave unexpectedly.
F
======================================================================
FAIL: test_available_cohort_groups_initialization (__main__.TestSeismogramInitialization)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "seismometer/reproduce.py", line 16, in test_available_cohort_groups_initialization
    _ = seismogram.available_cohort_groups
AttributeError: 'Seismogram' object has no attribute 'available_cohort_groups'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "seismometer/reproduce.py", line 18, in test_available_cohort_groups_initialization
    raise AssertionError(f"Test failed as expected with error: {e}")
AssertionError: Test failed as expected with error: 'Seismogram' object has no attribute 'available_cohort_groups'

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)
```

Thank you for your valuable contribution to this project and we appreciate your feedback! Please respond with an emoji if you find this script helpful. Feel free to comment below if any improvements are needed.

Best regards from an AI Agent!
@diehlbw 

Solution patch:
diff --git a/changelog/120.bugfix.rst b/changelog/120.bugfix.rst
new file mode 100644
index 00000000..929793ea
--- /dev/null
+++ b/changelog/120.bugfix.rst
@@ -0,0 +1 @@
+Initialize Seismogram values to prevent AttributeErrors like available_cohort_group
diff --git a/src/seismometer/seismogram.py b/src/seismometer/seismogram.py
index 621dbd78..b1550e90 100644
--- a/src/seismometer/seismogram.py
+++ b/src/seismometer/seismogram.py
@@ -64,6 +64,8 @@ def __init__(
         if config is None or dataloader is None:
             raise ValueError("Seismogram has not been initialized; requires Config and dataloader on initial call.")
 
+        self._initialize_attrs()
+
         self.config = config
         self.dataloader = dataloader
 
@@ -72,6 +74,23 @@ def __init__(
 
         self.copy_config_metadata()
 
+    def _initialize_attrs(self):
+        """
+        Initialize attributes
+        """
+        # _df_counts
+        self._start_time = None
+        self._end_time = None
+        self._prediction_count = 0
+        self._entity_count = 0
+        self._event_types_count = 0
+        self._cohort_attribute_count = 0
+        self._feature_counts = 0
+
+        # load data
+        self.available_cohort_groups = dict()
+        self.selected_cohort = (None, None)  # column, values
+
     def load_data(
         self, *, predictions: Optional[pd.DataFrame] = None, events: Optional[pd.DataFrame] = None, reset: bool = False
     ):


Test patch:
diff --git a/tests/test_seismogram.py b/tests/test_seismogram.py
index d8f84ca1..e19768ac 100644
--- a/tests/test_seismogram.py
+++ b/tests/test_seismogram.py
@@ -234,3 +234,25 @@ def test_censor_some_logs_warning(self, fake_seismo, tmp_path, caplog):
         assert "Some cohorts" in caplog.records[0].message
         assert "rareVals" in caplog.records[0].message
         assert sg.cohort_cols == ["cohort1", "cohort2", "rareVals"]
+
+
+class TestSeismogramAttrs:
+    @pytest.mark.parametrize(
+        "attr_name",
+        [
+            "start_time",
+            "end_time",
+            "prediction_count",
+            "entity_count",
+            "event_types_count",
+            "cohort_attribute_count",
+            "feature_count",
+            "target_event",
+            "dataframe",
+        ],
+    )
+    def test_attribute_exists(self, fake_seismo, attr_name):
+        sg = Seismogram()
+
+        # Ensure attribute is available
+        assert hasattr(sg, attr_name)