Skip to content

Commit b7b0569

Browse files
Filmbostock
authored andcommitted
document group & bin (observablehq#1360)
* document group and bin * edits * allow iterable thresholds * edits * more type specificity * edits * checkpoint edits * checkpoint edits * edits * fix observablehq#1384 - ChannelValueBinSpec --------- Co-authored-by: Mike Bostock <[email protected]>
1 parent cf82ab0 commit b7b0569

File tree

14 files changed

+724
-298
lines changed

14 files changed

+724
-298
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,7 @@ Most aggregation methods require binding the output channel to an input channel;
22872287
Plot.groupX({y: "sum"}, {x: "species", y: "body_mass_g"})
22882288
```
22892289
2290-
You can control whether a channel is computed before or after grouping. If a channel is declared only in *options* (and it is not a special group-eligible channel such as *x*, *y*, *z*, *fill*, or stroke), it will be computed after grouping and be passed the grouped data: each datum is the array of input data corresponding to the current group.
2290+
You can control whether a channel is computed before or after grouping. If a channel is declared only in *options* (and it is not a special group-eligible channel such as *x*, *y*, *z*, *fill*, or *stroke*), it will be computed after grouping and be passed the grouped data: each datum is the array of input data corresponding to the current group.
22912291
22922292
```js
22932293
Plot.groupX({y: "count"}, {x: "species", title: group => group.map(d => d.body_mass_g).join("\n")})

src/channel.d.ts

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ export interface Channel {
118118
/**
119119
* A channel’s values may be expressed as:
120120
*
121-
* * a function that returns the corresponding value for each datum
122-
* * a field name, to extract the corresponding value for each datum
123-
* * an iterable of values, typically of the same length as the data
124-
* * a channel transform that returns an iterable of values given the data
125-
* * a constant date, number, or boolean
126-
* * null to represent no value
121+
* - a function that returns the corresponding value for each datum
122+
* - a field name, to extract the corresponding value for each datum
123+
* - an iterable of values, typically of the same length as the data
124+
* - a channel transform that returns an iterable of values given the data
125+
* - a constant date, number, or boolean
126+
* - null to represent no value
127127
*/
128128
export type ChannelValue =
129129
| Iterable<any> // column of values
@@ -152,42 +152,26 @@ export type ChannelValueIntervalSpec = ChannelValueSpec | {value: ChannelValue;
152152
* The available inputs for imputing scale domains. In addition to a named
153153
* channel, an input may be specified as:
154154
*
155-
* * *data* - impute from mark data
156-
* * *width* - impute from |*x2* - *x1*|
157-
* * *height* - impute from |*y2* - *y1*|
158-
* * null - impute from input order
155+
* - *data* - impute from mark data
156+
* - *width* - impute from |*x2* - *x1*|
157+
* - *height* - impute from |*y2* - *y1*|
158+
* - null - impute from input order
159159
*/
160160
export type ChannelDomainValue = ChannelName | "data" | "width" | "height" | null;
161161

162162
/** Options for imputing scale domains from channel values. */
163163
export interface ChannelDomainOptions {
164164
/**
165165
* How to produce a singular value (for subsequent sorting) from aggregated
166-
* channel values. Defaults to *max*. A reducer may be specified as:
166+
* channel values; one of:
167167
*
168-
* * *first* - the first value, in input order
169-
* * *last* - the last value, in input order
170-
* * *count* - the number of elements (frequency)
171-
* * *distinct* - the number of distinct values
172-
* * *sum* - the sum of values
173-
* * *min* - the minimum value
174-
* * *min-index* - the zero-based index of the minimum value
175-
* * *max* - the maximum value
176-
* * *max-index* - the zero-based index of the maximum value
177-
* * *mean* - the mean value (average)
178-
* * *median* - the median value
179-
* * *mode* - the value with the most occurrences
180-
* * *pXX* - the percentile value, where XX is a number in [00,99]
181-
* * *deviation* - the standard deviation
182-
* * *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm)
183-
* * a function to be passed the array of values
184-
* * an object with a *reduce* method
185-
*
186-
* In the last case, the *reduce* method is repeatedly passed an index (an
187-
* array of integers) and the channel’s array of values; it must then return
188-
* the corresponding aggregate value for the bin.
168+
* - true (default) - alias for *max*
169+
* - false or null - disabled; don’t impute the scale domain
170+
* - a named reducer implementation such as *count* or *sum*
171+
* - a function that takes an array of values and returns the reduced value
172+
* - an object that implements the *reduceIndex* method
189173
*/
190-
reduce?: Reducer | true;
174+
reduce?: Reducer | boolean | null;
191175

192176
/** If true, use descending instead of ascending order. */
193177
reverse?: boolean;
@@ -209,7 +193,20 @@ export type ChannelDomainValueSpec = ChannelDomainValue | ({value: ChannelDomain
209193
/** How to impute scale domains from channel values. */
210194
export type ChannelDomainSort = {[key in ScaleName]?: ChannelDomainValueSpec} & ChannelDomainOptions;
211195

212-
/** How to reduce channel values, e.g. when binning or grouping. */
196+
/**
197+
* Output channels for aggregating transforms, such as bin and group. Each
198+
* declared output channel has an associated reducer, and typically a
199+
* corresponding input channel in *options*. Non-grouping channels declared in
200+
* *options* but not *outputs* are computed on reduced data after grouping,
201+
* which defaults to the array of data for the current group.
202+
*
203+
* If **title** is in *options* but not *outputs*, the reducer defaults to
204+
* summarizing the most common values. If **href** is in *options* but not
205+
* *outputs*, the reducer defaults to *first*. When **x1** or **x2** is in
206+
* *outputs*, reads the input channel **x** if **x1** or **x2** is not in
207+
* *options*; likewise for **y1** or **y2**, reads the input channel **y** if
208+
* **y1** or **y2** is not in *options*.
209+
*/
213210
export type ChannelReducers<T = Reducer> = {[key in ChannelName]?: T | {reduce: T; scale?: Channel["scale"]} | null};
214211

215212
/** Abstract (unscaled) values, and associated scale, per channel. */

src/interval.d.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
/** The built-in time intervals; UTC or local time, depending on context. */
1+
/**
2+
* The built-in time intervals; UTC or local time, depending on context. The
3+
* *week* interval is an alias for *sunday*. The *quarter* interval is every
4+
* three months, and the *half* interval is every six months, aligned at the
5+
* start of the year.
6+
*/
27
export type TimeIntervalName =
38
| "second"
49
| "minute"
@@ -65,27 +70,48 @@ export interface RangeIntervalImplementation<T> extends IntervalImplementation<T
6570
*/
6671
export interface NiceIntervalImplementation<T> extends RangeIntervalImplementation<T> {
6772
/**
68-
* Returns a new date representing the earliest interval boundary date after
69-
* or equal to date. For example, d3.timeDay.ceil(date) typically returns
70-
* 12:00 AM local time on the date following the given date.
73+
* Returns the value representing the least interval boundary value greater
74+
* than or equal to the specified *value*. For example, day.ceil(*date*)
75+
* typically returns 12:00 AM on the date following the given date.
7176
*
7277
* This method is idempotent: if the specified date is already ceilinged to
73-
* the current interval, a new date with an identical time is returned.
74-
* Furthermore, the returned date is the maximum expressible value of the
75-
* associated interval, such that interval.ceil(interval.ceil(date) + 1)
76-
* returns the following interval boundary date.
78+
* the current interval, the same value is returned. Furthermore, the returned
79+
* value is the maximum expressible value of the associated interval, such
80+
* that ceil(ceil(*value*) + *epsilon*) returns the following interval
81+
* boundary value.
7782
*/
7883
ceil(value: T): T;
7984
}
8085

8186
/** A literal that can be automatically promoted to an interval. */
8287
type LiteralInterval<T> = T extends Date ? TimeIntervalName : T extends number ? number : never;
8388

84-
/** How to partition a continuous range into discrete intervals. */
89+
/**
90+
* How to partition a continuous range into discrete intervals; one of:
91+
*
92+
* - an object that implements *floor* and *offset* methods
93+
* - a named time interval such as *day* (for date intervals)
94+
* - a number (for number intervals), defining intervals at integer multiples of *n*
95+
*/
8596
export type Interval<T = any> = LiteralInterval<T> | IntervalImplementation<T>;
8697

87-
/** An interval that supports the range method, say for thresholds or ticks. */
98+
/**
99+
* An interval that also supports the *range* method, used to subdivide a
100+
* continuous range into discrete partitions, say for thresholds or ticks; one
101+
* of:
102+
*
103+
* - an object that implements *floor*, *offset*, and *range* methods
104+
* - a named time interval such as *day* (for date intervals)
105+
* - a number (for number intervals), defining intervals at integer multiples of *n*
106+
*/
88107
export type RangeInterval<T = any> = LiteralInterval<T> | RangeIntervalImplementation<T>;
89108

90-
/** An interval that can be used to nice a scale domain. */
109+
/**
110+
* A range interval that also supports the *ceil* method, used to nice a scale
111+
* domain; one of:
112+
*
113+
* - an object that implements *floor*, *ceil*, *offset*, and *range* methods
114+
* - a named time interval such as *day* (for date intervals)
115+
* - a number (for number intervals), defining intervals at integer multiples of *n*
116+
*/
91117
export type NiceInterval<T = any> = LiteralInterval<T> | NiceIntervalImplementation<T>;

src/marks/vector.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type VectorShapeName = "arrow" | "spike";
66

77
/** A vector shape implementation. */
88
export interface VectorShapeImplementation {
9+
/** Draws a shape of the given *length* and *radius* to the given *context*. */
910
draw(context: CanvasPath, length: number, radius: number): void;
1011
}
1112

@@ -53,9 +54,9 @@ export interface VectorOptions extends MarkOptions {
5354
* The vector’s position along its orientation relative to its anchor point; a
5455
* constant. Assuming a default **rotate** angle of 0°, one of:
5556
*
56-
* * *start* - from [*x*, *y*] to [*x*, *y* - *l*]
57-
* * *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2]
58-
* * *end* - from [*x*, *y* + *l*] to [*x*, *y*]
57+
* - *start* - from [*x*, *y*] to [*x*, *y* - *l*]
58+
* - *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2]
59+
* - *end* - from [*x*, *y* + *l*] to [*x*, *y*]
5960
*
6061
* where [*x*, *y*] is the vector’s anchor point and *l* is the vector’s
6162
* (possibly scaled) length in pixels.

src/reducer.d.ts

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
11
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
22

3+
// For internal use.
34
export type ReducerPercentile =
45
| (`p${Digit}${Digit}` & Record<never, never>) // see https://github.com/microsoft/TypeScript/issues/29729
56
| "p25"
67
| "p50"
78
| "p75";
89

10+
/**
11+
* The built-in reducer implementations; one of:
12+
*
13+
* - *first* - the first value, in input order
14+
* - *last* - the last value, in input order
15+
* - *count* - the number of elements (frequency)
16+
* - *distinct* - the number of distinct values
17+
* - *sum* - the sum of values
18+
* - *proportion* - the sum proportional to the overall total (weighted frequency)
19+
* - *proportion-facet* - the sum proportional to the facet total
20+
* - *deviation* - the standard deviation
21+
* - *min* - the minimum value
22+
* - *min-index* - the zero-based index of the minimum value
23+
* - *max* - the maximum value
24+
* - *max-index* - the zero-based index of the maximum value
25+
* - *mean* - the mean value (average)
26+
* - *median* - the median value
27+
* - *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm)
28+
* - *mode* - the value with the most occurrences
29+
* - *pXX* - the percentile value, where XX is a number in [00,99]
30+
*/
931
export type ReducerName =
1032
| "first"
1133
| "last"
@@ -25,11 +47,30 @@ export type ReducerName =
2547
| "mode"
2648
| ReducerPercentile;
2749

28-
export type ReducerFunction = (values: any[]) => any;
50+
/**
51+
* A shorthand functional reducer implementation: given an array of input
52+
* channel *values*, returns the corresponding reduced output value.
53+
*/
54+
export type ReducerFunction<S = any, T = S> = (values: S[]) => T;
2955

30-
// TODO scope, label
31-
export interface ReducerImplementation {
32-
reduceIndex(index: number[], values: any[]): any;
56+
/** A reducer implementation. */
57+
export interface ReducerImplementation<S = any, T = S> {
58+
/**
59+
* Given an *index* representing the contents of the current group and the
60+
* input channel’s array of *values*, returns the corresponding reduced output
61+
* value. If no input channel is supplied (e.g., as with the *count* reducer)
62+
* then *values* may be undefined.
63+
*/
64+
reduceIndex(index: number[], values: S[]): T;
65+
// TODO scope
66+
// TODO label
3367
}
3468

69+
/**
70+
* How to reduce aggregated (binned or grouped) values; one of:
71+
*
72+
* - a named reducer implementation such as *count* or *sum*
73+
* - a function that takes an array of values and returns the reduced value
74+
* - an object that implements the *reduceIndex* method
75+
*/
3576
export type Reducer = ReducerName | ReducerFunction | ReducerImplementation;

0 commit comments

Comments
 (0)