(Re)Formatação de Bases de Dados

Programação
Tratamento de dados
workflows
Como tratar dados no R, nomeadamente, como converter dados do formato largo para o longo e do longo para o largo.
Autores
Afiliações

João O. Santos

ISPA

Cristina Mendonça

ISPA; WJCR

Vitória Melita

FP-UL

Mariona Pascual Peñas

UIB

João Raposo

ISPA

Marta Barros

FP-UL

0 (Re)Formatação de Bases de Dados

Neste capítulo vamos aprender a reformatar bases de dados. Quando me refiro a reformatação de bases de dados, incluo todos os processos de preparação dos dados para análise que alterem o formato dos dados e ou acrescentem novas colunas/variáveis. No capítulo anterior aprendemos a limpar os dados, neste tutorial vamos assumir que os dados já estão “limpos” mas ainda precisam de ser preparados antes de os podermos analisar.

A “Prática” é MUITO Diferente da “Aula Prática”

Como vimos no tutorial anterior, ao contrário das aulas práticas das cadeiras de estatística, na prática raramente temos bases de dados bem arranjadinhas. Na prática muitas vezes perdemos mais tempo a preparar os dados do que a analisá-los.

A “Prática” no R é MUITO Diferente para Melhor!

Creio que a maioria das pessoas que não usa o R (ou Python) para analisar os dados acaba por preparar os dados num programa como o Excel. A vantagem de usar o R para “limpar” os dados é ficarmos com todas as exclusões registadas num script, assegurando mais transparência e replicabilidade (no sentido em que podem replicar os nossos passos, não no sentido de aumentar a probabilidade dos nossos efeitos serem replicados). Apesar de eu considerar esta uma vantagem importante, creio que não trará grandes ganhos de eficiência por comparação ao Excel. No geral até é provável que demoremos mais tempo a escrever o script de “limpeza” dos dados do que se “limpássemos” os dados manualmente num programa como o Excel (e.g., Libreoffice Calc). No entanto, quando falamos de preparação dos dados, o uso do R, para além de continuar a assegurar a transparência e replicabilidade dos procedimentos, pode trazer enormes aumentos na nossa produtividade. Aliás, é possível até que o uso do R seja a única forma de conseguirmos preparar os nossos dados dentro do tempo que temos disponível. Por exemplo, imaginem que têm um estudo, com três condições intra-participantes, em que cada participante fez cem ensaios em cada condição. Imaginem ainda que o programa que usaram para recolher os dados registou a resposta dos participantes em colunas separadas, por condição e por ensaio. Isto quer dizer que neste caso a vossa base de dados terá no mínimo 300 colunas! Ora se tiverem que transformar essas colunas ou reformatar a estrutura da base de dados no Excel conseguem imaginar quanto tempo teriam que perder. O R permite-vos automatizar esse processo, evitando erros humanos e permitindo que poupem imenso tempo. Claro que até termos alguma prática com o R esse processo pode não parecer fácil e vamos perder algum tempo a escrever o script de reformatação dos dados. Mais tarde, com a prática, vão-se tornar cada vez a escrever esse script e os ganhos de eficiência vão aumentar. Para além disso, penso que será mais interessante e útil perdereminvestirem tempo a aprender a formatar dados no R que a fazer tarefas repetitivas no Excel… Notem que será injusto dizerem que perderam mais tempo a analisar os dados no R que no SPSS, se estiverem a somar o tempo que estiveram a reformatar dados ao tempo que estiveram a analisar dados, quando não somam o tempo que perderam no Excel ao tempo que perderam no SPSS.

Já chega de publicidade ao R não?

Mas…mas…o R tem tantas vantagens…

Era uma pergunta retórica… A resposta era NÃO!

Pronto…pronto…vamos a avançar…

O Que (re)formatar?

As reformatações mais simples que podemos fazer no R serão a transformação de algumas variáveis, o cálculo de novas a partir das existentes e a reconversão do tipo de variáveis (e.g., assumir que uma variável numérica contínua é tratada como tal). Outra transformação muito comum é passarmos uma base de dados do formato largo (i.e., wide) para o formato longo (i.e., long).

Quê??? Que é isso de formato largo e longo?

Eu ia explicar se não tivesses interrompido…

Eish… Cheira-me que alguém ficou sentido por eu ter interrompido a publicidade ao R…

Pois…talvez esse alguém tenha sentimentos…adiante…

