Hackeando o R: Rodando uma regressão com métodos bayesianos

By | Hackeando o R

No Hackeando o R de hoje, vamos finalizar a série sobre estatística bayesiana mostrando como pode ser feita a estimação de um modelo linear simples com MCMC. Diferentemente do método padrão, onde consideramos que a variável de resposta tem distribuição normal após controlarmos para efeitos lineares, os métodos bayesianos permitem maior liberdade quanto às hipóteses tomadas, dado que essencialmente tornam a estimação de parâmetros um problema de seleção de crenças iniciais. No caso a seguir, iremos fazer uma regressão linear robusta, supondo que a distribuição é na verdade uma t de Student.

A distribuição t em sua forma generalizada possui três parâmetros, de média, escala e seus graus de liberdade. Destes, a média é o de maior interesse, pois deve corresponder à previsão do modelo, que, conforme conhecemos, possui dois parâmetros (constante e linear). Note que, com isso, a estimação de y depende de um parâmetro que depende de outros dois, o que torna esse um modelo hierarquizado. Para rodarmos o algoritmo de MCMC, geramos crenças a priori dos parâmetros 'de ponta', e construímos a condicional em passos. Abaixo, geramos o modelo com distribuições usuais:

modelString = "
data {
Ntotal = length(y)
xm = mean(x)
ym = mean(y)
xsd = sd(x)
ysd = sd(y)
for ( i in 1:length(y) ) {
zx[i] = ( x[i] - xm ) / xsd
zy[i] = ( y[i] - ym ) / ysd
}
}

model {
for ( i in 1:Ntotal ) {
zy[i] ~ dt( zbeta0 + zbeta1 * zx[i] , 1/zsigma^2 , nu )
}

zbeta0 ~ dnorm( 0 , 1/(10)^2 ) 
zbeta1 ~ dnorm( 0 , 1/(10)^2 )
zsigma ~ dunif( 1.0E-3 , 1.0E+3 )
nu ~ dexp(1/30.0)

beta1 = zbeta1 * ysd / xsd 
beta0 = zbeta0 * ysd + ym - zbeta1 * xm * ysd / xsd 
sigma = zsigma * ysd
}
"
writeLines( modelString , con="TEMPmodel.txt" )

É importante notar que fizemos a padronização dos dados para rodar o modelo. Isso não é necessário, porém facilita a convergência do MCMC. Após isso, basta carregar os dados, disponíveis aqui,
e rodar o modelo.

myData = read.csv(file="HtWtData30.csv" )
xName = "height" ; yName = "weight"

y = myData[,yName]
x = myData[,xName]

dataList = list(
x = x ,
y = y
)

library(rjags)

parameters = c("beta0", "beta1", "sigma", "nu" )

jagsModel = jags.model( "TEMPmodel.txt" , data=dataList ,
n.chains=4 , n.adapt=500)

codaSamples = coda.samples(jagsModel, variable.names = parameters,
n.iter= 50000, thin=1)

plot(codaSamples)

Nested loop com o pacote purrr no R

By | Data Science

O pacote purrr é um dos pilares do tidyverse, ao lado do dplyr e do operador pipe em termos de utilidade e generalização. Um de seus principais objetivos é facilitar a intenção de fazer um loop com a sintaxe envolvida. Ao invés de ter que escrever um monte de códigos tediosos do tipo "for-loop" - que pouco diz ao leitor sobre o que o loop faz -, a função map do purrr pode ser lida quase como inglês puro e se encaixa perfeitamente em operações encadeadas (pipe).

Mas isso não é tudo que o purrr tem a oferecer. O pacote vai muito além das funcionalidades map, walk e suas variantes, ajudando ainda mais no trabalho com listas. Neste exercício vamos utilizar algumas dessas funcionalidades do purrr para aplicações do tipo nested loop.

Primeiro vamos entender o nested loop com um exemplo mínimo, sem usar o pacote purrr. Vamos supor que você tenha dois vetores numéricos de tamanhos diferentes e que você queira todas as combinações possíveis dos valores de ambos os vetores, concatenando os valores em um único vetor separado por um hífen. Isso poderia ser feito com o controle de fluxo for:


# Nested loop
for (i in 1:2) {
for (j in 1:4) {
print(paste(i, j, sep = "-"))
}
}

## [1] "1-1"
## [1] "1-2"
## [1] "1-3"
## [1] "1-4"
## [1] "2-1"
## [1] "2-2"
## [1] "2-3"
## [1] "2-4"

Com o purrr poderíamos fazer a mesma operação com a função walk neste contexto, onde o .y do loop de dentro é o vetor numérico 1:2 do loop de fora:


library(purrr)

# Nested loop
walk(1:2, ~walk(1:4, ~print(paste(.y, .x, sep = "-")), .y = .x))

## [1] "1-1"
## [1] "1-2"
## [1] "1-3"
## [1] "1-4"
## [1] "2-1"
## [1] "2-2"
## [1] "2-3"
## [1] "2-4"

Uma outra opção seria, em verdade, evitar o nested loop, já que é de leitura dificultosa e requer um trabalho adicional desnecessário. Portanto, seguindo o conselho do pai da linguagem de programação C++, Bjarne Stroustrup:

To become significantly more reliable, code must become more transparent. In particular, nested conditions and loops must be viewed with great suspicion. Complicated control flows confuse programmers. Messy code often hides bugs.

Poríamos criar antes uma combinação de valores usando a função cross2 e então iterar esses elementos em um loop:


library(magrittr)

cross2(1:2, 1:4) %>%
walk(
~print(paste(.[[1]], .[[2]], sep = "-"))
)

## [1] "1-1"
## [1] "2-1"
## [1] "1-2"
## [1] "2-2"
## [1] "1-3"
## [1] "2-3"
## [1] "1-4"
## [1] "2-4"

Muito melhor, concorda?

Agora vamos a um exemplo prático da vida real. Suponha que você esteja trabalhando com os dados desagregados da inflação brasileira, medida pelo IPCA do IBGE e que precise coletar esses dados usando sua API através do pacote sidrar no R. A coleta dos dados pela API tem uma limitação de 50 mil observações por requisição, portanto você precisa criar uma estratégia para coletar esses dados sem ultrapassar esse limite da API.

Uma maneira de fazer isso, não a melhor, é justamente através de um nested loop. Para operacionalizar vamos coletar os dados do IPCA provenientes das tabelas 1419 e 7060 e suas variáveis do SIDRA/IBGE:


library(sidrar)

# Vetor com códigos das tabelas do IPCA
tables <- c("Tabela 1419" = 1419, "Tabela 7060" = 7060)

# Vetor com códigos das variáveis da tabela
variables <- c(
"IPCA - Variacao mensal (%)" = 63,
"IPCA - Peso mensal" = 66,
"IPCA - Variação acumulada no ano (%)" = 69,
"IPCA - Variação acumulada em 12 meses (%)" = 2265
)

# Coletar dados com nested loop
ipca <- tables %>%
map_dfr(
~map_dfr(
.x = variables,
~get_sidra(
x = .y, # vetor de códigos das tabelas (tables)
variable = .x, # vetor de códigos das variáveis (variables)
period = "all"
),
.y = .x
)
)

A outra possibilidade é gerar primeiro uma lista com combinações entre os códigos de tabelas e códigos das variáveis e, então, iterar estes elementos em um único loop:


library(dplyr)

# Coletar dados iterando combinações no loop
ipca2 <- cross2(tables, variables) %>%
map_dfr(
~get_sidra(
x = .[[1]],
variable = .[[2]],
period = "all"
)
)

# Verificar se resultados são equivalentes
all.equal(
arrange(ipca, `Mês`),
arrange(ipca2, `Mês`)
)

## [1] TRUE

Vale enfatizar que eu apenas arranhei a superfície do assunto neste texto, mas espero ter pelo menos instigado você a fazer sua própria investigação sobre o que o purrr pode oferecer, além de tornar as iterações mais legíveis, principalmente para os casos de nested loop.

 

Hackeando o R: Utilizando o RJAGS

By | Hackeando o R

