1 Introdução

Nem sempre o auditor disporá dos dados necessários à realização de seus trabalhos armazenados em uma planilha eletrônica ou em arquivos nos quais possam, de pronto, serem importados pelo aplicativo que esteja utilizando para realizar a análise destes dados.

Às vezes, os dados estarão contidos em arquivos texto contendo a imagem de um relatório produzido pelo sistema contábil utilizado pela entidade na qual esteja realizando a auditoria.

Assim, o objetivo deste documento é apresentar algumas dicas de como os dados contidos nestes relatórios podem ser importados com o R. Para tanto, apresentaremos um passo a passo para a importação de seis relatórios com “layouts” distintos o que, acreditamos, dará ao leitor uma visão geral de como importar relatórios usando o R.

Os arquivos contendo os relatórios utilizados neste documento bem como outros cuja importação deixaremos a cargo do leitor como exercício, serão disponibilizados no arquivo Relatorios.zip.


2 Visão Geral da Importação de Relatórios com o R

Neste capítulo apresentaremos um passo a passo bem datalhado do processo de importação de relatórios com o R. É importante destacar que não existe uma regra rígida a ser seguida já que, como veremos, o processo de importação é extremamente dependente da estrutura/“layout” do relatório. Ainda assim, acreditamos que cinco etapas estarão presentes neste processo, as quais elencamos a seguir:

  • Importar para o R o arquivo texto
  • Identificar e extrair as linhas de interesse
  • Dividir as linhas nos campos desejados
  • Juntar os campos em um data frame
  • Tratar os dados, se necessário

Para ilustrar cada uma destas etapas, utilizaremos o relatório contido no arquivo Relatorio1.txt cujo “layout” apresentamos a seguir:

__ SIAFEM2005-EXEORC,CONSULTAS,LISNE ( LISTA NOTA DE EMPENHO ) _______________
CONSULTA EM 05/01/2007 AS 10:37                       USUARIO : MARCOS
UNIDADE GESTORA : 180100  - SECRETARIA DE ESTADO DE EDUCACAO
GESTAO          : 00001   - TESOURO
LICITACAO:   TODAS            FONTE: TODAS      EMPENHO:   TODAS
NATUREZA :   TODAS
NUMERO        EVENTO   CREDOR                                         V A L O R
-------------------------------------------------------------------------------
2005NE00001   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               31.425,26
2005NE00002   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            3.560.659,03
2005NE00003   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            2.732.876,84
2005NE00004   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           21.711.652,87
2005NE00005   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           11.153.100,00
2005NE00006   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.           15.839,20
2005NE00007   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.            3.016,96
2005NE00008   EMPENHO  CAIXA ECONOMICA FEDERAL                           964,39
2005NE00009   EMPENHO  CAIXA ECONOMICA FEDERAL                           225,25
2005NE00010   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           22.575.629,00
2005NE00011   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               10.959,26
2005NE00012   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                  370,36
2005NE00013   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                1.111,06
_______________________________________________________________________________
( 2005NE _____ ) PARA DETALHAR INFORME O NR. DO EMPENHO.

Este é o relatório dos empenhos emitidos produzido pelo SIAFEM1 com a transação >lisne e consiste de uma relação/listagem dos empenhos emitidos em determinado período por uma determinada Unidade Gestora.

De posse de um arquivo texto como o apresentado acima, como podemos extrair os dados de interesse usando o R?

De uma forma geral o “layout” de um relatório consiste de cabeçalho, rodapé e corpo. Usualmente é no corpo do relatório que estarão os dados que desejamos extrair.

No relatório apresentado acima, estas partes estão bem definidas: tudo o que está acima da linha pontilhada constitui o cabeçalho do relatório, contendo diversas informações sobre os dados apresentados; o que está abaixo da linha contínua é o rodapé, e tudo o que se encontra entre estas linhas é o corpo do relatório, onde se encontram os dados nos quais estamos interessados.

Como já mencionado anteriormente, a abordagem utilizada para a importação dos dados irá depender da estrutura do relatório, sendo certo que alguns relatórios, de estrutura mais simples, serão mais simples de importar do que outros, de estrutura mais complexa.

Embora o relatório que utilizaremos para ilustrar o procedimento de importação seja bem simples, as ideias básicas são as mesmas para a importação de relatórios mais complexos.

O relatório acima apresenta uma estrutura muito simples. Examinando um pouco mais o documento, vemos que o corpo do relatório encontra-se bem estruturado. Cada registro está contido em uma única linha do relatório e os campos estão bem delimitados2.

As linhas do arquivo que nos interessam e que constituem o corpo do relatório, são todas aquelas que iniciam com o número da nota de empenho. Todas as demais linhas podem ser desprezadas.


2.1 Passo 1: Importação do arquivo texto

Para importar todo o arquivo, linha a linha, usamos a função readLines().

# Define o diretório de trabalho
setwd("C:\\Users\\Marcos\\OneDrive\\Marcos\\GitHubTutoriais\\Importacao de Relatorios")

# Importa o arquivo texto
r1 <- readLines("Relatorio1.txt")

# Examinar o tipo de dados
class(r1)
## [1] "character"

Para inspecionar o conteúdo de r1:

r1
##  [1] "__ SIAFEM2005-EXEORC,CONSULTAS,LISNE ( LISTA NOTA DE EMPENHO ) _______________" 
##  [2] "CONSULTA EM 05/01/2007 AS 10:37                       USUARIO : MARCOS"         
##  [3] "UNIDADE GESTORA : 180100  - SECRETARIA DE ESTADO DE EDUCACAO"                   
##  [4] "GESTAO          : 00001   - TESOURO"                                            
##  [5] "LICITACAO:   TODAS            FONTE: TODAS      EMPENHO:   TODAS"               
##  [6] "NATUREZA :   TODAS"                                                             
##  [7] "NUMERO        EVENTO   CREDOR                                         V A L O R"
##  [8] "-------------------------------------------------------------------------------"
##  [9] "2005NE00001   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               31.425,26"
## [10] "2005NE00002   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            3.560.659,03"
## [11] "2005NE00003   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            2.732.876,84"
## [12] "2005NE00004   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           21.711.652,87"
## [13] "2005NE00005   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           11.153.100,00"
## [14] "2005NE00006   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.           15.839,20"
## [15] "2005NE00007   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.            3.016,96"
## [16] "2005NE00008   EMPENHO  CAIXA ECONOMICA FEDERAL                           964,39"
## [17] "2005NE00009   EMPENHO  CAIXA ECONOMICA FEDERAL                           225,25"
## [18] "2005NE00010   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           22.575.629,00"
## [19] "2005NE00011   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               10.959,26"
## [20] "2005NE00012   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                  370,36"
## [21] "2005NE00013   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                1.111,06"
## [22] "_______________________________________________________________________________"
## [23] "( 2005NE _____ ) PARA DETALHAR INFORME O NR. DO EMPENHO."                       
## [24] ""

Como pode ser visto, r1 é um vetor de caracteres onde cada elemento do vetor contém uma string correspondente a cada linha do relatório a ser importado.


2.2 Passo 2: Identificação e seleção das linhas de interesse

Esta etapa consiste em excluir do vetor r1 as linhas desnecessárias, deixando apenas aquelas que nos interessam.

A abordagem que utilizaremos para selecionar as linhas de interesse será com o uso de expressões regulares3.

