Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
picasso-plugin-q
Advanced tools
picasso-q-plugin
The q
plugin registers a q
dataset type that makes it a bit easier to extract data from a QIX hypercube. It also contains a brush helper that can be used to find appropriate selections in the underlying data engine.
npm install picasso-plugin-q
import picasso from 'picasso.js';
import picassoQ from 'picasso-plugin-q';
picasso.use(picassoQ); // register
q
datasetThis dataset type understands the QIX hypercube format and its internals, making it a bit easier to traverse and extract values from an otherwise complex structure.
const ds = picasso.data('q')({
key: 'qHyperCube', // path to the hypercube from the layout
data: layout.qHyperCube,
});
Dimensions, measures, attribute expressions and attribute dimensions are all recognized as fields and can be found using either the path or the title of the field:
const f = ds.field('Sales');
const ff = ds.field('qDimensionInfo/1/qAttrDimInfo/2');
Assuming we have a hypercube containing dimensions Year and Month, a measure Sales and an attribute expression on the first dimension containing color values:
// hypercube stub
{
qDimensionInfo: [
{ qFallbackTitle: 'Year', qAttrDimInfo: [{ qFallbackTitle: 'color' }, /* ... */], /* ... */ },
{ qFallbackTitle: 'Month', /* ... */ }
],
qMeasureInfo: [
{ qFallbackTitle: '# products', /* ... */ }
]
}
In a straight hypercube the qMatrix
might look like this:
[
[
{ "qText": "2011", "qNum": 2011, "qElemNumber": 0, "qState": "O", "qAttrExps": { "qValues": [{"qText": "red", "qNum": "NaN" }] } },
{ "qText": "Jan", "qNum": 1, "qElemNumber": 0, "qState": "S", "qAttrDims": { "qValues": [{ "qText": "Jan", "qElemNo": 0 }] } },
{ "qText": "61", "qNum": 61, "qElemNumber": 0, "qState": "L" }
],
[
{ "qText": "2011", "qNum": 2011, "qElemNumber": 0, "qState": "O", "qAttrExps": { "qValues": [{"qText": "blue", "qNum": "NaN" }] } },
{ "qText": "Feb", "qNum": 2, "qElemNumber": 1, "qState": "S", "qAttrDims": { "qValues": [{"qText": "Feb", "qElemNo": 1 }] } },
{ "qText": "62", "qNum": 62, "qElemNumber": 0, "qState": "L" }
],
[
{ "qText": "2012","qNum": 2012, "qElemNumber": 1, "qState": "O", "qAttrExps": { "qValues": [{"qText": "red", "qNum": "NaN" }] } },
{ "qText": "Jan", "qNum": 1, "qElemNumber": 0, "qState": "S", "qAttrDims": { "qValues": [{"qText": "Jan", "qElemNo": 0}] } },
{ "qText": "88", "qNum": 88, "qElemNumber": 0, "qState": "L" }
],
[
{ "qText": "2012", "qNum": 2012, "qElemNumber": 1, "qState": "O", "qAttrExps": { "qValues": [{"qText": "blue", "qNum": "NaN" }] } },
{ "qText": "Feb", "qNum": 2, "qElemNumber": 1, "qState": "S", "qAttrDims": { "qValues": [{"qText": "Feb","qElemNo": 1}] } },
{ "qText": "76", "qNum": 76, "qElemNumber": 0, "qState": "L" }
]
We can extract the unique Month values using:
ds.extract({
field: 'Month',
trackBy: (v) => v.qElemNumber,
});
// output
[
{ value: 0, label: 'Jan', source: { key: 'qHyperCube', field: 'qDimensionInfo/1' } },
{ value: 1, label: 'Feb', source: { key: 'qHyperCube', field: 'qDimensionInfo/1' } },
];
and attach aggregated properties on each item using props
:
ds.extract({
field: 'Month',
trackBy: v => v.qElemNumber
props: {
years: { field: 'Year', value: v => v.qText, reduce: values => values.join(' - ') },
color: { field: 'color', value: v => v.qText },
products: { field: '# products', reduce: 'sum' }
}
});
// output
[
{
value: 0, label: 'Jan', source: { key: 'qHyperCube', field: 'qDimensionInfo/1' },
years: { value: '2011 - 2012', source: { key: 'qHyperCube', field: 'qDimensionInfo/0' } }
color: { value: 'red', source: { key: 'qHyperCube', field: 'qDimensionInfo/0/qAttrExprInfo/0' } }
products: { value: 149, source: { key: 'qHyperCube', field: 'qMeasureInfo/0' } }
},
{
value: 1, label: 'Feb', source: { key: 'qHyperCube', field: 'qDimensionInfo/1' },
years: { value: '2011 - 2012', source: { key: 'qHyperCube', field: 'qDimensionInfo/0' } }
color: { value: 'blue', source: { key: 'qHyperCube', field: 'qDimensionInfo/0/qAttrExprInfo/0' } }
products: { value: 138, source: { key: 'qHyperCube', field: 'qMeasureInfo/0' } }
}
]
The default value
accessor for a field depends on the field type and the qMode
property of the hypercube:
cell => cell.qNum
or cell => cell.qValue
cell => cell.qElemNumber
or cell => cell.qElemNo
The default reduce
function is avg
for measures and first
for dimensions.
The QIX selections helper provides a mapping from brushed data points to suitable QIX selections.
Brushing dimension values is done by adding the value of qElemNumber
to the brush, and providing the path to the relevant dimension:
const b = chart.brush('selection');
b.addValue('qHyperCube/qDimensionInfo/2', 4);
b.addValue('qHyperCube/qDimensionInfo/2', 7);
Calling picassoQ.selections
with the above instance generates relevant QIX methods and parameters to apply a selection to:
const selection = picassoQ.selections(b)[0];
// {
// method: 'selectHyperCubeValues',
// params: [
// '/qHyperCubeDef', // path to hypercube to apply selection to
// 2, // dimension column
// [4, 7], // qElemNumbers
// false
// ]
// }
The selection can then be applied to a QIX model:
model[selection.method](...selection.params);
Brushing measure ranges:
const b = chart.brush('selection');
b.addRange('qHyperCube/qMeasureInfo/2', { min: 13, max: 35 });
const selection = picassoQ.selections(b)[0];
// {
// method: 'rangeSelectHyperCubeValues',
// params: ['/qHyperCubeDef', [
// {
// qMeasureIx: 2,
// qRange: { qMin: 13, qMax: 35, qMinIncEq: true, qMaxInclEq: true }
// }
// ]]
// }
Brushing dimension ranges:
const b = chart.brush('selection');
b.addRange('qHyperCube/qDimensionInfo/1', { min: 13, max: 35 });
const selection = picassoQ.selections(b)[0];
// {
// method: 'selectHyperCubeContinuousRange',
// params: ['/qHyperCubeDef', [
// {
// qDimIx: 1,
// qRange: { qMin: 13, qMax: 35, qMinIncEq: true, qMaxInclEq: false }
// }
// ]]
// }
Brushing by table row index and column:
const b = chart.brush('selection');
b.addValue('qHyperCube/qDimensionInfo/1', 10);
b.addValue('qHyperCube/qDimensionInfo/1', 13);
b.addValue('qHyperCube/qDimensionInfo/0', 11);
b.addValue('qHyperCube/qDimensionInfo/0', 17);
In the above case, rows 10
and 13
have been brushed on dimension 1
, and rows 11
and 17
on dimension 0
.
To extract the relevant information, byCells
is enabled:
const selection = picassoQ.selections(b, { byCells: true })[0];
// {
// method: 'selectHyperCubeCells',
// params: [
// '/qHyperCubeDef',
// [10, 13], // row indices in hypercube
// [1, 0] // column indices in hypercube
// ]
// }
Row indices are used from the first dimension that adds a value to a brush, qDimensionInfo/1
, in the case above.
To use values from another dimension, primarySource
should be set:
const selection = picassoQ.selections(b, {
byCells: true,
primarySource: 'qHyperCube/qDimensionInfo/0',
})[0];
// {
// method: 'selectHyperCubeCells',
// params: [
// '/qHyperCubeDef',
// [11, 17], // row indices in hypercube
// [1, 0] // column indices in hypercube
// ]
// }
Brush on attribute dimension values:
const b = chart.brush('selection');
b.addValue('qHyperCube/qDimensionInfo/2/qAttrDimInfo/3', 6);
b.addValue('qHyperCube/qDimensionInfo/2/qAttrDimInfo/3', 9);
const selection = picassoQ.selections(b)[0];
// {
// method: 'selectHyperCubeValues',
// params: [
// '/qHyperCubeDef/qDimensions/2/qAttributeDimensions/3', // path to hypercube to apply selection to
// 0, // dimension column in attribute dimension table
// [6, 9], // qElemNumbers
// false
// ]
// }
Brush on attribute expression range:
const b = chart.brush('selection');
b.addRange('qHyperCube/qMeasureInfo/1/qAttrExprInfo/2', { min: 11, max: 21 });
QIX selections on attribute expressions are similar to selections on measure ranges. In this case however, the index of the measure
is derived from the number of measures and attribute expressions that exist in the hypercube. Therefore, to calculate
the index, layout
containing the hypercube needs to be provided as a parameter:
const selection = picassoQ.selections(b, {}, layout)[0];
// {
// method: 'rangeSelectHyperCubeValues',
// params: ['/qHyperCubeDef', [
// {
// qMeasureIx: 7,
// qRange: { qMin: 11, qMax: 21, qMinIncEq: true, qMaxInclEq: true }
// }
// ]]
// }
Assuming a layout
of:
{
qHyperCube: {
qDimensionInfo: [
{ qAttrExprInfo: [{}] }
],
qMeasureInfo: [
{ qAttrExprInfo: [{}, {}] },
{ qAttrExprInfo: [{}, {}, {/* this is the one */ }] }
]
}
}
then qMeasureIx
is calculated as follows:
2
1
2
(from first measure)2
which results in 2 + 1 + 2 + 2 = 7
1.6.4 (2022-08-22)
Note: Version bump only for package picasso.js
FAQs
QIX plugin for picasso.js
The npm package picasso-plugin-q receives a total of 4,579 weekly downloads. As such, picasso-plugin-q popularity was classified as popular.
We found that picasso-plugin-q demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.