Module:TabUtils

Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules


This module provides utility functions for transforming tabular data sets in chart renderings.

See mw:Extension:Chart/Transforms for more documentation on this transform feature of the Charts system.

UsageUsage

select: subset a data set by column reference or comparison on row values

To use as a chart transform:

"transform": {
    "module": "TabUtils",
    "function": "select",
    "args": {
        "where": "date",
        "lte": "2000"
    }
}

To invoke as a utility function from another module:

local TabUtils = require( "Module:TabUtils" )
local tab = mw.ext.data.get( "Sample weekly temperature dataset.tab" )

-- Note this may mutate the original tab object
tab = TabUtils.select(tab, {
    ["where"] = "date",
    ["lte"] = "2000"
})


Arguments:

TodoTodo

Code

--
-- Experimental tabular data utilities for Chart transforms
-- See https://www.mediawiki.org/wiki/Extension:Chart/Transforms
--

local p = {}

--
-- build a map of column names -> indexes
-- @internal
--
function colmap( tab )
	local cols = {}
	for i, field in ipairs( tab.schema.fields ) do
		cols[field.name] = i
	end
	return cols
end

--
-- select: subset a data set by column reference or comparison on row values
--
-- Arguments:
-- * cols: comma-separated list of column names to keep, otherwise returns all cols
-- * where: column name to match on, otherwise returns all rows
-- * eq, ne, gt, lt, gte, lte: value to compare against to keep rows
-- * sort: column name to sort on
-- * order: sort order "asc" or "desc", defaults to "asc"
-- * decimate: keep only every nth row
--
function p.select( tab, args )
	local map = colmap( tab )

	if args.decimate then
		local n = tonumber( args.decimate )
		local newdata = {}
		for i, row in ipairs( tab.data ) do
			if i % n == 0 then
				table.insert( newdata, row )
			end
		end
		tab.data = newdata
	end

	local where = nil
	if args.where then
		local index = map[args.where]
		local field = tab.schema.fields[index]
		local function convert( val )
			if field.type == "number" then
				return tonumber( val )
			elseif field.type == "localized" then
				error( "todo: support localized fields" )
			else
				return val
			end
		end

		local comparator
		if args.eq then
			local against = convert( args.eq )
			comparator = function ( val )
				return val == against
			end
		elseif args.ne then
			local against = convert( args.ne )
			comparator = function ( val )
				return val ~= against
			end
		elseif args.gt then
			local against = convert( args.gt )
			comparator = function ( val )
				return val > against
			end
		elseif args.lt then
			local against = convert( args.lt )
			comparator = function ( val )
				return val < against
			end
		elseif args.gte then
			local against = convert( args.gte )
			comparator = function ( val )
				return val >= against
			end
		elseif args.lte then
			local against = convert( args.lte )
			comparator = function ( val )
				return val <= against
			end
		else
			error( "'where' must provide argument for 'eq', 'ne', 'gt', 'lt', 'gte', or 'lte'" )
		end

		local newdata = {}
		for _, row in ipairs( tab.data ) do
			if comparator( row[index] ) then
				table.insert( newdata, row )
			end
		end
		tab.data = newdata
	end

	if args.cols then
		local cols = mw.text.split( args.cols, ",", true )
		local newfields = {}
		local newdata = {}
		for i, name in ipairs( cols ) do
			table.insert( newfields, tab.schema.fields[map[name]] )
		end
		for n, row in ipairs( tab.data ) do
			local newrow = {}
			for i, name in ipairs( cols ) do
				table.insert( newrow, row[map[name]] )
			end
			table.insert( newdata, newrow )
		end
		tab.schema.fields = newfields
		tab.data = newdata
		map = colmap( tab )
	end
	
	if args.sort then
		local index = map[args.sort]
		if not index then
			error( "Missing sort column: " .. args.sort )
		end
		local comparator
		if args.order == "desc" then
			comparator = function ( a, b )
				return a[index] > b[index]
			end
		elseif args.order == "asc" or not args.order then
			comparator = function ( a, b )
				return a[index] < b[index]
			end
		else 
			error( "Invalid sort order: " .. args.order )
		end

		table.sort( tab.data, comparator )
	end

	return tab
end

--
-- Convenience function for testing/debugging, run from console
-- while editing this module.
--
function p.demo()
	local tab = mw.ext.data.get( "Sample_weekly_temperature_dataset.tab", "_" )
	mw.logObject( p.select( tab, {
		["cols"] = "month,high",
		["where"] = "high",
		["lt"] = "25"
	} ) )
end

function p.demoSort(order)
	local tab = mw.ext.data.get( "Sample_weekly_temperature_dataset.tab", "_" )
	mw.logObject( p.select( tab, {
		["cols"] = "month,high",
		["sort"] = "high",
		["order"] = order
	} ) )
end
return p
Category:Chart transform modules Category:Charts extension documentation Category:Modules in pre-alpha development