Module:ChartPercentage
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules
This module provides a transform for chart renderings that selects columns, computes percentages from numerator/denominator pairs, removes denominator columns, and sets multilingual titles for the remaining columns.
See mw:Extension:Chart/Transforms for more documentation on this transform feature of the Charts system.
See similar module: Module:Chart absolute to relative
UsageUsage
process: Compute percentages and set multilingual titles.
To use as a chart transform:
"transform": {
"module": "ChartPercentRename",
"function": "process",
"args": {
"cols": "pop_2020,pop_total,année",
"numerator_cols": "pop_2020",
"denominator_cols": "pop_total",
"translations": "fr:Population 2020 (%),Année;en:Population 2020 (%),Year"
}
}
To invoke as a utility function from another module:
local ChartPercentRename = require( "Module:ChartPercentRename" )
local tab = mw.ext.data.get( "Some dataset.tab" )
-- Note this may mutate the original tab object
tab = ChartPercentRename.process(tab, {
["cols"] = "pop_2020,pop_total,année",
["numerator_cols"] = "pop_2020",
["denominator_cols"] = "pop_total",
["translations"] = "fr:Population 2020 (%),Année;en:Population 2020 (%),Year"
})
Arguments:
cols: comma-separated list of column names to keep initially, otherwise allnumerator_cols: comma-separated list of numerator column namesdenominator_cols: comma-separated list of corresponding denominator column namestranslations: semicolon-separated list of language blocks in the formatlang:Title1,Title2;lang2:TitleA,TitleB(setsf.title[lang]for each numerator column)
The transform computes each numerator as (numerator / denominator) * 100, rounded to two decimal places, replaces the numerator values, and removes the denominator columns from the dataset.
Code
-- Module:ChartPercentRename
-- Selects columns, computes percentages (num/den * 100), removes denominators,
-- and sets multilingual column titles.
-- Format: "fr:Title1,Title2;en:TitleA,TitleB"
-- See: https://www.mediawiki.org/wiki/Extension:Chart/Transforms
local TabUtils = require("Module:TabUtils")
local p = {}
-- Helper: Build column name → index map for fast lookup
local function colmap(tab)
local map = {}
for i, field in ipairs(tab.schema.fields) do
map[field.name] = i
end
return map
end
-- Main function: process data with percentages and multilingual titles
-- @param tab Data table
-- @param args Arguments:
-- • cols : columns to keep (optional)
-- • numerator_cols : columns to use as numerators
-- • denominator_cols : columns to use as denominators (will be removed)
-- • translations : "fr:Pop %,Year;en:Pop %,Year"
-- @return Modified table
function p.process(tab, args)
-- Step 1: Select columns if specified
if args.cols and args.cols ~= "" then
tab = TabUtils.select(tab, {cols = args.cols})
end
-- Step 2: Compute percentages if numerator/denominator provided
if args.numerator_cols and args.denominator_cols then
local map = colmap(tab)
local numerators = mw.text.split(args.numerator_cols, ",")
local denominators = mw.text.split(args.denominator_cols, ",")
-- Validate pair count
if #numerators ~= #denominators then
return tab -- skip if mismatched
end
-- Process each row
for _, row in ipairs(tab.data) do
for i, numCol in ipairs(numerators) do
local numCol = mw.text.trim(numCol)
local denCol = mw.text.trim(denominators[i])
local numIdx = map[numCol]
local denIdx = map[denCol]
if numIdx and denIdx then
local den = row[denIdx]
local num = row[numIdx]
if den and den ~= 0 and num then
local value = (num / den) * 100
row[numIdx] = math.floor(value * 100 + 0.5) / 100 -- round to 2 decimals
end
end
end
end
-- Step 3: Remove denominator columns
local denSet = {}
for _, col in ipairs(denominators) do
denSet[mw.text.trim(col)] = true
end
local newFields = {}
local fieldMap = {} -- old index → new index
for i, f in ipairs(tab.schema.fields) do
if not denSet[f.name] then
table.insert(newFields, f)
fieldMap[i] = #newFields
end
end
-- Rebuild data rows
local newData = {}
for _, row in ipairs(tab.data) do
local newRow = {}
for oldIdx, newIdx in pairs(fieldMap) do
newRow[newIdx] = row[oldIdx]
end
table.insert(newData, newRow)
end
tab.schema.fields = newFields
tab.data = newData
end
-- Step 4: Apply multilingual titles
if args.translations and args.translations ~= "" then
local langEntries = mw.text.split(args.translations, ";")
local targetCols = mw.text.split(args.numerator_cols or args.cols or "", ",")
-- Skip if no columns to rename
if #targetCols == 0 or (targetCols[1] == "" and #targetCols == 1) then
return tab
end
for _, entry in ipairs(langEntries) do
local lang, titlesStr = entry:match("^%s*([%a%-]+)%s*:(.*)$")
if lang and titlesStr then
local titles = mw.text.split(titlesStr, ",")
for i, colName in ipairs(targetCols) do
colName = mw.text.trim(colName)
local title = mw.text.trim(titles[i] or "")
if title ~= "" then
for _, field in ipairs(tab.schema.fields) do
if field.name == colName then
field.title = field.title or {}
field.title[lang] = title
break
end
end
end
end
end
end
end
return tab
end
return p