Como organizar as informações para facilitar as análises
Nessa seção vamos usar mecanismos de pacotes já feitos no
R. Existem inúmeros pacotes, cada um com suas funções
e/ou bancos de dados para diferentes assuntos. Eles podem, inclusive,
ser apenas um aglomerado de diferentes pacotes para não precisar baixar
todos um por um. A primeira etapa de utilizar um pacote é a sua
instalação, isso é feito com o comando install.packages()
com o nome do pacote estando entre aspas (““) dentro dos parêntesis.
Assim podemos fazer com todos que se encontram no CRAN (repositório que
contém o que queremos). Na aula de hoje utilizaremos apenas o
tidyverse, que é um agregado de pacotes extremamente
populares hoje para manipulação e análise de dados.
install.packages("tidyverse")
Para que o R não fique muito pesado, todos os
pacotes baixados precisam ser carregados na sua seção do
R - isso é feito com o comando library()
com o pacote estando detro do parêntesis sem aspas.
Podemos notar que ao carregar o pacote dentro da nossa seção primeiro
vemos quais outros pacotes que foram carregados e suas respectivas
versões. Nesse exemplo, ao carregar o tidyverse, ele
contém o ggplot2, purrr,
tibble, dplyr, tidyr,
stringr, readr e
forcats. Além disso, aparece uma mensagem mostrando
“conflitos”. Esses conflitos são quando diferentes pacotes utilizam
funções de mesmo nome (as vezes para fazer coisas diferentes), assim o
R te avisa quais funções são iguais e quais serão as
utilizadas normalmente no programa. É possível usar qualquer função se
deixarmos explícito de qual pacote queremos (por exemplo
stats::filter()
)
Primeiro vamos baixar apenas uma das estações que fizemos na última aula. Ao final podemos repetir o que foi feito anteriormente e criar um loop para repetir para as outras 3 estações.
dados_ANA <- read.table(file = "dados/60435000.txt",
sep = "\t",
dec = ".",
header = T,
fileEncoding = "UTF-8")
Apesar de termos mexido nesses dados na última aula, o R não sabe disso, ele baixa o arquivo como se fosse um outro qualquer, assim a coluna da estação é do tipo “inteiro” e a segunda coluna (de data) está do tipo “caractere”. Vemos isso abaixo e em seguida corrigimos ambas para caractere e data, respectivamente.
class(dados_ANA$Cod_estacao)
[1] "integer"
class(dados_ANA$Data)
[1] "character"
dados_ANA$Cod_estacao <- as.character(dados_ANA$Cod_estacao)
dados_ANA$Data <- as.Date(dados_ANA$Data)
Agora que temos um arquivo do jeito que queríamos (diferente do que pegamos diretamente do servidor da ANA), fazer algumas análises podem ser bem fáceis. Se quisermos saber de quando a quando essa estação tem dados, basta vermos os valores mínimos e máximos da Data.
min(dados_ANA$Data)
[1] "1978-05-01"
max(dados_ANA$Data)
[1] "2021-11-30"
Se quisermos saber qual valor mínimo, máximo e médio de vazão podemos
também, mas aparece um problema! Nesses valores temos dados em branco,
ou NA
(Not Available). Dados faltantes sempre são
problemático e devem ser tratados quando possível. Para algumas funções
nativas, como max()
, min()
e
mean()
temos a opção de apenas ignorar eles com o argumento
na.rm = TRUE
, assim os valores máximos, médios e mínimos
são calculados sem considerar os dados em branco.
max(dados_ANA$Vazao, na.rm = T)
[1] 37.4633
min(dados_ANA$Vazao, na.rm = T)
[1] 0.0539
mean(dados_ANA$Vazao, na.rm = T)
[1] 2.096619
Antes mesmo de começar a fazer essas avaliações é interessante e
importante entender melhor os dados em branco. Primeiro, quantos
existem? Para isso podemos usar a função is.na()
, vamos
rodar ela apenas para os primeiros 15 dados de vazão para mostrar na
tela.
is.na(dados_ANA$Vazao)[1:15]
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[12] FALSE FALSE FALSE FALSE
Essa função retorna um dado TRUE
ou FALSE
para cada valor avaliado. Como dados lógicos também são
binários/numéricos (TRUE = 1 e FALSE =
0), podemos somar esses valores para ver quantos dados em
branco temos no total. Temos então
sum(is.na(dados_ANA[,3]))
dados em branco.
Para saber quais as datas que tem o valor de vazão em branco basta
aplicarmos esse is.na()
na coluna de datas.
dados_ANA$Data[is.na(dados_ANA$Vazao)]
[1] "1978-05-01" "1978-05-02" "1978-05-03" "1978-05-04" "1978-05-05"
[6] "1978-05-06" "1978-05-07" "1978-05-08" "1978-05-09" "1978-05-10"
[11] "1978-05-11" "2008-12-01" "2008-12-02" "2008-12-03" "2008-12-04"
[16] "2008-12-05" "2008-12-06" "2008-12-07" "2008-12-08" "2008-12-09"
[21] "2008-12-10" "2008-12-11" "2008-12-12" "2008-12-13" "2008-12-14"
[26] "2008-12-15" "2008-12-16" "2008-12-17" "2008-12-18" "2008-12-19"
[31] "2008-12-20" "2008-12-21" "2008-12-22" "2008-12-23" "2008-12-25"
[36] "2008-12-26" "2008-12-27" "2008-12-28" "2008-12-29" "2008-12-30"
[41] "2008-12-31" "2009-01-01" "2009-01-02" "2009-01-03" "2009-01-04"
[46] "2009-01-05" "2009-01-06" "2009-01-07" "2009-01-08" "2009-01-09"
[51] "2009-01-10" "2009-01-11" "2009-01-12" "2009-01-13" "2009-01-14"
[56] "2009-01-15" "2019-08-01" "2019-08-02" "2019-08-03" "2019-08-04"
[61] "2019-08-05" "2019-08-06" "2019-08-07" "2019-08-08" "2019-08-09"
[66] "2019-08-10" "2019-08-11" "2019-08-12" "2019-08-13" "2019-08-14"
[71] "2019-08-15" "2019-08-16" "2019-08-17" "2019-08-18" "2019-08-19"
[76] "2019-08-20" "2019-08-21" "2019-08-22" "2019-08-23" "2019-08-24"
[81] "2019-08-25" "2019-08-26" "2019-08-27" "2019-08-28" "2019-08-29"
[86] "2019-08-30" "2019-08-31"
E se quiser o último (a última data com vazão em branco), podemos
fazer o max()
dessas datas todas.
O que fazer com cada dado em branco vai depender do que se quer com os dados. Como completar essas falhas é uma temática extremamente relevante e difícil de responder até mesmo hoje. Pararemos por hora de falar desses valores, e não entraremos muito a fundo sobre o que fazer com eles (até porque depende do que se quer fazer) nesse curso.
Em hidrologia estatística, é muito comum utilizarmos o ano hidrológico ao invés do calendário “normal” (janeiro a dezembro) para fazer análises estatísticas. Onde começa o ano hidrológico também não é tão simples, mas a via de regra é: entre as secas e cheias.
Faremos aqui uma função para transformar nossa data em ano
hidrológico, assim conseguiremos fazer algumas análises anuais
interessantes. A função, chamada aqui de fun_ano_hidro()
tomará 2 argumentos: um sendo as datas que queremos transformar e o
outro em que mês que se inicia o ano hidrológico, aqui deixamos
pré-definido como 8, mas se quiser mudar isso, basta alterar o argumento
na hora de chamar ela (assim não ficamos preso a esse número)! Primeiro
devemos garantir que as datas estão no formato certo, portanto fazemos
essa transformação no começo da função. Em seguida definimos qual será o
ano hidrológico. O que fazemos é avaliar se o mês em questão é menor ou
maior do que o começo do ano hidrológico definido pela função. Se for
maior, significa que já entramos em outro ano hidrológico, portanto o
ano em questão é o seguinte (o mês 8, Agosto, de 2000 entra para o ano
hidrológico de 2001 e assim por diante).
fun_ano_hidro <- function(datas,
comeco_ano_hidro = 8){
# Se não tiver no formato de datas, fazer essa correção
datas <- as.Date(datas)
# Se vai pular (deslocar) um ano ou não
desloc_ano <- ifelse(month(datas) < comeco_ano_hidro, 0, 1)
# Ano Hidro
ano_hidro <- year(datas) + desloc_ano
return(ano_hidro)
}
Abaixo testamos ela fazendo uma nova coluna no nosso dataframe.
dados_ANA$ano_hidro <- fun_ano_hidro(dados_ANA$Data)
head(dados_ANA)
Cod_estacao Data Vazao ano_hidro
1 60435000 1978-05-01 NA 1978
2 60435000 1978-05-02 NA 1978
3 60435000 1978-05-03 NA 1978
4 60435000 1978-05-04 NA 1978
5 60435000 1978-05-05 NA 1978
6 60435000 1978-05-06 NA 1978
Aqui introduzimos um novo conceito do pacote
magrittr (ele está internamente no
tiyverse). Esse conceito se chama pipe
operator e é dado pelo símbolo %>%
. Basicamente
o que ele faz é tomar o que está na esquerda dele, e usar como argumento
da função a direita. Assim ao invés de escrever as funções umas dentro
das outras, podemos deixar o código mais limpo e fácil de entender. Olhe
o exemplo abaixo. Nele estamos tirando o log das vazões, com a função
log()
, depois calculando as diferenças desse log em um
passo de tempo com diff()
, calculamos o exponencial disso
com exp()
, em seguimas calculamos a média desses valores e
depois arredondamos isso para ter apenas 2 casas decimais.
É fácil se perder lendo isso pois no R normalmente
lemos de dentro para fora das funções (e portanto da direita para a
esquerda) e é mais fácil ainda confundir qual argumento entra em qual
função. Usando o pipe operator, dado por
%>%
, o código fica mais facilmente interpretável.
Selecionamos a vazão e em seguida tiramos o log, depois a diferença,
depois o expoente… Quando uma função tem diversos argumentos podemos
escrever explicitamente onde que o objeto a esquerda do pipe
vai entrar colocando um ponto no lugar dele, não precisando que ele
sempre entre no primeiro argumento da função.
[1] 1.03
Agora que temos o ano hidrológico para cada data, podemos fazer a nossa série de máximos, mínimos e médias anuais a partir dos dados diários. Primeiro cria-se um dataframe vazio com as colunas para cada ano, uma coluna para os máximos, outra para os mínimos e outra para as médias anuais. Podemos também criar uma quarta coluna para o número de valores NA nesse período.
Já iremos fazer isso no formato de função, para que possamos replicar
para as outras estações também! A função tomará como argumento a tabela
original de dados (dados_ANA
), e irá rodar a função
fun_ano_hidro()
internamente. Assim teremos uma nova coluna
criada com a função mutate()
. Em seguida agrupamos os dados
por estação (se tivesse mais de uma por exemplo) e por ano hidrológico
usando o group_by
e falamos o que faremos ao juntar esses
dados com o summarise()
. Um problema é se o ano hidrológico
estiver todo com falha, nesses casos as funções usadas retornarão
Inf
(infinito), -Inf
ou NaN
(Not a Number), portanto depois de agruparmos, iremos trocar
qualquer valor não-finito na tabela por NA.
Rodar uma outra função internamente requer alguns cuidados. Por exemplo, aqui assumimos que a tabela virá com uma coluna chamada “Data”. Caso isso não aconteca a nossa função não irá funcionar.
fun_resumo_ano <- function(dados_hidro){
tabela_resumo <-
dados_hidro %>%
mutate(ano_hidro = fun_ano_hidro(Data)) %>%
group_by(Cod_estacao, ano_hidro) %>%
summarise(maxima = max(Vazao, na.rm = T),
minima = min(Vazao, na.rm = T),
media = mean(Vazao, na.rm = T),
NAs = sum(is.na(Vazao)))
tabela_resumo[!is.finite(tabela_resumo$maxima), c(3, 4, 5)] <- NA
return(tabela_resumo)
}
Agora podemos aplicar essa função diretamente aos
dados_ANA
da última aula. Na realidade, podemos fazer um
loop para abrir cada uma das 4 estações, rodar essa função e
juntar essas tabelas em uma só com a função rbind()
- essa
função junta diferentes dataframes pelas mesmas colunas,
colocando um embaixo do outro. Abaixo rodamos o loop de outra
maneira, definindo antes quais os códigos das estações e rodando o
for()
variando de 1 a 4. Primeiro lemos a primeira estação
e, se for a primeira iteração, salvamos ela em um objeto chamado
tabela_final. Se não for a primeira iteração, juntamos elas com
o rbind()
.
estacoes_cod <- c(60435000, 60436000, 60436190, 60443000)
for(i in 1:4){
dados_proxy <- read.table(file = paste0("dados/", estacoes_cod[i],".txt"),
sep = "\t",
dec = ".",
header = T,
fileEncoding = "UTF-8")
dados_proxy <- fun_resumo_ano(dados_proxy)
if(i == 1) tabela_final <- dados_proxy
if(i != 1) tabela_final <- rbind(tabela_final, dados_proxy)
}
Por último, podemos salvar esse arquivo também, para usarmos nas aulas seguintes.
write.table(x = tabela_final,
file = "Resumo_estacoes.txt",
sep = "\t",
dec = ".",
row.names = FALSE,
fileEncoding = "UTF-8")
If you see mistakes or want to suggest changes, please create an issue on the source repository.
Text and figures are licensed under Creative Commons Attribution CC BY 4.0. Source code is available at https://github.com/DirceuReis/Mini_Curso_UnB_SemUniv_AnaliseHidro_R, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".