No Hackeando o R de hoje, vamos continuar a desenvolver o ferramental de estatística bayesiana, e para isso vamos mostrar como utilizar a interface do R para o programa JAGS (Just Another Gibbs Sampler), que é especializado em métodos de MCMC. No post da semana passada, apresentamos o algoritmo de Metropolis, que define um passo-a-passo para criar novas observações da distribuição desconhecida dos parâmetros que queremos estimar. Existem outros métodos de gerar essa amostragem, porém não iremos nos preocupar a fundo com eles, e deixar que o JAGS escolha o mais apropriado. Com isso, nosso trabalho passa a ser ter em mãos a distribuição a priori dos parâmetros, e a distribuição dos dados condicionada aos parâmetros. Para reproduzir os códigos desse post, você precisará baixar o JAGS, disponível aqui.

Conforme vimos, para fazer a estimação do parâmetro de interesse, precisamos de uma distribuição a priori, a distribuição condicional de uma amostra, e uma amostra que irá atualizar nossas crenças sobre o parâmetro. A estimação do MCMC será feita pelo JAGS, através do pacote rjags. Esse sistema trabalha com listas, logo devemos gerar uma lista da nossa amostra a partir dos dados dela. Os dados estão disponíveis aqui.

dados <- readr::read_csv('z15N50.csv')

dataList = list(y=dados$y)

Note que geramos uma lista com um único elemento, que é nossa amostra. O próximo passo é definir o modelo, na sintaxe do JAGS. Isso é feito com uma string, que é salva em um arquivo na mesma pasta em que estamos trabalhando. A sintaxe é de modo geral a mesma para os modelos, definindo para cada elemento da amostra (dentro do for) a sua distribuição em função do parâmetro, e a distribuição a priori do parâmetro. Abaixo, definimos uma priori Beta(1, 1), ou seja, uma distribuição uniforme.


modelString = "
model {
for ( i in 1:50 ) {
y[i] ~ dbern( theta )
}
theta ~ dbeta(1,1)
}
"
writeLines( modelString , con="TEMPmodel.txt" )

O último passo que resta para rodarmos o algoritmo é definirmos o ponto inicial. Isso é arbitrário, porém se escolhermos pontos iniciais ruins, a convergência pode demorar, de modo que é preferível que o ponto inicial esteja em uma região de alta probabilidade da posterior. Um método possível é selecionar diversos pontos e rodar múltiplos MCMCs, porém para reduzir o código, vamos utilizar a estimativa de MLE da amostra, que é a média. Novamente, precisamos colocar o elemento em uma lista, e o nome dele deve ser igual ao nome passado ao modelo acima.


thetaInit = sum(dados$y)/length(dados$y)
initsList = list(theta=thetaInit)

Com isso, basta carregar o pacote rjags e rodar a função jags.model. Antes de gerarmos nossa cadeia de Markov, tomamos duas medidas iniciais: a adaptação e o burn-in. A adaptação é uma série de amostras que o JAGS extrai para calibrar suas estimativas das condicionais, e também escolher a magnitude ótima para as tentativas de salto que são feitas a cada passo do algoritmo. Esse processo não gera uma cadeia de Markov, logo é descartado do resultado final.

Se nossa estimativa inicial é mal colocada, é possível que ela dê foco demasiado para regiões de baixa probabilidade da posterior nas primeiras observações, até que ela encontre regiões de maior probabilidade. Para evitar que isso contamine a distribuição estimada final, é padrão eliminarmos o trecho inicial do caminho percorrido, chamado de burn-in. Abaixo, deixamos 500 amostras para a adaptação, e 500 amostras de burn-in. Após isso, geramos 3334 da cadeia final com a função coda.samples(), resultando em cerca de 10000 observações, dado que estamos criando 3 cadeias.


library(rjags)

jagsModel = jags.model( file="TEMPmodel.txt" , data=dataList , inits=initsList ,
n.chains=3 , n.adapt=500 )

update( jagsModel , n.iter=500 )

codaSamples = coda.samples( jagsModel , variable.names=c("theta") ,
n.iter=3334 )

Para visualizar o resultado final, vamos utilizar as visualizações do coda, pacote que gerou as amostras do MCMC. O gráfico da esquerda mostra o caminho de cada simulação, enquanto o da direita é uma combinação das distribuições estimadas de cada cadeia.


plot(codaSamples)

Relatório AM #15 - Crédito

By | Indicadores

