@@ -42,7 +42,8 @@ class ae_value(Generic[T]):
4242 aesthetic values. e.g. if a value is a tuple, we don't want it to be
4343 seen as a sequence of values when assigning it to a dataframe column.
4444 The subclasses should be able to recognise valid aesthetic values and
45- repeat (using multiplication) the value any number of times.
45+ repeat (using multiplication) the value any number of times. i.e.
46+ broadcast the aesthetic.
4647 """
4748
4849 value : T
@@ -176,3 +177,45 @@ def is_numeric(obj) -> bool:
176177 return all (is_numeric (a ) and is_numeric (b ) for a , b in obj )
177178 except (ValueError , TypeError ):
178179 return False
180+
181+
182+ def broadcast_ae_value (value : T , ae : str , n : int ) -> Sequence [T ]:
183+ """
184+ Repeat an aesthetic value n times
185+
186+ Parameters
187+ ----------
188+ value :
189+ A single aesthetic value (e.g. a color tuple or linetype tuple)
190+ that should not be expanded element-wise.
191+ ae :
192+ Name of the aesthetic. Determines which [](`ae_value`) subclass
193+ validates and repeats the value.
194+ n :
195+ Number of times to repeat the value.
196+
197+ Returns
198+ -------
199+ :
200+ A sequence of length `n` containing the (validated) value.
201+
202+ Raises
203+ ------
204+ ValueError
205+ If `ae` is not one of the aesthetics
206+ (`color`, `colour`, `fill`, `linetype`, `shape`)
207+ that has an "atomic" handler.
208+ """
209+ lookup : dict [str , type [ae_value ]] = {
210+ "color" : color ,
211+ "linetype" : linetype ,
212+ "colour" : color ,
213+ "fill" : fill ,
214+ "shape" : shape ,
215+ }
216+ try :
217+ return lookup [ae ](value ) * n
218+ except KeyError as err :
219+ raise ValueError (
220+ f"Aesthetic { ae !r} does not have a broadcast handler."
221+ ) from err
0 commit comments