Skip to content

Commit

Permalink
StackedArea: Don't show interpolated values in tooltips (#3032)
Browse files Browse the repository at this point in the history
* 🐛 (tooltip) don't show interpolated values

* 💄 fix wording
  • Loading branch information
sophiamersmann authored Jan 3, 2024
1 parent 58a4a34 commit bf58f0a
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 8 deletions.
26 changes: 22 additions & 4 deletions packages/@ourworldindata/core-table/src/CoreTableColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import { Time, JsTypes, CoreValueType } from "./CoreTableConstants.js"
import { ColumnTypeNames, CoreColumnDef } from "./CoreColumnDef.js"
import { EntityName, OwidVariableRow } from "./OwidTableConstants.js" // todo: remove. Should not be on CoreTable
import { ErrorValue, ErrorValueTypes, isNotErrorValue } from "./ErrorValues.js"
import { getOriginalTimeColumnSlug } from "./OwidTableUtil.js"
import {
getOriginalTimeColumnSlug,
getOriginalValueColumnSlug,
} from "./OwidTableUtil.js"

interface ColumnSummary {
numErrorValues: number
Expand Down Expand Up @@ -394,6 +397,19 @@ export abstract class AbstractCoreColumn<JS_TYPE extends PrimitiveType> {
) as number[]
}

@imemo get originalValueColumnSlug(): string | undefined {
return getOriginalValueColumnSlug(this.table, this.slug)
}

@imemo get originalValues(): JS_TYPE[] {
const { originalValueColumnSlug } = this
if (!originalValueColumnSlug) return []
return this.table.getValuesAtIndices(
originalValueColumnSlug,
this.validRowIndices
) as JS_TYPE[]
}

