8  Importação e Exportação de Dados

Neste capítulo iremos aprender a importar e exportar dados contidos em arquivos locais. Apesar de não ser uma tarefa particularmente difícil, um analista de dados deve entender as diferentes características de cada formato de arquivo e como tirar vantagem deste conhecimento. Enquanto algumas facilitam a colaboração e troca de dados, outras podem oferecer um ganho significativo em tempo de execução na leitura e gravação.

Aqui iremos traçar uma lista abrangente com os seguintes formatos e extensões de arquivos:

Na última seção do capitulo também discutiremos sobre a importação de dados da internet utilizando pacotes especializados, tal como o {yfR} (Perlin 2023).

8.1 Primeiros passos

A primeira lição na importação de dados para o R é que o local do arquivo deve ser indicado explicitamente no código. Este endereço é passado para a função que irá ler o arquivo. Veja a definição a seguir:

my_file <- 'C:/Data/MyData.csv'

Note o uso de barras (/) para designar o diretório do arquivo. Referências relativas também funcionam, tal como em:

my_file <- 'Data/MyData.csv'

Neste caso, assume-se que na pasta atual de trabalho existe um diretório chamado Data e, dentro desse, um arquivo denominado MyData.csv. Se o endereço do arquivo é simplesmente o seu nome, assume-se que o mesmo encontra-se na raiz da pasta de trabalho. Para verificar o endereço atual de trabalho, utilize a função getwd() .

Use o autocomplete para achar o caminho de arquivos!

Aqui novamente reforço o uso do tab e autocomplete do RStudio. É muito mais fácil e prático encontrar arquivos do disco rígido do computador usando a navegação via tab do que copiar e colar o endereço do seu explorador de arquivos. Para usar, abra aspas no RStudio, coloque o cursor do mouse entre as aspas e aperte tab.

Um ponto importante aqui é que os dados serão importados e exportados no R como objetos do tipo dataframe (veja Seção 7.1). Isto é, uma tabela contida em um arquivo Excel ou .csv se transformará em um objeto do tipo dataframe no ambiente de trabalho do R. Quando exportarmos dados, o formato mais comum é esse mesmo tipo de objeto. Quando realizando a importação, é de fundamental importância que os dados sejam representados na classe correta. Uma vasta quantidade de erros podem ser evitados pela simples checagem das classes das colunas no dataframe resultante do processo de importação.

8.2 Arquivos csv

Considere o arquivo de dados no formato csv chamado 'CH04_ibovespa.csv', pertencente ao repositório do livro. Vamos copiar o mesmo para a pasta “Meus Documentos” com o uso do tilda (~):

# get location of file
my_f <- introR::data_path('CH04_ibovespa.csv')

# copy to ~
fs::file_copy(my_f, '~' )
R> [1] TRUE

Caso seja a primeira vez trabalhando com arquivos do tipo .csv, sugiro usar o explorador de arquivos do Windows e abrir CH04_ibovespa.csv com qualquer editor de texto instalado, tal como o Notepad (veja Figura 8.1). Observe que as primeiras linhas do arquivo definem os nomes das colunas: ref_date e price_close. Conforme notação internacional, as linhas são definidas pela quebra do texto e as colunas pelo uso da vírgula (,).

Figura 8.1: Tela do Notepad
O Formato Brasileiro

Quando trabalhando com dados brasileiros, a notação internacional pode gerar uma confusão desnecessária. Dados locais tendem a usar a vírgula para indicar valores decimais em números, assim como também datas no formato dia/mês/ano. Abaixo apresenta-se algumas regras de formatação de números e códigos para o caso brasileiro.

Decimal: O decimal no R é definido pelo ponto (.), tal como em 2.5 e não vírgula, como em 2,5. Esse é o padrão internacional, e a diferença para a notação brasileira gera muita confusão. Em nenhuma situação deve-se utilizar a vírgula como separador de casas decimais. Mesmo quando estiver exportando dados, sempre dê prioridade para o formato internacional.

Caracteres latinos: Devido ao seu padrão internacional, o R apresenta problemas para entender caracteres latinos, tal como cedilha e acentos. Caso possa evitar, não utilize esses tipos de caracteres no código para nomeação de variáveis ou arquivos. Nos objetos de classe texto (character), é possível utilizá-los desde que a codificação do objeto esteja correta (UTF-8 ou Latin1). Assim, recomenda-se que o código do R seja escrito na língua inglesa.

Formato das datas: Datas no R são formatadas de acordo com a norma ISO 8601, seguindo o padrão YYYY-MM-DD, onde YYYY é o ano em quatro números, MM é o mês e DD é o dia. Por exemplo, uma data em ISO 8601 é 2024-04-29. No Brasil, as datas são formatadas como DD/MM/YYYY. Reforçando a regra, sempre dê preferência ao padrão internacional. Vale salientar que a conversão entre um formato e outro é bastante fácil e foi mostrada na Seção 6.5.

O conteúdo de CH04_ibovespa.csv é bastante conservador e não será difícil importar o seu conteúdo. Porém, saiba que muitas vezes o arquivo .csv vem com informações extras de cabeçalho – o chamado metadata – ou diferentes formatações que exigem adaptações. Como sugestão para evitar problemas, antes de prosseguir para a importação de dados em um arquivo .csv, abra o arquivo em um editor de texto qualquer e siga os seguintes passos:

  1. Verifique a existência de texto antes dos dados e a necessidade de ignorar algumas linhas iniciais. A maioria dos arquivos .csv não contém cabeçalho, porém deves sempre checar. No R, a função de leitura de arquivos .csv possui uma opção para ignorar um definido número de linhas antes de começar a leitura do arquivo;

  2. Verifique a existência ou não dos nomes das colunas na primeira linha com os dados. Em caso negativo, verifique com o autor qual o nome (e significado) das colunas;

  3. Verifique qual o símbolo de separador de colunas. Comumente, seguindo notação internacional, será a vírgula, porém nunca se tem certeza sem checar;

  4. Para dados numéricos, verifique o símbolo de decimal, o qual deve ser o ponto (.) tal como em 2.5. Caso necessário, podes ajustar o símbolo na própria função de leitura;

  5. Verifique a codificação do arquivo de texto. Normalmente é UTF-8, Latin1 (ISO-8859) ou windows1252. Esses são formatos amplos e devem ser suficientes para a maioria dos idiomas. Sempre que você encontrar símbolos estranhos nas colunas de texto do dataframe resultante, o problema é devido a uma diferença na codificação entre o arquivo e o R. Os usuários do Windows podem verificar a codificação de um arquivo de texto abrindo-o no software Notepad++. As informações sobre a codificação estarão disponíveis no canto inferior direito do editor. No entanto, você precisa estar ciente de que o Notepad++ não faz parte da instalação do Windows e pode ser necessário instalá-lo em seu computador. Os usuários de Linux e Mac podem encontrar as mesmas informações em qualquer software editor de texto avançado, como o Kate e o vscode.

Não modifique dados brutos!

Sempre que você encontrar uma estrutura de texto inesperada em um arquivo .csv, use os argumentos da função de leitura csv para importar as informações corretamente. Repetindo, nunca modifique dados brutos do arquivo a ser importado. Use o código para lidar com diferentes estruturas de arquivos em formato csv. Pode parecer mais trabalhoso, mas essa política vai economizar muito tempo no futuro, pois, em algumas semanas, você provavelmente esquecerá como limpou manualmente aquele arquivo csv utilizado em pesquisa passada. Com o uso de código para a adaptação da importação de dados, sempre que você precisar atualizar o arquivo de dados, o código irá resolver todos os problemas, automatizando o processo.

8.2.1 Importação de Dados

O R possui uma função nativa chamada read.csv() para importar dados de arquivos .csv. Porém, esse é um dos muitos casos em que a alternativa do {tidyverse} (Wickham 2023)readr::read_csv() – é mais eficiente e mais fácil de trabalhar. Resumindo, readr::read_csv() lê arquivos mais rapidamente que read.csv() , além de usar regras mais inteligentes para definir as classes das colunas importadas.

# set file to read
my_f <- introR::data_path('CH04_ibovespa.csv')

# read data
my_df_ibov <- readr::read_csv(my_f)

O conteúdo do arquivo importado é convertido para um objeto do tipo dataframe no R. Conforme mencionado em Seção 7.1, cada coluna de um dataframe tem uma classe. Podemos verificar as classes de my_df_ibov usando a função dplyr::glimpse() :

# check content
dplyr::glimpse(my_df_ibov)
R> Rows: 3,215
R> Columns: 2
R> $ ref_date    <date> 2010-01-04, 2010-01-05, 2010-01-06, 2…
R> $ price_close <dbl> 70045, 70240, 70729, 70451, 70263, 704…

Observe que a coluna de datas (ref_date) foi importada como um vetor Date e os preços de fechamento como numéricos (dbl, precisão dupla). Isso é exatamente o que esperávamos. Internamente, a função readr::read_csv() identifica as classes das colunas de acordo com seu conteúdo.

