Module:ChartPercentage

Lua

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:

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
Category:Chart transform modules Category:Charts extension documentation Category:Modules in pre-alpha development