Using Code to Represent Deeper Understanding

Oct. 31, 2026

I believe everyone should learn to code, because it allows for creating digital representations of the concepts that exist in our minds, and this allows for testing them for accuracy.

In the previous post, however, I showed that a coded representation of an idea can still be incredibly shallow. In that example, I showed that creating custom R code for the arithmetic mean could simply reflect a basic formulaic understanding: “Add up the parts, divide by the number of parts.”

A deeper level of understanding might involve realizing that the arithmetic mean is only one specific case of a set of general functions (e.g., geometric mean, harmonic mean, exponential mean)…

For all these functions, there is some operation (e.g., addition, multiplication, etc.) conducted on each value in the distribution, and then they are aggregated in a way that shows what an “equal contribution” of each would be.

For example, with the arithmetic mean, we are adding all the parts up. And the mean represents the single value that could replace every datapoint in the distribution while still summing to the same total as the original dataset. In this way, it’s a sort of “typical” score in an additive sense.

Suppose we had 3 scores: (1, 2, 3)…

  • The arithmetic mean is (1 + 2 + 3)/3 = 2.
  • The sum of the original dataset is 1 + 2 + 3 = 6
  • If we replaced each value with the mean (2), we would get precisely the same sum: 2 + 2 + 2 = 6

So this family of means (which is infinitely large) says: If you perform some operation on all the scores, then the mean will be the value that could replace each without changing the result of the operation. In other words, it shows what an “equal contribution” to the operation would be.

If this understanding is correct, then we should be able to write a function in R (or whatever programming language you prefer) that takes as its inputs (1) a set of scores and (2) the operation being performed, and then produces the resulting mean.

Let’s take 4 examples from this family of means…

  1. Arithmetic Mean = Σx/n (Operation: Addition)
  2. Geometric Mean = ⁿ√(Πx) (Operation: Multiplication)
  3. Harmonic Mean = n / Σ(1/x) (Operation: Addition of reciprocals)
  4. Quadratic Mean = √(Σx²/n) (Operation: Addition of squares)

Pulling this off successfully will require understanding a more general algorithm that binds all these (and all others in this family) together…

Geometric Mean

When each value in a set is multiplied, the arithmetic mean produces misleading results.

Example: Suppose you invest $100. If your return in the first year is +30% (i.e., 130% of original) and then your return in the second year is -30% (i.e., 70% of Year 1), then what is the average growth?

The arithmetic mean would seem to suggest 0% growth…

  • Σx/n = (1.30 + (.70)) / 2 = 1
    • (In this case, 1 means 100% of the original, so zero return overall)

But this is obviously incorrect if we check step-by-step…

  1. Year 1: $100 ⋅ 1.30 = $130
  2. Year 2: $130 ⋅ .70 = $91

We’ve lost $9…so clearly it’s not 0% growth.

The better solution is the geometric mean, which gives the typical multiplicative value…

  • ⁿ√(Πx) = ²√(1.30 ⋅ .70) ≈ .9539

We could replace each year’s return with this value and then get the correct ending value…

  1. Year 1: $100 ⋅ .9539 = $95.39
  2. Year 2: $95.39 ⋅ .9539 ≈ $91

So what’s the link between the arithmetic mean and the geometric mean?

Well, the real question is, “What’s the link between addition and multiplication?” And the answer is logarithms.

Specifically, log(a·b) = log(a) + log(b).

This means that if we first take the log of each value, we can proceed to use the ordinary arithmetic mean…

  • [ln(1.3) + ln(.7)] / 2 ≈ -0.0472

…then exponentiate (i.e., return to original units) to get the correct answer!

  • e^-0.0472 ≈ .9539

This is the same result we got when using the geometric mean formula.

Harmonic Mean

When working with reciprocals, the ordinary arithmetic mean again fails…

Example: If you drive at 50 mph on the way to your destination, then you drive back at 40 mph, then what was your average speed?

It may seem intuitively clear that the answer is 45 mph (the midpoint between 40 and 50). That value is the arithmetic mean… and it is WRONG.

That calculation would only work if you traveled for the same time units each time (e.g., 50 mph for 1 hour and then 40 mph for 1 hour).

But you were traveling 10 mph faster on the way to the destination, so it did NOT take you the same amount of time each way.

The real solution is the harmonic mean, which expresses the calculation in terms of the thing that is constant (i.e., distance to and from the destination). It’s as simple as flipping the miles / hour fraction…

  • (1/50 + 1/40) / 2 = .0225 hours per mile (hpm?)

…then taking the reciprocal to put it back in the original units…

  • 1 / .0225 ≈ 44.44 mph

