Extension:Chart/Transforms/fr

Pour maximiser la capacité des éditeurs et éditrices à travailler avec des ensembles de données complexes, nous avons étendu Extension:Chart afin que les ensembles de données tabulaires puissent être transformés, modifiés, découpés ou générés à partir de code Lua, en utilisant le même moteur sous-jacent Extension:Scribunto qui pilote les modèles complexes avec la fonction d’analyseur syntaxique à 3$.

Chart format descriptions can include a transform which refers to a Lua Module: page and function, and passes some key-value parameters as an associative array.

L’ensemble de données source est traduit en objets Lua et transmis, avec les arguments, et le transformateur peut modifier ou remplacer l’ensemble des données tabulaires comme il le souhaite.

Le chargement de modules de données supplémentaires est enregistré à des fins d’invalidation du cache, de sorte qu’une modification du module référencé ou de toute page de données entraînera le réaffichage des pages avec le graphique.

Developer internals

See Extension:JsonConfig/Transforms for internals documentation or to add related support to another data type / output method.

Editor usage

Invocation

Actuellement, les transformations pour les graphiques doivent être configurées dans la page de description du format Data:.chart, comme suit :

{
    "license": "CC0-1.0",
    "version": 1,
    "type": "bar",
    "xAxis": {
        "title": { "en": "Day" }
    },
    "yAxis": {
        "title": { "en": "Temperature" }
    },
    "transform": {
        "module": "Weekly average temperature chart",
        "function": "convert_temps",
        "args": {
            "units": "C"
        }
    },
    "source": "Sample weekly temperature dataset.tab"
}