Nesta abordagem, devemos elaborar uma expressão regular que “case” com algum dos dados contidos nas linhas que desejamos extrair.

No relatório que estamos examinando, as linhas que nos interessam são as que iniciam com o número da nota de empenho. Os números das notas de empenho tem uma estrutura fixa que consiste de quatro dígitos seguido da string “NE”" seguido de cinco dígitos, ou seja: ddddNEddddd.

Uma expressão regular que “casa” com estas linhas é a seguinte: "^\\d{4}NE\\d{5}".

No R, a função grep() retorna o indice no vetor de dados (r1) onde a linha “casou” com a expressão regular ou com o argumento value= definido como verdadeiro (TRUE), retorna a string casada.

r1 <- grep("^\\d{4}NE\\d{5}", r1, value=TRUE)
r1
##  [1] "2005NE00001   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               31.425,26"
##  [2] "2005NE00002   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            3.560.659,03"
##  [3] "2005NE00003   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO            2.732.876,84"
##  [4] "2005NE00004   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           21.711.652,87"
##  [5] "2005NE00005   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           11.153.100,00"
##  [6] "2005NE00006   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.           15.839,20"
##  [7] "2005NE00007   EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.            3.016,96"
##  [8] "2005NE00008   EMPENHO  CAIXA ECONOMICA FEDERAL                           964,39"
##  [9] "2005NE00009   EMPENHO  CAIXA ECONOMICA FEDERAL                           225,25"
## [10] "2005NE00010   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO           22.575.629,00"
## [11] "2005NE00011   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO               10.959,26"
## [12] "2005NE00012   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                  370,36"
## [13] "2005NE00013   EMPENHO  SECRETARIA DE ESTADO DE EDUCACAO                1.111,06"

Como podemos ver, tudo que era desnecessário foi excluído, restando apenas o que nos interessa.


2.3 Passo 3: Dividir as linhas nos campos desejados

O relatório apresenta claramente 4 campos: número da nota de empenho, evento, credor e valor do empenho. A divisão das linhas selecionadas é feita com a função substr(), conforme mostrado a seguir:

NumNE  <- substr(r1, 1, 11)
Evento <- substr(r1, 12, 22)
Credor <- substr(r1, 23, 60)
Valor  <- substr(r1, 61, 79)
NumNE
##  [1] "2005NE00001" "2005NE00002" "2005NE00003" "2005NE00004" "2005NE00005"
##  [6] "2005NE00006" "2005NE00007" "2005NE00008" "2005NE00009" "2005NE00010"
## [11] "2005NE00011" "2005NE00012" "2005NE00013"
Evento
##  [1] "   EMPENHO " "   EMPENHO " "   EMPENHO " "   EMPENHO " "   EMPENHO "
##  [6] "   EMPENHO " "   EMPENHO " "   EMPENHO " "   EMPENHO " "   EMPENHO "
## [11] "   EMPENHO " "   EMPENHO " "   EMPENHO "
Credor
##  [1] " SECRETARIA DE ESTADO DE EDUCACAO     "
##  [2] " SECRETARIA DE ESTADO DE EDUCACAO     "
##  [3] " SECRETARIA DE ESTADO DE EDUCACAO     "
##  [4] " SECRETARIA DE ESTADO DE EDUCACAO     "
##  [5] " SECRETARIA DE ESTADO DE EDUCACAO     "
##  [6] " INSTITUTO NACIONAL DE SEGURO SOCIAL. "
##  [7] " INSTITUTO NACIONAL DE SEGURO SOCIAL. "
##  [8] " CAIXA ECONOMICA FEDERAL              "
##  [9] " CAIXA ECONOMICA FEDERAL              "
## [10] " SECRETARIA DE ESTADO DE EDUCACAO     "
## [11] " SECRETARIA DE ESTADO DE EDUCACAO     "
## [12] " SECRETARIA DE ESTADO DE EDUCACAO     "
## [13] " SECRETARIA DE ESTADO DE EDUCACAO     "
Valor
##  [1] "          31.425,26" "       3.560.659,03" "       2.732.876,84"
##  [4] "      21.711.652,87" "      11.153.100,00" "          15.839,20"
##  [7] "           3.016,96" "             964,39" "             225,25"
## [10] "      22.575.629,00" "          10.959,26" "             370,36"
## [13] "           1.111,06"

Com este procedimento, repartimos cada elemento do vetor r1 em quatro novos vetores, cada um contendo informações relativa a um campo do registro.

Para separar os campos é necessário identificarmos onde se inicia e termina a informação relativa a cada campo. Assim, a informação relativa ao número do empenho inicia na posição 1 da linha e termina na posição 11, a informação relativa ao evento vai da posição 12 à 22 e assim sucessivamente para os demais campos.


2.4 Passo 4: Juntar os campos em um data frame

Agora que temos 4 vetores (NumNE, Evento, Credor e Valor) cada um contendo informações acerca dos campos desejados, podemos reuní-los em um data frame da seguinte forma:

r1 <- data.frame(NumNE, Evento, Credor, Valor)
r1
##          NumNE      Evento                                 Credor
## 1  2005NE00001    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 2  2005NE00002    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 3  2005NE00003    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 4  2005NE00004    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 5  2005NE00005    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 6  2005NE00006    EMPENHO   INSTITUTO NACIONAL DE SEGURO SOCIAL. 
## 7  2005NE00007    EMPENHO   INSTITUTO NACIONAL DE SEGURO SOCIAL. 
## 8  2005NE00008    EMPENHO   CAIXA ECONOMICA FEDERAL              
## 9  2005NE00009    EMPENHO   CAIXA ECONOMICA FEDERAL              
## 10 2005NE00010    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 11 2005NE00011    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 12 2005NE00012    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
## 13 2005NE00013    EMPENHO   SECRETARIA DE ESTADO DE EDUCACAO     
##                  Valor
## 1            31.425,26
## 2         3.560.659,03
## 3         2.732.876,84
## 4        21.711.652,87
## 5        11.153.100,00
## 6            15.839,20
## 7             3.016,96
## 8               964,39
## 9               225,25
## 10       22.575.629,00
## 11           10.959,26
## 12              370,36
## 13            1.111,06


2.5 Passo 5: Tratamento dos dados

A importação dos dados está quase completa, faltando apenas alguns retoques tais como: extrair os caracteres brancos dos dados e converter a coluna Valor para o formato numérico. Isto pode ser feito da seguinte forma:

# Remoção dos caracteres brancos do campo Credor
r1$Credor <- gsub("^ +", "", r1$Credor)
r1$Credor <- gsub(" +$", "", r1$Credor)

O primeiro comando remove os caracteres brancos existentes no início dos dados e o segundo remove os caracteres brancos existentes no fim.

Ilustramos o procedimento apenas para a coluna Credor do data frame, mas o mesmo procedimento deve ser realizado em cada uma das 3 colunas restantes.

Para simplificar o procedimento, é conveniente definir uma função para remover os espaços em branco. Chamaremos esta função de removeBrancos() e a definiremos da seguinte forma:

 removeBrancos <- function(x) gsub("^ +", "", gsub(" +$", "", x))

Vejamos agora como converter o campo Valor para o formato numérico.