Quadratic Mean

When working with square units (e.g., tiles on a bathroom floor), the naive arithmetic mean again fails…

Example: Suppose you’ve got 100 tiles to cover the floor of a room. Half are 6 inches across, and half are 8 across. What’s the average coverage of a given tile? And how much area will this cover?

It might be tempting to just think, “Well the average tile is 7 inches across (i.e., the arithmetic mean of 6 and 8), so that’s 7² = 49 sq in. Multiply times 100 tiles to get 4,900 sq in.”

Wrong, wrong, wrong…

The 6″ tiles have area 6² = 36 sq in, and the 8″ tiles have area 8² = 64 sq in. The arithmetic mean is (36 + 64) / 2 = 50 sq in. And that multiplied times 100 tiles is 5,000 sq in.

The quadratic mean, √(Σx²/n), handles this correctly. First, calculate the mean of the squares…

  • Σx²/n = [50⋅6² + 50⋅8²] / 100 = 50 sq in

…And then we can take the square root to convert back to original units (inches rather than square inches)…

  • √50 ≈ 7.07107 in

The average tile’s side length is 7″… but that does not accurately reflect the total tile area!

(By the way, I’ve never laid tile before, so I don’t know if being off by this factor would be a serious problem or not. But I personally would want the correct calculation.)

Quasi-Arithmetic Mean

In case you haven’t noticed it yet, the general process is…

  1. Transform the scores
  2. Calculate the arithmetic mean: Σx/n
  3. Transform the scores back into original units (inverse transformation)

The general form of this process is called the quasi-arithmetic mean…

  • Mean = f ⁻¹ [Σf(x) / n]

(This formula is nothing new. It’s just expressing that 3-step process above using mathematical notation.)

The transformation (f) could be taking the logarithm (for the geometric mean), taking the reciprocal (for the harmonic mean), taking the square (for the quadratic mean), and so on.

Transform, calculate the arithmetic mean, then transform back to original units.


Finally, we’re ready to write R code that takes any set of scores as input, along with a specification regarding the type of operation conducted, and that then spits out the resulting mean (whether arithmetic, geometric, harmonic, or whatever).

Here’s a version where each step is explicitly laid out…

> # Custom Mean Function
> generalized_mean=function(data,transformation,inverse) {
+ t_data=transformation(data) # Transformation
+ m_t_data=sum(t_data)/length(t_data) # Arithmetic mean calculation
+ result=inverse(m_t_data) # Inverse transformation
+ return(result)
+ }
> # Example 1: Arithmetic Mean
> x1=1:3
> generalized_mean(x1,identity,identity)
[1] 2
> # Example 2: Geometric Mean
> x2=c(1.3,.7)
> generalized_mean(x2,log,exp)
[1] 0.9539392
> # Example 3: Harmonic Mean
> x3=c(50,40)
> generalized_mean(x3,function(x) 1/x, function(x) 1/x)
[1] 44.44444
> # Example 4: Quadratic Mean
> x4=c(rep(6,50),rep(8,50))
> generalized_mean(x4,function(x) x^2, sqrt)
[1] 7.071068

All the results match our above calculations.

But we can make it WAY cleaner by collapsing the function into a single line representing f ⁻¹ [Σf(x) / n]…

> # Cleaner Generalized Mean Code
> gen_mean_clean=function(x,f,inv_f) {inv_f(mean(f(x)))}
> # Arithmetic
> gen_mean_clean(x1,identity,identity)
[1] 2
> # Geometric
> gen_mean_clean(x2,log,exp)
[1] 0.9539392
> # Harmonic
> gen_mean_clean(x3,function(x) 1/x, function(x) 1/x)
[1] 44.44444
> # Quadratic
> gen_mean_clean(x4,function(x) x^2, sqrt)
[1] 7.071068

If we check our work using the standard formulas, we get the exact same answers…

> # Checking our work...
> mean(x1)
[1] 2
> prod(x2)^(1/length(x2))
[1] 0.9539392
> length(x3)/sum(1/x3)
[1] 44.44444
> sqrt(sum(x4^2)/length(x4))
[1] 7.071068

In each of the 4 cases, the mean finds the value that would constitute an “equal contribution” to some result, namely…

  1. The sum of scores for the arithmetic mean
  2. The product of scores (i.e., sum of log-scores) for the geometric mean
  3. The sum of reciprocals (e.g., total time) for the harmonic mean
  4. The sum of squares (e.g., total area) for the quadratic mean

If we can follow all of that, then I think it’s safe to say we have fairly deep understanding of the concept of the mean.