Como obter a frequência de uma variável no SAS

Mencionei o comando proc freq do SAS no post Frequência no R utilizando o pacote Hmisc. Agora, vamos nos aprofundar um pouco e ver o que é possível fazer com este comando.

O proc freq traz a frequência das variáveis que você quer analisar ou fazer qualquer tipo de análise descritiva, ou seja, traz o número de vezes que determinado valor da variável aparece. A sintaxe mais simples do comando é:
proc freq data= base_de_dados; 
    table variavel1 variavel2 ... variaveln; 
run;
Vamos utilizar os dados de crédito german_credit_2 como exemplo.
Assim como já fizemos no R, no post Árvore de Decisão no R, vamos discretizar as variáveis Creditability e CreditAmount, para só depois gerarmos as frequências.
No caso de Creditability, a discretização serve para visualizar melhor quem é bom e quem é mau pagador. No caso de CreditAmount, não conseguimos gerar uma frequência que seja útil, pois a variável é contínua. Pior ainda, se a base for muito grande, o comando muito provavelmente não irá funcionar. Afinal de contas, você estará trazendo o número de vezes que cada valor aparece. Separar em intervalos é o ideal nesse caso.
Note que no meu caso, a base foi importada para o SAS com o nome german_credit_21.
**** discretiza variaveis creditability e amount ****;
data german_credit_21_v2;
    set german_credit_21;

    length d_creditability $4.;
    if creditability = 1 then
    d_creditability = 'good';
    else d_creditability = 'bad';

    length d_creditAmount $9.;
    if creditAmount <= 2500 then
    d_creditAmount = '0000-2500';
    else if creditAmount > 2500 and creditAmount <= 5000 then
    d_creditAmount = '2500-5000';
    else d_creditAmount = '+5000';

run;

**** Frequencia de Creditability e Amount ****;
proc freq data= german_credit_21_v2;
    table d_creditability d_creditAmount;
run;
Na primeira tabela é possível ver a frequência e qual o percentual que representa do todo. Temos 300 clientes maus, sendo que isso representa 30% da nossa população. Além disso, temos também a frequência acumulada. Temos, na segunda tabela, 537 clientes que possuem dívida entre 0 e 2500, e 812 clientes que possuem entre 0 e 5000, sendo que esses clientes representam 81,20% da nossa população.
É possível também obter a frequência cruzada, ou seja, quantos clientes bons e ruins há em cada faixa de dívida:
**** Frequencia Cruzada de Creditability e Amount ****;
proc freq data= german_credit_21_v2 ;
    table d_creditability*d_creditAmount;
run;
E se você quiser deixar a tabela um pouco menos poluída, mantendo apenas a frequência, utilize o norow, nocol e/ou nopercent:
**** Nao traz frequencia por linha, coluna nem percentual ****;
proc freq data= german_credit_21_v2;
    table d_creditability*d_creditAmount / norow nocol nopercent;
run;
Também é possível salvar a tabela de frequência utilizando o out:
**** Cria uma tabela de output chamada tabela_frequencia ****;
proc freq data= german_credit_21_v2;
    table d_creditability*d_creditAmount / out= tabela_frequencia norow nocol nopercent;
run;
E se você quiser ordenar a tabela de frequência, basta acrescentar o order = Freq:
**** Sem ordenar ****;
proc freq data= german_credit_21_v2 ;
    table Guarantors;
run;

**** Ordena pela frequencia ****;
proc freq data= german_credit_21_v2 order= freq;
    table Guarantors;
run;
Assim como fizemos para o Creditability, nós poderíamos ter atribuído nomes ao invés de índices ao Guarantors. Porém, para não alterarmos nossa base original, poderíamos simplesmente aplicar uma formatação:
**** formata o nome ****;
proc format;
   value formatacao
        1 = 'none'
        2 = 'co-applicant'
3 = 'guarantor';
run;

**** gera frequencia e apresenta com nome formatado ****;
proc freq data = german_credit_21;
   tables Guarantors;
   format Guarantors formatacao.; 
run;

Trabalhando com espaços e acentos no SAS

A pior coisa é utilizar campos com espaços em branco e acentos quando se está programando. Não é recomendável e muitas vezes resulta em erros. Porém, às vezes temos que lidar com isso porque alguém construiu uma base com campos dessa forma e não quer mudar.

No SAS é mais fácil lidar com isso, basta acrescentar aspas e a letra n. É bem semelhante quando você utiliza data, só que precisa trocar o ‘d’ por ‘n’.

