Skip to content

Commit a1ede5e

Browse files
authored
Rename Scatter mark to Dots (#2942)
* Rename Scatter->Dots and make Dot(s) inherit from common base * Rename _marks/scatter to _marks/dot * Update nextgen demo * Formatting * More formatting
1 parent 8dad74c commit a1ede5e

File tree

7 files changed

+138
-128
lines changed

7 files changed

+138
-128
lines changed

doc/api.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ Mark objects
2929
Bar
3030
Bars
3131
Dot
32+
Dots
3233
Interval
3334
Path
3435
Paths
3536
Line
3637
Lines
3738
Ribbon
38-
Scatter
3939

4040
Stat objects
4141
~~~~~~~~~~~~

doc/nextgen/demo.ipynb

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
"metadata": {},
9494
"outputs": [],
9595
"source": [
96-
"so.Plot(tips, x=\"total_bill\", y=\"tip\").add(so.Scatter())"
96+
"so.Plot(tips, x=\"total_bill\", y=\"tip\").add(so.Dots())"
9797
]
9898
},
9999
{
@@ -111,7 +111,7 @@
111111
"metadata": {},
112112
"outputs": [],
113113
"source": [
114-
"so.Plot(tips).add(so.Scatter(), x=\"total_bill\", y=\"tip\")"
114+
"so.Plot(tips).add(so.Dots(), x=\"total_bill\", y=\"tip\")"
115115
]
116116
},
117117
{
@@ -131,8 +131,8 @@
131131
"source": [
132132
"(\n",
133133
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
134-
" .add(so.Scatter(color=\".6\"), data=tips.query(\"size != 2\"))\n",
135-
" .add(so.Scatter(), data=tips.query(\"size == 2\"))\n",
134+
" .add(so.Dots(color=\".6\"), data=tips.query(\"size != 2\"))\n",
135+
" .add(so.Dots(), data=tips.query(\"size == 2\"))\n",
136136
")"
137137
]
138138
},
@@ -153,7 +153,7 @@
153153
"source": [
154154
"(\n",
155155
" so.Plot(tips.to_dict(), x=\"total_bill\")\n",
156-
" .add(so.Scatter(), y=tips[\"tip\"].to_numpy())\n",
156+
" .add(so.Dots(), y=tips[\"tip\"].to_numpy())\n",
157157
")"
158158
]
159159
},
@@ -172,7 +172,7 @@
172172
"metadata": {},
173173
"outputs": [],
174174
"source": [
175-
"so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"time\").add(so.Scatter())"
175+
"so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"time\").add(so.Dots())"
176176
]
177177
},
178178
{
@@ -192,7 +192,7 @@
192192
"source": [
193193
"(\n",
194194
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\", fill=\"time\")\n",
195-
" .add(so.Scatter(fillalpha=.8))\n",
195+
" .add(so.Dots(fillalpha=.8))\n",
196196
")"
197197
]
198198
},
@@ -376,7 +376,7 @@
376376
"\n",
377377
"### Overplotting resolution: the Move\n",
378378
"\n",
379-
"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`:"
379+
"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`:"
380380
]
381381
},
382382
{
@@ -523,7 +523,7 @@
523523
"(\n",
524524
" so.Plot(planets, x=\"mass\", y=\"distance\")\n",
525525
" .scale(x=\"log\", y=\"log\")\n",
526-
" .add(so.Scatter())\n",
526+
" .add(so.Dots())\n",
527527
")"
528528
]
529529
},
@@ -545,7 +545,7 @@
545545
"(\n",
546546
" so.Plot(planets, x=\"mass\", y=\"distance\", color=\"orbital_period\")\n",
547547
" .scale(x=\"log\", y=\"log\", color=\"rocket\")\n",
548-
" .add(so.Scatter())\n",
548+
" .add(so.Dots())\n",
549549
")"
550550
]
551551
},
@@ -571,7 +571,7 @@
571571
" y=so.Continuous(transform=\"log\").tick(at=[3, 10, 30, 100, 300]),\n",
572572
" color=so.Continuous(\"rocket\", transform=\"log\"),\n",
573573
" )\n",
574-
" .add(so.Scatter())\n",
574+
" .add(so.Dots())\n",
575575
")"
576576
]
577577
},
@@ -596,7 +596,7 @@
596596
" y=\"log\",\n",
597597
" color=so.Nominal([\"b\", \"g\"], order=[\"Radial Velocity\", \"Transit\"])\n",
598598
" )\n",
599-
" .add(so.Scatter())\n",
599+
" .add(so.Dots())\n",
600600
")"
601601
]
602602
},
@@ -618,7 +618,7 @@
618618
"(\n",
619619
" so.Plot(planets, x=\"distance\", y=\"orbital_period\", pointsize=\"mass\")\n",
620620
" .scale(x=\"log\", y=\"log\", pointsize=None)\n",
621-
" .add(so.Scatter())\n",
621+
" .add(so.Dots())\n",
622622
")"
623623
]
624624
},
@@ -693,7 +693,7 @@
693693
"(\n",
694694
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
695695
" .facet(\"time\", order=[\"Dinner\", \"Lunch\"])\n",
696-
" .add(so.Scatter())\n",
696+
" .add(so.Dots())\n",
697697
")"
698698
]
699699
},
@@ -715,8 +715,8 @@
715715
"(\n",
716716
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
717717
" .facet(col=\"day\")\n",
718-
" .add(so.Scatter(color=\".75\"), col=None)\n",
719-
" .add(so.Scatter(), color=\"day\")\n",
718+
" .add(so.Dots(color=\".75\"), col=None)\n",
719+
" .add(so.Dots(), color=\"day\")\n",
720720
" .configure(figsize=(7, 3))\n",
721721
")"
722722
]
@@ -971,7 +971,7 @@
971971
"(\n",
972972
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
973973
" .on(ax)\n",
974-
" .add(so.Scatter())\n",
974+
" .add(so.Dots())\n",
975975
")"
976976
]
977977
},
@@ -994,7 +994,7 @@
994994
"(\n",
995995
" so.Plot(tips, x=\"total_bill\", y=\"tip\")\n",
996996
" .on(f)\n",
997-
" .add(so.Scatter())\n",
997+
" .add(so.Dots())\n",
998998
" .facet(\"time\")\n",
999999
")"
10001000
]
@@ -1018,14 +1018,14 @@
10181018
"sf1, sf2 = f.subfigures(1, 2)\n",
10191019
"(\n",
10201020
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\")\n",
1021-
" .add(so.Scatter())\n",
1021+
" .add(so.Dots())\n",
10221022
" .on(sf1)\n",
10231023
" .plot()\n",
10241024
")\n",
10251025
"(\n",
10261026
" so.Plot(tips, x=\"total_bill\", y=\"tip\", color=\"day\")\n",
10271027
" .facet(\"day\", wrap=2)\n",
1028-
" .add(so.Scatter())\n",
1028+
" .add(so.Dots())\n",
10291029
" .on(sf2)\n",
10301030
" .plot()\n",
10311031
")"
@@ -1056,7 +1056,7 @@
10561056
"name": "python",
10571057
"nbconvert_exporter": "python",
10581058
"pygments_lexer": "ipython3",
1059-
"version": "3.9.9"
1059+
"version": "3.9.13"
10601060
}
10611061
},
10621062
"nbformat": 4,

doc/nextgen/index.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
" color=\"smoker\", marker=\"smoker\", pointsize=\"size\",\n",
3131
" )\n",
3232
" .facet(\"time\")\n",
33-
" .add(so.Scatter())\n",
33+
" .add(so.Dots())\n",
3434
" .configure(figsize=(7, 4))\n",
3535
")"
3636
]
@@ -104,7 +104,7 @@
104104
"name": "python",
105105
"nbconvert_exporter": "python",
106106
"pygments_lexer": "ipython3",
107-
"version": "3.9.9"
107+
"version": "3.9.13"
108108
}
109109
},
110110
"nbformat": 4,

seaborn/_core/properties.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,6 @@ def mapping(x):
259259
class PointSize(IntervalProperty):
260260
"""Size (diameter) of a point mark, in points, with scaling by area."""
261261
_default_range = 2, 8 # TODO use rcparams?
262-
# TODO N.B. both Scatter and Dot use this but have different expected sizes
263-
# Is that something we need to handle? Or assume Dot size rarely scaled?
264-
# Also will Line marks have a PointSize property?
265262

266263
def _forward(self, values):
267264
"""Square native values to implement linear scaling of point area."""

seaborn/_marks/scatter.py renamed to seaborn/_marks/dot.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,7 @@
2424

2525

2626
@dataclass
27-
class Scatter(Mark):
28-
"""
29-
A point mark defined by strokes with optional fills.
30-
"""
31-
# TODO retype marker as MappableMarker
32-
marker: MappableString = Mappable(rc="scatter.marker", grouping=False)
33-
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
34-
pointsize: MappableFloat = Mappable(3, grouping=False) # TODO rcParam?
35-
color: MappableColor = Mappable("C0", grouping=False)
36-
alpha: MappableFloat = Mappable(1, grouping=False) # TODO auto alpha?
37-
fill: MappableBool = Mappable(True, grouping=False)
38-
fillcolor: MappableColor = Mappable(depend="color", grouping=False)
39-
fillalpha: MappableFloat = Mappable(.2, grouping=False)
27+
class DotBase(Mark):
4028

4129
def _resolve_paths(self, data):
4230

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

6149
resolved = resolve_properties(self, data, scales)
6250
resolved["path"] = self._resolve_paths(resolved)
51+
resolved["size"] = resolved["pointsize"] ** 2
6352

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

