Repository: ImperialCollegeLondon/PyProBE

Issue:
Add export to matlab function
Add a method to export a result object to matlab. It would write to a `.mat` file a `data` variable and an `info` variable. This would be primarily for using matlab as a plotting tool.

Discussion:

Patch:
diff --git a/pyprobe/result.py b/pyprobe/result.py
index ca12368..a3f9d02 100644
--- a/pyprobe/result.py
+++ b/pyprobe/result.py
@@ -11,6 +11,8 @@ import polars as pl
 from deprecated import deprecated
 from numpy.typing import NDArray
 from pydantic import BaseModel, Field, model_validator
+from scipy.io import savemat
+import re
 
 from pyprobe.plot import _retrieve_relevant_columns
 from pyprobe.units import split_quantity_unit, unit_from_regexp
@@ -600,6 +602,33 @@ class Result(BaseModel):
         data = pl.concat(data)
         return cls(base_dataframe=data, info=info)
 
+    def export_to_mat(self, filename: str) -> None:
+        """Export the data to a .mat file.
+
+        This method will export the data and info dictionary to a .mat file. The
+        variables in the .mat file will be named 'data' and 'info'. Column names and
+        dictionary keys will have any non-alphanumeric characters replaced with an
+        underscore, to comply with MATLAB variable naming rules.
+
+        Args:
+            filename: The name of the file to export to.
+        """
+        # Replace any non-alphanumeric character with an underscore in the DataFrame columns
+        renamed_data = self.data.rename(
+            {col: re.sub(r"\W", "_", col) for col in self.data.columns}
+        )
+
+        # Replace any non-alphanumeric character with an underscore in the info dictionary keys
+        renamed_info = {
+            re.sub(r"\W", "_", key): value for key, value in self.info.items()
+        }
+
+        variable_dict = {
+            "data": renamed_data.to_dict(),
+            "info": renamed_info,
+        }
+        savemat(filename, variable_dict, oned_as="column")
+
 
 def combine_results(
     results: List[Result],


Test patch:
diff --git a/tests/test_result.py b/tests/test_result.py
index 28fd089..683f171 100644
--- a/tests/test_result.py
+++ b/tests/test_result.py
@@ -6,6 +6,8 @@ import numpy.testing as np_testing
 import polars as pl
 import polars.testing as pl_testing
 import pytest
+from scipy.io import loadmat
+import os
 
 from pyprobe.result import PolarsColumnCache, Result, combine_results
 
@@ -480,3 +482,25 @@ def test_combine_results():
     pl_testing.assert_frame_equal(
         combined_result.data, expected_data, check_column_order=False
     )
+
+
+def test_export_to_mat(Result_fixture):
+    """Test the export to mat function."""
+    Result_fixture.export_to_mat("./test_mat.mat")
+    saved_data = loadmat("./test_mat.mat")
+    assert "data" in saved_data.keys()
+    assert "info" in saved_data.keys()
+    expected_columns = set(
+        [
+            "Current__A_",
+            "Step",
+            "Event",
+            "Time__s_",
+            "Capacity__Ah_",
+            "Voltage__V_",
+            "Date",
+        ]
+    )
+    actual_columns = set(saved_data["data"].dtype.names)
+    assert actual_columns == expected_columns
+    os.remove("./test_mat.mat")