Como eu ia explicar…uma base de dados no formato largo costuma ter uma linha por participante e uma coluna por cada medição/ensaio. Uma base de dados no formato longo, tem uma linha por cada medição/ensaio, acabando por ter várias linhas por participante. Para computarmos análises a dados com medidas repetidas (i.e., o mesmo participante realizou vários ensaios, ou respondeu à mesma pergunta mais que uma vez), normalmente dá-nos mais jeito termos os dados no formato longo do que no largo. Contudo, alguns programas de recolha de dados gravam os dados no formato largo. Nesses casos temos de converter do formato largo para longo. Da mesma forma que há programas de recolha de dados que gravam sempre os dados no formato largo, outros há que gravam sempre no longo. Nesses casos teremos então de converter do formato longo para largo.

Transformar Variáveis

Imaginemos uma base de dados em que cada linha é um/a participante e cada coluna corresponde aos tempos de resposta num ensaio.

head(ds)
pp t1 t2 t3
pp01 10094.906 10241.46 9973.047
pp02 10112.319 11880.76 12489.212
pp03 9611.322 10983.18 11354.406
pp04 9243.694 11898.51 12322.477
pp05 10591.523 10970.10 11249.347
pp06 10016.109 12010.43 12453.741

Podemos dizer que cada coluna corresponde a uma variável. A primeira contem o número do participante, nas três seguintes encontramos os tempos de resposta em cada ensaio. Com base nestes dados podemos acrescentar outras variáveis à nossa base de dados. Podemos também transformar estas variáveis em novas colunas ou alterar os valores presentes nelas. Obviamente que será impossível vermos tudo o que é possível fazer com estes dados, mas podemos mostrar alguns exemplos.

Acrescentar Variáveis

Imaginemos que eu sabia que na minha experiência metade dos participantes tinham sido colocados no grupo/condição de controlo e metade no grupo/condição experimental. Imaginemos ainda que a condição de cada participante não tinha sido registada na base de dados, mas tínhamos a certeza que a primeira metade dos dados correspondia aos participantes da condição controlo e a segunda metade aos da experimental.

Nesse caso, eu poderia criar uma nova coluna chamada cond que teria então a condição de cada participante. Sabendo que tinha 50 participantes e um participante por linha, saberia que as 25 primeiras linhas teriam nessa coluna o valor controlo e as últimas 25 linhas o valor experimental. Usando o que já aprendemos sobre data.frames poderia escrever as seguintes linhas de código.

# Atribui a condição controlo a todos os participantes.
ds$cond <- "controlo"
# Altera o valor de `cond`, nas últimas 25 linhas, para "experimental".
ds[26:50, "cond"] <- "experimental"

Já sabemos que podemos espreitar como ficou a base de dados usando as funções head() ou View(). Relembro que essas funções não devem ficar escritas nos nossos scripts por só serem úteis aquando da escrita do código e não em futuras execuções.

head(ds)
pp t1 t2 t3 cond
pp01 10094.906 10241.46 9973.047 controlo
pp02 10112.319 11880.76 12489.212 controlo
pp03 9611.322 10983.18 11354.406 controlo
pp04 9243.694 11898.51 12322.477 controlo
pp05 10591.523 10970.10 11249.347 controlo
pp06 10016.109 12010.43 12453.741 controlo

Conversões de Unidades

Muitas vezes podemos também converter os valores que estão nas nossas bases de dados para outras unidades, ou até inverter alguns itens duma escala. No exemplo que estamos a seguir, podemos ver que os tempos de resposta provavelmente estariam registados em milissegundos. Nesse caso poderíamos ter interesse em convertê-los para segundos. Podemos querer converter os valores que estão nas colunas ou podemos criar novas colunas com os valores alterados. Vamos assumir que queremos criar novas colunas. Nesse caso poderíamos escrever algo do género:

# Converte de milissegundos para segundos o valor de cada coluna
# guardando o resultado numa nova.
ds$t1_seg <- ds$t1 / 1000
ds$t2_seg <- ds$t2 / 1000
ds$t3_seg <- ds$t3 / 1000

Podemos voltar a ver como ficou com as mesmas funções de antes—head(), View()—(relembrando que essas funções não devem ficar nos scripts).

head(ds)
pp t1 t2 t3 cond t1_seg t2_seg t3_seg
pp01 10094.906 10241.46 9973.047 controlo 10.094906 10.24146 9.973047
pp02 10112.319 11880.76 12489.212 controlo 10.112319 11.88076 12.489212
pp03 9611.322 10983.18 11354.406 controlo 9.611322 10.98318 11.354406
pp04 9243.694 11898.51 12322.477 controlo 9.243694 11.89851 12.322477
pp05 10591.523 10970.10 11249.347 controlo 10.591523 10.97010 11.249346
pp06 10016.109 12010.43 12453.741 controlo 10.016110 12.01043 12.453741

Centrar e Escalonar Variáveis

Um tipo de alteração/conversão frequente, especialmente quando queremos computar regressões lineares, é centrar e escalonar variáveis. O R tem a função scale() que nos permite fazer exactamente isso. Mais uma vez, podemos escolher se queremos alterar os valores que estão na coluna ou criar uma nova coluna com esses valores. À semelhança dos exemplos anteriores, vamos assumir que queremos preservar os valores originais nas colunas originais e criar novas colunas para guardar os valores transformados.

# Centra e escalona cada coluna guardando o resultado numa nova.
ds$t1_centrada <- scale(ds$t1)
ds$t2_centrada <- scale(ds$t2)
ds$t3_centrada <- scale(ds$t3)

Por defeito a função scale() centra e escalona as variáveis, mas podem definir como TRUE ou FALSE os argumentos center e scale para alterar o comportamento. Já sabem que podem aprender mais sobre a função consultando o seu manual (i.e., help(scale)).

# Centrar sem escalonar.
# Versão abreviada.
scale(ds$t1, scale = FALSE)
# Versão longa (igual mas mais descritiva).
scale(ds$t1, center = TRUE, scale = FALSE)

# Escalonar sem centrar.
scale(ds$t1, center = FALSE)
# Versão longa (igual mas mais descritiva).
scale(ds$t1, center = FALSE, scale = TRUE)

# Não escalonar nem centrar.
scale(ds$t1, center = FALSE, scale = FALSE)
# Mas isso é literalmente deixar a variável como está...
# Portanto é um pouco parvo...mas é possível...o R não vos impede...
# Eu também sou parvo...e sou possível...o R deixa-me existir também...

Transformações Logarítmicas

Em investigações experimentais em que sejam recolhidos tempos de resposta é frequente aplicarmos uma transformação logarítmica a esses valores. Isso é feito para que a distribuição dos erros dum modelo que os tenha como variável dependente siga uma distribuição mais próxima da normal.

Nota: Tudo isto pode parecer Chinês a quem nunca tenha ouvido falar destas coisas, pelo que esta secção será mais útil para quem trabalhe com estes dados. De qualquer das formas se lerem o livro até ao fim, nomeadamente a Parte III, as frases anteriores farão mais sentido.

Nos exemplos abaixo continuamos a assumir que queremos manter a coluna com os valores originais guardando os logaritmos numa nova coluna.

ds$t1_log <- log(ds$t1)
ds$t2_log <- log(ds$t2)
ds$t3_log <- log(ds$t3)

A função log() computa os logaritmos de todos os elementos do primeiro argumento (o argumento x). Nos casos anteriores passámos à função os valores das colunas t1, t2 e t3 como argumento x. A função log() aceita um segundo argumento (o argumento base) com a base do logaritmo a calcular, sendo que por defeito a base é o exponencial de um.

# Base exponencial de um.
# Versão abreviada.
log(ds$t1)
# Versão longa (igual mas mais descritiva).
log(ds$t1, base = exp(1))

# Base 2
# "Atalho"
log2(ds$t1)
# Versão longa
log(ds$t1, base = 2)

# Base 10
# "Atalho"
log10(ds$t1)
# Versão longa
log(ds$t1, base = 10)

# Outras bases sem atalhos
# Base 5
log(ds$t1, 5)
# Base 7
log(ds$t1, 7)

(Re)formatar os Dados

Como vimos há alturas em que para além de transformar variáveis queremos mesmo alterar o formato dos dados. Mais concretamente, queremos passar os dados do formato largo para o formato longo, ou vice versa. O pacote tidyr, do projecto tidyverse, permite-nos passar os dados do formato largo para o formato longo, com a função pivot_longer(), bem como passar do formato longo para o largo, com a função pivot_wider(). Recomendo vivamente que leiam com atenção os tutoriais, exemplos e manuais das funções do pacote tidyr, disponíveis no site oficial do pacote. Relembro que se usarem um pacote, podem (e devem) citá-lo. Podem usar a função citation(), dando-lhe como argumento o nome do pacote que querem citar (entre parêntesis), para descobrir a citação recomendada pelos autores do pacote (e.g., citation("tidyr")).

Antes que nos esqueçamos vamos importar o pacote tidyr.

# Importar o pacote `tidyr`
library(tidyr)
Importante

