CentreForDigitalHumanities / CentreForDigitalHumanities/Textcavator
Restructure barchart components: outfactor aggregation functions
- Dominant language
- Python
- Stars
- 12
- Forks
- 3
- Avg merge
- 3d 3m
- Merged PRs (30d)
- 9
Description
The barchart component was initially presenting one type of information (the number of results), and split into two child components based on the x axis scale - either a categorical value or a date. (That distinction is non-trivial because the date version loads more fine-grained data when zooming in.)
To realise a term frequency graph, the logic for that was added to the barchart component. I considered using two component classes here (e.g. a `NumberOfResultsComponent` and a `TermFrequencyComponent`), but this would create a combinatoric mess with the already existing subclasses for categorical/date axes.
#710 and #1137 both make suggestions to use the barchart for a different type of data, but realising this within the existing `BarchartComponent` would be a complete mess. We should make a common abstraction that allows the barchart component to handle all these different types of data.
The way that these work is roughly the following:
For the number of results, total word count, and percentage of null values, we have an aggregation function $a$ which takes a query $Q$ describing a subset of the corpus. $a(Q)$ returns some quantity $x$, such as a number of documents.
In addition, we can formulate a bucket aggregation $b_a$, where we additionally specificy a field $F$. Then $b_a(Q, F) \mapsto \langle B, X \rangle$; it returns a vector of bins $B$ and a vector of results $X$ of the same length. E.g., $B$ could be a list of years and $X$ the number of documents per year.
At the moment, the term frequency is structured differently as it does not handle bucketing. So rather than a field $F$, it takes a vector of bins $B$ as input: $b_a(Q, B) \mapsto X$. $B$ is generated by the number of documents aggregation. This can be reworked into the format above, though, either by wrapping both calls in a single frontend function, or by handling binning within the backend view.
One complication here is that each aggregation function $a$ actually does not return a single real number, but several, which enables the frontend to switch between different normalisation options. (For example, the absolute and relative value.) Which values are returned depends somewhat on the graph.
So we could conceptualise a `AggregationData` class which contains:
- A `DataPoint` type that specifies what its results looks like
- Some specification of what options should be displayed for normalisation in the UI, e.g. a list of properties of `DataPoint` with a corresponding label.
- a function `bucketAggregate(queryModel: QueryModel, field: CorpusField): Promise` which fetches the results
- a function `toValue(point: DataPoint, normalization): number` which converts a datapoint to a y-axis value
- a function `tableHeaders(normalization)` to formulate the table view.
Then you can have subclasses like `ResultsAggregationData`, `TermFreqAggregationData`, `WordCountAggregationData`, `NullAggregationData`.
(Side note: such objects may also be used elsewhere, like in the multiple choice filter.)
This would make it fairly easy for the `BarchartComponent` to switch between different types of aggregations, and make it easier to add new aggregation types.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.