Repository: pandas-dev/pandas

Issue:
to_sparse and assign is giving the wrong results. 
Coming from [SO](https://stackoverflow.com/questions/48180206/assigning-a-column-to-a-sparsedataframe), when a boolean scalar is assigned to a new column using `assign`, it is leading to wrong results. Why is that so? 

`df = pd.DataFrame({"a":[1,2,3]}).to_sparse(fill_value=False)`
`df.assign(newcol=False)`

       a  newcol
    0  1     0.0
    1  2     0.0
    2  3     0.0

This works as expected if the dataframe is dense i.e 

   `df.to_dense().assign(newcol=False).to_sparse()`

       a  newcol
    0  1   False
    1  2   False
    2  3   False

Is this a bug or is this a genuine thing that's happening? 


Discussion:
xref to #18924 

This is quite tricky actually; you are starting with a ``SparseDataFrame`` which has some special properties; ``.assign()`` should convert this to a ``DataFrame``. 

So this is currently not supported.

cc @hexgnu 

Resolving patch:
diff --git a/pandas/core/sparse/array.py b/pandas/core/sparse/array.py
--- a/pandas/core/sparse/array.py
+++ b/pandas/core/sparse/array.py
@@ -26,7 +26,8 @@
     is_scalar, is_dtype_equal)
 from pandas.core.dtypes.cast import (
     maybe_convert_platform, maybe_promote,
-    astype_nansafe, find_common_type)
+    astype_nansafe, find_common_type, infer_dtype_from_scalar,
+    construct_1d_arraylike_from_scalar)
 from pandas.core.dtypes.missing import isna, notna, na_value_for_dtype
 
 import pandas._libs.sparse as splib
@@ -162,9 +163,9 @@ def __new__(cls, data, sparse_index=None, index=None, kind='integer',
                 data = np.nan
             if not is_scalar(data):
                 raise Exception("must only pass scalars with an index ")
-            values = np.empty(len(index), dtype='float64')
-            values.fill(data)
-            data = values
+            dtype = infer_dtype_from_scalar(data)[0]
+            data = construct_1d_arraylike_from_scalar(
+                data, len(index), dtype)
 
         if isinstance(data, ABCSparseSeries):
             data = data.values


Test patch:
diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py
--- a/pandas/tests/sparse/frame/test_frame.py
+++ b/pandas/tests/sparse/frame/test_frame.py
@@ -1257,3 +1257,14 @@ def test_quantile_multi(self):
 
         tm.assert_frame_equal(result, dense_expected)
         tm.assert_sp_frame_equal(result, sparse_expected)
+
+    def test_assign_with_sparse_frame(self):
+        # GH 19163
+        df = pd.DataFrame({"a": [1, 2, 3]})
+        res = df.to_sparse(fill_value=False).assign(newcol=False)
+        exp = df.assign(newcol=False).to_sparse(fill_value=False)
+
+        tm.assert_sp_frame_equal(res, exp)
+
+        for column in res.columns:
+            assert type(res[column]) is SparseSeries
diff --git a/pandas/tests/sparse/test_array.py b/pandas/tests/sparse/test_array.py
--- a/pandas/tests/sparse/test_array.py
+++ b/pandas/tests/sparse/test_array.py
@@ -113,6 +113,21 @@ def test_constructor_spindex_dtype(self):
         assert arr.dtype == np.int64
         assert arr.fill_value == 0
 
+    @pytest.mark.parametrize('scalar,dtype', [
+        (False, bool),
+        (0.0, 'float64'),
+        (1, 'int64'),
+        ('z', 'object')])
+    def test_scalar_with_index_infer_dtype(self, scalar, dtype):
+        # GH 19163
+        arr = SparseArray(scalar, index=[1, 2, 3], fill_value=scalar)
+        exp = SparseArray([scalar, scalar, scalar], fill_value=scalar)
+
+        tm.assert_sp_array_equal(arr, exp)
+
+        assert arr.dtype == dtype
+        assert exp.dtype == dtype
+
     def test_sparseseries_roundtrip(self):
         # GH 13999
         for kind in ['integer', 'block']: