Repository: ESSS/pytest-regressions (language: python)

Issue:
Testing NaN does not work
I have a numpy array that I have to test with inside a NaN that has to be there. Version 2.2.0 works, while version 2.3.0 does not work.

output of 2.2.0:
```
bertossa@pippo:~/analisi/tests$ pytest -sv . -k gk
WARNING: cannot import thermocepstrum
cite as:
Riccardo Bertossa, analisi
https://github.com/rikigigi/analisi
(c) 2017-2021
=========
COMPILED AT Jan 17 2022 14:47:14 by /usr/bin/c++ whith flags (Release) -O3 -DNDEBUG -O3 (Debug)  -g -DEIGEN_INITIALIZE_MATRICES_BY_NAN    -fsanitize=leak -fsanitize=undefined (build type was Release) on a Linux-5.4.0-94-generic whith processor x86_64
With python support: /usr/lib/x86_64-linux-gnu/libpython3.8.so
With gromacs XDR file conversion support
v0.3.4
============================================================================ test session starts ============================================================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-0.13.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/bertossa/analisi/tests
plugins: datadir-1.3.1, regressions-2.2.0
collected 6 items / 5 deselected / 1 selected                                                                                                                               

test_gk.py::test_gk [[ 1.15338509e+05 -3.54402664e+01 -3.54402664e+01 ...  0.00000000e+00
   0.00000000e+00  0.00000000e+00]
 [ 1.13409212e+05 -3.51673102e+01 -3.49856870e+01 ...  3.53293021e-02
   7.91862554e+04             nan]
 [ 1.07866235e+05 -3.41664742e+01 -3.38088613e+01 ...  5.21211914e-02
   1.55216495e+05  3.95931277e+04]
 ...
 [-2.73309429e+03 -2.66983885e-01  1.39421901e+00 ... -2.67101979e-02
   1.62004622e+05  5.34430500e+05]
 [-2.58976152e+03 -1.40244210e-01  1.24827661e+00 ... -2.65180986e-02
   1.59872994e+05  5.34257456e+05]
 [-2.37993149e+03 -2.79107527e-02  1.07349726e+00 ... -2.63087491e-02
   1.57897803e+05  5.34083800e+05]]
PASSED

============================================================================= warnings summary ==============================================================================
../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_selection.py:9
  /usr/lib/python3/dist-packages/ipywidgets/widgets/widget_selection.py:9: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    from collections import Mapping

../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/interaction.py:29
  /usr/lib/python3/dist-packages/ipywidgets/widgets/interaction.py:29: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    from collections import Iterable, Mapping

../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19
../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19
  /usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19: DeprecationWarning: Traits should be given as instances, not types (for example, `Int()`, not `Int`) Passing types is deprecated in traitlets 4.1.
    super(WidgetTraitTuple, self).__init__(Instance(Widget), Unicode, **kwargs)

test_gk.py::test_gk
test_gk.py::test_gk
  /home/bertossa/.local/lib/python3.8/site-packages/pytest_regressions/dataframe_regression.py:126: DeprecationWarning: `np.float` is a deprecated alias for the builtin `float`. To silence this warning, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.
  Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
    if data_type in [float, np.float, np.float16, np.float32, np.float64]:

-- Docs: https://docs.pytest.org/en/stable/warnings.html
================================================================ 1 passed, 5 deselected, 6 warnings in 1.66s ================================================================
bertossa@pippo:~/analisi/tests$ 


```

output of 2.3.0:

