Skip to main content

Attributes

This explains the attrbiutes file.

The attributes file contains JSON array of attributes.

[
{ "name": "att1", ... },
{ "name": "att2", ... },
]

Attribute

An attribute has the following structure:

// example attribute
{
"name": "brand",
"label": "Brand",
"ui": "select",
"allowAggregation": true,
"readOnly": true,
"levels": [
{ "value": 1, "label": "Brand 1" },
{ "value": 2, "label": "Brand 2" },
{ "value": 3, "label": "Brand 3" },
{ "value": 4, "label": "Brand 4" },
]
}

The following properties exist for attributes:

name

string (required): Uniquely identifies the attribute.

For style, it is recommended to be lowercase (camelcase or snake case) and contain no spaces. It may be helpful if the name is closely related to utility names.

type

string (optional): default is null

This tells the calculation how to use the attribute. If null, the attribute won't be used in calculations.

type: "categorical"

A categorical attribute consists of multiple discrete levels, each of which has its own utility. The calculation engine will pick the utility that corresponds with the selected level. It is most likely visualized with a dropdown box (ui = "select").

type: "interpolated"

An interpolated attribute consists of multiple levels, but the selected value can be between the level values. The calculation engine will interpolate between adjacent utilities as follows:

  • If the selected value exactly equals a level value, only the utility from that level is used.

  • Otherwise, the two levels nearest the selected value are determined (level1 and level2). A portion of level1 and level2 are used, according to a linear interpolation between them. In other words, if the selected value is very close to level1, it will use most of level1 and very little of level2 (e.g., 0.9 * level1 + 0.1 * level2). If the selected value is exactly in the middle, the calculation engine will use (0.5 * level1 + 0.5 * level2).

type: "slope"

A slope attribute consists of one or more ranges. Each range contains a set of levels. The ui will allow the user to enter an arbitrary value, but the calculation engine will use utilities from the product's range.

  • First, each product's range must be specified in the base case. If this attribute is named "price", the base case must include a value for "price.range".

  • Only the levels from the product's range are used (even if the selected price is outside the range).

  • Each level within the range has a "from" and "to" value.

    • If the selected value is less than the "from" value, the level's utility will not be used.
    • If the selected value is greater than or equal to "from" and less than "to", a fraction of the utility will be used. That fraction is a linear interpolation between the "from" and "to" values.
    • If the selected value is greater than or equal to the "to" value, the level's full utility will be used.

label

string (required): Display name for the attribute in the user interface.

ui

string (optional): Specifies the input visualizer that should be used to configure the attribute. If undefined or null, the attribute will not be shown in the ui.

ui: "range"

The range ui presents a text box with a [min-max] explanation to the right of it. A user can type an arbitrary value into text box.

Values outside the range are allowed (and used in calculation), but will cause an error indication near the input box.

Crazy values, e.g., "asdf" are also allowed but will cause an error indication. Rob needs to explain what value is used in the calculation when a crazy value is entered.

ui: "select"

A drop-down box will be presented so the user can select an option.

ui: "print"

Experiment. Just prints the raw value in a read only state. Not much work has gone into this yet.

allowAggregation

bool (optional); default is false

If true, the attribute will appear in a few places in the UI.

  • On the dashboard it will be allowed as a row grouping
  • On the visualization page it will be given its own chart

advanced

I think this is no longer used. It was just a way to designate an attribute as something the average user might not care to see. Advanced attributes were hidden by default until explicity made visible. (todo: find out if this is still used)

readOnly

bool (optional); default is false

If true, the user will see the attribute value in the specified ui, but not being able to change it.

min

double (optional)

Helps the user know the min tested value. Is not enforced. Might be used in calcs (todo: clarify).

max

double (optional)

Helps the user know the max tested value. Is not enforced. Might be used in calcs (todo: clarify).

format

d3Format string to describe how the value should be presented.

Ex: ".0f"

interval

I think this is not currently used. It could be used for a spinner button, where it defines how much to increment a value if a spinner button is clicked. It could also possibly define the step size to use for a level change report. But I don't think it's being used (todo: clarify).

levelChangeReport

bool (optional); default is false

If true, this attribute will be used in the level change report.

levels

An array of levels.

If attribute type equals "categorical", levels should specify value, util and label. (What is the purpose of value?). Util describes the column name in the utilities file to use in calculations.

Example:

[
{ "value": 1, "util": "sku1", "label": "SKU 1 description" },
{ "value": 2, "util": "sku2", "label": "SKU 2 description" },
{ "value": 3, "util": "sku3", "label": "SKU 3 description" },
{ "value": 4, "util": "sku4", "label": "SKU 4 description" },
]

If attribute type equals "interpolated", levels should specify index, value and util. (How are these used in calcs?).

Example:

"levels": [
{ "index": 1, "value": 3, "util": "position1" },
{ "index": 2, "value": 6, "util": "position2" },
{ "index": 3, "value": 20, "util": "position3" }
]

If attribute type equals "slope", levels should have a range, from, to and util.

Example:

[
{ "range": 1, "from": 0.29, "to": 0.39, "util": "price1" },
{ "range": 1, "from": 0.39, "to": 0.49, "util": "price2" },
{ "range": 1, "from": 0.49, "to": 0.59, "util": "price3" },
{ "range": 1, "from": 0.59, "to": 0.69, "util": "price4" },
{ "range": 1, "from": 0.69, "to": 0.79, "util": "price5" },
{ "range": 2, "from": 0.9, "to": 1, "util": "price8" },
{ "range": 2, "from": 1, "to": 1.15, "util": "price9" },
{ "range": 3, "from": 1.19, "to": 1.29, "util": "price12" },
{ "range": 3, "from": 1.29, "to": 1.39, "util": "price13" },
{ "range": 3, "from": 1.39, "to": 1.49, "util": "price14" },
{ "range": 4, "from": 1.79, "to": 1.99, "util": "price17" },
{ "range": 4, "from": 1.99, "to": 2.25, "util": "price18" },
{ "range": 4, "from": 2.25, "to": 2.49, "util": "price19" },
]

If attribute is not used in calculations, but a ui of "select" is specified, the levels array should value a value and label.

Example:

[
{ "value": 1, "label": "Brand A" },
{ "value": 2, "label": "Brand B" },
{ "value": 3, "label": "Brand C" },
{ "value": 4, "label": "Brand D" }
]