Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename Scatter mark to Dots #2942

Merged
merged 5 commits into from
Aug 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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