r1$Valor <- gsub("\\.", "", r1$Valor)
r1$Valor <- gsub(",", ".", r1$Valor)
r1$Valor <- as.numeric(r1$Valor)

Os comandos acima fazem, respectivamente o seguinte: remove os pontos, substitui a vírgula por ponto e converte para o formato numérico.

Aqui também parece conveniente definirmos uma função para simplificar este procedimento. Vamos chamá-la de convNum() e a definiremos da seguinte forma:

convNum <- function(x) as.numeric(gsub(",", ".", gsub("\\.", "", x)))

Após estes procedimentos os dados já estão prontos para análise. Vejamos:

 str(r1)
## 'data.frame':    13 obs. of  4 variables:
##  $ NumNE : Factor w/ 13 levels "2005NE00001",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ Evento: Factor w/ 1 level "   EMPENHO ": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Credor: chr  "SECRETARIA DE ESTADO DE EDUCACAO" "SECRETARIA DE ESTADO DE EDUCACAO" "SECRETARIA DE ESTADO DE EDUCACAO" "SECRETARIA DE ESTADO DE EDUCACAO" ...
##  $ Valor : num  31425 3560659 2732877 21711653 11153100 ...
r1
##          NumNE      Evento                               Credor
## 1  2005NE00001    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 2  2005NE00002    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 3  2005NE00003    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 4  2005NE00004    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 5  2005NE00005    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 6  2005NE00006    EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.
## 7  2005NE00007    EMPENHO  INSTITUTO NACIONAL DE SEGURO SOCIAL.
## 8  2005NE00008    EMPENHO               CAIXA ECONOMICA FEDERAL
## 9  2005NE00009    EMPENHO               CAIXA ECONOMICA FEDERAL
## 10 2005NE00010    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 11 2005NE00011    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 12 2005NE00012    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
## 13 2005NE00013    EMPENHO      SECRETARIA DE ESTADO DE EDUCACAO
##          Valor
## 1     31425.26
## 2   3560659.03
## 3   2732876.84
## 4  21711652.87
## 5  11153100.00
## 6     15839.20
## 7      3016.96
## 8       964.39
## 9       225.25
## 10 22575629.00
## 11    10959.26
## 12      370.36
## 13     1111.06
 summary(r1$Valor)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##      225     1111    15839  4753679  3560659 22575629

Em linhas gerais, estas são as etapas para a importação de relatórios.

Como no R uma mesma tarefa pode ser executada de diversas formas, não achamos que a metodologia aqui apresentada seja a mais elegante ou mesmo a mais eficiente. Apenas reflete nosso atual estágio de compreensão do ambiente R, sendo possível a existência de outras abordagens mais eficientes, elegantes ou mesmo mais simples.

Apenas para consolidar a metodologia apresentada, vamos realizar a importação de um outro relatório (Relatorio2.txt) cuja estrutura é similar à do relatório utilizado no exemplo anterior.

O layout do relatório é apresentado a seguir:

                    STOCK LEVEL REPORT                          

                    QUARTER 1    QUARTER 2    QUARTER 3    QUARTER 4
BEGINNING STOCK  |       1075          400          500          550 
REQUIRMENTS      |        675            0          200          100
PLANNED RECEIPT  |          0          100          250            0 
ENDING STOCK     |        400          500          550          450

Como pode ser visto, cada registro ocupa apenas uma linha, de forma que a abordagem apresentada anteriormente poderá ser aplicada.

## Passo 1 - Leitura do arquivo do dados
r2 <- readLines("Relatorio2.txt")
## Warning in readLines("Relatorio2.txt"): linha final incompleta encontrada
## em 'Relatorio2.txt'
r2
## [1] "                    STOCK LEVEL REPORT                          "     
## [2] ""                                                                     
## [3] "                    QUARTER 1    QUARTER 2    QUARTER 3    QUARTER 4" 
## [4] "BEGINNING STOCK  |       1075          400          500          550 "
## [5] "REQUIRMENTS      |        675            0          200          100" 
## [6] "PLANNED RECEIPT  |          0          100          250            0 "
## [7] "ENDING STOCK     |        400          500          550          450"
## Passo 2 - Identificar e extrair as linhas de interesse
r2 <- grep("^\\w", r2, value=TRUE)
r2
## [1] "BEGINNING STOCK  |       1075          400          500          550 "
## [2] "REQUIRMENTS      |        675            0          200          100" 
## [3] "PLANNED RECEIPT  |          0          100          250            0 "
## [4] "ENDING STOCK     |        400          500          550          450"

Agora vamos adotar uma variação do que foi feito anteriormente.

A parte do processo de importação relativa a dividr as linhas nos campos desejados e montar um data frame será comum à maioria dos formatos de relatórios com os quais um auditor possa se deparar. Assim, vamos definir uma pequena função para gerar um data frame a partir de um vetor de caracteres contendo as linhas com os dados de interesse. Esta funcão irá separar as linhas do relatório nos campos desejados. Chamaremos esta função de txt2df() e a definiremos da seguinte forma:

txt2df <- function(x, colunas, inicio, fim){
   ## x : vetor de caracteres contendo as linhas do arquivo
   ## colunas : vetor com os nomes dos campos
   ## inicio : vetor numérico contendo o número da coluna inicial do campo
   ## fim : vetor numérico contendo o número da coluna final do campo
   df <- matrix(nr=length(x), nc=length(colunas))
   for(k in colunas){
       i <- which(colunas == k)
       k <- substr(x, inicio[i], fim[i])
       df[,i] <- k
   }
   df <- as.data.frame(df)
   names(df) <- colunas
   df
}

Definida esta função, os passos 3 e 4 poderão ser reunidos em uma só etapa da seguinte forma:

## Passo 3 - Dividir as linhas nos campos desejados e
## Passo 4 - Juntas os campos em data frame
r2 <- txt2df(r2,
             colunas=c("NomeLinha", "Q1", "Q2", "Q3", "Q4"),
             inicio=c( 1, 19, 30, 43, 56),
             fim=c(17, 29, 42, 55, 68))
r2
##           NomeLinha          Q1            Q2            Q3            Q4
## 1 BEGINNING STOCK          1075           400           500           550
## 2 REQUIRMENTS               675             0           200           100
## 3 PLANNED RECEIPT             0           100           250             0
## 4 ENDING STOCK              400           500           550           450

Conforme pode ser visto dos argumentos fornecidos à função verifica-se que o campo NomeLinha inicia na coluna 1 e termina na coluna 17, o campo Q1 inicia na coluna 19 e termina na coluna 29 e assim sucessivamente para os demais campos.

A última etapa consiste em realizar a limpeza/tratamento dos dados. Devemos excluir os brancos e converter os valores dos campos Q1, Q2, Q3 e Q4 para o formato numérico.

## Passo 5 - Tratar os dados, se necessário
r2 <- as.data.frame(sapply(r2, removeBrancos), stringsAsFactors=FALSE)
r2[, 2:5] <- as.data.frame(sapply(r2[, 2:5], as.numeric))
r2
##         NomeLinha   Q1  Q2  Q3  Q4
## 1 BEGINNING STOCK 1075 400 500 550
## 2     REQUIRMENTS  675   0 200 100
## 3 PLANNED RECEIPT    0 100 250   0
## 4    ENDING STOCK  400 500 550 450

