Repository: mwaskom/seaborn

Issue:
Two bugs in catplot with kind="swarm"
```python
sns.catplot(
    tips, x="day", y="total_bill", col="time",
    height=3, kind="swarm",
)
```
![image](https://user-images.githubusercontent.com/315810/172026311-3a8af1bf-3bec-4d79-b1b2-0f114800d81a.png)

Issues:
- A palette is mapped to the x variable (`catplot` probably has some logic to do this to override `FacetGrid` passing `color`?
- The points on the left facet are not swarmed.

Interestingly, the latter issue does not arise when using `FacetGrid` directly:

```python
sns.FacetGrid(tips, col="time").map(sns.swarmplot, "day", "total_bill")
```
![image](https://user-images.githubusercontent.com/315810/172026349-8f5741f2-9b4e-457f-98b6-228ead7773c5.png)


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) Report
> Merging [#2845](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) (4be16d5) into [master](https://codecov.io/gh/mwaskom/seaborn/commit/de1ecf0e0d0064982ebf4f13e1b1afddd27c80ff?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) (de1ecf0) will **decrease** coverage by `4.12%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master    #2845      +/-   ##
==========================================
- Coverage   98.05%   93.92%   -4.13%     
==========================================
  Files          69       69              
  Lines       22187    22195       +8     
==========================================
- Hits        21755    20847     -908     
- Misses        432     1348     +916     
```


| [Impacted Files](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) | Coverage Δ | |
|---|---|---|
| [seaborn/\_oldcore.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9fb2xkY29yZS5weQ==) | `97.93% <100.00%> (-0.13%)` | :arrow_down: |
| [seaborn/categorical.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9jYXRlZ29yaWNhbC5weQ==) | `98.04% <100.00%> (-0.22%)` | :arrow_down: |
| [tests/test\_categorical.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-dGVzdHMvdGVzdF9jYXRlZ29yaWNhbC5weQ==) | `99.72% <100.00%> (-0.05%)` | :arrow_down: |
| [tests/test\_core.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-dGVzdHMvdGVzdF9jb3JlLnB5) | `99.47% <100.00%> (-0.21%)` | :arrow_down: |
| [seaborn/objects.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9vYmplY3RzLnB5) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [seaborn/matrix.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9tYXRyaXgucHk=) | `42.00% <0.00%> (-53.82%)` | :arrow_down: |
| [tests/test\_matrix.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-dGVzdHMvdGVzdF9tYXRyaXgucHk=) | `46.54% <0.00%> (-51.12%)` | :arrow_down: |
| [seaborn/\_docstrings.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9fZG9jc3RyaW5ncy5weQ==) | `87.50% <0.00%> (-12.50%)` | :arrow_down: |
| [seaborn/regression.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9yZWdyZXNzaW9uLnB5) | `86.19% <0.00%> (-11.97%)` | :arrow_down: |
| [seaborn/\_compat.py](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9fY29tcGF0LnB5) | `17.56% <0.00%> (-10.82%)` | :arrow_down: |
| ... and [17 more](https://codecov.io/gh/mwaskom/seaborn/pull/2845/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom). Last update [de1ecf0...4be16d5](https://codecov.io/gh/mwaskom/seaborn/pull/2845?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom).


Patch:
diff --git a/seaborn/_oldcore.py b/seaborn/_oldcore.py
index b23fd9fb..c9218c1f 100644
--- a/seaborn/_oldcore.py
+++ b/seaborn/_oldcore.py
@@ -93,7 +93,11 @@ class HueMapping(SemanticMapping):
 
         data = plotter.plot_data.get("hue", pd.Series(dtype=float))
 
-        if data.notna().any():
+        if data.isna().all():
+            if palette is not None:
+                msg = "Ignoring `palette` because no `hue` variable has been assigned."
+                warnings.warn(msg, stacklevel=4)
+        else:
 
             map_type = self.infer_map_type(
                 palette, norm, plotter.input_format, plotter.var_types["hue"]
diff --git a/seaborn/categorical.py b/seaborn/categorical.py
index 335674b5..7df91b05 100644
--- a/seaborn/categorical.py
+++ b/seaborn/categorical.py
@@ -167,6 +167,18 @@ class _CategoricalPlotterNew(_RelationalPlotter):
 
         return palette, hue_order
 
+    def _palette_without_hue_backcompat(self, palette, hue_order):
+        """Provide one cycle where palette= implies hue= when not provided"""
+        if "hue" not in self.variables and palette is not None:
+            msg = "Passing `palette` without assigning `hue` is deprecated."
+            warnings.warn(msg, FutureWarning, stacklevel=3)
+            self.legend = False
+            self.plot_data["hue"] = self.plot_data[self.cat_axis]
+            self.variables["hue"] = self.variables.get(self.cat_axis)
+            self.var_types["hue"] = self.var_types.get(self.cat_axis)
+            hue_order = self.var_levels.get(self.cat_axis)
+        return hue_order
+
     @property
     def cat_axis(self):
         return {"v": "x", "h": "y"}[self.orient]
@@ -352,19 +364,18 @@ class _CategoricalPlotterNew(_RelationalPlotter):
                 points.set_edgecolors(edgecolor)
 
             if not sub_data.empty:
-                point_collections[sub_data[self.cat_axis].iloc[0]] = points
+                point_collections[(ax, sub_data[self.cat_axis].iloc[0])] = points
 
         beeswarm = Beeswarm(
             width=width, orient=self.orient, warn_thresh=warn_thresh,
         )
-        for center, points in point_collections.items():
+        for (ax, center), points in point_collections.items():
             if points.get_offsets().shape[0] > 1:
 
                 def draw(points, renderer, *, center=center):
 
                     beeswarm(points, center)
 
-                    ax = points.axes
                     if self.orient == "h":
                         scalex = False
                         scaley = ax.get_autoscaley_on()
@@ -2798,6 +2809,7 @@ def stripplot(
 
     p._attach(ax)
 
+    hue_order = p._palette_without_hue_backcompat(palette, hue_order)
     palette, hue_order = p._hue_backcompat(color, palette, hue_order)
 
     color = _default_color(ax.scatter, hue, color, kwargs)
@@ -2922,6 +2934,7 @@ def swarmplot(
     if not p.has_xy_data:
         return ax
 
+    hue_order = p._palette_without_hue_backcompat(palette, hue_order)
     palette, hue_order = p._hue_backcompat(color, palette, hue_order)
 
     color = _default_color(ax.scatter, hue, color, kwargs)
@@ -3656,9 +3669,15 @@ def catplot(
         if not has_xy_data:
             return g
 
+        hue_order = p._palette_without_hue_backcompat(palette, hue_order)
         palette, hue_order = p._hue_backcompat(color, palette, hue_order)
         p.map_hue(palette=palette, order=hue_order, norm=hue_norm)
 
+        # Set a default color
+        # Otherwise each artist will be plotted separately and trip the color cycle
+        if hue is None and color is None:
+            color = "C0"
+
         if kind == "strip":
 
             # TODO get these defaults programmatically?


Test patch:
diff --git a/tests/test_categorical.py b/tests/test_categorical.py
index 7eee35ff..a5203713 100644
--- a/tests/test_categorical.py
+++ b/tests/test_categorical.py
@@ -2054,6 +2054,15 @@ class SharedScatterTests(SharedAxesLevelTests):
         for point_color in points.get_facecolors():
             assert to_rgb(point_color) in palette
 
+    def test_palette_with_hue_deprecation(self, long_df):
+        palette = "Blues"
+        with pytest.warns(FutureWarning, match="Passing `palette` without"):
+            ax = self.func(data=long_df, x="a", y=long_df["y"], palette=palette)
+        strips = ax.collections
+        colors = color_palette(palette, len(strips))
+        for strip, color in zip(strips, colors):
+            assert same_color(strip.get_facecolor()[0], color)
+
     def test_log_scale(self):
 
         x = [1, 10, 100, 1000]
@@ -2813,6 +2822,8 @@ class TestCatPlot(CategoricalFixture):
         g = cat.catplot(x="g", y="y", data=self.df, kind="strip")
         want_elements = self.g.unique().size
         assert len(g.ax.collections) == want_elements
+        for strip in g.ax.collections:
+            assert same_color(strip.get_facecolors(), "C0")
 
         g = cat.catplot(x="g", y="y", hue="h", data=self.df, kind="strip")
         want_elements = self.g.unique().size + self.h.unique().size
diff --git a/tests/test_core.py b/tests/test_core.py
index 3cd8b71a..a64d0637 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -316,6 +316,12 @@ class TestHueMapping:
         with pytest.raises(ValueError):
             HueMapping(p, norm="not a norm")
 
+    def test_hue_map_without_hue_dataa(self, long_df):
+
+        p = VectorPlotter(data=long_df, variables=dict(x="x", y="y"))
+        with pytest.warns(UserWarning, match="Ignoring `palette`"):
+            HueMapping(p, palette="viridis")
+
 
 class TestSizeMapping: