@graphenedata/cli 0.0.1
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/LICENSE.md +100 -0
- package/THIRD_PARTY_NOTICES.md +12 -0
- package/cli.ts +157 -0
- package/dist/cli/cli.js +43 -0
- package/dist/docs/data_apps/components/charts/annotations.md +673 -0
- package/dist/docs/data_apps/components/charts/area-chart.md +202 -0
- package/dist/docs/data_apps/components/charts/bar-chart.md +317 -0
- package/dist/docs/data_apps/components/charts/box-plot.md +190 -0
- package/dist/docs/data_apps/components/charts/bubble-chart.md +151 -0
- package/dist/docs/data_apps/components/charts/calendar-heatmap.md +112 -0
- package/dist/docs/data_apps/components/charts/custom-echarts.md +308 -0
- package/dist/docs/data_apps/components/charts/echarts-options.md +217 -0
- package/dist/docs/data_apps/components/charts/funnel-chart.md +106 -0
- package/dist/docs/data_apps/components/charts/heatmap.md +180 -0
- package/dist/docs/data_apps/components/charts/histogram.md +107 -0
- package/dist/docs/data_apps/components/charts/line-chart.md +265 -0
- package/dist/docs/data_apps/components/charts/mixed-type-charts.md +240 -0
- package/dist/docs/data_apps/components/charts/sankey-diagram.md +301 -0
- package/dist/docs/data_apps/components/charts/scatter-plot.md +134 -0
- package/dist/docs/data_apps/components/charts/sparkline.md +68 -0
- package/dist/docs/data_apps/components/data/big-value.md +153 -0
- package/dist/docs/data_apps/components/data/delta.md +89 -0
- package/dist/docs/data_apps/components/data/table.md +470 -0
- package/dist/docs/data_apps/components/data/value.md +97 -0
- package/dist/docs/data_apps/components/inputs/button-group.md +154 -0
- package/dist/docs/data_apps/components/inputs/checkbox.md +52 -0
- package/dist/docs/data_apps/components/inputs/date-input.md +131 -0
- package/dist/docs/data_apps/components/inputs/date-range.md +124 -0
- package/dist/docs/data_apps/components/inputs/dimension-grid.md +67 -0
- package/dist/docs/data_apps/components/inputs/dropdown.md +199 -0
- package/dist/docs/data_apps/components/inputs/index.md +3 -0
- package/dist/docs/data_apps/components/inputs/slider.md +126 -0
- package/dist/docs/data_apps/components/inputs/text-input.md +86 -0
- package/dist/docs/data_apps/components/maps/area-map.md +397 -0
- package/dist/docs/data_apps/components/maps/base-map.md +269 -0
- package/dist/docs/data_apps/components/maps/bubble-map.md +361 -0
- package/dist/docs/data_apps/components/maps/point-map.md +326 -0
- package/dist/docs/data_apps/components/maps/us-map.md +167 -0
- package/dist/docs/data_apps/components/ui/accordion.md +116 -0
- package/dist/docs/data_apps/components/ui/alert.md +37 -0
- package/dist/docs/data_apps/components/ui/big-link.md +19 -0
- package/dist/docs/data_apps/components/ui/details.md +58 -0
- package/dist/docs/data_apps/components/ui/download-data.md +41 -0
- package/dist/docs/data_apps/components/ui/embed.md +47 -0
- package/dist/docs/data_apps/components/ui/grid.md +45 -0
- package/dist/docs/data_apps/components/ui/image.md +61 -0
- package/dist/docs/data_apps/components/ui/info.md +47 -0
- package/dist/docs/data_apps/components/ui/last-refreshed.md +28 -0
- package/dist/docs/data_apps/components/ui/link-button.md +20 -0
- package/dist/docs/data_apps/components/ui/link.md +40 -0
- package/dist/docs/data_apps/components/ui/modal.md +57 -0
- package/dist/docs/data_apps/components/ui/note.md +32 -0
- package/dist/docs/data_apps/components/ui/print-format-components.md +85 -0
- package/dist/docs/data_apps/components/ui/tabs.md +122 -0
- package/dist/docs/graphene.md +129 -0
- package/dist/ui/app.css +332 -0
- package/dist/ui/assets/favicon.ico +0 -0
- package/dist/ui/component-utilities/autoFormatting.js +301 -0
- package/dist/ui/component-utilities/builtInFormats.js +482 -0
- package/dist/ui/component-utilities/chartContext.js +12 -0
- package/dist/ui/component-utilities/chartWindowDebug.js +21 -0
- package/dist/ui/component-utilities/checkInputs.js +95 -0
- package/dist/ui/component-utilities/convert.js +15 -0
- package/dist/ui/component-utilities/dateParsing.js +57 -0
- package/dist/ui/component-utilities/dropdownContext.ts +1 -0
- package/dist/ui/component-utilities/echarts.js +262 -0
- package/dist/ui/component-utilities/echartsThemes.js +453 -0
- package/dist/ui/component-utilities/formatTitle.js +24 -0
- package/dist/ui/component-utilities/formatting.js +258 -0
- package/dist/ui/component-utilities/getColumnExtents.js +79 -0
- package/dist/ui/component-utilities/getColumnSummary.js +67 -0
- package/dist/ui/component-utilities/getCompletedData.js +114 -0
- package/dist/ui/component-utilities/getDistinctCount.js +7 -0
- package/dist/ui/component-utilities/getDistinctValues.js +15 -0
- package/dist/ui/component-utilities/getSeriesConfig.js +236 -0
- package/dist/ui/component-utilities/getSortedData.js +7 -0
- package/dist/ui/component-utilities/getStackPercentages.js +43 -0
- package/dist/ui/component-utilities/getStackedData.js +17 -0
- package/dist/ui/component-utilities/getYAxisIndex.js +15 -0
- package/dist/ui/component-utilities/globalContexts.js +1 -0
- package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +119 -0
- package/dist/ui/component-utilities/inputUtils.ts +25 -0
- package/dist/ui/component-utilities/replaceNulls.js +14 -0
- package/dist/ui/component-utilities/tableUtils.ts +120 -0
- package/dist/ui/component-utilities/themeStores.ts +116 -0
- package/dist/ui/components/Area.svelte +174 -0
- package/dist/ui/components/AreaChart.svelte +150 -0
- package/dist/ui/components/Bar.svelte +326 -0
- package/dist/ui/components/BarChart.svelte +194 -0
- package/dist/ui/components/BigValue.svelte +69 -0
- package/dist/ui/components/Chart.svelte +1070 -0
- package/dist/ui/components/Column.svelte +172 -0
- package/dist/ui/components/DateRange.svelte +324 -0
- package/dist/ui/components/Dropdown.svelte +738 -0
- package/dist/ui/components/DropdownOption.svelte +21 -0
- package/dist/ui/components/ECharts.svelte +77 -0
- package/dist/ui/components/ErrorChart.svelte +54 -0
- package/dist/ui/components/GrapheneQuery.svelte +12 -0
- package/dist/ui/components/InlineDelta.svelte +150 -0
- package/dist/ui/components/Line.svelte +210 -0
- package/dist/ui/components/LineChart.svelte +178 -0
- package/dist/ui/components/PieChart.svelte +36 -0
- package/dist/ui/components/QueryLoad.svelte +82 -0
- package/dist/ui/components/Row.svelte +14 -0
- package/dist/ui/components/SortIcon.svelte +32 -0
- package/dist/ui/components/Table.svelte +19 -0
- package/dist/ui/components/TableCell.svelte +75 -0
- package/dist/ui/components/TableGroupRow.svelte +136 -0
- package/dist/ui/components/TableGroupToggle.svelte +42 -0
- package/dist/ui/components/TableHeader.svelte +242 -0
- package/dist/ui/components/TableRow.svelte +283 -0
- package/dist/ui/components/TableSubtotalRow.svelte +62 -0
- package/dist/ui/components/TableTotalRow.svelte +88 -0
- package/dist/ui/components/TextInput.svelte +92 -0
- package/dist/ui/components/_Table.svelte +516 -0
- package/dist/ui/internal/clientCache.ts +43 -0
- package/dist/ui/internal/queryEngine.ts +169 -0
- package/dist/ui/internal/telemetry.ts +28 -0
- package/dist/ui/internal/theme.ts +88 -0
- package/dist/ui/layout.svelte +3 -0
- package/dist/ui/playwright.config.ts +30 -0
- package/dist/ui/web.js +106 -0
- package/package.json +71 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import getDistinctValues from './getDistinctValues.js'
|
|
2
|
+
import {fmt} from './formatting.js'
|
|
3
|
+
|
|
4
|
+
export default function getSeriesConfig (
|
|
5
|
+
data,
|
|
6
|
+
x,
|
|
7
|
+
y,
|
|
8
|
+
series,
|
|
9
|
+
swapXY,
|
|
10
|
+
baseConfig,
|
|
11
|
+
name,
|
|
12
|
+
xMismatch, // this checks for scenarios where xType is string and xDataType is number. When this is the case, we need to inject strings into the x axis, or else it will cause echarts to think there are duplicate x-axis values (e.g., "4" and 4)
|
|
13
|
+
columnSummary,
|
|
14
|
+
seriesOrder,
|
|
15
|
+
size = undefined,
|
|
16
|
+
tooltipTitle = undefined,
|
|
17
|
+
y2 = undefined,
|
|
18
|
+
seriesLabelFmt = undefined,
|
|
19
|
+
) {
|
|
20
|
+
function generateTempConfig (seriesData, seriesName, yAxisIndex, baseConfig) {
|
|
21
|
+
let tempConfig = {
|
|
22
|
+
name: seriesName,
|
|
23
|
+
data: seriesData,
|
|
24
|
+
yAxisIndex: yAxisIndex,
|
|
25
|
+
}
|
|
26
|
+
tempConfig = {...baseConfig, ...tempConfig}
|
|
27
|
+
return tempConfig
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let i
|
|
31
|
+
let j
|
|
32
|
+
let tempConfig
|
|
33
|
+
let seriesConfig = []
|
|
34
|
+
let seriesData
|
|
35
|
+
let filteredData
|
|
36
|
+
let seriesName
|
|
37
|
+
let seriesDistinct
|
|
38
|
+
let yAxisIndex
|
|
39
|
+
|
|
40
|
+
// y = single, y2 = empty
|
|
41
|
+
// y = single, y2 = single
|
|
42
|
+
// y = single, y2 = array
|
|
43
|
+
|
|
44
|
+
// y = array, y2 = empty
|
|
45
|
+
// y = array, y2 = single
|
|
46
|
+
// y = array, y2 = array
|
|
47
|
+
|
|
48
|
+
// y = empty, y2 = empty
|
|
49
|
+
// y = empty, y2 = single
|
|
50
|
+
// y = empty, y2 = array
|
|
51
|
+
|
|
52
|
+
// colname, yAxisIndex
|
|
53
|
+
|
|
54
|
+
function combineVariables (variable1, variable2) {
|
|
55
|
+
// Returns an array of arrays, where each individual array is [column_name, yAxisIndex], where yAxisIndex is 0 for y and 1 for y2.
|
|
56
|
+
// E.g., [ ['sales', 0 ], ['gross_profit', 1]] - sales on primary axis, gross profit on secondary
|
|
57
|
+
let array = []
|
|
58
|
+
|
|
59
|
+
// Helper function to check if a value is undefined
|
|
60
|
+
function isUndefined (value) {
|
|
61
|
+
return typeof value === 'undefined'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Helper function to add non-undefined values to the array with source indicator
|
|
65
|
+
function addValuesToArray (value, source) {
|
|
66
|
+
if (!isUndefined(value)) {
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
value.forEach((item) => array.push([item, source]))
|
|
69
|
+
} else {
|
|
70
|
+
array.push([value, source])
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
addValuesToArray(variable1, 0)
|
|
76
|
+
addValuesToArray(variable2, 1)
|
|
77
|
+
|
|
78
|
+
return array
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let yList = combineVariables(y, y2)
|
|
82
|
+
|
|
83
|
+
// 1) Series column with single y column
|
|
84
|
+
if (series != null && yList.length === 1) {
|
|
85
|
+
seriesDistinct = getDistinctValues(data, series)
|
|
86
|
+
|
|
87
|
+
for (i = 0; i < seriesDistinct.length; i++) {
|
|
88
|
+
// Filter for specific series:
|
|
89
|
+
filteredData = data.filter((d) => d[series] === seriesDistinct[i])
|
|
90
|
+
|
|
91
|
+
if (swapXY) {
|
|
92
|
+
seriesData = filteredData.map((d) => [d[yList[0][0]], xMismatch ? d[x].toString() : d[x]])
|
|
93
|
+
} else {
|
|
94
|
+
seriesData = filteredData.map((d) => [xMismatch ? d[x].toString() : d[x], d[yList[0][0]]])
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Append size column if supplied (for bubble chart):
|
|
98
|
+
if (size) {
|
|
99
|
+
let sizeData = filteredData.map((d) => d[size])
|
|
100
|
+
seriesData.forEach((item, index) => item.push(sizeData[index]))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Append tooltip label if supplied:
|
|
104
|
+
if (tooltipTitle) {
|
|
105
|
+
let tooltipData = filteredData.map((d) => d[tooltipTitle])
|
|
106
|
+
seriesData.forEach((item, index) => item.push(tooltipData[index]))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Set series name:
|
|
110
|
+
seriesName = seriesDistinct[i] ?? 'null'
|
|
111
|
+
|
|
112
|
+
// Set y-axis index (used for multi-y axis charts):
|
|
113
|
+
yAxisIndex = yList[0][1]
|
|
114
|
+
|
|
115
|
+
tempConfig = generateTempConfig(seriesData, seriesName, yAxisIndex, baseConfig)
|
|
116
|
+
seriesConfig.push(tempConfig)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2) Series column with multiple y columns
|
|
121
|
+
if (series != null && yList.length > 1) {
|
|
122
|
+
seriesDistinct = getDistinctValues(data, series)
|
|
123
|
+
for (i = 0; i < seriesDistinct.length; i++) {
|
|
124
|
+
// Filter for specific series:
|
|
125
|
+
filteredData = data.filter((d) => d[series] === seriesDistinct[i])
|
|
126
|
+
|
|
127
|
+
for (j = 0; j < yList.length; j++) {
|
|
128
|
+
if (swapXY) {
|
|
129
|
+
seriesData = filteredData.map((d) => [
|
|
130
|
+
d[yList[j][0]],
|
|
131
|
+
xMismatch ? d[x].toString() : d[x],
|
|
132
|
+
])
|
|
133
|
+
} else {
|
|
134
|
+
seriesData = filteredData.map((d) => [
|
|
135
|
+
xMismatch ? d[x].toString() : d[x],
|
|
136
|
+
d[yList[j][0]],
|
|
137
|
+
])
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Append size column if supplied (for bubble chart):
|
|
141
|
+
if (size) {
|
|
142
|
+
let sizeData = filteredData.map((d) => d[size])
|
|
143
|
+
seriesData.forEach((item, index) => item.push(sizeData[index]))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Append tooltip label if supplied:
|
|
147
|
+
if (tooltipTitle) {
|
|
148
|
+
let tooltipData = filteredData.map((d) => d[tooltipTitle])
|
|
149
|
+
seriesData.forEach((item, index) => item.push(tooltipData[index]))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Set series name:
|
|
153
|
+
seriesName = (seriesDistinct[i] ?? 'null') + ' - ' + columnSummary[yList[j][0]].title
|
|
154
|
+
|
|
155
|
+
// Set y-axis index (used for multi-y axis charts):
|
|
156
|
+
yAxisIndex = yList[j][1]
|
|
157
|
+
|
|
158
|
+
tempConfig = generateTempConfig(seriesData, seriesName, yAxisIndex, baseConfig)
|
|
159
|
+
seriesConfig.push(tempConfig)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 3) Multiple y columns without series column
|
|
165
|
+
if (series == null && yList.length > 1) {
|
|
166
|
+
for (i = 0; i < yList.length; i++) {
|
|
167
|
+
if (swapXY) {
|
|
168
|
+
seriesData = data.map((d) => [d[yList[i][0]], xMismatch ? d[x].toString() : d[x]])
|
|
169
|
+
} else {
|
|
170
|
+
seriesData = data.map((d) => [xMismatch ? d[x].toString() : d[x], d[yList[i][0]]])
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Append size column if supplied (for bubble chart):
|
|
174
|
+
if (size) {
|
|
175
|
+
let sizeData = data.map((d) => d[size])
|
|
176
|
+
seriesData.forEach((item, index) => item.push(sizeData[index]))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Append tooltip label if supplied:
|
|
180
|
+
if (tooltipTitle) {
|
|
181
|
+
let tooltipData = data.map((d) => d[tooltipTitle])
|
|
182
|
+
seriesData.forEach((item, index) => item.push(tooltipData[index]))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
seriesName = columnSummary[yList[i][0]].title
|
|
186
|
+
|
|
187
|
+
// Set y-axis index (used for multi-y axis charts):
|
|
188
|
+
yAxisIndex = yList[i][1]
|
|
189
|
+
|
|
190
|
+
tempConfig = generateTempConfig(seriesData, seriesName, yAxisIndex, baseConfig)
|
|
191
|
+
seriesConfig.push(tempConfig)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 4) Single y column without series column
|
|
196
|
+
if (series == null && yList.length === 1) {
|
|
197
|
+
if (swapXY) {
|
|
198
|
+
seriesData = data.map((d) => [d[yList[0][0]], xMismatch ? d[x].toString() : d[x]])
|
|
199
|
+
} else {
|
|
200
|
+
seriesData = data.map((d) => [xMismatch ? d[x].toString() : d[x], d[yList[0][0]]])
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Append size column if supplied (for bubble chart):
|
|
204
|
+
if (size) {
|
|
205
|
+
let sizeData = data.map((d) => d[size])
|
|
206
|
+
seriesData.forEach((item, index) => item.push(sizeData[index]))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Append tooltip label if supplied:
|
|
210
|
+
if (tooltipTitle) {
|
|
211
|
+
let tooltipData = data.map((d) => d[tooltipTitle])
|
|
212
|
+
seriesData.forEach((item, index) => item.push(tooltipData[index]))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
seriesName = columnSummary[yList[0][0]].title
|
|
216
|
+
|
|
217
|
+
// Set y-axis index (used for multi-y axis charts):
|
|
218
|
+
yAxisIndex = yList[0][1]
|
|
219
|
+
|
|
220
|
+
tempConfig = generateTempConfig(seriesData, seriesName, yAxisIndex, baseConfig)
|
|
221
|
+
seriesConfig.push(tempConfig)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (seriesOrder) {
|
|
225
|
+
seriesConfig.sort((a, b) => seriesOrder.indexOf(a.name) - seriesOrder.indexOf(b.name))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// format series config:
|
|
229
|
+
if (seriesLabelFmt) {
|
|
230
|
+
seriesConfig.forEach((item) => {
|
|
231
|
+
item.name = fmt(item.name, seriesLabelFmt)
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return seriesConfig
|
|
236
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {tidy, groupBy, sum, mutateWithSummary, mutate, rate, rename} from '@tidyjs/tidy'
|
|
2
|
+
|
|
3
|
+
export default function getStackPercentages (data, groupCol, valueCol) {
|
|
4
|
+
let pctData
|
|
5
|
+
if (typeof valueCol !== 'object') {
|
|
6
|
+
pctData = tidy(
|
|
7
|
+
data,
|
|
8
|
+
groupBy(groupCol, mutateWithSummary({xTotal: sum(valueCol)})),
|
|
9
|
+
mutate({percentOfX: rate(valueCol, 'xTotal')}),
|
|
10
|
+
rename({
|
|
11
|
+
percentOfX: valueCol + '_pct',
|
|
12
|
+
}),
|
|
13
|
+
)
|
|
14
|
+
} else {
|
|
15
|
+
pctData = tidy(
|
|
16
|
+
data,
|
|
17
|
+
mutate({
|
|
18
|
+
valueSum: 0,
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < pctData.length; i++) {
|
|
23
|
+
pctData[i].valueSum = 0
|
|
24
|
+
for (let j = 0; j < valueCol.length; j++) {
|
|
25
|
+
pctData[i].valueSum = pctData[i].valueSum + pctData[i][valueCol[j]]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pctData = tidy(pctData, groupBy(groupCol, mutateWithSummary({xTotal: sum('valueSum')})))
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < valueCol.length; i++) {
|
|
32
|
+
pctData = tidy(
|
|
33
|
+
pctData,
|
|
34
|
+
mutate({percentOfX: rate(valueCol[i], 'xTotal')}),
|
|
35
|
+
rename({
|
|
36
|
+
percentOfX: valueCol[i] + '_pct',
|
|
37
|
+
}),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return pctData
|
|
43
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {tidy, groupBy, summarizeAt, sum} from '@tidyjs/tidy'
|
|
2
|
+
|
|
3
|
+
export default function getStackedData (data, groupCol, valueCol) {
|
|
4
|
+
let stackedData = tidy(data, groupBy(groupCol, [summarizeAt(valueCol, sum)]))
|
|
5
|
+
|
|
6
|
+
// If multiple y columns, iterate through data and add stack total column for sorting:
|
|
7
|
+
if (typeof valueCol === 'object') {
|
|
8
|
+
for (let i = 0; i < stackedData.length; i++) {
|
|
9
|
+
stackedData[i].stackTotal = 0
|
|
10
|
+
for (let j = 0; j < valueCol.length; j++) {
|
|
11
|
+
stackedData[i].stackTotal = stackedData[i].stackTotal + stackedData[i][valueCol[j]]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return stackedData
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Helper function for multi-series tooltips:
|
|
2
|
+
// Returns the yAxisIndex for a series since we can't currently access that in ECharts' params
|
|
3
|
+
export default function getYAxisIndex (componentIndex, yCount, y2Count) {
|
|
4
|
+
let totalPatternCount = yCount + y2Count
|
|
5
|
+
|
|
6
|
+
// Find the position of the index in the repeating sequence
|
|
7
|
+
let positionInPattern = componentIndex % totalPatternCount
|
|
8
|
+
|
|
9
|
+
// If the position lies within yCount, return 0, otherwise return 1
|
|
10
|
+
if (positionInPattern < yCount) {
|
|
11
|
+
return 0
|
|
12
|
+
} else {
|
|
13
|
+
return 1
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CUSTOM_FORMATTING_SETTINGS_CONTEXT_KEY = 'customFormattingSettings'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This function is used to find difference between consecutive elements in an array.
|
|
3
|
+
*
|
|
4
|
+
* @param {Array<number>} arr - The array from which differences need to be found.
|
|
5
|
+
* @return {Array<number>} An array containing the differences between consecutive elements.
|
|
6
|
+
*/
|
|
7
|
+
export function getDiffs (arr) {
|
|
8
|
+
let diffs = []
|
|
9
|
+
for (let i = 1; i < arr.length; i++) diffs.push(arr[i] - arr[i - 1])
|
|
10
|
+
return diffs
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The function is used to find greatest common divisor (gcd) between two numbers.
|
|
15
|
+
*
|
|
16
|
+
* @param {number} a - The first number to find gcd.
|
|
17
|
+
* @param {number} b - The second number to find gcd.
|
|
18
|
+
* @return {number} The greatest common divisor of the input numbers a and b.
|
|
19
|
+
*/
|
|
20
|
+
export function gcd (a, b) {
|
|
21
|
+
// Treat non-numeric types as 0
|
|
22
|
+
if (typeof a !== 'number' || isNaN(a)) a = 0
|
|
23
|
+
if (typeof b !== 'number' || isNaN(b)) b = 0
|
|
24
|
+
|
|
25
|
+
// Handle negative numbers properly
|
|
26
|
+
// Never reaches base case w/o this
|
|
27
|
+
a = Math.abs(a)
|
|
28
|
+
b = Math.abs(b)
|
|
29
|
+
|
|
30
|
+
// base case
|
|
31
|
+
if (b <= 0.01) {
|
|
32
|
+
return a
|
|
33
|
+
} else {
|
|
34
|
+
return gcd(b, a % b)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This function is used to find the minimum and maximum values in an array.
|
|
40
|
+
*
|
|
41
|
+
* @param {Array} values - An array from which min and max values should be determined.
|
|
42
|
+
* @param {Function} [valueof] - An optional function that defines how to obtain the measuring value.
|
|
43
|
+
* @return {Array} An array containing the minimum and maximum of numbers, respectively.
|
|
44
|
+
*/
|
|
45
|
+
export function extent (values, valueof) {
|
|
46
|
+
if (!Array.isArray(values)) throw new TypeError('Cannot calculate extent of non-array value.')
|
|
47
|
+
let min
|
|
48
|
+
let max
|
|
49
|
+
if (valueof === undefined) {
|
|
50
|
+
for (let candidate of values) {
|
|
51
|
+
if (typeof candidate !== 'number' || Number.isNaN(candidate)) continue
|
|
52
|
+
|
|
53
|
+
if (min === undefined || candidate < min) min = candidate
|
|
54
|
+
if (max === undefined || candidate > max) max = candidate
|
|
55
|
+
}
|
|
56
|
+
return [min, max]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let index = -1
|
|
60
|
+
for (let original of values) {
|
|
61
|
+
if (typeof original !== 'number') continue
|
|
62
|
+
let candidate = valueof(original, ++index, values)
|
|
63
|
+
if (candidate == null || Number.isNaN(candidate)) continue
|
|
64
|
+
|
|
65
|
+
if (min === undefined || candidate < min) min = candidate
|
|
66
|
+
if (max === undefined || candidate > max) max = candidate
|
|
67
|
+
}
|
|
68
|
+
return [min, max]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* This function generates a sequence of numbers as a vector from minimum to maximum with the given period increment.
|
|
73
|
+
*
|
|
74
|
+
* @param {Array<number>} values - An array containing the data to be sequenced.
|
|
75
|
+
* @param {number} period - The incremental value for each step in the sequence.
|
|
76
|
+
* @return {Array<number>} An array containing the sequenced numbers.
|
|
77
|
+
*/
|
|
78
|
+
export function vectorSeq (values, period) {
|
|
79
|
+
let [min, max] = extent(values)
|
|
80
|
+
|
|
81
|
+
let sequence = []
|
|
82
|
+
let value = min
|
|
83
|
+
while (value <= max) {
|
|
84
|
+
sequence.push(Math.round((value + Number.EPSILON) * 100000000) / 100000000)
|
|
85
|
+
value += period
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return sequence
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* This function is used to find the interval distance among numbers in an array.
|
|
93
|
+
*
|
|
94
|
+
* @param {Array<number>} arr - An array containing numbers from which interval is calculated.
|
|
95
|
+
* @return {number|undefined} The interval between numbers in the sorted array, or undefined if the array has only one element.
|
|
96
|
+
*/
|
|
97
|
+
export function findInterval (arr) {
|
|
98
|
+
if (arr.length <= 1) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Sort array ascending
|
|
103
|
+
arr.sort(function (a, b) {
|
|
104
|
+
return a - b
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// 1. Multiply array by 100
|
|
108
|
+
arr = arr.map(function (x) {
|
|
109
|
+
return x * 100000000
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// 2. Get diffs
|
|
113
|
+
arr = getDiffs(arr)
|
|
114
|
+
|
|
115
|
+
// 3. Calculate greatest common divisor of diffs and divide by 100
|
|
116
|
+
let interval = arr.reduce((a, b) => gcd(a, b)) / 100000000
|
|
117
|
+
interval = Math.round((interval + Number.EPSILON) * 100000000) / 100000000
|
|
118
|
+
return interval
|
|
119
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
export function toBoolean (value: any): boolean {
|
|
3
|
+
if (typeof value === 'boolean') return value
|
|
4
|
+
if (typeof value === 'number') return value !== 0
|
|
5
|
+
if (typeof value === 'string') {
|
|
6
|
+
let trimmed = value.trim().toLowerCase()
|
|
7
|
+
if (trimmed === 'true' || trimmed === 'yes' || trimmed === '1') return true
|
|
8
|
+
if (trimmed === 'false' || trimmed === 'no' || trimmed === '0' || trimmed === '') return false
|
|
9
|
+
}
|
|
10
|
+
return Boolean(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ensureArray<T> (value: T | T[] | undefined | null): T[] {
|
|
14
|
+
if (Array.isArray(value)) return value
|
|
15
|
+
if (value === undefined || value === null) return []
|
|
16
|
+
return [value]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function serializeValue (value: unknown): string {
|
|
20
|
+
if (value === null || value === undefined) return 'NULL'
|
|
21
|
+
if (typeof value === 'number' || typeof value === 'bigint') return String(value)
|
|
22
|
+
if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE'
|
|
23
|
+
let str = String(value)
|
|
24
|
+
return `'${str.replace(/'/g, "''")}'`
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {tidy, replaceNully} from '@tidyjs/tidy'
|
|
2
|
+
|
|
3
|
+
export default function replaceNulls (data, columns) {
|
|
4
|
+
let colObj = {}
|
|
5
|
+
if (typeof columns === 'object') {
|
|
6
|
+
for (let i = 0; i < columns.length; i++) {
|
|
7
|
+
colObj[columns[i]] = 0
|
|
8
|
+
}
|
|
9
|
+
} else {
|
|
10
|
+
colObj[columns] = 0
|
|
11
|
+
}
|
|
12
|
+
data = tidy(data, replaceNully(colObj))
|
|
13
|
+
return data
|
|
14
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {strictBuild} from './chartContext.js'
|
|
2
|
+
|
|
3
|
+
type ColumnSummary = {
|
|
4
|
+
id: string
|
|
5
|
+
type?: string
|
|
6
|
+
format?: any
|
|
7
|
+
columnUnitSummary?: any
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ColumnOption = {
|
|
11
|
+
id: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type ColumnLike = ColumnOption & Partial<ColumnSummary>
|
|
15
|
+
|
|
16
|
+
export const safeExtractColumn = <T extends ColumnLike>(column: T, columnSummary: ColumnSummary[]): ColumnSummary => {
|
|
17
|
+
let foundCols = columnSummary.filter(d => d.id === column.id)
|
|
18
|
+
if (!foundCols.length) {
|
|
19
|
+
let error = column.id === undefined
|
|
20
|
+
? new Error('please add an "id" property to all the <Column ... />')
|
|
21
|
+
: new Error(`column with id: "${column.id}" not found`)
|
|
22
|
+
if (strictBuild) throw error
|
|
23
|
+
console.warn(error.message)
|
|
24
|
+
return {id: column.id ?? ''}
|
|
25
|
+
}
|
|
26
|
+
return foundCols[0]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const weightedMean = (data: Record<string, unknown>[], valueCol: string, weightCol?: string | null): number | null => {
|
|
30
|
+
if (!weightCol) return null
|
|
31
|
+
if (!data.length) return null
|
|
32
|
+
|
|
33
|
+
let totalWeightedValue = 0
|
|
34
|
+
let totalWeight = 0
|
|
35
|
+
|
|
36
|
+
for (let item of data) {
|
|
37
|
+
let value = Number(item[valueCol] ?? 0)
|
|
38
|
+
let weight = Number(item[weightCol] ?? 0)
|
|
39
|
+
totalWeightedValue += value * weight
|
|
40
|
+
totalWeight += weight
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return totalWeight > 0 ? totalWeightedValue / totalWeight : 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const median = (data: Record<string, unknown>[], column: string): number => {
|
|
47
|
+
let values = data
|
|
48
|
+
.map(item => item[column])
|
|
49
|
+
.filter(val => val !== undefined && val !== null && !Number.isNaN(Number(val)))
|
|
50
|
+
.map(val => Number(val))
|
|
51
|
+
.sort((a, b) => a - b)
|
|
52
|
+
|
|
53
|
+
if (!values.length) return 0
|
|
54
|
+
|
|
55
|
+
let mid = Math.floor(values.length / 2)
|
|
56
|
+
return values.length % 2 !== 0 ? values[mid] : (values[mid - 1] + values[mid]) / 2
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const aggregateColumn = (
|
|
60
|
+
data: Record<string, unknown>[],
|
|
61
|
+
columnName: string,
|
|
62
|
+
aggType: string | undefined,
|
|
63
|
+
columnType?: string,
|
|
64
|
+
weightColumnName?: string | null,
|
|
65
|
+
): number | string | null => {
|
|
66
|
+
if (!data || !data.length) return null
|
|
67
|
+
|
|
68
|
+
if (!aggType && columnType === 'number') aggType = 'sum'
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
columnType !== 'number' &&
|
|
72
|
+
['sum', 'min', 'max', 'mean', 'weightedMean', 'median', undefined].includes(aggType as any)
|
|
73
|
+
) {
|
|
74
|
+
return '-'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let columnValues = data
|
|
78
|
+
.map(row => row[columnName])
|
|
79
|
+
.filter(val => val !== undefined && val !== null)
|
|
80
|
+
.map(val => Number(val))
|
|
81
|
+
|
|
82
|
+
switch (aggType) {
|
|
83
|
+
case 'sum':
|
|
84
|
+
return columnValues.reduce((sum, val) => sum + Number(val), 0)
|
|
85
|
+
case 'min':
|
|
86
|
+
return Math.min(...columnValues)
|
|
87
|
+
case 'max':
|
|
88
|
+
return Math.max(...columnValues)
|
|
89
|
+
case 'mean':
|
|
90
|
+
return columnValues.length
|
|
91
|
+
? columnValues.reduce((sum, val) => sum + Number(val), 0) / columnValues.length
|
|
92
|
+
: '-'
|
|
93
|
+
case 'count':
|
|
94
|
+
return data.length
|
|
95
|
+
case 'countDistinct':
|
|
96
|
+
return new Set(columnValues).size
|
|
97
|
+
case 'weightedMean':
|
|
98
|
+
if (!weightColumnName) return 'Weight column name required for weightedMean'
|
|
99
|
+
let totalWeight = 0
|
|
100
|
+
let weightedSum = 0
|
|
101
|
+
for (let row of data) {
|
|
102
|
+
let weight = Number(row[weightColumnName] ?? 0)
|
|
103
|
+
totalWeight += weight
|
|
104
|
+
weightedSum += Number(row[columnName] ?? 0) * weight
|
|
105
|
+
}
|
|
106
|
+
return totalWeight > 0 ? weightedSum / totalWeight : null
|
|
107
|
+
case 'median':
|
|
108
|
+
return median(data, columnName)
|
|
109
|
+
case undefined:
|
|
110
|
+
return '-'
|
|
111
|
+
default:
|
|
112
|
+
return aggType ?? '-'
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const getFinalColumnOrder = (columns: string[], priorityColumns: Array<string | undefined>): string[] => {
|
|
117
|
+
let priorities = priorityColumns.filter(Boolean) as string[]
|
|
118
|
+
let restColumns = columns.filter(key => !priorities.includes(key))
|
|
119
|
+
return [...priorities, ...restColumns]
|
|
120
|
+
}
|