Observe também como o código anterior apresentou a mensagem de título Column specification. Essa mensagem mostra como a função identifica as classes das colunas lendo as primeiras 1000 linhas do arquivo. Regras inteligentes tentam prever a classe com base no conteúdo importado. Para definir manualmente as classes das colunas, podemos utilizar as funções readr::cols() e readr::col_*:

library(readr)

# set cols from readr import message
my_cols <- cols(
  price_close = col_double(),
  ref_date = col_date(format = "")
)

# read file with readr::read_csv
my_df_ibov <- read_csv(my_f,
                       col_types = my_cols)

Como um exercício, vamos importar os mesmos dados, porém usando a classe character (texto) para colunas ref_date:

# set cols from readr import message
my_cols <- cols(
  price_close = col_double(),
  ref_date = col_character()
)

# read file with readr::read_csv
my_df_ibov <- read_csv(my_f,
                       col_types = my_cols)

# check content
glimpse(my_df_ibov)
R> Rows: 3,215
R> Columns: 2
R> $ ref_date    <chr> "2010-01-04", "2010-01-05", "2010-01-0…
R> $ price_close <dbl> 70045, 70240, 70729, 70451, 70263, 704…

Como esperado, a coluna de datas – ref_date – agora foi importada como texto. Assim, o uso de readr::read_csv() pode ser resumido em duas etapas: 1) leia o arquivo sem argumentos em readr::read_csv() ; 2) copie o texto das classes de coluna padrão da mensagem de saída e adicione como entrada col_types. O conjunto de passos anterior é suficiente para a grande maioria dos casos. O uso da mensagem com as classes das colunas é particularmente útil quando o arquivo importado tem várias colunas e a definição manual de cada classe exige muita digitação.

Uma alternativa mais prática no uso do readr::read_csv() é confiar na heurística da função e usar a definição padrão das colunas automaticamente. Para isto, basta definir a entrada col_types como a função readr::cols() , sem argumento. Veja a seguir:

# read file with readr::read_csv
my_df_ibov <- read_csv(my_f,
                       col_types = cols())

8.2.1.1 Um caso anormal

Agora, vamos estudar um caso anormal de arquivo .csv. No pacote do livro temos um arquivo chamado CH04_funky-csv-file.csv onde:

  • o cabeçalho possui texto com informações dos dados;
  • o arquivo usará a vírgula como decimal;
  • o texto do arquivo conterá caracteres latinos.

As primeiras 10 linhas dos arquivos contém o seguinte conteúdo:

R> Example of funky file:
R> - columns separated by ";"
R> - decimal points as ","
R> 
R> Data build in 2022-12-28
R> Origin: www.funkysite.com.br
R> 
R> ID;Race;Age;Sex;Hour;IQ;Height;Died
R> 001;White;80;Male;00:00:00;92;68;FALSE
R> 002;Hispanic;25;Female;00:00:00;99;68;TRUE

Note a existência do cabeçalho até linha de número 7 e as colunas sendo separadas pela símbolo de semi-vírgula (“;”).

Ao importar os dados com opções padrões (e erradas), teremos o resultado a seguir:

df_funky <- readr::read_csv(my_f, 
                     col_types = readr::cols())

dplyr::glimpse(df_funky)
R> Rows: 2
R> Columns: 1
R> $ `Example of funky file:` <chr> "- columns separated by \…

Claramente a importação deu errado, com a emissão de diversas mensagens de warning. Para resolver, utilizamos o seguinte código, estruturando todas as particularidades do arquivo:

df_not_funky <- readr::read_delim(
  file = my_f, 
  skip = 7, # how many lines do skip
  delim = ';', # column separator
  col_types = readr::cols(), # column types
  locale = readr::locale(decimal_mark = ',')# locale
)

dplyr::glimpse(df_not_funky)
R> Rows: 100
R> Columns: 8
R> $ ID     <chr> "001", "002", "003", "004", "005", "006", "…
R> $ Race   <chr> "White", "Hispanic", "Asian", "White", "Whi…
R> $ Age    <dbl> 80, 25, 25, 64, 76, 89, 33, 61, 23, 59, 80,…
R> $ Sex    <chr> "Male", "Female", "Male", "Male", "Female",…
R> $ Hour   <time> 00:00:00, 00:00:00, 00:00:00, 00:00:00, 00…
R> $ IQ     <dbl> 92, 99, 98, 105, 109, 84, 109, 109, 99, 126…
R> $ Height <dbl> 68, 68, 69, 69, 67, 73, 65, 72, 70, 66, 63,…
R> $ Died   <lgl> FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE…

Veja que agora os dados foram corretamente importados, com as classes corretas das colunas. Para isso, usamos a função alternativa readr::read_delim() . O pacote {readr} (Wickham, Hester, e Bryan 2024) também possui várias outras funções para situações específicas de importação. Caso a função readr::read_csv() não resolva o seu problema na leitura de algum arquivo de dados estruturado em texto, certamente outra função desse pacote resolverá.

