Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

document group & bin #1360

Merged
merged 11 commits into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2287,7 +2287,7 @@ Most aggregation methods require binding the output channel to an input channel;
Plot.groupX({y: "sum"}, {x: "species", y: "body_mass_g"})
```

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.
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.

```js
Plot.groupX({y: "count"}, {x: "species", title: group => group.map(d => d.body_mass_g).join("\n")})
Expand Down
65 changes: 31 additions & 34 deletions src/channel.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ export interface Channel {
/**
* A channel’s values may be expressed as:
*
* * a function that returns the corresponding value for each datum
* * a field name, to extract the corresponding value for each datum
* * an iterable of values, typically of the same length as the data
* * a channel transform that returns an iterable of values given the data
* * a constant date, number, or boolean
* * null to represent no value
* - a function that returns the corresponding value for each datum
* - a field name, to extract the corresponding value for each datum
* - an iterable of values, typically of the same length as the data
* - a channel transform that returns an iterable of values given the data
* - a constant date, number, or boolean
* - null to represent no value
*/
export type ChannelValue =
| Iterable<any> // column of values
Expand Down Expand Up @@ -152,42 +152,26 @@ export type ChannelValueIntervalSpec = ChannelValueSpec | {value: ChannelValue;
* The available inputs for imputing scale domains. In addition to a named
* channel, an input may be specified as:
*
* * *data* - impute from mark data
* * *width* - impute from |*x2* - *x1*|
* * *height* - impute from |*y2* - *y1*|
* * null - impute from input order
* - *data* - impute from mark data
* - *width* - impute from |*x2* - *x1*|
* - *height* - impute from |*y2* - *y1*|
* - null - impute from input order
*/
export type ChannelDomainValue = ChannelName | "data" | "width" | "height" | null;

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

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

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

/** Abstract (unscaled) values, and associated scale, per channel. */
Expand Down
48 changes: 37 additions & 11 deletions src/interval.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/** The built-in time intervals; UTC or local time, depending on context. */
/**
* The built-in time intervals; UTC or local time, depending on context. The
* *week* interval is an alias for *sunday*. The *quarter* interval is every
* three months, and the *half* interval is every six months, aligned at the
* start of the year.
*/
export type TimeIntervalName =
| "second"
| "minute"
Expand Down Expand Up @@ -65,27 +70,48 @@ export interface RangeIntervalImplementation<T> extends IntervalImplementation<T
*/
export interface NiceIntervalImplementation<T> extends RangeIntervalImplementation<T> {
/**
* Returns a new date representing the earliest interval boundary date after
* or equal to date. For example, d3.timeDay.ceil(date) typically returns
* 12:00 AM local time on the date following the given date.
* Returns the value representing the least interval boundary value greater
* than or equal to the specified *value*. For example, day.ceil(*date*)
* typically returns 12:00 AM on the date following the given date.
*
* This method is idempotent: if the specified date is already ceilinged to
* the current interval, a new date with an identical time is returned.
* Furthermore, the returned date is the maximum expressible value of the
* associated interval, such that interval.ceil(interval.ceil(date) + 1)
* returns the following interval boundary date.
* the current interval, the same value is returned. Furthermore, the returned
* value is the maximum expressible value of the associated interval, such
* that ceil(ceil(*value*) + *epsilon*) returns the following interval
* boundary value.
*/
ceil(value: T): T;
}

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

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

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

/** An interval that can be used to nice a scale domain. */
/**
* A range interval that also supports the *ceil* method, used to nice a scale
* domain; one of:
*
* - an object that implements *floor*, *ceil*, *offset*, and *range* methods
* - a named time interval such as *day* (for date intervals)
* - a number (for number intervals), defining intervals at integer multiples of *n*
*/
export type NiceInterval<T = any> = LiteralInterval<T> | NiceIntervalImplementation<T>;
7 changes: 4 additions & 3 deletions src/marks/vector.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type VectorShapeName = "arrow" | "spike";

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

Expand Down Expand Up @@ -53,9 +54,9 @@ export interface VectorOptions extends MarkOptions {
* The vector’s position along its orientation relative to its anchor point; a
* constant. Assuming a default **rotate** angle of 0°, one of:
*
* * *start* - from [*x*, *y*] to [*x*, *y* - *l*]
* * *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2]
* * *end* - from [*x*, *y* + *l*] to [*x*, *y*]
* - *start* - from [*x*, *y*] to [*x*, *y* - *l*]
* - *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2]
* - *end* - from [*x*, *y* + *l*] to [*x*, *y*]
*
* where [*x*, *y*] is the vector’s anchor point and *l* is the vector’s
* (possibly scaled) length in pixels.
Expand Down
49 changes: 45 additions & 4 deletions src/reducer.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

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

/**
* The built-in reducer implementations; one of:
*
* - *first* - the first value, in input order
* - *last* - the last value, in input order
* - *count* - the number of elements (frequency)
* - *distinct* - the number of distinct values
* - *sum* - the sum of values
* - *proportion* - the sum proportional to the overall total (weighted frequency)
* - *proportion-facet* - the sum proportional to the facet total
* - *deviation* - the standard deviation
* - *min* - the minimum value
* - *min-index* - the zero-based index of the minimum value
* - *max* - the maximum value
* - *max-index* - the zero-based index of the maximum value
* - *mean* - the mean value (average)
* - *median* - the median value
* - *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm)
* - *mode* - the value with the most occurrences
* - *pXX* - the percentile value, where XX is a number in [00,99]
*/
export type ReducerName =
| "first"
| "last"
Expand All @@ -25,11 +47,30 @@ export type ReducerName =
| "mode"
| ReducerPercentile;

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

// TODO scope, label
export interface ReducerImplementation {
reduceIndex(index: number[], values: any[]): any;
/** A reducer implementation. */
export interface ReducerImplementation<S = any, T = S> {
/**
* Given an *index* representing the contents of the current group and the
* input channel’s array of *values*, returns the corresponding reduced output
* value. If no input channel is supplied (e.g., as with the *count* reducer)
* then *values* may be undefined.
*/
reduceIndex(index: number[], values: S[]): T;
// TODO scope
// TODO label
}

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