B.4 Leitura de arquivos texto com funções da base do R

A função nativa do mais usada para leitura de dados de um arquivo texto é a read.table(). Os dados lidos são armazenados em um dataframe.

Essa função possui diversos parâmetros para especificar a importação de acordo com as peculiaridades do formato de dados do arquivo.

O valor default do parâmetro sep é um ou mais caracteres de espaço e tabs. Devido as diversas opções de separadores existem outras funções envelopes da read.table(), com a diferença no separador, por exemplo as funções: read.csv(), read.csv2(), read.delim() que chamam a read.table(), dentro corpo da função, com o argumento sep definido como ,, ; e \t, respectivamente. Para detalhes sobre essas funções o help de cada uma. Uma vez que essas funções aceitam qualquer argumento da read.table() elas são mais convenientes que usar a read.table() e configurar os argumentos apropriados manualmente.

Alguns argumentos da função read.table() são:

  • file nome do arquivo
  • header lógico (TRUE ou FALSE) indicando se o arquivo tem ou não linha de cabeçalho
  • sep um caractere indicando como as colunas são separadas
  • colClasses, um vetor caractere indicando as classes de cada coluna no conjunto de dados
  • nrows, número de linhas no conjunto de dados
  • comment.char, um caractere indicando o caractere usado como comentário (para ignorar essas linhas)
  • skip, o número de linhas que devem ser "puladas" desde o início do arquivo
  • stringsAsFactors, lógico, as variáveis do tipo character devem ser codificadas como factor?

Esse último argumento pode ser definido também através da configuração global de opções no R pelo comando: options(stringsAsFactors=FALSE).

Quando se faz a leitura de dados com read.table("nome_do_arquivo") o R automaticamente:

  • pula linhas que começam com '#'
  • descobre quantas linhas tem o arquivo e quanta memória precisa ser alocada
  • descobre qual o tipo de variável em cada coluna

Vamos ver alguns exemplos de leitura de dados hidrometeorológicos no formato texto amplamente usados em aplicações da Meteorologia.

B.4.1 Dados hidrometeorológicos brasileiros

B.4.1.1 hidroweb-ANA

O sistema hidroweb é o maior base de dados hidrológicos brasileira. No trecho de código a seguir baixamos um arquivo de dados de precipitação obtidos no sistema hidroweb que foi salvo no site do livro.

# arquivo de exemplo disponível no GitHub
hidroweb_url_file <- "https://raw.github.com/lhmet/adar-ufsm/master/data/CHUVAS.TXT"
#arquivo temporário, você pode substituir tempfile() por um caminho de seu computador, p.ex. "~/Downloads/CHUVAS.TXT"
hidroweb_dest_file <- tempfile()
download.file(
  url = hidroweb_url_file, 
  destfile = hidroweb_dest_file
)

Antes de importar os dados você precisa visualizar o arquivo para extrair as informações básicas necessárias para sua importação. Você pode abri-lo em um editor de texto.

# caracteres não devem tratados como fatores
options(stringsAsFactors = FALSE)
# leitura de dados da ANA
dprec <- read.csv2(file = hidroweb_url_file, 
                   skip = 15, 
                   head = TRUE, 
                   fill = TRUE)
# primeiras linhas
head(dprec)
# últimas linhas
tail(dprec)
 # corrigindo nome da primeira coluna
 names(dprec)[1] <- "EstacaoCodigo"
 # removendo última coluna que só tem NAs
 dprec <- dprec[ , -ncol(dprec)]
  # estrutura dos dados
  str(dprec)
# Fazendo a mesma leitura com read.table
dprec2 <- read.table(file = hidroweb_url_file, 
                   skip = 15, 
                   head = T, 
                   stringsAsFactors = FALSE,
                   fill = T,
                   sep = ";",
                   dec = ",")
head(dprec2)

B.4.1.2 BDMEP-INMET

O Banco de Dados Meteorológicos para Ensino e Pesquisa do INMET é uma das principais fonte de dados climáticos brasileiros. Abaixo veremos como importar um arquivo de dados diários de uma estação meteorológica convencional. Os dados foram obtidos no BDMEP e salvos no site do livro.

No trecho de código a seguir baixamos o arquivo de dados da estação 83004.

# arquivo de exemplo disponível no GitHub
bdmep_url_file <- "https://raw.githubusercontent.com/lhmet/adar-ufsm/master/data/83004.txt"
#arquivo temporário, você pode substituir tempfile() por um caminho de seu computador, p.ex. "~/Downloads/CHUVAS.TXT"
bdmep_dest_file <- tempfile()
download.file(
  url = bdmep_url_file, 
  destfile = bdmep_dest_file
)
x <- read.csv2(file = bdmep_dest_file, 
               header = FALSE, 
               skip = 16,
               stringsAsFactors = FALSE,
               na.strings = ""
               )
head(x)
str(x)

Os dados lidos não incluíram a linha de cabeçalho com os nomes das variáveis. Nós pulamos essa linha porque o nome das variáveis está de acordo como número de colunas do arquivo. Então se tentarmos ler um arquivo de dados que contém linhas com número de registros diferentes ocorrerá um erro pois os dados não são tabulares.

Outro aspecto nos dados lidos é que aparecem vários <NA>, que é o símbolo para dados do tipo character faltantes. A razão dos terem sido interpretados dessa forma deve-se a um caractere (</pre>) encontrado na última linha do arquivo.

