seaborn icon indicating copy to clipboard operation
seaborn copied to clipboard

Passing scalar variable assignments in Plot.add should be possible

Open mwaskom opened this issue 1 year ago • 1 comments

This works:

so.Plot(x=[1, 2, 3, 4], ymin=0, ymax=[1, 2, 3, 4]).add(so.Interval())

This fails:

so.Plot(x=[1, 2, 3, 4], ymax=[1, 2, 3, 4]).add(so.Interval(), ymin=0)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/miniconda3/envs/seaborn-py39-latest/lib/python3.9/site-packages/IPython/core/formatters.py:343, in BaseFormatter.__call__(self, obj)
    341     method = get_real_method(obj, self.print_method)
    342     if method is not None:
--> 343         return method()
    344     return None
    345 else:

File ~/code/seaborn/seaborn/_core/plot.py:232, in Plot._repr_png_(self)
    230 def _repr_png_(self) -> tuple[bytes, dict[str, float]]:
--> 232     return self.plot()._repr_png_()

File ~/code/seaborn/seaborn/_core/plot.py:678, in Plot.plot(self, pyplot)
    675 plotter = Plotter(pyplot=pyplot)
    677 # Process the variable assignments and initialize the figure
--> 678 common, layers = plotter._extract_data(self)
    679 plotter._setup_figure(self, common, layers)
    681 # Process the scale spec for coordinate variables and transform their data

File ~/code/seaborn/seaborn/_core/plot.py:810, in Plotter._extract_data(self, p)
    808 for layer in p._layers:
    809     spec = layer.copy()
--> 810     spec["data"] = common_data.join(layer.get("source"), layer.get("vars"))
    811     layers.append(spec)
    813 return common_data, layers

File ~/code/seaborn/seaborn/_core/data.py:93, in PlotData.join(self, data, variables)
     90 disinherit = [k for k, v in variables.items() if v is None]
     92 # Create a new dataset with just the info passed here
---> 93 new = PlotData(data, variables)
     95 # -- Update the inherited DataSource with this new information
     97 drop_cols = [k for k in self.frame if k in new.frame or k in disinherit]

File ~/code/seaborn/seaborn/_core/data.py:57, in PlotData.__init__(self, data, variables)
     51 def __init__(
     52     self,
     53     data: DataSource,
     54     variables: dict[str, VariableSpec],
     55 ):
---> 57     frame, names, ids = self._assign_variables(data, variables)
     59     self.frame = frame
     60     self.names = names

File ~/code/seaborn/seaborn/_core/data.py:260, in PlotData._assign_variables(self, data, variables)
    255             ids[key] = id(val)
    257 # Construct a tidy plot DataFrame. This will convert a number of
    258 # types automatically, aligning on index in case of pandas objects
    259 # TODO Note: this fails when variable specs *only* have scalars!
--> 260 frame = pd.DataFrame(plot_data)
    262 return frame, names, ids

File ~/miniconda3/envs/seaborn-py39-latest/lib/python3.9/site-packages/pandas/core/frame.py:636, in DataFrame.__init__(self, data, index, columns, dtype, copy)
    630     mgr = self._init_mgr(
    631         data, axes={"index": index, "columns": columns}, dtype=dtype, copy=copy
    632     )
    634 elif isinstance(data, dict):
    635     # GH#38939 de facto copy defaults to False only in non-dict cases
--> 636     mgr = dict_to_mgr(data, index, columns, dtype=dtype, copy=copy, typ=manager)
    637 elif isinstance(data, ma.MaskedArray):
    638     import numpy.ma.mrecords as mrecords

File ~/miniconda3/envs/seaborn-py39-latest/lib/python3.9/site-packages/pandas/core/internals/construction.py:502, in dict_to_mgr(data, index, columns, dtype, typ, copy)
    494     arrays = [
    495         x
    496         if not hasattr(x, "dtype") or not isinstance(x.dtype, ExtensionDtype)
    497         else x.copy()
    498         for x in arrays
    499     ]
    500     # TODO: can we get rid of the dt64tz special case above?
--> 502 return arrays_to_mgr(arrays, columns, index, dtype=dtype, typ=typ, consolidate=copy)

File ~/miniconda3/envs/seaborn-py39-latest/lib/python3.9/site-packages/pandas/core/internals/construction.py:120, in arrays_to_mgr(arrays, columns, index, dtype, verify_integrity, typ, consolidate)
    117 if verify_integrity:
    118     # figure out the index, if necessary
    119     if index is None:
--> 120         index = _extract_index(arrays)
    121     else:
    122         index = ensure_index(index)

File ~/miniconda3/envs/seaborn-py39-latest/lib/python3.9/site-packages/pandas/core/internals/construction.py:664, in _extract_index(data)
    661         raise ValueError("Per-column arrays must each be 1-dimensional")
    663 if not indexes and not raw_lengths:
--> 664     raise ValueError("If using all scalar values, you must pass an index")
    666 elif have_series:
    667     index = union_indexes(indexes)

ValueError: If using all scalar values, you must pass an index

mwaskom avatar Aug 03 '22 02:08 mwaskom

(Incidentally, this specific plot should be possible with a Stem mark that uses y rather than ymin/ymax, but it demonstrates the point)

mwaskom avatar Aug 03 '22 02:08 mwaskom