Skip to content

Commit

Permalink
Rename Scatter mark to Dots (#2942)
Browse files Browse the repository at this point in the history
* Rename Scatter->Dots and make Dot(s) inherit from common base

* Rename _marks/scatter to _marks/dot

* Update nextgen demo

* Formatting

* More formatting
  • Loading branch information
mwaskom authored Aug 7, 2022
1 parent 8dad74c commit a1ede5e
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 128 deletions.
2 changes: 1 addition & 1 deletion doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ Mark objects
Bar
Bars
Dot
Dots
Interval
Path
Paths
Line
Lines
Ribbon
Scatter

Stat objects
~~~~~~~~~~~~
Expand Down
42 changes: 21 additions & 21 deletions doc/nextgen/demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"metadata": {},
"outputs": [],
"source": [
"so.Plot(tips, x=\"total_bill\", y=\"tip\").add(so.Scatter())"
"so.Plot(tips, x=\"total_bill\", y=\"tip\").add(so.Dots())"
]
},
{
Expand All @@ -111,7 +111,7 @@
"metadata": {},
"outputs": [],
"source": [
"so.Plot(tips).add(so.Scatter(), x=\"total_bill\", y=\"tip\")"
"so.Plot(tips).add(so.Dots(), x=\"total_bill\", y=\"tip\")"
]
},
{
Expand All @@ -131,8 +131,8 @@
"source": [
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
" .add(so.Scatter(color=\".6\"), data=tips.query(\"size != 2\"))\n",
" .add(so.Scatter(), data=tips.query(\"size == 2\"))\n",
" .add(so.Dots(color=\".6\"), data=tips.query(\"size != 2\"))\n",
" .add(so.Dots(), data=tips.query(\"size == 2\"))\n",
")"
]
},
Expand All @@ -153,7 +153,7 @@
"source": [
"(\n",
" so.Plot(tips.to_dict(), x=\"total_bill\")\n",
" .add(so.Scatter(), y=tips[\"tip\"].to_numpy())\n",
" .add(so.Dots(), y=tips[\"tip\"].to_numpy())\n",
")"
]
},
Expand All @@ -172,7 +172,7 @@
"metadata": {},
"outputs": [],
"source": [
"so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"time\").add(so.Scatter())"
"so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"time\").add(so.Dots())"
]
},
{
Expand All @@ -192,7 +192,7 @@
"source": [
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\", fill=\"time\")\n",
" .add(so.Scatter(fillalpha=.8))\n",
" .add(so.Dots(fillalpha=.8))\n",
")"
]
},
Expand Down Expand Up @@ -376,7 +376,7 @@
"\n",
"### Overplotting resolution: the Move\n",
"\n",
"Existing seaborn functions have parameters that allow adjustments for overplotting, such as `dodge=` in several categorical functions, `jitter=` in several functions based on scatterplots, and the `multiple=` parameter in distribution functions. In the new interface, those adjustments are abstracted away from the particular visual representation into the concept of a `Move`:"
"Existing seaborn functions have parameters that allow adjustments for overplotting, such as `dodge=` in several categorical functions, `jitter=` in several functions based on scatter plots, and the `multiple=` parameter in distribution functions. In the new interface, those adjustments are abstracted away from the particular visual representation into the concept of a `Move`:"
]
},
{
Expand Down Expand Up @@ -523,7 +523,7 @@
"(\n",
" so.Plot(planets, x=\"mass\", y=\"distance\")\n",
" .scale(x=\"log\", y=\"log\")\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -545,7 +545,7 @@
"(\n",
" so.Plot(planets, x=\"mass\", y=\"distance\", color=\"orbital_period\")\n",
" .scale(x=\"log\", y=\"log\", color=\"rocket\")\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -571,7 +571,7 @@
" y=so.Continuous(transform=\"log\").tick(at=[3, 10, 30, 100, 300]),\n",
" color=so.Continuous(\"rocket\", transform=\"log\"),\n",
" )\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -596,7 +596,7 @@
" y=\"log\",\n",
" color=so.Nominal([\"b\", \"g\"], order=[\"Radial Velocity\", \"Transit\"])\n",
" )\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -618,7 +618,7 @@
"(\n",
" so.Plot(planets, x=\"distance\", y=\"orbital_period\", pointsize=\"mass\")\n",
" .scale(x=\"log\", y=\"log\", pointsize=None)\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand Down Expand Up @@ -693,7 +693,7 @@
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
" .facet(\"time\", order=[\"Dinner\", \"Lunch\"])\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -715,8 +715,8 @@
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
" .facet(col=\"day\")\n",
" .add(so.Scatter(color=\".75\"), col=None)\n",
" .add(so.Scatter(), color=\"day\")\n",
" .add(so.Dots(color=\".75\"), col=None)\n",
" .add(so.Dots(), color=\"day\")\n",
" .configure(figsize=(7, 3))\n",
")"
]
Expand Down Expand Up @@ -971,7 +971,7 @@
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
" .on(ax)\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
")"
]
},
Expand All @@ -994,7 +994,7 @@
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
" .on(f)\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
" .facet(\"time\")\n",
")"
]
Expand All @@ -1018,14 +1018,14 @@
"sf1, sf2 = f.subfigures(1, 2)\n",
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\")\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
" .on(sf1)\n",
" .plot()\n",
")\n",
"(\n",
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\")\n",
" .facet(\"day\", wrap=2)\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
" .on(sf2)\n",
" .plot()\n",
")"
Expand Down Expand Up @@ -1056,7 +1056,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.9.13"
}
},
"nbformat": 4,
Expand Down
4 changes: 2 additions & 2 deletions doc/nextgen/index.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
" color=\"smoker\", marker=\"smoker\", pointsize=\"size\",\n",
" )\n",
" .facet(\"time\")\n",
" .add(so.Scatter())\n",
" .add(so.Dots())\n",
" .configure(figsize=(7, 4))\n",
")"
]
Expand Down Expand Up @@ -104,7 +104,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.9.13"
}
},
"nbformat": 4,
Expand Down
3 changes: 0 additions & 3 deletions seaborn/_core/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,6 @@ def mapping(x):
class PointSize(IntervalProperty):
"""Size (diameter) of a point mark, in points, with scaling by area."""
_default_range = 2, 8 # TODO use rcparams?
# TODO N.B. both Scatter and Dot use this but have different expected sizes
# Is that something we need to handle? Or assume Dot size rarely scaled?
# Also will Line marks have a PointSize property?

def _forward(self, values):
"""Square native values to implement linear scaling of point area."""
Expand Down
78 changes: 42 additions & 36 deletions seaborn/_marks/scatter.py → seaborn/_marks/dot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,7 @@


@dataclass
class Scatter(Mark):
"""
A point mark defined by strokes with optional fills.
"""
# TODO retype marker as MappableMarker
marker: MappableString = Mappable(rc="scatter.marker", grouping=False)
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
pointsize: MappableFloat = Mappable(3, grouping=False) # TODO rcParam?
color: MappableColor = Mappable("C0", grouping=False)
alpha: MappableFloat = Mappable(1, grouping=False) # TODO auto alpha?
fill: MappableBool = Mappable(True, grouping=False)
fillcolor: MappableColor = Mappable(depend="color", grouping=False)
fillalpha: MappableFloat = Mappable(.2, grouping=False)
class DotBase(Mark):

def _resolve_paths(self, data):

Expand All @@ -60,28 +48,14 @@ def _resolve_properties(self, data, scales):

resolved = resolve_properties(self, data, scales)
resolved["path"] = self._resolve_paths(resolved)
resolved["size"] = resolved["pointsize"] ** 2

if isinstance(data, dict): # TODO need a better way to check
if isinstance(data, dict): # Properties for single dot
filled_marker = resolved["marker"].is_filled()
else:
filled_marker = [m.is_filled() for m in resolved["marker"]]

resolved["linewidth"] = resolved["stroke"]
resolved["fill"] = resolved["fill"] * filled_marker
resolved["size"] = resolved["pointsize"] ** 2

resolved["edgecolor"] = resolve_color(self, data, "", scales)
resolved["facecolor"] = resolve_color(self, data, "fill", scales)

# Because only Dot, and not Scatter, has an edgestyle
resolved.setdefault("edgestyle", (0, None))

