@ggterm/core 0.2.7 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-plot.js +387 -6
- package/dist/cli.js +378 -5
- package/dist/geoms/index.d.ts +1 -0
- package/dist/geoms/index.d.ts.map +1 -1
- package/dist/geoms/ridgeline.d.ts +52 -0
- package/dist/geoms/ridgeline.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +382 -5
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/render-geoms.d.ts +6 -0
- package/dist/pipeline/render-geoms.d.ts.map +1 -1
- package/dist/pipeline/scales.d.ts.map +1 -1
- package/dist/stats/density.d.ts +5 -0
- package/dist/stats/density.d.ts.map +1 -1
- package/dist/stats/density2d.d.ts +36 -0
- package/dist/stats/density2d.d.ts.map +1 -0
- package/dist/stats/index.d.ts +3 -1
- package/dist/stats/index.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/cli-plot.js
CHANGED
|
@@ -652,7 +652,7 @@ function isCategoricalField(data, field) {
|
|
|
652
652
|
for (const row of data) {
|
|
653
653
|
const value = row[field];
|
|
654
654
|
if (value !== null && value !== undefined) {
|
|
655
|
-
if (typeof value === "string"
|
|
655
|
+
if (typeof value === "string") {
|
|
656
656
|
return true;
|
|
657
657
|
}
|
|
658
658
|
}
|
|
@@ -1049,6 +1049,44 @@ function getPointColor(row, aes, colorScale) {
|
|
|
1049
1049
|
}
|
|
1050
1050
|
return DEFAULT_POINT_COLOR;
|
|
1051
1051
|
}
|
|
1052
|
+
function parseColorToRgba(color, fallback = { r: 128, g: 128, b: 128, a: 1 }) {
|
|
1053
|
+
if (!color)
|
|
1054
|
+
return fallback;
|
|
1055
|
+
if (typeof color === "object" && color !== null && "r" in color) {
|
|
1056
|
+
return color;
|
|
1057
|
+
}
|
|
1058
|
+
if (typeof color === "string") {
|
|
1059
|
+
if (color.startsWith("#")) {
|
|
1060
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
|
|
1061
|
+
if (result) {
|
|
1062
|
+
return {
|
|
1063
|
+
r: parseInt(result[1], 16),
|
|
1064
|
+
g: parseInt(result[2], 16),
|
|
1065
|
+
b: parseInt(result[3], 16),
|
|
1066
|
+
a: 1
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
const namedColors = {
|
|
1071
|
+
red: { r: 255, g: 0, b: 0, a: 1 },
|
|
1072
|
+
blue: { r: 0, g: 0, b: 255, a: 1 },
|
|
1073
|
+
green: { r: 0, g: 128, b: 0, a: 1 },
|
|
1074
|
+
black: { r: 0, g: 0, b: 0, a: 1 },
|
|
1075
|
+
white: { r: 255, g: 255, b: 255, a: 1 },
|
|
1076
|
+
gray: { r: 128, g: 128, b: 128, a: 1 },
|
|
1077
|
+
grey: { r: 128, g: 128, b: 128, a: 1 },
|
|
1078
|
+
yellow: { r: 255, g: 255, b: 0, a: 1 },
|
|
1079
|
+
orange: { r: 255, g: 165, b: 0, a: 1 },
|
|
1080
|
+
purple: { r: 128, g: 0, b: 128, a: 1 },
|
|
1081
|
+
cyan: { r: 0, g: 255, b: 255, a: 1 },
|
|
1082
|
+
magenta: { r: 255, g: 0, b: 255, a: 1 }
|
|
1083
|
+
};
|
|
1084
|
+
const named = namedColors[color.toLowerCase()];
|
|
1085
|
+
if (named)
|
|
1086
|
+
return named;
|
|
1087
|
+
}
|
|
1088
|
+
return fallback;
|
|
1089
|
+
}
|
|
1052
1090
|
function renderGeomPoint(data, geom, aes, scales, canvas) {
|
|
1053
1091
|
const defaultShape = getPointShape(geom.params.shape);
|
|
1054
1092
|
const positionType = getPositionType(geom.position);
|
|
@@ -1447,7 +1485,7 @@ function renderGeomHLine(_data, geom, _aes, scales, canvas) {
|
|
|
1447
1485
|
if (yintercept === undefined)
|
|
1448
1486
|
return;
|
|
1449
1487
|
const cy = Math.round(scales.y.map(yintercept));
|
|
1450
|
-
const color = geom.params.color
|
|
1488
|
+
const color = parseColorToRgba(geom.params.color);
|
|
1451
1489
|
const startX = Math.round(scales.x.range[0]);
|
|
1452
1490
|
const endX = Math.round(scales.x.range[1]);
|
|
1453
1491
|
canvas.drawHLine(startX, cy, endX - startX + 1, "─", color);
|
|
@@ -1457,7 +1495,7 @@ function renderGeomVLine(_data, geom, _aes, scales, canvas) {
|
|
|
1457
1495
|
if (xintercept === undefined)
|
|
1458
1496
|
return;
|
|
1459
1497
|
const cx = Math.round(scales.x.map(xintercept));
|
|
1460
|
-
const color = geom.params.color
|
|
1498
|
+
const color = parseColorToRgba(geom.params.color);
|
|
1461
1499
|
const startY = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
|
|
1462
1500
|
const endY = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
|
|
1463
1501
|
canvas.drawVLine(cx, startY, endY - startY + 1, "│", color);
|
|
@@ -1768,6 +1806,120 @@ function renderGeomViolin(data, geom, _aes, scales, canvas) {
|
|
|
1768
1806
|
}
|
|
1769
1807
|
}
|
|
1770
1808
|
}
|
|
1809
|
+
function renderGeomRidgeline(data, geom, aes, scales, canvas) {
|
|
1810
|
+
const scaleFactor = geom.params.scale ?? 0.9;
|
|
1811
|
+
const alpha = geom.params.alpha ?? 0.8;
|
|
1812
|
+
const showOutline = geom.params.outline ?? true;
|
|
1813
|
+
const fixedFill = geom.params.fill;
|
|
1814
|
+
const fixedColor = geom.params.color;
|
|
1815
|
+
const plotLeft = Math.round(scales.x.range[0]);
|
|
1816
|
+
const plotRight = Math.round(scales.x.range[1]);
|
|
1817
|
+
const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
|
|
1818
|
+
const plotBottom = Math.round(Math.max(scales.y.range[0], scales.y.range[1]));
|
|
1819
|
+
const groups = new Map;
|
|
1820
|
+
const groupKeys = [];
|
|
1821
|
+
for (const row of data) {
|
|
1822
|
+
const key = String(row.y ?? "default");
|
|
1823
|
+
if (!groups.has(key)) {
|
|
1824
|
+
groups.set(key, { data: [], index: Number(row.yIndex) ?? groupKeys.length });
|
|
1825
|
+
groupKeys.push(key);
|
|
1826
|
+
}
|
|
1827
|
+
groups.get(key).data.push(row);
|
|
1828
|
+
}
|
|
1829
|
+
const numGroups = groupKeys.length;
|
|
1830
|
+
if (numGroups === 0)
|
|
1831
|
+
return;
|
|
1832
|
+
const plotHeight = Math.abs(plotBottom - plotTop);
|
|
1833
|
+
const ridgeBaseHeight = plotHeight / numGroups;
|
|
1834
|
+
const defaultColors = [
|
|
1835
|
+
{ r: 79, g: 169, b: 238, a: 1 },
|
|
1836
|
+
{ r: 238, g: 136, b: 102, a: 1 },
|
|
1837
|
+
{ r: 102, g: 204, b: 153, a: 1 },
|
|
1838
|
+
{ r: 204, g: 102, b: 204, a: 1 },
|
|
1839
|
+
{ r: 255, g: 200, b: 87, a: 1 },
|
|
1840
|
+
{ r: 138, g: 201, b: 222, a: 1 },
|
|
1841
|
+
{ r: 255, g: 153, b: 153, a: 1 },
|
|
1842
|
+
{ r: 170, g: 170, b: 170, a: 1 }
|
|
1843
|
+
];
|
|
1844
|
+
let parsedFillColor = null;
|
|
1845
|
+
if (fixedFill) {
|
|
1846
|
+
parsedFillColor = parseColorToRgba(fixedFill);
|
|
1847
|
+
}
|
|
1848
|
+
const sortedKeys = [...groupKeys].sort((a, b) => {
|
|
1849
|
+
const idxA = groups.get(a)?.index ?? 0;
|
|
1850
|
+
const idxB = groups.get(b)?.index ?? 0;
|
|
1851
|
+
return idxB - idxA;
|
|
1852
|
+
});
|
|
1853
|
+
for (const groupKey of sortedKeys) {
|
|
1854
|
+
const group = groups.get(groupKey);
|
|
1855
|
+
if (!group)
|
|
1856
|
+
continue;
|
|
1857
|
+
const groupIndex = group.index;
|
|
1858
|
+
const groupData = group.data;
|
|
1859
|
+
const sorted = [...groupData].sort((a, b) => {
|
|
1860
|
+
const ax = Number(a.x) || 0;
|
|
1861
|
+
const bx = Number(b.x) || 0;
|
|
1862
|
+
return ax - bx;
|
|
1863
|
+
});
|
|
1864
|
+
if (sorted.length < 2)
|
|
1865
|
+
continue;
|
|
1866
|
+
const baseline = plotTop + (groupIndex + 0.5) * ridgeBaseHeight;
|
|
1867
|
+
let fillColor;
|
|
1868
|
+
if (parsedFillColor) {
|
|
1869
|
+
fillColor = parsedFillColor;
|
|
1870
|
+
} else if ((aes.fill || aes.color) && scales.color) {
|
|
1871
|
+
const mappedColor = scales.color.map(groupKey);
|
|
1872
|
+
if (typeof mappedColor === "object" && "r" in mappedColor) {
|
|
1873
|
+
fillColor = mappedColor;
|
|
1874
|
+
} else {
|
|
1875
|
+
fillColor = defaultColors[groupIndex % defaultColors.length];
|
|
1876
|
+
}
|
|
1877
|
+
} else {
|
|
1878
|
+
fillColor = defaultColors[groupIndex % defaultColors.length];
|
|
1879
|
+
}
|
|
1880
|
+
const alphaFillColor = {
|
|
1881
|
+
r: Math.round(fillColor.r * alpha + 255 * (1 - alpha) * 0.1),
|
|
1882
|
+
g: Math.round(fillColor.g * alpha + 255 * (1 - alpha) * 0.1),
|
|
1883
|
+
b: Math.round(fillColor.b * alpha + 255 * (1 - alpha) * 0.1),
|
|
1884
|
+
a: 1
|
|
1885
|
+
};
|
|
1886
|
+
const maxRidgeHeight = ridgeBaseHeight * scaleFactor * 1.5;
|
|
1887
|
+
const outlinePoints = [];
|
|
1888
|
+
for (const row of sorted) {
|
|
1889
|
+
const xVal = Number(row.x);
|
|
1890
|
+
const scaled = Number(row.scaled) || 0;
|
|
1891
|
+
const px = Math.round(scales.x.map(xVal));
|
|
1892
|
+
const ridgeHeight = scaled * maxRidgeHeight;
|
|
1893
|
+
const py = Math.round(baseline - ridgeHeight);
|
|
1894
|
+
if (px < plotLeft || px > plotRight)
|
|
1895
|
+
continue;
|
|
1896
|
+
const fillTop = Math.max(plotTop, py);
|
|
1897
|
+
const fillBottom = Math.min(plotBottom, Math.round(baseline));
|
|
1898
|
+
for (let y = fillTop;y <= fillBottom; y++) {
|
|
1899
|
+
canvas.drawChar(px, y, "█", alphaFillColor);
|
|
1900
|
+
}
|
|
1901
|
+
if (showOutline) {
|
|
1902
|
+
outlinePoints.push({ x: px, y: Math.max(plotTop, py) });
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
if (showOutline && outlinePoints.length > 1) {
|
|
1906
|
+
const outlineColor = fixedColor ? parseColorToRgba(fixedColor) : { r: Math.min(255, fillColor.r + 40), g: Math.min(255, fillColor.g + 40), b: Math.min(255, fillColor.b + 40), a: 1 };
|
|
1907
|
+
for (let i = 0;i < outlinePoints.length - 1; i++) {
|
|
1908
|
+
const p1 = outlinePoints[i];
|
|
1909
|
+
const p2 = outlinePoints[i + 1];
|
|
1910
|
+
if (Math.abs(p2.x - p1.x) <= 1) {
|
|
1911
|
+
if (p1.y >= plotTop && p1.y <= plotBottom) {
|
|
1912
|
+
canvas.drawChar(p1.x, p1.y, "▄", outlineColor);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
const last = outlinePoints[outlinePoints.length - 1];
|
|
1917
|
+
if (last.y >= plotTop && last.y <= plotBottom) {
|
|
1918
|
+
canvas.drawChar(last.x, last.y, "▄", outlineColor);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1771
1923
|
function renderGeomTile(data, geom, aes, scales, canvas) {
|
|
1772
1924
|
const alpha = geom.params.alpha ?? 1;
|
|
1773
1925
|
const plotLeft = Math.round(scales.x.range[0]);
|
|
@@ -2016,7 +2168,7 @@ function renderGeomAbline(_data, geom, _aes, scales, canvas) {
|
|
|
2016
2168
|
const intercept = geom.params.intercept ?? 0;
|
|
2017
2169
|
const linetype = geom.params.linetype ?? "solid";
|
|
2018
2170
|
const lineChar = linetype === "dotted" ? "·" : linetype === "dashed" ? "╌" : "─";
|
|
2019
|
-
const color = geom.params.color
|
|
2171
|
+
const color = parseColorToRgba(geom.params.color);
|
|
2020
2172
|
const plotLeft = Math.round(scales.x.range[0]);
|
|
2021
2173
|
const plotRight = Math.round(scales.x.range[1]);
|
|
2022
2174
|
const plotTop = Math.round(Math.min(scales.y.range[0], scales.y.range[1]));
|
|
@@ -2206,6 +2358,10 @@ function renderGeom(data, geom, aes, scales, canvas, coordType) {
|
|
|
2206
2358
|
case "violin":
|
|
2207
2359
|
renderGeomViolin(data, geom, aes, scales, canvas);
|
|
2208
2360
|
break;
|
|
2361
|
+
case "ridgeline":
|
|
2362
|
+
case "joy":
|
|
2363
|
+
renderGeomRidgeline(data, geom, aes, scales, canvas);
|
|
2364
|
+
break;
|
|
2209
2365
|
case "tile":
|
|
2210
2366
|
case "raster":
|
|
2211
2367
|
case "bin2d":
|
|
@@ -3314,6 +3470,52 @@ function stat_ydensity(params = {}) {
|
|
|
3314
3470
|
}
|
|
3315
3471
|
};
|
|
3316
3472
|
}
|
|
3473
|
+
function stat_xdensity(params = {}) {
|
|
3474
|
+
return {
|
|
3475
|
+
type: "xdensity",
|
|
3476
|
+
compute(data, aes) {
|
|
3477
|
+
const groups = new Map;
|
|
3478
|
+
const groupOrder = [];
|
|
3479
|
+
for (const row of data) {
|
|
3480
|
+
const groupKey = String(row[aes.y] ?? "default");
|
|
3481
|
+
const xVal = row[aes.x];
|
|
3482
|
+
if (xVal === null || xVal === undefined)
|
|
3483
|
+
continue;
|
|
3484
|
+
const numX = Number(xVal);
|
|
3485
|
+
if (isNaN(numX))
|
|
3486
|
+
continue;
|
|
3487
|
+
if (!groups.has(groupKey)) {
|
|
3488
|
+
groups.set(groupKey, []);
|
|
3489
|
+
groupOrder.push(groupKey);
|
|
3490
|
+
}
|
|
3491
|
+
groups.get(groupKey).push(numX);
|
|
3492
|
+
}
|
|
3493
|
+
const result = [];
|
|
3494
|
+
let groupIndex = 0;
|
|
3495
|
+
for (const groupKey of groupOrder) {
|
|
3496
|
+
const xValues = groups.get(groupKey);
|
|
3497
|
+
if (xValues.length < 2) {
|
|
3498
|
+
groupIndex++;
|
|
3499
|
+
continue;
|
|
3500
|
+
}
|
|
3501
|
+
const tempData = xValues.map((v) => ({ x: v }));
|
|
3502
|
+
const densityResult = computeDensity(tempData, "x", params);
|
|
3503
|
+
for (const d of densityResult) {
|
|
3504
|
+
result.push({
|
|
3505
|
+
x: d.x,
|
|
3506
|
+
y: groupKey,
|
|
3507
|
+
yIndex: groupIndex,
|
|
3508
|
+
density: d.density,
|
|
3509
|
+
scaled: d.scaled,
|
|
3510
|
+
height: d.scaled
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
groupIndex++;
|
|
3514
|
+
}
|
|
3515
|
+
return result;
|
|
3516
|
+
}
|
|
3517
|
+
};
|
|
3518
|
+
}
|
|
3317
3519
|
|
|
3318
3520
|
// src/stats/smooth.ts
|
|
3319
3521
|
function linearRegression(xs, ys) {
|
|
@@ -3791,6 +3993,124 @@ function stat_qq_line(params = {}) {
|
|
|
3791
3993
|
};
|
|
3792
3994
|
}
|
|
3793
3995
|
|
|
3996
|
+
// src/stats/density2d.ts
|
|
3997
|
+
function gaussian2d(dx, dy, hx, hy) {
|
|
3998
|
+
const ux = dx / hx;
|
|
3999
|
+
const uy = dy / hy;
|
|
4000
|
+
return Math.exp(-0.5 * (ux * ux + uy * uy)) / (2 * Math.PI * hx * hy);
|
|
4001
|
+
}
|
|
4002
|
+
function scottBandwidth2d(values) {
|
|
4003
|
+
const n = values.length;
|
|
4004
|
+
if (n < 2)
|
|
4005
|
+
return 1;
|
|
4006
|
+
const mean = values.reduce((a, b) => a + b, 0) / n;
|
|
4007
|
+
const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / (n - 1);
|
|
4008
|
+
const std = Math.sqrt(variance);
|
|
4009
|
+
return std * Math.pow(n, -1 / 6);
|
|
4010
|
+
}
|
|
4011
|
+
function computeDensity2d(data, xField, yField, params = {}) {
|
|
4012
|
+
const points = [];
|
|
4013
|
+
const xValues = [];
|
|
4014
|
+
const yValues = [];
|
|
4015
|
+
for (const row of data) {
|
|
4016
|
+
const xVal = row[xField];
|
|
4017
|
+
const yVal = row[yField];
|
|
4018
|
+
if (xVal === null || xVal === undefined || yVal === null || yVal === undefined)
|
|
4019
|
+
continue;
|
|
4020
|
+
const x = Number(xVal);
|
|
4021
|
+
const y = Number(yVal);
|
|
4022
|
+
if (isNaN(x) || isNaN(y))
|
|
4023
|
+
continue;
|
|
4024
|
+
points.push({ x, y });
|
|
4025
|
+
xValues.push(x);
|
|
4026
|
+
yValues.push(y);
|
|
4027
|
+
}
|
|
4028
|
+
if (points.length < 3) {
|
|
4029
|
+
return [];
|
|
4030
|
+
}
|
|
4031
|
+
const n = params.n ?? 50;
|
|
4032
|
+
const nx = params.nx ?? n;
|
|
4033
|
+
const ny = params.ny ?? n;
|
|
4034
|
+
const adjust = params.adjust ?? 1;
|
|
4035
|
+
let hx, hy;
|
|
4036
|
+
if (Array.isArray(params.h)) {
|
|
4037
|
+
hx = params.h[0] * adjust;
|
|
4038
|
+
hy = params.h[1] * adjust;
|
|
4039
|
+
} else if (params.h !== undefined) {
|
|
4040
|
+
hx = params.h * adjust;
|
|
4041
|
+
hy = params.h * adjust;
|
|
4042
|
+
} else {
|
|
4043
|
+
hx = scottBandwidth2d(xValues) * adjust;
|
|
4044
|
+
hy = scottBandwidth2d(yValues) * adjust;
|
|
4045
|
+
}
|
|
4046
|
+
if (hx <= 0)
|
|
4047
|
+
hx = 1;
|
|
4048
|
+
if (hy <= 0)
|
|
4049
|
+
hy = 1;
|
|
4050
|
+
const xMin = Math.min(...xValues);
|
|
4051
|
+
const xMax = Math.max(...xValues);
|
|
4052
|
+
const yMin = Math.min(...yValues);
|
|
4053
|
+
const yMax = Math.max(...yValues);
|
|
4054
|
+
const xPad = 3 * hx;
|
|
4055
|
+
const yPad = 3 * hy;
|
|
4056
|
+
const gridXMin = xMin - xPad;
|
|
4057
|
+
const gridXMax = xMax + xPad;
|
|
4058
|
+
const gridYMin = yMin - yPad;
|
|
4059
|
+
const gridYMax = yMax + yPad;
|
|
4060
|
+
const xStep = (gridXMax - gridXMin) / (nx - 1);
|
|
4061
|
+
const yStep = (gridYMax - gridYMin) / (ny - 1);
|
|
4062
|
+
const results = [];
|
|
4063
|
+
const nPoints = points.length;
|
|
4064
|
+
for (let i = 0;i < nx; i++) {
|
|
4065
|
+
const gx = gridXMin + i * xStep;
|
|
4066
|
+
for (let j = 0;j < ny; j++) {
|
|
4067
|
+
const gy = gridYMin + j * yStep;
|
|
4068
|
+
let density = 0;
|
|
4069
|
+
for (const pt of points) {
|
|
4070
|
+
density += gaussian2d(gx - pt.x, gy - pt.y, hx, hy);
|
|
4071
|
+
}
|
|
4072
|
+
density /= nPoints;
|
|
4073
|
+
results.push({
|
|
4074
|
+
x: gx,
|
|
4075
|
+
y: gy,
|
|
4076
|
+
z: density,
|
|
4077
|
+
density,
|
|
4078
|
+
level: density
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
return results;
|
|
4083
|
+
}
|
|
4084
|
+
function stat_density_2d(params = {}) {
|
|
4085
|
+
return {
|
|
4086
|
+
type: "density_2d",
|
|
4087
|
+
compute(data, aes) {
|
|
4088
|
+
if (aes.color) {
|
|
4089
|
+
const groups = new Map;
|
|
4090
|
+
for (const row of data) {
|
|
4091
|
+
const group = String(row[aes.color] ?? "default");
|
|
4092
|
+
if (!groups.has(group)) {
|
|
4093
|
+
groups.set(group, []);
|
|
4094
|
+
}
|
|
4095
|
+
groups.get(group).push(row);
|
|
4096
|
+
}
|
|
4097
|
+
const result = [];
|
|
4098
|
+
for (const [group, groupData] of groups) {
|
|
4099
|
+
const density = computeDensity2d(groupData, aes.x, aes.y, params);
|
|
4100
|
+
for (const d of density) {
|
|
4101
|
+
result.push({
|
|
4102
|
+
...d,
|
|
4103
|
+
[aes.color]: group
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
return result;
|
|
4108
|
+
}
|
|
4109
|
+
return computeDensity2d(data, aes.x, aes.y, params);
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
|
|
3794
4114
|
// src/facets/index.ts
|
|
3795
4115
|
function as_labeller(labels) {
|
|
3796
4116
|
return (value) => labels[value] ?? value;
|
|
@@ -4068,6 +4388,14 @@ function applyStatTransform(data, geom, aes) {
|
|
|
4068
4388
|
adjust: geom.params.adjust
|
|
4069
4389
|
});
|
|
4070
4390
|
return ydensityStat.compute(data, aes);
|
|
4391
|
+
} else if (geom.stat === "xdensity") {
|
|
4392
|
+
const xdensityStat = stat_xdensity({
|
|
4393
|
+
bw: geom.params.bw,
|
|
4394
|
+
kernel: geom.params.kernel,
|
|
4395
|
+
n: geom.params.n,
|
|
4396
|
+
adjust: geom.params.adjust
|
|
4397
|
+
});
|
|
4398
|
+
return xdensityStat.compute(data, aes);
|
|
4071
4399
|
} else if (geom.stat === "smooth") {
|
|
4072
4400
|
const smoothStat = stat_smooth({
|
|
4073
4401
|
method: geom.params.method,
|
|
@@ -4109,6 +4437,15 @@ function applyStatTransform(data, geom, aes) {
|
|
|
4109
4437
|
drop: geom.params.drop
|
|
4110
4438
|
});
|
|
4111
4439
|
return bin2dStat.compute(data, aes);
|
|
4440
|
+
} else if (geom.stat === "density_2d") {
|
|
4441
|
+
const density2dStat = stat_density_2d({
|
|
4442
|
+
h: geom.params.bandwidth,
|
|
4443
|
+
n: geom.params.n,
|
|
4444
|
+
nx: geom.params.nx,
|
|
4445
|
+
ny: geom.params.ny,
|
|
4446
|
+
adjust: geom.params.adjust
|
|
4447
|
+
});
|
|
4448
|
+
return density2dStat.compute(data, aes);
|
|
4112
4449
|
}
|
|
4113
4450
|
return data;
|
|
4114
4451
|
}
|
|
@@ -4177,6 +4514,14 @@ function renderToCanvas(spec, options) {
|
|
|
4177
4514
|
scaleData = applyStatTransform(spec.data, geom, spec.aes);
|
|
4178
4515
|
scaleAes = { ...spec.aes, x: "x", y: "y", fill: "fill" };
|
|
4179
4516
|
break;
|
|
4517
|
+
} else if (geom.stat === "density_2d") {
|
|
4518
|
+
scaleData = applyStatTransform(spec.data, geom, spec.aes);
|
|
4519
|
+
scaleAes = { ...spec.aes, x: "x", y: "y" };
|
|
4520
|
+
break;
|
|
4521
|
+
} else if (geom.stat === "xdensity") {
|
|
4522
|
+
scaleData = applyStatTransform(spec.data, geom, spec.aes);
|
|
4523
|
+
scaleAes = { ...spec.aes, x: "x", y: "y" };
|
|
4524
|
+
break;
|
|
4180
4525
|
}
|
|
4181
4526
|
}
|
|
4182
4527
|
scaleData = applyCoordTransform(scaleData, scaleAes, spec.coord);
|
|
@@ -4213,6 +4558,10 @@ function renderToCanvas(spec, options) {
|
|
|
4213
4558
|
geomAes = { ...spec.aes, x: "x", y: "y", xend: "xend", yend: "yend" };
|
|
4214
4559
|
} else if (geom.stat === "bin2d") {
|
|
4215
4560
|
geomAes = { ...spec.aes, x: "x", y: "y", fill: "fill" };
|
|
4561
|
+
} else if (geom.stat === "density_2d") {
|
|
4562
|
+
geomAes = { ...spec.aes, x: "x", y: "y" };
|
|
4563
|
+
} else if (geom.stat === "xdensity") {
|
|
4564
|
+
geomAes = { ...spec.aes, x: "x", y: "y" };
|
|
4216
4565
|
}
|
|
4217
4566
|
geomData = applyCoordTransform(geomData, geomAes, spec.coord);
|
|
4218
4567
|
}
|
|
@@ -5135,8 +5484,32 @@ function geom_qq_line(options = {}) {
|
|
|
5135
5484
|
};
|
|
5136
5485
|
}
|
|
5137
5486
|
|
|
5487
|
+
// src/geoms/ridgeline.ts
|
|
5488
|
+
function geom_ridgeline(options = {}) {
|
|
5489
|
+
return {
|
|
5490
|
+
type: "ridgeline",
|
|
5491
|
+
stat: "xdensity",
|
|
5492
|
+
position: "identity",
|
|
5493
|
+
params: {
|
|
5494
|
+
scale: options.scale ?? 0.9,
|
|
5495
|
+
alpha: options.alpha ?? 0.8,
|
|
5496
|
+
fill: options.fill,
|
|
5497
|
+
color: options.color,
|
|
5498
|
+
adjust: options.adjust ?? 1,
|
|
5499
|
+
n: options.n ?? 128,
|
|
5500
|
+
outline: options.outline ?? true
|
|
5501
|
+
}
|
|
5502
|
+
};
|
|
5503
|
+
}
|
|
5504
|
+
var geom_joy;
|
|
5505
|
+
var init_ridgeline = __esm(() => {
|
|
5506
|
+
geom_joy = geom_ridgeline;
|
|
5507
|
+
});
|
|
5508
|
+
|
|
5138
5509
|
// src/geoms/index.ts
|
|
5139
|
-
var init_geoms = () => {
|
|
5510
|
+
var init_geoms = __esm(() => {
|
|
5511
|
+
init_ridgeline();
|
|
5512
|
+
});
|
|
5140
5513
|
|
|
5141
5514
|
// src/stats/index.ts
|
|
5142
5515
|
var init_stats = __esm(() => {
|
|
@@ -9645,6 +10018,7 @@ __export(exports_src, {
|
|
|
9645
10018
|
geom_smooth: () => geom_smooth,
|
|
9646
10019
|
geom_segment: () => geom_segment,
|
|
9647
10020
|
geom_rug: () => geom_rug,
|
|
10021
|
+
geom_ridgeline: () => geom_ridgeline,
|
|
9648
10022
|
geom_ribbon: () => geom_ribbon,
|
|
9649
10023
|
geom_rect: () => geom_rect,
|
|
9650
10024
|
geom_raster: () => geom_raster,
|
|
@@ -9656,6 +10030,7 @@ __export(exports_src, {
|
|
|
9656
10030
|
geom_linerange: () => geom_linerange,
|
|
9657
10031
|
geom_line: () => geom_line,
|
|
9658
10032
|
geom_label: () => geom_label,
|
|
10033
|
+
geom_joy: () => geom_joy,
|
|
9659
10034
|
geom_hline: () => geom_hline,
|
|
9660
10035
|
geom_histogram: () => geom_histogram,
|
|
9661
10036
|
geom_freqpoly: () => geom_freqpoly,
|
|
@@ -10564,6 +10939,8 @@ var GEOM_TYPES = [
|
|
|
10564
10939
|
"freqpoly",
|
|
10565
10940
|
"boxplot",
|
|
10566
10941
|
"violin",
|
|
10942
|
+
"ridgeline",
|
|
10943
|
+
"joy",
|
|
10567
10944
|
"area",
|
|
10568
10945
|
"ribbon",
|
|
10569
10946
|
"rug",
|
|
@@ -10958,7 +11335,7 @@ Error: Unknown geometry type "${geomType}"`);
|
|
|
10958
11335
|
Available geom types:`);
|
|
10959
11336
|
console.error(` Points/Lines: point, line, path, step, smooth, segment`);
|
|
10960
11337
|
console.error(` Bars/Areas: bar, col, histogram, freqpoly, area, ribbon`);
|
|
10961
|
-
console.error(` Distributions: boxplot, violin, qq, density_2d`);
|
|
11338
|
+
console.error(` Distributions: boxplot, violin, ridgeline, joy, qq, density_2d`);
|
|
10962
11339
|
console.error(` Uncertainty: errorbar, errorbarh, crossbar, linerange, pointrange`);
|
|
10963
11340
|
console.error(` 2D: tile, rect, raster, bin2d, contour, contour_filled`);
|
|
10964
11341
|
console.error(` Text: text, label`);
|
|
@@ -11103,6 +11480,10 @@ If you want a univariate plot, try: histogram, bar, qq, or freqpoly`);
|
|
|
11103
11480
|
case "violin":
|
|
11104
11481
|
plot = plot.geom(geom_violin());
|
|
11105
11482
|
break;
|
|
11483
|
+
case "ridgeline":
|
|
11484
|
+
case "joy":
|
|
11485
|
+
plot = plot.geom(geom_ridgeline());
|
|
11486
|
+
break;
|
|
11106
11487
|
case "bar":
|
|
11107
11488
|
plot = plot.geom(geom_bar());
|
|
11108
11489
|
break;
|