8.2.2 Exportação de Dados

Para exportar tabelas em um arquivo .csv, basta utilizar a função readr::write_csv() . No próximo exemplo iremos criar dados artificiais, salvar em um dataframe e exportar para um arquivo .csv temporário. Veja a seguir:

library(readr)

# set number of observations
N <- 100

# create dataframe with random data
my_df <- data.frame(y = runif(N),
                    z = rep('a', N))

# write to file
f_out <- tempfile(fileext = '.csv')
write_csv(x = my_df, file = f_out)

No exemplo anterior, salvamos o dataframe chamado my_df para o arquivo file2f0e49676f05.csv, localizado na pasta temporária do computador. Podemos verificar o arquivo importando o seu conteúdo:

my_df <- read_csv(f_out,
                  col_types = cols(y = col_double(),
                                   z = col_character() ) )
print(head(my_df))
R> # A tibble: 6 × 2
R>       y z    
R>   <dbl> <chr>
R> 1 0.662 a    
R> 2 0.784 a    
R> 3 0.164 a    
R> 4 0.562 a    
R> 5 0.633 a    
R> 6 0.798 a

O resultado está conforme o esperado, um dataframe com duas colunas, a primeira com números e a segunda com texto.

Note que toda exportação com função readr::write_csv() irá ser formatada, por padrão, com a notação internacional. Caso quiser algo diferentes, verifique as opções disponíveis na função readr::write_delim() , a qual é muito mais flexível.

8.3 Arquivos Excel (xls e xlsx)

Em Finanças e Economia, é bastante comum encontrarmos dados salvos em arquivos do tipo Microsoft Excel, com extensão .xls (antiga) ou .xlsx (moderna). Apesar de não ser um formato de armazenamento de dados eficiente, esse é um programa de planilhas bastante popular devido às suas funcionalidades. É muito comum que informações sejam armazenadas e distribuídas dessa forma. Por exemplo, dados históricos do Tesouro Direto são disponibilizados como arquivos .xls no site do tesouro nacional. A CVM (Comissão de Valores Mobiliários) e ANBIMA (Associação Brasileira das Entidades dos Mercados Financeiro e de Capitais) também tem preferência por esse tipo de formato em alguns dados publicados em seu site.

A desvantagem de usar arquivos do Excel para armazenar dados é sua baixa portabilidade e o maior tempo necessário para leitura e gravação. Isso pode não ser um problema para tabelas pequenas, mas ao lidar com um grande volume de dados, o uso de arquivos Excel é frustrante e não aconselhável. Se possível, evite o uso de arquivos do Excel em seu ciclo de trabalho.

8.3.1 Importação de Dados

O R não possui uma função nativa para importar dados do Excel e, portanto, deve-se instalar e utilizar certos pacotes para realizar essa operação. Existem diversas opções, porém, os principais pacotes são, em ordem de importância, {readxl} (Wickham e Bryan 2023), {xlsx} (Dragulescu e Arendt 2020), {tidyxl} (Garmonsway 2023) e {XLConnect} (Mirai Solutions GmbH 2024).

Apesar de os pacotes anteriores terem objetivos semelhantes, cada um tem suas peculiaridades. Caso a leitura de arquivos do Excel seja algo importante no seu trabalho, aconselho-o fortemente a estudar as diferenças entre esses pacotes. Por exemplo, pacote {tidyxl} (Garmonsway 2023) permite a leitura de dados não-estruturados de um arquivo Excel, enquanto {XLConnect} (Mirai Solutions GmbH 2024) possibilita a abertura de uma conexão ativa entre o R e o Excel, onde o usuário pode transmitir dados entre um e o outro, formatar células, criar gráficos no Excel e muito mais. Nesta seção, daremos prioridade para funções do pacote {readxl} (Wickham e Bryan 2023), o qual é um dos mais fáceis e diretos de se utilizar.

Imagine agora a existência de um arquivo chamado CH04_ibovespa-Excel.xlsx que contenha os mesmos dados do Ibovespa que importamos na seção anterior. A importação das informações contidas nesse arquivo para o R será realizada através da função readxl::read_excel() :

library(readxl)
library(dplyr)

# set file
my_f <- introR::data_path("CH04_ibovespa-Excel.xlsx")

# read xlsx into dataframe
my_df <- read_excel(my_f, sheet = 'Sheet1')

