9.5 Arquivos binários

Arquivo texto favorecem a legibilidade humana dos dados mas tem precisão numérica limitada. Formatos binários diminuem substancialmente o tamanho, o tempo de leitura e escrita de arquivos. Por isso, vários softwares armazenam dados no formato binário.

Após a leitura e o processamento de dados brutos você provavelmente os salvará para uso futuro. Este procedimento é recomendado para evitar de ter que repetir todo processamento novamente. Para dados que ocupam espaço significativo (por exemplo com mais de 1 milhão de linhas) é mais eficiente salvar os dados em um formato binário, uma vez que dessa forma os tempos de escrita e leitura são menores.

9.5.1 Formatos binários nativos do R

9.5.1.1 RDdata

Para mostrar como usar as funções save() e load() vamos utilizar os dados pluviométricos lidos anteriormente (dprec) e selecionar as colunas de interesse. O quadro de dados será salvo em um arquivo binário do R com a extensão .RData.

# início dos dados dprec
head(dprec[, 1:10])
# colunas referentes do dia 1 a 31 de cada ano
chuva_df <- dprec[, c(3, 14:44)]
# gerando arquivo em um diretório temporário
arq_chuva <- tempfile(fileext = ".RData")
save(chuva_df, file = arq_chuva)
# verificando se arquivo foi salvo no diretório
file.exists(arq_chuva)

Como o objeto chuva_df foi salvo em um arquivo, vamos removê-lo e então recuperá-lo carregando os dados armazenado no arquivo chuva_df.RData.

# apagando chuva_df do ambiente de trabalho
rm(chuva_df)
# verifica existência de objeto
exists(chuva_df)
# carregando chuva_df
load(file = arq_chuva)
ls()
# para carregar os dados e saber o nome com que foram salvos
print(load(file = arq_chuva))
head(chuva_df[, 1:10])

Um vantagem desse formato é que os objetos criados podem ser lidos pelo independente do sistema operacional e da arquitetura do computador, tornando muito prático o acesso aos dados. Cada vez que uma sessão do R é finalizada, uma janela surge perguntando se deseja salvar o espaço de trabalho (save the workspace image), que nada mais é do que um arquivo binário chamado .RData no diretório de trabalho. Assim quando iniciar a sessão se o arquivo .RData estiver no diretório de trabalho ele será automaticamente carregado tornando todos os objetos da última sessão disponíveis novamente. Se você deseja salvar o espaço de trabalho em outro momento use a função save.image().

Quando desejamos salvar só uma parte dos dados uma opção é usar a função rm() (uma abreviação de remove) para remover objetos que não são de interesse antes de finalizar a sessão do R. A função save()permite salvar mais de um objeto em um mesmo arquivo.

file_dados_prec <- tempfile(fileext = ".Rdata") 
save(cab, chuva_df, file = file_dados_prec)
#save(cab, chuva_df, file = "../output-adar/dados_prec.RData")
ls()
rm(cab, chuva_df)
ls()
# carrega e imprime na tela nome dos dados carregados
print(load(file_dados_prec))
ls()

9.5.1.2 RDS

As funções readRDS() e writeRDS() são similares a load() e save(), respectivamente, exceto que elas lidam com um único objeto. Em contrapartida elas possuem a flexibilidade nomear o objeto lido com um nome diferente do qual ele foi salvo. Vamos alterar o formato da data do quadro de dados chuva_df e salvá-lo no arquivo chuva_df.rds.

# salvar dados em um arquivo rds
head(chuva_df[, 1:10])
# alterando formato de datas da coluna Data
chuva_df$Data <- as.Date(x = chuva_df$Data, format = "%d/%m/%Y")
file_rds_chuva_df <- tempfile(fileext = ".Rdata")
saveRDS(object = chuva_df, file = file_rds_chuva_df)
file.exists(file_rds_chuva_df)

Após salvar o quadro de dados chuva_df vamos removê-lo do ambiente da sessão e recuperá-lo com a função readRDS().

# removendo chuva_df do ambiente
rm(chuva_df)
# recuperando dados do arquivo em uma variável com nome diferente do original
prec_ana <- readRDS(file_rds_chuva_df)
head(prec_ana[, 1:10])

