Introduction to formulas
Formulas are a special language used on Conjointly to perform tasks such as:
- Adding calculated variable in your experiment,
- Calculating revenue or gross profit in the simulator,
- Weighting respondents,
- Piping answers from previous questions into later questions
- Redirecting respondents.
The language is inspired by R. While knowledge of R is not required, those familiar with R will find it easy to use formulas on Conjointly. One key difference is a much more relaxed approach to data types. For example, 1 + "2" is not allowed in R, but it is allowed on Conjointly and returns 3.
You can test and debug your formulas using Interactive debugger in the preview mode. The debugger can also be accessed by adding ?debugger=on to your survey URL (see example).
There are only four data types in formulas:
- Numbers (e.g.
1,2.5,-3.14), which are always treated as double-precision floating point numbers (“numeric” type in R). However, for comparisons, precision is limited to six decimal places. - Strings (e.g.
"hello","world"). Some operations and functions are case-sensitive (e.g. string comparison), while others are not (e.g. substring search). - Booleans (i.e.
TRUEorFALSE, which can be spelt in any case likeTrUeortrue). - Undefined, which is a special value that is returned when a variable is not defined. When formulas are used in piping inside the survey, an undefined value will be shown as 🤷 (which makes it easy to spot an error).
Like in R, scalars (i.e. single values) and vectors (i.e. lists of values) are not distinguished as separate data types. If a vector is supplied where a scalar is expected, the first element of the vector is used.
Algebraic functions
Algebraic functions perform basic mathematical operations. They always expect numbers as operands and return a number.
| Operation | Syntax | Example usage | Result |
|---|---|---|---|
| Addition | x+y | 20+15 | 35 |
| Subtraction | x-y | 10-3 | 7 |
| Multiplication | x*y | 7*3 | 21 |
| Division | x/y | 10/2 | 5 |
| Exponential | x^y | 4^2 | 16 |
| Modulo | x%%y | 10%%3 | 1 |
Max and min functions
The max and min functions return the minimum or maximum value from a set of arguments. They always return a number. All arguments are taken to be numbers or vectors of numbers.
| Function | Description | Example usage | Result |
|---|---|---|---|
max(item1, item2, [item3], ...) | Returns the highest value among the provided arguments | max(3, -9, 7.5) | 7.5 |
min(item1, item2, [item3], ...) | Returns the lowest value among the provided arguments | min(3, -9, 7.5) | -9 |
These functions require at least 1 argument and at maximum 500 arguments. If more are provided, only the first 500 are considered.
Example usages of min and max functions are provided below:
| Situation | Formula | Result | Comment |
|---|---|---|---|
| Arguments are numbers and string | max(3, -9, "25", 8) | 25 | because string "25" is converted into number 25 |
| Arguments are Boolean values | min(TRUE, FALSE) | 0 | because TRUE is forced into 1, FALSE is forced into 0 |
| Arguments are non-numeric strings | max("cat", "", "4") | 4 | because "cat" and "" are forced into 0, "4" is converted into 4 |
| Arguments include vector | max([1, 2, 30, -5], "10", 7) | 30 | |
| Arguments are vector with Boolean | min([FALSE, 2, 3], 10, TRUE) | 0 | because the first element of vector FALSE is forced into 0 |
| Argument includes function | max(badFunction(234), -5) | undefined | because badFunction returns undefined |
| Only one argument | max(-5) | -5 | |
| Only one string argument | max("male") | 0 |
Rounding functions
Rounding functions are used to adjust numerical values to a desired precision. All arguments are taken to be numbers.
| Function | Returns | Description | Example usage | Result |
|---|---|---|---|---|
round(value) | Number | Returns the value of a number rounded to the nearest integer. | round(1.2345) | 1 (number) |
ceil(value) | Number | Returns the smallest integer greater than or equal to a given number. | ceil(1.2345) | 2 (number) |
floor(value) | Number | Returns the largest integer less than or equal to a given number. | floor(1.2345) | 1 (number) |
toFixed(value, fractionDigits) | String | Converts a number to a string, retaining the specified number of decimals indicated by the optional parameter fractionDigits, which must be an integer from 0 to 20. If fractionDigits is not given or is misspecified, it is assumed to be 0. | toFixed(1.2345, 2) | 1.23 (string) |
Example usages are provided below.
| Situation | Formula | Result | Comment |
|---|---|---|---|
The GET variable gender is male | round(GETvariable("gender")) | 0 | because GETvariable("gender") is forced into 0 |
round("female") | 0 | because "female" is forced into 0 | |
The GET variable age is 20.02323212424 | toFixed(GETvariable("age")) | "20" | because age is converted into the number 20.02323212424 |
The GET variable age is 20.02323212424 | toFixed(GETvariable("age"),40) | "20" | because fractionDigits is misspecified and therefore assumed to be 0 |
The GET variable age is 20.02323212424 | toFixed(GETvariable("age"),-1.33) | "20" | because fractionDigits is misspecified and therefore assumed to be 0 |
The GET variable freq is -23.31 | ceil(GETvariable("freq")) | -23 | because ceil rounds up |
Comparison operators
Comparison operators are used to compare two variables. Comparisons operators always return TRUE or FALSE.
| Operation | Syntax | Example usage | Result |
|---|---|---|---|
| Equals | x==y | 5==10 | FALSE |
| Not equals | x!=y | 10!=3 | TRUE |
| Greater than | x>y | 5>19 | FALSE |
| Equal to or greater than | x>=y | 4>=4 | TRUE |
| Less than | x<y | 6<10 | TRUE |
| Equal to or less than | x<=y | 5<=1 | FALSE |
Precision of numbers for the purposes of comparisons is limited to six decimal places. It is achieved by checking if the absolute value of differences is greater than one millionth.
Comparison operators expect numbers as arguments, with one exception: the == and != operators can also be used to compare strings case-sensitively alphabetically if both arguments are strings. In all other cases, strings are converted to numbers according to the rules described above.
Example usages of comparison operators are provided below.
| Situation | Formula | Result | Comment |
|---|---|---|---|
The GET variable gender is male | GETvariable("gender") < 5 | TRUE | because "male" is forced into 0 |
The GET variable gender was not recorded | GETvariable("gender") < 5 | TRUE | because the empty string is forced into 0 |
The GET variable gender was not recorded | GETvariable("gender") == 0 | TRUE | because the empty string is forced into 0 |
The GET variable gender was not recorded | GETvariable("gender") != 0 | FALSE | because the empty string is forced into 0 |
The GET variable age is 20 | GETvariable("age") < 5 | FALSE | because GETvariable("age") is converted into the number 20 |
"3" < 5 | TRUE | because 3 is forced into the number 3 | |
"5" < 4 | FALSE | because 5 is forced into the number 5 | |
"5" == 5 | TRUE | because 5 is forced into the number 5 | |
"a"=="b" | FALSE | because both strings are compared alphabetically | |
"a"=="a" | TRUE | because both strings are compared alphabetically | |
"male" < " bird" | FALSE | because both strings are forced into the number 0 | |
"male" == "Male" | FALSE | because comparison is case-sensitive | |
"male" == "male" | TRUE | ||
"0" == "" | FALSE | because both values are strings and compared as such | |
0 == "" | TRUE | because the empty string is forced into 0 | |
1.0000001 == 1.0000002 | TRUE | because the absolute value of the difference is 0.0000001 < 0.000001 | |
1.0000001 == 1.0000005 | TRUE | because the absolute value of the difference is 0.0000004 < 0.000001 | |
1.0000001 == 1.0000012 | FALSE | because the absolute value of the difference is 0.0000011 > 0.000001 | |
"" == FALSE | TRUE | because both FALSE is forced into 0 and "" is forced into 0 | |
undefined == 0 | undefined | because undefined is contagious | |
undefined == "cat" | undefined | because undefined is contagious | |
undefined > TRUE | undefined | because undefined is contagious | |
undefined == undefined | undefined | because comparison operators expect numbers as arguments (unless both are strings) and undefined is contagious | |
"undefined" == undefined | undefined |
Boolean operators
Boolean operators are used to combine or exclude comparison operations. The operands are taken to be Booleans. The Boolean operators always return TRUE or FALSE.
| Operation | Syntax | Example usage | Result |
|---|---|---|---|
| Not | !x | !(10==5) | TRUE |
| And | x&y | TRUE&(10==5) | FALSE |
| Or | x|y | (10<4)|(10>2) | TRUE |
If a vector of numbers is supplied, operation is done on the first element of the vector only.
if and ifs functions
These functions return values depending on whether a condition is true or not:
if('condition', 'then', ['else'])checks condition and returns a value if it isTRUEor a different value if it isFALSE.ifs('condition1', 'then1', ['condition2'], ['then2'], ..., ['else'])returns a value that corresponds to the first true condition, or the “else” value if all specified conditions are false.
The conditions are taken to be Booleans. Conditions can be specified using comparison operators.
If the “else” value is not specified, it is taken to be undefined.
Example usages of if are provided below:
| Formula | Result | Comment |
|---|---|---|
if(3,3,5) | 3 (number) | because 3!=0 is TRUE |
if(0,3,5) | 5 (number) | because 0!=0 is FALSE |
if(FALSE,4,"male") | male (string) | |
if(FALSE,4) | undefined | |
if(TRUE,4,"male") | 4 (number) | |
if(answer(234),4,"male") (but question 234 is not part of this experiment) | undefined | because undefined values are contagious |
if(undefined,4,"male") | undefined | because undefined is contagious |
if(badFunction(234),4,"male") | undefined | because the function badFunction does not exist |
Example usages of ifs are provided below:
| Formula | Result | Comment |
|---|---|---|
ifs(3,3,5,3,0) | 3 (number) | because 3!=0 is TRUE |
ifs(0,3,5,4,-1) | 4 (number) | because 0!=0 is FALSE, but 5!=0 is TRUE |
ifs(FALSE,4,TRUE,"male") | "male" (string) | |
ifs(FALSE,4) | undefined | because the “else” condition is empty here |
ifs(TRUE,4) | 4 (number) | |
ifs(TRUE,4,"male") | 4 (number) | |
ifs(answer(234),4,"male") (but question 234 is not part of this experiment) | undefined | because undefined values are contagious |
ifs(badFunction(234),4,"male") | undefined | because the function badFunction does not exist |