Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
4c29c23
feat: use `narwhals` for data schema inference
peter-gy Jul 8, 2025
904fb99
chore: remove unnecessary type `cast`
peter-gy Jul 8, 2025
e09f921
chore: add `vertical_scrolling_y` and `vertical_scrolling_row` prefs
peter-gy Jul 21, 2025
0d43e3b
chore: add `high_cardinality_facet` pref
peter-gy Jul 21, 2025
67bf47d
fix: allow entropy to be zero when validating spec
peter-gy Jul 21, 2025
572fa2f
feat: `dracox` initial
peter-gy Jul 21, 2025
ffde893
chore: add `same_mark_type` and `same_channel` prefs
peter-gy Jul 25, 2025
80fe880
chore: add `facet_used_before_color` preference
peter-gy Jul 25, 2025
d95e5e1
chore: adjust `bin_low_unique`
peter-gy Jul 27, 2025
12e9323
chore: add `low_cardinality_categorical_lte10`
peter-gy Jul 27, 2025
8e240b8
chore: add `date_not_line`
peter-gy Jul 27, 2025
394f717
chore: remove `low_cardinality_categorical_lte10`
peter-gy Jul 27, 2025
af79960
chore: move `mypy` config to `pyproject.toml`
peter-gy Jul 27, 2025
64a5c4f
fix: get rid of type errors
peter-gy Jul 27, 2025
95ed93c
feat: add `skew` to data profile
peter-gy Jul 27, 2025
2d38220
chore: add `skewed_not_log`
peter-gy Jul 27, 2025
c6eb8d7
feat: support `symlog` scale
peter-gy Jul 27, 2025
6e48793
chore: various weight adjustments and new rules
peter-gy Jul 27, 2025
6cf1083
refactor: simplify `facet_used_before_color` preference and update `a…
peter-gy Jul 27, 2025
e9ed485
feat: add `@api(require(facet, fieldname))` to express API
peter-gy Jul 27, 2025
2166a6b
chore: add `bin_string` preference
peter-gy Jul 27, 2025
a825139
feat: facilitate debugging weight key mismatches
peter-gy Jul 28, 2025
f1f9c70
chore: remove duplicate API
peter-gy Jul 28, 2025
8ae5172
chore: add preferences and weights for low/high cardinality categoric…
peter-gy Jul 28, 2025
6bda890
chore: add `value_bin` preference
peter-gy Jul 28, 2025
849973e
fix: check weight key consistency properly
peter-gy Jul 28, 2025
6b2395b
fix: address type issues
peter-gy Jul 28, 2025
77c6f97
chore: add preference and weight for high cardinality point color bef…
peter-gy Jul 29, 2025
d849a9e
fix: handle `skew` of constant fields
peter-gy Jul 29, 2025
d298eef
feat: add __repr__ method to `DracoChartSpec`
peter-gy Jul 29, 2025
0ce34e1
feat: add `min_length` and `max_length` properties to `StringFieldProps`
peter-gy Jul 29, 2025
872d2f0
chore: include new string field prompts into schema
peter-gy Jul 29, 2025
b2daacd
fix: adjust skew threshold and update facet weights
peter-gy Jul 29, 2025
8cdc879
refactor: extract common renderer utilities
peter-gy Jul 30, 2025
9a35969
feat: allow passing custom field labels to renderer
peter-gy Jul 30, 2025
d659928
feat: include all data fields in altair plot tooltips
peter-gy Jul 30, 2025
c3ce5a6
chore: adjust weights
peter-gy Jul 30, 2025
244b734
chore: add `d_d_point_color` pref
peter-gy Jul 30, 2025
f140d80
fix: include data schema facts into all facts
peter-gy Jul 30, 2025
503f702
feat: add preferences for string field label lengths in facets
peter-gy Jul 30, 2025
c02f2f4
feat: generalize label-length based rules to x, y, row, col
peter-gy Jul 30, 2025
30297b7
feat: default to displaying lines with `point` on Altair charts
peter-gy Jul 31, 2025
069e8fb
feat: allow passing mark config to Altair renderer
peter-gy Jul 31, 2025
66680e8
feat: allow passing custom renderer instance to `DracoChartSpec.render`
peter-gy Jul 31, 2025
fee8554
chore: also consider facets for `same_field_*` rules
peter-gy Jul 31, 2025
2cfc0d3
chore: various adjustments
peter-gy Jul 31, 2025
281174e
chore: renderer tweaks
peter-gy Jul 31, 2025
6490482
fix: handle edge case where `std` is none in schema
peter-gy Aug 6, 2025
978d1e8
feat: compute span of temporal fields and set time unit in renderer a…
peter-gy Aug 12, 2025
d2efdee
chore: update todo comment
peter-gy Aug 12, 2025
8bb0cf2
docs: add todo comment
peter-gy Aug 12, 2025
599fb3c
chore: various adjustments
peter-gy Aug 13, 2025
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
13 changes: 0 additions & 13 deletions .mypy.ini

This file was deleted.

2 changes: 1 addition & 1 deletion draco/asp/define.lp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ domain(scale_channel,V,C) :-

% @definition(scale_type) Scale types.
domain(discrete_scale,(ordinal;categorical)).
domain(continuous_scale,(log;linear)).
domain(continuous_scale,(log;symlog;linear)).
domain(scale_type,T) :- domain(discrete_scale,T).
domain(scale_type,T) :- domain(continuous_scale,T).

Expand Down
240 changes: 240 additions & 0 deletions draco/asp/express.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
% ====== Field Control ======

% @api(require(field, fieldname)) Guarantee that a field is visualized via encoding or facet
violation(field_not_visualized) :-
require(field, FieldName),
not attribute((encoding,field),_,FieldName),
not attribute((facet,field),_,FieldName).

% @api(require((field, encoding), fieldname)) Guarantee that a specific field is used in an encoding
violation(field_not_in_encoding) :-
require((field, encoding), FieldName),
not attribute((encoding,field),_,FieldName).

% @api(require((field, facet), fieldname)) Guarantee that a specific field is used in a facet
violation(field_not_in_facet) :-
require((field, facet), FieldName),
not attribute((facet,field),_,FieldName).

% @api(forbid(field, fieldname)) Forbid a field from being used in any visualization
violation(forbidden_field_used) :-
forbid(field, FieldName),
attribute((encoding,field),_,FieldName).

violation(forbidden_field_used) :-
forbid(field, FieldName),
attribute((facet,field),_,FieldName).

% @api(observed(field, fieldname)) Field is used (in encoding or facet)
observed(field, FieldName) :-
attribute((encoding,field),_,FieldName).

observed(field, FieldName) :-
attribute((facet,field),_,FieldName).

% @api(observed((field, encoding), fieldname)) Field is used in an encoding
observed((field, encoding), FieldName) :-
attribute((encoding,field),_,FieldName).

% @api(observed((field, facet), fieldname)) Field is used in a facet
observed((field, facet), FieldName) :-
attribute((facet,field),_,FieldName).

% ====== Mark Control ======

% @api(require(mark, marktype)) Guarantee that a specific mark type is used
violation(required_mark_missing) :-
require(mark, MarkType),
not attribute((mark,type),_,MarkType).

% @api(forbid(mark, marktype)) Forbid a specific mark type from being used
violation(forbidden_mark_used) :-
forbid(mark, MarkType),
attribute((mark,type),_,MarkType).

% @api(observed(mark, marktype)) Mark type is used
observed(mark, MarkType) :-
attribute((mark,type),_,MarkType).

% ====== Channel Control ======

% @api(require(channel, channelname)) Guarantee that a specific channel is used
violation(required_channel_missing) :-
require(channel, Channel),
not attribute((encoding,channel),_,Channel).

% @api(forbid(channel, channelname)) Forbid a specific channel from being used
violation(forbidden_channel_used) :-
forbid(channel, Channel),
attribute((encoding,channel),_,Channel).

% @api(observed(channel, channelname)) Channel is used
observed(channel, Channel) :-
attribute((encoding,channel),_,Channel).

% ====== Facet Control ======

% @api(require(facet)) Guarantee that faceting is used
violation(faceting_missing) :-
require(facet),
not entity(facet,_,_).

% @api(forbid(facet)) Forbid any faceting
violation(faceting_forbidden) :-
forbid(facet),
entity(facet,_,_).

% @api(require((facet, channel), channelname)) Require faceting on a specific channel (row/col)
violation(facet_channel_missing) :-
require((facet, channel), Channel),
not attribute((facet,channel),_,Channel).

% @api(forbid((facet, channel), channelname)) Forbid faceting on a specific channel
violation(facet_channel_forbidden) :-
forbid((facet, channel), Channel),
attribute((facet,channel),_,Channel).

% @api(observed(facet)) Faceting is used
observed(facet) :-
entity(facet,_,_).

% @api(observed((facet, channel), channelname)) Specific facet channel is used
observed((facet, channel), Channel) :-
attribute((facet,channel),_,Channel).

% ====== Binning Control ======

% @api(require(binning)) Guarantee that at least one encoding uses binning
violation(binning_missing) :-
require(binning),
not attribute((encoding,binning),_,_).

% @api(forbid(binning)) Forbid any binning
violation(binning_forbidden) :-
forbid(binning),
attribute((encoding,binning),_,_).

% @api(require(binning, fieldname)) Require a specific field to be binned
violation(field_not_binned) :-
require(binning, FieldName),
attribute((encoding,field),E,FieldName),
not attribute((encoding,binning),E,_).

% @api(forbid(binning, fieldname)) Forbid a specific field from being binned
violation(field_binned_forbidden) :-
forbid(binning, FieldName),
attribute((encoding,field),E,FieldName),
attribute((encoding,binning),E,_).

% @api(observed(binning)) Binning is used
observed(binning) :-
attribute((encoding,binning),_,_).

% @api(observed(binning, fieldname)) Specific field is binned
observed(binning, FieldName) :-
attribute((encoding,field),E,FieldName),
attribute((encoding,binning),E,_).

% ====== Aggregation Control ======

% @api(require(aggregate)) Guarantee that at least one encoding uses aggregation
violation(aggregation_missing) :-
require(aggregate),
not attribute((encoding,aggregate),_,_).

% @api(forbid(aggregate)) Forbid any aggregation
violation(aggregation_forbidden) :-
forbid(aggregate),
attribute((encoding,aggregate),_,_).

% @api(require(aggregate, aggtype)) Require a specific aggregation type to be used
violation(required_aggregate_missing) :-
require(aggregate, AggType),
not attribute((encoding,aggregate),_,AggType).

% @api(forbid(aggregate, aggtype)) Forbid a specific aggregation type
violation(forbidden_aggregate_used) :-
forbid(aggregate, AggType),
attribute((encoding,aggregate),_,AggType).

% @api(observed(aggregate)) Aggregation is used
observed(aggregate) :-
attribute((encoding,aggregate),_,_).

% @api(observed(aggregate, aggtype)) Specific aggregation type is used
observed(aggregate, AggType) :-
attribute((encoding,aggregate),_,AggType).

% ====== Scale Control ======

% @api(require(scale, scaletype)) Require a specific scale type to be used
violation(required_scale_missing) :-
require(scale, ScaleType),
not attribute((scale,type),_,ScaleType).

% @api(forbid(scale, scaletype)) Forbid a specific scale type
violation(forbidden_scale_used) :-
forbid(scale, ScaleType),
attribute((scale,type),_,ScaleType).

% @api(require((scale, channel), scaletype)) Require a specific scale type for a specific channel
violation(required_channel_scale_missing) :-
require((scale, Channel), ScaleType),
entity(scale,_,S),
attribute((scale,channel),S,Channel),
not attribute((scale,type),S,ScaleType).

% @api(forbid((scale, channel), scaletype)) Forbid a specific scale type for a specific channel
violation(forbidden_channel_scale_used) :-
forbid((scale, Channel), ScaleType),
entity(scale,_,S),
attribute((scale,channel),S,Channel),
attribute((scale,type),S,ScaleType).

% @api(observed(scale, scaletype)) Scale type is used
observed(scale, ScaleType) :-
attribute((scale,type),_,ScaleType).

% @api(observed((scale, channel), scaletype)) Specific scale type used on specific channel
observed((scale, Channel), ScaleType) :-
entity(scale,_,S),
attribute((scale,channel),S,Channel),
attribute((scale,type),S,ScaleType).

% ====== Stack Control ======

% @api(require(stack)) Guarantee that stacking is used
violation(stacking_missing) :-
require(stack),
not attribute((encoding,stack),_,_).

% @api(forbid(stack)) Forbid any stacking
violation(stacking_forbidden) :-
forbid(stack),
attribute((encoding,stack),_,_).

% @api(require(stack, stackmethod)) Require a specific stacking method
violation(required_stack_method_missing) :-
require(stack, StackMethod),
not attribute((encoding,stack),_,StackMethod).

% @api(forbid(stack, stackmethod)) Forbid a specific stacking method
violation(forbidden_stack_method_used) :-
forbid(stack, StackMethod),
attribute((encoding,stack),_,StackMethod).

% @api(observed(stack)) Stacking is used
observed(stack) :-
attribute((encoding,stack),_,_).

% @api(observed(stack, stackmethod)) Specific stack method is used
observed(stack, StackMethod) :-
attribute((encoding,stack),_,StackMethod).

% ====== Definition Declarations ======
% Tell Clingo these predicates are defined externally to silence "atom does not occur in any rule head" warnings.

#defined require/1.
#defined require/2.
#defined forbid/1.
#defined forbid/2.
33 changes: 33 additions & 0 deletions draco/asp/helpers.lp
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,36 @@ helper((encoding,field),E,F) :-
helper((facet,field),FC,F) :-
attribute((facet,field),FC,N),
attribute((field,name),F,N).

% @helper(field_skewed) Whether a field is skewed (absolute skew > 5).
helper(field_skewed,F) :-
attribute((field,skew),F,S),
|S| > 2.

% @helper(total_facet_cardinality) The total cardinality of all facets in a view (product of individual facet cardinalities).
helper(total_facet_cardinality,V,1) :-
entity(view,root,V),
not entity(facet,V,_).
helper(total_facet_cardinality,V,N) :-
entity(facet,V,F),
attribute((facet,channel),F,row),
helper(facet_cardinality,F,N),
not entity(facet,V,F2),
F2 != F,
attribute((facet,channel),F2,col).
helper(total_facet_cardinality,V,N) :-
entity(facet,V,F),
attribute((facet,channel),F,col),
helper(facet_cardinality,F,N),
not entity(facet,V,F2),
F2 != F,
attribute((facet,channel),F2,row).
helper(total_facet_cardinality,V,N) :-
entity(facet,V,F1),
entity(facet,V,F2),
F1 != F2,
attribute((facet,channel),F1,row),
attribute((facet,channel),F2,col),
helper(facet_cardinality,F1,N1),
helper(facet_cardinality,F2,N2),
N = N1 * N2.
Loading
Loading