From fc0778cc958b342618c94269b66326b14c77d8a8 Mon Sep 17 00:00:00 2001 From: Hanjin Liu Date: Wed, 7 Feb 2024 00:54:28 +0900 Subject: [PATCH] update docs --- docs/canvas/basics.md | 106 +++++++++++++++ docs/canvas/grid.md | 66 ++++++++++ docs/canvas/index.md | 108 +-------------- docs/layers/face_layers.md | 1 - docs/layers/index.md | 5 + docs/layers/line_layers.md | 42 ------ docs/layers/lines.md | 111 ++++++++++++++++ docs/layers/markers.md | 254 ++++++++++++++++++++++++++++++++++++ mkdocs.yml | 5 +- whitecanvas/canvas/_base.py | 125 +++++++++++++++++- 10 files changed, 670 insertions(+), 153 deletions(-) create mode 100644 docs/canvas/basics.md create mode 100644 docs/canvas/grid.md delete mode 100644 docs/layers/line_layers.md create mode 100644 docs/layers/lines.md create mode 100644 docs/layers/markers.md diff --git a/docs/canvas/basics.md b/docs/canvas/basics.md new file mode 100644 index 00000000..45892a2f --- /dev/null +++ b/docs/canvas/basics.md @@ -0,0 +1,106 @@ +# Basics + +In `whitecanvas`, a "canvas" is an object with a layer list, axes elements, labels and +methods to handle them. A "grid" is a collection of canvases and is implemented with +rendering backends. + +## Create Canvases + +Canvas is created by [`new_canvas`][whitecanvas.core.new_canvas] method. + +- `backend`: the name of the backend to use. +- `size`: the size of the canvas (width × height in pixels). +- `palette`: the name of the color palette to use. Any input that is accepted by the + [cmap.Colormap](https://cmap-docs.readthedocs.io/en/latest/catalog/) can be used, + which includes: + 1. The name of the built-in colormaps, such as "viridis", "plasma", etc. + 2. Sequence of colors, such as `["red", "green", "blue"]`. + 3. A `cmap.Colormap` object. + +``` python +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib", size=(400, 300), palette="tab10") +``` + +A `SingleCanvas` object is returned. It can be considered as a mixture of a canvas +and a grid. All of the functions mentioned above are implemented in this object. +Therefore, you can show the canvas by calling the `show` method. + +``` python +#!skip +canvas.show() # show the canvas, depending on the backend +``` + +## Namespaces + +To make the API tidy, each element of a canvas is organized in namespaces. By using +namespaces, we can avoid the long list of arguments and make the code more readable. +For example, the x-limit of the canvas is controlled by the property `canvas.x.lim`. + +Following list shows the namespaces and the properties associated with them. + +- `canvas.{x, y}`: the x/y-axis. + - (*property*) `canvas.{x, y}.lim`: the x/y-limit as a tuple of floats. + - (*property*) `canvas.{x, y}.color`: the color of the x/y-axis. + - (*property*) `canvas.{x, y}.flipped`: whether the x/y-axis is flipped. + - `canvas.{x, y}.label`: the x/y-axis label. + - (*property*) `canvas.{x, y}.label.text`: the text of the label. + - (*property*) `canvas.{x, y}.label.color`: the color of the label as `ndarray`. + - (*property*) `canvas.{x, y}.label.size`: the font size of the label. + - (*property*) `canvas.{x, y}.label.family`: the font family of the label. + - (*property*) `canvas.{x, y}.label.visible`: the visibility of the label. + - `canvas.{x, y}.ticks`: the x/y-axis ticks. + - (*property*) `canvas.{x, y}.ticks.pos`: the position of the ticks (read-only). + - (*property*) `canvas.{x, y}.ticks.labels`: the labels of the ticks (read-only). + - (*property*) `canvas.{x, y}.ticks.color`: the color of the ticks. + - (*property*) `canvas.{x, y}.ticks.size`: the size of the ticks. + - (*property*) `canvas.{x, y}.ticks.rotation`: the color of the tick labels. + - (*property*) `canvas.{x, y}.ticks.visible`: the visibility of the ticks. + +- `canvas.title`: the title object of the canvas. + - (*property*) `canvas.title.text`: the text of the title. + - (*property*) `canvas.title.color`: the color of the title as `ndarray`. + - (*property*) `canvas.title.size`: the font size of the title. + - (*property*) `canvas.title.family`: the font family of the title. + - (*property*) `canvas.title.visible`: the visibility of the title. + +!!! note + `canvas.title`, `canvas.{x, y}.label` and `canvas.{x, y}.ticks` share the same + properties (`color`, `size`, `family` and `visible`) related to the text design. + +Event handlers are also organized in namespaces. At any level, the value-changed event +for the parameter `X` is in `events.X`. For example, when the x-limit is changed, signal +will be emitted from `canvas.x.events.lim`. See [Event Handling](../events/index.md) for +more details. + +## Update Canvas Appearance + +Canvases have helper functions of name `update_*` to update the appearance. They return +the canvas itself, so that you can chain the methods after the constructor. + +``` python +# update the color of x/y-axis +canvas = new_canvas().update_axes(color="gray") + +# update the text labels +canvas = new_canvas().update_labels(x="time", y="value", title="My Plot") +``` + +## Add Layers + +All the plotting elements are added to the canvas as a "layer". In `whitecanvas`, a +layer is rarely constructed directly, but is returned by the `add_*` methods. Same +method **always** returns the same type of layer. + +``` python +#!name: canvas_basics_0 +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") +line = canvas.add_line([0, 1, 2, 3], [0, 1, 1, 0]) +markers = canvas.add_markers([0, 1, 2, 3], [0, 1, 1, 0]) +canvas.show() +``` + +See [Layers](../layers/index.md) for more details. diff --git a/docs/canvas/grid.md b/docs/canvas/grid.md new file mode 100644 index 00000000..376b06bb --- /dev/null +++ b/docs/canvas/grid.md @@ -0,0 +1,66 @@ +# Canvas Grid + +## Vertical/Horizontal Grid + +``` python +#!name: canvas_grid_vertical +from whitecanvas import vgrid + +canvas = vgrid(3, backend="matplotlib") + +c0 = canvas.add_canvas(0) +c0.add_text(0, 0, "Canvas 0") +c1 = canvas.add_canvas(1) +c1.add_text(0, 0, "Canvas 1") +c2 = canvas.add_canvas(2) +c2.add_text(0, 0, "Canvas 2") +canvas.show() +``` + + +``` python +#!name: canvas_grid_horizontal +from whitecanvas import hgrid + +canvas = hgrid(3, backend="matplotlib") + +c0 = canvas.add_canvas(0) +c0.add_text(0, 0, "Canvas 0") +c1 = canvas.add_canvas(1) +c1.add_text(0, 0, "Canvas 1") +c2 = canvas.add_canvas(2) +c2.add_text(0, 0, "Canvas 2") +canvas.show() +``` + +## 2D Grid + +``` python +#!name: canvas_grid_2d +from whitecanvas import grid + +canvas = grid(2, 2, backend="matplotlib") + +for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]: + c = canvas.add_canvas(i, j) + c.add_text(0, 0, f"Canvas ({i}, {j})") +canvas.show() +``` + +## Non-uniform Grid + +The `*_nonuniform` functions allow you to create a grid with non-uniform sizes. +Instead of specifying the number of rows and columns, these functions take a list of size ratios. + +``` python +#!name: canvas_grid_2d_nonuniform + +from whitecanvas import grid_nonuniform + +canvas = grid_nonuniform([1, 2], [2, 1], backend="matplotlib") + +for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]: + c = canvas.add_canvas(i, j) + c.add_text(0, 0, f"Canvas ({i}, {j})") +canvas.show() +``` diff --git a/docs/canvas/index.md b/docs/canvas/index.md index d98db70d..13155336 100644 --- a/docs/canvas/index.md +++ b/docs/canvas/index.md @@ -1,106 +1,6 @@ # Canvas -In `whitecanvas`, a "canvas" is an object with a layer list, axes elements, labels and -methods to handle them. A "grid" is a collection of canvases and is implemented with -rendering backends. - -## Create Canvases - -Canvas is created by `new_canvas` method. - -- `backend`: the name of the backend to use. -- `size`: the size of the canvas (width × height in pixels). -- `palette`: the name of the color palette to use. Any input that is accepted by the - [cmap.Colormap](https://cmap-docs.readthedocs.io/en/latest/catalog/) can be used, - which includes: - 1. The name of the built-in colormaps, such as "viridis", "plasma", etc. - 2. Sequence of colors, such as `["red", "green", "blue"]`. - 3. A `cmap.Colormap` object. - -``` python -from whitecanvas import new_canvas - -canvas = new_canvas("matplotlib", size=(400, 300), palette="tab10") -``` - -A `SingleCanvas` object is returned. It can be considered as a mixture of a canvas -and a grid. All of the functions mentioned above are implemented in this object. -Therefore, you can show the canvas by calling the `show` method. - -``` python -#!skip -canvas.show() # show the canvas, depending on the backend -``` - -## Namespaces - -To make the API tidy, each element of a canvas is organized in namespaces. By using -namespaces, we can avoid the long list of arguments and make the code more readable. -For example, the x-limit of the canvas is controlled by the property `canvas.x.lim`. - -Following list shows the namespaces and the properties associated with them. - -- `canvas.{x, y}`: the x/y-axis. - - (*property*) `canvas.{x, y}.lim`: the x/y-limit as a tuple of floats. - - (*property*) `canvas.{x, y}.color`: the color of the x/y-axis. - - (*property*) `canvas.{x, y}.flipped`: whether the x/y-axis is flipped. - - `canvas.{x, y}.label`: the x/y-axis label. - - (*property*) `canvas.{x, y}.label.text`: the text of the label. - - (*property*) `canvas.{x, y}.label.color`: the color of the label as `ndarray`. - - (*property*) `canvas.{x, y}.label.size`: the font size of the label. - - (*property*) `canvas.{x, y}.label.family`: the font family of the label. - - (*property*) `canvas.{x, y}.label.visible`: the visibility of the label. - - `canvas.{x, y}.ticks`: the x/y-axis ticks. - - (*property*) `canvas.{x, y}.ticks.pos`: the position of the ticks (read-only). - - (*property*) `canvas.{x, y}.ticks.labels`: the labels of the ticks (read-only). - - (*property*) `canvas.{x, y}.ticks.color`: the color of the ticks. - - (*property*) `canvas.{x, y}.ticks.size`: the size of the ticks. - - (*property*) `canvas.{x, y}.ticks.rotation`: the color of the tick labels. - - (*property*) `canvas.{x, y}.ticks.visible`: the visibility of the ticks. - -- `canvas.title`: the title object of the canvas. - - (*property*) `canvas.title.text`: the text of the title. - - (*property*) `canvas.title.color`: the color of the title as `ndarray`. - - (*property*) `canvas.title.size`: the font size of the title. - - (*property*) `canvas.title.family`: the font family of the title. - - (*property*) `canvas.title.visible`: the visibility of the title. - -!!! note - `canvas.title`, `canvas.{x, y}.label` and `canvas.{x, y}.ticks` share the same - properties (`color`, `size`, `family` and `visible`) related to the text design. - -Event handlers are also organized in namespaces. At any level, the value-changed event -for the parameter `X` is in `events.X`. For example, when the x-limit is changed, signal -will be emitted from `canvas.x.events.lim`. See [Event Handling](../events/index.md) for -more details. - -## Update Canvas Appearance - -Canvases have helper functions of name `update_*` to update the appearance. They return -the canvas itself, so that you can chain the methods after the constructor. - -``` python -# update the color of x/y-axis -canvas = new_canvas().update_axes(color="gray") - -# update the text labels -canvas = new_canvas().update_labels(x="time", y="value", title="My Plot") -``` - -## Add Layers - -All the plotting elements are added to the canvas as a "layer". In `whitecanvas`, a -layer is rarely constructed directly, but is returned by the `add_*` methods. Same -method **always** returns the same type of layer. - -``` python -#!name: canvas_basics_0 -from whitecanvas import new_canvas - -canvas = new_canvas("matplotlib") -line = canvas.add_line([0, 1, 2, 3], [0, 1, 1, 0]) -markers = canvas.add_markers([0, 1, 2, 3], [0, 1, 1, 0]) -canvas.show() -``` - -See [Layers](../layers/index.md) for more details. +- [Basics](basics.md) +- [Namespaces](namespaces.md) +- [Canvas Grid](grid.md) +- [Working with the Backend Objects](native_objects.md) diff --git a/docs/layers/face_layers.md b/docs/layers/face_layers.md index 9fee11aa..0a09e89a 100644 --- a/docs/layers/face_layers.md +++ b/docs/layers/face_layers.md @@ -2,7 +2,6 @@ There are several layers that is composed of faces and edges. -- `Markers` ... a layers composed of markers for scatter plots. - `Bars` ... a layer composed of bars. - `Band` ... a layer composed of a band region (fill-between region). - `Spans` ... a layer composed of infinitely long spans. diff --git a/docs/layers/index.md b/docs/layers/index.md index e69de29b..b7c1fc87 100644 --- a/docs/layers/index.md +++ b/docs/layers/index.md @@ -0,0 +1,5 @@ +# Layers + +- [Lines](lines.md) +- [Markers](markers.md) +- [Layer Groups](layer_groups.md) diff --git a/docs/layers/line_layers.md b/docs/layers/line_layers.md deleted file mode 100644 index 8cd4f2c2..00000000 --- a/docs/layers/line_layers.md +++ /dev/null @@ -1,42 +0,0 @@ -# Line-type Layers - -There are several layers that is composed of only lines. - -- `Line` ... a simple line. -- `InfLine` ... a straight line that extends to infinity -- `InfCurve` ... a curve that extends to infinity -- `Errorbar` ... lines representing error bars -- `Rug` ... lines representing rug plots - -These layers have following properties in common. - -- `color` ... color of the lines. Any color-like object is accepted. -- `width` ... width of the lines. Should be a non-negative number. -- `style` ... style of the lines. Should be one of `"-"`, `":"`, - `"-."`, `"--"`. - -!!! note - `style` is not supported in some backends. - -These properties can be configured in function calls, via properties or the `update` -method. - -``` python -#!name: line_layers_properties -import numpy as np -from whitecanvas import new_canvas - -canvas = new_canvas("matplotlib") - -# function call -layer = canvas.add_line(np.arange(10), color="black", width=2, style=":") - -# properties -layer.color = "#FF36D9" -layer.width = 2.5 -layer.style = "-" - -# update method -layer.update(color=[0.0, 1.0, 0.0, 1.0], width=1, style="--") -canvas.show() -``` diff --git a/docs/layers/lines.md b/docs/layers/lines.md new file mode 100644 index 00000000..46b327c8 --- /dev/null +++ b/docs/layers/lines.md @@ -0,0 +1,111 @@ +# Lines + +There are several layers that is composed of only lines. + +- `Line` ... a simple line. +- `InfLine` ... a straight line that extends to infinity +- `InfCurve` ... a curve that extends to infinity +- `Errorbar` ... lines representing error bars +- `Rug` ... lines representing rug plots + +These layers have following properties in common. + +- `color` ... color of the lines. Any color-like object is accepted. +- `width` ... width of the lines. Should be a non-negative number. +- `style` ... style of the lines. Should be one of `"-"`, `":"`, `"-."`, `"--"`. + +!!! note + `style` is not supported in some backends. + +These properties can be configured in function calls, via properties or the `update` +method. + +``` python +#!name: line_layer_properties +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +# function call +layer = canvas.add_line([0, 2, 1, 3, 4, 2, -1], color="black", width=2, style=":") + +# properties +layer.color = "#FF36D9" +layer.width = 2.5 +layer.style = "-" + +# update method +layer.update(color=[0.0, 1.0, 0.0, 1.0], width=1, style="--") +canvas.show() +``` + +## Line + +`Line` is a simple line defined by two arrays of x and y coordinates. It is usually +created by the [`add_line`][whitecanvas.canvas.CanvasBase.add_line] method. This method +accepts several ways to define the line coordinates. + +``` python +#!name: line_layer_coordinates +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +canvas.add_line([0, 1, 0, -1, 0]) # only y values +canvas.add_line([0, 1, 2, 3, 4], [1, 2, 1, 0, 1]) # x and y values +canvas.add_line(np.arange(5), np.array([2, 3, 2, 1, 2])) # numpy arrays +canvas.add_line(np.array([[0, 3], [1, 4], [2, 3], [3, 2], [4, 3]])) # (N, 2) array +canvas.show() +``` + +## InfLine + +`InfLine` is a straight line that extends to infinity. Practically, it is achieved by +connecting a callback that updates the line coordinates when the canvas view range is +updated. + +`InfLine` is usually created by the [`add_infline`][whitecanvas.canvas.CanvasBase.add_infline] method, or in the specific cases, [`add_vline`][whitecanvas.canvas.CanvasBase.add_vline] and [`add_hline`][whitecanvas.canvas.CanvasBase.add_hline] for vertical and horizontal lines, respectively. + +``` python +#!name: infline_layer +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") +canvas.add_infline((0, 1), 45, color="black") # y = x + 1 +canvas.add_vline(2, color="red") # x = 2 +canvas.add_hline(-1, color="blue") # y = -1 +canvas.x.lim = (-3, 3) +canvas.y.lim = (-3, 3) +canvas.show() +``` + +## InfCurve + +`InfCurve` is a curve that extends to infinity, defined by an arbitrary model function. +Practically, it is achieved by connecting a callback that resamples the curve when the +canvas view range is updated. + +`InfCurve` is usually created by the [`add_infcurve`][whitecanvas.canvas.CanvasBase. +add_infcurve] method. The input model function must be defined as `model(x, ...)` where +`x` is `ndarray` of the x-coordinates and the rest of the arguments are the parameters +of the model. The parameters can be set by the `with_params` method of the returned +layer. + +``` python +#!name: infcurve_layer +import numpy as np +from whitecanvas import new_canvas + +def model(x, freq, phase): + return np.sin(x * freq - phase) + +canvas = new_canvas("matplotlib") +canvas.add_infcurve(model, color="black").with_params(freq=2, phase=0) +canvas.add_infcurve(model, color="black", style=":").with_params(freq=2, phase=1.6) +canvas.x.lim = (-3, 3) +canvas.y.lim = (-3, 3) +canvas.show() +``` diff --git a/docs/layers/markers.md b/docs/layers/markers.md new file mode 100644 index 00000000..8dc51c0d --- /dev/null +++ b/docs/layers/markers.md @@ -0,0 +1,254 @@ +# Markers + +`Markers` is a layer for scatter plots. It can be created with the `add_markers` method. + +``` python +#!name: markers_layer +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") +layer = canvas.add_markers([0, 1, 2, 3], [0, 1, 1, 0]) +canvas.show() +``` + +Symbol and size of the markers can easily be configured with the `symbol` and `size` +arguments. + +``` python +#!name: markers_layer_with_symbol_and_size +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") +for i, symbol in enumerate(["o", "s", "x", "+"]): + for j, size in enumerate([5, 10, 15, 20]): + x = [i - 0.2, i - 0.2, i + 0.2, i + 0.2] + y = [j - 0.2, j + 0.2, j - 0.2, j + 0.2] + layer = canvas.add_markers(x, y, symbol=symbol, size=size) +canvas.show() +``` + +!!! note + If the symbol is edge-only, its color and visibility are automatically updated to + ensure the markers are informative. + +## Marker Properties + +`Markers` layer has two namespaces: `face` and `edge`. `face` has following properties: + +- `color` ... color of the faces. Any color-like object is accepted. +- `hatch` ... hatch pattern of the faces. Should be one of `""`, `"-"`, `"|`, `"+"`, + `"/"`, `"\\"`, `"x"` or `"."`. + +!!! note + `hatch` is not supported in some backends. + +`edge` has following properties: + +- `color` ... color of the lines. Any color-like object is accepted. +- `width` ... width of the lines. Should be a non-negative number. +- `style` ... style of the lines. Should be one of `"-"`, `":"`, `"-."`, `"--"`. + +!!! note + `style` is not supported in some backends. + +Methods for adding these layers always configure the `face` properties with the +arguments. You can use the `with_edge` method of the output layer to set edge +properties. This separation is very helpful to prevent the confusion of the arguments, +especially the colors. + +``` python +#!name: markers_layer_with_edge +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +canvas.add_markers(np.sin(np.arange(10)), color="yellow").with_edge(color="black") +canvas.show() +``` + +All the properties can be set via properties of `face` and `edge`, or the `update` +method. + +``` python +layer.face.color = "yellow" +layer.face.hatch = "x" + +layer.edge.color = "black" +layer.edge.width = 2 +layer.edge.style = "--" + +# use `update` +layer.face.update(color="yellow", hatch="x") +layer.edge.update(color="black", width=2, style="--") +``` + +## Multi-face and Multi-edge Markers + +`Markers` supports multi-face and multi-edge. This means that you can create a layer +with multiple colors, widths, etc. + +To do this, you have to call `with_face_multi` or `with_edge_multi` method. +Here's an example of `Markers` with multi-faces. + +``` python +#!name: markers_layer_multifaces +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +layer = ( + canvas + .add_markers(np.arange(10), np.sin(np.arange(10))) + .with_face_multi(color=np.random.random((10, 3))) # random colors +) +canvas.show() +``` + +After calling `with_face_multi`, the layer `face` property will return arrays instead +of scalar values. + +``` python +#!skip +layer.face.color # (N, 4) array of RGBA colors +layer.face.hatch # (N,) array of hatchs +layer.face.alpha # (N,) array of alpha values +``` + +!!! note + IDE **can** detect whether a `Markers` layer is multi-face or not. `Markers` class + is a generic class with type variables for the face types. Since `with_face_multi` + is correctly typed, IDE will recognize the returned layer as a multi-face layer. + +Similarly, you can use `with_edge_multi` to create multi-edge markers. + +``` python +#!name: markers_layer_multiedges +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +layer = ( + canvas + .add_markers(np.arange(10), np.sin(np.arange(10))) + .with_edge_multi(width=np.abs(np.sin(np.arange(10))) * 2.5) +) +canvas.show() +``` + +## Multi-size Markers + +`with_size_multi` method can be used to create multi-size markers. + +``` python +#!name: markers_layer_multisizes +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +layer = ( + canvas + .add_markers(np.arange(10), np.sin(np.arange(10))) + .with_size_multi(np.abs(np.sin(np.arange(10))) * 16) +) +canvas.show() +``` + + +## Hover Text + +### Give a sequence of hover texts + +`with_hover_text` method sets the hover text of the markers. + +``` python +#!skip +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +layer = ( + canvas + .add_markers(np.arange(10), np.sin(np.arange(10))) + .with_hover_text([f"point {i}" for i in range(10)]) +) +canvas.show() +``` + +### Give a hover template + +``` python +#!skip +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") + +layer = ( + canvas + .add_markers(np.arange(10), np.sin(np.arange(10))) + .with_hover_template("x={x}, y={y}, i={i}") +) +canvas.show() +``` + +## Picking Markers + +Markers have a `picked` event. You can connect a callback to the event to handle the +picking. + +``` python +#!skip +import numpy as np +from whitecanvas import new_canvas + +canvas = new_canvas("matplotlib") +layer = canvas.add_markers([0, 1, 2], [0, 0, 0]) + +@layer.events.picked.connect +def _on_pick(picked): + print(f"picked indices: {picked}") +``` + +## Methods for Better Interpretability + +`Markers` is implemented with the following methods to make the plot more interpretable. + +### Color by density + +Scatter plot is good at visualizing the outliers, but not at telling the distribution +when the density is high. In this case, `color_by_density` method is very useful. It +colors the markers by the density of the points using kernel density estimation. + +``` python +#!name: markers_layer_color_by_density +#!width: 500 +import numpy as np +from whitecanvas import hgrid + +rng = np.random.default_rng(999) +x = rng.normal(size=1000) +y = rng.normal(size=1000) + +canvas = hgrid(2, backend="matplotlib") +( + canvas + .add_canvas(0) + .update_labels(title="no coloring") + .add_markers(x, y) +) +( + canvas + .add_canvas(1) + .update_labels(title="with coloring") + .add_markers(x, y) + .color_by_density(cmap="viridis") +) +canvas.show() +``` diff --git a/mkdocs.yml b/mkdocs.yml index 7efa2d17..963bd375 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,11 +24,14 @@ nav: - Quick Start: quick_start.md - Canvas: - Overview: canvas/index.md + - Basics: canvas/basics.md - Namespaces: canvas/namespaces.md + - Canvas Grid: canvas/grid.md - Working with the Backend Objects: canvas/native_objects.md - Layers: - Overview: layers/index.md - - Line Layers: layers/line_layers.md + - Lines: layers/lines.md + - Markers: layers/markers.md - Face&Edge Layers: layers/face_layers.md - Layer Groups: layers/layer_groups.md - Categorical Plot: diff --git a/whitecanvas/canvas/_base.py b/whitecanvas/canvas/_base.py index 8327c5e0..15ea8a4e 100644 --- a/whitecanvas/canvas/_base.py +++ b/whitecanvas/canvas/_base.py @@ -352,13 +352,16 @@ def cat( data : tabular data Any categorizable data. Currently, dict, pandas.DataFrame, and polars.DataFrame are supported. - + x : str, optional + Name of the column that will be used for the x-axis. Must be numerical. + y : str, optional + Name of the column that will be used for the y-axis. Must be numerical. update_labels : bool, default True If True, update the x/y labels to the corresponding names. Returns ------- - CategorizedPlot + CatPlotter Plotter object. """ plotter = _df.CatPlotter(self, data, x, y, update_label=update_labels) @@ -372,6 +375,26 @@ def cat_x( *, update_labels: bool = True, ) -> _df.XCatPlotter[Self, _DF]: + """ + Categorize input data for plotting with x-axis as a categorical axis. + + Parameters + ---------- + data : tabular data + Any categorizable data. Currently, dict, pandas.DataFrame, and + polars.DataFrame are supported. + x : str or sequence of str, optional + Name of the column(s) that will be used for the x-axis. Must be categorical. + y : str, optional + Name of the column that will be used for the y-axis. Must be numerical. + update_labels : bool, default True + If True, update the x/y labels to the corresponding names. + + Returns + ------- + XCatPlotter + Plotter object. + """ return _df.XCatPlotter(self, data, x, y, update_labels) def cat_y( @@ -382,6 +405,26 @@ def cat_y( *, update_labels: bool = True, ) -> _df.YCatPlotter[Self, _DF]: + """ + Categorize input data for plotting with y-axis as a categorical axis. + + Parameters + ---------- + data : tabular data + Any categorizable data. Currently, dict, pandas.DataFrame, and + polars.DataFrame are supported. + x : str, optional + Name of the column that will be used for the x-axis. Must be numerical. + y : str or sequence of str, optional + Name of the column(s) that will be used for the y-axis. Must be categorical. + update_labels : bool, default True + If True, update the x/y labels to the corresponding names. + + Returns + ------- + YCatPlotter + Plotter object + """ return _df.YCatPlotter(self, data, y, x, update_labels) def cat_xy( @@ -392,6 +435,26 @@ def cat_xy( *, update_labels: bool = True, ) -> _df.XYCatPlotter[Self, _DF]: + """ + Categorize input data for plotting with both axes as categorical. + + Parameters + ---------- + data : tabular data + Any categorizable data. Currently, dict, pandas.DataFrame, and + polars.DataFrame are supported. + x : str or sequence of str, optional + Name of the column(s) that will be used for the x-axis. Must be categorical. + y : str or sequence of str, optional + Name of the column(s) that will be used for the y-axis. Must be categorical. + update_labels : bool, default True + If True, update the x/y labels to the corresponding names. + + Returns + ------- + XYCatPlotter + Plotter object + """ return _df.XYCatPlotter(self, data, x, y, update_labels) def stack_over(self, layer: _L0) -> StackOverPlotter[Self, _L0]: @@ -406,11 +469,13 @@ def stack_over(self, layer: _L0) -> StackOverPlotter[Self, _L0]: will result in a bar plot like this + ``` ┌───┐ ├───│┌───┐ │ │├───│ ├───│├───│ ─┴───┴┴───┴─ + ``` """ if not isinstance(layer, (_l.Bars, _l.Band, _lg.StemPlot, _lg.LabeledBars)): raise TypeError( @@ -968,7 +1033,7 @@ def add_infcurve( width = theme._default("line.width", width) style = theme._default("line.style", style) layer = _l.InfCurve( - model, bounds=bounds, name=name, color=color, width=width, + model, bounds=bounds, name=name, color=color, width=width, alpha=alpha, style=style, antialias=antialias, backend=self._get_backend(), ) # fmt: skip return self.add_layer(layer) @@ -979,11 +1044,36 @@ def add_hline( *, name: str | None = None, color: ColorType | None = None, - width: float = 1.0, + width: float | None = None, style: LineStyle | str = LineStyle.SOLID, alpha: float = 1.0, antialias: bool = True, ) -> _l.InfLine: + """ + Add a infinite horizontal line to the canvas. + + Parameters + ---------- + y : float + Y coordinate of the line. + name : str, optional + Name of the layer. + color : color-like, optional + Color of the bars. + width : float, optional + Line width. Use the theme default if not specified. + style : str or LineStyle, optional + Line style. Use the theme default if not specified. + alpha : float, default 1.0 + Alpha channel of the line. + antialias : bool, default True + Antialiasing of the line. + + Returns + ------- + InfLine + The infline layer. + """ return self.add_infline( (0, y), 0, name=name, color=color, width=width, style=style, alpha=alpha, antialias=antialias @@ -995,11 +1085,36 @@ def add_vline( *, name: str | None = None, color: ColorType | None = None, - width: float = 1.0, + width: float | None = None, style: LineStyle | str = LineStyle.SOLID, alpha: float = 1.0, antialias: bool = True, ) -> _l.InfLine: + """ + Add a infinite vertical line to the canvas. + + Parameters + ---------- + x : float + X coordinate of the line. + name : str, optional + Name of the layer. + color : color-like, optional + Color of the bars. + width : float, optional + Line width. Use the theme default if not specified. + style : str or LineStyle, optional + Line style. Use the theme default if not specified. + alpha : float, default 1.0 + Alpha channel of the line. + antialias : bool, default True + Antialiasing of the line. + + Returns + ------- + InfLine + The infline layer. + """ return self.add_infline( (x, 0), 90, name=name, color=color, width=width, style=style, alpha=alpha, antialias=antialias,