# glimpse contents
glimpse(my_df)
R> Rows: 3,215
R> Columns: 2
R> $ ref_date    <dttm> 2010-01-04, 2010-01-05, 2010-01-06, 2…
R> $ price_close <dbl> 70045, 70240, 70729, 70451, 70263, 704…

Observe que, nesse caso, as datas já foram importadas com a formatação correta na classe dttm (datetime). Essa é uma vantagem ao utilizar arquivos do Excel: a classe dos dados do arquivo original é levada em conta no momento da importação. O lado negativo desse formato é a baixa portabilidade dos dados e o maior tempo necessário para a execução da importação. Como regra geral, dados importados do Excel apresentarão um tempo de carregamento mais alto do que dados importados de arquivos .csv.

8.3.2 Exportação de Dados

A exportação para arquivo Excel também é fácil. Assim como para a importação, não existe uma função nativa do R que execute esse procedimento. Para tal tarefa, temos pacotes {xlsx} (Dragulescu e Arendt 2020) e {writexl} (Ooms 2024). Para fins de ilustração de uso, vamos utilizar o pacote {writexl} (Ooms 2024)

# set number of rows
N <- 50

# create random dataframe
my_df <- data.frame(y = seq(1,N),
                    z = rep('a',N))

# write to xlsx
f_out <- fs::file_temp(ext = '.xlsx')

writexl::write_xlsx(x = my_df, path = f_out)

8.4 Formato .RData e .rds

O R possui dois formatos nativos para salvar objetos de sua área de trabalho para um arquivo local com extensão RData ou rds. O grande benefício, em ambos os casos, é que o arquivo resultante é compacto e o seu acesso é muito rápido. A desvantagem é que os dados perdem portabilidade para outros programas. A diferença entre um formato e outro é que arquivos RData podem salvar mais de um objeto, enquanto o formato .rds salva apenas um. Na prática, porém, essa não é uma restrição forte. No R existe um objeto do tipo lista que incorpora outros. Portanto, caso salvarmos uma lista em um arquivo .rds, podemos gravar no disco quantos objetos forem necessários.

8.4.1 Importação de Dados

Para carregar os dados de um aquivo RData, utilizamos a função load() :

# set a object
my_x <- 1:100

# set temp name of RData file
my_file <- introR::data_path('CH04_example-Rdata.RData')

# load it
load(file = my_file)

O arquivo CH04_example-Rdata.RData possui dois objetos, my_x e my_y, os quais se tornam disponíveis na área de trabalho depois da chamada de load() .

O processo de importação para arquivos .rds é muito semelhante. A diferença é no uso da função readr::read_rds() :

# set file path
my_file <- introR::data_path('CH04_example-rds.rds')

# load content into workspace
my_x <- readr::read_rds(file = my_file)

Comparando o código entre o uso de arquivos .RData e .rds, note que um benefício no uso de .rds é a explícita definição do objeto na área de trabalho. Isto é, o conteúdo de my_file em readr::read_rds() é explicitamente salvo em my_x. Quando usamos a função load() , no código não fica claro qual o nome do objeto que foi importado. Isso é particularmente inconveniente quando é necessário modificar o nome do objeto importado.

Use formato rds

Como sugestão, dê preferência ao uso do formato .rds, o qual deve resultar em códigos mais transparentes. A diferença de velocidade de acesso e gravação entre um e outro é mínima. O benefício de importar vários objetos em um mesmo arquivo com o formato RData torna-se irrelevante quando no uso de objetos do tipo lista (veja Seção 7.2), os quais podem incorporar outros objetos no seu conteúdo.

8.4.2 Exportação de Dados

Para criar um novo arquivo RData, utilizamos a função save() :

# set vars
my_x <- 1:100
my_y <- 1:100

# write to RData
my_file <- fs::file_temp(ext = '.RData')
save(list = c('my_x', 'my_y'),
     file = my_file)

Podemos verificar a existência do arquivo::

fs::file_exists(my_file)
R> /tmp/RtmprBT8kI/file2f0e5a6c6f3c.RData 
R>                                   TRUE

Observe que o arquivo file2f0e5a6c6f3c.RData está disponível na pasta temporária.

Já para arquivos .rds, salvamos o objeto com função readr::write_rds() :

# set data and file
my_x <- 1:100
my_file <- fs::file_temp(ext = ".rds")

# save as .rds
readr::write_rds(my_x, my_file)

# read it
my_x2 <- readr::read_rds(my_file)

# test equality
print(identical(my_x, my_x2))
R> [1] TRUE

O comando identical() testa a igualdade entre os objetos e, como esperado, verificamos que my_x e my_x2 são exatamente iguais.

8.5 Arquivos fst

