force categorical treatment of user annotations (#1663)

This commit is contained in:
Bruce Martin
2020-07-22 17:02:19 -07:00
committed by GitHub
parent 03bad04436
commit 83d572cde2
5 changed files with 39 additions and 20 deletions
+19 -10
View File
@@ -34,7 +34,8 @@ export const annotationCreateCategoryAction = (
throw new Error("name collision on annotation category create"); throw new Error("name collision on annotation category create");
let initialValue; let initialValue;
let categories; let newSchema;
let ctor;
if (categoryToDuplicate) { if (categoryToDuplicate) {
/* if we are duplicating a category, retrieve it */ /* if we are duplicating a category, retrieve it */
const catDupSchema = schema.annotations.obsByName[categoryToDuplicate]; const catDupSchema = schema.annotations.obsByName[categoryToDuplicate];
@@ -47,25 +48,33 @@ export const annotationCreateCategoryAction = (
.fetch("obs", categoryToDuplicate); .fetch("obs", categoryToDuplicate);
const col = catToDupDf.col(categoryToDuplicate); const col = catToDupDf.col(categoryToDuplicate);
initialValue = col.asArray(); initialValue = col.asArray();
({ categories } = col.summarize()); const { categories } = col.summarizeCategorical();
// all user-created annotations must have the unassigned category // all user-created annotations must have the unassigned category
if (!categories.includes(globals.unassignedCategoryLabel)) { if (!categories.includes(globals.unassignedCategoryLabel)) {
categories.push(globals.unassignedCategoryLabel); categories.push(globals.unassignedCategoryLabel);
} }
ctor = initialValue.constructor;
newSchema = {
...catDupSchema,
name: newCategoryName,
categories,
writable: true,
};
} else { } else {
/* else assign to the standard default value */ /* else assign to the standard default value */
initialValue = globals.unassignedCategoryLabel; initialValue = globals.unassignedCategoryLabel;
categories = [globals.unassignedCategoryLabel]; ctor = Array;
newSchema = {
name: newCategoryName,
type: "categorical",
categories: [globals.unassignedCategoryLabel],
writable: true,
};
} }
const obsCrossfilter = prevObsCrossfilter.addObsColumn( const obsCrossfilter = prevObsCrossfilter.addObsColumn(
{ newSchema,
name: newCategoryName, ctor,
type: "categorical",
categories,
writable: true,
},
Array,
initialValue initialValue
); );
+6 -8
View File
@@ -90,10 +90,10 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
If an array, it must be of same size as nObs and same type as Ctor If an array, it must be of same size as nObs and same type as Ctor
*/ */
colSchema.writable = true; colSchema.writable = true;
const col = colSchema.name; const colName = colSchema.name;
if ( if (
_getColumnSchema(this.schema, "obs", col) || _getColumnSchema(this.schema, "obs", colName) ||
this._cache.obs.hasCol(col) this._cache.obs.hasCol(colName)
) { ) {
throw new Error("column already exists"); throw new Error("column already exists");
} }
@@ -109,11 +109,9 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
} else { } else {
data = new Ctor(this.nObs).fill(value); data = new Ctor(this.nObs).fill(value);
} }
o._cache.obs = this._cache.obs.withCol(col, data); o._cache.obs = this._cache.obs.withCol(colName, data);
o.schema = addObsAnnoColumn(this.schema, col, { _normalizeCategoricalSchema(colSchema, o._cache.obs.col(colName));
...colSchema, o.schema = addObsAnnoColumn(this.schema, colName, colSchema);
writable: true,
});
return o; return o;
} }
+7 -1
View File
@@ -66,7 +66,12 @@ export function _isContinuousType(schema) {
export function _normalizeCategoricalSchema(colSchema, col) { export function _normalizeCategoricalSchema(colSchema, col) {
const { type, writable } = colSchema; const { type, writable } = colSchema;
if (type === "string" || type === "boolean" || type === "categorical") { if (
type === "string" ||
type === "boolean" ||
type === "categorical" ||
writable
) {
const categorySet = new Set( const categorySet = new Set(
col.summarizeCategorical().categories.concat(colSchema.categories ?? []) col.summarizeCategorical().categories.concat(colSchema.categories ?? [])
); );
@@ -79,4 +84,5 @@ export function _normalizeCategoricalSchema(colSchema, col) {
if (colSchema.categories) { if (colSchema.categories) {
colSchema.categories = catLabelSort(writable, colSchema.categories); colSchema.categories = catLabelSort(writable, colSchema.categories);
} }
return colSchema;
} }
@@ -16,6 +16,7 @@ class Continuous extends React.PureComponent {
const allContinuousNames = schema.annotations.obs.columns const allContinuousNames = schema.annotations.obs.columns
.filter((col) => col.type === "int32" || col.type === "float32") .filter((col) => col.type === "int32" || col.type === "float32")
.filter((col) => col.name !== obsIndex) .filter((col) => col.name !== obsIndex)
.filter((col) => !col.writable) // skip user annotations - they will be treated as categorical
.map((col) => col.name); .map((col) => col.name);
return ( return (
@@ -64,7 +64,12 @@ function topNCategories(colSchema, summary, N) {
export function isSelectableCategoryName(schema, name) { export function isSelectableCategoryName(schema, name) {
const { index } = schema.annotations.obs; const { index } = schema.annotations.obs;
return name && name !== index && isCategoricalAnnotation(schema, name); const colSchema = schema.annotations.obsByName[name];
return (
name &&
name !== index &&
(isCategoricalAnnotation(schema, name) || colSchema.writable)
);
} }
export function selectableCategoryNames(schema, names) { export function selectableCategoryNames(schema, names) {