No Relatório AM dessa semana, vamos comentar sobre o setor de crédito. Como podemos ver nos gráficos abaixo, as concessões de crédito nos últimos anos contam a história recente da economia brasileira através da confiança que os credores têm no estado geral da economia, no caso das concessões livres. Essa medida, que de modo geral está em escala muito maior que o crédito direcionado, sofreu forte impacto com a crise de 2016, porém já estava em plena recuperação no começo de 2020. Com a incerteza causada pela pandemia, há um duro choque nas concessões, porém os dados indicam novamente a recuperação. Por outro lado, o crédito direcionado, que esteve em seu auge até 2015, não se recuperou da crise de 2016, aumentando apenas durante a pandemia, com a concessão de crédito sendo utilizada como um combate à recessão.

Outro efeito da crise de 2016 que pode ser observado é o recuo da participação do crédito privado no crédito total. Isso pode ser considerado indicativo de que, com o estouro da crise, os credores estavam sendo mais cautelosos frente à incerteza da economia, o que é corroborado pela diminuição do estoque de crédito entre seu pico em 2016 e 2019.

Para finalizar, visualizamos abaixo a pesquisa trimestral de condições de crédito. É interessante notar que os movimentos não são perfeitamente sincronizados com a tendência generalizada, com a oferta de crédito habitacional e para grandes empresas sendo desfavorecida logo no começo de 2015, enquanto que o crédito para consumo e para pequenas empresas (MPMEs) demorou a se retrair. Com a pandemia, todos os 4 tipos tiveram redução nas condições de oferta, e, apesar das últimas pesquisas apresentarem melhora, o patamar do final de 2019 não foi recuperado.

Criando uma versão "janela-móvel" de qualquer função

By | Data Science

O que são cálculos em janela móvel e por quê são importantes em análise de dados e de séries temporais? Neste texto exploramos a criação e aplicação de funções em janelas móveis usando o R.

Em análise de séries temporais, nada é estático. Uma correlação pode existir em um subconjunto temporal ou uma média pode variar de um dia para o outro. Os cálculos em janelas móveis simplesmente aplicam funções a um subconjunto de largura fixa (ou não) desses dados (também conhecido como uma janela móvel), indexando uma observação a cada cálculo.

Existem alguns motivos comuns pelos quais você pode querer usar um cálculo em janela móvel em análise de séries temporais:

  • Obter a tendência central ao longo do tempo (média, mediana)
  • Obter a volatilidade ao longo do tempo (desvio-padrão, variância)
  • Detectar mudanças de tendência (médias móveis rápidas vs. lentas)
  • Obter uma relação entre duas séries temporais ao longo do tempo (covariância, correlação)

O tipo de cálculo em janela móvel mais simples existente no R é o base::cumsum() que faz a soma acumulada de valores de um vetor, no qual cada janela móvel é definida como sendo todos os valores anteriores ao atual. Um exemplo visual de soma acumula de uma sequência de números de 1 a 10:

Entendida a importância e intuição do funcionamento dessas operações, passemos a alguns exemplos práticos. Demonstraremos como operacionalizar esses cálculos em janelas móveis com as principais opções de pacotes de R disponíveis atualmente para essa finalidade:

  • timetk
  • slider
  • runner

Essa lista com certeza não esgota todas as possibilidades, existem outras opções e fica ao critério do usuário escolher o que melhor se adequa ao seu contexto.

Pacotes

Para reproduzir os códigos aqui expostos é necessário alguns pacotes de R, o código a seguir verifica se os mesmos estão instalados, instala caso necessário e carrega os mesmos para a memória:

# Instalar/carregar pacotes
if(!require("pacman")) install.packages("pacman")
pacman::p_load(
"magrittr",
"dplyr",
"ggplot2",
"ggthemes",
"tidyquant",
"timetk",
"tidyr",
"slider",
"lubridate",
"runner"
)

O pacote {timetk}

O pacote timetk é um toolkit para visualização, tratamento e transformação de séries temporais e oferece as funções slidify() e slidify_vec() para operações em janela móvel.

Como exemplo inicial, utilizamos os dataset FANG que traz os preços diários de 4 ações de tecnologia listadas na NASDAQ:

# Dados de preços diários de ações (FANG)
df_fang <- tidyquant::FANG
df_fang
# # A tibble: 4,032 x 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28
## 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8
## 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8
## 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4
## 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1
## 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6
## 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3
## 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7
## 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0
## 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1
## # ... with 4,022 more rows

Calculando uma média móvel de 30 períodos para o preço ajustado (adjusted):

df_fang %>%
dplyr::select(symbol, date, adjusted) %>%
dplyr::group_by(symbol) %>% 
# Criar média móvel
dplyr::mutate(
roll_avg_30 = timetk::slidify_vec(
.x = adjusted,
.f = mean, 
.period = 30, 
.align = "right", 
.partial = TRUE
)
) %>%
tidyr::pivot_longer(cols = c(adjusted, roll_avg_30)) %>%
# Gerar gráfico
timetk::plot_time_series(
date, 
value, 
.title = "Média móvel de 30 períodos",
.color_var = name,
.facet_ncol = 2, 
.smooth = FALSE, 
.interactive = FALSE
)

Para mais operações simultaneamente pode ser interessante primeiro definir as funções utilizando o slidify() para depois calcular a operação em janelas móveis:

# Média móvel
roll_avg_30 <- timetk::slidify(.f = mean, .period = 30, .align = "right", .partial = TRUE)
# Soma acumulada
roll_sum_30 <- timetk::slidify(.f = sum, .period = 30, .align = "right", .partial = TRUE)
# Desvio padrão móvel
roll_sd_30 <- timetk::slidify(.f = sd, .period = 30, .align = "right", .partial = TRUE)
# Regressão móvel (rolling regression)
roll_lm_90 <- timetk::slidify(~ lm(..1 ~ ..2 + ..3), .period = 90, .unlist = FALSE, .align = "right")

E então aplicar as funções criadas:

df_fang %>%
dplyr::select(symbol, date, adjusted, volume) %>%
dplyr::group_by(symbol) %>% 
# Operações em janelas móveis
dplyr::mutate(
numeric_date = as.numeric(date),
m_avg_30 = roll_avg_30(adjusted),
m_sum_30 = roll_sum_30(adjusted),
m_sd_30 = roll_sd_30(adjusted),
m_lm_90 = roll_lm_90(adjusted, volume, numeric_date)
) %>% 
dplyr::filter(!is.na(m_lm_90))

## # A tibble: 3,676 x 9
## # Groups: symbol [4]
## symbol date adjusted volume numeric_date m_avg_30 m_sum_30 m_sd_30
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2013-05-10 26.7 30847100 15835 26.8 805. 0.842
## 2 FB 2013-05-13 26.8 29068800 15838 26.9 807. 0.805
## 3 FB 2013-05-14 27.1 24930300 15839 26.9 808. 0.756
## 4 FB 2013-05-15 26.6 30299800 15840 27.0 809. 0.747
## 5 FB 2013-05-16 26.1 35499100 15841 26.9 808. 0.762
## 6 FB 2013-05-17 26.2 29462700 15842 26.9 807. 0.766
## 7 FB 2013-05-20 25.8 42402900 15845 26.9 806. 0.794
## 8 FB 2013-05-21 25.7 26261300 15846 26.8 805. 0.822
## 9 FB 2013-05-22 25.2 45314500 15847 26.7 802. 0.863
## 10 FB 2013-05-23 25.1 37663100 15848 26.6 799. 0.880
## # ... with 3,666 more rows, and 1 more variable: m_lm_90 <list>

O pacote {slider}

O pacote slider oferece uma sintaxe familiar ao purrr para calcular operações em janelas móveis.

O exemplo mais simples de sua utilização é o cálculo de uma média móvel dado um vetor numérico. A janela móvel é indicada pelo argumento .before:

# Média móvel (2 "períodos") com alinhamento a direita
slider::slide_dbl(
.x = 1:5, 
.f = ~mean(.x), 
.before = 1, # valor atual + 1 valor anterior
.complete = TRUE 
)

O pacote oferece algumas funções derivadas de slide_dbl() para os cálculos mais comuns: slide_mean(), slide_sum(), slide_prod(), slide_min() e slide_max().