Nunca se esqueçam que para usarem as funções de um pacote têm primeiro de importar esse pacote. Tecnicamente podem importar o pacote em qualquer linha do vosso script que esteja antes da linha que usa alguma função do pacote. Por uma questão de estilo e organização recomendo vivamente que importem todos os pacotes que vão usar num script, no início desse script. Se enquanto escrevem um script se lembrarem que precisam de mais um pacote, voltem ao topo do dito script para o adicionarem. Lembrem-se também que para poderem importar um pacote têm de o ter instalado no computador que estão a usar. Podem instalar o pacote com install.packages("nome_do_pacote_entre_aspas"). Só precisam de instalar o pacote uma vez, portanto não incluam linhas de código que instalam pacotes nos scripts de análise de dados. Em vez disso, criem um script à parte em que instalam todos os pacotes e que só precisa de ser executado uma vez. Releiam a secção sobre pacotes, do capítulo A gramática do R.

Largo Para Longo

Voltemos à nossa base de dados, onde vemos que temos uma linha por participante e uma coluna por cada medição dos tempos de resposta. Ou seja, a base de dados está no formato largo.

head(ds)
pp t1 t2 t3 cond t1_seg t2_seg t3_seg t1_centrada t2_centrada t3_centrada t1_log t2_log t3_log
pp01 10094.906 10241.46 9973.047 controlo 10.094906 10.24146 9.973047 0.5930145 -0.5612164 -0.7871396 9.219786 9.234199 9.207641
pp02 10112.319 11880.76 12489.212 controlo 10.112319 11.88076 12.489212 0.6419612 1.1290397 1.2114526 9.221510 9.382676 9.432621
pp03 9611.322 10983.18 11354.406 controlo 9.611322 10.98318 11.354406 -0.7663500 0.2035621 0.3100750 9.170697 9.304121 9.337361
pp04 9243.694 11898.51 12322.477 controlo 9.243694 11.89851 12.322477 -1.7997576 1.1473317 1.0790147 9.131697 9.384168 9.419180
pp05 10591.523 10970.10 11249.347 controlo 10.591523 10.97010 11.249346 1.9890131 0.1900720 0.2266261 9.267809 9.302929 9.328065
pp06 10016.109 12010.43 12453.741 controlo 10.016110 12.01043 12.453741 0.3715155 1.2627321 1.1832776 9.211950 9.393531 9.429776

Para os dados ficarem no formato longo teremos então de ter uma base de dados com três linhas por participante, uma por cada ensaio. Essa base terá agora apenas duas colunas, uma com o identificador do participante e outra com os tempos de resposta (independentemente do ensaio).

ds <- pivot_longer(ds, c("t1", "t2", "t3"))
Aviso

As colunas adicionadas por pivot_longer() aparecem no fim da tabela. Se a base de dados tiver muitas colunas não vão aparecer quando fizermos print da variável. Daí que o melhor seja seleccionarmos a coluna com o identificador do participante e imprimirmos só essa e as criadas/transformadas por pivot_longer().

print(ds[ ,c("pp", "name", "value")])
# A tibble: 150 × 3
   pp    name   value
   <chr> <chr>  <dbl>
 1 pp01  t1    10095.
 2 pp01  t2    10241.
 3 pp01  t3     9973.
 4 pp02  t1    10112.
 5 pp02  t2    11881.
 6 pp02  t3    12489.
 7 pp03  t1     9611.
 8 pp03  t2    10983.
 9 pp03  t3    11354.
10 pp04  t1     9244.
# ℹ 140 more rows

A função pivot_longer() permite especificarmos que nome queremos dar à coluna que vai conter os valores da variável dependente e a coluna que vai conter os níveis das medições repetidas. Os valores por defeito da função serão value e name respectivamente, mas podemos e devemos substituir por uns mais identificativos para quando formos analisar os dados o código ser mais legível.

Para podermos demonstrar vamos ter de inverter o que fizemos antes, ou seja, passar agora os dados do formato longo para o largo, mais à frente já vamos falar mais sobre isto, mas por agora podem ver que usamos a função oposta a pivot_longer() que dá pelo nome de pivot_wider().

# Passa os dados para o formato largo para voltarem ao estado inicial.
ds <- pivot_wider(ds)

Agora podemos voltar a passar para o formato longo especificando nomes diferentes (e.g., trial e response_time) dos usados por defeito (i.e., name e value).

ds <- pivot_longer(ds, c("t1", "t2", "t3"),
                   names_to = "trial", values_to = "response_time")

print(ds[ ,c("pp", "trial", "response_time")])
# A tibble: 150 × 3
   pp    trial response_time
   <chr> <chr>         <dbl>
 1 pp01  t1           10095.
 2 pp01  t2           10241.
 3 pp01  t3            9973.
 4 pp02  t1           10112.
 5 pp02  t2           11881.
 6 pp02  t3           12489.
 7 pp03  t1            9611.
 8 pp03  t2           10983.
 9 pp03  t3           11354.