/**
* True if the column has only 1 unique value. ErrorValues are counted as values, so
* something like [DivideByZeroError, 2, 2] would not be constant.
Expand Down Expand Up @@ -476,15 +492,17 @@ export abstract class AbstractCoreColumn<JS_TYPE extends PrimitiveType> {
// todo: remove? Should not be on CoreTable
// assumes table is sorted by time
@imemo get owidRows(): OwidVariableRow<JS_TYPE>[] {
const entities = this.allEntityNames
const times = this.originalTimes
const values = this.values
const entities = this.allEntityNames
const originalValues = this.originalValues
return range(0, times.length).map((index) => {
return {
return omitUndefinedValues({
entityName: entities[index],
time: times[index],
value: values[index],
}
originalValue: originalValues[index],
})
})
}

Expand Down
13 changes: 12 additions & 1 deletion packages/@ourworldindata/core-table/src/OwidTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
import { ErrorValue, ErrorValueTypes, isNotErrorValue } from "./ErrorValues.js"
import {
getOriginalTimeColumnSlug,
makeOriginalValueSlugFromColumnSlug,
makeOriginalTimeSlugFromColumnSlug,
timeColumnSlugFromColumnDef,
toPercentageColumnDef,
Expand Down Expand Up @@ -757,6 +758,14 @@ export class OwidTable extends CoreTable<OwidRow, OwidColumnDef> {
this.get(maybeTimeColumnSlug) ??
(this.get(OwidTableSlugs.time) as CoreColumn) // CovidTable does not have a day or year column so we need to use time.

const originalColumnSlug =
makeOriginalValueSlugFromColumnSlug(columnSlug)
const originalColumnDef = {
...columnDef,
slug: originalColumnSlug,
display: { includeInTable: false },
}

// todo: we can probably do this once early in the pipeline so we dont have to do it again since complete and sort can be expensive.
const withAllRows = this.complete([
this.entityNameSlug,
Expand All @@ -773,18 +782,20 @@ export class OwidTable extends CoreTable<OwidRow, OwidColumnDef> {

const columnStore = {
...withAllRows.columnStore,
[originalColumnSlug]: withAllRows.columnStore[columnSlug],
[columnSlug]: interpolationResult.values,
}

return this.transform(
columnStore,
[
...this.defs,
originalColumnDef,
{
...timeColumn.def,
},
],
`Interpolated values in column ${columnSlug} linearly`,
`Interpolated values in column ${columnSlug} linearly and appended column ${originalColumnSlug} with the original values`,
TransformType.UpdateColumnDefs
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ export interface OwidVariableRow<ValueType extends PrimitiveType> {
entityName: EntityName
time: Time
value: ValueType
originalValue?: ValueType
}
13 changes: 13 additions & 0 deletions packages/@ourworldindata/core-table/src/OwidTableUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export function makeOriginalTimeSlugFromColumnSlug(slug: ColumnSlug): string {
return `${slug}-originalTime`
}

export function makeOriginalValueSlugFromColumnSlug(slug: ColumnSlug): string {
return `${slug}-originalValue`
}

export function getOriginalTimeColumnSlug(
table: CoreTable,
slug: ColumnSlug
Expand All @@ -22,6 +26,15 @@ export function getOriginalTimeColumnSlug(
return table.timeColumn.slug
}

export function getOriginalValueColumnSlug(
table: CoreTable,
slug: ColumnSlug
): ColumnSlug | undefined {
const originalValueSlug = makeOriginalValueSlugFromColumnSlug(slug)
if (table.has(originalValueSlug)) return originalValueSlug
return undefined
}

export function toPercentageColumnDef(
columnDef: CoreColumnDef,
type = ColumnTypeNames.Percentage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
OwidTable,
CoreColumn,
BlankOwidTable,
isNotErrorValueOrEmptyCell,
} from "@ourworldindata/core-table"
import {
autoDetectSeriesStrategy,
Expand Down Expand Up @@ -385,6 +386,10 @@ export class AbstractStackedChart
time: row.time,
value: row.value,
valueOffset: 0,
interpolated:
this.shouldRunLinearInterpolation &&
isNotErrorValueOrEmptyCell(row.value) &&
!isNotErrorValueOrEmptyCell(row.originalValue),
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,58 @@ it("should drop missing values at start or end", () => {
expect(chart.series[1].points.length).toEqual(3)
})

it("should mark interpolated values as fake", () => {
const csv = `gdp,year,entityName
10,2000,france
0,2001,france
,2002,france
,2003,france
8,2005,france
,2006,france
2,2000,uk
3,2004,uk`
const table = new OwidTable(csv, [
{ slug: "gdp", type: ColumnTypeNames.Numeric },
{ slug: "year", type: ColumnTypeNames.Year },
])

const manager: ChartManager = {
table,
yColumnSlugs: ["gdp"],
selection: table.availableEntityNames,
}

const chart = new StackedAreaChart({ manager })

// indices are reversed because stacked charts reverse the stacking order
const pointsFrance = chart.series[1].points
const pointsUK = chart.series[0].points

// year 2000
expect(pointsFrance[0].interpolated).toBeFalsy()
expect(pointsFrance[0].fake).toBeFalsy()
expect(pointsUK[0].interpolated).toBeFalsy()
expect(pointsUK[0].fake).toBeFalsy()

// year = 2001
expect(pointsFrance[1].interpolated).toBeFalsy()
expect(pointsFrance[1].fake).toBeFalsy()
expect(pointsUK[1].interpolated).toBeTruthy()
expect(pointsUK[1].fake).toBeTruthy()

// year = 2004
expect(pointsFrance[2].interpolated).toBeTruthy()
expect(pointsFrance[2].fake).toBeTruthy()
expect(pointsUK[2].interpolated).toBeFalsy()
expect(pointsUK[2].fake).toBeFalsy()

// year = 2005
expect(pointsFrance[3].interpolated).toBeFalsy()
expect(pointsFrance[3].fake).toBeFalsy()
expect(pointsUK[3].interpolated).toBeFalsy()
expect(pointsUK[3].fake).toBeTruthy() // true since it's zero-filled
})

describe("externalLegendBins", () => {
const table = SynthesizeFruitTable({
timeRange: [2000, 2010],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
SampleColumnSlugs,
SynthesizeFruitTableWithStringValues,
SynthesizeGDPTable,
OwidTable,
ColumnTypeNames,
} from "@ourworldindata/core-table"
import { ChartManager } from "../chart/ChartManager"
import { SelectionArray } from "../selection/SelectionArray"
Expand Down Expand Up @@ -99,3 +101,41 @@ it("filters non-numeric values", () => {
)
).toBeTruthy()
})

it("should not mark any values as interpolated by default", () => {
const csv = `gdp,year,entityName
10,2000,france
0,2001,france
,2002,france
,2003,france
8,2005,france
,2006,france
2,2000,uk
3,2004,uk`
const table = new OwidTable(csv, [
{ slug: "gdp", type: ColumnTypeNames.Numeric },
{ slug: "year", type: ColumnTypeNames.Year },
])

const manager: ChartManager = {
table,
yColumnSlugs: ["gdp"],
selection: table.availableEntityNames,
}

const chart = new StackedBarChart({ manager })

// indices are reversed because stacked charts reverse the stacking order
const pointsFrance = chart.series[1].points
const pointsUK = chart.series[0].points

expect(pointsFrance[0].interpolated).toBeFalsy() // year = 2000
expect(pointsFrance[1].interpolated).toBeFalsy() // year = 2001
expect(pointsFrance[2].interpolated).toBeFalsy() // year = 2004
expect(pointsFrance[3].interpolated).toBeFalsy() // year = 2005

expect(pointsUK[0].interpolated).toBeFalsy() // year = 2000
expect(pointsUK[1].interpolated).toBeFalsy() // year = 2001
expect(pointsUK[2].interpolated).toBeFalsy() // year = 2004
expect(pointsUK[3].interpolated).toBeFalsy() // year = 2005
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface StackedPoint<PositionType extends StackedPointPositionType> {
value: number
valueOffset: number
time: number
interpolated?: boolean
fake?: boolean
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isArrayOfNumbers,
findGreatestCommonDivisorOfArray,
rollingMap,
omitUndefinedValues,
} from "@ourworldindata/utils"
import { StackedPointPositionType, StackedSeries } from "./StackedConstants"

Expand Down Expand Up @@ -64,13 +65,14 @@ export const withMissingValuesAsZeroes = <
const point = pointsByPosition[position]
const value = point?.value ?? 0
const time = point?.time ?? 0
return {
return omitUndefinedValues({
time,
position,
value,
valueOffset: 0,
fake: !point,
}
interpolated: point?.interpolated,
fake: !point || !!point.interpolated,
})
}),
}
})
Expand Down

0 comments on commit bf58f0a

Please sign in to comment.