Manipulação de Textos – Parte 1


***

Parte do livro Introdução à análise de dados com R.  Este trabalho está em andamento, o texto é bastante preliminar e sofrerá muitas alterações. 

Quer fazer sugestões? Deixe um comentário abaixo ou, se você sabe utilizar o github, acesse aqui.

Não copie ou reproduza este material sem autorização.

Volte para ver atualizações!

***

Criando textos

No R, textos são representados por vetores do tipo character. Você pode criar manualmente um elemento do tipo character colocando o texto entre aspas, podendo ser tanto aspas simples (‘texto’) quanto aspas duplas (“texto”).

# criando um vetor de textos
# aspas simples
x1 <- 'texto 1'

# aspas duplas
x2 <- "texto 2"

Como já vimos, para saber se um objeto é do tipo texto você pode utilizar a função is.character() e também é possível converter objetos de outros tipos para textos utilizando a função as.character().

# criando um vetor de inteiros
x3 <- 1:10

# É texto? Não.
is.character(x3)
## [1] FALSE
# Convertendo para texto
x3 <- as.character(x3)

# Agora é texto? Sim.
is.character(x3)
## [1] TRUE

Operações com textos

Operações como ordenação e comparações são definidas para textos. A ordenação de um texto é feita de maneira lexicográfica, tal como em um dicionário.

# ordenação de letras
sort(c("c", "d", "a", "f"))
## [1] "a" "c" "d" "f"
# ordenação de palavras
# tal como um dicionário
sort(c("cor", "casa", "árvore", "zebra", "branco", "banco"))
## [1] "árvore" "banco"  "branco" "casa"   "cor"    "zebra"

Como a comparação é lexicográfica, é preciso tomar alguns cuidados. Por exemplo, o texto “2” é maior do que o texto “100”. Se por acaso seus números forem transformados em texto, você não vai receber uma mensagem de erro na comparação "2" > "100" mas sim um resultado errado: TRUE.

# CUIDADO!
2   >  100
## [1] FALSE
"2" > "100"
## [1] TRUE
# b > a
"b" > "a"
## [1] TRUE
# A > a
"A" > "a"
## [1] TRUE
# casa > banana
"casa" > "banana"
## [1] TRUE

Imprimindo textos

Se você estiver usando o R de modo interativo, chamar o objeto fará com que ele seja exibido na tela usando print().

# Imprime texto na tela
print(x1)
## [1] "texto 1"
# Quando em modo interativo
# Equivalente a print(x1)
x1
## [1] "texto 1"

Se você não estiver usando o R de modo interativo — como ao dar source() em um script ou dentro de um loop — é preciso chamar explicitamente uma função que exiba o texto na tela.

# sem print não acontece nada
for(i in 1:3) i

# com print o valor de i é exibido
for(i in 1:3) print(i)
## [1] 1
## [1] 2
## [1] 3

Existem outras opções para “imprimir” e formatar textos além do print(). Uma função bastante utilizada para exibir textos na tela é a função cat() (concatenate and print).

cat(x1)
## texto 1
cat("A função cat exibe o texto sem aspas:", x1)
## A função cat exibe o texto sem aspas: texto 1

Por padrão, cat() separa os textos com um espaço em branco, mas é possível alterar este comportamento com o argumento sep.

cat(x1, x2)
## texto 1 texto 2
cat(x1, x2, sep = " - ")
## texto 1 - texto 2

Outra funções úteis são sprintf() e format(), principalmente para formatar e exibir números. Para mais detalhes sobre as funções, olhar a ajuda ?sprintf e ?format.

# %.2f (float, 2 casas decimais)
sprintf("R$ %.2f", 312.12312312)
## [1] "R$ 312.12"
# duas casas decimais, separador de milhar e decimal
format(10500.5102, nsmall=2, big.mark=".", decimal.mark=",")
## [1] "10.500,51"

Caracteres especiais

Como fazemos para gerar um texto com separação entre linhas no R? Criemos a separação de linhas manualmente para ver o que acontece:

texto_nova_linha <- "texto
com nova linha"

texto_nova_linha
## [1] "texto\ncom nova linha"

Note que aparece um \n no meio do texto. Isso é um caractere especial: \n simboliza justamente uma nova linha. Quando você exibe um texto na tela com print(), caracteres especiais não são processados e aparecem de maneira literal. Já se você exibir o texto na tela usando cat(), os caracteres especiais serão processados. No nosso exemplo, o \n será exibido como uma nova linha.

# print: \n aparece literalmente
print(texto_nova_linha)
## [1] "texto\ncom nova linha"
# cat: \n aparece como nova linha
cat(texto_nova_linha)
## texto
## com nova linha

Caracteres especiais são sempre “escapados” com a barra invertida \ . Além da nova linha (\n), outros caracteres especiais recorrentes são o tab (\t) e a própria barra invertida, que precisa ela mesma ser escapada (\\). Vejamos alguns exemplos:

cat("colocando uma \nnova linha")
## colocando uma 
## nova linha
cat("colocando um \ttab")
## colocando um     tab
cat("colocando uma \\ barra")
## colocando uma \ barra
cat("texto com novas linhas e\numa barra no final\n\\")
## texto com novas linhas e
## uma barra no final
## \

Para colocar aspas simples ou duplas dentro do texto há duas opções. A primeira é alternar entre as aspas simples e duplas, uma para definir o objeto do tipo character e a outra servido literalmente como aspas.

# Aspas simples dentro do texto
aspas1 <- "Texto com 'aspas' simples dentro"
aspas1 
## [1] "Texto com 'aspas' simples dentro"
# Aspas duplas dentro do texto
aspas2  <- 'Texto com "aspas" duplas dentro'
cat(aspas2)
## Texto com "aspas" duplas dentro

Outra opção é colocar as aspas como caracter expecial. Neste caso, não é preciso alternar entre aspas simples e duplas.

aspas3 <- "Texto com \"aspas\" duplas"
cat(aspas3)
## Texto com "aspas" duplas
aspas4 <- 'Texto com \'aspas\' simples'
cat(aspas4)
## Texto com 'aspas' simples

Utilidade das funções de exibição

Qual a utilidade de funções que exibam coisas na tela?

Um caso bastante comum é exibir mensagens durante a execução de alguma rotina ou função. Por exemplo, você pode exibir o percentual de conclusão de um loop a cada 25 rodadas:

for(i in 1:100){ 

  # imprime quando o resto da divisão
  # de i por 25 é igual a 0
  if(i %% 25 == 0){
    cat("Executando: ", i, "%\n", sep = "")
  }

  # alguma rotina
  Sys.sleep(0.01)
}
## Executando: 25%
## Executando: 50%
## Executando: 75%
## Executando: 100%

Outro uso frequente é criar métodos de exibição para suas próprias classes. Vejamos um exemplo simples de uma fução base do R, a função rle(), que computa tamanhos de sequências repetidas de valores em um vetor. O resultado da função é uma lista, mas ao exibirmos o objeto na tela, o print não é igual ao de uma lista comum:

x <- rle(c(1,1,1,0))

# resultado é uma lista
str(x)
## List of 2
##  $ lengths: int [1:2] 3 1
##  $ values : num [1:2] 1 0
##  - attr(*, "class")= chr "rle"
# print do objeto na tela 
# não é como uma lista comum
x
## Run Length Encoding
##   lengths: int [1:2] 3 1
##   values : num [1:2] 1 0
# tirando a classe do objeto
# veja que o print agora é como uma lista comum
unclass(x)
## $lengths
## [1] 3 1
## 
## $values
## [1] 1 0

Isso ocorre porque a classe rle tem um método de print próprio, print.rle():

print.rle <- function (x, digits = getOption("digits"), prefix = "", ...) 
{
    if (is.null(digits)) 
        digits <- getOption("digits")
    cat("", "Run Length Encoding\n", "  lengths:", sep = prefix)
    utils::str(x$lengths)
    cat("", "  values :", sep = prefix)
    utils::str(x$values, digits.d = digits)
    invisible(x)
}

Tamanho do texto

A função nchar() retorna o número de caracteres de um elemento do tipo texto. Note que isso é diferente da função length() que retorna o tamanho do vetor.

# O vetor x1 tem apenas um elemento
length(x1)
## [1] 1
# O elemento do vetor x1 tem 7 caracteres
# note que espaços em brancos contam
nchar(x1) 
## [1] 7

