Repository: NCAS-CMS/cfdm

Issue:
Error from `Data.second_element` for some data shapes
For some `Data` shapes, the retrieval of the second element causes an error:

```python
>>> import cfdm
>>> import numpy as np
>>> d = cfdm.Data(np.arange(6).reshape(1, 3, 2))  # This shape is OK
>>> d.array
array([[[0],
        [1],
        [2],
        [3],
        [4],
        [5]]])
>>> d.second_element()
1
>>> d = cfdm.Data(np.arange(6).reshape(1, 6, 1))  # Not OK
>>> d.second_element()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[51], line 1
----> 1 d.second_element()

File ~/cfdm/cfdm/data/data.py:3009, in Data.second_element(self)
   2981 def second_element(self):
   2982     """Return the second element of the data as a scalar.
   2983 
   2984     .. versionadded:: (cfdm) 1.7.0
   (...)
   3007 
   3008     """
-> 3009     return self._item(
   3010         (slice(0, 1, 1),) * (self.ndim - 1) + (slice(1, 2, 1),)
   3011     )

File ~/cfdm/cfdm/data/data.py:655, in Data._item(self, index)
    652 array = self[index].array
    654 if not numpy.ma.isMA(array):
--> 655     return array.item()
    657 mask = array.mask
    658 if mask is numpy.ma.nomask or not mask.item():

ValueError: can only convert an array of size 1 to a Python scalar
```


```python
>>> cfdm.environment(paths=False)
Platform: Linux-5.15.0-67-generic-x86_64-with-glibc2.35
HDF5 library: 1.12.1
netcdf library: 4.8.1
Python: 3.10.9
netCDF4: 1.6.0
numpy: 1.22.3
cfdm.core: 1.10.0.3
cftime: 1.6.2
netcdf_flattener: 1.2.0
cfdm: 1.10.0.3
```

Discussion:

Patch:
diff --git a/Changelog.rst b/Changelog.rst
index 05fef7152..0987737fc 100644
--- a/Changelog.rst
+++ b/Changelog.rst
@@ -1,3 +1,14 @@
+Version 1.10.?.?
+----------------
+
+**2023-??-??**
+
+* Fixed bug that caused `cf.Data.second_element` to fail for some data
+  shapes, namely for a final axis with size one.
+  (https://github.com/NCAS-CMS/cfdm/issues/249)
+
+----
+
 Version 1.10.0.3
 ----------------
 
diff --git a/cfdm/data/data.py b/cfdm/data/data.py
index fb08dc004..39acd7570 100644
--- a/cfdm/data/data.py
+++ b/cfdm/data/data.py
@@ -3006,9 +3006,7 @@ class Data(Container, NetCDFHDF5, Files, core.Data):
         bar <class 'str'>
 
         """
-        return self._item(
-            (slice(0, 1, 1),) * (self.ndim - 1) + (slice(1, 2, 1),)
-        )
+        return self._item(np.unravel_index(1, self.shape))
 
     @_inplace_enabled(default=False)
     def to_memory(self, inplace=False):


Test patch:
diff --git a/cfdm/test/test_Data.py b/cfdm/test/test_Data.py
index 940a0d585..80a52610b 100644
--- a/cfdm/test/test_Data.py
+++ b/cfdm/test/test_Data.py
@@ -730,6 +730,21 @@ class DataTest(unittest.TestCase):
         with self.assertRaises(ValueError):
             d._original_filenames(update="file4.nc", clear=True)
 
+    def test_Data_first_element(self):
+        """Test the `first_element` Data methods."""
+        d = cfdm.Data(np.arange(6).reshape(1, 6, 1))
+        self.assertEqual(d.first_element(), 0)
+
+    def test_Data_second_element(self):
+        """Test the `second_element` Data methods."""
+        d = cfdm.Data(np.arange(6).reshape(1, 6, 1))
+        self.assertEqual(d.second_element(), 1)
+
+    def test_Data_last_element(self):
+        """Test the `last_element` Data methods."""
+        d = cfdm.Data(np.arange(6).reshape(1, 6, 1))
+        self.assertEqual(d.last_element(), 5)
+
 
 if __name__ == "__main__":
     print("Run date:", datetime.datetime.now())