Pacote {fst} (Klik 2022) foi especialmente desenhado para possibilitar a gravação e leitura de dados tabulares de forma rápida e com mínimo uso do espaço no disco. O uso deste formato é particularmente benéfico quando se está trabalhando com volumosas bases de dados em computadores potentes. O grande truque do formato fst é usar todos núcleos do computador para importar e exportar dados, enquanto todos os demais formatos se utilizam de apenas um. Como logo veremos, o ganho em velocidade é bastante significativo.

8.5.1 Importação de Dados

O uso do formato fst é bastante simples. Utilizamos a função fst::read_fst() para ler arquivos:

library(fst)

my_file <- introR::data_path('CH04_example-fst.fst')
my_df <- read_fst(my_file)

dplyr::glimpse(my_df)
R> Rows: 100
R> Columns: 8
R> $ ID     <chr> "001", "002", "003", "004", "005", "006", "…
R> $ Race   <fct> Black, White, Hispanic, Black, White, White…
R> $ Age    <int> 33, 35, 23, 87, 65, 51, 58, 67, 22, 52, 52,…
R> $ Sex    <fct> Male, Female, Male, Female, Male, Male, Fem…
R> $ Hour   <dbl> 0.00000000, 0.00000000, 0.00000000, 0.00000…
R> $ IQ     <dbl> 108, 108, 85, 106, 92, 92, 88, 100, 86, 80,…
R> $ Height <dbl> 72, 63, 77, 72, 71, 74, 64, 69, 63, 72, 70,…
R> $ Died   <lgl> FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE…

Assim como para os demais casos, os dados estão disponíveis na área de trabalho após a importação.

8.5.2 Exportação de Dados

Utilizamos a função fst::write_fst() para gravar arquivos no formato fst, :

library(fst)

# create dataframe
N <- 1000
my_file <- fs::file_temp(ext = '.fst')
my_df <- data.frame(x = runif(N))

# write to fst
write_fst(x = my_df, path = my_file)

8.5.3 Testando o Tempo de Execução do Formato fst

Como um teste do potencial do pacote {fst} (Klik 2022), a seguir vamos cronometrar o tempo de leitura e gravação entre fst e rds para um dataframe com grande quantidade de dados: 5,000,000 linhas e 2 colunas. Iremos reportar também o tamanho do arquivo resultante.

library(fst)

# set number of rows
N <- 5000000

# create random dfs
my_df <- data.frame(y = seq(1,N),
                    z = rep('a',N))

# set files
my_file_rds <- fs::file_temp(ext = ".rds")
my_file_fst <- fs::file_temp(ext = ".fst")

# test write
time_write_rds <- system.time(readr::write_rds(my_df, my_file_rds ))
time_write_fst <- system.time(fst::write_fst(my_df, my_file_fst ))

# test read
time_read_rds <- system.time(readr::read_rds(my_file_rds))
time_read_fst <- system.time(fst::read_fst(my_file_fst))

# test file size (MB)
file_size_rds <- file.size(my_file_rds)/1000000
file_size_fst <- file.size(my_file_fst)/1000000

Após a execução, vamos verificar o resultado:

# results
my_formats <- c('.rds', '.fst')
results_read <- c(time_read_rds[3], time_read_fst[3])
results_write<- c(time_write_rds[3], time_write_fst[3])
results_file_size <- c(file_size_rds , file_size_fst)

# print text
my_text <- paste0('\nTime to WRITE dataframe with ',
                  my_formats, ': ',
                  results_write, ' seconds', collapse = '')
message(my_text)
R> 
R> Time to WRITE dataframe with .rds: 0.574 seconds
R> Time to WRITE dataframe with .fst: 0.0950000000000002 seconds
my_text <- paste0('\nTime to READ dataframe with ',
                  my_formats, ': ',
                  results_read, ' seconds', collapse = '')
message(my_text)
R> 
R> Time to READ dataframe with .rds: 0.737 seconds
R> Time to READ dataframe with .fst: 0.109 seconds
my_text <- paste0('\nResulting FILE SIZE for ',
                  my_formats, ': ',
                  results_file_size, ' MBs', collapse = '')
message(my_text)
R> 
R> Resulting FILE SIZE for .rds: 65.000177 MBs
R> Resulting FILE SIZE for .fst: 14.791938 MBs

A diferença é gritante! O formato fst não somente lê e grava com mais rapidez mas o arquivo resultante também é menor. Porém, saiba que os resultados anteriores foram compilados em um computador com 16 núcleos. É possível que a diferença de tempo para um computador mais modesto não seja tão significativa.

Use formato fst para banco de dados volumosos

