Exemple pràctic

Per a tenir una idea de com «crear scripts en un connector», en comparació amb l'enfocament directe que heu vist en els capítols anteriors, crearem el connector complet de la prova t una vegada més; aquesta vegada només amb les funcions R del paquet rkwarddev.

Suggeriment

El paquet afegirà un diàleg nou de la IGU al RKWard a FitxerExportaCrea un script de connector del RKWard. Com suggereix el nom, podeu crear esquelets de connectors per a una edició posterior. Aquest diàleg al seu torn es va generar amb un script del rkwarddev que trobareu al directori «demo» del paquet i de codi font de paquet instal·lat, com a exemple addicional. També podeu executar-la cridant demo("skeleton_dialog")

Descripció de la IGU

Us adonareu immediatament que el flux de treball és molt diferent: al contrari d'escriure directament codi XML, no comenceu amb la definició <document>, sinó directament amb els elements del connector que us agradaria tenir al diàleg. Podeu assignar cada element d'interfície, ja siguin caselles de selecció, menús desplegables, «varslots» o qualsevol altra cosa, a objectes individuals de l'R, i després combinar aquests objectes amb la IGU real. El paquet té funcions per a cada etiqueta XML que es pot utilitzar per a definir la IGU del connector, i la majoria fins i tot tenen el mateix nom, només amb el prefix rk.XML.*. Per exemple, la definició d'una variable <varselector> i dos elements <varslot> per a la variable "x" i "y" de l'exemple de prova t es pot fer mitjançant:

variables <- rk.XML.varselector(id.name="vars")
var.x <- rk.XML.varslot("compare", source=variables, types="number", required=TRUE, id.name="x")
var.y <- rk.XML.varslot("against", source=variables, types="number", required=TRUE, id.name="y")
                

El detall més interessant probablement és source=variables: Una característica destacada del paquet és que totes les funcions poden generar ID automàtics, de manera que no us heu de molestar pensant o recordant en valors d'id per a referir-vos a un element específic del connector. Senzillament, podeu donar els objectes R com a referència, ja que totes les funcions que necessiten un ID d'algun altre element també poden llegir-lo des d'aquests objectes. rk.XML.varselector() és una mica especial, ja que normalment no té contingut específic per a crear un ID (pot, però només si especifiqueu una etiqueta), de manera que hem d'establir un nom d'ID. Però rk.XML.varslot() no necessitaria els arguments id.name, així que això seria suficient:

variables <- rk.XML.varselector(id.name="vars")
var.x <- rk.XML.varslot("compare", source=variables, types="number", required=TRUE)
var.y <- rk.XML.varslot("against", source=variables, types="number", required=TRUE)
                

Per tal de tornar a crear el codi d'exemple fins aquest punt, haureu d'establir tots els valors d'ID manualment. Però com que el paquet facilitarà les nostres vides, a partir d'ara ja no ens preocuparà.

Suggeriment

El rkwarddev és capaç de fer molta automatització per a ajudar-vos a construir els vostres connectors. No obstant això, podria ser preferible no utilitzar-ho en tota la seva màxima extensió. Si el vostre objectiu és produir codi que no només funcioni, sinó que també es pugui llegir fàcilment i en comparació l'script generador amb un ésser humà, hauríeu de considerar establir sempre ID útils amb id.name. Anomenar els vostres objectes R idèntics a aquests ID també ajudarà a obtenir codi de script que sigui fàcil d'entendre.

Si voleu veure com es veu el codi XML de l'element definit si l'exporteu a un fitxer, només podeu cridar l'objecte pel seu nom. Per tant, si heu anomenat «var.x» en la vostra sessió R, hauríeu de veure quelcom com això:

<varslot id="vrsl_compare" label="compare" source="vars" types="number" required="true" />
                

Algunes etiquetes només són útils en el context d'altres. Per tant, per exemple, no trobareu una funció per a l'etiqueta <option>. En lloc d'això, tant els botons d'opció com les llistes desplegables es defineixen incloent les seves opcions com una llista amb nom, on els noms representen les etiquetes que es mostraran en el diàleg, i el seu valor és un vector amb nom que pot tenir dues entrades, val per al valor d'una opció i el booleà chk per a especificar si aquesta opció està marcada de manera predeterminada.

test.hypothesis <- rk.XML.radio("using test hypothesis",
        options=list(
                "Two-sided"=c(val="two.sided"),
                "First is greater"=c(val="greater"),
                "Second is greater"=c(val="less")
        )
)
                

El resultat quedarà així:

<radio id="rad_usngtsth" label="using test hypothesis">
        <option label="Two-sided" value="two.sided" />
        <option label="First is greater" value="greater" />
        <option label="Second is greater" value="less" />
</radio>
                