Para que o conjunto de dados fique com uma aparência bem semelhante ao relatório, podemos atribuir às linhas do data frame os nomes contidos na coluna NomeLinha. Isto pode ser feito da seguinte forma:

rownames(r2) <- r2$NomeLinha
r2$NomeLinha <- NULL
str(r2)
## 'data.frame':    4 obs. of  4 variables:
##  $ Q1: num  1075 675 0 400
##  $ Q2: num  400 0 100 500
##  $ Q3: num  500 200 250 550
##  $ Q4: num  550 100 0 450
r2
##                   Q1  Q2  Q3  Q4
## BEGINNING STOCK 1075 400 500 550
## REQUIRMENTS      675   0 200 100
## PLANNED RECEIPT    0 100 250   0
## ENDING STOCK     400 500 550 450

Pronto. Os dados já se encontram prontos para análise. É oportuno lembrar que havendo a necessidade de se importar sempre os mesmos relatórios, todos estes passos podem ser “empacotados” em uma função que receberá o arquivo texto contendo o relatório como argumento e retornará um data frame com os dados prontos para análise.


3 Outros Exemplares de Relatórios

Neste tópico, apresentaremos algumas dicas de como importar relatórios com “layout” um pouco mais complexos do que os apresentados anteriormente.

Nosso objetivo é, que observando as soluções apresentadas, o leitor possa adaptá-las ao seu caso específico.


3.1 Relatório 3

O próximo relatório (Relatorio3.txt), cujo “layout” apresentamos a seguir, distingue-se do primeiro e do segundo pelo fato de que cada registro ocupa duas linhas do corpo do relatório.


==========================================================
Employee Hours Report                             Page:  1
Company: XYZ Company                 Date Printed:02/02/99
==========================================================
Employee           Date     Regular Hours/    Other Hours/
                            Overtime              Code
----------------------------------------------------------
Doe, John        01/23/99     23.3                 1.6
                              15.6                  AC
----------------------------------------------------------
Wacks, Gene      02/01/99      1.6                 0.0
                               0.0
----------------------------------------------------------
Peters, Kate     01/30/99     40.3                 3.4
                               1.1                  VA
----------------------------------------------------------

Em relatórios com esta característica, ou seja, nos quais cada registro ocupa uma quantidade fixa de linhas (2, 3, 4, etc.), será necessário estabelecer o que denominaremos de “linhas âncoras”, ou seja, linhas que servirão de base para a extração dos dados contidos nas demais linhas que constituem o registro.

No caso deste relatório, as linhas âncoras serão as linhas que contém o nome dos funcionários. A partir destas linhas, podemos buscar as informações relativas às “horas extras” (Overtime) e “código” (Code) que constam da linha seguinte.

Desejamos que os dados, após serem importados, estejam no seguinte formato:

   Empregado  Data      HorasRegulares HorasExtras OutrasHoras Codigo
Doe, John     01/23/99  23.3           16.6         1.6        AC
Wacks, Gene   02/01/99   1.6            0.0         0.0 
Peters, Kate  01/30/99  40.3            1.4         3.4        VA

Apresentaremos o código para a importação do relatório que está contido no arquivo Relatorio3.txt:

## Passo 1 - Importar as linhas do arquivo
r3 <- readLines("Relatorio3.txt")
## Warning in readLines("Relatorio3.txt"): linha final incompleta encontrada
## em 'Relatorio3.txt'
r3
##  [1] "=========================================================="
##  [2] "Employee Hours Report                             Page:  1"
##  [3] "Company: XYZ Company                 Date Printed:02/02/99"
##  [4] "=========================================================="
##  [5] "Employee           Date     Regular Hours/    Other Hours/"
##  [6] "                            Overtime              Code"    
##  [7] "----------------------------------------------------------"
##  [8] "Doe, John        01/23/99     23.3                 1.6"    
##  [9] "                              15.6                  AC"    
## [10] "----------------------------------------------------------"
## [11] "Wacks, Gene      02/01/99      1.6                 0.0"    
## [12] "                               0.0"                        
## [13] "----------------------------------------------------------"
## [14] "Peters, Kate     01/30/99     40.3                 3.4"    
## [15] "                               1.1                  VA"    
## [16] "----------------------------------------------------------"
## Passo 2 - Extrair as linhas de interesse
## (linhas do corpo do relatório)
idxLinhasAncoras <- grep("^\\w+, \\w+ +", r3)
linha1 <- r3[idxLinhasAncoras]
linha2 <- r3[idxLinhasAncoras + 1] 
linha1
## [1] "Doe, John        01/23/99     23.3                 1.6"
## [2] "Wacks, Gene      02/01/99      1.6                 0.0"
## [3] "Peters, Kate     01/30/99     40.3                 3.4"
linha2
## [1] "                              15.6                  AC"
## [2] "                               0.0"                    
## [3] "                               1.1                  VA"

Os dados que nos interessam estão contidos nos vetores linha1 e linha2. O vetor linha1 conté as informações relativas ao nome do empregado, data, horas regulares e outras horas, enquanto o vetor linha2 contém informações relativas a horas extras e código.

Falta agora o terceiro passo, que é separar adequadamente os campos. Para a realização desta etapa é necessário identificar em que coluna começa e termina cada campo.

Uma maneira de se fazer isso é abrir o arquivo em um editor de texto e contar o início e fim de cada campo.

## Passos 3 e 4 : Dividir as linhas nos campos desejados
## Campos na linha 1
df1 <- txt2df(linha1,
              colunas=c("Employee", "Date", "RegularHours", "OtherHours"),
              inicio=c( 1, 18, 26, 35),
              fim=c(17, 25, 34, 58))
df1
##            Employee     Date RegularHours           OtherHours
## 1 Doe, John         01/23/99         23.3                  1.6
## 2 Wacks, Gene       02/01/99          1.6                  0.0
## 3 Peters, Kate      01/30/99         40.3                  3.4
# Campos na linha 2
df2 <- txt2df(linha2,
              colunas=c("Overtime", "Code"),
              inicio=c(26, 35),
              fim=c(34, 58))
df2
##    Overtime                 Code
## 1      15.6                   AC
## 2       0.0                     
## 3       1.1                   VA
## Agrupar os dois data frames em um
r3 <- cbind(df1, df2)
r3
##            Employee     Date RegularHours           OtherHours  Overtime
## 1 Doe, John         01/23/99         23.3                  1.6      15.6
## 2 Wacks, Gene       02/01/99          1.6                  0.0       0.0
## 3 Peters, Kate      01/30/99         40.3                  3.4       1.1
##                   Code
## 1                   AC
## 2                     
## 3                   VA
## Passo 5: limpar/preparar os dados
r3$Employee <- removeBrancos(r3$Employee)
r3$Date <- as.Date(removeBrancos(r3$Date), "%m/%d/%y")
r3$RegularHours <- as.numeric(removeBrancos(r3$RegularHours))
r3$Overtime <- as.numeric(removeBrancos(r3$Overtime))
r3$OtherHours <- as.numeric(removeBrancos(r3$OtherHours))
r3$Code <- removeBrancos(r3$Code)
str(r3)
## 'data.frame':    3 obs. of  6 variables:
##  $ Employee    : chr  "Doe, John" "Wacks, Gene" "Peters, Kate"
##  $ Date        : Date, format: "1999-01-23" "1999-02-01" ...
##  $ RegularHours: num  23.3 1.6 40.3
##  $ OtherHours  : num  1.6 0 3.4
##  $ Overtime    : num  15.6 0 1.1
##  $ Code        : chr  "AC" "" "VA"
r3
##       Employee       Date RegularHours OtherHours Overtime Code
## 1    Doe, John 1999-01-23         23.3        1.6     15.6   AC
## 2  Wacks, Gene 1999-02-01          1.6        0.0      0.0     
## 3 Peters, Kate 1999-01-30         40.3        3.4      1.1   VA


