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

Linetype tuples don't work. #352

Closed
TyberiusPrime opened this issue Feb 3, 2020 · 4 comments · Fixed by #353
Closed

Linetype tuples don't work. #352

TyberiusPrime opened this issue Feb 3, 2020 · 4 comments · Fixed by #353
Labels

Comments

@TyberiusPrime
Copy link
Contributor

TyberiusPrime commented Feb 3, 2020

The documentation says scale_linetype_manual, parameters, values:
"Tuples of the form (offset, (on, off, on, off, ....)) e.g. (0, (1, 1)), (1, (2, 2)), (2, (5, 3, 1, 3))"

But that does not work :(.

import pandas as pd
import plotnine as p9
import numpy as np
line_type_count = 3
df = pd.DataFrame(
    {
        "x": np.random.random(size=100),
        "y": np.random.random(size=100),
        "lt": ([str(x) for x in range(line_type_count)] * 200)[:100],
    }
)
import plotnine as p9

g = p9.ggplot(df)
g += p9.geom_line(p9.aes(x='x', y='y', ymax='y+1', linetype="lt", group='lt'))
g += p9.scale_linetype_manual(values=(
    (0, (1, 1)), 
    (1, (2, 2)), 
    (2, (5, 3, 1, 3))
))
g
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/ssd/upstream/dev/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

/ssd/upstream/dev/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    397                         if cls is not object \
    398                                 and callable(cls.__dict__.get('__repr__')):
--> 399                             return _repr_pprint(obj, self, cycle)
    400 
    401             return _default_pprint(obj, self, cycle)

/ssd/upstream/dev/lib/python3.7/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
    687     """A pprint that just redirects to the normal repr function."""
    688     # Find newlines and replace them with p.break_()
--> 689     output = repr(obj)
    690     for idx,output_line in enumerate(output.splitlines()):
    691         if idx:

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/ggplot.py in __repr__(self)
     86         # in the jupyter notebook.
     87         if not self.figure:
---> 88             self.draw()
     89         plt.show()
     90         return '<ggplot: (%d)>' % self.__hash__()

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/ggplot.py in draw(self, return_ggplot)
    179         # new frames knowing that they are separate from the original.
    180         with pd.option_context('mode.chained_assignment', None):
--> 181             return self._draw(return_ggplot)
    182 
    183     def _draw(self, return_ggplot=False):

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/ggplot.py in _draw(self, return_ggplot)
    186         # assign a default theme
    187         self = deepcopy(self)
--> 188         self._build()
    189 
    190         # If no theme we use the default

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/ggplot.py in _build(self)
    321         if len(npscales):
    322             layers.train(npscales)
--> 323             layers.map(npscales)
    324 
    325         # Train coordinate system

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/layer.py in map(self, scales)
    107     def map(self, scales):
    108         for l in self:
--> 109             l.data = scales.map_df(l.data)
    110 
    111     def finish_statistics(self):

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/scales/scales.py in map_df(self, df)
    195         # Each scale maps the columns it understands
    196         for sc in self:
--> 197             df = sc.map_df(df)
    198         return df
    199 

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/scales/scale.py in map_df(self, df)
    285         aesthetics = set(self.aesthetics) & set(df.columns)
    286         for ae in aesthetics:
--> 287             df[ae] = self.map(df[ae])
    288 
    289         return df

/ssd/upstream/plotnine_dev/linestyle_errors/plotnine/scales/scale.py in map(self, x, limits)
    438 
    439         if self.na_translate:
--> 440             bool_idx = pd.isnull(x) | pd.isnull(pal_match)
    441             if bool_idx.any():
    442                 pal_match = [x if i else self.na_value

/ssd/upstream/dev/lib/python3.7/site-packages/pandas/core/ops/__init__.py in wrapper(self, other)
   1319         #   integer dtypes.  Otherwise these are boolean ops
   1320         filler = fill_int if is_self_int_dtype and is_other_int_dtype else fill_bool
-> 1321         res_values = na_op(self.values, ovalues)
   1322         unfilled = self._constructor(res_values, index=self.index, name=res_name)
   1323         filled = filler(unfilled)

/ssd/upstream/dev/lib/python3.7/site-packages/pandas/core/ops/__init__.py in na_op(x, y)
   1252     def na_op(x, y):
   1253         try:
-> 1254             result = op(x, y)
   1255         except TypeError:
   1256             assert not isinstance(y, (list, ABCSeries, ABCIndexClass))

ValueError: operands could not be broadcast together with shapes (100,) (100,2) ```


(Matplotlib is 3.1.2, and I can't find any test for scale_linetype_manual)
@TyberiusPrime
Copy link
Contributor Author

(The problem seems to be that pd.isnull([[2, (5, 3, 1, 3)]],) returns [False, False] instead of False.

@TyberiusPrime
Copy link
Contributor Author

Fixing that (PR upcoming - https://github.com/TyberiusPrime/plotnine/tree/bug_352 for now), reveals another underlying bug where it will only work if you have at least one 'string' linetype in your manual linetype... stay tuned.

@has2k1
Copy link
Owner

has2k1 commented Feb 3, 2020

By tradition, pandas does not like non-scalar values in dataframes, so there bugs around cases like these. In fact when I think about it, I expect 2 to 3 cascading fixes just to get this to work.

@TyberiusPrime
Copy link
Contributor Author

In fact when I think about it, I expect 2 to 3 cascading fixes just to get this to work.
Indeed, that's pretty much what I found.

There is also one minor issue that a line definition like (0, (20,20) will create a legend entry that looks like a solid line. Don't see how we would fix that though .

@has2k1 has2k1 added the bug label Feb 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants