Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(table): optimize tableForSelection calls #3486

Merged
merged 3 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/@ourworldindata/core-table/src/OwidTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,51 @@ export class OwidTable extends CoreTable<OwidRow, OwidColumnDef> {
)
}

// Drop _all rows_ for an entity if there is any column that has no valid values for that entity.
dropEntitiesThatHaveNoDataInSomeColumn(columnSlugs: ColumnSlug[]): this {
const indexesByEntityName = this.rowIndicesByEntityName

// Iterate over all entities, and remove them as we go if they have no data in some column
const entityNamesToKeep = new Set(indexesByEntityName.keys())

for (let i = 0; i <= columnSlugs.length; i++) {
const slug = columnSlugs[i]
const col = this.get(slug)

// Optimization, if there are no error values in this column, we can skip this column
if (col.numErrorValues === 0) continue

for (const entityName of entityNamesToKeep) {
const indicesForEntityName = indexesByEntityName.get(entityName)
if (!indicesForEntityName)
throw new Error("Unexpected: entity not found in index map")

// Optimization: We don't care about the number of valid/error values, we just need
// to know if there is at least one valid value
const hasSomeValidValueForEntityInCol =
indicesForEntityName.some((index) =>
isNotErrorValue(col.valuesIncludingErrorValues[index])
)

// Optimization: If we find a column that this entity has no data in we can remove
// it immediately, no need to iterate over other columns also
if (!hasSomeValidValueForEntityInCol)
entityNamesToKeep.delete(entityName)
}
}

// const entityNamesToDrop = differenceOfSets([
marcelgerber marked this conversation as resolved.
Show resolved Hide resolved
// this.availableEntityNameSet,
// entityNamesToKeep,
// ])

return this.columnFilter(
this.entityNameSlug,
(rowEntityName) => entityNamesToKeep.has(rowEntityName as string),
`Drop entities that have no data in some column: ${columnSlugs.join(", ")}`
)
}

private sumsByTime(columnSlug: ColumnSlug): Map<number, number> {
const timeValues = this.timeColumn.values
const values = this.get(columnSlug).values as number[]
Expand Down
11 changes: 3 additions & 8 deletions packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import {
OwidTable,
CoreColumn,
isNotErrorValue,
BlankOwidTable,
} from "@ourworldindata/core-table"
import {
autoDetectSeriesStrategy,
Expand Down Expand Up @@ -312,13 +311,9 @@ export class LineChart
this.yColumnSlugs
)

const groupedByEntity = table
.groupBy(table.entityNameColumn.slug)
.filter((t) => !t.hasAnyColumnNoValidValue(this.yColumnSlugs))

if (groupedByEntity.length === 0) return BlankOwidTable()

table = groupedByEntity[0].concat(groupedByEntity.slice(1))
table = table.dropEntitiesThatHaveNoDataInSomeColumn(
this.yColumnSlugs
)
}

return table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
import {
OwidTable,
CoreColumn,
BlankOwidTable,
isNotErrorValueOrEmptyCell,
} from "@ourworldindata/core-table"
import {
Expand Down Expand Up @@ -103,15 +102,7 @@ export class AbstractStackedChart
})
}

const groupedByEntity = table
.groupBy(table.entityNameColumn.slug)
.map((t: OwidTable) =>
t.dropRowsWithErrorValuesForAnyColumn(this.yColumnSlugs)
)

if (groupedByEntity.length === 0) return BlankOwidTable()

table = groupedByEntity[0].concat(groupedByEntity.slice(1))
Comment on lines -106 to -114
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out, this here never needed the groupBy call at all: dropRowsWithErrorValuesForAnyColumn is entirely row-based, and decides on a row-by-row basis.

table = table.dropRowsWithErrorValuesForAnyColumn(this.yColumnSlugs)
}

return table
Expand Down