Devido ao uso de todos os núcleos do computador, o formato fst é altamente recomendado quando estiver trabalhando com dados volumosos em um computador potente. Não somente os arquivos resultantes serão menores, mas o processo de gravação e leitura será consideravelmente mais rápido.

8.6 Dados Não-Estruturados e Outros Formatos

Os pacotes e formatos anteriores são suficientes para resolver o problema de importação de dados na grande maioria das situações. Apesar disso, vale destacar que o R possui outras funções específicas para diferentes formatos. Isso inclui arquivos exportados de outros softwares, tal como SPSS, Matlab, entre vários outros. Se esse for o seu caso, sugiro um estudo aprofundado do pacote {foreign} (R Core Team 2023).

Em alguns casos nos deparamos com dados armazenados de uma forma não estruturada, tal como um texto qualquer. Pode-se importar o conteúdo de um arquivo de texto linha por linha através da função readr::read_lines() . Veja o exemplo a seguir, onde importamos o conteúdo inteiro do livro Pride and Prejudice:

# set file to read
my_f <- introR::data_path('CH04_price-and-prejudice.txt')

# read file line by line
my_txt <- readr::read_lines(my_f)

# print 50 characters of first fifteen lines
print(stringr::str_sub(string = my_txt[1:15], 
              start = 1, 
              end = 50))
R>  [1] "                            [Illustration:"        
R>  [2] ""                                                  
R>  [3] "                             GEORGE ALLEN"         
R>  [4] "                               PUBLISHER"          
R>  [5] ""                                                  
R>  [6] "                        156 CHARING CROSS ROAD"    
R>  [7] "                                LONDON"            
R>  [8] ""                                                  
R>  [9] "                             RUSKIN HOUSE"         
R> [10] "                                   ]"              
R> [11] ""                                                  
R> [12] "                            [Illustration:"        
R> [13] ""                                                  
R> [14] "               _Reading Jane’s Letters._      _Cha"
R> [15] "                                   ]"

Neste exemplo, arquivo CH04_price-and-prejudice.txt contém todo o conteúdo do livro Pride and Prejudice de Jane Austen, disponível gratuitamente pelo projeto Gutenberg. Importamos todo o conteúdo do arquivo como um vetor de texto denominado my_txt.

8.6.1 Exportando de Dados Não-Estruturados

Em algumas situações, é necessário exportar algum tipo de texto para um arquivo. Por exemplo: quando se precisa salvar o registro de um procedimento em um arquivo de texto; ou quando se precisa gravar informações em um formato específico não suportado pelo R. Esse procedimento é bastante simples. Junto à função readr::write_lines() , basta indicar um arquivo de texto para a saída com o argumento file. Veja a seguir:

# set file
my_f <- fs::file_temp(ext = '.txt')

# set some string
my_text <- paste0('Today is ', Sys.Date(), '\n', 
                  'Tomorrow is ', Sys.Date()+1)

# save string to file
readr::write_lines(x = my_text, file = my_f, append = FALSE)

No exemplo, criamos um objeto de texto com uma mensagem sobre a data atual e gravamos a saída no arquivo temporário file2f0e73cad2eb.txt. Podemos checar o resultado com a função readr::read_lines() :

print(readr::read_lines(my_f))
R> [1] "Today is 2024-04-29"    "Tomorrow is 2024-04-30"

8.7 Selecionando o Formato

Após entendermos a forma de salvar e carregar dados de arquivos locais em diferentes formatos, é importante discutirmos sobre a escolha do formato. O usuário deve levar em conta três pontos nessa decisão:

  • velocidade de importação e exportação;
  • tamanho do arquivo resultante;
  • compatibilidade com outros programas e sistemas.

Na grande maioria das situações, o uso de arquivos csv satisfaz esses quesitos. Ele nada mais é do que um arquivo de texto que pode ser aberto, visualizado e importado em qualquer programa. Desse modo, fica muito fácil compartilhar dados compatíveis com outros usuários. Além disso, o tamanho de arquivos csv geralmente não é exagerado em computadores modernos. Caso necessário, podes compactar o arquivo .csv usando o programa 7zip, o qual irá diminuir consideravelmente o tamanho do arquivo. Por esses motivos, o uso de arquivos csv para importações e exportações é preferível na grande maioria das situações.

Existem casos, porém, onde a velocidade de importação e exportação pode fazer diferença. Caso abrir mão de portabilidade não faça diferença ao projeto, o formato rds é ótimo e prático. Se este não foi suficiente, então a melhor alternativa é partir para o fst, o qual usa maior parte do hardware do computador para importar os dados. Como sugestão, caso puder, apenas evite o formato do Excel, o qual é o menos eficiente de todos.

8.8 Importação de dados via Pacotes

