install.packages("tidyr")(Re)Formatação de Bases de Dados
0 Reformatação: Mudar a Forma sem Perder o Sentido
Depois de limpar os dados, ainda podemos precisar de os reorganizar. Nesta página vamos transformar variáveis e passar entre formatos largo (wide) e longo (long). Há uma pergunta que vale a pena fazer antes e depois de cada transformação: o que representa uma linha desta tabela?
Mudar a forma da tabela não muda, por si só, aquilo que foi medido. O tidyr é especialmente útil quando condições, momentos ou medidas ficaram codificados nos nomes das colunas. Mas nenhuma função consegue decidir por nós qual é a unidade observacional, a chave ou a escala que fazem sentido no estudo. A limpeza de dados trata da origem e dos critérios de inclusão. A introdução aos data.frames cobre a selecção básica antes de chegarmos aos pivôs.
Instale o pacote uma vez, fora do script, se necessário:
Um Pequeno Conjunto de Ensaios
Cada linha representa uma pessoa e t1, t2 e t3 são tempos de resposta em milissegundos. Os valores são inventados: servem para mostrar a estrutura, não para tirar conclusões sobre respostas humanas. Por agora, uma linha significa «uma pessoa». Depois do pivô, vai passar a significar «uma pessoa num ensaio».
| pp | t1 | t2 | t3 | cond |
|---|---|---|---|---|
| pp01 | 944 | 1122 | 893 | controlo |
| pp02 | 977 | 1036 | 978 | controlo |
| pp03 | 1156 | 1040 | 897 | controlo |
| pp04 | 1007 | 1011 | 927 | controlo |
| pp05 | 1013 | 944 | 937 | controlo |
| pp06 | 1172 | 1179 | 831 | experimental |
Transformações: Preservar o Original e Documentar a Escala
Uma conversão de unidade pode criar uma coluna nova para que o antes e o depois continuem lado a lado. O mesmo cuidado ajuda com qualquer variável derivada: transformar não é uma etapa obrigatória para tornar os dados “melhores”. Tem de haver uma razão para mudar a escala.
# Create seconds while preserving the original millisecond variables.
ds$t1_seg <- ds$t1 / 1000
ds$t2_seg <- ds$t2 / 1000
ds$t3_seg <- ds$t3 / 1000
# Create a condition indicator with an explicit, inspectable rule.
ds$cond_experimental <- ds$cond == "experimental"
# Reverse-score a 0--100 item without overwriting the original.
item <- c(0, 25, 50, 75, 100)
item_invertido <- 100 - item
# A log transform changes the response scale; check positivity first.
stopifnot(all(ds$t1 > 0))
ds$log_t1 <- log(ds$t1)
ds[, c("pp", "t1", "t1_seg", "cond_experimental", "log_t1")]| pp | t1 | t1_seg | cond_experimental | log_t1 |
|---|---|---|---|---|
| pp01 | 944 | 0.944 | FALSE | 6.850126 |
| pp02 | 977 | 0.977 | FALSE | 6.884487 |
| pp03 | 1156 | 1.156 | FALSE | 7.052721 |
| pp04 | 1007 | 1.007 | FALSE | 6.914731 |
| pp05 | 1013 | 1.013 | FALSE | 6.920672 |
| pp06 | 1172 | 1.172 | TRUE | 7.066467 |
| pp07 | 1046 | 1.046 | TRUE | 6.952729 |
| pp08 | 873 | 0.873 | TRUE | 6.771936 |
| pp09 | 931 | 0.931 | TRUE | 6.836259 |
| pp10 | 955 | 0.955 | TRUE | 6.861711 |
Também podemos padronizar uma coluna. scale() transforma os valores em z-scores: a nova variável fica centrada em 0 e cada unidade corresponde a um desvio-padrão. Isto é muitas vezes útil quando queremos pôr variáveis em escalas comparáveis.
# Transform t1 into z-scores.
ds$t1_z <- as.numeric(scale(ds$t1))
ds[, c("t1", "t1_z")]| t1 | t1_z |
|---|---|
| 944 | -0.6630503 |
| 977 | -0.3179295 |
| 1156 | 1.5540895 |
| 1007 | -0.0041833 |
| 1013 | 0.0585660 |
| 1172 | 1.7214208 |
| 1046 | 0.4036868 |
| 873 | -1.4055830 |
| 931 | -0.7990070 |
| 955 | -0.5480100 |
Por exemplo, um t1_z próximo de 1 indica um valor cerca de um desvio-padrão acima da média. Um valor próximo de -1, cerca de um desvio-padrão abaixo. Para já, é esta a ideia importante. Se mais tarde precisar de reutilizar exactamente a mesma média e o mesmo desvio-padrão noutro conjunto de dados, aí vale a pena olhar com mais detalhe para o que scale() guarda.
Largo para Longo: Uma Linha Passa a Ser uma Medição
O formato largo é cómodo para leitura: uma pessoa por linha e uma medição por coluna. Para representar ensaios, fazer gráficos ou ajustar muitos modelos, é frequentemente mais claro ter uma linha por medição. O ponto importante é não confundir a nova forma da tabela com novas unidades independentes. Três linhas da mesma pessoa continuam a ser três medições da mesma pessoa.
Antes do pivô, uma linha representa uma pessoa e pp é a chave esperada. Depois do pivô, uma linha representa uma pessoa num ensaio e a chave esperada passa a ser pp + ensaio. Uma combinação repetida pode indicar uma duplicação acidental ou uma dimensão que falta na chave. Não a agregue só para fazer desaparecer um aviso.
Escreva o significado da linha e a chave antes de transformar, e confirme-os outra vez no resultado.
ds <- pivot_longer(ds, cols = c(t1, t2, t3), names_to = "ensaio",
values_to = "tempo_ms")
dplyr::glimpse(ds)Rows: 30
Columns: 10
$ pp <chr> "pp01", "pp01", "pp01", "pp02", "pp02", "pp02", "pp0…
$ cond <chr> "controlo", "controlo", "controlo", "controlo", "con…
$ t1_seg <dbl> 0.944, 0.944, 0.944, 0.977, 0.977, 0.977, 1.156, 1.1…
$ t2_seg <dbl> 1.122, 1.122, 1.122, 1.036, 1.036, 1.036, 1.040, 1.0…
$ t3_seg <dbl> 0.893, 0.893, 0.893, 0.978, 0.978, 0.978, 0.897, 0.8…
$ cond_experimental <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
$ log_t1 <dbl> 6.850126, 6.850126, 6.850126, 6.884487, 6.884487, 6.…
$ t1_z <dbl> -0.663050308, -0.663050308, -0.663050308, -0.3179294…
$ ensaio <chr> "t1", "t2", "t3", "t1", "t2", "t3", "t1", "t2", "t3"…
$ tempo_ms <dbl> 944, 1122, 893, 977, 1036, 978, 1156, 1040, 897, 100…
dplyr::count(ds, pp)| pp | n |
|---|---|
| pp01 | 3 |
| pp02 | 3 |
| pp03 | 3 |
| pp04 | 3 |
| pp05 | 3 |
| pp06 | 3 |
| pp07 | 3 |
| pp08 | 3 |
| pp09 | 3 |
| pp10 | 3 |
glimpse() mostra os tipos e as dimensões sem despejar todas as linhas na página. count(ds, pp) confirma que cada participante tem três medições. Não é uma contagem de participantes independentes. pivot_longer() devolve um tibble. Quando o imprimimos na consola, a sua representação compacta começa habitualmente por algo como # A tibble: 30 × 10: isto indica 30 linhas e 10 colunas neste exemplo, não 30 pessoas independentes. No Quarto, a saída pode aparecer como uma tabela formatada. Por isso usamos também glimpse(), que explicita as dimensões e os tipos das colunas. count(ds, pp) resume quantas linhas pertencem a cada participante: neste exemplo, esperamos que cada pp tenha 3.
Agora cada participante tem três linhas: ensaio diz-nos de que coluna veio o valor e tempo_ms guarda a medição. O grão da linha mudou de pessoa para pessoa × ensaio. Não criámos três participantes. pp continua a identificar a pessoa e é a unidade que agrupa as medidas repetidas.
A contagem ajuda a inspeccionar o resultado, mas não substitui uma verificação explícita da chave. Aqui, a combinação pp + ensaio deve aparecer uma só vez:
chave <- c("pp", "ensaio")
stopifnot(anyDuplicated(ds[chave]) == 0)Da Forma ao Modelo
Para dados completos e equilibrados, com uma observação por pessoa × ensaio, a nova forma permite escrever uma única variável de resposta, tempo_ms, e uma variável que identifica o ensaio. Tornamos pp e ensaio factores explícitos. Como estas são medidas repetidas da mesma pessoa, a análise deve reservar essa estrutura no modelo. Não devemos tratar as 30 linhas como 30 pessoas independentes.
Anova Table (Type 3 tests)
Response: tempo_ms
Effect df MSE F ges p.value
1 ensaio 1.44, 12.99 14761.80 1.05 .080 .354
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
Sphericity correction method: GG
Este exemplo liga a reformatação ao workflow de medidas repetidas. Não é uma regra para todos os delineamentos. A fórmula diz que ensaio varia dentro de pp, que agrupa as três medições de cada pessoa. Para compreender a estrutura do modelo e as suas limitações, consulte a página teórica. A referência prática de medidas repetidas mostra como continuar quando há mais factores, dados incompletos ou uma pergunta sobre as mudanças entre pessoas.
A atribuição a ds substituiu a forma larga pela forma longa neste exemplo. As medições são as mesmas. Mudou o grão de cada linha. Quando precisarmos da forma larga outra vez, pivot_wider() criará um novo objecto. names_to e values_to dão nomes legíveis às duas colunas que o pivô cria.
Longo para Largo: A Chave Determina o Resultado
pivot_wider() usa names_from para escolher os nomes das novas colunas e values_from para escolher os valores. id_cols permite dizer explicitamente que colunas identificam cada linha que queremos obter.
largos <- pivot_wider(
ds,
id_cols = c(pp, cond),
names_from = ensaio,
values_from = tempo_ms
)Podemos verificar a estrutura e a dimensão do resultado assim. Como largos é um tibble, a sua impressão também mostrará de forma compacta o número de linhas e colunas:
print(largos)# A tibble: 10 × 5
pp cond t1 t2 t3
<fct> <chr> <dbl> <dbl> <dbl>
1 pp01 controlo 944 1122 893
2 pp02 controlo 977 1036 978
3 pp03 controlo 1156 1040 897
4 pp04 controlo 1007 1011 927
5 pp05 controlo 1013 944 937
6 pp06 experimental 1172 1179 831
7 pp07 experimental 1046 1050 1084
8 pp08 experimental 873 803 1015
9 pp09 experimental 931 1070 886
10 pp10 experimental 955 953 1125
[1] "pp" "cond" "t1" "t2" "t3"
Em dados reais, vários valores para a mesma combinação de pp e ensaio dizem-nos que a chave está incompleta ou que há duplicações. Conte-os antes de alargar. Separadamente, confirme que cada pp tem uma só cond, porque a condição é constante dentro da pessoa neste exemplo:
duplicados <- ds[duplicated(ds[c("pp", "ensaio")]) |
duplicated(ds[c("pp", "ensaio")], fromLast = TRUE), ]
nrow(duplicados)[1] 0
cond_por_pp <- unique(ds[c("pp", "cond")])
stopifnot(anyDuplicated(cond_por_pp["pp"]) == 0)Não resolva automaticamente este diagnóstico com values_fn = mean. Primeiro perceba porque existem vários valores e o que significaria fazer a média deles. Depois de qualquer pivô, confira dimensões, nomes e a unicidade da chave pp + ensaio que espera encontrar.
Um pivô correr sem erro não prova que a reformatação está correcta. Compare o número esperado de linhas e colunas antes e depois e investigue qualquer combinação repetida da chave indicada no aviso acima.
As técnicas seguintes são úteis quando os nomes das colunas codificam mais de uma dimensão ou medida. Se um pivô simples já resolve o seu problema, pode saltar esta secção e voltar quando encontrar uma tabela mais teimosa.
Padrões nos Nomes: names_sep e names_pattern
Quando os nomes têm várias dimensões, names_to pode receber várias colunas. Com names_sep separamos por um delimitador fixo. Com names_pattern usamos os grupos de captura duma expressão regular.
medidas <- data.frame(
pp = 1:3,
rt_easy_short = c(500, 520, 480),
rt_easy_long = c(700, 680, 720),
rt_hard_short = c(650, 640, 670),
rt_hard_long = c(900, 880, 920)
)
# Split each name into measure, difficulty, and length with names_sep.
por_sep <- pivot_longer(
medidas,
-pp,
names_to = c("medida", "dificuldade", "comprimento"),
names_sep = "_",
values_to = "valor"
)
por_sep| pp | medida | dificuldade | comprimento | valor |
|---|---|---|---|---|
| 1 | rt | easy | short | 500 |
| 1 | rt | easy | long | 700 |
| 1 | rt | hard | short | 650 |
| 1 | rt | hard | long | 900 |
| 2 | rt | easy | short | 520 |
| 2 | rt | easy | long | 680 |
| 2 | rt | hard | short | 640 |
| 2 | rt | hard | long | 880 |
| 3 | rt | easy | short | 480 |
| 3 | rt | easy | long | 720 |
| 3 | rt | hard | short | 670 |
| 3 | rt | hard | long | 920 |
# Capture the same dimensions with a regular expression.
por_padrao <- pivot_longer(
medidas,
-pp,
names_pattern = "(rt)_(easy|hard)_(short|long)",
names_to = c("medida", "dificuldade", "comprimento"),
values_to = "valor"
)A expressão regular funciona como um contrato: confirme que todos os nomes seleccionados correspondem ao padrão. Um nome que não corresponda pode produzir NA na informação extraída ou denunciar que seleccionámos uma coluna que não devia estar ali.
Várias Colunas de Valores e .value
Às vezes uma parte do nome não é um nível da variável, mas o nome da própria coluna de valores. O marcador especial .value diz ao pivot_longer() para criar uma coluna para essa parte.
condicoes <- data.frame(
pp = 1:3,
rt_easy = c(500, 520, 480),
acc_easy = c(.90, .85, .95),
rt_hard = c(800, 760, 820),
acc_hard = c(.75, .80, .70)
)
# Make rt and acc value columns, with difficulty as the repeated dimension.
condicoes_longas <- pivot_longer(
condicoes,
-pp,
names_to = c(".value", "dificuldade"),
names_sep = "_"
)
condicoes_longas| pp | dificuldade | rt | acc |
|---|---|---|---|
| 1 | easy | 500 | 0.90 |
| 1 | hard | 800 | 0.75 |
| 2 | easy | 520 | 0.85 |
| 2 | hard | 760 | 0.80 |
| 3 | easy | 480 | 0.95 |
| 3 | hard | 820 | 0.70 |
O mesmo princípio pode ser escrito com names_pattern, o que é útil quando o separador não é regular. Em vez de guardar tudo numa coluna valor, esta forma mantém separadas medidas como rt e acc.
condicoes_padrao <- pivot_longer(
condicoes,
-pp,
names_pattern = "(rt|acc)_(easy|hard)",
names_to = c(".value", "dificuldade")
)Para alargar várias medidas, passe vários nomes em values_from. names_from também pode ter várias colunas. names_sep controla como os níveis aparecem nos nomes finais.
longas_com_grupo <- expand.grid(
pp = 1:3,
dificuldade = c("easy", "hard"),
bloco = c("A", "B")
)
longas_com_grupo$rt <- 450 + 40 * (longas_com_grupo$dificuldade == "hard") +
10 * (longas_com_grupo$bloco == "B") + longas_com_grupo$pp
longas_com_grupo$acc <- .95 - .10 * (longas_com_grupo$dificuldade == "hard") -
.02 * (longas_com_grupo$bloco == "B")
largas_multiplas <- pivot_wider(
longas_com_grupo,
id_cols = pp,
names_from = c(dificuldade, bloco),
values_from = c(rt, acc),
names_sep = "_"
)
names(largas_multiplas)[1] "pp" "rt_easy_A" "rt_hard_A" "rt_easy_B" "rt_hard_B"
[6] "acc_easy_A" "acc_hard_A" "acc_easy_B" "acc_hard_B"
A escolha de id_cols, names_from e values_from descreve a chave e as medidas, não apenas o aspecto final da tabela. Se a combinação não for única, pivot_wider() pode criar uma lista ou pedir uma função de agregação. Pare e investigue antes de escolher uma função só para fazer o aviso desaparecer.
Pivôs em Etapas: Tornar o Caminho Legível
Nem sempre compensa transformar tudo numa única chamada. Às vezes é mais fácil auditar o que fizemos se alongarmos primeiro para limpar os nomes e só depois alargarmos as dimensões de que precisamos. Este exemplo começa com dois momentos e duas medidas numa tabela larga.
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)
)
# Stage 1: extract time and measure from the column names.
longos_limpos <- pivot_longer(
confusos,
-c(pp, idade),
names_pattern = "(pre|pos)_(ansiedade|depressao)",
names_to = c("momento", "medida"),
values_to = "pontuacao"
)
# Stage 2: widen only the dimension needed by the next analysis.
comparacao_momentos <- pivot_wider(
longos_limpos,
id_cols = c(pp, idade, medida),
names_from = momento,
values_from = pontuacao
)
comparacao_momentos| 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 |
Também podemos fazer o percurso inverso: alargar primeiro duas medidas e alongar depois para uma coluna de valores. O importante é que, em cada etapa, consigamos dizer o que significa uma linha e inspeccionar se essa interpretação continua a fazer sentido.
# Widen by measure, keeping one row per person and moment.
por_medida <- pivot_wider(
longos_limpos,
id_cols = c(pp, idade, momento),
names_from = medida,
values_from = pontuacao
)
# Then gather the selected measures again for a downstream comparison.
novamente_longos <- pivot_longer(
por_medida,
c(ansiedade, depressao),
names_to = "medida",
values_to = "pontuacao"
)
stopifnot(nrow(novamente_longos) == nrow(longos_limpos))Salvaguardas Antes e Depois
Antes de transformar, responda a estas perguntas. Vale a pena escrevê-las num comentário junto do código: uma tabela muito elegante também consegue contar uma história errada.
- O que representa cada linha neste momento?
- Que colunas identificam inequivocamente essa linha?
- Que nomes contêm dimensões e quais contêm medidas?
- Quantas linhas e combinações esperamos no resultado?
Depois, inspeccione names(), dim(), alguns casos e a unicidade da nova chave. Para uma chave pp, momento e medida, por exemplo:
chave <- c("pp", "momento", "medida")
anyDuplicated(longos_limpos[chave])[1] 0
# A tibble printout gives a compact dimensions check after each pivot.
longos_limpos| pp | idade | momento | medida | pontuacao |
|---|---|---|---|---|
| 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 |
| 3 | 35 | pos | ansiedade | 14 |
| 3 | 35 | pos | depressao | 8 |
comparacao_momentos| 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 |
anyDuplicated() == 0 diz-nos que a chave é única. Não demonstra que essa é a chave cientificamente correcta. Compare também alguns valores conhecidos antes e depois. Uma coluna trocada pode continuar a produzir uma tabela perfeitamente válida aos olhos do R.
Para aprofundar, consulte a documentação oficial de pivot_longer() e de pivot_wider().
Exercícios
- Crie
t1_minet2_min, preservando os milissegundos. Explique o factor de conversão e indique as unidades antes e depois da transformação. - Passe
dspara longo e escreva uma frase que defina exactamente uma linha do resultado. Verifique a chave mínimapp–ensaioe confirme separadamente que cadapptem uma sócondantes de voltar a largo. Passe o resultado a umlm()como no exemplo, explique por queppestá na fórmula e confirme que o objecto largo não foi alterado. - Construa uma tabela com nomes como
score_pre,score_pos,tempo_preetempo_pos. Use.valuepara obter colunasscoreetempo, e confirme que cada pessoa e momento aparece uma vez. - Use
names_patternpara separar nomes com três dimensões, por exemplort_easy_short. Faça uma tabela de contagens dos níveis extraídos e investigue qualquerNA. - Faça um
pivot_wider()com váriosnames_fromevalues_from. Antes de agregar duplicados, crie um pequeno exemplo que contenha uma chave repetida; explique por que a média, a soma ou nenhuma agregação seria apropriada. - Reorganize
confusosem duas etapas diferentes das mostradas. Compare as dimensões e as chaves depois de cada etapa e descreva qual versão é mais fácil de auditar. - Use
scale()para transformar uma variável quantitativa em z-scores. Veja alguns valores antes e depois e explique o que significam aproximadamentez = 0,z = 1ez = -1. - Se usar um colega ou um LLM para diagnosticar uma chave duplicada, mostre primeiro o código que tentou e peça ajuda para identificar a unidade observacional e a chave antes de pedir uma solução pronta.
Soluções
Exercício 1
base_wide <- largos
base_wide$t1_min <- base_wide$t1 / 60000
base_wide$t2_min <- base_wide$t2 / 60000
base_wide[, c("t1", "t1_min", "t2", "t2_min")]| t1 | t1_min | t2 | t2_min |
|---|---|---|---|
| 944 | 0.0157333 | 1122 | 0.0187000 |
| 977 | 0.0162833 | 1036 | 0.0172667 |
| 1156 | 0.0192667 | 1040 | 0.0173333 |
| 1007 | 0.0167833 | 1011 | 0.0168500 |
| 1013 | 0.0168833 | 944 | 0.0157333 |
| 1172 | 0.0195333 | 1179 | 0.0196500 |
| 1046 | 0.0174333 | 1050 | 0.0175000 |
| 873 | 0.0145500 | 803 | 0.0133833 |
| 931 | 0.0155167 | 1070 | 0.0178333 |
| 955 | 0.0159167 | 953 | 0.0158833 |
Exercício 2
Aqui usamos o objecto largo guardado anteriormente para não alterar o original. Uma linha de dados_longos representa uma pessoa num ensaio.
largo_original <- largos
dados_longos <- pivot_longer(
largo_original,
cols = c(t1, t2, t3),
names_to = "ensaio",
values_to = "tempo_ms"
)
stopifnot(anyDuplicated(dados_longos[c("pp", "ensaio")]) == 0)
stopifnot(anyDuplicated(largo_original[c("pp", "cond")]) == 0)
modelo_lm <- lm(tempo_ms ~ ensaio + pp, data = dados_longos)
modelo_lm
Call:
lm(formula = tempo_ms ~ ensaio + pp, data = dados_longos)
Coefficients:
(Intercept) ensaiot2 ensaiot3 pppp02 pppp03 pppp04
998.567 13.400 -50.100 10.667 44.667 -4.667
pppp05 pppp06 pppp07 pppp08 pppp09 pppp10
-21.667 74.333 73.667 -89.333 -24.000 24.667
pp entra na fórmula para representar diferenças sistemáticas entre pessoas; as três linhas da mesma pessoa não são observações independentes. Um modelo misto, como o aov_4() mostrado acima, é uma alternativa mais apropriada quando queremos modelar explicitamente as medidas repetidas.
Exercício 3
medidas <- data.frame(
pp = 1:3,
score_pre = c(12, 15, 11), score_pos = c(14, 16, 13),
tempo_pre = c(600, 550, 620), tempo_pos = c(580, 530, 610)
)
medidas_longas <- pivot_longer(
medidas, -pp,
names_to = c(".value", "momento"),
names_sep = "_"
)
medidas_longas| pp | momento | score | tempo |
|---|---|---|---|
| 1 | pre | 12 | 600 |
| 1 | pos | 14 | 580 |
| 2 | pre | 15 | 550 |
| 2 | pos | 16 | 530 |
| 3 | pre | 11 | 620 |
| 3 | pos | 13 | 610 |
stopifnot(anyDuplicated(medidas_longas[c("pp", "momento")]) == 0).value transforma a primeira parte do nome em duas colunas de valores, score e tempo, em vez de juntar ambas numa única coluna valor.
Exercício 4
nomes <- data.frame(
pp = 1:4,
rt_easy_short = c(500, 520, 480, 510),
rt_easy_long = c(700, 680, 720, 710),
rt_hard_short = c(650, 640, 670, 660),
rt_hard_long = c(900, 880, 920, 910)
)
longos <- pivot_longer(
nomes, -pp,
names_to = c("medida", "dificuldade", "comprimento"),
names_pattern = "^(rt)_(easy|hard)_(short|long)$",
values_to = "valor"
)
table(longos$dificuldade, longos$comprimento, useNA = "ifany")
long short
easy 4 4
hard 4 4
medida dificuldade comprimento
0 0 0
Se surgirem NA, inspeccionamos as linhas correspondentes e comparamos os nomes originais com o padrão, em vez de os remover automaticamente:
| pp | medida | dificuldade | comprimento | valor |
|---|
names_sep = "_" neste exemplo; names_pattern é mais útil quando a separação não é regular.
Exercício 5
repetida <- data.frame(
pp = c(1, 1, 1, 2), momento = c("pre", "pre", "pos", "pre"),
grupo = c("A", "A", "A", "B"),
score = c(10, 12, 14, 9), tempo = c(600, 620, 580, 700)
)
chave <- c("pp", "momento", "grupo")
repetida[duplicated(repetida[chave]) | duplicated(repetida[chave], fromLast = TRUE), ]| pp | momento | grupo | score | tempo |
|---|---|---|---|---|
| 1 | pre | A | 10 | 600 |
| 1 | pre | A | 12 | 620 |
larga <- pivot_wider(
repetida, id_cols = pp,
names_from = c(grupo, momento), values_from = c(score, tempo),
names_sep = "_"
)
larga| pp | score_A_pre | score_A_pos | score_B_pre | tempo_A_pre | tempo_A_pos | tempo_B_pre |
|---|---|---|---|---|---|---|
| 1 | 10, 12 | 14 | NULL | 600, 620 | 580 | NULL |
| 2 | NULL | NULL | 9 | NULL | NULL | 700 |
pp–momento–grupo tem duas medições. Não há uma resposta universal: nenhuma agregação é adequada se a repetição for um erro ou se representar unidades que deviam permanecer separadas; a média pode ser adequada para réplicas que queremos resumir, e a soma apenas quando a medida é aditiva. Depois de justificar a decisão, poderíamos usar, por exemplo, values_fn = mean.
Exercício 6
Este percurso extrai primeiro o nome completo e separa-o numa etapa distinta.
por_nome <- pivot_longer(
confusos, -c(pp, idade),
names_to = "nome", values_to = "pontuacao"
)
por_nome <- separate(por_nome, nome, into = c("momento", "medida"), sep = "_")
resultado <- pivot_wider(
por_nome,
id_cols = c(pp, idade, momento),
names_from = medida,
values_from = pontuacao
)
stopifnot(anyDuplicated(por_nome[c("pp", "momento", "medida")]) == 0)
stopifnot(anyDuplicated(resultado[c("pp", "idade", "momento")]) == 0)
dim(por_nome)[1] 12 5
dim(resultado)[1] 6 5
names(resultado)[1] "pp" "idade" "momento" "ansiedade" "depressao"
names_pattern pode ser mais compacta; esta torna explícita a etapa de separar os nomes, o que pode facilitar a auditoria.
Exercício 7
x <- largos$t1
z <- as.numeric(scale(x))
data.frame(t1 = x, t1_z = z)| t1 | t1_z |
|---|---|
| 944 | -0.6630503 |
| 977 | -0.3179295 |
| 1156 | 1.5540895 |
| 1007 | -0.0041833 |
| 1013 | 0.0585660 |
| 1172 | 1.7214208 |
| 1046 | 0.4036868 |
| 873 | -1.4055830 |
| 931 | -0.7990070 |
| 955 | -0.5480100 |
z = 0 corresponde aproximadamente à média, z = 1 a um desvio-padrão acima dela e z = -1 a um desvio-padrão abaixo. Poderíamos padronizar outra variável quantitativa, desde que a escolha da média e do desvio-padrão seja adequada à pergunta.
Exercício 8
Primeiro mostramos o diagnóstico que tentámos, sem esconder a estrutura dos dados:
tentativa <- dados_longos[duplicated(dados_longos[c("pp", "ensaio")]) |
duplicated(dados_longos[c("pp", "ensaio")],
fromLast = TRUE), ]
tentativa| pp | cond | ensaio | tempo_ms |
|---|
pedido_ajuda <- paste(
"Tentei verificar a chave pp-ensaio com duplicated().",
"Que unidade observacional representam as linhas e que coluna falta,",
"se alguma, para tornar a chave única? Não proponha ainda uma agregação."
)
pedido_ajuda[1] "Tentei verificar a chave pp-ensaio com duplicated(). Que unidade observacional representam as linhas e que coluna falta, se alguma, para tornar a chave única? Não proponha ainda uma agregação."