```
bertossa@pippo:~/analisi/tests$ pytest -sv . -k gk
WARNING: cannot import thermocepstrum
cite as:
Riccardo Bertossa, analisi
https://github.com/rikigigi/analisi
(c) 2017-2021
=========
COMPILED AT Jan 17 2022 14:47:14 by /usr/bin/c++ whith flags (Release) -O3 -DNDEBUG -O3 (Debug)  -g -DEIGEN_INITIALIZE_MATRICES_BY_NAN    -fsanitize=leak -fsanitize=undefined (build type was Release) on a Linux-5.4.0-94-generic whith processor x86_64
With python support: /usr/lib/x86_64-linux-gnu/libpython3.8.so
With gromacs XDR file conversion support
v0.3.4
============================================================================ test session starts ============================================================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-0.13.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/bertossa/analisi/tests
plugins: datadir-1.3.1, regressions-2.3.0
collected 6 items / 5 deselected / 1 selected                                                                                                                               

test_gk.py::test_gk [[ 1.15338509e+05 -3.54402664e+01 -3.54402664e+01 ...  0.00000000e+00
   0.00000000e+00  0.00000000e+00]
 [ 1.13409212e+05 -3.51673102e+01 -3.49856870e+01 ...  3.53293021e-02
   7.91862554e+04             nan]
 [ 1.07866235e+05 -3.41664742e+01 -3.38088613e+01 ...  5.21211914e-02
   1.55216495e+05  3.95931277e+04]
 ...
 [-2.73309429e+03 -2.66983885e-01  1.39421901e+00 ... -2.67101979e-02
   1.62004622e+05  5.34430500e+05]
 [-2.58976152e+03 -1.40244210e-01  1.24827661e+00 ... -2.65180986e-02
   1.59872994e+05  5.34257456e+05]
 [-2.37993149e+03 -2.79107527e-02  1.07349726e+00 ... -2.63087491e-02
   1.57897803e+05  5.34083800e+05]]
FAILED

================================================================================= FAILURES ==================================================================================
__________________________________________________________________________________ test_gk __________________________________________________________________________________

left = array(['115338.50895090569', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.026308749054506145', '157897.80272819981',
       '534083.79967621446'], dtype=object)
right = array(['115338.50895090567', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.02630874905450617', '157897.80272819955',
       '534083.79967621388'], dtype=object)
op = <built-in function sub>, is_cmp = False

    def _na_arithmetic_op(left, right, op, is_cmp: bool = False):
        """
        Return the result of evaluating op on the passed in values.
    
        If native types are not compatible, try coercion to object dtype.
    
        Parameters
        ----------
        left : np.ndarray
        right : np.ndarray or scalar
        is_cmp : bool, default False
            If this a comparison operation.
    
        Returns
        -------
        array-like
    
        Raises
        ------
        TypeError : invalid operation
        """
        if isinstance(right, str):
            # can never use numexpr
            func = op
        else:
            func = partial(expressions.evaluate, op)
    
        try:
>           result = func(left, right)

../../.local/lib/python3.8/site-packages/pandas/core/ops/array_ops.py:166: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

op = <built-in function sub>
a = array(['115338.50895090569', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.026308749054506145', '157897.80272819981',
       '534083.79967621446'], dtype=object)
b = array(['115338.50895090567', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.02630874905450617', '157897.80272819955',
       '534083.79967621388'], dtype=object)
use_numexpr = True

    def evaluate(op, a, b, use_numexpr: bool = True):
        """
        Evaluate and return the expression of the op on a and b.
    
        Parameters
        ----------
        op : the actual operand
        a : left operand
        b : right operand
        use_numexpr : bool, default True
            Whether to try to use numexpr.
        """
        op_str = _op_str_mapping[op]
        if op_str is not None:
            if use_numexpr:
                # error: "None" not callable
>               return _evaluate(op, op_str, a, b)  # type: ignore[misc]

../../.local/lib/python3.8/site-packages/pandas/core/computation/expressions.py:239: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

op = <built-in function sub>, op_str = '-'
a = array(['115338.50895090569', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.026308749054506145', '157897.80272819981',
       '534083.79967621446'], dtype=object)
b = array(['115338.50895090567', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.02630874905450617', '157897.80272819955',
       '534083.79967621388'], dtype=object)

    def _evaluate_standard(op, op_str, a, b):
        """
        Standard evaluation.
        """
        if _TEST_MODE:
            _store_test_result(False)
>       return op(a, b)
E       TypeError: unsupported operand type(s) for -: 'str' and 'str'

../../.local/lib/python3.8/site-packages/pandas/core/computation/expressions.py:69: TypeError

During handling of the above exception, another exception occurred:

analisi_log = <pyanalisi.pyanalisi.ReadLog object at 0x7fdb87a47470>, num_regression = <pytest_regressions.num_regression.NumericRegressionFixture object at 0x7fdb87a28130>

    def test_gk(analisi_log,num_regression):
        import pyanalisi
        gk=pyanalisi.GreenKubo(analisi_log,'',1,['c_flux[1]','c_vcm[1][1]'], False, 2000, 2,False,0,4,False,1,100)
        gk.reset(analisi_log.getNtimesteps()-2000)
        gk.calculate(0)
        m=np.array(gk,copy=False)
        print(m)
>       num_regression.check({'gk':m.flatten()})

test_gk.py:11: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.local/lib/python3.8/site-packages/pandas/core/ops/common.py:69: in new_method
    return method(self, other)
../../.local/lib/python3.8/site-packages/pandas/core/arraylike.py:100: in __sub__
    return self._arith_method(other, operator.sub)
../../.local/lib/python3.8/site-packages/pandas/core/series.py:5526: in _arith_method
    result = ops.arithmetic_op(lvalues, rvalues, op)
../../.local/lib/python3.8/site-packages/pandas/core/ops/array_ops.py:224: in arithmetic_op
    res_values = _na_arithmetic_op(left, right, op)
../../.local/lib/python3.8/site-packages/pandas/core/ops/array_ops.py:173: in _na_arithmetic_op
    result = _masked_arith_op(left, right, op)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = array(['115338.50895090569', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.026308749054506145', '157897.80272819981',
       '534083.79967621446'], dtype=object)
y = array(['115338.50895090567', '-35.440266364698509', '-35.440266364698509',
       ..., '-0.02630874905450617', '157897.80272819955',
       '534083.79967621388'], dtype=object)
op = <built-in function sub>

    def _masked_arith_op(x: np.ndarray, y, op):
        """
        If the given arithmetic operation fails, attempt it again on
        only the non-null elements of the input array(s).
    
        Parameters
        ----------
        x : np.ndarray
        y : np.ndarray, Series, Index
        op : binary operator
        """
        # For Series `x` is 1D so ravel() is a no-op; calling it anyway makes
        # the logic valid for both Series and DataFrame ops.
        xrav = x.ravel()
        assert isinstance(x, np.ndarray), type(x)
        if isinstance(y, np.ndarray):
            dtype = find_common_type([x.dtype, y.dtype])
            # error: Argument "dtype" to "empty" has incompatible type
            # "Union[dtype, ExtensionDtype]"; expected "Union[dtype, None, type,
            # _SupportsDtype, str, Tuple[Any, int], Tuple[Any, Union[int,
            # Sequence[int]]], List[Any], _DtypeDict, Tuple[Any, Any]]"
            result = np.empty(x.size, dtype=dtype)  # type: ignore[arg-type]
    
            if len(x) != len(y):
                raise ValueError(x.shape, y.shape)
            else:
                ymask = notna(y)
    
            # NB: ravel() is only safe since y is ndarray; for e.g. PeriodIndex
            #  we would get int64 dtype, see GH#19956
            yrav = y.ravel()
            mask = notna(xrav) & ymask.ravel()
    
            # See GH#5284, GH#5035, GH#19448 for historical reference
            if mask.any():
>               result[mask] = op(xrav[mask], yrav[mask])
E               TypeError: unsupported operand type(s) for -: 'str' and 'str'

../../.local/lib/python3.8/site-packages/pandas/core/ops/array_ops.py:112: TypeError
============================================================================= warnings summary ==============================================================================
../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_selection.py:9
  /usr/lib/python3/dist-packages/ipywidgets/widgets/widget_selection.py:9: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    from collections import Mapping

../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/interaction.py:29
  /usr/lib/python3/dist-packages/ipywidgets/widgets/interaction.py:29: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    from collections import Iterable, Mapping

../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19
../../../../usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19
  /usr/lib/python3/dist-packages/ipywidgets/widgets/widget_link.py:19: DeprecationWarning: Traits should be given as instances, not types (for example, `Int()`, not `Int`) Passing types is deprecated in traitlets 4.1.
    super(WidgetTraitTuple, self).__init__(Instance(Widget), Unicode, **kwargs)

-- Docs: https://docs.pytest.org/en/stable/warnings.html
========================================================================== short test summary info ==========================================================================
FAILED test_gk.py::test_gk - TypeError: unsupported operand type(s) for -: 'str' and 'str'
================================================================ 1 failed, 5 deselected, 4 warnings in 1.94s ================================================================
bertossa@pippo:~/analisi/tests$ 


```