Para séries temporais o pacote oferece ainda um recurso interessante de realizar a operação com janela móvel relativa a um índice com as funções slide_index(). Se você já quis calcular algo como uma “média móvel de 3 meses”, em que o número de dias em cada mês é irregular, você pode gostar desta função.

No exemplo abaixo, primeiro é calculado uma média móvel de 90 períodos sem considerar o índice e depois é calculado outra média móvel, mas considerando um índice de 3 meses. A função slide_index_mean() realiza o cálculo da média móvel de 3 meses considerando o valor atual em relação a mesma data aproximada de 3 meses anteriores (não necessariamente 90 observações anteriores). O resultado é uma média móvel diária relativa a uma janela móvel de 3 meses:


# Calcular médias móveis
df_amzn_roll <- df_fang %>%
dplyr::filter(symbol == "AMZN") %>%
dplyr::select(date, symbol, adjusted) %>%
dplyr::mutate(
roll_avg_90 = slider::slide_mean(adjusted, before = 89, complete = TRUE),
roll_idx_3m = slider::slide_index_mean(
x = adjusted, # vetor numérico
i = date, # vetor índice (datas)
before = ~ .x %m-% base::months(3), # para evitar erro gerado por NAs
complete = TRUE
)
)

# Gerar gráfico
df_amzn_roll %>%
dplyr::filter(date <= lubridate::as_date("2014-06-30")) %>%
tidyr::pivot_longer(cols = c(adjusted, roll_avg_90, roll_idx_3m)) %>%
timetk::plot_time_series(
date,
value,
.title = "AMZN",
.color_var = name,
.smooth = FALSE,
.interactive = FALSE
)

Perceba que a média móvel que considera o índice de 3 meses tem início anterior, pois considera que os meses possuem número de dias irregulares e se ajusta a tal.

O pacote {runner}

O pacote runner oferece um framework arrojado para calcular operações com janelas móveis de tamanho fixo ou variante, opções de inclusão de lags, tratamento de NAs, computação paralela, e é desenhado para dados em formato de séries temporais ou longitudinais.

Exemplos simples de médias móveis com e sem lag:


# Média móvel de 2 "períodos"
runner::runner(1:5, f = mean, k = 2, na_pad = TRUE)

## [1] NaN 1.5 2.5 3.5 4.5

# Média móvel de 2 "períodos" com 1 lag
runner::runner(1:5, f = mean, k = 2, lag = 1, na_pad = TRUE)

## [1] NaN NaN 1.5 2.5 3.5

Às vezes, as observações de um conjunto de dados não são espaçados igualmente (ausência de fins de semana, feriados, etc.) e, portanto, o tamanho da janela móvel deve variar para manter o período de tempo esperado. Para essa situação, podemos especificar o argumento idx para que a função em execução seja aplicada nas janelas móveis dependendo da data. Neste exemplo abaixo a janela móvel possui o índice semanal variante conforme a data:


# Criar vetores de dados
dates <- Sys.Date() + cumsum(sample(1:5, 20, replace = TRUE)) # série temporal irregular
values <- 1:20

dates

## [1] "2021-08-23" "2021-08-24" "2021-08-26" "2021-08-30" "2021-09-04"
## [6] "2021-09-08" "2021-09-12" "2021-09-13" "2021-09-16" "2021-09-18"
## [11] "2021-09-23" "2021-09-26" "2021-09-27" "2021-09-30" "2021-10-03"
## [16] "2021-10-07" "2021-10-11" "2021-10-15" "2021-10-17" "2021-10-20"

values

## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# Calcular média móvel com janela variante
runner::runner(
x = values,
k = "7 days",
idx = dates,
f = mean
)

## [1] 1.0 1.5 2.0 3.0 4.5 5.5 6.5 7.0 8.0 8.5 10.5 11.5 12.0 13.0 14.0
## [16] 15.5 16.5 17.5 18.0 19.0

Espero que esta exploração com exemplos de cálculos com funções em janelas móveis tenha sido proveitosa, apesar de breve e resumida. Recomendo fortemente consultar as documentações dos pacotes, pois os mesmos fornecem todas as informações, detalhes, motivação e mais exemplos.

 

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais
e conteúdos exclusivos sobre Análise de Dados!

Assinar Gratuitamente
{"cart_token":"","hash":"","cart_data":""}