Expand description
Perspective supports expression columns, which are virtual columns
calculated as part of the crate::View, optionally using values from its
underlying crate::Table’s columns. Such expression columns are defined
in Perspective’s expression language, an extended version of
ExprTK, which is itself quite similar
(in design and features) to expressions in Excel.
§UI
Expression columns can be created in <perspective-viewer> by clicking the
“New Column” button at the bottom of the column list, or via the API by
adding the expression to the expressions config key when calling
viewer.restore().
By default, such expression columns are not “used”, and will appear above
the Table’s other deselected columns in the column list, with an
additional set of buttons for:
- Editing the column’s expression. Doing so will update the definitions of all usage of the expression column.
- Deleting the column. Clicking
Reset(or calling thereset()method) will not delete expressions unless theShiftkey is held (ortrueparameter supplied, respectively). This button only appears if the expression column i unused.
To use the column, just drag/select the column as you would a normal column, e.g. as a “Filter”, “Group By”, etc. Expression columns will recalculate whenever their dependent columns update.
§Perspective Extensions to ExprTK
ExprTK has its own excellent documentation which covers the core langauge in depth, which is an excellent place to start in learning the basics. In addition to these features, Perspective adds a few of its own custom extensions and syntax.
§Static Typing
In addition to float values which ExprTK supports natively, Perspective’s
expression language also supports Perspective’s other types date,
datetime, integer, boolean; as well as rudimentary type-checking,
which will report an error when the values/columns supplied as
arguments cannot be resolved to the expected type, e.g. length(x) expects
an argument x of type string and is not a valid expression for an x of
another type. Perspective supplies a set of cast functions for converting
between types where possible e.g. string(x) to cast a variable x to a
string.
§Expression Column Name
Expressions can be named by providing a comment as the first line of the
expression. This name will be used in the <perspective-viewer> UI when
referring to the column, but will also be used in the API when specifying
e.g. group_by or sort fields. When creating a new column via
<oerspective-viewer>’s expression editor, new columns will get a default
name (which you may delete or change):
// New Column 1Without such a comment, an expression will show up in the
<perspective-viewer> API and UI as itself (clipped to a reasonable length
for the latter).
§Referencing crate::Table Columns
Columns from the crate::Table can be referenced in an expression with
double quotes.
// Expected Sales ("Sales" * 10) + "Profit"§String Literals
In contrast to standard ExprTK, string literals are declared with single quotes:
// Profitable
if ("Profit" > 0) {
'Stonks'
} else {
'Not Stonks'
}§Extended Library
Perspective adds many of its own functions in addition to ExprTK’s
standard ones, including common functions for datetime and string types
such as substring(), bucket(), day_of_week(), etc. A full list of
available functions is available in the
Expression Columns API.
§Examples
§Casting
Just 2, as an integer (numeric literals currently default to float
unless cast).
integer(2)§Variables
// My Column Name
var incrementedBy200 := "Sales" + 200;
var half := incrementedBy200 / 2;
half// Complex Expression
var upperCustomer := upper("Customer Name");
var separator := concat(upperCustomer, ' | ');
var profitRatio := floor(percent_of("Profit", "Sales")); // Remove trailing decimal.
var combined := concat(separator, string(profitRatio));
var percentDisplay := concat(combined, '%');
percentDisplay§Conditionals
// Conditional
var priceAdjustmentDate := date(2016, 6, 18);
var finalPrice := "Sales" - "Discount";
var additionalModifier := 0;
if("Order Date" > priceAdjustmentDate) {
finalPrice -= 5;
additionalModifier -= 2;
}
else
finalPrice += 5;
finalPrice + additionalModifier§Perspective ExprTK Extensions
var ${1:x := 1}Declare a new local variableabs(${1:x})Absolute value of xavg(${1:x})Average of all inputsbucket(${1:x}, ${2:y})Bucket x by yceil(${1:x})Smallest integer >= xexp(${1:x})Natural exponent of x (e ^ x)floor(${1:x})Largest integer <= xfrac(${1:x})Fractional portion (after the decimal) of xiclamp(${1:x})Inverse clamp x within a rangeinrange(${1:x})Returns whether x is within a rangelog(${1:x})Natural log of xlog10(${1:x})Base 10 log of xlog1p(${1:x})Natural log of 1 + x where x is very smalllog2(${1:x})Base 2 log of xlogn(${1:x}, ${2:N})Base N log of x where N >= 0max(${1:x})Maximum value of all inputsmin(${1:x})Minimum value of all inputsmul(${1:x})Product of all inputspercent_of(${1:x})Percent y of xpow(${1:x}, ${2:y})x to the power of yroot(${1:x}, ${2:N})N-th root of x where N >= 0round(${1:x})Round x to the nearest integersgn(${1:x})Sign of x: -1, 1, or 0sqrt(${1:x})Square root of xsum(${1:x})Sum of all inputstrunc(${1:x})Integer portion of xacos(${1:x})Arc cosine of x in radiansacosh(${1:x})Inverse hyperbolic cosine of x in radiansasin(${1:x})Arc sine of x in radiansasinh(${1:x})Inverse hyperbolic sine of x in radiansatan(${1:x})Arc tangent of x in radiansatanh(${1:x})Inverse hyperbolic tangent of x in radianscos(${1:x})Cosine of xcosh(${1:x})Hyperbolic cosine of xcot(${1:x})Cotangent of xsin(${1:x})Sine of xsinc(${1:x})Sine cardinal of xsinh(${1:x})Hyperbolic sine of xtan(${1:x})Tangent of xtanh(${1:x})Hyperbolic tangent of xdeg2rad(${1:x})Convert x from degrees to radiansdeg2grad(${1:x})Convert x from degrees to gradiansrad2deg(${1:x})Convert x from radians to degreesgrad2deg(${1:x})Convert x from gradians to degreesconcat(${1:x}, ${2:y})Concatenate string columns and string literals, such as: concat(“State” ’, ’, “City”)order(${1:input column}, ${2:value}, ...)Generates a sort order for a string column based on the input order of the parameters, such as: order(“State”, ‘Texas’, ‘New York’)upper(${1:x})Uppercase of xlower(${1:x})Lowercase of xhour_of_day(${1:x})Return a datetime’s hour of the day as a stringmonth_of_year(${1:x})Return a datetime’s month of the year as a stringday_of_week(${1:x})Return a datetime’s day of week as a stringnow()The current datetime in local timetoday()The current date in local timeis_null(${1:x})Whether x is a null valueis_not_null(${1:x})Whether x is not a null valuenot(${1:x})not xtrueBoolean value truefalseBoolean value falseif (${1:condition}) {} else if (${2:condition}) {} else {}An if/else conditional, which evaluates a condition such as: if (“Sales” > 100) { true } else { false }for (${1:expression}) {}A for loop, which repeatedly evaluates an incrementing expression such as: var x := 0; var y := 1; for (x < 10; x += 1) { y := x + y }string(${1:x})Converts the given argument to a stringinteger(${1:x})Converts the given argument to a 32-bit integer. If the result over/under-flows, null is returnedfloat(${1:x})Converts the argument to a floatdate(${1:year}, ${1:month}, ${1:day})Given a year, month (1-12) and day, create a new datedatetime(${1:timestamp})Given a POSIX timestamp of milliseconds since epoch, create a new datetimeboolean(${1:x})Converts the given argument to a booleanrandom()Returns a random float between 0 and 1, inclusive.match(${1:string}, ${2:pattern})Returns True if any part of string matches pattern, and False otherwise.match_all(${1:string}, ${2:pattern})Returns True if the whole string matches pattern, and False otherwise.search(${1:string}, ${2:pattern})Returns the substring that matches the first capturing group in pattern, or null if there are no capturing groups in the pattern or if there are no matches.indexof(${1:string}, ${2:pattern}, ${3:output_vector})Writes into index 0 and 1 of output_vector the start and end indices of the substring that matches the first capturing group in pattern. Returns true if there is a match and output was written, or false if there are no capturing groups in the pattern, if there are no matches, or if the indices are invalid.substring(${1:string}, ${2:start_idx}, ${3:length})Returns a substring of string from start_idx with the given length. If length is not passed in, returns substring from start_idx to the end of the string. Returns null if the string or any indices are invalid.replace(${1:string}, ${2:pattern}, ${3:replacer})Replaces the first match of pattern in string with replacer, or return the original string if no replaces were made.replace_all(${1:string}, ${2:pattern}, ${3:replacer})Replaces all non-overlapping matches of pattern in string with replacer, or return the original string if no replaces were made.index()Looks up the index value of the current rowcol(${1:string})Looks up a column value by namevlookup(${1:string}, ${2:uint64})Looks up a value in another column by index