3.2 Relatório 4

Vamos ao quarto exemplar de relatório, um pouco mais complexo que os anteriores, contido no arquivo Relatorio4.txt cujo “layout” é mostrado a seguir:

Product Sales Report                              Page:  1
Company: XYZ Company          For the month ended:09/30/98
==========================================================
Sales Person       Date         Qty        Price     Class
----------------------------------------------------------
            Product: Tape 

Doe, John        09/23/98     1,120     11,225.60       AC
                 09/24/98    21,123    233,112.67       VA
                 09/27/98        23         32.22       CS
Wacks, Gene      09/01/98     3,766      3,455.55       AC
Peters, Kate     09/30/98     4,000      3,999.44       VA
                 09/02/98     1,653      6,775.55       OT

            Product: Stapler

Perrol, Barb     09/13/98     3,320     11,225.60       AC
Zonker, Pete     09/11/98       766        453.53       AC
                 09/20/98     1,455      2,543.43       VA
Perkins, Joe     09/12/98        23         23.55       OT

No relatório acima temos a seguinte característica que o distingue dos três anteriores e por isso exigirá uma nova estratégia para sua importação: os registros estão agrupados por produto e cada vendedor (Sales Person) possui uma quantidade variável de registros a ele associados.

Gostaríamos que os dados, após importados, estivessem no seguinte formato:

Product SalesPerson  Date     Qty     Price       Class
Tape    Doe, John    09/23/98  1,120   11,225.60  AC
Tape    Doe, John    09/24/98 21,123  233,112.67  VA
Tape    Doe, John    09/27/98     23       32.22  CS
Tape    Wacks, Gene  09/01/98  3,766    3,455.55  AC
Tape    Peters, Kate 09/30/98  4,000    3,999.44  VA
Tape    Peters, Kate 09/02/98  1,653    6,775.55  OT
Stapler Perrol, Barb 09/13/98  3,320   11,225.60  AC
Stapler Zonker, Pete 09/11/98    766      453.53  AC
Stapler Zonker, Pete 09/20/98  1,455    2,543.43  VA
Stapler Perkins, Joe 09/12/98     23       23.55  OT

Apresentamos a seguir as etapas para a importação deste relatório.

O primeiro passo consiste apenas em realizar a leitura do arquivo.

## Passo 1 - importação do relatório
r4 <- readLines("Relatorio4.txt")
r4 <- r4[r4 != ""] # Excluir linhas em branco...

Considerando que será necessário identificar as linhas correspondentes a cada produto, vamos obter o número das mesmas. Isso pode ser feito da seguinte forma:

## Passo 2 - Identificar e marcar/extrair as linhas de interesse
ancora <- grep("Product:", r4)
ancora
## [1]  6 13

As linhas contendo os dados relativos ao produto Tape vão da linha 7 à 12 e as linhas contendo os dados relativoa ao produto Stapler vão da linha 14 até a última linha de r4, ou seja, a linha 17. Será necessário introduzir este valor na variável ancora acrescido de uma unidade. Mais adiante ficará esclarecida a razão deste acréscimo.

ancora <- c(ancora, length(r4) + 1)
ancora
## [1]  6 13 18

Agora que sabemos onde inicia e termina as linhas contendo os dados de cada produto, vamos construir uma lista na qual cada componente refira-se a um produto e contenha as linhas a ele correspondentes.

Inicialmente vamos obter um vetor com os nomes dos produtos que será utilizado para dar nome aos componentes da lista que iremos criar.

nomesProdutos <- grep("Product:", r4, value=TRUE)
nomesProdutos <- gsub("Product:(.*)", "\\1", nomesProdutos)
nomesProdutos <- removeBrancos(nomesProdutos)
nomesProdutos
## [1] "Tape"    "Stapler"

Agora vamos criar a lista. A criação da lista exigirá um loop pelos nomes dos produtos.

lista <- list()
for(k in nomesProdutos){
  i <- which(nomesProdutos == k)
  inicio <- ancora[i]
  fim <- ancora[i + 1]
  bloco <- r4[(inicio + 1):(fim - 1)]
  lista[[k]] <- txt2df(bloco,
                       colunas=c("SalesPerson", "Date", "Qty", "Price", "Class"),
                       inicio=c( 1, 18, 26, 36, 50),
                       fim=c(17, 25, 35, 49, 58))
}
lista
## $Tape
##         SalesPerson     Date        Qty          Price     Class
## 1 Doe, John         09/23/98      1,120      11,225.60        AC
## 2                   09/24/98     21,123     233,112.67        VA
## 3                   09/27/98         23          32.22        CS
## 4 Wacks, Gene       09/01/98      3,766       3,455.55        AC
## 5 Peters, Kate      09/30/98      4,000       3,999.44        VA
## 6                   09/02/98      1,653       6,775.55        OT
## 
## $Stapler
##         SalesPerson     Date        Qty          Price     Class
## 1 Perrol, Barb      09/13/98      3,320      11,225.60        AC
## 2 Zonker, Pete      09/11/98        766         453.53        AC
## 3                   09/20/98      1,455       2,543.43        VA
## 4 Perkins, Joe      09/12/98         23          23.55        OT

Como podemos ver, temos uma lista onde cada componente consiste de um data frame contendo os dados relativos a um produto.

O próximo passo é juntar estes data frames e criar uma nova coluna para identificar a que produto pertence cada registro. Os comandos a seguir mostram como realizar esta tarefa.