Discussion / hints:
Hi @rikigigi, thanks for the report. 
@rikigigi Thanks for the report.

I tried the code below, but it passes. Don't know if I misunderstood the issue, I thought it should fail.
```
def test_array_with_nan(num_regression):
    data1 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.1]])
    data2 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.1]])
    num_regression.check({"data1": data1.flatten(), "data2": data2.flatten()})
```

Could you please help me reproduce, with a minimal test case?
If this does fail in your machine, could you send versions of `numpy` and `pandas`, so I can verify I'm using the same as you?

Thanks in advance.
Sorry, it must be something else, your test code is succesful on my two different machines, also saving my data with np.save and then testing works with both 2.2 and 2.3. My test suite is still failing on 2.3 on both machines. 
To reproduce at the moment you could try to clone and follows instructions at https://github.com/rikigigi/analisi/tree/devel , but I understand that it is a little bit too much. 
I'm trying to get a working minimal case
ok, got a minimal working example in:
https://github.com/rikigigi/regression

can you confirm that here by running pytest with the 2.2 and 2.3 you get different outcomes?
```
pip list | egrep 'numpy|pandas'
numpy                  1.20.2              
pandas                 1.3.5           
```
Thank you! Yes, I'm able to reproduce now.
I'll make some investigation and get back to this ASAP.
Quick update (still not solved):