A função nchar() é vetorizada.

# vetor do tipo character
y <- c("texto 1", "texto 11")

# vetor tem dois elementos
length(y)
## [1] 2
# O primeiro elemento tem 7 caracteres
# O segundo 8.
nchar(y) # vetorizada
## [1] 7 8

Manipulando textos

Manipulação de textos é uma atividade bastante comum na análise de dados. O R possui uma série de funções para isso e suporta o uso de expressões regulares. Nesta seção veremos exemplos simples das principais funções de manipulação de textos. Na próxima seção abordaremos um pouco de expressões regulares.

Colando (ou concatenando) textos

A função paste() é uma das funções mais úteis para manipulação de textos. Como o próprio nome diz, ela serve para “colar” textos.

# Colando textos
tipo <- "Apartamento"
bairro <- "Asa Sul"
texto <- paste(tipo,"na", bairro )
texto
## [1] "Apartamento na Asa Sul"

Por default, paste() separa os textos com um espaço em branco. Você pode alterar isso modificando o argumento sep. Caso não queira nenhum espaço entre as strings, basta definir sep = "" ou utilizar a função paste0(). Como usual, todas essas funções são vetorizadas.

# separação padrão
paste("x", 1:5)
## [1] "x 1" "x 2" "x 3" "x 4" "x 5"
# separando por ponto
paste("x", 1:5, sep=".")
## [1] "x.1" "x.2" "x.3" "x.4" "x.5"
# sem separação
paste("x", 1:5, sep ="")
## [1] "x1" "x2" "x3" "x4" "x5"
# sem separação, usando paste0.
paste0("x", 1:5)
## [1] "x1" "x2" "x3" "x4" "x5"

Note que foram gerados 5 elementos diferentes nos exemplos acima. É possível “colar” todos os elementos em um único texto com a opção collapse().

paste("x", 1:5, sep="", collapse=" ")
## [1] "x1 x2 x3 x4 x5"

Separando textos

Outra atividade frequente em análise de dados é separar um texto em elementos diferentes. Por exemplo, suponha que você tenha que trabalhar com um conjunto de números, mas que eles estejam em um formato de texto separados por ponto e vírgula:

dados <- "1;2;3;4;5;6;7;8;9;10"
dados
## [1] "1;2;3;4;5;6;7;8;9;10"

Com a função strsplit() é fácil realizar essa tarefa:

dados_separados <- strsplit(dados, split=";")
dados_separados
## [[1]]
##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"

Note que o resultado da função é uma lista. Agora é possível converter os dados em números e trabalhar normalmente.

# convertendo o resultado em número
dados_separados <- as.numeric(dados_separados[[1]])

# agora é possível trabalhar com os números
# média
mean(dados_separados)
## [1] 5.5
# soma
sum(dados_separados)
## [1] 55

Encontrando partes de um texto

Quando você estiver trabalhando com suas bases de dados, muitas vezes será preciso encontrar certas palavras ou padrões dentro do texto. Por exemplo, imagine que você tenha uma base de dados de aluguéis de apartamentos e você gostaria de encontrar imóveis em um certo endereço. Vejamos este exemplo com dados online de aluguel em Brasília.

# Carrega arquivo de anúncios de aluguel (2014)
arquivo <- url("https://dl.dropboxusercontent.com/u/44201187/aluguel.rds")
con <- gzcon(arquivo)
aluguel <- readRDS(con)
close(con)

Vejamos a estrutura da nossa base de dados:

str(aluguel)
## 'data.frame':    2612 obs. of  5 variables:
##  $ bairro  : chr  "Asa Norte" "Asa Norte" "Sudoeste" "Asa Norte" ...
##  $ endereco: chr  "CLN 310 BLOCO A " "SCRN 716 BLOCO G ENT. 26 3º ANDAR" "QMSW 06 ED.STUDIO IN" "CLN 406 BLOCO D - ED. POP CENTER (APARTAMENTO)" ...
##  $ quartos : num  1 1 1 1 1 1 1 1 1 1 ...
##  $ m2      : num  22.9 26 30 30 30 30 30 30 28 30 ...
##  $ preco   : num  650 750 800 800 800 820 850 850 850 860 ...
##  - attr(*, "na.action")=Class 'omit'  Named int [1:120] 15943 16001 17264 17323 18600 18659 19935 19996 21278 22617 ...
##   .. ..- attr(*, "names")= chr [1:120] "15943" "16001" "17264" "17323" ...

