Skip to content

Commit 0b42123

Browse files
author
Ben Lerner
committed
feature: restore tooltips on dot plots, and restore the "categorical dot plots" that used to be a post-processing hackaround
1 parent c9c8388 commit 0b42123

File tree

2 files changed

+161
-32
lines changed

2 files changed

+161
-32
lines changed

src/arr/trove/charts.arr

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,22 @@ default-dot-plot-series = {
12471247
useImageSizes: true,
12481248
}
12491249

1250+
type CategoricalDotPoint ={
1251+
label :: String,
1252+
count :: Number
1253+
}
1254+
1255+
type CategoricalDotPlotSeries = {
1256+
ps :: List<CategoricalDotPoint>,
1257+
color :: Option<I.Color>,
1258+
legend :: String
1259+
}
1260+
1261+
default-categorical-dot-plot-series = {
1262+
color: none,
1263+
legend: '',
1264+
}
1265+
12501266
type IntervalPoint = {
12511267
label :: String,
12521268
x :: Number,
@@ -1565,6 +1581,11 @@ data DataSeries:
15651581
method use-image-sizes(self, use-image-sizes :: Boolean):
15661582
self.constr()(self.obj.{useImageSizes: use-image-sizes})
15671583
end,
1584+
| categorical-dot-plot-series(obj :: CategoricalDotPlotSeries) with:
1585+
is-single: true,
1586+
constr: {(): categorical-dot-plot-series},
1587+
color: color-method,
1588+
legend: legend-method,
15681589
| function-plot-series(obj :: FunctionPlotSeries) with:
15691590
is-single: false,
15701591
constr: {(): function-plot-series},
@@ -2104,36 +2125,15 @@ fun dot-chart-from-list(input-labels :: CL.LoS) -> DataSeries block:
21042125
input-labels.each(check-string)
21052126

21062127
# Walk through the (sorted) values, creating lists of labels and counts
2107-
unique-counts = foldl(
2108-
lam(acc, elt):
2109-
labels = acc.{0}
2110-
counts = acc.{1}
2111-
if labels.member(elt):
2112-
{labels; counts.set(0, counts.get(0) + 1)}
2113-
else:
2114-
{link(elt, labels); link(1, counts)}
2115-
end
2116-
end,
2117-
{[list: ]; [list: ]},
2118-
input-labels.sort())
2119-
2120-
labels = unique-counts.{0}
2121-
values = unique-counts.{1}
2122-
rational-values = map(num-to-rational, values)
2123-
2124-
# set the vAxis values, and create the data series
2125-
{max-positive-height; max-negative-height} = prep-axis(rational-values)
2126-
2127-
data-series = default-bar-chart-series.{
2128-
tab: to-table2-n(labels, rational-values),
2129-
dot-chart: true,
2130-
axis-top: max-positive-height,
2131-
axis-bottom: max-negative-height,
2132-
annotations: values.map({(_): [list: none]}) ^ list-to-table2,
2133-
intervals: values.map({(_): [list: [raw-array: ]]}) ^ list-to-table2,
2134-
} ^ bar-chart-series
2128+
unique-counts = for fold(acc from [SD.mutable-string-dict: ], label from input-labels) block:
2129+
acc.set-now(label, acc.get-now(label).or-else(0) + 1)
2130+
acc
2131+
end
21352132

2136-
data-series.make-axis(max-positive-height, max-negative-height)
2133+
labels = unique-counts.keys-list-now().sort()
2134+
default-categorical-dot-plot-series.{
2135+
ps: labels.map({(l): { label: l, count: unique-counts.get-value-now(l) }})
2136+
} ^ categorical-dot-plot-series
21372137
end
21382138

21392139
fun grouped-bar-chart-from-list(
@@ -2435,6 +2435,12 @@ fun render-chart(s :: DataSeries) -> ChartWindow:
24352435
CL.dot-chart(self, obj)
24362436
end
24372437
} ^ dot-chart-window
2438+
| categorical-dot-plot-series(obj) =>
2439+
default-dot-chart-window-object.{
2440+
method render(self) block:
2441+
CL.categorical-dot-chart(self, obj)
2442+
end
2443+
} ^ dot-chart-window
24382444
| pie-chart-series(obj) =>
24392445
default-pie-chart-window-object.{
24402446
method render(self): CL.pie-chart(self, obj) end

src/js/trove/charts-lib.js

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
values: {
1212
'pie-chart': "tany",
1313
'dot-chart': "tany",
14+
'categorical-dot-chart': "tany",
1415
'bar-chart': "tany",
1516
'multi-bar-chart': "tany",
1617
'histogram': "tany",
@@ -1865,7 +1866,7 @@
18651866
fill: { value: color },
18661867
stroke: { value: 'white' },
18671868
strokeWidth: { value: 0.25 },
1868-
tooltip: '{ title: datum.Label, Value: datum.value, BinNum: datum.binNum, Bin0: datum.bin0, Bin1: datum.bin1 }'
1869+
tooltip: { signal: '{ title: datum.label, Value: datum.value }' }
18691870
},
18701871
update: {
18711872
xc: { scale: 'binScale', field: 'value' },
@@ -1882,7 +1883,7 @@
18821883
width: autosizeImage ? undefined : { value: pointSize },
18831884
height: autosizeImage ? undefined : { value: pointSize },
18841885
image: { field: 'image' },
1885-
tooltip: '{ title: datum.Label, Value: datum.value, BinNum: datum.binNum, Bin0: datum.bin0, Bin1: datum.bin1 }'
1886+
tooltip: { signal: '{ title: datum.label, Value: datum.value }' }
18861887
},
18871888
update: {
18881889
xc: { scale: 'binScale', field: 'value' },
@@ -1910,7 +1911,128 @@
19101911
marks,
19111912
onExit: defaultImageReturn,
19121913
};
1913-
}
1914+
}
1915+
1916+
function categoricalDotChart(globalOptions, rawData) {
1917+
const defaultColor = default_colors[0];
1918+
const color = getColorOrDefault(get(rawData, 'color'), defaultColor);
1919+
const legend = get(rawData, 'legend') || '';
1920+
1921+
const points = RUNTIME.ffi.toArray(get(rawData, 'ps'));
1922+
1923+
const title = globalOptions['title'];
1924+
const width = globalOptions['width'];
1925+
const height = globalOptions['height'];
1926+
const xAxisLabel = globalOptions['x-axis'];
1927+
const yAxisLabel = globalOptions['y-axis'];
1928+
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
1929+
1930+
1931+
1932+
console.log(points);
1933+
const fixedPoints = points.map((p) => ({
1934+
label: get(p, 'label'),
1935+
count: toFixnum(get(p, 'count'))
1936+
}));
1937+
const data = [
1938+
{
1939+
name: 'bars',
1940+
values: fixedPoints
1941+
},
1942+
{
1943+
name: 'dotsData',
1944+
values: fixedPoints.flatMap((p) =>
1945+
Array.from({length: p.count}).map((_, i) => ({ label: p.label, value: i }))
1946+
)
1947+
},
1948+
];
1949+
console.log(data)
1950+
const signals = [
1951+
{ name: 'dotSize', update: "scale('secondary', 1) - scale('secondary', 0)" },
1952+
];
1953+
const scales = [
1954+
{
1955+
name: 'primary',
1956+
type: 'band',
1957+
range: 'width',
1958+
domain: { data: 'bars', field: 'label' }
1959+
},
1960+
{
1961+
name: 'secondary',
1962+
type: 'linear',
1963+
range: 'height',
1964+
nice: true, zero: true,
1965+
domain: { data: 'bars', field: 'count' }
1966+
}
1967+
];
1968+
const axes = [
1969+
{ orient: 'bottom', scale: 'primary', zindex: 1, title: xAxisLabel },
1970+
{ orient: 'bottom', scale: 'primary', zindex: 0, grid: true, ticks: false, labels: false },
1971+
{ orient: 'left', scale: 'secondary', grid: true, ticks: true, labels: true, title: yAxisLabel, zindex: 1 }
1972+
];
1973+
const marks = [
1974+
{
1975+
type: 'symbol',
1976+
name: 'dots',
1977+
from: { data: 'dotsData' },
1978+
encode: {
1979+
enter: {
1980+
shape: { value: 'circle' },
1981+
fillOpacity: { value: 0.5 },
1982+
fill: { value: color },
1983+
stroke: { value: 'white' },
1984+
strokeWidth: { value: 0.25 },
1985+
tooltip: { signal: '{ title: datum.label, Value: datum.value }' },
1986+
xc: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.5 } },
1987+
yc: { scale: 'secondary', field: 'value', offset: { signal: '0.5 * dotSize' } },
1988+
size: { signal: '0.8 * 0.8 * dotSize * dotSize' },
1989+
}
1990+
}
1991+
},
1992+
{
1993+
type: 'rect',
1994+
name: 'blocks',
1995+
from: { data: 'bars' },
1996+
encode: {
1997+
enter: {
1998+
x: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.25 } },
1999+
x2: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.75 } },
2000+
y: { scale: 'secondary', value: 0 },
2001+
y2: { scale: 'secondary', field: 'count' },
2002+
tooltip: { signal: `{ title: datum.label, Count: datum.count }`},
2003+
stroke: { value: 'gray' },
2004+
strokeWidth: { value: 2 },
2005+
fill: { value: 'transparent' },
2006+
},
2007+
update: {
2008+
opacity: { value: 0 },
2009+
},
2010+
hover: {
2011+
opacity: { value: 1 }
2012+
}
2013+
}
2014+
},
2015+
];
2016+
2017+
const ans = {
2018+
"$schema": "https://vega.github.io/schema/vega/v6.json",
2019+
description: title,
2020+
title: title ? { text: title } : '',
2021+
width,
2022+
height,
2023+
padding: 0,
2024+
autosize: 'fit',
2025+
background,
2026+
data,
2027+
signals,
2028+
scales,
2029+
axes,
2030+
marks,
2031+
onExit: defaultImageReturn,
2032+
};
2033+
console.log(ans);
2034+
return ans;
2035+
}
19142036

19152037
function computeDomain(domainValues) {
19162038
const domainMin = Math.min(...domainValues);
@@ -3153,6 +3275,7 @@
31533275
'histogram': makeFunction(histogram),
31543276
'box-plot': makeFunction(boxPlot),
31553277
'dot-chart': makeFunction(dotChart),
3278+
'categorical-dot-chart': makeFunction(categoricalDotChart),
31563279
'plot': makeFunction(plot),
31573280
},
31583281
{

0 commit comments

Comments
 (0)