Skip to content

Commit

Permalink
impl pointplot 2d
Browse files Browse the repository at this point in the history
  • Loading branch information
hanjinliu committed Jan 31, 2024
1 parent 084db47 commit ca05c19
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/categorical/categorical_axis.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ canvas
```

``` python
#!name: categorical_axis_boxplot_0
#!name: categorical_axis_barplot_0
canvas = new_canvas("matplotlib")
canvas.cat(df).add_barplot(
offset=["category", "replicate"],
Expand Down
32 changes: 19 additions & 13 deletions whitecanvas/canvas/dataframe/_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,19 +856,25 @@ def add_heatmap(
canvas.y.ticks.set_labels(*layer._generate_yticks())
return canvas.add_layer(layer)

# TODO: implement this
# def add_2dpointplot(
# self,
# x: str,
# y: str,
# value: str,
# *,
# name: str | None = None,
# color: NStr | None = None,
# width: str | None = None,
# style: NStr | None = None,
# ):
# ...
def add_pointplot2d(
self,
x: str,
y: str,
*,
name: str | None = None,
color: NStr | None = None,
hatch: NStr | None = None,
size: float | None = None,
capsize: float = 0.15,
):
canvas = self._canvas()
layer = _lt.DFPointPlot2D(
parse(self._df), x, y, name=name, color=color, hatch=hatch, size=size,
capsize=capsize, backend=canvas._get_backend(),
) # fmt: skip
if self._update_label:
self._update_xy_label(x, y)
return canvas.add_layer(layer)

### Aggregation ###

Expand Down
5 changes: 5 additions & 0 deletions whitecanvas/layers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ def name(self) -> str:
def name(self, name: str):
self._base_layer.name = name

@property
def base(self) -> _L:
"""The base layer."""
return self._base_layer

def bbox_hint(self) -> NDArray[np.floating]:
"""Return the bounding box hint using the base layer."""
return self._base_layer.bbox_hint()
Expand Down
61 changes: 55 additions & 6 deletions whitecanvas/layers/group/labeled.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,7 @@ def markers(self) -> Markers[_NFace, _NEdge, _Size]:
return self._children[0]


def _init_mean_sd(
x,
data,
color,
) -> tuple[NDArray[np.floating], NDArray[np.floating], NDArray[np.floating]]:
def _init_mean_sd(x, data, color):
x, data = check_array_input(x, data)
color = as_color_array(color, len(x))

Expand Down Expand Up @@ -436,6 +432,59 @@ def from_arrays(
xerr, yerr = _init_error_bars(x, y, err_data, orient, capsize, backend)
return cls(plot, xerr=xerr, yerr=yerr, name=name)

@classmethod
def from_arrays_2d(
cls,
xdata: list[ArrayLike1D],
ydata: list[ArrayLike1D],
*,
name: str | None = None,
capsize: float = 0.15,
color: ColorType | list[ColorType] = "blue",
alpha: float = 1.0,
hatch: str | Hatch = Hatch.SOLID,
backend: str | Backend | None = None,
) -> LabeledPlot[_mixin.MultiFace, _mixin.MultiEdge, float]:
def _estimate(arrs: list[NDArray[np.number]]):
_mean = []
_sd = []
for arr in arrs:
_mean.append(np.mean(arr))
_sd.append(np.std(arr, ddof=1))
return np.array(_mean), np.array(_sd)

xmean, xsd = _estimate(xdata)
ymean, ysd = _estimate(ydata)
markers = Markers(
xmean,
ymean,
backend=backend,
).with_face_multi(
color=color,
hatch=hatch,
alpha=alpha,
)
lines = Line(xmean, ymean, backend=backend)
plot = Plot(lines, markers)
lines.visible = False
xerr = Errorbars(
ymean,
xmean - xsd,
xmean + xsd,
orient=Orientation.HORIZONTAL,
capsize=capsize,
backend=backend,
)
yerr = Errorbars(
xmean,
ymean - ysd,
ymean + ysd,
orient=Orientation.VERTICAL,
capsize=capsize,
backend=backend,
)
return cls(plot, xerr=xerr, yerr=yerr, name=name)


class PlotFace(_mixin.FaceNamespace):
_layer: LabeledPlot[_mixin.MultiFace, _mixin.MultiEdge, float]
Expand All @@ -461,7 +510,7 @@ def hatch(self) -> _mixin.EnumArray[Hatch]:
def hatch(self, hatch: str | Hatch | Iterable[str | Hatch]):
ndata = self._layer._main_object_layer().ndata
hatches = as_any_1d_array(hatch, ndata, dtype=object)
self._layer.markers.with_face_multi(hatch=hatches)
self._layer._main_object_layer().with_face_multi(hatch=hatches)
self.events.hatch.emit(hatches)

@property
Expand Down
2 changes: 2 additions & 0 deletions whitecanvas/layers/tabular/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
DFLines,
DFMarkerGroups,
DFMarkers,
DFPointPlot2D,
)

__all__ = [
Expand All @@ -22,4 +23,5 @@
"DFBars",
"DFBoxPlot",
"DFHeatmap",
"DFPointPlot2D",
]
26 changes: 25 additions & 1 deletion whitecanvas/layers/tabular/_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,4 +789,28 @@ def _generate_yticks(self):


class DFPointPlot2D(_shared.DataFrameLayerWrapper[_lg.LabeledPlot, _DF], Generic[_DF]):
...
def __init__(
self,
source: DataFrameWrapper[_DF],
x: str,
y: str,
*,
color: str | tuple[str, ...] | None = None,
hatch: str | tuple[str, ...] | None = None,
size: float | None = None,
capsize: float = 0.15,
name: str | None = None,
backend: str | Backend | None = None,
):
cols = _shared.join_columns(color, hatch, source=source)
xdata = []
ydata = []
for _, sub in source.group_by(cols):
xdata.append(sub[x])
ydata.append(sub[y])
base = _lg.LabeledPlot.from_arrays_2d(
xdata, ydata, name=name, capsize=capsize, backend=backend
)
if size is not None:
base.markers.size = size
super().__init__(base, source)

0 comments on commit ca05c19

Please sign in to comment.