10 pp04  t1            9244.
# ℹ 140 more rows

Vamos mais uma vez desfazer o que fizemos, voltando a pôr a base de dados no seu estado original para podermos ver mais funcionalidades da função pivot_longer(). Notem que desta vez, como as colunas já não têm os nomes usados por pivot_longer() por defeito, porque usamos os argumentos names_to e values_to, para usarmos a função pivot_wider() temos de especificar os argumentos simétricos de names_from e values_from.

Usar Padrões nos Nomes das Colunas

Vimos que para passar os dados para o formato longo podemos escrever o nome de todas as colunas que vão passar a ser os valores da coluna name (sendo que os valores que estavam nessas colunas passarão para a coluna values). O contudo as funções do pacote tidyr, assim como outras da família tidyverse, permitem-nos poupar muito trabalho usando padrões para identificar as colunas, em vez de ter de as listar todas. Isto é muito útil, por exemplo, quando têm mesmo muitos ensaios/colunas, e precisariam de muito tempo e espaço para as listar manualmente. Não teremos espaço para listar todos os padrões, mas podemos ver alguns exemplos.

Padrões regex

Os padrões regex são bastante usados por programadores para tratamento de texto. Permitem encontrar e substituir padrões de forma muito eficiente. Várias linguagens de programação e mesmo programas (e.g., excel) permitem que usemos regex para automatizar algumas procuras (com ou sem substituição). É verdade que hoje em dia temos ferramentas como os LLM (vulgo AI) que rapidamente conseguem reformatar texto. Se pedirmos a um LLM “converte todas as letras da frase abaixo para maiúscula”, ou “acrescenta um zero à esquerda de todos os números menores que 10, para que 1 fique 01, 2 fique 02, etc…”, até modelos mais limitados conseguirão completar a tarefa. Contudo, regex já permitia resolver problemas deste tipo há décadas atrás, ainda hoje é capaz de correr offline em máquinas com menos poder de processamento que uma torradeira. Também no R vamos poder usar regex para trabalharmos com strings (i.e., palavras e/ou texto) de forma muito eficiente. Dominando regex poderemos seleccionar colunas, renomeá-las, reformatar nomes dos níveis de VIs nominais—e converter dados do formato largo para o longo e vice-versa usando as funções do tidyr.

Expressões regulares (ou regex) são padrões que descrevem conjuntos de strings. Podemos usar regex com funções do R como gsub() para substituir texto e com names_pattern em pivot_longer() para extrair informação dos nomes das colunas.

# Converts t1 to trial_1, t3 to trial_3
colnames(ds) <- gsub("t(\\d+)", "trial_\\1", colnames(ds))

print(colnames(ds))
 [1] "pp"               "trial_1"          "trial_2"          "trial_3"         
 [5] "cond"             "trial_1_seg"      "trial_2_seg"      "trial_3_seg"     
 [9] "trial_1_centrada" "trial_2_centrada" "trial_3_centrada" "trial_1_log"     
[13] "trial_2_log"      "trial_3_log"     

Neste exemplo, vamos primeiro usar gsub() para adicionar “_original” aos nomes das colunas t1, t2, t3. O padrão "t(\\d$)" significa: - t - a letra t - (\\d$) - um dígito (\\d) no final da string ($), capturado num grupo

# Adiciona "_original" ao final dos nomes t1, t2, t3
colnames(ds) <- gsub("t(\\d$)", "t\\1_original", colnames(ds))
head(ds)
pp trial_1 trial_2 trial_3 cond trial_1_seg trial_2_seg trial_3_seg trial_1_centrada trial_2_centrada trial_3_centrada trial_1_log trial_2_log trial_3_log
pp01 10094.906 10241.46 9973.047 controlo 10.094906 10.24146 9.973047 0.5930145 -0.5612164 -0.7871396 9.219786 9.234199 9.207641
pp02 10112.319 11880.76 12489.212 controlo 10.112319 11.88076 12.489212 0.6419612 1.1290397 1.2114526 9.221510 9.382676 9.432621
pp03 9611.322 10983.18 11354.406 controlo 9.611322 10.98318 11.354406 -0.7663500 0.2035621 0.3100750 9.170697 9.304121 9.337361
pp04 9243.694 11898.51 12322.477 controlo 9.243694 11.89851 12.322477 -1.7997576 1.1473317 1.0790147 9.131697 9.384168 9.419180
pp05 10591.523 10970.10 11249.347 controlo 10.591523 10.97010 11.249346 1.9890131 0.1900720 0.2266261 9.267809 9.302929 9.328065
pp06 10016.109 12010.43 12453.741 controlo 10.016110 12.01043 12.453741 0.3715155 1.2627321 1.1832776 9.211950 9.393531 9.429776

