Suponhamos um cenário em que o objectivo é o de decidir se se deve conceder ou não crédito a um cliente de uma instituição bancária. O primeiro passo, conforme mencionado acima, seria o de entrevistar os peritos da instituição neste domínio, e tentar obter as regras de decisão que usam. Suponhamos por razões de simplificação do exemplo, que chegamos ao seguinte conjunto de regras:
Dado este conjunto de regras de decisão, o nosso objectivo será o de construir um sistema que em face de um novo cliente e as suas características, possa tomar a decisão sobre se devemos ou não conceder crédito ao cliente.
A linguagem R possui uma instrução que permite testar condições e executar diferentes instruções dependendo do resultado desse teste. Este tipo de instrução é em tudo idêntica a uma regra de decisão e portanto adequa-se perfeitamente aos nossos objectivos. Antes de usarmos a instrução para implementar as nossas regras de decisão, vejamos alguns pequenos exemplos para melhor a compreender.
A instrução chama-se IF e tem a seguinte sintaxe,
em que expressão é uma condição lógica com o resultado TRUE ou FALSE.
Vejamos um exemplo de uma utilização desta instrução. Suponhamos que pretendemos implementar a regra de decisão ``SE o montante pedido é baixo ENTÃO conceder crédito SENÃO não conceder''. Imaginemos que temos o tipo de montante pedido (baixo, médio ou alto) numa variável chamada montante. Podíamos usar uma instrução IF para colocar a nossa decisão numa variável decisão, do seguinte modo,
> montante <- 'alto' > if (montante == 'baixo') decisão <- 'conceder' else decisão <- 'não conceder' > decisão [1] "não conceder"
Se o valor da variável montante fosse 'baixo' o resultado seria outro,
> montante <- 'baixo' > if (montante == 'baixo') decisão <- 'conceder' else decisão <- 'não conceder' > decisão [1] "conceder"
Estar a repetir estas mesmas instruções sempre que queremos testar uma nova situação, não é muito prático, apesar de com as teclas com setas o R nos permitir obter as instruções digitadas anteriormente no seu prompt. Sempre que temos um conjunto de instruções que repetimos frequentemente é mais prático tirar partido do facto de o R nos permitir criar funções, e deste modo criar uma nova função que possamos depois usar sempre que quisermos repetir as instruções. Vejamos um exemplo simples com a implementação da regra de decisão apresentada acima,
> crédito <- function(montante) { + if (montante == 'baixo') decisão <- 'conceder' else decisão <- 'não conceder' + decisão + } > crédito('alto') [1] "não conceder" > crédito('médio') [1] "não conceder" > crédito('baixo') [1] "conceder"
A primeira instrução deste exemplo cria uma nova função chamada crédito. As funções em R são objectos como os outros que vimos até agora (vectores, matrizes, etc.), e portanto para decidirmos o seu conteúdo usamos também a instrução de atribuição. O conteúdo de uma função tem um formato específico que devemos seguir. Concretamente, o conteúdo de uma função tem usualmente o seguinte aspecto,
<-
function(parâmetros da função) {
}
As chavetas delimitam o conjunto de instruções que são executadas sempre que alguém decide utilizar a função. Cada instrução é escrita numa linha diferente. Qualquer função é suposta produzir um resultado (por exemplo sqrt(2) produz como resultado a raiz quadrada de 2). O resultado das funções criadas pelo utilizador é o último valor calculado na função. Na nossa função crédito o último valor calculado na função é o valor da variável decisão, que nós garantimos anteriormente, usando uma instrução IF, que teria a decisão de crédito correcta, consoante o valor da variável montante.
As funções criadas pelo utilizador podem, como qualquer função do R, ter argumentos (parâmetros), que são os valores sobre os quais queremos aplicar a nossa função. Cada parâmetro da nossa função é um objecto e como tal tem um nome. Por exemplo, a nossa função crédito tem um só parâmetro que nós resolvemos chamar montante. Quando usamos a função crédito devemos invocá-la com um valor qualquer nesse argumento, que é suposto ser o valor para o qual nós queremos que a função nos dê o resultado que ela calcula. Assim, quando fazemos no prompt do R algo do género ``> crédito('alto')
'', o que estamos a pedir ao R é o resultado de aplicar a função crédito ao valor 'alto'. Este valor ``alto'' é armazenado pelo R no objecto montante que é o nome que demos ao argumento da função, e depois disso as instruções da função são executadas. Neste exemplo concreto, a execução da função consiste em levar a cabo a instrução IF e depois ``mostrar'' o valor do objecto decisão.
Em casos em que as funções têm mais do que um argumento, os seus nomes são separados por vírgulas.
As chamadas a funções (criadas por nós ou já existentes no R) pode ser feita usando o nome dos argumentos, o que pode ser mais claro nalgumas situações, conforme já foi mencionado anteriormente. Por exemplo, poderíamos usar a nossa função crédito do seguinte modo,
> credito(montante='baixo') [1] "conceder"
Após esta pequena explicação sobre a criação de funções em R e também sobre a instrução IF, voltemos ao nosso objectivo que era o de implementar um conjunto de regras de decisão que obtivemos junto dos peritos humanos em concessão de crédito, e que foram apresentadas anteriormente.
Olhando para as regras de decisão podemos observar que elas utilizam informação sobre o montante pedido, o salário do cliente e o facto de o cliente ter ou não conta no banco. É com base nesta informação que a decisão é tomada. Neste contexto, a melhor maneira de implementarmos em R estas regras de decisão seria a de criar uma função parecida com o pequeno exemplo mostrado anteriormente, simplesmente usando 3 argumentos, que seriam a informação necessária para tomar a decisão. Vejamos então como criar essa função,
> crédito <- function(montante, salário, conta) { + if (montante=='médio' & salário=='baixo') + decisão <- 'não conceder' + else if (montante=='médio' & salário=='normal') + decisão <- 'conceder' + else if (montante=='baixo') + decisão <- 'conceder' + else if (montante=='alto' & conta=='sim') + decisão <- 'conceder' + else if (montante=='alto' & conta=='não') + decisão <- 'não conceder' + else decisão <- 'não conceder' + decisão + } > crédito('médio','normal','sim') [1] "conceder"
Ao termos decidido atribuir o mesmo nome à função, fizemos com que o R ``perdesse'' a função que tínhamos introduzido anteriormente. Isto porque, como mencionamos anteriormente, as funções são objectos como outros quaisquer, e a instrução de atribuição é destrutiva, i.e. ao atribuirmos um valor a um objecto ele deixa de armazenar o valor que anteriormente guardava.
Suponhamos agora que temos um conjunto de clientes para os quais pretendemos tomar uma decisão relativamente à concessão de crédito. Poderíamos ter os seus dados num data frame,
> clientes <- edit(data.frame()) > clientes montante salário conta 1 médio baixo sim 2 alto normal não 3 baixo normal sim 4 médio alto não 5 alto alto sim
Na primeira instrução usamos o ``editor de data frames'' do R para preencher os dados dos clientes que são mostrados na segunda instrução.
Vejamos agora como aplicar a função crédito() a todas as linhas deste data frame sem ter que fazer uma chamada à função para tratar cada linha. Uma das maneiras mais fáceis de o fazer é usando um ciclo. A ideia dos ciclos é permitir a execução repetida de instruções. No nosso caso pretendemos chamar a função crédito() para cada uma das linhas do data frame clientes. Vejamos como podemos fazer isso com um ciclo FOR,
> for(linha in 1:nrow(clientes)) { + decisão <- crédito(clientes[linha,'montante'],clientes[linha,'salário'],clientes[linha,'conta']) + cat('Cliente n.',linha,' : ',decisão,'\n') + } Cliente n. 1 : não conceder Cliente n. 2 : não conceder Cliente n. 3 : conceder Cliente n. 4 : não conceder Cliente n. 5 : conceder
Vejamos então qual o funcionamento da instrução FOR. A sua sintaxe genérica é,
A leitura informal desta instrução é a seguinte: executar instruções tantas vezes quantos os elementos de vector. Em cada execução variável vai tomar o valor do respectivo elemento de vector.
No exemplo concreto apresentado acima, a variável linha vai tomar os valores do vector formado pelos números de 1 a nrow(clientes). Esta última função, como mencionado anteriormente, dá como resultado o número de linhas, neste caso do data frame clientes, ou seja 5 no nosso exemplo concreto. Portanto a variável linha vai tomar os valores 1, 2, ...até 5, em cada uma das execuções das instruções que estão contidas no ciclo. As instruções contidas no ciclo são delimitadas pelas chavetas, e no nosso exemplo são duas. As chavetas servem para, como vimos quando criamos a nossa primeira função, delimitar um conjunto de instruções.
A primeira instrução do ciclo FOR vai atribuir ao objecto decisão o resultado de chamar a nossa função crédito com os valores correspondentes à linha ``linha'' do data frame. Como a variável linha vai tomar, sucessivamente, os valores de 1 até 5, isto quer dizer que de cada vez que as instruções do ciclo forem executadas nós vamos obter a decisão para uma nova linha do data frame.
A segunda instrução do ciclo serve para ``comunicar'' ao utilizador a decisão para cada linha (cliente). A instrução CAT pode ser usada para escrever coisas no écran. O resultado da sua execução é a escrita no écran dos argumentos com que foi chamada. Se estes forem strings, são escritos directamente, como é o caso do argumento ``Cliente n.''. Se forem objectos, o que é escrito é o valor do objecto, como é o caso do segundo argumento com que chamamos a função (a variável linha). Existem strings, que são interpretadas de forma especial pela função CAT. Este é o caso da string ``\n
'', que ao ser ``escrita'' tem como resultado fazer o R mudar de linha no écran.