Uma das grandes vantagens de se utilizar o R é a quantidade de dados que podem ser importados através da internet, seja via download de arquivos ou APIs oficiais. Isso é especialmente prático pois uma base de dados pode ser atualizada através de um simples comando, evitando o tedioso trabalho de coleta manual. Ao usarmos pacotes para importar dados, esta etapa da pesquisa se torna reproduzível e mais rápida, facilitando o compartilhamento e futura execução do nosso código. O uso de pacotes para a importação de dados externos é uma tendência muito forte no uso do R, e é importante que entenda o processo funciona.

Abaixo segue uma lista e descrição de pacotes importante na área de finanças e economia, os quais uso fortemente no meu dia a dia como professor e pesquisador. Mesmo que você não seja da área, poderás retirar ótimas lições aqui sobre como realizar a importação dos dados da internet.

{yfR} (Perlin 2023)
Importa dados de preços diários de ações e índices do Yahoo Finance.
{GetTDData} (Perlin 2024)
Importa dados de títulos de dívida pública do Brasil diretamente do site do Tesouro Direto.
{rb3} (Freitas e Perlin 2023)
Importa diversos dados diretos do site da B3, incluindo preços de transação, curvas de juros históricas e muito mais.
{GetBCBData} (Perlin 2022)
Importa dados do grande repositório de séries temporais do Banco Central do Brasil, local obrigatório para qualquer economista que trabalha com dados.
{GetDFPData2} (Perlin e Kirch 2023)
Importa dados do sistema DFP – Demonstrativos Financeiros Padronizados – de empresas negociadas na B3, a bolsa Brasileira. O repositório inclui documentos financeiros tal como o balanço patrimonial, demonstrativos de resultados, entre vários outros.
{GetFREData} (Perlin e Kirch 2022)
Importa dados do sistema FRE – Formulário de Referência – da bolsa Brasileira. Esta inclui diversos eventos e informações corporativas tal como composição do conselho e diretoria, remuneração dos conselheiros, entre outras.

Cada um dos pacotes anteriores possui um tutorial de introdução em suas páginas do github. Por exemplo, pacote {yfR} (Perlin 2023) possui um tutorial em https://docs.ropensci.org/yfR/. Lá encontrarás uma descrição completa do pacotes, assim como códigos reproduzíveis no seu computador, basta copiar e colar na sua sessão do R. Esse é um padrão comum entres os pacotes. Assim, para aprender mais sobre cada um, basta ir no respectivo site do github.

8.9 Exercícios


Q.1 - Use função introR::data_path para acessar o arquivo SP500.csv no repositório de dados do livro. Importe o conteúdo do arquivo no R com função readr::read_csv() . Quantas linhas existem no dataframe resultante?


Q.2 - No link https://eeecon.uibk.ac.at/~zeileis/grunfeld/Grunfeld.csv você encontrará um arquivo .csv para os dados Grunfeld. Esta é uma tabela particularmente famosa devido ao seu uso como dados de referência em modelos econométricos do tipo painel. Usando função readr::read_csv() , leia este arquivo usando o link direto como entrada em readr::read_csv() . Quantas colunas você encontra no dataframe resultante?


Q.3 - No pacote do livro existe um arquivo de dados chamado CH04_another-funky-csv-file.csv. Este possui um formato particularmente bizarro para os dados. Abra o mesmo em um editor de texto e procure entender como as colunas são separadas e qual o símbolo para o decimal. Após isso, veja as entradas da função read.table e importe a tabela na sessão do R. Caso somarmos o número de linhas com o número de colunas da tabela importada, qual o resultado?


Q.4 - Use função introR::data_path para acessar o arquivo CH04_example-tsv.tsv no repositório de dados do livro. Note que as colunas dos dados estão separadas pelo símbolo de tabulação ('\t'). Após ler o manual do readr::read_delim, importe as informações deste arquivo para o seu computador. Quantas linhas o arquivo contém?


Q.5 - Crie um dataframe com duas colunas contendo números aleatórios da distribuição Normal. Exporte o dataframe resultante para cada um dos cinco formatos: csv, rds, xlsx, fst. Qual dos formatos ocupou maior espaço na memória do computador? Dica: file.size calcula o tamanho de arquivos dentro do próprio R.


Q.6 - Melhore o código anterior com a mensuração do tempo de execução necessário para gravar os dados nos diferentes formatos. Qual formato teve a gravação mais rápida? Dica: use função system.time ou pacote tictoc para calcular os tempos de execução.


Q.7 - Para o código anterior, redefina o valor de my_N para 1000000. Esta mudança modifica as respostas das duas últimas perguntas?