Les paramètres module et function sont équivalents aux deux premiers paramètres du {{#invoke:}} de Scribunto et renvoient à la page Module: avec le code source et la fonction spécifique à invoquer.

'“'Important:” comme les pages Data:, le code Lua Module: sera chargé et exécuté dans le contexte du magasin de données centralisé wiki (par exemple, Wikimedia Commons).

Cela signifie que, quel que soit le wiki sur lequel vous effectuez le rendu, vous exécuterez le code dans un endroit centralisé -- les modules peuvent donc être partagés entre les projets et les langues, et doivent tenir compte des bonnes pratiques en matière de réutilisation et de localisation.

Les arguments sont des paires au format texte nom-valeur. Si vous passez des nombres, assurez-vous de les convertir correctement si nécessaire.

Arguments can be overridden on the {{#chart:}} invocation by prefixing them with "arg" like arg:name=value; this allows using different parameters for each chart invocation with the same format, which we expect to be useful for a lot of cases with complex datasets.

<!-- Default Celsius -->
{{#chart:Weekly average temperature chart.chart}}
<!-- Override units to Fahrenheit -->
{{#chart:Weekly average temperature chart.chart|arg:units=F}}

Représentations de la courbe C non transformée par rapport à la courbe F transformée en utilisant le même échantillon de données de température :

Graphique en degrés Celsius -> Graphique en fahrenheit

Code layout

Vous pouvez nommer vos fonctions comme vous le souhaitez et inclure des fonctions connexes dans le même module ou même utiliser un module pour fournir à la fois des fonctions de modèle et des fonctions de transformation des données. Nous sommes flexibles !

Votre fonction de transformation doit prendre deux arguments : un objet de données tabulaires converti à partir de JSON, et une liste d'arguments provenant de l’invocation qui contiendra des paires clé-valeur. Retourner un objet de données tabulaires modifié (il peut s’agir de l’objet d’entrée après modification, ou d’un nouvel objet dans la même disposition).

local p = {}

local function celsius_to_fahrenheit(val)
	return val * 1.8 + 32
end

--
-- input data:
-- * tabular JSON strcuture with 1 label and 2 temp rows stored in C
--
-- arguments:
-- * units: "F" or "C"
--
function p.convert_temps(tab, args)
	if args.units == "C" then
		-- Stored data is in Celsius
		return tab
	elseif args.units == "F" then
		-- Have to convert if asked for Fahrenheit
		for _, row in ipairs(tab.data) do
			-- first column is month
			row[2] = celsius_to_fahrenheit(row[2])
			row[3] = celsius_to_fahrenheit(row[3])
		end
		return tab
	else
		error("Units must be either 'C' or 'F'")
	end
end

return p

Formatage des données

Voir Aide:Données tabulaires pour la documentation générale sur le format des données tabulaires. La transformation fonctionnera avec une transformation Lua des données, ce qui signifie que les objets et les tableaux basés sur 0 apparaitront comme des tables Lua avec des clés de type chaine de caractères ou des index numériques basés sur 1. Ils seront retransformés en JSON à la sortie pour être traités par le moteur de rendu.

Exemple d’un petit ensemble de données :

{
    "license": "CC0-1.0",
    "description": {
        "en": "Sample monthly temperature data in degrees C"
    },
    "schema": {
        "fields": [
            {
                "name": "month",
                "type": "localized",
                "title": { "en": "Month" }
            },
            {
                "name": "low",
                "type": "number",
                "title": { "en": "Low temp" }
            },
            {
                "name": "high",
                "type": "number",
                "title": { "en": "High temp" }
            }
        ]
    },
    "data": [
        [ { "en": "January" }, 5, 20 ],
        [ { "en": "July" }, 15, 30 ]
    ]
}

Attention, la validation du format des données sera appliquée après la transformation, de sorte que tout format non valide sera signalé par une erreur si vous passez par le pipeline de transformation proprement dit.

Chaines régionalisées

Les chaines localisables au format { "lang": "string" } sont préservées dans ce traitement, et si vous renvoyez une chaine multilingue, elle sera localisée au mieux pour le contexte approprié de la sortie au niveau du rendu.

Toutefois, s’il est coûteux d’extraire toutes les chaines possibles, il devrait être acceptable de ne rechercher que la langue d’affichage de la page actuelle et de l’inclure.

Null/nil

Notez que Lua ne permet pas de stocker une valeur nil dans un tableau ; cela signifie que les tableaux JSON contenant des valeurs null ne sont pas bien gérés par le filtrage Lua.

Si votre ensemble de données nécessite de travailler avec des cellules de données vides pour lesquelles null semble approprié dans le JSON, il se peut que nous devions réfléchir à la manière de mieux le prendre en charge.

For instance you can iterate using the tab.schema.fields list, which always contains every column, and use the array indexes as you got them rather than appending with table.insert:

proc sum(tab, args)
    -- Append a sum field
    table.insert(tab.schema.fields, {
        ["name"] = "sum",
        ["type"] = "number",
        ["title"] = {
            ["en"] = "Sum"
        }
    })
    local sum_index = #tab.schema.fields

    for i, row in ipairs(tab.data) do
        local sum = 0
        -- iterate over schema.fields, not row which
        -- may have "holes" in it
        for j, field in ipairs(tab.schema.fields) do
            if row[j] then
                sum = sum + row[j]
            end
        end

        -- Do not use table.insert here!
        -- It could go in the wrong column due to adjacent nils.
        row[sum_index] = sum
    end
end

Chargement d’ensembles de données supplémentaires

Additional Data: pages may be loaded via mw.ext.data.get(); note that if you pass the optional language parameter as _ you'll get the full multilingual strings, otherwise it'll pare them down to just the rendering language.

Essayez de ne pas charger trop de données supplémentaires, car cela pourrait augmenter la durée d’exécution et l’utilisation des ressources.

Vous pouvez également charger du code Lua supplémentaire ou des modules de données, qui seront enregistrés pour la gestion de l’invalidation du cache.

Sources de données externes

Tout ce que vous pouvez exécuter à partir d’un module Lua invoqué à partir d’un modèle, vous pouvez l’exécuter ici - cependant, soyez conscient que cela n’inclut pas actuellement les interfaces pour récupérer Wikidata Query Service ou RESTbase, donc les anciennes utilisations de Graph qui nécessitent ces interfaces ne sont pas encore prêtes à être portées.

There is some limited interface for data fetches from Wikidata but this is likely of limited use for charts for now.

De futures API en Lua sont possibles pour d’autres types de recherches, comme dans RESTbase ou via des requêtes SPARQL, et nous espérons être en mesure d’aborder de nombreuses fonctionnalités de ce type à l’avenir par le biais de projets de la liste de souhaits de la communauté.

Performance

Currently the transform and the chart render are run fresh on every page parse, and the resulting output saved into the parser cache. If transforms are found to be unexpectedly expensive we may need to add more aggressive caching and/or resource limits in place.

Assume that speed and memory are constrained and you should aim to conserve them for the best reader and editor experience alike. There are hard limits on memory usage and processing time, which will be enforced similarly as for template Lua code.

Remember: limits for memory and CPU time are the same as for Lua modules used in templates. Using too much memory or running too long will result in the script being canceled and an error message being shown on the page.

Note that input Data: pages are restricted in production to 2 megabytes, but the chart renderer may limit input data size further -- final size limits are to be determined, but if you find youself hitting the limits consider decimating a large data set to fewer data points.

Remember: there's a strict limit on chart input data, as data sets must not only render fast on the server, they are sent to the client for interactive rendering.

For an example if a tabular data set has 24k rows and will be rendered to a chart for mobile and desktop computers, you have many more data points than pixels -- decimating to 1 out of 10 data points either in the input data set or through a transform should reduce processing time. (See the example under #Decimating input data.)

Testing hints

Lua runtime errors should be returned through the pipeline and reported when rendering is attempted. To iterate more quickly you can test your code at the transform or bare Lua level.

Special:ApiSandbox

You can test the actual JSON-transform pipeline with Commons:Special:ApiSandbox pointed at action=jsontransform, which will produce nicely formatted output from actual input pages.

Debug console

You can wrap a test harness around your module to call it from the Lua editor debugger console during preview, in a pinch:

local p = {}

-- Your fancy transform code here
function p.transform(tab, args)
    return tab
end

-- Put p.test() in the debug console while you're editing the code to test
-- with a specific sample data set / args
function p.test(func, args, tabname, lang)
    -- pass "_" for lang to get the raw multilingual source data
    local tab = mw.ext.data.get(tabname or "Chart Example Data.tab", lang or "_")
    local args = args or {
       ["key"] = "value"
    }
    tab = p[func or 'transform'](tab, args)
    return mw.dumpObject(tab)
end

return p

Real World Examples

Chart transform modules can be found at commons:Category:Chart transform modules.

Chart pages that include transforms can be added to commons:Category:Transformed charts.

Climate example

Category:DraftsCategory:Drafts

commons:Data:Climate_Paris.tab has a lot of interesting data in it but unfortunately if we render it as a chart directly the chart itself is not useful:

A whole bunch of things that shouldn't be on same Y axis (mm, days, celcius)Month of the year-50050100150200250JanuaryMaySeptemberRecord high (°C)Mean daily maximum (°C)Mean daily (°C)Mean daily minimum (°C)Record low (°C)Average precipitation (mm)Average precipitation days (> 1mm)Average snowy daysAverage relative humidity (%)Mean monthly sunshine hoursPercentage possible sunshineAverage ultraviolet index

We can use transforms to focus on certain columns of data and to make conversions such as Celsius to Fahrenheit or mm to inches:

Selecting curves and columns

A simple transform in commons:Module:TabUtils can be used to take the data and using the exported filter function "select" with argument "cols": "month,averageprecip" selects the first column (labeled month) of commons:Data:Climate_Paris.tab as xseries data and column 5 (averageprecip) as an yseries in commons:Data:Climate_Paris/transformed.chart:

    "transform": {
        "module": "TabUtils",
        "function": "select",
        "args": {
            "cols": "month,averageprecip"
        }
    },

Calling {{#chart:Climate_Paris/transformed.chart}} on a wiki page results in: Average precipitation (mm)Month of the year010203040506070JanuaryMaySeptemberAverage precipitation (mm)

Other columns can be chosen in the wikicode call by overriding the args of a .chart page that includes a TabUtils transform. Here, the two temperature columns recordhigh and meandaily are chosen as yseries. Note that the yaxis title can not be changed this way, so in this case, the yaxis title is wrong:

{{#chart:Climate Paris/transformed.chart
|arg:cols=month,recordhigh,meandaily
}}

This override results in the following chart: Average precipitation (mm)Month of the year01020304050JanuaryMaySeptemberRecord high (°C)Mean daily (°C)

If the .chart page default should show all curves, but allow selection of curves in the wikicode as in the above example, it is sufficient if the .chart json code includes the following:

    "transform": {
        "module": "TabUtils",
        "function": "select"
    }

Decimating input points

The select transform in Commons:Module:TabUtils can also decimate the input data set if you have more points than you need to produce a graph (or more than can be fed into the chart renderer successfully!)

Here's a sample that uses a 10x decimation with the select transform to make a too-large data set render:

    "transform": {
        "module": "TabUtils",
        "function": "select",
        "args": {
            "decimate": "10"
        }
    },

y(k)log(k)00,10,20,30,40,50,60,7051015202530y(k)Markov constant chart

Category:JsonConfig transforms documentation/fr
Category:Drafts Category:JsonConfig transforms documentation/fr Category:Pages using the Chart extension