Repository: mwaskom/seaborn

Issue:
histplot ignoring multiple= when hue argument is a numpy array or list
e.g.

```
sns.histplot(x=tips["total_bill"], hue=tips["time"].to_numpy(), multiple="dodge")
```

It works fine when passing a pandas Series directly or referencing a column in an array.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=h1) Report
> Merging [#2462](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=desc) (dc30328) into [master](https://codecov.io/gh/mwaskom/seaborn/commit/d0acb8cd8308d66cb95795e2fdad99c4220403d8?el=desc) (d0acb8c) will **decrease** coverage by `5.93%`.
> The diff coverage is `83.33%`.

[![Impacted file tree graph](https://codecov.io/gh/mwaskom/seaborn/pull/2462/graphs/tree.svg?width=650&height=150&src=pr&token=SLuzHnA9Bl)](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2462      +/-   ##
==========================================
- Coverage   97.44%   91.50%   -5.94%     
==========================================
  Files          17       17              
  Lines        6312     6313       +1     
==========================================
- Hits         6151     5777     -374     
- Misses        161      536     +375     
```


| [Impacted Files](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [seaborn/distributions.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9kaXN0cmlidXRpb25zLnB5) | `95.78% <83.33%> (-0.56%)` | :arrow_down: |
| [seaborn/matrix.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9tYXRyaXgucHk=) | `42.44% <0.00%> (-53.42%)` | :arrow_down: |
| [seaborn/algorithms.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9hbGdvcml0aG1zLnB5) | `87.50% <0.00%> (-12.50%)` | :arrow_down: |
| [seaborn/regression.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9yZWdyZXNzaW9uLnB5) | `85.53% <0.00%> (-12.27%)` | :arrow_down: |
| [seaborn/\_statistics.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9fc3RhdGlzdGljcy5weQ==) | `96.48% <0.00%> (-3.52%)` | :arrow_down: |
| [seaborn/\_docstrings.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9fZG9jc3RyaW5ncy5weQ==) | `97.22% <0.00%> (-2.78%)` | :arrow_down: |
| [seaborn/utils.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi91dGlscy5weQ==) | `91.83% <0.00%> (-2.73%)` | :arrow_down: |
| [seaborn/\_core.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9fY29yZS5weQ==) | `98.06% <0.00%> (-0.56%)` | :arrow_down: |
| [seaborn/categorical.py](https://codecov.io/gh/mwaskom/seaborn/pull/2462/diff?src=pr&el=tree#diff-c2VhYm9ybi9jYXRlZ29yaWNhbC5weQ==) | `98.32% <0.00%> (-0.22%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=footer). Last update [d0acb8c...dc30328](https://codecov.io/gh/mwaskom/seaborn/pull/2462?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/doc/releases/v0.12.0.txt b/doc/releases/v0.12.0.txt
index 82ab2a9a..6ae8da68 100644
--- a/doc/releases/v0.12.0.txt
+++ b/doc/releases/v0.12.0.txt
@@ -18,13 +18,17 @@ v0.12.0 (Unreleased)
 
 - |Enhancement| In :func:`histplot`, added `stat="percent"` as an option for normalization such that bar heights sum to 100 (:pr:`2461`).
 
+- |Enhancement| |Fix| Improved integration with the matplotlib color cycle in most axes-level functions (:pr:`2449`).
+
 - |Fix| In :func:`lineplot, allowed the `dashes` keyword to set the style of a line without mapping a `style` variable (:pr:`2449`).
 
 - |Fix| In :func:`rugplot`, fixed a bug that prevented the use of datetime data (:pr:`2458`).
 
 - |Fix| In :func:`histplot` and :func:`kdeplot`, fixed a bug where the `alpha` parameter was ignored when `fill=False` (:pr:`2460`).
 
-- |Fix| |Enhancement| Improved integration with the matplotlib color cycle in most axes-level functions (:pr:`2449`).
+- |Fix| In :func:`histplot` and :func:`kdeplot`, fixed a bug where the `multiple` was ignored when `hue` was provided as a vector without a name (:pr:`2462`).
+
+ - |Defaults| In :func:`displot`, the default alpha value now adjusts to a provided `multiple` parameter even when `hue` is not assigned (:pr:`2462`).
 
 - Made `scipy` an optional dependency and added `pip install seaborn[all]` as a method for ensuring the availability of compatible `scipy` and `statsmodels` libraries at install time. This has a few minor implications for existing code, which are explained in the Github pull request (:pr:`2398`).
 
diff --git a/doc/tutorial/distributions.ipynb b/doc/tutorial/distributions.ipynb
index 843ee9bb..59452c5a 100644
--- a/doc/tutorial/distributions.ipynb
+++ b/doc/tutorial/distributions.ipynb
@@ -245,7 +245,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "sns.displot(penguins, x=\"flipper_length_mm\", col=\"sex\", multiple=\"dodge\")"
+    "sns.displot(penguins, x=\"flipper_length_mm\", col=\"sex\")"
    ]
   },
   {
diff --git a/seaborn/distributions.py b/seaborn/distributions.py
index 65d86277..0b07d687 100644
--- a/seaborn/distributions.py
+++ b/seaborn/distributions.py
@@ -225,8 +225,16 @@ class _DistributionPlotter(VectorPlotter):
         return discrete
 
     def _resolve_multiple(self, curves, multiple):
+        """Modify the density data structure to handle multiple densities."""
+
+        # Default baselines have all densities starting at 0
+        baselines = {k: np.zeros_like(v) for k, v in curves.items()}
+
+        # TODO we should have some central clearinghouse for checking if any
+        # "grouping" (terminnology?) semantics have been assigned
+        if "hue" not in self.variables:
+            return curves, baselines
 
-        # Modify the density data structure to handle multiple densities
         if multiple in ("stack", "fill"):
 
             # Setting stack or fill means that the curves share a
@@ -261,11 +269,6 @@ class _DistributionPlotter(VectorPlotter):
                                            .shift(1, axis=1)
                                            .fillna(0))
 
-        else:
-
-            # All densities will start at 0
-            baselines = {k: np.zeros_like(v) for k, v in curves.items()}
-
         if multiple == "dodge":
 
             # Account for the unique semantic (non-faceting) levels
@@ -413,13 +416,6 @@ class _DistributionPlotter(VectorPlotter):
         else:
             common_norm = False
 
-        # Turn multiple off if no hue or if hue exists but is redundant with faceting
-        facet_vars = [self.variables.get(var, None) for var in ["row", "col"]]
-        if "hue" not in self.variables:
-            multiple = None
-        elif self.variables["hue"] in facet_vars:
-            multiple = None
-
         # Estimate the smoothed kernel densities, for use later
         if kde:
             # TODO alternatively, clip at min/max bins?
@@ -503,7 +499,8 @@ class _DistributionPlotter(VectorPlotter):
 
         # Default alpha should depend on other parameters
         if fill:
-            if multiple == "layer":
+            # Note: will need to account for other grouping semantics if added
+            if "hue" in self.variables and multiple == "layer":
                 default_alpha = .5 if element == "bars" else .25
             elif kde:
                 default_alpha = .5
@@ -903,7 +900,7 @@ class _DistributionPlotter(VectorPlotter):
             log_scale,
         )
 
-        # Note: raises when no hue and multiple != layer. A problem?
+        # Adjust densities based on the `multiple` rule
         densities, baselines = self._resolve_multiple(densities, multiple)
 
         # Control the interaction with autoscaling by defining sticky_edges
@@ -916,9 +913,11 @@ class _DistributionPlotter(VectorPlotter):
         else:
             sticky_support = []
 
-        # XXX unfilled kdeplot is ignoring
         if fill:
-            default_alpha = .25 if multiple == "layer" else .75
+            if multiple == "layer":
+                default_alpha = .25
+            else:
+                default_alpha = .75
         else:
             default_alpha = 1
         alpha = plot_kws.pop("alpha", default_alpha)  # TODO make parameter?


Test patch:
diff --git a/seaborn/tests/test_distributions.py b/seaborn/tests/test_distributions.py
index a2f8628e..4ab3dfd9 100644
--- a/seaborn/tests/test_distributions.py
+++ b/seaborn/tests/test_distributions.py
@@ -1217,6 +1217,17 @@ class TestHistPlotUnivariate(SharedAxesLevelTests):
         assert_array_almost_equal(layer_xs[1], dodge_xs[1])
         assert_array_almost_equal(layer_xs[0], dodge_xs[0] - bw / 2)
 
+    def test_hue_as_numpy_dodged(self, long_df):
+        # https://github.com/mwaskom/seaborn/issues/2452
+
+        ax = histplot(
+            long_df,
+            x="y", hue=long_df["a"].to_numpy(),
+            multiple="dodge", bins=1,
+        )
+        # Note hue order reversal
+        assert ax.patches[1].get_x() < ax.patches[0].get_x()
+
     def test_multiple_input_check(self, flat_series):
 
         with pytest.raises(ValueError, match="`multiple` must be"):