Agora usamos pivot_longer() com names_pattern para separar o nome da coluna em duas partes: “trial” e “var”. O padrão "(.*)_(.*)" significa:

  • (.*) - captura qualquer coisa antes do underscore
  • _ - o separador underscore
  • (.*) - captura qualquer coisa depois do underscore
ds <- pivot_longer(ds, -c("pp", "cond"),
                   names_pattern = "trial_(.*)", names_to = "trial",
                   values_to = "dv")
print(ds)
# A tibble: 600 × 4
   pp    cond     trial             dv
   <chr> <chr>    <chr>          <dbl>
 1 pp01  controlo 1          10095.   
 2 pp01  controlo 2          10241.   
 3 pp01  controlo 3           9973.   
 4 pp01  controlo 1_seg         10.1  
 5 pp01  controlo 2_seg         10.2  
 6 pp01  controlo 3_seg          9.97 
 7 pp01  controlo 1_centrada     0.593
 8 pp01  controlo 2_centrada    -0.561
 9 pp01  controlo 3_centrada    -0.787
10 pp01  controlo 1_log          9.22 
# ℹ 590 more rows

Longo Para Largo

Já fomos usando a função pivot_wider() para desfazer o que fomos fazendo com pivot_longer(). Agora vamos falar um pouco melhor do que esta função faz.

A função pivot_wider() pega em dados que estão no formato longo e alarga-os. É essencialmente o oposto de pivot_longer(). A função precisa de saber qual a coluna que contém os nomes das novas colunas a criar (names_from) e qual a coluna que contém os valores para essas colunas (values_from).

Vamos voltar a pôr os nossos dados no formato largo original:

# Primeiro, vamos reverter para o estado anterior
ds <- pivot_wider(ds, names_from = "trial", values_from = "dv",
                  names_sep = "_")

head(ds)
pp cond 1 2 3 1_seg 2_seg 3_seg 1_centrada 2_centrada 3_centrada 1_log 2_log 3_log
pp01 controlo 10094.906 10241.46 9973.047 10.094906 10.24146 9.973047 0.5930145 -0.5612164 -0.7871396 9.219786 9.234199 9.207641
pp02 controlo 10112.319 11880.76 12489.212 10.112319 11.88076 12.489212 0.6419612 1.1290397 1.2114526 9.221510 9.382676 9.432621
pp03 controlo 9611.322 10983.18 11354.406 9.611322 10.98318 11.354406 -0.7663500 0.2035621 0.3100750 9.170697 9.304121 9.337361
pp04 controlo 9243.694 11898.51 12322.477 9.243694 11.89851 12.322477 -1.7997576 1.1473317 1.0790147 9.131697 9.384168 9.419180
pp05 controlo 10591.523 10970.10 11249.347 10.591523 10.97010 11.249346 1.9890131 0.1900720 0.2266261 9.267809 9.302929 9.328065
pp06 controlo 10016.109 12010.43 12453.741 10.016110 12.01043 12.453741 0.3715155 1.2627321 1.1832776 9.211950 9.393531 9.429776

Casos Mais Complexos

Em alguns casos mais específicos e complexos podemos ter de “alargar” os dados a mais, para depois “alongá-los” apenas um pouco, de forma a que fiquem apenas um pouco mais largos do que estavam. Noutros casos fazemos o oposto, “alongamos” demasiado, depois “alargamos” um pouco, de forma a ficarem apenas um pouco mais alargados do que estavam ao início. Temos casos ainda mais específicos, em que podemos usar as funções pivot_longer_spec() ou pivot_wider_spec(), para “alongar” ou “alargar” os dados de acordo com uma especificação concreta que queiramos, passo o pleonasmo, especificar.

Criar Mais que Duas Colunas ao Alongar

Às vezes queremos extrair informação para mais que duas colunas quando alongamos dados. Por exemplo, imagine que temos colunas com nomes como “t1_seg_original”, “t2_seg_original”, etc., e queremos separar em três colunas: trial, unidade, e tipo.

# Exemplo com múltiplas colunas de output
# Criar dados de exemplo
exemplo <- data.frame(pp = 1:5,
                      t1_seg_original = rnorm(5, 10, 2),
                      t1_ms_transformado = rnorm(5, 10000, 500),
                      t2_seg_original = rnorm(5, 12, 2),
                      t2_ms_transformado = rnorm(5, 12000, 500))