9.5.2 NetCDF (Network Common Data Form)

NetCDF é um formato binário, auto-descritivo e independente do SO criado para criar e distribuir arranjos multidimensionais de dados gradeados. Originalmente foi desenvolvido para o armazenamento e distribuição de dados climáticos, tais como os gerados por modelos climáticos e sistemas de assimilação de dados como as reanálises.

As bibliotecas NetCDF são mantidas pelo Unidata. Dados no formato NetCDF são acessíveis no R pelos pacotes ncdf4 (Pierce 2017) e raster (Hijmans 2017). Esses pacotes fornecem o suporte necessário para leitura e escrita de arquivos NetCDF.

O pacote ncdf foi um dos primeiros pacotes de interface com dados NetCDF, mas com suporte somente para versão 3. O pacote que o substituiu foi o ncdf4 e suporta tanto arquivos no formato NetCDF 3 como 4. O pacote raster baseia-se no ncdf4 para fornecer através da função brick() uma importação e exportação fácil de dados NetCDF com o R.

9.5.2.1 Pré-requisitos

Para utilizar o pacote ncdf4 é necessário instalar os pacotes linux mostrados abaixo.

## Install pacotes NetCDF
$ sudo apt-get install libnetcdf-dev libudunits2-dev

Pacotes necessários:

library(ncdf4)
library(raster)
library(RColorBrewer)
library(fields)

9.5.2.2 Arquivo exemplo

Os exemplos a seguir usam o pacote ncdf4 para ler arquivo NetCDF com dados climáticos do Climate Research Unit CRU, consistindo de valores médios de longo prazo (1961-1990) da temperatura do ar próximo à superfície com resolução espacial de 0,5 º na área continental. As dimensões da array são: 720 (longitudes) x 360 (latitudes) x 12 (meses).

dados_cru_url <- "https://www.dropbox.com/s/ynp9i42is1flg43/cru10min30_tmp.nc?dl=1"
dest_file_nc <- file.path(tempdir(), "cru10min30_tmp.nc")
download.file(
  url = dados_cru_url,
  destfile = dest_file_nc,
  mode = "wb"
)

Abrindo arquivo NetCDF e obtendo informações básicas.

dest_file_nc
file.exists(dest_file_nc)
# variável de interesse, tmp: temperatura do ar
dname <- "tmp"  
# abre o arquivo NetCDF
ncin <- nc_open(filename = dest_file_nc)
print(ncin)
# estrutura dos dados
#str(ncin)
# classe
class(ncin)
# modo
mode(ncin)

Agora, vamos ler as coordenadas de longitude e latitude.

lon <- ncvar_get(nc = ncin, varid = "lon")
nlon <- dim(lon)
head(lon)
lat <- ncvar_get(
  nc = ncin,
  varid = "lat",
  verbose = FALSE
)
nlat <- dim(lat)
head(lat)
c(nlon, nlat)

Vamos obter a variável temporal e seus atributos usando as funções ncvarget() e ncatt_get. Depois fechamos o acesso ao arquivo NetCDF.

tempo <- ncvar_get(nc = ncin, varid = "time")
(tunits <- ncatt_get(
  nc = ncin,
  varid = "time",
  attname = "units"
))
(nt <- dim(tempo))
temp_arranjo <- ncvar_get(nc = ncin, varid = dname)
# resumo da estrutura dos dados
str(temp_arranjo)
# nome longo da variável
(dlname <- ncatt_get(
  nc = ncin,
  varid = dname,
  attname = "long_name"
))

# unidades da variável
(dunits <- ncatt_get(
  nc = ncin,
  varid = dname,
  attname = "units"
))
# valor definido para valores faltantes
(fillvalue <- ncatt_get(
  nc = ncin,
  varid = dname,
  attname = "_FillValue"
))
# fechando arquivo
nc_close(ncin)

As variáveis do arquivo NetCDF são lidas e escritas como vetores (p.ex.: longitudes), matrizes (como o campo espacial de um momento), ou arranjos (Apêndice A) multidimensionais (campos espaciais de uma variável em diversos tempos ou níveis de altitude).