69-
resolved["linewidth"] = resolved["stroke"]
7058
resolved["fill"] = resolved["fill"] * filled_marker
71-
resolved["size"] = resolved["pointsize"] ** 2
72-
73-
resolved["edgecolor"] = resolve_color(self, data, "", scales)
74-
resolved["facecolor"] = resolve_color(self, data, "fill", scales)
75-
76-
# Because only Dot, and not Scatter, has an edgestyle
77-
resolved.setdefault("edgestyle", (0, None))
78-
79-
fc = resolved["facecolor"]
80-
if isinstance(fc, tuple):
81-
resolved["facecolor"] = fc[0], fc[1], fc[2], fc[3] * resolved["fill"]
82-
else:
83-
fc[:, 3] = fc[:, 3] * resolved["fill"] # TODO Is inplace mod a problem?
84-
resolved["facecolor"] = fc
8559

8660
return resolved
8761

@@ -129,33 +103,31 @@ def _legend_artist(
129103
)
130104

131105

132-
# TODO change this to depend on ScatterBase?
133106
@dataclass
134-
class Dot(Scatter):
107+
class Dot(DotBase):
135108
"""
136-
A point mark defined by shape with optional edges.
109+
A mark suitable for dot plots or less-dense scatterplots.
137110
"""
138111
marker: MappableString = Mappable("o", grouping=False)
112+
pointsize: MappableFloat = Mappable(6, grouping=False) # TODO rcParam?
113+
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
139114
color: MappableColor = Mappable("C0", grouping=False)
140115
alpha: MappableFloat = Mappable(1, grouping=False)
141116
fill: MappableBool = Mappable(True, grouping=False)
142117
edgecolor: MappableColor = Mappable(depend="color", grouping=False)
143118
edgealpha: MappableFloat = Mappable(depend="alpha", grouping=False)
144-
pointsize: MappableFloat = Mappable(6, grouping=False) # TODO rcParam?
145119
edgewidth: MappableFloat = Mappable(.5, grouping=False) # TODO rcParam?
146120
edgestyle: MappableStyle = Mappable("-", grouping=False)
147121

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

124+
resolved = super()._resolve_properties(data, scales)
152125
filled = resolved["fill"]
153126

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

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

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

167139
filled = np.squeeze(filled)
168140
if isinstance(main_color, tuple):
141+
# TODO handle this in resolve_color
169142
main_color = tuple([*main_color[:3], main_color[3] * filled])
170143
else:
171144
main_color = np.c_[main_color[:, :3], main_color[:, 3] * filled]
172145
resolved["facecolor"] = main_color
173146

174147
return resolved
148+
149+
150+
@dataclass
151+
class Dots(DotBase):
152+
"""
153+
A dot mark defined by strokes to better handle overplotting.
154+
"""
155+
# TODO retype marker as MappableMarker
156+
marker: MappableString = Mappable(rc="scatter.marker", grouping=False)
157+
pointsize: MappableFloat = Mappable(3, grouping=False) # TODO rcParam?
158+
stroke: MappableFloat = Mappable(.75, grouping=False) # TODO rcParam?
159+
color: MappableColor = Mappable("C0", grouping=False)
160+
alpha: MappableFloat = Mappable(1, grouping=False) # TODO auto alpha?
161+
fill: MappableBool = Mappable(True, grouping=False)
162+
fillcolor: MappableColor = Mappable(depend="color", grouping=False)
163+
fillalpha: MappableFloat = Mappable(.2, grouping=False)
164+
165+
def _resolve_properties(self, data, scales):
166+
167+
resolved = super()._resolve_properties(data, scales)
168+
resolved["linewidth"] = resolved.pop("stroke")
169+
resolved["facecolor"] = resolve_color(self, data, "fill", scales)
170+
resolved["edgecolor"] = resolve_color(self, data, "", scales)
171+
resolved.setdefault("edgestyle", (0, None))
172+
173+
fc = resolved["facecolor"]
174+
if isinstance(fc, tuple):
175+
resolved["facecolor"] = fc[0], fc[1], fc[2], fc[3] * resolved["fill"]
176+
else:
177+
fc[:, 3] = fc[:, 3] * resolved["fill"] # TODO Is inplace mod a problem?
178+
resolved["facecolor"] = fc
179+
180+
return resolved

seaborn/objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from seaborn._marks.area import Area, Ribbon # noqa: F401
3333
from seaborn._marks.bar import Bar, Bars # noqa: F401
3434
from seaborn._marks.line import Line, Lines, Path, Paths, Interval # noqa: F401
35-
from seaborn._marks.scatter import Dot, Scatter # noqa: F401
35+
from seaborn._marks.dot import Dot, Dots # noqa: F401
3636

3737
from seaborn._stats.base import Stat # noqa: F401
3838
from seaborn._stats.aggregation import Agg, Est # noqa: F401

0 commit comments

Comments
 (0)