# Alongar para 3 colunas: trial, unidade, tipo
exemplo_longo <- pivot_longer(exemplo, -pp,
                               names_pattern = "t(\\d+)_(.*)_(.*)",
                               names_to = c("trial", "unidade", "tipo"),
                               values_to = "valor")

head(exemplo_longo)
pp trial unidade tipo valor
1 1 seg original 11.883472
1 1 ms transformado 10599.328106
1 2 seg original 11.954364
1 2 ms transformado 11642.818205
2 1 seg original 8.040375
2 1 ms transformado 10126.944934

Alargar Várias Colunas

Podemos alargar dados usando múltiplas colunas simultaneamente. Isto é útil quando temos várias medidas que queremos separar:

# Alargar usando múltiplas colunas
# Suponha que queremos separar por unidade E por tipo
exemplo_muito_largo <- pivot_wider(exemplo_longo,
                                   id_cols = c("pp", "trial"),
                                   names_from = c("unidade", "tipo"),
                                   values_from = "valor",
                                   names_sep = "_")

head(exemplo_muito_largo)
pp trial seg_original ms_transformado
1 1 11.883472 10599.33
1 2 11.954364 11642.82
2 1 8.040375 10126.94
2 2 13.381052 12659.01
3 1 9.125245 10040.78
3 2 9.926401 11610.71

Alargar Para Depois Alongar

Às vezes precisamos alargar demais e depois alongar um pouco. Isto acontece quando temos dados numa estrutura que não serve directamente ao nosso propósito:

# Exemplo: dados que precisam de ser reorganizados
# Suponha que temos isto:
dados_confusos <- data.frame(pp = 1:3, idade = c(25, 30, 35),
                             pre_ansiedade = c(15, 20, 18),
                             pre_depressao = c(10, 15, 12),
                             pos_ansiedade = c(10, 15, 14),
                             pos_depressao = c(5, 10, 8))

# Queremos: pp, idade, tempo (pre/pos), medida (ansiedade/depressao), valor

# Passo 1: Alongar tudo exceto pp e idade
dados_alongados <- pivot_longer(dados_confusos, -c("pp", "idade"),
                                names_pattern = "(.*)_(.*)",
                                names_to = c("tempo", "medida"),
                                values_to = "valor")

head(dados_alongados, 10)
pp idade tempo medida valor
1 25 pre ansiedade 15
1 25 pre depressao 10
1 25 pos ansiedade 10
1 25 pos depressao 5
2 30 pre ansiedade 20
2 30 pre depressao 15
2 30 pos ansiedade 15
2 30 pos depressao 10
3 35 pre ansiedade 18
3 35 pre depressao 12

Alongar Para Depois Alargar

O oposto também pode ser necessário - alongar demais e depois alargar para a estrutura desejada:

# Continuando o exemplo anterior...
# Se quisermos tempo em colunas mas medidas alongadas:
dados_reorg <- pivot_wider(dados_alongados,
                           names_from = tempo,
                           values_from = valor)

head(dados_reorg)
pp idade medida pre pos
1 25 ansiedade 15 10
1 25 depressao 10 5
2 30 ansiedade 20 15
2 30 depressao 15 10
3 35 ansiedade 18 14
3 35 depressao 12 8

Onde Procurar Ajuda

Para obter mais informação sobre as funções do pacote tidyr, podem usar:

# Ver documentação de uma função específica
help(pivot_longer)
help(pivot_wider)

# Ver todas as vinhetas (tutoriais) do pacote
vignette(package = "tidyr")

# Ver uma vinheta específica
vignette("pivot", package = "tidyr")

# Site oficial com documentação completa
browseURL("https://tidyr.tidyverse.org")

As vinhetas são particularmente úteis pois contêm exemplos práticos e explicações detalhadas de como usar as funções do pacote.

Exercícios

Exercício 1

  • Importe a base de dados demo_ds.csv e guarde-a numa variável.

  • Exclua os participantes que não consentiram em participar e os menores de idade, com apenas uma linha de código.

  • Grave os dados “limpos” num novo ficheiro clean_demo.csv algures no seu computador.

  • Importe a base de dados limpa e guarde-a numa variável que utilizará nos exercícios seguintes.

  • Releia o código usando a terminologia que já aprendemos (e.g., guarda o output da função…invocada com os argumentos…na variável…).

Soluções

Clique para ver as soluções
dados <- read.csv("../data/demo_ds.csv")
# dados <- read.csv(file.choose()) # Para interface gráfica.

dados <- subset(dados, consents == "Yes" & age >= 18)