Vamos extrair o campo espacial de um passo de tempo (1 mês), criar um quadro de dados onde cada linha será um ponto de grade e a coluna representa uma variável, por exemplo: longitude, latitude e temperatura.

m <- 1
# campo espacial do primeiro dia de dados
temp_jan <- temp_arranjo[, , m]
str(temp_jan)
# outra função para visualizar dados com 3D
image.plot(lon, lat, temp_jan, col = rev(brewer.pal(10, "RdBu")))
9.5.2.2.1 Forma fácil de importar NetCDF

O pacote raster fornece uma função para fácil importação de arquivos NetCDF. Os dados importados são retornados no formato específico do pacote (classe de dados RasterBrick). Esta classe de dados, corresponde a uma estrutura de dados espaciais gradeados, regularmente espaçados, podendo ter uma ou mais dimensões.

Quando o dados gradeados possuem somente uma variável em um único tempo, como por exemplo a altitude do terreno (z), temos 2 dimensões espaciais x (longitude), y (latitude) e z. Neste caso, o dado é um raster e sua classe de dados é denominada RasterLayer no pacote raster, ou seja os dados possuem somente uma camada. Quando os dados possuem mais de uma camada, como no casos de campos espaciais de temperatura em diferentes meses (cada mês é uma camada) a classe de dados é denominada Rasterbrick.

Para importar dados em formato NetCDF que tenham mais uma camada no R, usamos a função brick() do pacote raster.

brick_tar_cru <- brick(dest_file_nc)
brick_tar_cru

O resultado da importação de um RasterBrick mostra no console do R informações sobre as dimensões dos dados, a resolução espacial, os limites do domínio espacial, o sistema de coordenadas de referência, o arquivo fonte dos dados, o nome das camadas, eventualmente as datas e nome da variável importada do arquivo NetCDF.

Quando o arquivo NetCDF possui mais de uma variável é necessário definir o nome da variável de interesse através do argumento varname. No exemplo acima poderíamos ter chamado a função raster::brick() com brick(dest_file_nc, varname = "tmp"). Mas como há somente uma variável no arquivo NetCDF deste exemplo a especificação deste argumento é opcional.

Os nomes das camadas, são acessados e alterados com função names(), da mesma forma que em quadro de dadoss.

# substituindo a letra "X" dos nomes por "Mes_"
names(brick_tar_cru) <- gsub("X", "Mes_", names(brick_tar_cru))
names(brick_tar_cru)

Um gráfico pode ser gerado através da funções plot(). Por default são mostrados no máximo 16 camadas de um RasteBrick.

plot(brick_tar_cru, col = rev(brewer.pal(10, "RdBu")))

Os dados em formato RasterBrick, RasterStack ou RasterLayer podem ser convertidos para quadro de dados por meio da função raster::as.data.frame().

df_tar_cru <- as.data.frame(
  x = brick_tar_cru,
  xy = TRUE, 
  na.rm = TRUE
  #long = TRUE
)
str(df_tar_cru)
head(df_tar_cru)

Os argumentos usados na função as.dataframe() correspondem a:

  • x é o objeto Raster* (onde * significa RasterBrick, RasterStack ou RasterLayer)

  • xy é um argumento lógico, se TRUE (verdadeiro) inclui as coordenadas espaciais (longitude e altitude) das células do raster como colunas no quadro de dados de saída

  • na.rm é um argumento opcional lógico, tal que se for TRUE remove linhas com valores NA. Isto é particularmente útil para grandes conjuntos de dados com muitos valores NAs e em regiões oceânicas, como no arquivo de exemplo, onde não há dados medidos. Note que se na.rm = FALSE (TRUE) o quadro de dados resultante terá (poderá ter) um número de linhas igual ao (menor que o) número de células do RasterBrick.

nrow(df_tar_cru) < ncell(brick_tar_cru)
  • long é um argumento opcional lógico. Se for TRUE (verdadeiro) os valores são reestruturados de um formato amplo para um formato longo (veja a seção 10.3.2.1 para definição de dados no formato longo e amplo).

Como exercício, rode novamente o trecho de código anterior, mudando os valores dos argumentos lógicos e observe as mudanças nas dimensões do quadro de dados resultante.