BUG: `DataFrame.sparse.from_spmatrix` hard codes an invalid ``fill_value`` for certain subtypes
State: closed

- [x] Closes #59063.
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature.
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.


Labels:
name: Sparse
description: Sparse Data Type

Comments:
Thanks @christopher-titchen 
This seems to be responsible for a breaking change in a workflow of mine.  We consume the output of a sklearn OneHotEncoder, which is sparse with float type, and instantiate a sparse pandas frame from it.  That used to produce values of 1.0 and 0.0, and now produces instead 1.0 and np.nan.

It doesn't look like the sparse instantiation allows the fill_value; is there another easy way we can adjust to the new behavior?  (Casting to integers would be fine for this particular case, although our code is more generic than just OneHotEncoder results, so I'm not positive that's generalizable.)
> This seems to be responsible for a breaking change in a workflow of mine. We consume the output of a sklearn OneHotEncoder, which is sparse with float type, and instantiate a sparse pandas frame from it. That used to produce values of 1.0 and 0.0, and now produces instead 1.0 and np.nan.
> 
> It doesn't look like the sparse instantiation allows the fill_value; is there another easy way we can adjust to the new behavior? (Casting to integers would be fine for this particular case, although our code is more generic than just OneHotEncoder results, so I'm not positive that's generalizable.)

Hi Ben. I have also used a similar workflow in the past, so I totally understand where you are coming from. It can be convenient to store the sparse output from a ``OneHotEncoder`` in a ``DataFrame`` if you want row and column labels with reasonably fast slicing and do not need to perform arithmetic operations and matrix vector products. I originally added a ``fill_value`` parameter to ``from_spmatrix`` in this PR for use cases like this, but I had to revert the changes, so we will have to raise a separate issue to potentially change the method signature going forward.

However, after a quick look, it seems pretty straightforward to fix this issue in your workflow for now by using ``DataFrame.fillna(0.)`` to fill ``np.nan`` with ``0.`` and correctly change the ``fill_value`` attribute for each ``SparseDtype`` column without modifying the index. I added an example below with an identity matrix. I hope this helps! 😄

<img width="402" alt="image" src="https://github.com/pandas-dev/pandas/assets/109701765/6b46ee88-10c8-4132-a9bb-5a76825d7410">
@christopher-titchen thanks. Filling with zero (after) is a reasonable alternative to casting to integers (before), since apparently pandas is clever enough to change the fill_value. But the broader-setting problem (that my last sentence before hinted at) persists; I should've made that the scope to start with. I'll make a new issue with that in mind.