Possible improvements to antialiased line implementation
- Dominant language
- Python
- Stars
- 3.6k
- Forks
- 376
- Avg merge
- 4h 32m
- Merged PRs (30d)
- 1
Description
The current state of antialiased lines with various reductions was summaries in PR #1146. Test code and images are reproduced here:
```python
import datashader as ds
import datashader.transfer_functions as tf
import numpy as np
import pandas as pd
df = pd.DataFrame(dict(ystart=[0.2, 0, 1, 0.8], yend=[0.2, 1, 0, 0.8], value=[1, 2, 3, 4]))
cvs = ds.Canvas(plot_width=200, plot_height=150, x_range=(-0.1, 1.1), y_range=(-0.1, 1.1))
kwargs = dict(source=df, x=np.asarray([0, 1]), y=["ystart", "yend"], axis=1, line_width=15)
for i, reduction in enumerate([
ds.any(), ds.count(), ds.sum("value"), ds.min("value"), ds.max("value"),
ds.first("value"), ds.last("value"), ds.mean("value")
]):
agg = cvs.line(agg=reduction, **kwargs)
im = tf.shade(agg, how="linear")
ds.utils.export_image(im, f"temp{i}")
```

Possible improvement (again copied from PR #1146):
There is no workaround for this, it would need a more complicated implementation as we don't store the information required to do this. We would need to separate out the antialias `weighting` (what would conventionally be called the `alpha` but trying not to use that term here) from the normal aggregate numbers. For the `count` of a single line segment the `count` agg would be 1 for every pixel in the line right up to the edges, and the `weighting` would be 1 along the middle of the line, down to just about 0 at the extreme edges. The 2D agg returned to the user would be the product of the two, giving the same number as we currently return (for a single line).
For a compound reduction of a `count` and `sum` we can imaging 3 aggs, `count`, `sum` and `weighting`. For the `count` we'd return `count*weighting` to the user, for the `sum` we'd return `sum*weighting`, and for the `mean` we'd return `(sum/count)*weighing`, giving you exactly what you want.
In general, we couldn't just have a single `weighting` per `canvas.line` call, we'd need a `weighing` per agg. So maybe we'd think of changing each of the current agg's shapes from `(height, width)` to `(height, width, 2)`, so the agg is attached to its weighting.
We would need to decide on appropriate mathematics to combine say `(value1, weighting1)` with `(value2, weighting2)` for a particular pixel. Let's assume we want a linear combination, so the combined value must be `value1*weighting1 + value2*weighting2`. Or is it? It might be more sensible to say we want the combined `value` and `weighting` so that `value*weighting = value1*weighting1 + value2*weighting2` because the `value` needs to be stored unweighted (the whole purpose of this approach is to keep the `weighting` separate from the `value`). This maths is not dissimilar to combined rendering of RGB and A separately, but there we have the concept of rendering on top of existing colors in a non-commutative way. So it is more complicated than that!
In summary, to obtain a mean that more in line with what is expected would require:
- Twice as much memory for the 3D aggs.
- Slightly more complicated code, but only at the very highest and lowest levels as we'd still be scanning the source dataframe just once, only with 3D aggs.
- Thought about appropriate maths to combine values and weights (which may or may not exist)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing PR #1146 and the reproduced Canvas.line reduction example. Clarify the mathematics for combining values and antialias weighting, including count, sum, and mean, before deciding the implementation scope. Done means antialiased line reductions produce the intended mean and preserve appropriate aggregate behavior across the shown reductions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data-visualization
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100