Repository: owid/owid-grapher (2025-07-10T16:55:07Z)

Issue:
Stacked charts with irregular time intervals interpolate incorrectly
## Description

Stacked area charts with irregular time points where some time series have values for all time points and other only for some interpolate without taking the spacing of the time points into account.

## Expected behaviour

When doing linear interpolation across a longer time span with irregularly placed time points, interpolation should work correctly.

## Steps to reproduce

Steps to reproduce the behavior:

1. Open the literacy rate chart in the admin: https://admin.owid.io/admin/charts/2617/edit
2. Change the chart type to stacked area
3. Select only Germany. Between 1750 and 1950, no real values exist.
4. Add France, which has additional values between 1900 and 1950. 
5. Observe that Germany shows a upwards sloping curve - the reason is that the 8 additional points are calculated as if they were spaced evenly (each incrementing by the same amount) even though they are not spaced evenly in time.

## Screenshots

<img width="807" height="570" alt="Image" src="https://github.com/user-attachments/assets/4c89bbb9-1b24-4752-a161-7af1da56d734" />



Discussion:

Patch:
diff --git a/packages/@ourworldindata/core-table/src/CoreTableUtils.ts b/packages/@ourworldindata/core-table/src/CoreTableUtils.ts
index 4ab07f8d1a4..93b349566c2 100644
--- a/packages/@ourworldindata/core-table/src/CoreTableUtils.ts
+++ b/packages/@ourworldindata/core-table/src/CoreTableUtils.ts
@@ -245,8 +245,8 @@ export function linearInterpolation(
             isNotErrorValueOrEmptyCell(prevValue) &&
             isNotErrorValueOrEmptyCell(nextValue)
         ) {
-            const distLeft = index - prevNonBlankIndex
-            const distRight = nextNonBlankIndex - index
+            const distLeft = timesAsc[index] - timesAsc[prevNonBlankIndex]
+            const distRight = timesAsc[nextNonBlankIndex] - timesAsc[index]
             value =
                 (prevValue * distRight + nextValue * distLeft) /
                 (distLeft + distRight)


Test patch:
diff --git a/packages/@ourworldindata/core-table/src/CoreTableUtils.test.ts b/packages/@ourworldindata/core-table/src/CoreTableUtils.test.ts
index d5128cef495..ce861ab5ef6 100755
--- a/packages/@ourworldindata/core-table/src/CoreTableUtils.test.ts
+++ b/packages/@ourworldindata/core-table/src/CoreTableUtils.test.ts
@@ -242,6 +242,14 @@ describe(linearInterpolation, () => {
             ErrorValueTypes.NoValueForInterpolation,
         ])
     })
+
+    it("interpolates with uneven time spacing", () => {
+        const values = [4, ErrorValueTypes.MissingValuePlaceholder, 1]
+        const timesAsc = [1900, 1919, 1920]
+        linearInterpolation(values, timesAsc, {})
+        // For 1919: should be 4 + (1-4) * (1919-1900)/(1920-1900) = 4 + (-3) * 19/20 = 4 - 2.85 = 1.15
+        expect(values).toEqual([4, 1.15, 1])
+    })
 })
 
 describe("immutable memoization", (): void => {