mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-09 04:08:12 +08:00
finish group() implementation
This commit is contained in:
@@ -129,10 +129,7 @@ class BitArray {
|
|||||||
|
|
||||||
for (let w = 0; w < width; w++) {
|
for (let w = 0; w < width; w++) {
|
||||||
const bitmask = this.bitmask[w];
|
const bitmask = this.bitmask[w];
|
||||||
|
if (!bitmask || bitarray[w * length + index] !== bitmask) return false;
|
||||||
// Wha?? XXXX
|
|
||||||
// if (!bitmask || bitarray[w * length + index] !== bitmask) return false;
|
|
||||||
if (bitmask && bitarray[w * length + index] !== bitmask) return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,35 +149,49 @@ class ScalarDimension {
|
|||||||
_updateFilters(newFilter) {
|
_updateFilters(newFilter) {
|
||||||
newFilter = PositiveIntervals.canonicalize(newFilter);
|
newFilter = PositiveIntervals.canonicalize(newFilter);
|
||||||
|
|
||||||
|
// XXX removed optimization for now
|
||||||
// special case optimization - select all/none can bypass
|
// special case optimization - select all/none can bypass
|
||||||
// more complex work and just clobber everything.
|
// more complex work and just clobber everything.
|
||||||
//
|
//
|
||||||
if (newFilter.length === 0) {
|
// if (newFilter.length === 0) {
|
||||||
this.crossfilter.selection.deselectAll(this._id);
|
// this.crossfilter.selection.deselectAll(this._id);
|
||||||
} else if (
|
// } else if (
|
||||||
newFilter.length === 1 &&
|
// newFilter.length === 1 &&
|
||||||
newFilter[0][0] === 0 &&
|
// newFilter[0][0] === 0 &&
|
||||||
newFilter[0][1] == this.index.length
|
// newFilter[0][1] == this.index.length
|
||||||
) {
|
// ) {
|
||||||
this.crossfilter.selection.selectAll(this._id);
|
// this.crossfilter.selection.selectAll(this._id);
|
||||||
} else {
|
// } else {
|
||||||
const adds = PositiveIntervals.difference(newFilter, this.currentFilter);
|
|
||||||
const dels = PositiveIntervals.difference(this.currentFilter, newFilter);
|
const adds = PositiveIntervals.difference(newFilter, this.currentFilter);
|
||||||
dels.forEach(interval =>
|
const dels = PositiveIntervals.difference(this.currentFilter, newFilter);
|
||||||
this.crossfilter.selection.deselectIndirectFromRange(
|
|
||||||
this._id,
|
this.crossfilter.filters.forEach(f =>
|
||||||
this.index,
|
f.dim.groups.forEach(grp => grp._updateReduceDel(this, dels))
|
||||||
interval
|
);
|
||||||
)
|
|
||||||
);
|
dels.forEach(interval =>
|
||||||
adds.forEach(interval =>
|
this.crossfilter.selection.deselectIndirectFromRange(
|
||||||
this.crossfilter.selection.selectIndirectFromRange(
|
this._id,
|
||||||
this._id,
|
this.index,
|
||||||
this.index,
|
interval
|
||||||
interval
|
)
|
||||||
)
|
);
|
||||||
);
|
|
||||||
}
|
adds.forEach(interval =>
|
||||||
|
this.crossfilter.selection.selectIndirectFromRange(
|
||||||
|
this._id,
|
||||||
|
this.index,
|
||||||
|
interval
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.crossfilter.filters.forEach(f =>
|
||||||
|
f.dim.groups.forEach(grp => grp._updateReduceAdd(this, adds))
|
||||||
|
);
|
||||||
|
|
||||||
|
// XXX removed optimization
|
||||||
|
// }
|
||||||
|
|
||||||
this.currentFilter = newFilter;
|
this.currentFilter = newFilter;
|
||||||
}
|
}
|
||||||
@@ -393,13 +407,19 @@ class EnumDimension extends ScalarDimension {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group(groupValue) {
|
||||||
|
const grp = new EnumGroup(groupValue, this.value.constructor, this);
|
||||||
|
this.groups.push(grp);
|
||||||
|
return grp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Groups! Map/reduce
|
// Groups! Map/reduce
|
||||||
//
|
//
|
||||||
// XXX: potential optimizations not implemented
|
// XXX: potential optimizations not implemented
|
||||||
// - groupValue may be identity - could skip creating separate group map
|
// - groupValue may be identity - could skip creating separate group map
|
||||||
// - only works for scalars so far
|
// - only works for scalars so far (no enum)
|
||||||
//
|
//
|
||||||
class ScalarGroup {
|
class ScalarGroup {
|
||||||
constructor(groupValue, groupValueType, dimension) {
|
constructor(groupValue, groupValueType, dimension) {
|
||||||
@@ -433,10 +453,60 @@ class ScalarGroup {
|
|||||||
this._reduce();
|
this._reduce();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the group reduction incrementally
|
// Update the group reduction incrementally. Called when *any* dimension filter
|
||||||
_updateReduce(adds, dels) {
|
// changes. Guaranteed to be called AFTER the crossfilter is updated.
|
||||||
// XXX
|
//
|
||||||
throw new Error("unimplemented");
|
// Arguments:
|
||||||
|
// * dim: the dimension that is changing
|
||||||
|
// * intv: interval list of newly selected values on `dim` (adds)
|
||||||
|
//
|
||||||
|
_updateReduceAdd(dim, intv) {
|
||||||
|
// console.log("_updateReduceAdd", dim, intv);
|
||||||
|
|
||||||
|
// ignore updates to self, as we don't reduce inclusive of our filter
|
||||||
|
if (dim === this.dimension || intv.length === 0) return;
|
||||||
|
|
||||||
|
// Each item in the range was just added to `dim`. It was NOT previously
|
||||||
|
// selected - reduceAdd if it is now selected.
|
||||||
|
const selection = this.dimension.crossfilter.selection;
|
||||||
|
const data = this.dimension.crossfilter.data;
|
||||||
|
intv.forEach(rng => {
|
||||||
|
for (let r = rng[0]; r < rng[1]; r++) {
|
||||||
|
const i = dim.index[r];
|
||||||
|
if (selection.isSelectedIgnoringDim(i, this.dimension.id())) {
|
||||||
|
const group = this.groups[this.groupIndex[i]];
|
||||||
|
group.value = this.reduceAdd(group.value, data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the group reduction incrementally. Called when *any* dimension filter
|
||||||
|
// changes. Guaranteed to be called BEFORE the crossfilter is updated.
|
||||||
|
//
|
||||||
|
// Arguments:
|
||||||
|
// * dim: the dimension that is changing
|
||||||
|
// * intv: interval list of previously selected values on `dim` (dels)
|
||||||
|
//
|
||||||
|
_updateReduceDel(dim, intv) {
|
||||||
|
// console.log("_updateReduceDel", dim, intv);
|
||||||
|
|
||||||
|
// ignore updates to self, as we don't reduce inclusive of our filter
|
||||||
|
if (dim === this.dimension || intv.length === 0) return;
|
||||||
|
|
||||||
|
// Each item in the range will be remved from `dim`. reduceRemove if it
|
||||||
|
// is currently selected.
|
||||||
|
const selection = this.dimension.crossfilter.selection;
|
||||||
|
const data = this.dimension.crossfilter.data;
|
||||||
|
intv.forEach(rng => {
|
||||||
|
for (let r = rng[0]; r < rng[1]; r++) {
|
||||||
|
const i = dim.index[r];
|
||||||
|
if (selection.isSelectedIgnoringDim(i, this.dimension.id())) {
|
||||||
|
const group = this.groups[this.groupIndex[i]];
|
||||||
|
group.value = this.reduceRemove(group.value, data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce the entire data set, creating both the group index and the
|
// Reduce the entire data set, creating both the group index and the
|
||||||
@@ -457,7 +527,6 @@ class ScalarGroup {
|
|||||||
|
|
||||||
// Create groupIndex - index map between data record index and group index
|
// Create groupIndex - index map between data record index and group index
|
||||||
for (let i = 0, len = this.mapValue.length; i < len; i++) {
|
for (let i = 0, len = this.mapValue.length; i < len; i++) {
|
||||||
// this.groupIndex[index[i]] = groupIndexByName[this.mapValue[i]];
|
|
||||||
this.groupIndex[i] = groupIndexByName[this.mapValue[i]];
|
this.groupIndex[i] = groupIndexByName[this.mapValue[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,6 +579,24 @@ class ScalarGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EnumGroup extends ScalarGroup {
|
||||||
|
constructor(groupValue, groupValueType, dimension) {
|
||||||
|
super(groupValue, groupValueType, dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
const res = [];
|
||||||
|
this.groups.forEach(e =>
|
||||||
|
res.push({
|
||||||
|
key: this.dimension.enumIndex[e.key],
|
||||||
|
value: e.value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
res.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wrapper for backwards compat with crossfilter.
|
// Wrapper for backwards compat with crossfilter.
|
||||||
//
|
//
|
||||||
function crossfilter(data) {
|
function crossfilter(data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user