write.csv(dados, "../data/clean_demo.csv", row.names = FALSE)
# Para interface gráfica:
#write.csv(dados, file.choose(new = TRUE), row.names = FALSE)

dados <- read.csv("../data/clean_demo.csv")
# dados <- read.csv(file.choose()) # Para interface gráfica.



Exercício 2

  • Crie uma nova coluna log_rt com os logaritmos naturais dos tempos de reposta. Clique para ver dica:

    A função log() computa o logaritmo natural.

  • Crie uma nova variável com a junção da variável cond e lab (no fundo uma variável com a interacção das duas).

    As funções paste() e paste0() permitem “colar” strings com e sem separadores entre elas, respectivamente.

  • Crie duas colunas i_phase1 e i_phase2 com os valores da phase1 e phase2 invertidos, respectivamente. Ou seja, se as respostas originais variam de 0 a 100, quem respondeu 0 deverá passar a ter 100 e quem respondeu 100 deverá passar a 0.

  • Crie uma nova coluna rcd_age com os valores da idade recodificados para intervalos menos identificativos.

Soluções

Clique para ver as soluções
dados$log_rt <- log(dados$rts)

dados$cond_lap <- paste0(dados$cond, dados$lab)

dados$i_phase1 <- (dados$phase1 - 100) * -1
dados$i_phase2 <- (dados$phase2 - 100) * -1

# Por exemplo intervalos em décadas (15-24, 25-34, 35-44, etc...).
dados$rcd_age <- round(dados$age / 10) * 10

Exercício 3

  • Instale (se não tiver instalado) e importe o pacote tidyr.

  • Passe a base de dados do formato largo (wide) para o formato longo (long), criando uma coluna com as resposta a phase1 e phase2 e outra com a medida (i.e., phase1 ou phase2). Ou seja, a base de dados passará a ter duas linhas por participante, uma com as suas resposta na phase1 e outra com as respostas na phase2. Clique para ver dica:

    A função pivot_longer() converte os dados para um formato mais longo.

  • Passe a base de dados para um formato mais largo revertendo o passo anterior usando a função pivot_wider().

  • Passe a base de dados para um formato ainda mais largo criando uma coluna diferente para os tempos de resposta em cada condição (cond).

  • Defina o que implica uma base de dados estar exclusivamente no formato longo (long), estar exclusivamente no formato largo (wide), ou estar num formato misto.

  • Liste uma vantagem e desvantagem de cada tipo de formato que descreveu anteriormente.

Soluções

Clique para ver as soluções
# Installs the `tidyr` package - to do it once (if not installed)
#install.packages("tidyr")

# Import the package
library(tidyr)

# Pivot data to long format
dados <- pivot_longer(dados, c("phase1", "phase2"),
                      names_to = "phase", values_to = "score")
# Check results (in the console, I'd avoid it to the script)
#head(dados)

# Pivot data back to wide format
dados <- pivot_wider(dados, names_from = "phase", values_from = "score")
#head(dados)

# Separate columns for RTs per between-pp condition (a stupid idea)
dados <- pivot_wider(dados, names_from = "cond", values_from = "rts")
head(dados)
pp_number consents age gender mood lab log_rt cond_lap i_phase1 i_phase2 rcd_age phase1 phase2 high med low
1 Yes 19 female 31 B 7.652546 highB 82 20 20 18 80 2106 NA NA
2 Yes 23 female 25 A 7.718241 highA 73 17 20 27 83 2249 NA NA
3 Yes 41 female 28 B 8.085795 highB 77 5 40 23 95 3248 NA NA
4 Yes 26 female 29 A 7.565793 highA 70 19 30 30 81 1931 NA NA
5 Yes 26 female 39 B 7.787382 highB 72 12 30 28 88 2410 NA NA
6 Yes 42 female 27 A 8.146419 highA 82 11 40 18 89 3451 NA NA
# Definições:
# - Formato longo exclusivo: Cada observação está numa linha separada,
#   com colunas para identificar o tipo de medida. Ideal para análises
#   que requerem medidas repetidas.
#
# - Formato largo exclusivo: Cada sujeito tem uma linha, e diferentes
#   medidas estão em colunas separadas. Mais intuitivo para leitura humana.
#
# - Formato misto: Algumas variáveis estão em formato longo e outras em
#   formato largo.
#
# Vantagens e desvantagens:
# - Longo: (+) Ideal para análises estatísticas modernas (lme4, ggplot2)
#          (-) Menos intuitivo, ocupa mais linhas
# - Largo: (+) Mais intuitivo, compacto
#          (-) Difícil de usar com muitas funções do R moderno