Para que os dados numéricos não sejam interpretados como caractere nós poderíamos executar a função read.table(..., nrows = 5878), que ignoraria a última linha do arquivo e os dados seriam interpretados como numeric.

x1 <- read.csv2(file=bdmep_dest_file, 
               header = FALSE, 
               skip = 16,
               stringsAsFactors = FALSE,
               dec = ".",
               na.strings = "",
               nrows = 5878 
)
head(x1)
str(x1)

Outra alternativa seria converter as colunas de interesse (todas exceto as de 1 a 3) para numeric através da função as.numeric() usando a função apply ao longo das colunas:

# corrigindo classe dos dados
# convertendo de character para numeric
x[, -c(1:3)] <- apply(x[,-c(1:3)], 2, as.numeric)
str(x)
# razão dos avisos
#as.numeric("NA")

Mas e o nome das variáveis? Nós ignoramos a linha de cabeçalho por que nos dados do INMET ocorre uma variável denominada VelocidadeVentoInsolacao. Essa string deveria ser separada em duas. Vamos fazer essa adequação aos dados.

# lendo somente o nome das variaveis
vnames <- read.csv2(file=bdmep_dest_file, 
                    header = FALSE, 
                    skip = 15,
                    stringsAsFactors = FALSE,
                    dec = ".",
                    na.strings = "",
                    nrows = 1)
# convertendo de dataframe para vetor
vnames <- c(t(vnames))
vnames
# número de variáveis é diferente do número de colunas do arquivo
length(vnames) == ncol(x)
# corrigindo nomes das variaveis
#   substitui "VelocidadeVentoInsolacao" por "VelocidadeVento"
vnames[13] <- "VelocidadeVento"
# acresenta na 14a posição dos nomes a variável "insolacao" e
# desloca os elementos orginais do vetor 
vnames <- c(vnames[1:13], "insolacao", vnames[14:length(vnames)])
length(vnames)
ncol(x)
names(x) <- vnames
head(x)

Finalmente vamos escrever os dados do INMET corretamente organizados.

bdmep_dest_file_clean <- file.path(tempdir(), "83004_clean.txt")
write.csv2(x,
           file = bdmep_dest_file_clean, 
           na = "-9999",
           row.names = FALSE)

B.4.2 Arquivos formatados com largura fixa

Alguns arquivos texto com dados tabulares podem não conter separadores (para p.ex. economizar espaço de disco). Outros arquivos podem ser formatados usando largura fixa para reservar o espaço de cada variável, o que aumenta a legibilidade dos dados em editor de texto.

Nesses casos a função read.fwf() é conveniente. Vamos usar como exemplo o arquivo de dados do Índice de Oscilação Sul (SOI) obtido no site do National Weather Service - Climate Prediction Center (NWS-CPC).

# link para os dados do SOI
noaa_url_file <- "http://www.cpc.ncep.noaa.gov/data/indices/soi"

Abrindo o link dos dados no navegador para visualização dos dados.

browseURL(url = noaa_url_file)

Leitura dos dados:

#soi <- read.fwf(file = link,                           # nome do arquivo ou link
soi <- read.fwf(file = noaa_url_file,                       # sem internet, usar esse arquivo
                skip = 4,                               # pula 4 linhas
                header = FALSE,                             # sem cabeçalho
                nrows = 70,                             # num. de linhas
                widths = c(4, rep(6,12)),                # largura dos campos das variáveis
                na.strings = "-999.9",                  # string para dados faltantes
                col.names = scan(noaa_url_file,             # varredura do arquivo
                #col.names = scan(link,             # varredura do arquivo
                                 what = "character",    # tipo dos dados a serem lidos
                                 skip = 3,              # pula 3 linhas
                                 nmax = 13)             # num. max de registros a serem lidos
                )
head(soi)

Vamos alterar a estrutura dos dados: ao invés dos dados serem distribuídos ao longo das colunas, vamos estruturá-los como série temporal, ou seja cada valor mensal corresponderá a uma linha.

# converte a matriz de dados para um vetor (em sequencia cronológica)
soi_v <- c(t(soi[, -1]))
# criando um dataframe com valores de SOI, mes e ano
soi_df <- data.frame(ano = rep(soi$YEAR, each = 12),
                     mes = rep(1:12, length(soi[,1])),
                     soi = soi_v)
# escrevendo dados SOI em um arquivo CSV
soi_csv_file <- file.path(tempdir(), "SOI.csv")
write.csv(x = soi_df, 
          file = soi_csv_file, 
          na = "-999.9", 
          row.names = FALSE)

Vamos ler os dados reestruturados que foram salvos no formato csv usando uma função que permite a escolha do arquivo de forma iterativa.

# leitura de dados com escolha interativa do arquivo
soi.df <- read.csv(file = file.choose(),
                   # file.choose só é válido em sistema *unix
                   # no windows é choose.file()
                   header = TRUE,
                   na.strings = "-999.9")

Navegue até o diretório do arquivo SOI.csv e clique duas vezes sobre ele.

soi.df <- read.csv(file = soi_csv_file,
                   header = TRUE,
                   na.strings = "-999.9")
head(soi.df)
str(soi.df)

A função read.fortran() é uma função similar à read.fwf() e permite usar especificações de colunas no estilo Fortran.