Tot el que falta als elements de la pestanya «Configuració bàsica» és la casella de selecció per a mostres aparellades, i l'estructuració de tots aquests elements en files i columnes:

check.paired <- rk.XML.cbox("Paired sample", value="1", un.value="0")
basic.settings <- rk.XML.row(variables, rk.XML.col(var.x, var.y, test.hypothesis, check.paired))
                

rk.XML.cbox() és una excepció rara on el nom de la funció no conté el nom complet de l'etiqueta, per a desar algun tecleig per a aquest element utilitzat sovint. Això és el que basic.settings conté ara:

<row id="row_vTFSPP10TF">
        <varselector id="vars" />
        <column id="clm_vrsTFSPP10">
                <varslot id="vrsl_compare" label="compare" source="vars" types="number" required="true" />
                <varslot id="vrsl_against" label="against" i18n_context="compare against" source="vars" types="number" required="true" />
                <radio id="rad_usngtsth" label="using test hypothesis">
                        <option label="Two-sided" value="two.sided" />
                        <option label="First is greater" value="greater" />
                        <option label="Second is greater" value="less" />
                </radio>
                <checkbox id="chc_Pardsmpl" label="Paired sample" value="1" value_unchecked="0" />
        </column>
</row>
                

De manera similar, les línies següents crearan objectes R per als elements de la pestanya «Opcions», introduint funcions per a «spinboxes» (botons de selecció de valors), «frames» i «stretch»:

check.eqvar <- rk.XML.cbox("assume equal variances", value="1", un.value="0")
conf.level <- rk.XML.spinbox("confidence level", min=0, max=1, initial=0.95)
check.conf <- rk.XML.cbox("print confidence interval", val="1", chk=TRUE)
conf.frame <- rk.XML.frame(conf.level, check.conf, rk.XML.stretch(), label="Confidence Interval")
                

Tot el que hem de fer ara és reunir els objectes en un «tabbook», i col·locar-lo en una secció «dialog»:

full.dialog <- rk.XML.dialog(
        label="Two Variable t-Test",
        rk.XML.tabbook(tabs=list("Basic settings"=basic.settings, "Options"=list(check.eqvar, conf.frame)))
)
                

També podem crear la secció de l'assistent amb les seves dues pàgines utilitzant els mateixos objectes, de manera que els seus ID s'extrauran per a les etiquetes <copy>:

full.wizard <- rk.XML.wizard(
                label="Two Variable t-Test",
                rk.XML.page(
                        rk.XML.text("As a first step, select the two variables you want to compare against
                                each other. And specify, which one you theorize to be greater. Select two-sided,
                                if your theory does not tell you, which variable is greater."),
                        rk.XML.copy(basic.settings)),
                rk.XML.page(
                        rk.XML.text("Below are some advanced options. It is generally safe not to assume the
                                variables have equal variances. An appropriate correction will be applied then.
                                Choosing \"assume equal variances\" may increase test-strength, however."),
                        rk.XML.copy(check.eqvar),
                        rk.XML.text("Sometimes it is helpful to get an estimate of the confidence interval of
                                the difference in means. Below you can specify whether one should be shown, and
                                which confidence-level should be applied (95% corresponds to a 5% level of
                                significance)."),
                        rk.XML.copy(conf.frame)))
                

Això és per a la IGU. El document global es combinarà al final amb rk.plugin.skeleton().

Codi JavaScript

Fins ara, l'ús del paquet rkwarddev podria no haver ajudat tant. Això canviarà ara mateix.

En primer lloc, de la mateixa manera que no ens havien d'importar els ID dels elements en definir la disposició de la IGU, no ens haurà d'importar els noms de les variables JavaScript en el pas següent. Si voleu més control, podeu escriure codi JavaScript net i enganxar-lo al fitxer generat. Però probablement és molt més eficient fer-ho de la manera del rkwarddev.

El més notable és que no haureu de definir cap variable, ja que rk.plugin.skeleton() pot explorar el codi XML i definir automàticament totes les variables que probablement necessitareu; per exemple, no us molestareu a incloure una casella de selecció si després no utilitzeu el seu valor o estat. Així que podem començar a escriure el codi R real que genera JS immediatament.

Suggeriment

La funció rk.JS.scan() també pot explorar els fitxers XML existents per a les variables.

El paquet té algunes funcions per a construccions de codi JS que s'utilitzen habitualment en connectors del RKWard, com la funció echo() o les condicions if() {...} else {...}. Hi ha algunes diferències entre el JS i l'R, p. ex., per a paste() a l'R utilitzeu la coma per concatenar les cadenes de caràcters, mentre que per a echo() en el JS utilitzeu «+», i les línies han d'acabar amb un punt i coma. Mitjançant l'ús de les funcions R, gairebé podeu oblidar-vos d'aquestes diferències i continuar escrivint codi R.

Aquestes funcions poden prendre diferents classes d'objectes d'entrada: text net, objectes R amb codi XML com a dalt, o al seu torn resultats d'altres funcions JS del paquet. Al final, sempre cridareu rk.paste.JS(), el qual es comporta de manera similar a paste(), però depenent dels objectes d'entrada els reemplaçarà amb el seu ID XML, el nom de variable JavaScript o fins i tot blocs de codi JavaScript complets.

Per a l'exemple de la prova t, necessitem dos objectes JS: un per a calcular els resultats, i un per a imprimir-los a la funció printout():

JS.calc <- rk.paste.JS(
        echo("res <- t.test (x=", var.x, ", y=", var.y, ", hypothesis=\"", test.hypothesis, "\""),
        js(
                if(check.paired){
                        echo(", paired=TRUE")
                },
                if(!check.paired && check.eqvar){
                        echo(", var.equal=TRUE")
                },
                if(conf.level != "0.95"){
                        echo(", conf.level=", conf.level)
                },
                linebreaks=TRUE
        ),
        echo(")\n"),
        level=2
)

JS.print <- rk.paste.JS(echo("rk.print (res)\n"), level=2)
                

Com podeu veure, el rkwarddev també proporciona una implementació R de la funció echo(). Torna exactament una cadena de caràcters amb una versió JS vàlida d'ella mateixa. També us podeu adonar que tots els objectes R aquí són els que hem creat abans. Se substituiran automàticament pels seus noms de variables, per la qual cosa això hauria de ser bastant intuïtiu. Sempre que necessiteu aquest reemplaçament, es pot utilitzar la funció id(), que també retornarà exactament una cadena de caràcters de tots els objectes que s'han indicat (podríeu dir que es comporta com paste() amb una substitució d'objecte molt específica).

La funció js() és un embolcall que permet utilitzar les condicions if(){...} else {...} de l'R com les que esteu acostumat a fer. Es traduiran directament al codi JS. També conserva alguns operadors com <, >= o ||, de manera que podeu comparar lògicament els objectes R sense la necessitat de posar cometes la major part del temps. Mirem l'objecte «JS.calc» resultant, que ara conté una cadena de caràcters amb aquest contingut:

echo("res <- t.test (x=" + vrslCompare + ", y=" + vrslAgainst + ", hypothesis=\"" + radUsngtsth + "\"");
        if(chcPardsmpl) {
                echo(", paired=TRUE");
        } else {}
        if(!chcPardsmpl && chcAssmqlvr) {
                echo(", var.equal=TRUE");
        } else {}
        if(spnCnfdnclv != "0.95") {
                echo(", conf.level=" + spnCnfdnclv);
        } else {}
        echo(")\n");
                

Nota

Alternativament per a les condicions imbricades if() en js(), podeu utilitzar la funció ite(), que es comporta de manera similar a ifelse() de l'R. No obstant això, les sentències condicionals construïdes utilitzant ite() solen ser més difícils de llegir i s'han de substituir per js() sempre que sigui possible.

Mapa de connectors

Aquesta secció és molt curta: no cal escriure cap .pluginmap, ja que es pot generar automàticament amb rk.plugin.skeleton(). La jerarquia del menú es pot especificar mitjançant l'opció pluginmap:

[...]
        pluginmap=list(
                name="Two Variable t-Test",
                hierarchy=list("analysis", "means", "t-Test"))
        [...]
                        

Pàgina d'ajuda

Aquesta secció també és molt curta: rk.plugin.skeleton() no pot escriure una pàgina d'ajuda completa a partir de la informació que té. Però també pot explorar el document XML cercant elements que probablement mereixen una entrada de pàgina d'ajuda, i crear automàticament una plantilla de pàgines d'ajuda per al nostre connector. Tot el que hem de fer després és escriure algunes línies per a cada secció llistada.

Suggeriment

La funció rk.rkh.scan() també pot explorar els fitxers XML existents per a crear un esquelet de fitxer d'ajuda.

Generació dels fitxers del connector

Ara ve el pas final, en el qual lliurarem tots els objectes generats a rk.plugin.skeleton():

plugin.dir <- rk.plugin.skeleton("t-Test",
        xml=list(
                dialog=full.dialog,
                wizard=full.wizard),
        js=list(
                results.header="Two Variable t-Test",
                calculate=JS.calc,
                printout=JS.print),
        pluginmap=list(
                name="Two Variable t-Test",
                hierarchy=list("analysis", "means", "t-Test")),
        load=TRUE,
        edit=TRUE,
        show=TRUE)
                        

Els fitxers es crearan en un directori temporal predeterminat. Les últimes tres opcions no són necessàries, però són molt útils: load=TRUE afegirà automàticament el connector nou a la configuració del RKWard (ja que està en un directori temporal i, per tant, deixarà d'existir quan es tanqui el RKWard, s'eliminarà automàticament de nou pel RKWard durant l'inici següent), edit=TRUE obrirà tots els fitxers creats per a editar-los a les pestanyes de l'editor del RKWard, i show=TRUE intentarà llançar directament el connector, de manera que podreu examinar com es veu sense un clic. Podríeu considerar afegir overwrite=TRUE si executareu l'script repetidament (p. ex., després dels canvis al codi), ja que de manera predeterminada no se sobreescriuran fitxers.

L'objecte resultant «plugin.dir» conté el camí al directori on es va crear el connector. Això pot ser útil en combinació amb la funció rk.build.package(), per a construir un paquet R real per a compartir el vostre connector amb altres; p. ex., per enviant-lo a l'equip de desenvolupament del RKWard per a afegir-lo al nostre repositori de connectors.

L'script complet

Per a recapitular tot l'anterior, aquí hi ha l'script complet per a crear l'exemple de prova t que funciona. Afegint el codi ja explicat, també carrega el paquet si cal, i utilitza l'entorn local(), de manera que tots els objectes creats no acabaran a l'espai de treball actual (excepte el «plugin.dir»):

require(rkwarddev)

local({
        variables <- rk.XML.varselector(id.name="vars")
        var.x <- rk.XML.varslot("compare", source=variables, types="number", required=TRUE)
        var.y <- rk.XML.varslot("against", source=variables, types="number", required=TRUE)
        test.hypothesis <- rk.XML.radio("using test hypothesis",
                options=list(
                        "Two-sided"=c(val="two.sided"),
                        "First is greater"=c(val="greater"),
                        "Second is greater"=c(val="less")
                )
        )
        check.paired <- rk.XML.cbox("Paired sample", value="1", un.value="0")
        basic.settings <- rk.XML.row(variables, rk.XML.col(var.x, var.y, test.hypothesis, check.paired))

        check.eqvar <- rk.XML.cbox("assume equal variances", value="1", un.value="0")
        conf.level <- rk.XML.spinbox("confidence level", min=0, max=1, initial=0.95)
        check.conf <- rk.XML.cbox("print confidence interval", val="1", chk=TRUE)
        conf.frame <- rk.XML.frame(conf.level, check.conf, rk.XML.stretch(), label="Confidence Interval")

        full.dialog <- rk.XML.dialog(
                label="Two Variable t-Test",
                rk.XML.tabbook(tabs=list("Basic settings"=basic.settings, "Options"=list(check.eqvar, conf.frame)))
        )

        full.wizard <- rk.XML.wizard(
                        label="Two Variable t-Test",
                        rk.XML.page(
                                rk.XML.text("As a first step, select the two variables you want to compare against
                                        each other. And specify, which one you theorize to be greater. Select two-sided,
                                        if your theory does not tell you, which variable is greater."),
                                rk.XML.copy(basic.settings)),
                        rk.XML.page(
                                rk.XML.text("Below are some advanced options. It is generally safe not to assume the
                                        variables have equal variances. An appropriate correction will be applied then.
                                        Choosing \"assume equal variances\" may increase test-strength, however."),
                                rk.XML.copy(check.eqvar),
                                rk.XML.text("Sometimes it is helpful to get an estimate of the confidence interval of
                                        the difference in means. Below you can specify whether one should be shown, and
                                        which confidence-level should be applied (95% corresponds to a 5% level of
                                        significance)."),
                                rk.XML.copy(conf.frame)))

        JS.calc <- rk.paste.JS(
                echo("res <- t.test (x=", var.x, ", y=", var.y, ", hypothesis=\"", test.hypothesis, "\""),
                js(
                        if(check.paired){
                        echo(", paired=TRUE")
                        },
                        if(!check.paired && check.eqvar){
                        echo(", var.equal=TRUE")
                        },
                        if(conf.level != "0.95"){
                        echo(", conf.level=", conf.level)
                        },
                        linebreaks=TRUE
                ),
                echo(")\n"), level=2)

        JS.print <- rk.paste.JS(echo("rk.print (res)\n"), level=2)

        plugin.dir <<- rk.plugin.skeleton("t-Test",
                xml=list(
                        dialog=full.dialog,
                        wizard=full.wizard),
                js=list(
                        results.header="Two Variable t-Test",
                        calculate=JS.calc,
                        printout=JS.print),
                pluginmap=list(
                        name="Two Variable t-Test",
                        hierarchy=list("analysis", "means", "t-Test")),
                load=TRUE,
                edit=TRUE,
                show=TRUE,
                overwrite=TRUE)
})