Vamos supor que você receba os dados da turma de alunos do post Visualizando seus dados: histograma e queira criar uma marcação chamada Reprovação que receberá o número 1 para alunos que tiraram notas menor que 6, basta você criar o campo ‘Reprovação’n:

data dados_v2;
    set dados;
    if notas < 6 then 'Reprovação'n = 1;
    else 'Reprovação'n = 0;
run;

Poderíamos também cair em uma situação que seria necessário criar um campo Data de Atualização com a data do dia de hoje. Para isso, temos duas opções:

## primeira opcao:
data dados_v2;
    set dados;
    'Data de Atualização'n = today();
run;

## segunda opcao:
data dados_v2;
    set dados;
    'Data de Atualização'n = '13JUL2016'd;
run;

Contador e função oposta ao lag no SAS

Veja que exercício de lógica interessante:

Pense em uma base com clientes que possuem vários carros. Você tem linhas contendo o nome do cliente, o nome do carro que ele possui e a data de compra do veículo. Você precisa descobrir quais clientes possuem apenas um carro. A que eu vou demonstrar talvez não seja a mais eficiente, até porque eu precisei de alguns dados ao longo do estudo, como por exemplo qual a ordem em que o veículo foi comprado. Sem isso, não sei se eu teria pensado de outra forma. O legal da forma que foi feito o tratamento é que trabalha o raciocínio lógico e ainda apresenta alguns algoritmos úteis.

O passo a passo utilizado é o seguinte: ordene seus dados pelo nome e data de compra do veículo; crie um contador para representar se é o primeiro carro que aparece do cliente; crie uma coluna adicional para saber qual o contador da linha seguinte. Veja que se você tiver na linha 10 o valor 1 no contador, e na linha 11 você tiver o valor 2, é sinal de que o cliente possui pelo menos dois veículos. No entanto, se você tiver o valor 1 no contador da linha 10 e o valor 1 no contador da linha 11, o seu cliente da linha 10 possui apenas 1 carro.

Foi falado um pouco sobra a função lag() do SAS no post Média Móvel (Bônus: Código SAS e função lag). Ela simplesmente pega o valor da linha de cima da coluna que você quer. Por exemplo, se eu fizer lag(conta_carro), eu vou saber qual o valor da coluna conta_carro da linha anterior a que estou verificando. No caso apresentado nesse post, precisamos de uma função que faça o contrário da lag, ao invés de retornar o valor da linha anterior, deve retornar o valor da linha seguinte. Você verá que não existe uma função pronta para esse caso, mas o script é bem simples e se encontra na Parte 5 do código abaixo.

Veja o exemplo completo que você pode simular e acompanhar o passo a passo explicado acima:
**** Parte 1: Cria uma base de clientes ****;
data base_clientes;
    input nome $ carro $ DtCompra mmddyy8.;
    cards;
    Andre Onix 02/05/15
    Andre Palio 12/10/13
    Andre Uno 01/05/14
    Paulo Idea 10/06/15
    Jose Vectra 01/04/16
    Jose Eco 12/05/16 28
    Maria Corsa 10/25/14
    Maria Celta 02/01/16
    Renata Onix 02/09/12
    Renata Palio 06/05/11
    Renata Fit 02/11/16
    Renata Civic 05/22/14
    Michael Fit 07/13/15
    Bianca Clio 09/05/15
    Thais March 08/05/16
    Renan Palio 11/05/13
    Renan Gol 11/05/14
    ;
run;

**** Parte 2: Trata a variavel data ****;
data base_clientes_v2;
    set base_clientes;
    format DtCompra date9.;
run;

**** Parte 3: Ordena pelo nome do cliente e data da compra do veiculo ****;
proc sort data= base_clientes_v2; by nome DtCompra; run;

**** Parte 4: Cria um contador do numero de carros = conta qtas vezes o nome aparece****;
data base_clientes_v3;
    set base_clientes_v2;
    by nome;
    primeiro = first.nome;
    ultimo = last.nome;
    if primeiro = 1 then
    conta_carro = 1;
    else conta_carro+1;
run;

**** Parte 5: Cria uma coluna com o valor da linha seguinte da coluna conta_carro ****;
data base_clientes_v4;
    recno=_n_+1;
    set base_clientes_v3 end=last;
    if not last
    then set base_clientes_v3 (keep=conta_carro rename=(conta_carro=next_conta_carro)) point=recno;
    else call missing(next_conta_carro);
