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:
cols
: comma-separated list of column names to keep, otherwise returns all colswhere
: column name to match on, otherwise returns all rowseq
,ne
,gt
,lt
,gte
,lte
: value to compare against to keep rowssort
: column name to sort onorder
: sort order "asc
" or "desc
", defaults to "asc
"decimate
: keep only every nth row
TodoTodo
- support sort on multiple columns
- allow combining multiple gt/gte/lt/lte to allow ranges
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