has2k1 / plotnine

A Grammar of Graphics for Python
https://plotnine.org
MIT License
4k stars 213 forks source link

Conditional colors for element_text() #724

Closed wendywangwwt closed 9 months ago

wendywangwwt commented 10 months ago

ggplot allows the color argument in element_text() to take a vector of colors, for example shown in this thread: https://stackoverflow.com/questions/49614133/conditional-formatting-ggplot-label-ticks-without-explicitly-repeat-all-tick-bre

Currently even if I convert my list of colors into a tuple (as this argument in plotnine seems to take tuples in addition to a string), it still ends with a ValueError:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /opt/conda/lib/python3.10/site-packages/IPython/core/formatters.py:706, in PlainTextFormatter.__call__(self, obj)
    699 stream = StringIO()
    700 printer = pretty.RepresentationPrinter(stream, self.verbose,
    701     self.max_width, self.newline,
    702     max_seq_length=self.max_seq_length,
    703     singleton_pprinters=self.singleton_printers,
    704     type_pprinters=self.type_printers,
    705     deferred_pprinters=self.deferred_printers)
--> 706 printer.pretty(obj)
    707 printer.flush()
    708 return stream.getvalue()

File /opt/conda/lib/python3.10/site-packages/IPython/lib/pretty.py:410, in RepresentationPrinter.pretty(self, obj)
    407                         return meth(obj, self, cycle)
    408                 if cls is not object \
    409                         and callable(cls.__dict__.get('__repr__')):
--> 410                     return _repr_pprint(obj, self, cycle)
    412     return _default_pprint(obj, self, cycle)
    413 finally:

File /opt/conda/lib/python3.10/site-packages/IPython/lib/pretty.py:778, in _repr_pprint(obj, p, cycle)
    776 """A pprint that just redirects to the normal repr function."""
    777 # Find newlines and replace them with p.break_()
--> 778 output = repr(obj)
    779 lines = output.splitlines()
    780 with p.group():

File /opt/conda/lib/python3.10/site-packages/plotnine/ggplot.py:114, in ggplot.__repr__(self)
    110 def __repr__(self) -> str:
    111     """
    112     Print/show the plot
    113     """
--> 114     figure = self.draw(show=True)
    116     dpi = figure.get_dpi()
    117     W = int(figure.get_figwidth() * dpi)

File /opt/conda/lib/python3.10/site-packages/plotnine/ggplot.py:240, in ggplot.draw(self, show)
    237     self._draw_watermarks()
    239     # Artist object theming
--> 240     self.theme.apply()
    241     figure.set_layout_engine(PlotnineLayoutEngine(self))
    243 return self.figure

File /opt/conda/lib/python3.10/site-packages/plotnine/themes/theme.py:235, in theme.apply(self)
    227 """
    228 Apply this theme, then apply additional modifications in order.
    229 
   (...)
    232 base class method is called.
    233 """
    234 for th in self.themeables.values():
--> 235     th.apply(self)

File /opt/conda/lib/python3.10/site-packages/plotnine/themes/themeable.py:199, in themeable.apply(self, theme)
    197 self.apply_figure(theme.figure, theme._targets)
    198 for ax in theme.axs:
--> 199     self.apply_ax(ax)

File /opt/conda/lib/python3.10/site-packages/plotnine/themes/themeable.py:772, in axis_text_y.apply_ax(self, ax)
    770 labels = ax.get_yticklabels()  # pyright: ignore
    771 for l in labels:
--> 772     l.set(**properties)

File /opt/conda/lib/python3.10/site-packages/matplotlib/artist.py:147, in Artist.__init_subclass__.<locals>.<lambda>(self, **kwargs)
    139 if not hasattr(cls.set, '_autogenerated_signature'):
    140     # Don't overwrite cls.set if the subclass or one of its parents
    141     # has defined a set method set itself.
    142     # If there was no explicit definition, cls.set is inherited from
    143     # the hierarchy of auto-generated set methods, which hold the
    144     # flag _autogenerated_signature.
    145     return
--> 147 cls.set = lambda self, **kwargs: Artist.set(self, **kwargs)
    148 cls.set.__name__ = "set"
    149 cls.set.__qualname__ = f"{cls.__qualname__}.set"

File /opt/conda/lib/python3.10/site-packages/matplotlib/artist.py:1231, in Artist.set(self, **kwargs)
   1227 def set(self, **kwargs):
   1228     # docstring and signature are auto-generated via
   1229     # Artist._update_set_signature_and_docstring() at the end of the
   1230     # module.
-> 1231     return self._internal_update(cbook.normalize_kwargs(kwargs, self))

File /opt/conda/lib/python3.10/site-packages/matplotlib/artist.py:1223, in Artist._internal_update(self, kwargs)
   1216 def _internal_update(self, kwargs):
   1217     """
   1218     Update artist properties without prenormalizing them, but generating
   1219     errors as if calling `set`.
   1220 
   1221     The lack of prenormalization is to maintain backcompatibility.
   1222     """
-> 1223     return self._update_props(
   1224         kwargs, "{cls.__name__}.set() got an unexpected keyword argument "
   1225         "{prop_name!r}")

File /opt/conda/lib/python3.10/site-packages/matplotlib/artist.py:1199, in Artist._update_props(self, props, errfmt)
   1196             if not callable(func):
   1197                 raise AttributeError(
   1198                     errfmt.format(cls=type(self), prop_name=k))
-> 1199             ret.append(func(v))
   1200 if ret:
   1201     self.pchanged()

File /opt/conda/lib/python3.10/site-packages/matplotlib/text.py:996, in Text.set_color(self, color)
    993 # "auto" is only supported by axisartist, but we can just let it error
    994 # out at draw time for simplicity.
    995 if not cbook._str_equal(color, "auto"):
--> 996     mpl.colors._check_color_like(color=color)
    997 self._color = color
    998 self.stale = True

File /opt/conda/lib/python3.10/site-packages/matplotlib/colors.py:243, in _check_color_like(**kwargs)
    241 for k, v in kwargs.items():
    242     if not is_color_like(v):
--> 243         raise ValueError(f"{v!r} is not a valid value for {k}")

ValueError: ('blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red', 'red') is not a valid value for color

Being able to conditionally color the text elements in a plot is very helpful. For example, one may want to have a bar chart showing the frequency of the most frequent words, and s/he could color the words (x/y axis tick labels) by word type.

has2k1 commented 10 months ago

This is something we can support.