Temos mais de 2 mil anúnciso, como encontrar aqueles apartamentos que queremos, como, por exemplos, os que contenham “CLN 310” no endereço? Neste caso você pode utilizar a função grep() para encontrar padrões dentro do texto. A função retornará o índice das observações que contém o texto:

busca_indice <- grep(pattern = "CLN 310", aluguel$endereco)
busca_indice
## [1]    1 1812
aluguel[busca_indice, ]
##         bairro                            endereco quartos   m2 preco
## 1    Asa Norte                    CLN 310 BLOCO A        1 22.9   650
## 1812 Asa Norte CLN 310 BLOCO E ENTRADA 52 SALA 216       0 30.0   900

Uma variante da função grep() é a função grepl(), que realiza a mesma coisa, mas ao invés de retornar um índice numérico, retorna um vetor lógico:

busca_logico <- grepl(pattern = "CLN 310", aluguel$endereco)
str(busca_logico)
##  logi [1:2612] TRUE FALSE FALSE FALSE FALSE FALSE ...
aluguel[busca_indice, ]
##         bairro                            endereco quartos   m2 preco
## 1    Asa Norte                    CLN 310 BLOCO A        1 22.9   650
## 1812 Asa Norte CLN 310 BLOCO E ENTRADA 52 SALA 216       0 30.0   900

Nossa busca é útil, mas ainda é simples. Quando aprendermos expressões regulares, essas buscas ficarão bem mais poderosas. Lá também aprenderemos outras funções como regexpr(), gregexpr(), regexec() e regmatches().

Substituindo partes de um texto

A função sub() substitui o primeiro padrão (pattern) que encontra:

texto2 <- paste(texto, ", Apartamento na Asa Norte")
texto2
## [1] "Apartamento na Asa Sul , Apartamento na Asa Norte"
# Vamos substituir "apartamento" por "Casa"
# Mas apenas o primeiro caso
sub(pattern = "Apartamento",
    replacement = "Casa",
    texto2) 
## [1] "Casa na Asa Sul , Apartamento na Asa Norte"

Já a função gsub() substitui todos os padrões que encontra:

# Vamos substituir "apartamento" por "Casa"
# Agora em todos os casos
gsub(pattern="Apartamento",
    replacement="Casa",
    texto2)
## [1] "Casa na Asa Sul , Casa na Asa Norte"

Você pode usar as funções sub() e gsub() para “deletar” partes indesejadas do texto, basta colocar como replacement um caractere vazio "". Um exemplo bem corriqueiro, quando se trabalha com com nomes de arquivos, é a remoção das extensões:

# nomes dos arquivos
arquivos <- c("simulacao_1.csv","simulacao_2.csv")

# queremos eliminar a extensão .csv
# note que o ponto precisa ser escapado
nomes_sem_csv <- gsub("\\.csv", "", arquivos)
nomes_sem_csv
## [1] "simulacao_1" "simulacao_2"

Extraindo partes específicas de um texto

Às vezes você precisa extrair apenas algumas partes específicas de um texto, em determinadas posições. Para isso você pode usar as funções substr() e substring().

Para essas funções, você basicamente passa as posições dos caracteres inicial e final que deseja extrair.

# extraindo caracteres da posição 4 à posição 8
x <- "Um texto de exemplo"
substr(x, start = 4, stop = 8)
## [1] "texto"

É possível utilizar essas funções para alterar partes específicas do texto.

# substituindo caracteres da posição 4 à posição 8
substr(x, start = 4, stop = 8) <- "TEXTO"
x
## [1] "Um TEXTO de exemplo"

A principal diferença entre substr() e substring() é que a segunda permite você passar vários valores iniciais e finais:

