Repository: mwaskom/seaborn

Issue:
catplot with redundant hue assignment creates empty legend with title
```python
sns.catplot(tips, x="day", y="total_bill", hue="day", col="sex", row="smoker", kind="box", height=3)
```
![image](https://github.com/mwaskom/seaborn/assets/315810/53b0af1e-e133-46bb-92bd-78c91db0221b)

Set `legend=False` to workaround, but with default `legend='auto'` this should be disabled due to the redundancy.

Discussion:
mwaskom: Thanks for taking a crack at this, but it's not a sufficient change. First, that simple comparison will raise if any of the variables are specified using vector data (i.e. pandas series or numpy arrays, where the `==` operator works element-wise and returns a vector). Second, we want to continue supporting a (redundant) legend when the user asks for it explicitly by passing `legend=True`. You'll want to take a look at how this is currently handled in the axes-level functions, i.e. in this method: https://github.com/mwaskom/seaborn/blob/863539d71e281d88a93de64581827b166d9e2f22/seaborn/categorical.py#L411
abdulwaheedsoudagar: > Thanks for taking a crack at this, but it's not a sufficient change. First, that simple comparison will raise if any of the variables are specified using vector data (i.e. pandas series or numpy arrays, where the `==` operator works element-wise and returns a vector). Second, we want to continue supporting a (redundant) legend when the user asks for it explicitly by passing `legend=True`. You'll want to take a look at how this is currently handled in the axes-level functions, i.e. in this method:
> 
> https://github.com/mwaskom/seaborn/blob/863539d71e281d88a93de64581827b166d9e2f22/seaborn/categorical.py#L411

Thank you for responding. I have a brief inquiry. The problem of the legend not showing up occurs when the setting is "legend = auto." Yet, when it's set to "legend = full" or "legend = brief," it appears correctly, when hue and x values are same. Should the legend only be displayed when it's set to "True", or also when it's "full" or "brief"?
mwaskom: > Should the legend only be displayed when it's set to "True", or also when it's "full" or "brief"?

`"True"` (as a string type) is not a valid argument for `legend` but `True` (as a boolean type) is. The only place where we want conditional logic about a redundant legend is when `legend="auto"`. Otherwise it should appear when requested (with `True`, `"brief"`, or `"full"`) and be suppressed when not (with `False`).
abdulwaheedsoudagar: > > Should the legend only be displayed when it's set to "True", or also when it's "full" or "brief"?
> 
> `"True"` (as a string type) is not a valid argument for `legend` but `True` (as a boolean type) is. The only place where we want conditional logic about a redundant legend is when `legend="auto"`. Otherwise it should appear when requested (with `True`, `"brief"`, or `"full"`) and be suppressed when not (with `False`).

Thanks, Based on your comment I have modified the code, legend title is disabled based on x and y values and hue datatype. 
In function _configure_legend, legend title is not decided, it is decieded in line 3098, so I added conditions for this https://github.com/abdulwaheedsoudagar/seaborn/blob/d4ec331b57a58287b05cc49191b4b5b54797118f/seaborn/categorical.py#L3098 statement.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) Report
> Merging [#3540](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) (d4ec331) into [master](https://app.codecov.io/gh/mwaskom/seaborn/commit/863539d71e281d88a93de64581827b166d9e2f22?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) (863539d) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540/graphs/tree.svg?width=650&height=150&src=pr&token=SLuzHnA9Bl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom)](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom)

```diff
@@           Coverage Diff           @@
##           master    #3540   +/-   ##
=======================================
  Coverage   98.34%   98.34%           
=======================================
  Files          75       75           
  Lines       24605    24612    +7     
=======================================
+ Hits        24197    24204    +7     
  Misses        408      408           
```


| [Files](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom) | Coverage Δ | |
|---|---|---|
| [seaborn/categorical.py](https://app.codecov.io/gh/mwaskom/seaborn/pull/3540?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Michael+Waskom#diff-c2VhYm9ybi9jYXRlZ29yaWNhbC5weQ==) | `98.91% <100.00%> (+<0.01%)` | :arrow_up: |

</details>
mwaskom: Can you please add a test? Essentially you want to assert that the `FacetGrid` returned by `catplot` under this condition has a null `legend` attribute.
abdulwaheedsoudagar: > Can you please add a test? Essentially you want to assert that the `FacetGrid` returned by `catplot` under this condition has a null `legend` attribute.

Sure, I will add

Patch:
diff --git a/seaborn/categorical.py b/seaborn/categorical.py
index bb40d5d3..6b4d85da 100644
--- a/seaborn/categorical.py
+++ b/seaborn/categorical.py
@@ -409,12 +409,10 @@ class _CategoricalPlotter(VectorPlotter):
                     data[col] = inv(data[col])
 
     def _configure_legend(self, ax, func, common_kws=None, semantic_kws=None):
-
         if self.legend == "auto":
             show_legend = not self._redundant_hue and self.input_format != "wide"
         else:
             show_legend = bool(self.legend)
-
         if show_legend:
             self.add_legend_data(ax, func, common_kws, semantic_kws=semantic_kws)
             handles, _ = ax.get_legend_handles_labels()
@@ -2830,7 +2828,11 @@ def catplot(
         if saturation < 1:
             color = desaturate(color, saturation)
 
-    edgecolor = p._complement_color(kwargs.pop("edgecolor", default), color, p._hue_map)
+    if kind in ["strip", "swarm"]:
+        kwargs = _normalize_kwargs(kwargs, mpl.collections.PathCollection)
+        kwargs["edgecolor"] = p._complement_color(
+            kwargs.pop("edgecolor", default), color, p._hue_map
+        )
 
     width = kwargs.pop("width", 0.8)
     dodge = kwargs.pop("dodge", False if kind in undodged_kinds else "auto")
@@ -2841,7 +2843,6 @@ def catplot(
 
         jitter = kwargs.pop("jitter", True)
         plot_kws = kwargs.copy()
-        plot_kws["edgecolor"] = edgecolor
         plot_kws.setdefault("zorder", 3)
         plot_kws.setdefault("linewidth", 0)
         if "s" not in plot_kws:
@@ -2858,7 +2859,6 @@ def catplot(
 
         warn_thresh = kwargs.pop("warn_thresh", .05)
         plot_kws = kwargs.copy()
-        plot_kws["edgecolor"] = edgecolor
         plot_kws.setdefault("zorder", 3)
         if "s" not in plot_kws:
             plot_kws["s"] = plot_kws.pop("size", 5) ** 2
@@ -3095,7 +3095,11 @@ def catplot(
         g._update_legend_data(ax)
         ax.legend_ = None
 
-    if legend and "hue" in p.variables and p.input_format == "long":
+    if legend == "auto":
+        show_legend = not p._redundant_hue and p.input_format != "wide"
+    else:
+        show_legend = bool(legend)
+    if show_legend:
         g.add_legend(title=p.variables.get("hue"), label_order=hue_order)
 
     if data is not None:


Test patch:
diff --git a/tests/test_categorical.py b/tests/test_categorical.py
index 7031d094..cc3fa5b5 100644
--- a/tests/test_categorical.py
+++ b/tests/test_categorical.py
@@ -2307,7 +2307,7 @@ class TestBarPlot(SharedAggTests):
             dict(data="long", x="a", y="y", errorbar=("pi", 50)),
             dict(data="long", x="a", y="y", errorbar=None),
             dict(data="long", x="a", y="y", capsize=.3, err_kws=dict(c="k")),
-            dict(data="long", x="a", y="y", color="blue", ec="green", alpha=.5),
+            dict(data="long", x="a", y="y", color="blue", edgecolor="green", alpha=.5),
         ]
     )
     def test_vs_catplot(self, long_df, wide_df, null_df, flat_series, kwargs):
@@ -3125,6 +3125,14 @@ class TestCatPlot(CategoricalFixture):
         with pytest.raises(ValueError, match="Invalid `kind`: 'wrong'"):
             catplot(long_df, kind="wrong")
 
+    def test_legend_with_auto(self):
+
+        g1 = catplot(self.df, x="g", y="y", hue="g", legend='auto')
+        assert g1._legend is None
+
+        g2 = catplot(self.df, x="g", y="y", hue="g", legend=True)
+        assert g2._legend is not None
+
 
 class TestBeeswarm: