6 Indicators
Many programs must perform calculations that vary based on conditions of one kind or another. There are many different ways to accomplish this but one very powerful (and common) solution is to use a multiplicative variable that takes on the value zero when the condition isn’t satisfied and the value one when it is.
Motivation
Variables of this kind are common in many branches of mathematics and are called indicator variables.[1] They are often denoted using a lowercase delta (i.e., [latex]\delta[/latex])[2], often with a subscript to denote the condition (e.g., [latex]\delta_s[/latex] to indicate whether a person smokes or not). Indicator variables are then multiplied by other variables in more complex expressions.
For example, suppose you are writing a program to predict the birth weight of babies (in grams) from the gestation period (in weeks). You might theorize that the weight will be lower if the mother smokes during pregnancy. Ignoring whether the mother did or didn’t smoke, after collecting data from a (random or representative) sample of the population, you might determine a relationship like the following:
[latex]w = -2200 + 148.2 g[/latex]
where [latex]w[/latex] (the dependent variable) denotes the birth weight (in grams), and [latex]g[/latex] (an independent variable) denotes the gestation period (in weeks).[3] Accounting for the mother’s smoking behavior, you might determine that the birth weight was, on average, 238.6 grams lower when the mother smoked. You now need to decide how to account for this in your program.
Thinking About The Problem
What you want to do is lower [latex]w[/latex] when the mother smoked and leave [latex]w[/latex] unchanged when the mother didn’t smoke. Since there are only two possible states (i.e., the mother smoked or didn’t), you might be tempted to use a boolean
variable to keep track of this information. However, it turns out that it is better to use a discrete variable that is assigned either 0
or 1
, rather than one that takes on the values true
or false
. The reason is that you can use a numeric variable with the multiplication operator, and you can’t do so with a boolean
variable. In other words, a boolean
variable can’t be either the right-side or the left-side operand of the multiplication operator.
In particular, suppose you add another independent variable, [latex]\delta_s[/latex], and assign the value [latex]1[/latex] to [latex]\delta_s[/latex] if the mother smoked during pregnancy and assign the value [latex]0[/latex] to [latex]\delta_s[/latex] otherwise. Then, the equation for [latex]w[/latex] can be expressed succinctly as follows:
[latex]w = -2200 + 148.2 g - 238.6 \delta_s[/latex]
In this way, [latex]w[/latex] will be reduced by [latex]238.6[/latex] when [latex]\delta_s[/latex] is [latex]1[/latex] and will be left unchanged when [latex]\delta_s[/latex] is 0.
Note that you could define the indicator variable differently. Specifically, you could assign [latex]1[/latex] to [latex]\delta_s[/latex] if the mother didn’t smoke during pregnancy and assign [latex]0[/latex] to it otherwise. In this case, the equation for [latex]w[/latex] would be [latex]w = -2438.6 + 148.2 g + 238.6 \delta_s[/latex] (i.e., the constant would change and the sign of the last term would be reversed). The two indicators are called converses of each other.
The Pattern
In the simplest cases, all you need to do to use this pattern is to define an int
variable, assign 0
or 1
to it as appropriate, and then use it multiplicatively in an expression.[4] In more complicated cases, you may need multiple indicators, each with its own multiplier.
The converse indicator must take on the value 1
when the original indicator takes on the value 0
, and vice versa. This can be accomplished by subtracting the original indicator’s value from 1
and assigning the result to the converse indicator. In other words, the converse indicator is simply 1 minus the original indicator. Which is the “original” and which is the “converse” is completely arbitrary.
This idea can be expressed as the following pattern:
total = base + (indicator * adjustment);
with the converse indicator given by:
converse = 1 - indicator;
Examples
Returning to the birth weight example, the code for calculating the weight can be implemented as follows:
w = -2200.0 + (148.2 * g) - (238.6 * delta_s);
where w
contains the weight, g
contains the gestation period, and delta_s
contains the value 1
if the mother smoked and 0
otherwise.[5] Initializing g
to the average gestation period of 40.0
weeks, you could then use the statement to compare the birth weights for the two possible values of delta_s
. A delta_s
of 0
would result in a birth weight of 3728.0
while a delta_s
of 1
would result in a birth weight of 3489.4
.
As another example, suppose the fine associated with a first parking ticket is smaller than the fine associated with subsequent parking tickets (specifically, $10.00 for the first ticket and $45.00 for subsequent tickets). In this case, if you assign 0
to ticketedIndicator
when the person has no prior parking tickets and assign 1
to it otherwise, then you can write the statement to calculate the fine as follows.
baseFine = 10.00; repeatOffenderPenalty = 35.00; totalFine = baseFine + (ticketedIndicator * repeatOffenderPenalty);
As a final example, consider a rental car company that charges a base rate of $19.95 per day. There is a surcharge of $5.00 per day if multiple people drive the car, and a surcharge of $10.00 per day if any driver is under 25 years of age. If you assign 1
to multiIndicator
when there are multiple drivers and you assign 1
to youngIndicator
when there are any drivers under 25, then you can write the statement to calculate the rate as follows:
baseRate = 19.95; ageSurcharge = 10.00; multiSurcharge = 5.00; rate = baseRate + (multiIndicator * multiSurcharge) + (youngIndicator * ageSurcharge);
Some Warnings
The descriptions of the examples in this chapter may have led you to use a different solution than the one discussed above. While you may, in the end, prefer such a solution, you should think carefully about the advantages and disadvantages before you make any decisions.
Using if
Statements
You might be attempted to use a boolean
variable, if
statement, and the updating pattern from Chapter 1 rather than an indicator, and there are times when this is appropriate. However, in general, indicators are much less verbose.
For example, returning to the birth weight problem, if you assign true
to smoker
when the mother smoked during the pregnancy, then you can calculate the birth weight as follows:
w = -2200.0 + (148.2 * g); if (smoker) { w -= 238.6; }
This solution is much less concise than the solution that uses indicator variables. It also treats the continuous independent variable ([latex]g[/latex] in this case) and the discrete independent variable ([latex]\delta_s[/latex] in this case) differently, for no apparent reason.
This approach gets even more verbose as the number of discrete independent variables increases. For example, returning to the car rental problem, if you assign true
to areMultipleDrivers
when there are multiple drivers and you assign true
to areYoung
when there are any drivers under 25, then you can calculate the rental rate as follows:
baseRate = 19.95; ageSurcharge = 10.00; multiSurcharge = 5.00; rate = baseRate; if (areMultipleDrivers) { rate += multiSurcharge; } if (areYoung) { rate += ageSurcharge; }
When using indicator variables, each additional discrete independent variable only leads to an additional term in the single assignment statement. When using boolean
variables, each additional discrete independent variable leads to an additional if
statement.
Using Ternary Operators
You might also be tempted to use a boolean
variable, the ternary conditional operator, and the updating pattern from Chapter 1 rather than an indicator, but this is almost never appropriate. For example, returning to the parking ticket problem, if you assign the value true
to the boolean
variable hasBeenTicketed
when the person has a previous ticket, then you can calculate the total fine as follows:
baseFine = 10.00; repeatOffenderPenalty = 35.00; totalFine = hasBeenTicketed ? baseFine + repeatOffenderPenalty : baseFine;
Some people do prefer this solution to the one that uses an if
for stylistic reasons. That is, they think the ternary conditional operator is more concise. However, it is not more concise than the solution that uses an indicator variable, so it is hard to argue that it should be preferred.
Further, when the number of discrete independent variables increases this approach gets much less concise. Returning to the car rental problem you could calculate the rental rate as follows:
baseRate = 19.95; ageSurcharge = 10.00; multiSurcharge = 5.00; rate = areMultipleDrivers ? baseRate + multiSurcharge + (areYoung ? ageSurcharge : 0.0) : baseRate + (areYoung ? ageSurcharge : 0.0);
However, this statement is very verbose (and, many people think, difficult to understand).
You could, instead, calculate the rental rate as follows:
baseRate = 19.95; ageSurcharge = 10.00; multiSurcharge = 5.00; rate = baseRate; rate += areMultipleDrivers ? multiSurcharge : 0; rate += areYoung ? ageSurcharge : 0;
Again, while some people may prefer this solution to the one that uses if
statements because it is more concise, it is less concise than the solution that uses indicator variables.
- In statistics, they are sometimes called dummy variables. ↵
- Don't confuse this with an uppercase delta (i.e., Δ) that is often used to denote a change. ↵
- Don't be confused by the negative constant term. This model is only appropriate for longer gestation periods, in which cases the birth weight will be positive. ↵
- You can also use a
double
variable for the indicator if the indicator is to be multiplied by adouble
variable. However, this is a situation in which most people agree that it is appropriate to use anint
and type promotion. ↵ - In programming languages that allow them in identifiers, it is common to use the underscore character to indicate a subscript. ↵