Repository: pandas-dev/pandas
Pull request: BUG: all-na corner case for str.cat
- [x] closes #24044, split off from #23167
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

Problem statement:
BUG: .str.cat for all-na corner case
A corner case that wrongly casts to FloatIndex if the result is all-na (e.g. due to alignment)
```
import pandas as pd
>>> idx = pd.Index(['a', 'b', 'c'])
>>> idz = pd.Index(['d', 'e', 'f'])
>>> idx.str.cat(idz, join='left')
Float64Index([nan, nan, nan], dtype='float64')
```

Hints from issue discussion:

Commit message: BUG: all-na corner case for str.cat

Patch to review:
diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst
index fc505128a2e20..57930a3cd25b2 100644
--- a/doc/source/whatsnew/v0.24.0.rst
+++ b/doc/source/whatsnew/v0.24.0.rst
@@ -1364,6 +1364,7 @@ Strings
 - Bug in :meth:`Index.str.partition` was not nan-safe (:issue:`23558`).
 - Bug in :meth:`Index.str.split` was not nan-safe (:issue:`23677`).
 - Bug :func:`Series.str.contains` not respecting the ``na`` argument for a ``Categorical`` dtype ``Series`` (:issue:`22158`)
+- Bug in :meth:`Index.str.cat` when the result contained only NaN (:issue:`24044`)
 
 Interval
 ^^^^^^^^
diff --git a/pandas/core/strings.py b/pandas/core/strings.py
index 0b791f6f91aa3..995700e79cb50 100644
--- a/pandas/core/strings.py
+++ b/pandas/core/strings.py
@@ -2260,9 +2260,11 @@ def cat(self, others=None, sep=None, na_rep=None, join=None):
             result = cat_core(all_cols, sep)
 
         if isinstance(self._orig, Index):
-            result = Index(result, name=self._orig.name)
+            # add dtype for case that result is all-NA
+            result = Index(result, dtype=object, name=self._orig.name)
         else:  # Series
-            result = Series(result, index=data.index, name=self._orig.name)
+            result = Series(result, dtype=object, index=data.index,
+                            name=self._orig.name)
         return result
 
     _shared_docs['str_split'] = ("""
diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py
index 117984ce89743..ba9d0ae0e2e89 100644
--- a/pandas/tests/test_strings.py
+++ b/pandas/tests/test_strings.py
@@ -630,11 +630,30 @@ def test_str_cat_align_mixed_inputs(self, join):
         with pytest.raises(ValueError, match=rgx):
             s.str.cat([t, z], join=join)
 
-    def test_str_cat_raises(self):
-        # non-strings hiding behind object dtype
-        s = Series([1, 2, 3, 4], dtype='object')
-        with pytest.raises(TypeError, match="unsupported operand type.*"):
-            s.str.cat(s)
+    @pytest.mark.parametrize('box', [Series, Index])
+    @pytest.mark.parametrize('other', [Series, Index])
+    def test_str_cat_all_na(self, box, other):
+        # GH 24044
+
+        # check that all NaNs in caller / target work
+        s = Index(['a', 'b', 'c', 'd'])
+        s = s if box == Index else Series(s, index=s)
+        t = other([np.nan] * 4, dtype='object')
+        # add index of s for alignment
+        t = t if other == Index else Series(t, index=s)
+
+        # all-NA target
+        expected = Index([np.nan] * 4, dtype='object')
+        expected = expected if box == Index else Series(expected,
+                                                        index=s.index)
+        result = s.str.cat(t, join='left')
+        assert_series_or_index_equal(result, expected)
+
+        # all-NA caller (only for Series)
+        if other == Series:
+            expected = Series([np.nan] * 4, dtype='object', index=t.index)
+            result = t.str.cat(s, join='left')
+            tm.assert_series_equal(result, expected)
 
     def test_str_cat_special_cases(self):
         s = Series(['a', 'b', 'c', 'd'])


Code review comments:
diff_hunk: @@ -630,11 +630,30 @@ def test_str_cat_align_mixed_inputs(self, join):
         with pytest.raises(ValueError, match=rgx):
             s.str.cat([t, z], join=join)
 
-    def test_str_cat_raises(self):
-        # non-strings hiding behind object dtype
-        s = Series([1, 2, 3, 4], dtype='object')
-        with pytest.raises(TypeError, match="unsupported operand type.*"):
-            s.str.cat(s)
+    @pytest.mark.parametrize('box', [Series, Index])
+    @pytest.mark.parametrize('other', [Series, Index])
+    def test_str_cat_all_na(self, box, other):
+        # GH 24044
+
+        # check that all NaNs in caller / target work
+        s = Index(['a', 'b', 'c', 'd'])
+        s = s if box == Index else Series(s, index=s)
+        t = other([np.nan] * 4, dtype='object')
+        # add index of s for alignment
+        t = t if other == Index else Series(t, index=s)
+
+        # all-NA target
+        expected = Index([np.nan] * 4, dtype='object')
+        expected = expected if box == Index else Series(expected,
path: pandas/tests/test_strings.py
text: @user1:
use the
```
if box == Series:
     .....
``` 
for this one