fc = resolved["facecolor"]
if isinstance(fc, tuple):
resolved["facecolor"] = fc[0], fc[1], fc[2], fc[3] * resolved["fill"]
else:
fc[:, 3] = fc[:, 3] * resolved["fill"] # TODO Is inplace mod a problem?
resolved["facecolor"] = fc

return resolved

Expand Down Expand Up @@ -129,33 +103,31 @@ def _legend_artist(
)


# TODO change this to depend on ScatterBase?
@dataclass
class Dot(Scatter):
class Dot(DotBase):
"""
A point mark defined by shape with optional edges.
A mark suitable for dot plots or less-dense scatterplots.
"""
marker: MappableString = Mappable("o", grouping=False)
pointsize: MappableFloat = Mappable(6, grouping=False) # TODO rcParam?
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
color: MappableColor = Mappable("C0", grouping=False)
alpha: MappableFloat = Mappable(1, grouping=False)
fill: MappableBool = Mappable(True, grouping=False)
edgecolor: MappableColor = Mappable(depend="color", grouping=False)
edgealpha: MappableFloat = Mappable(depend="alpha", grouping=False)
pointsize: MappableFloat = Mappable(6, grouping=False) # TODO rcParam?
edgewidth: MappableFloat = Mappable(.5, grouping=False) # TODO rcParam?
edgestyle: MappableStyle = Mappable("-", grouping=False)

def _resolve_properties(self, data, scales):
# TODO this is maybe a little hacky, is there a better abstraction?
resolved = super()._resolve_properties(data, scales)

resolved = super()._resolve_properties(data, scales)
filled = resolved["fill"]

main_stroke = resolved["stroke"]
edge_stroke = resolved["edgewidth"]
resolved["linewidth"] = np.where(filled, edge_stroke, main_stroke)

# Overwrite the colors that the super class set
main_color = resolve_color(self, data, "", scales)
edge_color = resolve_color(self, data, "edge", scales)

Expand All @@ -166,9 +138,43 @@ def _resolve_properties(self, data, scales):

filled = np.squeeze(filled)
if isinstance(main_color, tuple):
# TODO handle this in resolve_color
main_color = tuple([*main_color[:3], main_color[3] * filled])
else:
main_color = np.c_[main_color[:, :3], main_color[:, 3] * filled]
resolved["facecolor"] = main_color

return resolved


@dataclass
class Dots(DotBase):
"""
A dot mark defined by strokes to better handle overplotting.
"""
# TODO retype marker as MappableMarker
marker: MappableString = Mappable(rc="scatter.marker", grouping=False)
pointsize: MappableFloat = Mappable(3, grouping=False) # TODO rcParam?
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
color: MappableColor = Mappable("C0", grouping=False)
alpha: MappableFloat = Mappable(1, grouping=False) # TODO auto alpha?
fill: MappableBool = Mappable(True, grouping=False)
fillcolor: MappableColor = Mappable(depend="color", grouping=False)
fillalpha: MappableFloat = Mappable(.2, grouping=False)

def _resolve_properties(self, data, scales):

resolved = super()._resolve_properties(data, scales)
resolved["linewidth"] = resolved.pop("stroke")
resolved["facecolor"] = resolve_color(self, data, "fill", scales)
resolved["edgecolor"] = resolve_color(self, data, "", scales)
resolved.setdefault("edgestyle", (0, None))

fc = resolved["facecolor"]
if isinstance(fc, tuple):
resolved["facecolor"] = fc[0], fc[1], fc[2], fc[3] * resolved["fill"]
else:
fc[:, 3] = fc[:, 3] * resolved["fill"] # TODO Is inplace mod a problem?
resolved["facecolor"] = fc

return resolved
2 changes: 1 addition & 1 deletion seaborn/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from seaborn._marks.area import Area, Ribbon # noqa: F401
from seaborn._marks.bar import Bar, Bars # noqa: F401
from seaborn._marks.line import Line, Lines, Path, Paths, Interval # noqa: F401
from seaborn._marks.scatter import Dot, Scatter # noqa: F401
from seaborn._marks.dot import Dot, Dots # noqa: F401

from seaborn._stats.base import Stat # noqa: F401
from seaborn._stats.aggregation import Agg, Est # noqa: F401
Expand Down
Loading

0 comments on commit a1ede5e

Please sign in to comment.