## Juntar os data frames e identificar os registros
lista <- do.call(rbind, lista)
lista
##                 SalesPerson     Date        Qty          Price     Class
## Tape.1    Doe, John         09/23/98      1,120      11,225.60        AC
## Tape.2                      09/24/98     21,123     233,112.67        VA
## Tape.3                      09/27/98         23          32.22        CS
## Tape.4    Wacks, Gene       09/01/98      3,766       3,455.55        AC
## Tape.5    Peters, Kate      09/30/98      4,000       3,999.44        VA
## Tape.6                      09/02/98      1,653       6,775.55        OT
## Stapler.1 Perrol, Barb      09/13/98      3,320      11,225.60        AC
## Stapler.2 Zonker, Pete      09/11/98        766         453.53        AC
## Stapler.3                   09/20/98      1,455       2,543.43        VA
## Stapler.4 Perkins, Joe      09/12/98         23          23.55        OT
## Remover os números que foram acrescidos ao fim dos nomes...
lista <- transform(lista, Produtos = sub("\\.\\d+$", "", row.names(lista)))
lista
##                 SalesPerson     Date        Qty          Price     Class
## Tape.1    Doe, John         09/23/98      1,120      11,225.60        AC
## Tape.2                      09/24/98     21,123     233,112.67        VA
## Tape.3                      09/27/98         23          32.22        CS
## Tape.4    Wacks, Gene       09/01/98      3,766       3,455.55        AC
## Tape.5    Peters, Kate      09/30/98      4,000       3,999.44        VA
## Tape.6                      09/02/98      1,653       6,775.55        OT
## Stapler.1 Perrol, Barb      09/13/98      3,320      11,225.60        AC
## Stapler.2 Zonker, Pete      09/11/98        766         453.53        AC
## Stapler.3                   09/20/98      1,455       2,543.43        VA
## Stapler.4 Perkins, Joe      09/12/98         23          23.55        OT
##           Produtos
## Tape.1        Tape
## Tape.2        Tape
## Tape.3        Tape
## Tape.4        Tape
## Tape.5        Tape
## Tape.6        Tape
## Stapler.1  Stapler
## Stapler.2  Stapler
## Stapler.3  Stapler
## Stapler.4  Stapler
## Mudar os nomes das linhas...
rownames(lista) <- 1:nrow(lista)
lista
##          SalesPerson     Date        Qty          Price     Class Produtos
## 1  Doe, John         09/23/98      1,120      11,225.60        AC     Tape
## 2                    09/24/98     21,123     233,112.67        VA     Tape
## 3                    09/27/98         23          32.22        CS     Tape
## 4  Wacks, Gene       09/01/98      3,766       3,455.55        AC     Tape
## 5  Peters, Kate      09/30/98      4,000       3,999.44        VA     Tape
## 6                    09/02/98      1,653       6,775.55        OT     Tape
## 7  Perrol, Barb      09/13/98      3,320      11,225.60        AC  Stapler
## 8  Zonker, Pete      09/11/98        766         453.53        AC  Stapler
## 9                    09/20/98      1,455       2,543.43        VA  Stapler
## 10 Perkins, Joe      09/12/98         23          23.55        OT  Stapler

Agora chegamos na etapa em que precisamos realizar o tratamento dos dados.

## Excluir os brancos antes e depois dos dados
lista <- as.data.frame(sapply(lista, removeBrancos), stringsAsFactors=FALSE)

## Converter para o formato numérico as colunas relativas a volores
lista$Qty <- as.numeric(sub(",", "", lista$Qty))
lista$Price <- as.numeric(sub(",", "", lista$Price))

## Preencher os brancos nos nomes dos vendedores... 
nomes <- lista$SalesPerson[lista$SalesPerson != ""]
repete <- diff(c(which(lista$SalesPerson != ""), length(lista$SalesPerson) + 1))
lista$SalesPerson <- rep(nomes, repete)
str(lista)
## 'data.frame':    10 obs. of  6 variables:
##  $ SalesPerson: chr  "Doe, John" "Doe, John" "Doe, John" "Wacks, Gene" ...
##  $ Date       : chr  "09/23/98" "09/24/98" "09/27/98" "09/01/98" ...
##  $ Qty        : num  1120 21123 23 3766 4000 ...
##  $ Price      : num  11225.6 233112.7 32.2 3455.6 3999.4 ...
##  $ Class      : chr  "AC" "VA" "CS" "AC" ...
##  $ Produtos   : chr  "Tape" "Tape" "Tape" "Tape" ...
lista
##     SalesPerson     Date   Qty     Price Class Produtos
## 1     Doe, John 09/23/98  1120  11225.60    AC     Tape
## 2     Doe, John 09/24/98 21123 233112.67    VA     Tape
## 3     Doe, John 09/27/98    23     32.22    CS     Tape
## 4   Wacks, Gene 09/01/98  3766   3455.55    AC     Tape
## 5  Peters, Kate 09/30/98  4000   3999.44    VA     Tape
## 6  Peters, Kate 09/02/98  1653   6775.55    OT     Tape
## 7  Perrol, Barb 09/13/98  3320  11225.60    AC  Stapler
## 8  Zonker, Pete 09/11/98   766    453.53    AC  Stapler
## 9  Zonker, Pete 09/20/98  1455   2543.43    VA  Stapler
## 10 Perkins, Joe 09/12/98    23     23.55    OT  Stapler


3.3 Relatório 5

Vamos a mais um relatório, sendo que este, cujo “layout” apresentamos a seguir, é bem diferente dos anteriores.

Name:     Jan Vandam
Address:  123 Somewhere Str
          AnyTown, AL 12345
          USA
Tel:      123-456-7890
Fax:      747-868-2938
Email:    jan@hola.net
Contact:  Pete Caliber

Name:     Roland Rolse
Address:  456 Elsewher Ave
          ThisTown JX1 TH8
          UK
Tel:      +44-171-456-7890
Fax:      +44-171868-2938

Name:     Rick Rikkel
Address:  1 St. Michael Str
          Nowhere, XX 99999
          USA
Email:    Rik@Rik.com
Tel:      222-333-4444
Contact:  Bill Doe

Os dados relativos a cada campo de um registro encontram-se localizados em linhas distintas. Também é possível observar que nem todos os registros possuem os dados relativos a todos os campos. Por exemplo, o registro relativo a Roland Rolse não possui dados relativos a Email e Contact. Também deve ser notado que os dados relativos ao endereço ocupam três linhas.

A abordagem para a importação dos dados relativos a este relatório pode ser semelhante à adotada para o relatório anterior, ou seja, colocar em uma lista as linhas relativas a cada registro e posteriormente realizar as manipulações necessárias à extração dos dados desejados. Vejamos como implementar a importação deste relatório.

## Ler o arquivo contendo o relatório
r5 <- readLines("Relatorio5.txt")
## Warning in readLines("Relatorio5.txt"): linha final incompleta encontrada
## em 'Relatorio5.txt'
r5 <- r5[r5 != ""] # Excluir linhas em branco...
## Definir as linhas âncoras para a criação da lista
ancora <- grep("^Name:", r5)
ancora <- c(ancora, length(r5) + 1)
ancora
## [1]  1  9 15 22
## Obtenção dos nomes dos componentes das listas
nomes <- grep("^Name:", r5, value=TRUE)
nomes <- gsub("^Name:(.*)", "\\1", nomes)
nomes <- removeBrancos(nomes)
nomes
## [1] "Jan Vandam"   "Roland Rolse" "Rick Rikkel"
lista <- list()

for(k in nomes){
  i <- which(nomes == k)
  inicio <- ancora[i]
  fim <- ancora[i + 1]
  lista[[k]] <- r5[(inicio + 1):(fim - 1)]
}

lista
## $`Jan Vandam`
## [1] "Address:  123 Somewhere Str" "          AnyTown, AL 12345"
## [3] "          USA"               "Tel:      123-456-7890"     
## [5] "Fax:      747-868-2938"      "Email:    jan@hola.net"     
## [7] "Contact:  Pete Caliber"     
## 
## $`Roland Rolse`
## [1] "Address:  456 Elsewher Ave" "          ThisTown JX1 TH8"
## [3] "          UK"               "Tel:      +44-171-456-7890"
## [5] "Fax:      +44-171868-2938" 
## 
## $`Rick Rikkel`
## [1] "Address:  1 St. Michael Str" "          Nowhere, XX 99999"
## [3] "          USA"               "Email:    Rik@Rik.com"      
## [5] "Tel:      222-333-4444"      "Contact:  Bill Doe"

Obtida a lista, agora vamos iniciar as manipulações para obter os dados necessários.