# pega caracteres de (4 a 8) e de (10 a 11)
substring(x, first = c(4, 10), last = c(8, 11))
## [1] "TEXTO" "de"
# pega caracteres de (1 ao último), (2 ao último) ...
substring("abcdef", first = 1:6)
## [1] "abcdef" "bcdef"  "cdef"   "def"    "ef"     "f"

**** A SEGUIR ****

  • expressões regulares
    • regmatches, regexpr, gregexpr, regexc
  • fuzzy matching
  • stringr

Simulações – Parte 1


***

Parte do livro Introdução à análise de dados com R.  Este trabalho está em andamento, o texto é bastante preliminar e sofrerá muitas alterações. 

Quer fazer sugestões? Deixe um comentário abaixo ou, se você sabe utilizar o github, acesse aqui.

Não copie ou reproduza este material sem autorização.

Volte para ver atualizações!

***

Distribuições de probabilidade

O R vem com diversas funções para simular distribuições estatísticas. Em geral essas funções têm o seguinte formato: rnomedadistribuicao, dnomedadistribuicao, pnomedadistribuicao ou qnomedadistribuicao. Mais detalhadamente, a primeira letra da função, que pode ser r, d, p ou q, indica, respectivamente, se a função é: (i) geradora de variáveis aleatórias; (ii) de densidade; (iii) de distribuição acumulada; ou, (iv) de quantil. E, logo em seguida, temos um nome abreviado da distribuição de probabilidade.

Captura de Tela 2016-01-17 às 13.58.01

Dessa forma, por exemplo, se você quiser gerar dados aleatórios de uma distribuição normal a função para tanto é rnorm (r pois trata-se de um gerador de números aleatórios e norm pois trata-se da distribuição normal).

Na tabela abaixo temos várias das distribuições presentes de forma nativa no R:

Captura de Tela 2016-01-17 às 13.58.19

Sementes para as simulações

Durante todo o livro nós utilizamos o comando set.seed quando fizemos simulações. Isso garante que os resultados obtidos possam ser reproduzidos em qualquer computador.

Veja que, se rodarmos o comando rnorm sem definir o estado do gerador de números aleatórios com set.seed, você não conseguirá obter os mesmos valores em seu computador:

rnorm(1)
## [1] 0.5748481

rnorm(1)
## [1] 0.4052027

Contudo, uma vez definida a semente, obteremos sempre o mesmo valor:

set.seed(1)
rnorm(1)
## [1] -0.6264538

set.seed(1)
rnorm(1)
## [1] -0.6264538

O básico de r,d p,q com a distribuição normal

Para começar a entender o que cada função do R faz, trabalhemos cada uma delas usando a distribuição normal. A função densidade da distribuição normal-padrão (uma normal com média zero e desvio-padrão igual a um) tem a seguinte forma:

plot of chunk grafico densidade

A primeira dúvida que alguém pode ter é: como extrair valores aleatoriamente desta distribuição? Vejamos:

# semente para reproducibilidade
set.seed(2)

# gerando 5 variáveis aleatórias da distribuição Normal(0,1)
x1 <- rnorm(5)
x1
## [1] -0.89691455  0.18484918  1.58784533 -1.13037567 -0.08025176

Com o comando acima geramos 5 valores da normal-padrão.

Mas e se quisermos valores de uma normal com média e desvio-padrão diferentes? Para isso, basta mudarmos os parâmetros mean sd (standard deviation) da função rnorm:

# semente para reproducibilidade
set.seed(2)

# gerando 6 variáveis aleatórias da distribuição Normal(10,2)
x2 <- rnorm(5, mean = 10, sd = 2)
x2
## [1]  8.206171 10.369698 13.175691  7.739249  9.839496

Com o código acima, geramos 5 valores de uma distribuição normal com média 10 e desvio-padrão 2. Entretanto, você também poderia ter gerado os mesmos valores a partir da normal-padrão: x2 nada mais é do que x1*2 + 10:

all.equal(x1*2 + 10, x2)
## [1] TRUE

Saber como tranformar uma distribuição em outra é algo bastante útil e pode poupar bastante tempo na hora de fazer simulações. Veremos exemplos práticos disso nos exercícios.

Às vezes, ao invés de gerar números aleatórios, nós temos valores que, presume-se, foram gerados por uma distribuição normal, e queremos saber a densidade ou a probabilidade associada àquele valor.

Por exemplo, supondo uma distribuição normal-padrão, qual a probabilidade de x ser menor do que 1.65? Isto é, queremos saber o valor da área hachurada da curva de densidade:

plot of chunk grafico area densidade
Para responder essa pergunta, você vai usar a função pnorm:

# probabilidade de X < 1.65
pnorm(1.65)
## [1] 0.9505285

Note que aproximadamente 95% dos valores da normal-padrão estão abaixo de 1.65. E se quisermos fazer a pergunta contrária: qual o valor de x tal que 95% dos valores da curva estejam abaixo deste valor? Para isso usamos a função qnorm:

qnorm(0.95)
## [1] 1.644854

Para calcularmos os valores da função densidade utilizamos a função dnorm. Vejamos como fazer isso reproduzindo os gráficos da função densidade exibidos anteriormente:

# Sequencia de -3 a 3 igualmente espaçada e
# com valores redondos
x <- pretty(c(-3, 3), 1000)

# Função densidade de -3 a 3
y <- dnorm(x)

# Gráfico Função Densidade
plot.new()
plot.window(xlim=range(x), ylim=range(y))
axis(1); axis(2)
polygon(x, y, col = "lightblue")
title(main = "Distribuição Normal \nFunção Densidade")

# Gráfico Função Densidade Hachurado
plot.new()
plot.window(xlim=range(x), ylim=range(y))
axis(1);axis(2)
polygon(x, y, col = "lightblue")
title(main = "Distribuição Normal \nFunção Densidade")
z <- 1.65
lines(c(z, z), y = c(dnorm(-3), dnorm(z)))
polygon(c(x[x<=z], z), c(y[x<=z], min(y)), density = 10, angle = 45)
text(x = z + 0.3, y = dnorm(z) + 0.01,"1.65")

Exemplo 1: Teorema Central do Limite

O teorema cental do limite nos diz que, sob certas condições de regularidade (como variância finita), quanto mais observações tivermos, a distribuição amostral da média de uma variável aleatória será aproximadamente normal, independentemente do formato original da distribuição.

Vejamos um exemplo com a distribuição exponencial. A função densidade da exponencial pode ser escrita como f(x) = \lambda e^{-\lambda x}, com média E(X) = \frac{1}{\lambda} e desvio padrão DP(X) = \frac{1}{\lambda}.

Para nosso exemplo, tomaremos \lambda = 1. Assim, temos que E(X) = DP(X) = 1 e, segundo o teorema central do limite, a variável (\bar{x}-1)\sqrt{n} tende a uma normal-padrão (\bar{x} é a média amostral de x)

Note que o formato do histograma da exponencial (\lambda = 1) não se parece com o formato de sino da distribuição normal, que vimos na seção anterior:

# semente para reproducibilidade
set.seed(10)

# geramos 1000 variávels aleatórias de uma distribuição exponencial
x <- rexp(n = 1000, rate = 1)

# histograma
hist(x, col = "lightblue", main = "Distribuição Exponencial", freq = F)

plot of chunk hist exp

Entretanto, o que ocorre com a distribuição de (\bar{x}-1)\sqrt{n}  quando aumentamos o valor de n? Façamos uma simulação para seis valores de tamanho amostral diferentes: 1, 5, 10, 100, 500 e 1000.

# Simulacõees TCL - exponencial

# semente para reproducibilidade
set.seed(100)

# diferentes tamanhos amostrais que iremos simular
n <- c(1, 5, 10, 100, 500, 1000)

# número de replicações para cada n
n.rep <- 1000

## simulações
sims <- lapply(n, function(n) replicate(n.rep, (mean(rexp(n)) - 1)*sqrt(n)))

Na prática, a simulação toda foi feita com apenas uma linha, combinando o lapply com replicate. Explicando melhor o código acima, com o comando lapply(n, ...) estamos dizendo para o R que iremos aplicar uma função para cada valor de n. Mas que função estamos aplicando? Neste caso, a função anômima function(n) replicate(n.rep, (mean(rexp(n)) - 1)*sqrt(n)). Mais detalhadamente, com o comando replicate(n.rep, (mean(rexp(n)) - 1)*sqrt(n))) repetimos n.rep vezes a expressão (mean(rexp(n)) - 1)*sqrt(n)), que nada mais é do que a média padronizada de uma exponencial (\lambda = 1) de tamanho amostral n multiplicada por \sqrt{n}