So, apparently the problem is how we currently deal with `nan` in the saved files.

(a) We save dataframe files like this: https://github.com/ESSS/pytest-regressions/blob/5d626068933569e3f8e37939dc12ab5d051c9025/src/pytest_regressions/dataframe_regression.py#L159-L169

This means that `np.nan` is stored as an empty string.

(b) We load them like this: https://github.com/ESSS/pytest-regressions/blob/5d626068933569e3f8e37939dc12ab5d051c9025/src/pytest_regressions/dataframe_regression.py#L101-L102

This means that the loaded empty string that should be loaded as `np.nan` is actually being loaded as empty string, and, thus, the whole data is loaded as `str`. Thus, when we try to compute any operation, it'll yield errors like `TypeError: unsupported operand type(s) for -: 'str' and 'str'`.

A dummy fix would be to change the `_check_fn` with
```
        obtained_data = pd.read_csv(str(obtained_filename), keep_default_na=True)
        expected_data = pd.read_csv(str(expected_filename), keep_default_na=True)
```
(Changed `keep_default_na=False` to `keep_default_na=True`).

But I need to remember why `keep_default_na=False` was there in the first place, before doing this.
Just out of curiosity: "But why my first example didn't work?"

```
def test_array_with_nan(num_regression):
    data1 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.1]])
    data2 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.1]])
    num_regression.check({"data1": data1.flatten(), "data2": data2.flatten()})
```

Because in this case, the expected and obtained data are the same, and in your case, there's difference between the numerical values:

```
a = array(['115338.50895090569 <<<< HERE',  (...)
b = array(['115338.50895090567 <<<< HERE', (...)
```

So, in my case, even thought I'm comparing strings (not numbers), the values are the same, thus the test passes.
But if I generate the `csv` file and then change the test to the code below:

```
def test_array_with_nan(num_regression):
    data1 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.123456]])
    data2 = np.array([[1.1, 1.1, 1.1], [1.1, np.nan, 1.1]])
    num_regression.check({"data1": data1.flatten(), "data2": data2.flatten()})
```

I get the same error: `TypeError: unsupported operand type(s) for -: 'str' and 'str'`

Fix patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ca685ad..ca40515 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,8 @@
+2.3.1 (2022-01-18)
+------------------
+
+* `#84 <https://github.com/ESSS/pytest-regressions/pull/84>`__: (Bugfix) Properly handle empty and NaN values on num_regression and dataframe_regression.
+
 2.3.0 (2022-01-04)
 ------------------
 


Test patch:
diff --git a/src/pytest_regressions/dataframe_regression.py b/src/pytest_regressions/dataframe_regression.py
index 4d2b235..d758828 100644
--- a/src/pytest_regressions/dataframe_regression.py
+++ b/src/pytest_regressions/dataframe_regression.py
@@ -98,8 +98,8 @@ def _check_fn(self, obtained_filename, expected_filename):
 
         __tracebackhide__ = True
 
-        obtained_data = pd.read_csv(str(obtained_filename), keep_default_na=False)
-        expected_data = pd.read_csv(str(expected_filename), keep_default_na=False)
+        obtained_data = pd.read_csv(str(obtained_filename))
+        expected_data = pd.read_csv(str(expected_filename))
 
         comparison_tables_dict = {}
         for k in obtained_data.keys():
@@ -133,6 +133,12 @@ def _check_fn(self, obtained_filename, expected_filename):
                 )
             else:
                 not_close_mask = obtained_column.values != expected_column.values
+                # If Empty/NaN data is expected, then the values are equal:
+                not_close_mask[
+                    np.logical_and(
+                        pd.isna(obtained_column.values), pd.isna(expected_column.values)
+                    )
+                ] = False
 
             if np.any(not_close_mask):
                 diff_ids = np.where(not_close_mask)[0]