Address1 <- lapply(lista, function(x) x[grep("^Address:", x)])
Address2 <- lapply(lista, function(x) x[grep("^Address:", x) + 1])
Address3 <- lapply(lista, function(x) x[grep("^Address:", x) + 2])
Tel      <- lapply(lista, function(x) x[grep("^Tel:", x)])
Fax      <- lapply(lista, function(x) x[grep("^Fax:", x)])
Email    <- lapply(lista, function(x) x[grep("^Email:", x)])
Contact  <- lapply(lista, function(x) x[grep("^Contact:", x)])

Contact
## $`Jan Vandam`
## [1] "Contact:  Pete Caliber"
## 
## $`Roland Rolse`
## character(0)
## 
## $`Rick Rikkel`
## [1] "Contact:  Bill Doe"

Neste processo os registros que não possuem todos os campos não tem valor para a informação correspondente o que é representado pela expressão character(0). Este é o caso, por exemplo, do campo Contact que não apresenta valor para Roland Rolse. Para podermos montar os data frames, estes valores vazios devem ser preenchidos. Nossa escolha será substituí-los por NA.

Os comandos a seguir ilustram como fazer isso.

Fax <- lapply(Fax, function(x) ifelse(length(x) == 0, NA, x))
Email <- lapply(Email, function(x) ifelse(length(x) == 0, NA, x))
Contact <- lapply(Contact, function(x) ifelse(length(x) == 0, NA, x))

Agora podemos começar a montar o data frame.

r5 <- data.frame(Names=names(lista),
                 Address1=unlist(Address1),
                 Address2=unlist(Address2),
                 Address3=unlist(Address3),
                 Tel=unlist(Tel),
                 Fax=unlist(Fax),
                 Email=unlist(Email),
                 Contact=unlist(Contact))

r5
##                     Names                    Address1
## Jan Vandam     Jan Vandam Address:  123 Somewhere Str
## Roland Rolse Roland Rolse  Address:  456 Elsewher Ave
## Rick Rikkel   Rick Rikkel Address:  1 St. Michael Str
##                                 Address2      Address3
## Jan Vandam             AnyTown, AL 12345           USA
## Roland Rolse            ThisTown JX1 TH8            UK
## Rick Rikkel            Nowhere, XX 99999           USA
##                                     Tel                       Fax
## Jan Vandam       Tel:      123-456-7890    Fax:      747-868-2938
## Roland Rolse Tel:      +44-171-456-7890 Fax:      +44-171868-2938
## Rick Rikkel      Tel:      222-333-4444                      <NA>
##                               Email                Contact
## Jan Vandam   Email:    jan@hola.net Contact:  Pete Caliber
## Roland Rolse                   <NA>                   <NA>
## Rick Rikkel   Email:    Rik@Rik.com     Contact:  Bill Doe

Obtido o data frame, devemos fazer a limpeza dos dados.

## Excluir "Address:", "Tel:", "Fax:", "Email:" e "Contact:"
r5$Address1 <- gsub("^Address:(.+)", "\\1", r5$Address1)
r5$Tel <- gsub("^Tel:(.+)", "\\1", r5$Tel)
r5$Fax <- gsub("^Fax:(.+)", "\\1", r5$Fax)
r5$Email <- gsub("^Email:(.+)", "\\1", r5$Email)
r5$Contact <- gsub("^Contact:(.+)", "\\1", r5$Contact)
## Remover os brancos antes e depois dos dados
r5 <- as.data.frame(sapply(r5, removeBrancos),stringsAsFactors=FALSE)
## Modificar os nomes do data frame
row.names(r5) <- 1:nrow(r5)
r5
##          Names          Address1          Address2 Address3
## 1   Jan Vandam 123 Somewhere Str AnyTown, AL 12345      USA
## 2 Roland Rolse  456 Elsewher Ave  ThisTown JX1 TH8       UK
## 3  Rick Rikkel 1 St. Michael Str Nowhere, XX 99999      USA
##                Tel             Fax        Email      Contact
## 1     123-456-7890    747-868-2938 jan@hola.net Pete Caliber
## 2 +44-171-456-7890 +44-171868-2938         <NA>         <NA>
## 3     222-333-4444            <NA>  Rik@Rik.com     Bill Doe


3.4 Relatório 6

O nosso sexto e último relatório, cujo “layout” apresentamos a seguir, possui uma estrutura muito parecida com a do relatório número 4, diferindo daquele no fato de que o nome dos produtos vem abaixo das linhas contendo os dados a ele pertencentes.

Assim, a importacão deste relatório segue o mesmo procedimento utilizado para a importação do relatório número 4, sendo necessário apenas fazer os ajustes em razão da âncora neste relatório estar abaixo das linhas e não acima como no relatório 4.

----------------------------------------------------------------------------------
                           INVENTORY MASTER REPORT                      12/31/1998 
                           MAROON AUTO PARTS, INC.
----------------------------------------------------------------------------------
  ITEM NO    DESCRIPTION     ITEM COST   QTY    INV COST    LAST PURCH   LAST SALE

  219434342  GASKET SET         123.35     3      370.05     19980301     19980630
  798734776  OIL PUMP           345.23     1      345.23     19970403     19960704
  872375762  VALVE GUIDE         10.25     8       82.00     19980523     19980723
  897987237  VALVE SPRING         4.95     8       39.60     19980523     19980723
  987987765  PISTON SET         805.00     2    1,610.00     19980523     19930913

SUBTOTAL ENGINE PARTS                     22    2,446.88          

  987293744  TORSION BAR        218.50     2      437.00     19980427     19981010
  098230984  SWAY BAR           399.00     1      399.00     19981010     19970119
  958430987  CV JOINTS          318.75     3      956.25     19881112     19980909
  092834844  STRUT ASSEMBLY     449.00     3    1,347.00     19970617     19970530

SUBTOTAL SUSPENSION                        9    3,139.25

GRANDTOTAL                                31    5,586.13

Vamos à importação do relatório.

## Leitura do arquivo contendo o relatório
r6 <- readLines("Relatorio6.txt")
## Warning in readLines("Relatorio6.txt"): linha final incompleta encontrada
## em 'Relatorio6.txt'
## Pré-preparo dos dados...
Produtos <- grep("^SUBTOTAL", r6)
itens <- grep("^ +\\d{9}", r6)
r6 <- r6[sort(c(itens, Produtos))]
r6
##  [1] "  219434342  GASKET SET         123.35     3      370.05     19980301     19980630"
##  [2] "  798734776  OIL PUMP           345.23     1      345.23     19970403     19960704"
##  [3] "  872375762  VALVE GUIDE         10.25     8       82.00     19980523     19980723"
##  [4] "  897987237  VALVE SPRING         4.95     8       39.60     19980523     19980723"
##  [5] "  987987765  PISTON SET         805.00     2    1,610.00     19980523     19930913"
##  [6] "SUBTOTAL ENGINE PARTS                     22    2,446.88          "                
##  [7] "  987293744  TORSION BAR        218.50     2      437.00     19980427     19981010"
##  [8] "  098230984  SWAY BAR           399.00     1      399.00     19981010     19970119"
##  [9] "  958430987  CV JOINTS          318.75     3      956.25     19881112     19980909"
## [10] "  092834844  STRUT ASSEMBLY     449.00     3    1,347.00     19970617     19970530"
## [11] "SUBTOTAL SUSPENSION                        9    3,139.25"