O resultado de nossas simulações está na lista sims que tem a seguinte estrutura:

## nomes para as listas
names(sims) <- as.character(n)

## estrutura do resultado
str(sims)
## List of 6
##  $ 1   : num [1:1000] -0.0758 -0.2762 -0.8954 2.0974 -0.3752 ...
##  $ 5   : num [1:1000] -0.25122 0.00986 -1.19437 -1.29553 1.14441 ...
##  $ 10  : num [1:1000] -0.237 1.042 0.523 1.228 0.929 ...
##  $ 100 : num [1:1000] -0.303 0.43 -0.25 -0.339 -0.659 ...
##  $ 500 : num [1:1000] 2.072 0.239 -0.91 0.421 -0.353 ...
##  $ 1000: num [1:1000] -1.0039 0.0282 -0.0259 -0.0925 -0.9421 ...

Perceba que temos uma lista com 6 elementos, um para cada n diferente. Você pode acessar os resultados da lista ou pelo índice ou pelo nome do elemento:

# pega os resultados de n = 1000
sims[[6]]
sims[["1000"]]

Vejamos todos os resultados da simulação ao mesmo tempo em um gráfico. O histograma dos valores simulados estão em azul claro e a função densidade da normal-padrão em vermelho.

plot of chunk tcl ggplot2

Quando n = 1, a distribuição segue o mesmo formato da exponencial. Todavia, note que a convergência para a distribuição normal ocorre bem rapidamente neste exemplo. Com n = 100 as diferenças entre a normal e os dados simulados já se tornam bastante pequenas.

Fizemos o gráfico acima com o ggplot2:

library(ggplot2)
library(reshape2)

# Prepara base de dados para gráfico
## Transforma em data.frame
sims.df <- as.data.frame(do.call("cbind", sims))

## Empilha para o ggplot2
sims.df <- melt(sims.df,
                variable.name = "n",
                value.name = "Valor")
sims.df$n <- paste("n =", sims.df$n)
sims.df$n <- factor(sims.df$n, levels = unique(sims.df$n))

# Histogramas vs Densidade Normal (ggplot2)
ggplot(sims.df, aes(x = Valor)) +
  # Histograma
  geom_histogram(aes(y = ..density..),
                 fill = "lightblue",
                 col = "black",
                 binwidth = 0.5) + xlim(c(-6, 6)) +
  # Uma faceta para cada n
  facet_wrap(~n) +
  ## Densidade da normal(0,1) para comparação
  stat_function(fun = dnorm,
                col = "red", size = 0.8) +
  # Titulo principal e do eixo Y
  ggtitle("Teorema Central do Limite\nDistribuição Expoencial") +
  ylab("Densidade") +
  ## Tema em preto e branco
  theme_bw()

Sua vez!

Nós simulamos o teorema central do limite usando funções da família apply: lapply e replicate. Isso permite nos expressarmos de maneira bastante concisa, em apenas uma linha.

Como você faria a mesma simulação usando loops? Compare os resultados e veja se eles são idênticos.

# Resposta sugerida

# Com FOR
## para reproducibilidade
set.seed(100)
## tamanho amostral
n <- c(1, 5, 10, 100, 500, 1000)
## numero de replicacoes
n.rep <- 1000

# lista para armazenar os resultados para cada n
sims.for <- vector("list", length(n))

## começo do for
## faremos n.rep replicacoes para cada n
## para cada i de n
for (i in seq_along(n)) {

  # crie um vetor temporario para realizar n.rep repetições
  temp <- numeric(n.rep)

  # realiza n.rep repetições de (mean(rexp(n[i])) - 1)*sqrt(n[i])
  for (j in 1:n.rep) {
    temp[j] <- (mean(rexp(n[i])) - 1)*sqrt(n[i])
  }

  # guarda resultado na lista
  sims.for[[i]] <- temp
}

# nomes para os resultados da lista
names(sims.for) <- n

# compara com simulação anterior
all.equal(sims, sims.for)
## [1] TRUE