Repository: DHI/mikeio
Pull request #458 (2022-10-28 13:55:36)

add dfs0 property items_dataframe
@ecomodeller , I often find myself needing to get an overview of the content of dfs files without reading any data. Returning this as a dafarame allows easy export and stacking when looping multiple files. For now I added to dfs0 only and you can do:

```
import mikeio
ds = mikeio.open("testdata/random.dfs0")
ds.items_dataframe
```
![image](https://user-images.githubusercontent.com/29595985/198625652-a54addad-7aff-417e-9320-0e8862aed516.png)

Seems like it would have to be added to many places. And maybe it's just a (too) specialized form of `ds.items`?



Patch:
diff --git a/mikeio/dfsutil.py b/mikeio/dfsutil.py
index fb869ef4..ba4b7efb 100644
--- a/mikeio/dfsutil.py
+++ b/mikeio/dfsutil.py
@@ -2,7 +2,7 @@ from datetime import datetime
 from typing import Iterable, List, Tuple, Union
 import numpy as np
 import pandas as pd
-from .eum import EUMType, EUMUnit, ItemInfo, TimeAxisType
+from .eum import EUMType, EUMUnit, ItemInfo, TimeAxisType, ItemInfoList
 from .custom_exceptions import ItemsError
 
 from mikecore.DfsFile import DfsDynamicItemInfo, DfsFileInfo
@@ -159,7 +159,7 @@ def _get_item_info(
     dfsItemInfo: List[DfsDynamicItemInfo],
     item_numbers: List[int] = None,
     ignore_first: bool = False,
-) -> List[ItemInfo]:
+) -> ItemInfoList:
     """Read DFS ItemInfo for specific item numbers
 
     Parameters
@@ -186,4 +186,4 @@ def _get_item_info(
         data_value_type = dfsItemInfo[item].ValueType
         item = ItemInfo(name, itemtype, unit, data_value_type)
         items.append(item)
-    return items
+    return ItemInfoList(items)
diff --git a/mikeio/eum.py b/mikeio/eum.py
index ab70bf20..4f40f018 100644
--- a/mikeio/eum.py
+++ b/mikeio/eum.py
@@ -12,13 +12,15 @@ Examples
 degree Kelvin
 
 """
-from typing import List
+from typing import List, Sequence
 from mikecore.DfsFile import DataValueType
 from mikecore.eum import eumWrapper
 from enum import IntEnum
 
 from mikeio.helpers import to_datatype
 
+import pandas as pd
+
 
 def type_list(search=None):
     """Get a dictionary of the EUM items
@@ -1454,3 +1456,12 @@ class ItemInfo:
             return f"{self.name} <{self.type.display_name}> ({self.unit.display_name})"
         else:
             return f"{self.name} <{self.type.display_name}> ({self.unit.display_name}) - {self.data_value_type}"
+
+class ItemInfoList(list):
+
+    def __init__(self, items: Sequence[ItemInfo]):
+        super().__init__(items)
+
+    def to_dataframe(self):
+        data = [{"name": item.name, "type": item.type.name, "unit": item.unit.name} for item in self]
+        return pd.DataFrame(data)


Test patch:
diff --git a/tests/test_dfs0.py b/tests/test_dfs0.py
index b6eb4550..6b06cd25 100644
--- a/tests/test_dfs0.py
+++ b/tests/test_dfs0.py
@@ -112,6 +112,13 @@ def test_read_all_time_steps_without_reading_items():
     assert isinstance(dfs.time, pd.DatetimeIndex)
     assert len(dfs.time) == 1000
 
+def test_items_dataframe():
+    dfs = mikeio.open("tests/testdata/random.dfs0")
+    df = dfs.items.to_dataframe()
+    assert "name" in df.columns
+    assert "type" in df.columns # or EUMType ?
+    assert df.type.iloc[1] == "Water_Level" # Is this the correct way to show it?
+
 
 def test_read_all_time_steps_without_reading_items_neq():
     dfs0file = r"tests/testdata/da_diagnostic.dfs0"