Para facilitar as etapas posteriores, realizamos um “pré-processamento” dos dados com vistas a excluir as linhas indesejadas. Esta etapa pode consistir apenas em excluir as linhas em branco, como já feito anteriormente ou algo um pouco mais elaborado, como o que fizemos acima.

Na verdade não existe uma rigidez nestas etapas, a estrutura do relatório específico que se queira importar é que irá ditar o que será necessário fazer para que seja possível importá-lo.

As demais etapas serão semelhantes àquelas feitas para o relatório 4.

Vamos agora obter os nomes dos produtos para a criação da lista:

nomes <- grep("^SUBTOTAL", r6, value=TRUE)
nomes <- substr(nomes, 9, 38)
nomes <- removeBrancos(nomes)

Agora vamos criar a lista. Antes, contudo, será necessário realizar uma pequena modificação no vetor r6 que consistirá em introduzir um item na posição 1 do vetor para facilitar o loop de criação da lista.

r6 <- c("", r6) # Inclusão de uma linha em branco na posição 1
ancora <- grep("^SUBTOTAL", r6)
ancora <- c(1, ancora)

Observe que, em razão da identificação dos produtos a que se refere as linhas estar na parte inferior, houve a necessidade de se fazer alterações no vetor âncora. Agora podemos criar a lista.

lista <- list()
for(k in nomes){
  i <- which(nomes == k)
  inicio <- ancora[i]
  fim <- ancora[i + 1]
  bloco <- r6[(inicio + 1):(fim - 1)]
  lista[[k]]<- txt2df(bloco,                                                                                                                        colunas=c("ITEM_NO", "DESCRIPTION", "ITEM_COST", "QTY", "INV_COST", "LAST_PURCH", "LAST_SALE"),
                      inicio=c( 1, 12, 30, 39, 45, 57, 70),
                      fim=c(11, 29, 38, 44, 56, 69, 82))
}

lista <- do.call(rbind, lista)
lista <- transform(lista, Produtos = sub("\\.\\d+$", "", row.names(lista)))
rownames(lista) <- 1:nrow(lista)

lista                                                      
##       ITEM_NO        DESCRIPTION ITEM_COST    QTY     INV_COST
## 1   219434342   GASKET SET          123.35      3       370.05
## 2   798734776   OIL PUMP            345.23      1       345.23
## 3   872375762   VALVE GUIDE          10.25      8        82.00
## 4   897987237   VALVE SPRING          4.95      8        39.60
## 5   987987765   PISTON SET          805.00      2     1,610.00
## 6   987293744   TORSION BAR         218.50      2       437.00
## 7   098230984   SWAY BAR            399.00      1       399.00
## 8   958430987   CV JOINTS           318.75      3       956.25
## 9   092834844   STRUT ASSEMBLY      449.00      3     1,347.00
##      LAST_PURCH     LAST_SALE     Produtos
## 1      19980301      19980630 ENGINE PARTS
## 2      19970403      19960704 ENGINE PARTS
## 3      19980523      19980723 ENGINE PARTS
## 4      19980523      19980723 ENGINE PARTS
## 5      19980523      19930913 ENGINE PARTS
## 6      19980427      19981010   SUSPENSION
## 7      19981010      19970119   SUSPENSION
## 8      19881112      19980909   SUSPENSION
## 9      19970617      19970530   SUSPENSION

Agora é só realizar a limpeza e o tratamento dos dados.

## Excluir os brancos antes e depois
lista <- as.data.frame(sapply(lista, removeBrancos), stringsAsFactors=FALSE)
## Converter para o formato numérico as colunas relativas a valores
lista$ITEM_COST <- as.numeric(sub(",", "", lista$ITEM_COST))
lista$QTY <- as.numeric(lista$QTY)
lista$INV_COST <- as.numeric(sub(",", "", lista$INV_COST))
## Converter para o formato de data as colunas relativas a datas
lista$LAST_PURCH <- as.Date(lista$LAST_PURCH, "%Y%m%d")
lista$LAST_SALE <- as.Date(lista$LAST_SALE, "%Y%m%d")
str(lista)
## 'data.frame':    9 obs. of  8 variables:
##  $ ITEM_NO    : chr  "219434342" "798734776" "872375762" "897987237" ...
##  $ DESCRIPTION: chr  "GASKET SET" "OIL PUMP" "VALVE GUIDE" "VALVE SPRING" ...
##  $ ITEM_COST  : num  123.35 345.23 10.25 4.95 805 ...
##  $ QTY        : num  3 1 8 8 2 2 1 3 3
##  $ INV_COST   : num  370.1 345.2 82 39.6 1610 ...
##  $ LAST_PURCH : Date, format: "1998-03-01" "1997-04-03" ...
##  $ LAST_SALE  : Date, format: "1998-06-30" "1996-07-04" ...
##  $ Produtos   : chr  "ENGINE PARTS" "ENGINE PARTS" "ENGINE PARTS" "ENGINE PARTS" ...
lista
##     ITEM_NO    DESCRIPTION ITEM_COST QTY INV_COST LAST_PURCH  LAST_SALE
## 1 219434342     GASKET SET    123.35   3   370.05 1998-03-01 1998-06-30
## 2 798734776       OIL PUMP    345.23   1   345.23 1997-04-03 1996-07-04
## 3 872375762    VALVE GUIDE     10.25   8    82.00 1998-05-23 1998-07-23
## 4 897987237   VALVE SPRING      4.95   8    39.60 1998-05-23 1998-07-23
## 5 987987765     PISTON SET    805.00   2  1610.00 1998-05-23 1993-09-13
## 6 987293744    TORSION BAR    218.50   2   437.00 1998-04-27 1998-10-10
## 7 098230984       SWAY BAR    399.00   1   399.00 1998-10-10 1997-01-19
## 8 958430987      CV JOINTS    318.75   3   956.25 1988-11-12 1998-09-09
## 9 092834844 STRUT ASSEMBLY    449.00   3  1347.00 1997-06-17 1997-05-30
##       Produtos
## 1 ENGINE PARTS
## 2 ENGINE PARTS
## 3 ENGINE PARTS
## 4 ENGINE PARTS
## 5 ENGINE PARTS
## 6   SUSPENSION
## 7   SUSPENSION
## 8   SUSPENSION
## 9   SUSPENSION


4 Considerações Finais

Esperamos que com os exemplos apresentados neste documento o leitor possa importar seus próprios relatórios.

Acompanhando este documento juntamos os relatórios nele utilizados e mais 5 outros (Relatorio7.txt, Relatorio.8.txt, Relatorio9.txt, Relatorio10.txt e XYZ_CORP.txt) que poderão ser utilizados para treinamento com base nos exemplos apresentados. Todos os arquivos contendo os relatórios estão contidos no arquivo Relatorios.zip.


  1. Sistema Integrado de Administração Financeira para Estados e Municípios

  2. Para efeito deste documento definiremos registo como o conjunto dos atributos de um item, os campos são onde os atributos de cada item estão especificados e as linhas são cada linha do arquivo a ser importado.

  3. Para maiores informações sobre expressões regulares consultar: http://aurelio.net/er/apostilaconhecendo-regex.pdf
    Outra fonte é: https://www.rstudio.com/wp-content/uploads/2016/09/RegExCheatsheet.pdf