@@ -140,6 +146,9 @@ def _check_fn(self, obtained_filename, expected_filename):
                 diff_expected_data = expected_column[diff_ids]
                 if obtained_column.values.dtype == bool:
                     diffs = np.logical_xor(obtained_column, expected_column)[diff_ids]
+                elif obtained_column.values.dtype == object:
+                    diffs = diff_obtained_data.copy()
+                    diffs[:] = "?"
                 else:
                     diffs = np.abs(obtained_column - expected_column)[diff_ids]
 
@@ -154,6 +163,10 @@ def _check_fn(self, obtained_filename, expected_filename):
             error_msg += "To update values, use --force-regen option.\n\n"
             for k, comparison_table in comparison_tables_dict.items():
                 error_msg += f"{k}:\n{comparison_table}\n\n"
+            if obtained_column.values.dtype == object:
+                error_msg += (
+                    "WARNING: diffs for this kind of data type cannot be computed."
+                )
             raise AssertionError(error_msg)
 
     def _dump_fn(self, data_object, filename):
diff --git a/tests/test_dataframe_regression.py b/tests/test_dataframe_regression.py
index f17518d..d8af87e 100644
--- a/tests/test_dataframe_regression.py
+++ b/tests/test_dataframe_regression.py
@@ -238,10 +238,17 @@ def test_string_array(dataframe_regression):
     data1 = {"potato": ["delicious", "nutritive", "yummy"]}
     dataframe_regression.check(pd.DataFrame.from_dict(data1))
 
-    # TODO: The following fails with a confusing error message.
-    # Try wrong data
-    # data1 = {"potato": ["delicious", "nutritive", "yikes"]}
-    # dataframe_regression.check(pd.DataFrame.from_dict(data1))
+    data1 = {"potato": ["delicious", "nutritive", "yikes"]}
+    with pytest.raises(AssertionError) as excinfo:
+        dataframe_regression.check(pd.DataFrame.from_dict(data1))
+    obtained_error_msg = str(excinfo.value)
+    assert "Values are not sufficiently close." in obtained_error_msg
+    assert "To update values, use --force-regen option." in obtained_error_msg
+    assert "2           yikes           yummy    ?" in obtained_error_msg
+    assert (
+        "WARNING: diffs for this kind of data type cannot be computed"
+        in obtained_error_msg
+    )
 
 
 def test_non_pandas_dataframe(dataframe_regression):
@@ -254,7 +261,7 @@ def test_non_pandas_dataframe(dataframe_regression):
         dataframe_regression.check(data)
 
 
-def test_frame(dataframe_regression):
+def test_dataframe_with_empty_strings(dataframe_regression):
     df = pd.DataFrame.from_records(
         [
             {"a": "a", "b": "b"},
diff --git a/tests/test_dataframe_regression/test_frame.csv b/tests/test_dataframe_regression/test_dataframe_with_empty_strings.csv
similarity index 100%
rename from tests/test_dataframe_regression/test_frame.csv
rename to tests/test_dataframe_regression/test_dataframe_with_empty_strings.csv
diff --git a/tests/test_num_regression.py b/tests/test_num_regression.py
index 27abbc2..a517553 100644
--- a/tests/test_num_regression.py
+++ b/tests/test_num_regression.py
@@ -293,3 +293,14 @@ def test_simple_list_of_mostly_numbers(num_regression):
         match="Only objects that can be coerced to numpy arrays are valid for numeric_data_regression fixture.",
     ):
         num_regression.check({"data1": data1, "data2": data2})
+
+
+def test_array_dtype_stored_correctly(num_regression):
+    """
+    Related to bug #84, where data type was being stored incorrectly due to np.nan values in array.
+    Problem would only occur if the compared data has any difference, when evaluated as string.
+
+    ..see: https://github.com/ESSS/pytest-regressions/issues/84
+    """
+    data1 = np.array([1.100001, np.nan, 1.1])
+    num_regression.check({"data1": data1})
diff --git a/tests/test_num_regression/test_array_dtype_stored_correctly.csv b/tests/test_num_regression/test_array_dtype_stored_correctly.csv
new file mode 100644
index 0000000..4103410
--- /dev/null
+++ b/tests/test_num_regression/test_array_dtype_stored_correctly.csv
@@ -0,0 +1,4 @@
+,data1
+0,1.1000000000000001
+1,
+2,1.1000000000000001