Para que você tenha uma ideia de como “criar um script para um plugin” funciona, em comparação com a abordagem direta que você viu nos capítulos anteriores, criaremos o plugin completo do teste t mais uma vez -- desta vez apenas com as funções R do pacote rkwarddev.
Dica
O pacote adicionará uma nova caixa de diálogo GUI ao RKWard em → → . Como o nome sugere, você pode criar esqueletos de plugins para edição posterior com ele. Esta caixa de diálogo foi gerada por um script rkwarddev que você pode encontrar no diretório “demo” do pacote instalado e nas fontes do pacote, como um exemplo adicional. Você também pode executá-lo chamando demo("skeleton_dialog")
Você notará imediatamente que o fluxo de trabalho é consideravelmente diferente: Ao contrário de escrever o código XML diretamente, você não começa com a definição do <document>, mas diretamente com os elementos do plugin que você gostaria de ter na caixa de diálogo. Você pode atribuir cada elemento da interface -- sejam caixas de seleção, menus suspensos, slots de variáveis ou qualquer outra coisa -- a objetos R individuais e, em seguida, combinar esses objetos para formar a GUI propriamente dita. O pacote possui funções para cada tag XML que pode ser usada para definir a GUI do plugin, e a maioria delas tem até o mesmo nome, apenas com o prefixo rk.XML.*. Por exemplo, definir um elemento <varselector> e dois elementos <varslot> para as variáveis "x" e "y" do teste t de exemplo pode ser feito da seguinte forma:
variables <- rk.XML.varselector(id.name="vars")
var.x <- rk.XML.varslot("comparar", source=variables, types="number", required=TRUE, id.name="x")
var.y <- rk.XML.varslot("contra", source=variables, types="number", required=TRUE, id.name="y")
O detalhe mais interessante é provavelmente source=variables: Uma característica importante do pacote é que todas as funções podem gerar IDs automáticos, então você não precisa se preocupar em pensar em valores de id ou em lembrá-los para se referir a um elemento específico do plugin. Você pode simplesmente fornecer os objetos R como referência, pois todas as funções que precisam de um ID de algum outro elemento também podem lê-lo desses objetos. rk.XML.varselector() é um pouco especial, pois geralmente não tem um conteúdo específico para gerar um ID (pode ter, mas apenas se você especificar um rótulo), então precisamos definir um nome de ID. Mas rk.XML.varslot() não precisaria dos argumentos id.name aqui, então isso seria suficiente:
variables <- rk.XML.varselector(id.name="vars")
var.x <- rk.XML.varslot("comparar", source=variables, types="number", required=TRUE)
var.y <- rk.XML.varslot("contra", source=variables, types="number", required=TRUE)
Para recriar o código de exemplo exatamente como estava, você teria que definir todos os valores de ID manualmente. Mas como o pacote facilitará nossa vida, a partir de agora não nos preocuparemos mais com isso.
Dica
rkwarddev é capaz de muita automação para ajudar você a criar seus plugins. No entanto, pode ser preferível não usá-lo em sua totalidade. Se seu objetivo é produzir um código que não apenas funcione mas também possa ser facilmente lido e comparado ao seu script gerador por um ser humano, você deve considerar sempre definir IDs úteis com id.name. Nomear seus objetos R de forma idêntica a esses IDs também ajudará a obter um código de script fácil de entender.
Se você quiser ver como o código XML do elemento definido se parece se você o exportasse para um arquivo, basta chamar o objeto pelo nome. Então, se você agora chamar “var.x” na sua sessão do R, você deverá ver algo como isto:
<varslot id="vrsl_compare" label="comparar" source="vars" types="number" required="true" />
Algumas tags só são úteis no contexto de outras. Portanto, por exemplo, você não encontrará uma função para a tag <option>. Em vez disso, tanto os botões de opção quanto as listas suspensas são definidos incluindo suas opções como uma lista nomeada, onde os nomes representam os rótulos a serem exibidos na caixa de diálogo, e seu valor é um vetor nomeado que pode ter duas entradas: val para o valor de uma opção e o booleano chk para especificar se esta opção está marcada por padrão.
test.hypothesis <- rk.XML.radio("usar hipótese de teste",
options=list(
"Dois lados"=c(val="two.sided"),
"Primeiro é maior"=c(val="greater"),
"Segundo é maior"=c(val="less")
)
)
O resultado é o seguinte:
<radio id="rad_usngtsth" label="usar hipótese de teste">
<option label="Dois lados" value="two.sided" />
<option label="Primeiro é maior" value="greater" />
<option label="Segundo é maior" value="less" />
</radio>
Tudo o que falta nos elementos da aba “Configurações básicas” é a caixa de seleção para amostras pareadas e a estruturação de todos esses elementos em linhas e colunas:
check.paired <- rk.XML.cbox("Amostras pareadas", value="1", un.value="0")
basic.settings <- rk.XML.row(variables, rk.XML.col(var.x, var.y, test.hypothesis, check.paired))
A rk.XML.cbox() é uma rara exceção onde o nome da função não contém o nome completo da tag, para economizar digitação para este elemento frequentemente usado. Isto é o que basic.settings agora contém:
<row id="row_vTFSPP10TF">
<varselector id="vars" />
<column id="clm_vrsTFSPP10">
<varslot id="vrsl_compare" label="comparar" source="vars" types="number" required="true" />
<varslot id="vrsl_against" label="contra" i18n_context="comparar contra" source="vars" types="number" required="true" />
<radio id="rad_usngtsth" label="usar hipótese de teste">
<option label="Dois lados" value="two.sided" />
<option label="Primeiro é maior" value="greater" />
<option label="Segundo é maior" value="less" />
</radio>
<checkbox id="chc_Pardsmpl" label="Amostras pareadas" value="1" value_unchecked="0" />
</column>
</row>
De maneira semelhante, as próximas linhas criarão objetos R para os elementos da aba “Opções”, introduzindo funções para caixas de seleção, molduras e espaços:
check.eqvar <- rk.XML.cbox("assumir variâncias iguais", value="1", un.value="0")
conf.level <- rk.XML.spinbox("nível de confiança", min=0, max=1, initial=0.95)
check.conf <- rk.XML.cbox("imprimir intervalo de confiança", val="1", chk=TRUE)
conf.frame <- rk.XML.frame(conf.level, check.conf, rk.XML.stretch(), label="Confidence Interval")
Agora, tudo o que precisamos fazer é juntar os objetos em um tabbook e colocá-lo em uma seção de diálogo:
full.dialog <- rk.XML.dialog(
label="Teste t com duas variáveis",
rk.XML.tabbook(tabs=list("Configurações básicas"=basic.settings, "Opções"=list(check.eqvar, conf.frame)))
)
Também podemos criar a seção do assistente com suas duas páginas usando os mesmos objetos, portanto, seus IDs serão extraídos para as tags <copy>:
full.wizard <- rk.XML.wizard(
label="Teste t com duas variáveis",
rk.XML.page(
rk.XML.text("Como primeiro passo, selecione duas variáveis que você deseja comparar.
E especifique qual você teoriza que é maior. Selecione dois lados,
se sua teoria não te informa qual variável é maior."),
rk.XML.copy(basic.settings)),
rk.XML.page(
rk.XML.text("Abaixo estão algumas opções avançadas. É geralmente seguro não considerar que as
variáveis tenha variância igual. Uma correção apropriada será aplicada então.
Escolher \"assumir variâncias iguais\" pode aumentar a força do teste, no entanto."),
rk.XML.copy(check.eqvar),
rk.XML.text("Algumas vezes é útil obter uma estimativa do intervalo de confiança da
diferença em significado. Abaixo você pode especificar se um deve ser exibido, e
qual nível de confiança deve ser aplicado (95% corresponde a 5% de nível de
significância)."),
rk.XML.copy(conf.frame)))
Isso é tudo para a GUI. O documento global será combinado no final por rk.plugin.skeleton().
Até agora, usar o pacote rkwarddev pode não parecer ter ajudado muito. Isso vai mudar agora.
Primeiramente, assim como não precisamos nos preocupar com os IDs dos elementos ao definir o layout da GUI, também não precisamos nos preocupar com os nomes das variáveis JavaScript na próxima etapa. Se você quiser mais controle, pode escrever código JavaScript puro e colá-lo no arquivo gerado. Mas provavelmente é muito mais eficiente fazer isso da maneira do rkwarddev.
O mais notável é que você não precisa definir nenhuma variável manualmente, pois rk.plugin.skeleton() pode analisar seu código XML e definir automaticamente todas as variáveis que você provavelmente precisará -- por exemplo, você não se preocuparia em incluir uma caixa de seleção se não usar seu valor ou estado posteriormente. Assim, podemos começar a escrever o código JS que gera o código R imediatamente.
Dica
A função rk.JS.scan() também pode analisar arquivos XML em busca de variáveis.
O pacote possui algumas funções para construções de código JS que são comumente usadas em plugins RKWard, como a função echo() ou as condições if() {...} else {...}. Existem algumas diferenças entre JS e R, por exemplo, para paste() em RKWard você usa a vírgula para concatenar strings de caracteres, enquanto para echo() em JS você usa “+”, e as linhas devem terminar com um ponto e vírgula. Ao usar as funções R, você pode praticamente esquecer essas diferenças e continuar escrevendo código R.
Essas funções podem receber diferentes classes de objetos de entrada: texto simples, objetos R com código XML como o acima ou, por sua vez, resultados de outras funções JS do pacote. No final, você sempre chamará rk.paste.JS(), que se comporta de forma semelhante a paste(), mas, dependendo dos objetos de entrada, irá substituí-los por seus IDs XML, nomes de variáveis JavaScript ou até mesmo blocos de código JavaScript completos.
Para o exemplo do teste t, precisamos de dois objetos JS: um para calcular os resultados e outro para imprimi-los na função 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)
Como você pode ver, rkwarddev também fornece uma implementação de R da função echo(). Ela retorna exatamente uma string de um caractere com uma versão JS válida de si mesma. Você também pode notar que todos os objetos R aqui são aqueles que criamos anteriormente. Eles serão automaticamente substituídos por seus nomes de variáveis, então isso deve ser bastante intuitivo. Sempre que você precisar apenas dessa substituição, a função id() pode ser usada, que também retornará exatamente uma string de um caractere de todos os objetos que lhe foram fornecidos (você poderia dizer que ela se comporta como paste() com uma substituição de objeto muito específica).
A função js() é um wrapper que permite usar as condições if(){...} else {...} do R como você já está acostumado. Elas serão traduzidas diretamente para código JS. Também preserva alguns operadores como <, >= ou ||, para que você possa comparar logicamente seus objetos R sem a necessidade de aspas na maioria das vezes. Vejamos o objeto “JS.calc” resultante, que agora contém uma string de caracteres com o seguinte conteúdo:
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
Alternativamente, para condições if() aninhadas em js(), você pode usar a função ite(), que se comporta de forma semelhante à função ifelse() do pacote R. No entanto, instruções condicionais construídas usando ite() geralmente são mais difíceis de ler e devem ser substituídas por js() sempre que possível.
Esta seção é muito curta: Não precisamos escrever um .pluginmap pois ele pode ser gerado automaticamente por rk.plugin.skeleton(). A hierarquia do menu pode ser especificada através da opção pluginmap:
[...]
pluginmap=list(
name="Teste t com duas variáveis",
hierarchy=list("análise", "significado", "Teste t"))
[...]
Esta seção também é muito curta: rk.plugin.skeleton() não consegue escrever uma página de ajuda completa com as informações que possui. Mas ela consegue analisar o documento XML em busca de elementos que provavelmente merecem uma entrada na página de ajuda e criar automaticamente um modelo de página de ajuda para o nosso plugin. Tudo o que precisamos fazer depois é escrever algumas linhas para cada seção listada.
Dica
A função rk.rkh.scan() também pode analisar arquivos XML para criar um esqueleto de arquivo de ajuda.
Agora vem a etapa final, na qual entregaremos todos os objetos gerados para rk.plugin.skeleton():
plugin.dir <- rk.plugin.skeleton("Teste t",
xml=list(
dialog=full.dialog,
wizard=full.wizard),
js=list(
results.header="Teste t com duas variáveis",
calculate=JS.calc,
printout=JS.print),
pluginmap=list(
name="Two Variable t-Test",
hierarchy=list("análise", "significados", "Teste t")),
load=TRUE,
edit=TRUE,
show=TRUE)
Os arquivos serão criados em um diretório temporário por padrão. As três últimas opções não são necessárias, mas são muito úteis: load=TRUE adicionará automaticamente o novo plugin à configuração do RKWard (já que ele está em um diretório temporário e, portanto, deixará de existir quando o RKWard for fechado, ele será removido automaticamente pelo RKWard na próxima inicialização), edit=TRUE abrirá todos os arquivos criados para edição nas abas do editor do RKWard e show=TRUE tentará iniciar o plugin diretamente, para que você possa ver como ele é sem precisar clicar. Você pode considerar adicionar overwrite=TRUE se você for executar seu script repetidamente (por exemplo, após alterações no código), pois, por padrão, nenhum arquivo será sobrescrito.
O objeto de resultado “plugin.dir” contém o caminho para o diretório no qual o plugin foi criado. Isso pode ser útil em combinação com a função rk.build.package(), para construir um pacote Rreal para compartilhar seu plugin com outras pessoas -- por exemplo, enviando-o para a equipe de desenvolvimento da RKWard para ser adicionado ao nosso repositório de plugins.
Para recapitular tudo o que foi dito acima, aqui está o script completo para criar o exemplo de teste t funcional. Além do código já explicado, ele também carrega o pacote, se necessário, e usa o ambiente local(), portanto, todos os objetos criados não ficarão no seu espaço de trabalho atual (exceto o arquivo “plugin.dir”):
require(rkwarddev)
local({
variables <- rk.XML.varselector(id.name="vars")
var.x <- rk.XML.varslot("comparar", source=variables, types="number", required=TRUE)
var.y <- rk.XML.varslot("contra", source=variables, types="number", required=TRUE)
test.hypothesis <- rk.XML.radio("usar hipótese de teste",
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("Amostra pareadas", 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("assumir variâncias iguais", value="1", un.value="0")
conf.level <- rk.XML.spinbox("nível de confiança", min=0, max=1, initial=0.95)
check.conf <- rk.XML.cbox("imprimir intervalo de confiança", val="1", chk=TRUE)
conf.frame <- rk.XML.frame(conf.level, check.conf, rk.XML.stretch(), label="Intervalo de confiança")
full.dialog <- rk.XML.dialog(
label="Teste t com duas variáveis",
rk.XML.tabbook(tabs=list("Configurações básicas"=basic.settings, "Opções"=list(check.eqvar, conf.frame)))
)
full.wizard <- rk.XML.wizard(
label="Teste t com duas variáveis",
rk.XML.page(
rk.XML.text("Como primeiro passo, selecione duas variáveis que você deseja comparar.
E especifique qual você teoriza que é maior. Selecione dois lados,
se sua teoria não te informa qual variável é maior."),
rk.XML.copy(basic.settings)),
rk.XML.page(
rk.XML.text("Abaixo estão algumas opções avançadas. É geralmente seguro não considerar que as
variáveis tenha variância igual. Uma correção apropriada será aplicada então.
Escolher \"assumir variâncias iguais\" pode aumentar a força do teste, no entanto."),
rk.XML.copy(check.eqvar),
rk.XML.text("Algumas vezes é útil obter uma estimativa do intervalo de confiança da
diferença em significado. Abaixo você pode especificar se um deve ser exibido, e
qual nível de confiança deve ser aplicado (95% corresponde a 5% de nível de
significância)."),
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="Teste t com duas variáveis",
calculate=JS.calc,
printout=JS.print),
pluginmap=list(
name="Two Variable t-Test",
hierarchy=list("análise", "significados", "Teste t")),
load=TRUE,
edit=TRUE,
show=TRUE,
overwrite=TRUE)
})