run;

**** Parte 6: Mantem apenas quem tem conta_carro = 1 e que a proxima linha eh 1 tb ****;
data clientes_com_um_carro;
    set base_clientes_v4;
    if conta_carro = 1 and next_conta_carro = 1;
run;

Seja mais ágil utilizando firstobs e inobs no SAS

Quando se trabalha com milhões de dados, qualquer tratamento vai consumir alguns bons minutos – ou até horas – do seu dia. Ou seja, se você escrever seu código, rodar e ele apresentar algum erro, lá se vai uma parte do seu dia. Como ninguém é perfeito, esse processo de escrever + executar + problemas se repete várias vezes ao longo do dia. Logo, você vai gastar horas para escrever um código, muitas vezes simples, e vai passar horas ocioso. Porque você vai escrever um trecho do código, vai colocar para rodar, vai esperar alguns minutos e aí sim vai resolver um problema. Para depois fazer isso de novo. E aí segue seu dia, chato e improdutivo.

Evitar o problema descrito é mais simples do que parece. Ao menos parte dele é solucionado testando em uma amostra. Por exemplo, você quer converter o formato de data da sua base. Não escreva o código e fique lá 20 minutos rodando enquanto você não faz nada, ou finge fazer. Teste essa conversão em 10 linhas da base de dados e veja o que ocorre. Se o processo for grande, envolver várias etapas, siga testando com essas 10 linhas fazendo todos os tratamentos, seja enriquecendo a base com dados de outras fontes, seja tratando os campos. Para pegar apenas 10 linhas, você tem as duas opções abaixo, uma para quem usa data step e outra para quem usa proc sql:

* primeira opcao via data step
* seleciona do primeiro ao decimo elemento
* util caso voce queira selecionar elementos do meio da tabela;
data seleciona_amostra;
    set base (firstobs = 1 obs = 10);
run;

* segunda opcao via proc sql;
proc sql inobs = 10;
    select * from base;
run;

Bem simples. Desta forma não precisa mais escrever um trecho, deixar rodando por 1h para só depois checar se deu certo.

Espero que tenha ajudado. Dúvidas, sugestões ou críticas é só escrever aí embaixo.

Machine Learning no Nerdcast

Quem diria que meu podcast preferido faria um episódio de uma das minhas áreas de estudos preferida. Para ouvir é só clicar no link:

NerdTech 5 – Machine Learning

Para quem não conhece muito, a definição de aprendizado de máquina (Wikipedia):

A aprendizagem automática ou aprendizado de máquina (em inglês: “machine learning”) é um sub-campo da inteligência artificial dedicado ao desenvolvimento de algoritmos e técnicas que permitam ao computador aprender, isto é, que permitam ao computador aperfeiçoar seu desempenho em alguma tarefa.

Frequência no R utilizando o pacote Hmisc

Como estou acostumado com o proc freq do SAS para validar algumas informações e ver se faz sentido o que estou fazendo, procurei algo semelhante no R. Encontrei o describe() do pacote Hmisc. Claro que deve existir outro pacote que faz isso, mas esse foi o primeiro que encontrei.

O proc freq do SAS, como o nome já indica, calcula a frequência de seus dados. Por exemplo, se você tem uma base que contém homem e mulher, você pode utilizar um código parecido com esse:

proc freq data = base_de_dados; table sexo; run;

Onde base_de_dados seria o nome da sua base e sexo o nome da coluna contendo o sexo das pessoas.Esse código retorna para você o número e a porcentagem de pessoas de cada sexo. Analogamente, você poderia fazer o seguinte código no R:

install.packages("Hmisc");
library("Hmisc");
describe(base_de_dados$sexo);

E o resultado seria o mesmo.

Para não ficar muito abstrato, segue um exemplo com a base de dados german_credit_21 já utilizada no post Árvore de Decisão no R. Eu tinha discretizado algumas variáveis naquela época, e estava refazendo isso hoje. Apenas para garantir que tinha feito da forma correta, utilizei o describe(). Veja que ele me retornou que 30% da minha base possui Creditability ‘bad’ e 70% ‘good’. Além disso, pude ver também como foi dividido o CreditAmount. Bem simples, uma função só e você já tem uma bela descrição para trabalhar:

install.packages("Hmisc");
library("Hmisc");
describe(dados);