Autor

Maria Clara Figueiredo e Victória Raspante

Data de Publicação

07/09/2026, 13:27

1 📌 Introdução

A análise de dados é uma ferramenta de grande valor para o acompanhamento e o controle de qualidade dos materiais empregados nas obras, no âmbito da Engenharia Civil. No controle tecnológico do concreto, informações como resistência à compressão, abatimento, relação água/cimento e idade dos corpos de prova auxiliam na avaliação do desempenho do material e na identificação de possíveis resultados fora do comportamento esperado.

Entretanto, dados obtidos em ensaios, registros de campo e planilhas podem apresentar valores ausentes, erros de digitação e inconsistências de preenchimento. Por esse motivo, antes da realização das análises estatísticas, é importante verificar a qualidade dos registros e realizar os tratamentos necessários para garantir maior confiabilidade aos resultados.

Nesse contexto, este relatório utilizará a linguagem R para inspecionar, tratar e explorar uma base de dados relacionada ao controle tecnológico do concreto, buscando transformar os dados brutos em informações úteis para sua análise e interpretação.

2 🎯 Objetivos

2.1 Objetivo geral

Processar e analisar uma base de dados referente ao controle tecnológico do concreto, utilizando prioritariamente as ferramentas da família tidyverse, dentro da linguagem R, para identificar e tratar inconsistências e obter informações relevantes para a interpretação dos resultados.

2.2 Objetivos específicos

  • Inspecionar a estrutura e as características da base de dados;
  • Identificar valores ausentes, erros de digitação e possíveis inconsistências;
  • Padronizar e tratar as variáveis para realização das análises;
  • Aplicar recursos da família tidyverse, dentro da linguagem R para manipulação e exploração dos dados;
  • Calcular e comparar resultados relacionados às propriedades do concreto;
  • Investigar relações entre variáveis relevantes para o controle tecnológico;
  • Interpretar os resultados obtidos considerando sua aplicação na Engenharia Civil.

3 ⚙️ Metodologia

O desenvolvimento deste trabalho será realizado a partir de uma base de dados relacionada ao controle tecnológico do concreto, contendo informações referentes aos corpos de prova, à sua localização na obra, ao tipo de concreto, à idade de ensaio e às características do material. O processamento e a análise dos dados serão realizados utilizando a linguagem R, priorizando as ferramentas da família tidyverse e o operador |> para a construção dos fluxos de processamento.

Inicialmente, será realizada a importação e a inspeção da base de dados, buscando compreender sua estrutura, identificar as variáveis presentes e verificar como foram reconhecidas pelo R. Nessa etapa, também serão analisadas as dimensões da base e os tipos das variáveis, permitindo identificar possíveis problemas que possam interferir nas etapas seguintes.

Em seguida, será realizada a investigação da qualidade dos dados, considerando a existência de valores ausentes, erros de digitação e formatação, inconsistências na padronização das categorias e valores que apresentem comportamento diferente do restante da base. Para essa investigação, serão utilizadas ferramentas dos pacotes dplyr, tidyr, stringr e readr. Quando necessário, serão realizadas correções, conversões e padronizações, mantendo-se uma cópia da base original.

Após o tratamento, será realizada a exploração dos dados por meio de estatísticas descritivas e agrupamentos, considerando a resistência à compressão e sua relação com o tipo de concreto, o bloco da obra e a idade dos corpos de prova. Também serão analisadas outras características do material, como consumo de cimento, relação água/cimento, abatimento, densidade e absorção do agregado. Para essas análises, serão utilizadas principalmente as funções de manipulação e transformação disponíveis no pacote dplyr.

Além disso, serão criadas variáveis derivadas a partir das informações existentes, permitindo comparar a resistência observada com a resistência de referência de cada classe de concreto, classificar os resultados e identificar valores superiores à média de cada classe. Serão realizadas, também, transformações simultâneas em diferentes variáveis por meio da função across(), buscando explorar as possibilidades de processamento oferecidas pelas ferramentas do tidyverse.

Por fim, os dados tratados serão utilizados na análise de situações relacionadas à prática profissional da Engenharia Civil. Serão realizadas comparações entre os blocos da obra e as classes de concreto, além da análise do comportamento da resistência em função da idade, do consumo de cimento e da relação água/cimento. As análises serão complementadas por representações gráficas elaboradas com o pacote ggplot2 e por tabelas organizadas com knitr::kable(), buscando facilitar a interpretação dos resultados obtidos no controle tecnológico do concreto.

Principais funções e comandos utilizados no R

  • read_delim() e parse_double() - importação e conversão dos dados;
  • glimpse() e summary() - inspeção da estrutura e resumo da base;
  • summarise(), n() e across() - obtenção de estatísticas e aplicação de operações a diferentes variáveis;
  • is.na(), if_any() e drop_na() - identificação, localização e tratamento dos valores ausentes;
  • select(), filter(), slice_head() e slice_tail() - seleção de variáveis e observações;
  • pivot_longer() - reorganização da estrutura dos dados;
  • mutate() e transmute() - criação e transformação de variáveis;
  • str_replace(), str_replace_all(), str_trim(), str_to_upper(), str_to_title() e str_detect() - correção, padronização e investigação de textos;
  • count(), distinct() e arrange() - contagem, identificação e organização dos registros;
  • group_by() e ungroup() - agrupamento e desagrupamento das observações;
  • case_when() e if_else() - aplicação de condições para correção, classificação e criação de variáveis;
  • mean(), median(), sd(), min() e max() - cálculo das medidas estatísticas;
  • ggplot(), geom_boxplot(), geom_point(), geom_line(), geom_col() e geom_smooth() - elaboração das representações gráficas;
  • knitr::kable() - organização e apresentação das tabelas no relatório;
  • |> - construção dos fluxos de processamento e transformação dos dados.

4 🔍 Resultados e Discussão

4.1 Importação do banco de dados

A base de dados foi importada para o R através da função read_delim()e atribuída a dados.

Código
library(tidyverse)

dados <- read_delim(
  "base_processamento_dados_engenharia_civil.csv",
  delim = ";",
  locale = locale(decimal_mark = "."),
  show_col_types = FALSE
)

Para verificar a correta importação dos dados, foram visualizadas as seis primeiras e as seis últimas observações da base utilizando, respectivamente, as funções slice_head() e slice_tail().

Código
dados |>
  slice_head(n = 6) |>
  knitr::kable(
    caption = "Primeiras observações da base de dados",
    digits = 2
  )
Primeiras observações da base de dados
id_corpo_prova obra tipo_concreto idade_dias resistencia_mpa cimento_kg_m3 relacao_a_c abatimento_mm densidade_kg_m3 absorção_agregado_pct
CP-001 Bloco A C35 28 36.3 395 0.5 111 2332 2.30
CP-002 Bloco A C30 7 28.4 340 0.54 114 2389 1.85
CP-003 Bloco C C30 56 36.1 338 0.56 106 2427 1.89
CP-004 Bloco A C40 28 47.2 407 0.47 142 2345 1.97
CP-005 Bloco B C25 28 27.9 316 0.61 133 2448 2.31
CP-006 Bloco B C40 28 41.5 398 0.43 98 2324 0.80
Código
dados |>
  slice_tail(n = 6) |>
  knitr::kable(
    caption = "Últimas observações da base de dados",
    digits = 2
  )
Últimas observações da base de dados
id_corpo_prova obra tipo_concreto idade_dias resistencia_mpa cimento_kg_m3 relacao_a_c abatimento_mm densidade_kg_m3 absorção_agregado_pct
CP-095 Bloco A C25 28 30.3 327 0.63 124 2334 1.55
CP-096 Bloco D C25 7 21.3 295 0.59 117 2349 1.72
CP-097 Bloco A C25 28 26.7 356 0.64 122 2440 NA
CP-098 Bloco A C35 14 37.0 375 0.46 137 2387 2.04
CP-099 Bloco C C25 56 31.1 332 0.62 81 2414 1.88
CP-100 Bloco A C30 56 30.9 377 0.55 109 2346 1.13

Para verificar as dimensões da base, utilizou-se summarise(), em conjunto com n() para contabilizar o número de observações e ncol() para obter a quantidade de variáveis.

Código
dados |>
  summarise(
    observacoes = n(),
    variaveis = ncol(dados)
  ) |>
  knitr::kable(
    caption = "Dimensões da base de dados"
  )
Dimensões da base de dados
observacoes variaveis
100 10

Portanto, a base de dados é composta por 100 observações e 10 variáveis.

Código
dados |>
  glimpse()
Rows: 100
Columns: 10
$ id_corpo_prova        <chr> "CP-001", "CP-002", "CP-003", "CP-004", "CP-005"…
$ obra                  <chr> "Bloco A", "Bloco A", "Bloco C", "Bloco A", "Blo…
$ tipo_concreto         <chr> "C35", "C30", "C30", "C40", "C25", "C40", "C25",…
$ idade_dias            <dbl> 28, 7, 56, 28, 28, 28, 56, 28, 14, 7, 7, 28, 28,…
$ resistencia_mpa       <chr> "36.3", "28.4", "36.1", "47.2", "27.9", "41.5", …
$ cimento_kg_m3         <chr> "395", "340", "338", "407", "316", "398", "351",…
$ relacao_a_c           <chr> "0.5", "0.54", "0.56", "0.47", "0.61", "0.43", "…
$ abatimento_mm         <dbl> 111, 114, 106, 142, 133, 98, 99, 116, 125, 127, …
$ densidade_kg_m3       <dbl> 2332, 2389, 2427, 2345, 2448, 2324, 2385, 2387, …
$ absorção_agregado_pct <dbl> 2.30, 1.85, 1.89, 1.97, 2.31, 0.80, 1.18, 1.33, …

A estrutura da base foi inspecionada por meio da função glimpse(). Com o resultado obtido, já é possível verificar como o R reconheceu as variáveis, mas para uma visualização mais clara, será utilizada a função class() aplicada às colunas da base por meio de across()

Código
tipos_variaveis <- dados |>
  summarise(
    across(
      everything(),
      ~ class(.x)
    )
  ) |>
  pivot_longer(
    cols = everything(),
    names_to = "Variável",
    values_to = "Tipo reconhecido pelo R"
  )

tipos_variaveis |>
  knitr::kable(
    caption = "Tipos das variáveis reconhecidos pelo R"
  )
Tipos das variáveis reconhecidos pelo R
Variável Tipo reconhecido pelo R
id_corpo_prova character
obra character
tipo_concreto character
idade_dias numeric
resistencia_mpa character
cimento_kg_m3 character
relacao_a_c character
abatimento_mm numeric
densidade_kg_m3 numeric
absorção_agregado_pct numeric

Dessa forma, verificou-se que idade_dias, abatimento_mm, densidade_kg_m3 e absorção_agregado_pct foram reconhecidas pelo R como variáveis numéricas. Por outro lado, id_corpo_prova, obra, tipo_concreto, resistencia_mpa, cimento_kg_m3 e relacao_a_c foram reconhecidas como texto. Entretanto, resistencia_mpa, cimento_kg_m3e relacao_a_c representam grandezas quantitativas e, portanto, indicam a possível presença de inconsistências de formatação ou digitação nessas variáveis.

4.2 Investigação dos Dados

4.2.1 Dados Ausentes

Para observar se há dados ausentes, utilizou-se a função is.na() em conjunto com summarise() e across(), permitindo contabilizar a quantidade de valores ausentes em cada variável da base. Em seguida, por meio da função if_any() foram identificadas as observações que apresentam pelo menos um valor ausente.

Código
dados |>
  summarise(
    across(
      everything(),
      ~ sum(is.na(.x))
    )
  ) |>
  pivot_longer(
    cols = everything(),
    names_to = "Variável",
    values_to = "Valores ausentes"
  ) |>
  knitr::kable(
    caption = "Quantidade de valores ausentes por variável"
  )
Quantidade de valores ausentes por variável
Variável Valores ausentes
id_corpo_prova 0
obra 0
tipo_concreto 0
idade_dias 0
resistencia_mpa 1
cimento_kg_m3 0
relacao_a_c 1
abatimento_mm 1
densidade_kg_m3 1
absorção_agregado_pct 1
Código
registros_ausentes <- dados |>
  mutate(
    possui_na = if_any(
      everything(),
      is.na
    )
  ) |>
  filter(possui_na) |>
  select(-possui_na)

registros_ausentes |>
  knitr::kable(
    caption = "Observações contendo valores ausentes",
    digits = 2
  )
Observações contendo valores ausentes
id_corpo_prova obra tipo_concreto idade_dias resistencia_mpa cimento_kg_m3 relacao_a_c abatimento_mm densidade_kg_m3 absorção_agregado_pct
CP-015 Bloco B C35 14 36.3 363 0.49 NA 2359 1.93
CP-038 Bloco C C25 28 25.7 309 NA 146 2334 2.12
CP-077 Bloco D C25 14 19.0 307 0.63 119 NA 1.38
CP-092 Bloco B C35 14 NA 370 0.47 111 2380 2.38
CP-097 Bloco A C25 28 26.7 356 0.64 122 2440 NA

A análise identificou cinco valores ausentes na base de dados, distribuídos entre as variáveis resistencia_mpa, relacao_a_c, abatimento_mm, densidade_kg_m3 e absorção_agregado_pct, cada uma apresentando um registro ausente. As demais variáveis não apresentaram valores ausentes.

Essa análise possibilitou localizar exatamente os corpos de prova associados a essas ausências, o que será importante para definir posteriormente o tratamento mais adequado para cada caso.

Ressalta-se uma diferença em relação ao procedimento adotado no Relatório 02, em que a base foi importada utilizando a função read.csv2(), da Base R, e os campos sem preenchimento foram mantidos como strings vazias (""), sendo necessário identificá-los por meio da comparação dados == "". No presente relatório, a importação foi realizada com a função read_delim(), do pacote readr, que reconhece os campos vazios como valores ausentes (NA). Dessa forma, a identificação das ausências pode ser realizada diretamente com is.na().

4.2.2 Variáveis Quantitativas

Considerando que algumas variáveis de natureza quantitativa foram reconhecidas pelo R como texto, realizou-se uma tentativa de conversão de seus valores para o formato numérico.

Para facilitar a investigação, as variáveis foram reorganizadas com pivot_longer() e convertidas temporariamente por meio de parse_double().

Foram considerados potencialmente problemáticos os registros originalmente preenchidos cuja tentativa de conversão resultou em NA, permitindo identificar automaticamente os valores incompatíveis com o formato numérico esperado.

Código
variaveis_quantitativas_texto <- c(
  "resistencia_mpa",
  "cimento_kg_m3",
  "relacao_a_c"
)

problemas_identificados <- dados |>
  select(
    id_corpo_prova,
    all_of(variaveis_quantitativas_texto)
  ) |>
  pivot_longer(
    cols = all_of(variaveis_quantitativas_texto),
    names_to = "Variável",
    values_to = "Valor"
  ) |>
  mutate(
    valor_teste = parse_double(Valor)
  ) |>
  filter(
    !is.na(Valor) & is.na(valor_teste)
  ) |>
  select(
    id_corpo_prova,
    Variável,
    Valor
  )

problemas_identificados |>
  knitr::kable(
    caption = "Valores com inconsistências de formatação ou digitação",
    col.names = c(
      "Corpo de prova",
      "Variável",
      "Valor identificado"
    )
  )
Valores com inconsistências de formatação ou digitação
Corpo de prova Variável Valor identificado
CP-008 resistencia_mpa 38,7
CP-019 resistencia_mpa 3O,4
CP-045 relacao_a_c 0,55
CP-064 cimento_kg_m3 390O

A tentativa de conversão das variáveis quantitativas originalmente reconhecidas como texto permitiu identificar quatro registros com problemas de formatação ou digitação. Nos corpos de prova CP-008, CP-019 e CP-045, foram encontrados valores com vírgula como separador decimal (38,7 e 0,55, respectivamente). Nos corpos de prova CP-019 e CP-064, foram identificados caracteres alfabéticos em valores que deveriam ser exclusivamente numéricos, correspondentes a 3O,4 e 390O, indicando a utilização da letra “O” no lugar do algarismo zero.

4.2.3 Variáveis Qualitativas

Para verificar se todas as categorias de obra estão escritas de forma padronizada, utilizou-se as funções count() e distinct().

Código
dados |>
  count(obra) |>
  knitr::kable(
    caption = "Frequência das categorias da variável obra",
    col.names = c("Obra", "Frequência")
  )
Frequência das categorias da variável obra
Obra Frequência
Bloco A 28
Bloco B 28
Bloco B 1
Bloco C 28
Bloco D 15
Código
dados |>
  distinct(obra) |>
  arrange(obra)
# A tibble: 5 × 1
  obra      
  <chr>     
1 "Bloco A" 
2 "Bloco B" 
3 "Bloco B "
4 "Bloco C" 
5 "Bloco D" 

Observa-se que a variável obra apresenta uma inconsistência de padronização, um dos registros da categoria “Bloco B” apresenta um espaço adicional ao final do texto, fazendo com que o R o reconheça como uma categoria diferente de “Bloco B”.

Já para verificar se todas as categorias da variável tipo_concreto estão escritas de forma padronizada, utilizou-se a função count().

Código
dados |>
  count(tipo_concreto) |>
  knitr::kable(
    caption = "Frequência das categorias da variável tipo_concreto",
    col.names = c("Tipo de concreto", "Frequência")
  )
Frequência das categorias da variável tipo_concreto
Tipo de concreto Frequência
C25 29
C30 31
C35 23
C40 16
c30 1

Assim, foram identificadas cinco categorias distintas na variável tipo_concreto: C25, C30, C35, C40 e c30. Observa-se que a categoria c30, registrada em uma única observação, representa a mesma classe de concreto que C30, diferenciando-se apenas pelo uso da letra minúscula. Dessa forma, identifica-se uma inconsistência na padronização dessa variável.

4.3 Limpeza da Base de Dados

Antes de iniciar a etapa de limpeza e transformação dos dados, foi criada uma cópia da base original, denominada dados_limpos, conforme apresentado no código a seguir.

Código
dados_limpos <- dados

Importante destacar que a preservação da base original é recomendável para garantir a rastreabilidade das alterações realizadas durante o processamento dos dados. Dessa forma, caso seja necessário revisar alguma decisão de limpeza ou comparar os valores tratados com os registros inicialmente importados, a base original permanece disponível e inalterada.

4.3.1 Dados Ausentes

Considerando a baixa ocorrência de dados ausentes em relação ao número total de observações da base, optou-se pela exclusão das linhas que possuem alguma variável vazia. Essa abordagem permite trabalhar com observações completas nas análises subsequentes, sem a necessidade de estimar ou imputar valores não observados. Dessa forma, utilizou-se a função drop_na(), do pacote tidyr, que exclui as linhas contendo pelo menos um valor “NA”.

Código
dados_limpos <- dados_limpos |>
  drop_na()

Para conferir o resultado aplicou-se summarise(), n() e is.na().

Código
dados_limpos |>
  summarise(
    across(everything(), ~ sum(is.na(.x)))
  ) |>
  pivot_longer(
    cols = everything(),
    names_to = "Variável",
    values_to = "Valores ausentes"
  ) |>
  knitr::kable(
    caption = "Verificação dos valores ausentes após o tratamento",
    col.names = c("Variável", "Valores ausentes")
  )
Verificação dos valores ausentes após o tratamento
Variável Valores ausentes
id_corpo_prova 0
obra 0
tipo_concreto 0
idade_dias 0
resistencia_mpa 0
cimento_kg_m3 0
relacao_a_c 0
abatimento_mm 0
densidade_kg_m3 0
absorção_agregado_pct 0

4.3.2 Variáveis Quantitativas

Após a identificação das inconsistências de digitação e formatação nas variáveis quantitativas, realizou-se o tratamento dos registros no objeto dados_limpos. As vírgulas utilizadas como separador decimal foram substituídas por pontos. Os registros contendo a letra “O” foram tratados individualmente, considerando a natureza de cada erro: em 3O,4, a letra foi substituída pelo algarismo zero, resultando em 30.4, enquanto em 390O a letra excedente foi removida, resultando em 390.

Código
dados_limpos <- dados_limpos |>
  mutate(
    resistencia_mpa = resistencia_mpa |>
      str_replace_all(",", ".") |>
      str_replace("3O.4", "30.4"),
    
    relacao_a_c = relacao_a_c |>
      str_replace_all(",", "."),
    
    cimento_kg_m3 = cimento_kg_m3 |>
      str_replace("390O", "390")
  )

dados_limpos <- dados_limpos |>
  mutate(
    resistencia_mpa = parse_double(resistencia_mpa),
    cimento_kg_m3 = parse_double(cimento_kg_m3),
    relacao_a_c = parse_double(relacao_a_c)
  )

A Tabela abaixo apresenta os registros após a correção das inconsistências de digitação e formatação identificadas nas variáveis quantitativas.

Código
dados_limpos |>
  filter(
    id_corpo_prova %in% c(
      "CP-008",
      "CP-019",
      "CP-045",
      "CP-064"
    )
  ) |>
  select(
    id_corpo_prova,
    resistencia_mpa,
    cimento_kg_m3,
    relacao_a_c
  ) |>
  knitr::kable(
    caption = "Registros após a correção das inconsistências",
    col.names = c(
      "Corpo de prova",
      "Resistência (MPa)",
      "Cimento (kg/m³)",
      "Relação a/c"
    )
  )
Registros após a correção das inconsistências
Corpo de prova Resistência (MPa) Cimento (kg/m³) Relação a/c
CP-008 38.7 347 0.54
CP-019 30.4 356 0.59
CP-045 42.9 407 0.55
CP-064 25.6 390 0.59

Logo, os registros anteriormente identificados com inconsistências de digitação e formatação foram corrigidos, resultando na padronização do separador decimal e na correção dos caracteres indevidamente inseridos nos valores.

Em seguida, aplicou-se a função summary() para realizar uma análise estatística dos dados.

Código
dados_limpos |>
  summary()
   id_corpo_prova        obra      tipo_concreto   idade_dias    
 Length   :95     Length   :95   Length   :95    Min.   :  7.00  
 N.unique :95     N.unique : 5   N.unique : 5    1st Qu.: 14.00  
 N.blank  : 0     N.blank  : 0   N.blank  : 0    Median : 28.00  
 Min.nchar: 6     Min.nchar: 7   Min.nchar: 3    Mean   : 28.44  
 Max.nchar: 6     Max.nchar: 8   Max.nchar: 3    3rd Qu.: 28.00  
                                                 Max.   :280.00  
 resistencia_mpa cimento_kg_m3    relacao_a_c     abatimento_mm   
 Min.   :21.30   Min.   :295.0   Min.   :0.4300   Min.   :  64.0  
 1st Qu.:29.30   1st Qu.:340.0   1st Qu.:0.5100   1st Qu.:  99.0  
 Median :32.70   Median :353.0   Median :0.5600   Median : 109.0  
 Mean   :33.83   Mean   :358.5   Mean   :0.5536   Mean   : 121.6  
 3rd Qu.:38.55   3rd Qu.:385.0   3rd Qu.:0.5950   3rd Qu.: 122.5  
 Max.   :47.50   Max.   :418.0   Max.   :0.6500   Max.   :1250.0  
 densidade_kg_m3 absorção_agregado_pct
 Min.   : 238    Min.   : 0.800       
 1st Qu.:2348    1st Qu.: 1.475       
 Median :2374    Median : 1.830       
 Mean   :2350    Mean   : 1.953       
 3rd Qu.:2388    3rd Qu.: 2.100       
 Max.   :2464    Max.   :18.400       

Observa-se, ainda, alguns valores suspeitos:

  • idade_dias: máximo = 280, fora do padrão de idades obsevados (7, 14, 28, 56);
  • abatimento_mm: máximo = 12.500, muito acima da média e mediana observadas;
  • densidade_kg_m3: mínimo = 238, muito abaixo da média e mediana observadas;
  • absorção_agregado_pct: máximo = 18,4, muito acima da média e mediana observadas.

Considerando que possivelmente foram erros de digitação ao preencher os dados, fez-se os devidos ajustes.

Código
dados_limpos <- dados_limpos |>
  mutate(
    idade_dias = if_else(
      id_corpo_prova == "CP-085", 28, idade_dias
    ),
    abatimento_mm = if_else(
      id_corpo_prova == "CP-032", 125, abatimento_mm
    ),
    densidade_kg_m3 = if_else(
      id_corpo_prova == "CP-053", 2380, densidade_kg_m3
    ),
    absorção_agregado_pct = if_else(
      id_corpo_prova == "CP-072", 1.84, absorção_agregado_pct
    )
  )
Código
dados_limpos |>
  filter(
    id_corpo_prova %in% c(
      "CP-032",
      "CP-053",
      "CP-072",
      "CP-085"
    )
  ) |>
  select(
    id_corpo_prova,
    idade_dias,
    abatimento_mm,
    densidade_kg_m3,
    absorção_agregado_pct
  ) |>
  knitr::kable(
    caption = "Registros após a correção dos valores inconsistentes",
    col.names = c(
      "Corpo de prova",
      "Idade (dias)",
      "Abatimento (mm)",
      "Densidade (kg/m³)",
      "Absorção (%)"
    ),
    digits = 2
  )
Registros após a correção dos valores inconsistentes
Corpo de prova Idade (dias) Abatimento (mm) Densidade (kg/m³) Absorção (%)
CP-032 14 125 2383 2.13
CP-053 28 97 2380 1.79
CP-072 14 98 2414 1.84
CP-085 28 141 2377 1.86

4.3.3 Variáveis Qualitativas

Para as variáveis qualitativas obra e tipo_concreto, foram identificadas inconsistências de padronização. Na variável obra, foi identificado um espaço adicional ao final de um dos registros da categoria “Bloco B”, enquanto, para tipo_concreto, foi identificada a categoria c30 escrita com letra minúscula. Para a correção, utilizaram-se as funções str_trim() e str_to_upper(), do pacote stringr, respectivamente.

Código
dados_limpos <- dados_limpos |>
  mutate(
    obra = str_trim(obra),
    tipo_concreto = str_to_upper(tipo_concreto)
  )

dados_limpos |>
  count(obra) |>
  knitr::kable(
    caption = "Categorias da variável obra após a padronização",
    col.names = c("Obra", "Frequência")
  )
Categorias da variável obra após a padronização
Obra Frequência
Bloco A 27
Bloco B 27
Bloco C 27
Bloco D 14
Código
dados_limpos |>
  count(tipo_concreto) |>
  knitr::kable(
    caption = "Categorias da variável tipo_concreto após a padronização",
    col.names = c("Tipo de concreto", "Frequência")
  )
Categorias da variável tipo_concreto após a padronização
Tipo de concreto Frequência
C25 26
C30 32
C35 21
C40 16

Dessa forma, ambas as variáveis qualitativas passaram a apresentar categorias consistentes e adequadamente padronizadas.

Após a realização das etapas de tratamento e correção das inconsistências identificadas, obteve-se a base de dados limpa, cuja estrutura e estatísticas descritivas são apresentadas a seguir.

Código
dados_limpos |>
  glimpse()
Rows: 95
Columns: 10
$ id_corpo_prova        <chr> "CP-001", "CP-002", "CP-003", "CP-004", "CP-005"…
$ obra                  <chr> "Bloco A", "Bloco A", "Bloco C", "Bloco A", "Blo…
$ tipo_concreto         <chr> "C35", "C30", "C30", "C40", "C25", "C40", "C25",…
$ idade_dias            <dbl> 28, 7, 56, 28, 28, 28, 56, 28, 14, 7, 7, 28, 28,…
$ resistencia_mpa       <dbl> 36.3, 28.4, 36.1, 47.2, 27.9, 41.5, 23.7, 38.7, …
$ cimento_kg_m3         <dbl> 395, 340, 338, 407, 316, 398, 351, 347, 346, 315…
$ relacao_a_c           <dbl> 0.50, 0.54, 0.56, 0.47, 0.61, 0.43, 0.65, 0.54, …
$ abatimento_mm         <dbl> 111, 114, 106, 142, 133, 98, 99, 116, 125, 127, …
$ densidade_kg_m3       <dbl> 2332, 2389, 2427, 2345, 2448, 2324, 2385, 2387, …
$ absorção_agregado_pct <dbl> 2.30, 1.85, 1.89, 1.97, 2.31, 0.80, 1.18, 1.33, …
Código
dados_limpos |>
  summary()
   id_corpo_prova        obra      tipo_concreto   idade_dias   
 Length   :95     Length   :95   Length   :95    Min.   : 7.00  
 N.unique :95     N.unique : 4   N.unique : 4    1st Qu.:14.00  
 N.blank  : 0     N.blank  : 0   N.blank  : 0    Median :28.00  
 Min.nchar: 6     Min.nchar: 7   Min.nchar: 3    Mean   :25.79  
 Max.nchar: 6     Max.nchar: 7   Max.nchar: 3    3rd Qu.:28.00  
                                                 Max.   :56.00  
 resistencia_mpa cimento_kg_m3    relacao_a_c     abatimento_mm  
 Min.   :21.30   Min.   :295.0   Min.   :0.4300   Min.   : 64.0  
 1st Qu.:29.30   1st Qu.:340.0   1st Qu.:0.5100   1st Qu.: 99.0  
 Median :32.70   Median :353.0   Median :0.5600   Median :109.0  
 Mean   :33.83   Mean   :358.5   Mean   :0.5536   Mean   :109.7  
 3rd Qu.:38.55   3rd Qu.:385.0   3rd Qu.:0.5950   3rd Qu.:122.5  
 Max.   :47.50   Max.   :418.0   Max.   :0.6500   Max.   :175.0  
 densidade_kg_m3 absorção_agregado_pct
 Min.   :2293    Min.   :0.800        
 1st Qu.:2348    1st Qu.:1.475        
 Median :2374    Median :1.830        
 Mean   :2373    Mean   :1.779        
 3rd Qu.:2388    3rd Qu.:2.095        
 Max.   :2464    Max.   :2.920        

Após o processo de limpeza, a base passou a apresentar 95 observações e 10 variáveis. As variáveis quantitativas foram adequadamente convertidas para o tipo numérico (<dbl>), permitindo a obtenção de suas estatísticas descritivas. Além disso, foram corrigidas as inconsistências de digitação e formatação previamente identificadas, bem como a padronização da variável qualitativa tipo_concreto. Os registros contendo valores ausentes foram removidos, reduzindo a base original de 100 para 95 observações.

A análise do resumo da base após o tratamento também demonstra a correção dos valores considerados inconsistentes. A idade máxima dos corpos de prova passou a ser 56 dias, o abatimento máximo 175 mm, a densidade passou a apresentar valores compatíveis com a ordem de grandeza dos demais registros e a absorção máxima foi reduzida após a correção do valor discrepante. Dessa forma, a base dados_limpos encontra-se estruturada para realização das análises estatísticas.

4.4 Processamento dos Dados

4.4.1 Resistência Média

Com o objetivo de avaliar o comportamento da resistência à compressão dos corpos de prova, inicialmente foi calculada a resistência média considerando o conjunto total de observações. Posteriormente, os dados foram agrupados por bloco da obra, tipo de concreto e idade do corpo de prova, utilizando-se as funções group_by() e summarise() do pacote dplyr.

Código
dados_limpos |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  knitr::kable(
    caption = "Resistência média à compressão dos corpos de prova geral",
    col.names = "Resistência média (MPa)",
    digits = 2
  )
Resistência média à compressão dos corpos de prova geral
Resistência média (MPa)
33.83
Código
dados_limpos |>
  group_by(obra) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  knitr::kable(
    caption = "Resistência média à compressão por bloco da obra",
    col.names = c("Obra", "Resistência média (MPa)"),
    digits = 2
  )
Resistência média à compressão por bloco da obra
Obra Resistência média (MPa)
Bloco A 33.43
Bloco B 35.68
Bloco C 32.62
Bloco D 33.35
Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  knitr::kable(
    caption = "Resistência média à compressão por tipo de concreto",
    col.names = c("Tipo de concreto", "Resistência média (MPa)"),
    digits = 2
  )
Resistência média à compressão por tipo de concreto
Tipo de concreto Resistência média (MPa)
C25 27.45
C30 32.08
C35 37.14
C40 43.33
Código
dados_limpos |>
  group_by(idade_dias) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  arrange(idade_dias) |>
  knitr::kable(
    caption = "Resistência média à compressão por idade do corpo de prova",
    col.names = c("Idade (dias)", "Resistência média (MPa)"),
    digits = 2
  )
Resistência média à compressão por idade do corpo de prova
Idade (dias) Resistência média (MPa)
7 28.78
14 35.83
28 34.96
56 33.12

Para comparar os blocos da obra quanto à resistência à compressão, calculou-se a resistência média dos corpos de prova para cada bloco por meio das funções group_by() e summarise(). Posteriormente, os resultados foram ordenados de forma decrescente utilizando arrange(), permitindo identificar os blocos que apresentaram a maior e a menor resistência média.

Código
dados_limpos |>
  group_by(obra) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  arrange(desc(resistencia_media)) |>
  knitr::kable(
    caption = "Resistência média à compressão por bloco da obra",
    col.names = c(
      "Obra",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média à compressão por bloco da obra
Obra Resistência média (MPa)
Bloco B 35.68
Bloco A 33.43
Bloco D 33.35
Bloco C 32.62

Conforme apresentado na tabela, o Bloco B apresentou a maior resistência média à compressão, com 35,68 MPa, enquanto o Bloco C apresentou a menor resistência média, com 32,62 MPa. Os Blocos A e D apresentaram valores intermediários, de 33,43 MPa e 33,35 MPa.

Para avaliar a resistência à compressão em função da classe do concreto, calculou-se a resistência média dos corpos de prova para cada categoria de tipo_concreto. Os dados foram agrupados por classe e, posteriormente, ordenados de forma decrescente de acordo com a resistência média obtida.

Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  arrange(desc(resistencia_media)) |>
  knitr::kable(
    caption = "Resistência média à compressão por tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média à compressão por tipo de concreto
Tipo de concreto Resistência média (MPa)
C40 43.33
C35 37.14
C30 32.08
C25 27.45

Conforme apresentado na tabela, a classe C40 apresentou a maior resistência média à compressão, com 43,33 MPa, seguida pelas classes C35, C30 e C25, com resistências médias de 37,14 MPa, 32,08 MPa e 27,45 MPa, respectivamente. Observa-se, portanto, um aumento da resistência média à compressão conforme aumenta a classe de resistência do concreto, comportamento compatível com a classificação adotada para os diferentes tipos de concreto.

Para comparar as características médias das diferentes classes de concreto, os dados foram agrupados por tipo_concreto utilizando group_by(). Em seguida, utilizou-se summarise() em conjunto com across() para calcular simultaneamente os valores médios de resistência à compressão, consumo de cimento, relação água/cimento, abatimento e densidade.

Código
medias_tipo_concreto <- dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    across(
      c(
        resistencia_mpa,
        cimento_kg_m3,
        relacao_a_c,
        abatimento_mm,
        densidade_kg_m3
      ),
      mean
    ),
    .groups = "drop"
  )

medias_tipo_concreto |>
  knitr::kable(
    caption = "Valores médios das variáveis por tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "Resistência (MPa)",
      "Cimento (kg/m³)",
      "Relação a/c",
      "Abatimento (mm)",
      "Densidade (kg/m³)"
    ),
    digits = 2
  )
Valores médios das variáveis por tipo de concreto
Tipo de concreto Resistência (MPa) Cimento (kg/m³) Relação a/c Abatimento (mm) Densidade (kg/m³)
C25 27.45 322.35 0.62 113.58 2374.00
C30 32.08 351.44 0.57 110.72 2383.47
C35 37.14 379.24 0.51 107.14 2363.29
C40 43.33 404.12 0.48 104.94 2362.50

Os resultados mostram que o aumento da classe do concreto está associado ao aumento da resistência média à compressão e do consumo médio de cimento. A resistência média aumentou de 27,45 MPa, para o concreto C25, para 43,33 MPa, no C40, enquanto o consumo médio de cimento passou de 322,35 kg/m³ para 404,12 kg/m³.

Em relação à razão água/cimento, observa-se comportamento inverso, com redução de 0,62 no C25 para 0,48 no C40. O abatimento médio também apresentou redução gradual entre as classes, variando de 113,58 mm no C25 a 104,94 mm no C40. Por outro lado, a densidade apresentou valores relativamente próximos entre as classes, variando entre 2.362,50 e 2.383,47 kg/m³. De modo geral, os resultados evidenciam diferenças nas características médias dos concretos em função de suas respectivas classes.

4.4.2 Resistência Relativa

Para o cálculo da resistência relativa, adotou-se como resistência de referência o valor correspondente à classe de resistência de cada concreto, sendo 25 MPa para C25, 30 MPa para C30, 35 MPa para C35 e 40 MPa para C40. A variável resistencia_referencia foi definida utilizando-se a função case_when(), enquanto a variável resistencia_relativa foi obtida pela razão entre a resistência à compressão observada e a respectiva resistência de referência.

Código
dados_limpos <- dados_limpos |>
  mutate(
    resistencia_referencia = case_when(
      tipo_concreto == "C25" ~ 25,
      tipo_concreto == "C30" ~ 30,
      tipo_concreto == "C35" ~ 35,
      tipo_concreto == "C40" ~ 40
    ),
    resistencia_relativa = resistencia_mpa / resistencia_referencia
  )

dados_limpos <- dados_limpos |>
  select(-resistencia_referencia)

dados_limpos |>
  select(
    id_corpo_prova,
    tipo_concreto,
    resistencia_mpa,
    resistencia_relativa
  ) |>
  slice_head(n = 10) |>
  knitr::kable(
    caption = "Resistência relativa dos corpos de prova",
    col.names = c(
      "Corpo de prova",
      "Tipo de concreto",
      "Resistência (MPa)",
      "Resistência relativa"
    ),
    digits = 2
  )
Resistência relativa dos corpos de prova
Corpo de prova Tipo de concreto Resistência (MPa) Resistência relativa
CP-001 C35 36.3 1.04
CP-002 C30 28.4 0.95
CP-003 C30 36.1 1.20
CP-004 C40 47.2 1.18
CP-005 C25 27.9 1.12
CP-006 C40 41.5 1.04
CP-007 C25 23.7 0.95
CP-008 C30 38.7 1.29
CP-009 C30 30.3 1.01
CP-010 C25 25.3 1.01

A resistência relativa permite comparar a resistência observada com a resistência de referência de cada classe de concreto. Valores superiores a 1,00 indicam resistência acima da referência, enquanto valores inferiores a 1,00 indicam resistência abaixo da referência.

4.4.3 Classificação quanto a Resistência

Para a classificação dos corpos de prova, utilizou-se como critério a resistência relativa calculada anteriormente. Os corpos de prova com resistência relativa inferior a 1 foram classificados como “Abaixo da referência”, enquanto aqueles com valor igual ou superior a 1 foram classificados como “Atende ou supera a referência”. Esse critério permite avaliar a resistência observada em relação ao valor de referência adotado para cada classe de concreto.

Código
dados_limpos <- dados_limpos |>
  mutate(
    classificacao = case_when(
      resistencia_relativa < 1 ~ "Abaixo da referência",
      resistencia_relativa >= 1 ~ "Atende ou supera a referência"
    )
  )

dados_limpos |>
  count(classificacao) |>
  knitr::kable(
    caption = "Classificação dos corpos de prova quanto à resistência",
    col.names = c("Classificação", "Frequência")
  )
Classificação dos corpos de prova quanto à resistência
Classificação Frequência
Abaixo da referência 23
Atende ou supera a referência 72

Com base nos resultados, dos 95 corpos de prova analisados, 72 atenderam ou superaram a resistência de referência, enquanto 23 apresentaram resistência abaixo da referência. Dessa forma, observa-se predominância de corpos de prova com resistência igual ou superior ao valor adotado como referência para sua respectiva classe de concreto.

Para verificar se a resistência de cada corpo de prova se encontra acima da média de sua respectiva classe de concreto, os dados foram agrupados por tipo_concreto. Em seguida, utilizou-se mutate() para criar a variável lógica acima_media, que assume TRUE quando a resistência observada é superior à média do grupo e FALSE caso contrário.

Código
dados_limpos <- dados_limpos |>
  group_by(tipo_concreto) |>
  mutate(
    acima_media = resistencia_mpa > mean(resistencia_mpa)
  ) |>
  ungroup()

dados_limpos |>
  count(tipo_concreto, acima_media) |>
  knitr::kable(
    caption = "Classificação dos corpos de prova em relação à média de sua classe",
    col.names = c(
      "Tipo de concreto",
      "Acima da média",
      "Frequência"
    )
  )
Classificação dos corpos de prova em relação à média de sua classe
Tipo de concreto Acima da média Frequência
C25 FALSE 10
C25 TRUE 16
C30 FALSE 17
C30 TRUE 15
C35 FALSE 12
C35 TRUE 9
C40 FALSE 8
C40 TRUE 8

A variável acima_media permitiu identificar a distribuição dos corpos de prova em relação à resistência média de cada classe de concreto. Para as classes C25, C30, C35 e C40, foram identificados, respectivamente, 16, 15, 9 e 8 corpos de prova com resistência superior à média de sua respectiva classe.

4.5 Agrupamento dos Dados

Para verificar a distribuição das observações entre as diferentes classes de concreto, os dados foram agrupados pela variável tipo_concreto. Em seguida, utilizou-se summarise() em conjunto com n() para determinar a quantidade de corpos de prova pertencentes a cada classe.

Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    quantidade = n()
  ) |>
  knitr::kable(
    caption = "Quantidade de observações por tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "Quantidade de observações"
    )
  )
Quantidade de observações por tipo de concreto
Tipo de concreto Quantidade de observações
C25 26
C30 32
C35 21
C40 16

A distribuição dos corpos de prova entre as classes de concreto não é uniforme. A classe C30 apresentou o maior número de observações, com 32 registros, seguida pelas classes C25, com 26, C35, com 21, e C40, com 16 observações.

Para comparar a resistência média entre as diferentes classes de concreto, os dados foram agrupados por tipo_concreto e resumidos por meio das funções group_by() e summarise(). Posteriormente, utilizou-se arrange() para ordenar os resultados de forma decrescente em relação à resistência média.

Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    resistencia_media = mean(resistencia_mpa)
  ) |>
  arrange(desc(resistencia_media)) |>
  knitr::kable(
    caption = "Resistência média por tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média por tipo de concreto
Tipo de concreto Resistência média (MPa)
C40 43.33
C35 37.14
C30 32.08
C25 27.45

Assim como visto anteriormente, a classe C40 apresenta a maior resistência média, seguida pelas classes C35, C30 e C25, com 43,33 MPa, 37,14 MPa, 32,08 MPa e 27,45 MPa, respectivamente.

Para caracterizar a distribuição da resistência à compressão em cada classe de concreto, foram calculados o número de observações, a média, a mediana, o desvio-padrão e os valores mínimo e máximo. Para isso, os dados foram agrupados por tipo_concreto e as estatísticas foram obtidas simultaneamente por meio da função summarise().

Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    quantidade = n(),
    media = mean(resistencia_mpa),
    mediana = median(resistencia_mpa),
    desvio_padrao = sd(resistencia_mpa),
    minimo = min(resistencia_mpa),
    maximo = max(resistencia_mpa)
  ) |>
  knitr::kable(
    caption = "Estatísticas da resistência por tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "N",
      "Média (MPa)",
      "Mediana (MPa)",
      "Desvio-padrão (MPa)",
      "Mínimo (MPa)",
      "Máximo (MPa)"
    ),
    digits = 2
  )
Estatísticas da resistência por tipo de concreto
Tipo de concreto N Média (MPa) Mediana (MPa) Desvio-padrão (MPa) Mínimo (MPa) Máximo (MPa)
C25 26 27.45 27.95 2.77 21.3 31.6
C30 32 32.08 31.80 3.57 24.5 39.4
C35 21 37.14 36.90 3.77 29.9 44.6
C40 16 43.33 43.15 3.08 37.3 47.5

Observa-se que as médias e medianas apresentam valores próximos em todas as classes de concreto, indicando pouca diferença entre essas medidas de tendência central. Os desvios-padrão variaram entre 2,77 MPa e 3,77 MPa, sendo a classe C35 a que apresentou maior dispersão dos valores de resistência e a classe C25 a menor. Além disso, verifica-se o aumento dos valores de resistência à compressão, incluindo mínimos e máximos, conforme aumenta a classe do concreto.

Complementarmente, uma tabela com várias estatísticas é mais informativa do que a apresentação apenas da média, pois permite avaliar não somente o valor central dos dados, mas também sua dispersão e amplitude. A mediana possibilita comparar a tendência central com a média, o desvio-padrão indica a variabilidade dos resultados e os valores mínimo e máximo mostram os extremos observados. Dessa forma, é possível obter uma caracterização mais completa da resistência à compressão em cada classe de concreto.

4.6 Selecionando e Reorganizando Variáveis

Para a continuidade das análises, foram selecionadas somente as variáveis quantitativas relacionadas às propriedades do concreto. Para isso, utilizou-se a função select() em conjunto com all_of(), permitindo selecionar as variáveis previamente definidas como de interesse.

Código
variaveis_quantitativas <- c(
  "idade_dias",
  "resistencia_mpa",
  "cimento_kg_m3",
  "relacao_a_c",
  "abatimento_mm",
  "densidade_kg_m3",
  "absorção_agregado_pct"
)

dados_quantitativos <- dados_limpos |>
  select(all_of(variaveis_quantitativas))

A tabela a seguir apresenta as primeiras observações da nova base formada exclusivamente pelas variáveis quantitativas selecionadas.

Código
dados_quantitativos |>
  slice_head(n = 10) |>
  knitr::kable(
    caption = "Variáveis quantitativas relacionadas às propriedades do concreto",
    col.names = c(
      "Idade (dias)",
      "Resistência (MPa)",
      "Cimento (kg/m³)",
      "Relação a/c",
      "Abatimento (mm)",
      "Densidade (kg/m³)",
      "Absorção (%)"
    ),
    digits = 2
  )
Variáveis quantitativas relacionadas às propriedades do concreto
Idade (dias) Resistência (MPa) Cimento (kg/m³) Relação a/c Abatimento (mm) Densidade (kg/m³) Absorção (%)
28 36.3 395 0.50 111 2332 2.30
7 28.4 340 0.54 114 2389 1.85
56 36.1 338 0.56 106 2427 1.89
28 47.2 407 0.47 142 2345 1.97
28 27.9 316 0.61 133 2448 2.31
28 41.5 398 0.43 98 2324 0.80
56 23.7 351 0.65 99 2385 1.18
28 38.7 347 0.54 116 2387 1.33
14 30.3 346 0.54 125 2328 2.92
7 25.3 315 0.60 127 2413 1.94

Para calcular simultaneamente a média das variáveis quantitativas selecionadas, utilizou-se a função across() em conjunto com summarise(). Essa abordagem permite aplicar a mesma função a diversas variáveis sem a necessidade de especificar individualmente o cálculo para cada coluna.

Código
dados_quantitativos |>
  summarise(
    across(
      everything(),
      mean
    )
  ) |>
  pivot_longer(
    everything(),
    names_to = "Variável",
    values_to = "Média"
  ) |>
  knitr::kable(
    caption = "Média das variáveis quantitativas",
    col.names = c("Variável", "Média"),
    digits = 2
  )
Média das variáveis quantitativas
Variável Média
idade_dias 25.79
resistencia_mpa 33.83
cimento_kg_m3 358.49
relacao_a_c 0.55
abatimento_mm 109.74
densidade_kg_m3 2372.88
absorção_agregado_pct 1.78

Em comparação com o uso individual de summarise(), a utilização de across() torna o código mais compacto e facilita sua manutenção, principalmente quando há um grande número de variáveis. Caso novas variáveis sejam incluídas na seleção, a mesma operação pode ser aplicada a elas sem a necessidade de acrescentar manualmente novos cálculos. Dessa forma, across() apresenta maior praticidade e facilidade de manutenção para esse tipo de análise.

Para realizar uma transformação simultânea em diferentes variáveis quantitativas, utilizou-se mutate() em conjunto com across(). Os valores de resistência, consumo de cimento, abatimento e densidade foram divididos pelas respectivas médias, gerando variáveis relativas. Essa transformação permite comparar valores que apresentam diferentes unidades e ordens de grandeza em uma escala comum, na qual valores iguais a 1 correspondem à média de cada variável.

Código
dados_transformados <- dados_limpos |>
  mutate(
    across(
      c(
        resistencia_mpa,
        cimento_kg_m3,
        abatimento_mm,
        densidade_kg_m3
      ),
      ~ .x / mean(.x),
      .names = "{.col}_relativo"
    )
  )

dados_transformados |>
  select(
    id_corpo_prova,
    ends_with("_relativo")
  ) |>
  slice_head(n = 10) |>
  knitr::kable(
    caption = "Variáveis quantitativas transformadas em relação às respectivas médias",
    digits = 2
  )
Variáveis quantitativas transformadas em relação às respectivas médias
id_corpo_prova resistencia_mpa_relativo cimento_kg_m3_relativo abatimento_mm_relativo densidade_kg_m3_relativo
CP-001 1.07 1.10 1.01 0.98
CP-002 0.84 0.95 1.04 1.01
CP-003 1.07 0.94 0.97 1.02
CP-004 1.40 1.14 1.29 0.99
CP-005 0.82 0.88 1.21 1.03
CP-006 1.23 1.11 0.89 0.98
CP-007 0.70 0.98 0.90 1.01
CP-008 1.14 0.97 1.06 1.01
CP-009 0.90 0.97 1.14 0.98
CP-010 0.75 0.88 1.16 1.02

A transformação permitiu expressar os valores das diferentes propriedades em relação às respectivas médias. Dessa forma, valores superiores a 1 indicam resultados acima da média da variável analisada, enquanto valores inferiores a 1 representam resultados abaixo da média. Essa padronização facilita a comparação entre propriedades que possuem diferentes unidades e ordens de grandeza.

Na sequência, foram calculadas simultaneamente a média e o desvio-padrão das variáveis quantitativas para cada tipo de concreto. Para isso, utilizou-se group_by() para o agrupamento das observações e summarise() em conjunto com across(), permitindo aplicar as mesmas funções estatísticas a diferentes variáveis.

Código
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    across(
      c(
        idade_dias,
        resistencia_mpa,
        cimento_kg_m3,
        relacao_a_c,
        abatimento_mm,
        densidade_kg_m3,
        absorção_agregado_pct
      ),
      list(
        media = mean,
        desvio_padrao = sd
      ),
      .names = "{.col}_{.fn}"
    )
  ) |>
  pivot_longer(
    cols = -tipo_concreto,
    names_to = c("Variavel", ".value"),
    names_pattern = "(.*)_(media|desvio_padrao)"
  ) |>
  knitr::kable(
    caption = "Média e desvio-padrão das variáveis quantitativas por tipo de concreto",
    digits = 2
  )
Média e desvio-padrão das variáveis quantitativas por tipo de concreto
tipo_concreto Variavel media desvio_padrao
C25 idade_dias 27.73 14.88
C25 resistencia_mpa 27.45 2.77
C25 cimento_kg_m3 322.35 14.84
C25 relacao_a_c 0.62 0.02
C25 abatimento_mm 113.58 21.58
C25 densidade_kg_m3 2374.00 31.90
C25 absorção_agregado_pct 1.92 0.35
C30 idade_dias 25.81 15.95
C30 resistencia_mpa 32.08 3.57
C30 cimento_kg_m3 351.44 12.24
C30 relacao_a_c 0.57 0.02
C30 abatimento_mm 110.72 16.55
C30 densidade_kg_m3 2383.47 38.26
C30 absorção_agregado_pct 1.63 0.52
C35 idade_dias 25.33 12.81
C35 resistencia_mpa 37.14 3.77
C35 cimento_kg_m3 379.24 12.41
C35 relacao_a_c 0.51 0.03
C35 abatimento_mm 107.14 20.48
C35 densidade_kg_m3 2363.29 33.09
C35 absorção_agregado_pct 1.85 0.43
C40 idade_dias 23.19 7.55
C40 resistencia_mpa 43.33 3.08
C40 cimento_kg_m3 404.12 6.67
C40 relacao_a_c 0.48 0.03
C40 abatimento_mm 104.94 21.55
C40 densidade_kg_m3 2362.50 28.02
C40 absorção_agregado_pct 1.75 0.41

Em comparação com a utilização de vários comandos separados, o uso de across() torna o código mais compacto e facilita sua manutenção, pois permite aplicar simultaneamente as mesmas funções a um conjunto de variáveis. Dessa forma, caso seja necessário incluir novas variáveis ou estatísticas na análise, a alteração pode ser realizada em um único trecho do código, reduzindo repetições e tornando a análise mais organizada.

4.7 Textos e Categorias

Na sequência, foram avaliados os valores da variável obra com o objetivo de identificar possíveis inconsistências textuais. Para a padronização, utilizaram-se funções do pacote stringr, removendo espaços excedentes no início ou no final dos textos e uniformizando o uso de letras maiúsculas e minúsculas.

Código
dados_limpos <- dados_limpos |>
  mutate(
    obra = str_trim(obra),
    obra = str_to_title(obra)
  )

dados |>
  transmute(
    Antes = obra,
    Depois = str_to_title(str_trim(obra))
  ) |>
  distinct()
# A tibble: 5 × 2
  Antes      Depois 
  <chr>      <chr>  
1 "Bloco A"  Bloco A
2 "Bloco C"  Bloco C
3 "Bloco B"  Bloco B
4 "Bloco D"  Bloco D
5 "Bloco B " Bloco B

Após a padronização, os valores da variável obra passaram a apresentar uma estrutura textual uniforme, reduzindo o risco de que diferenças de formatação sejam interpretadas como categorias distintas durante as análises.

Na sequência, utilizou-se a função str_detect() para localizar os registros que apresentam o padrão textual “Bloco B” na variável obra. A função identifica a presença do padrão especificado e, em conjunto com filter(), permite selecionar apenas as observações correspondentes.

Código
dados_limpos |>
  filter(
    str_detect(obra, "Bloco B")
  ) |>
  select(
    id_corpo_prova,
    obra,
    tipo_concreto,
    resistencia_mpa
  ) |>
  knitr::kable(
    caption = "Registros identificados pelo padrão textual 'Bloco B'",
    col.names = c(
      "Corpo de prova",
      "Obra",
      "Tipo de concreto",
      "Resistência (MPa)"
    ),
    digits = 2
  )
Registros identificados pelo padrão textual ‘Bloco B’
Corpo de prova Obra Tipo de concreto Resistência (MPa)
CP-005 Bloco B C25 27.9
CP-006 Bloco B C40 41.5
CP-009 Bloco B C30 30.3
CP-013 Bloco B C35 35.6
CP-014 Bloco B C35 39.4
CP-017 Bloco B C35 38.4
CP-018 Bloco B C30 34.7
CP-019 Bloco B C30 30.4
CP-020 Bloco B C40 41.7
CP-027 Bloco B C30 39.4
CP-030 Bloco B C35 44.6
CP-035 Bloco B C35 41.1
CP-037 Bloco B C30 36.6
CP-040 Bloco B C25 29.4
CP-042 Bloco B C30 32.3
CP-047 Bloco B C25 26.2
CP-048 Bloco B C30 32.1
CP-056 Bloco B C35 37.9
CP-057 Bloco B C30 33.5
CP-059 Bloco B C30 29.9
CP-063 Bloco B C35 36.2
CP-072 Bloco B C40 37.3
CP-074 Bloco B C25 27.8
CP-080 Bloco B C35 35.1
CP-081 Bloco B C40 47.5
CP-088 Bloco B C35 36.9
CP-091 Bloco B C35 39.6

Além disso, a utilização de padrões de texto auxilia no diagnóstico de bases de dados, permitindo localizar categorias específicas e identificar possíveis inconsistências de preenchimento, como diferenças de grafia, uso de letras maiúsculas e minúsculas ou caracteres indesejados. Essa verificação contribui para a padronização e maior consistência dos dados antes das análises.

4.8 Visualização dos Dados

  • Distribuição da resistência à compressão entre as diferentes classes de concreto

Para comparar a distribuição da resistência à compressão entre as diferentes classes de concreto, foi elaborado um gráfico do tipo boxplot. Essa representação permite visualizar a posição central dos dados, sua dispersão e possíveis valores extremos em cada grupo.

Código
ggplot(
  dados_limpos,
  aes(
    x = tipo_concreto,
    y = resistencia_mpa
  )
) +
  geom_boxplot() +
  labs(
    title = "Distribuição da resistência à compressão por tipo de concreto",
    x = "Tipo de concreto",
    y = "Resistência à compressão (MPa)"
  ) +
  theme_minimal()

A partir do gráfico, observa-se um aumento progressivo dos valores de resistência à compressão conforme aumenta a classe do concreto. A classe C25 apresenta os menores valores, seguida pelas classes C30, C35 e C40. Além disso, as medianas acompanham essa tendência crescente, indicando que concretos de classes superiores apresentam, de forma geral, maiores resistências. A dispersão dos resultados apresenta variações entre as classes, sendo possível avaliar pelo boxplot a amplitude e a concentração dos valores em cada grupo.

  • Relação entre o consumo de cimento e a resistência à compressão do concreto

Para investigar a relação entre o consumo de cimento e a resistência à compressão do concreto, foi elaborado um gráfico de dispersão. Cada ponto representa uma observação da base de dados, enquanto a linha de tendência permite visualizar o comportamento geral da relação entre as variáveis.

Código
ggplot(
  dados_limpos,
  aes(
    x = cimento_kg_m3,
    y = resistencia_mpa
  )
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    se = FALSE
  ) +
  labs(
    title = "Relação entre consumo de cimento e resistência à compressão",
    x = "Consumo de cimento (kg/m³)",
    y = "Resistência à compressão (MPa)"
  ) +
  theme_minimal()

A partir do gráfico, observa-se uma tendência crescente entre o consumo de cimento e a resistência à compressão, indicando que maiores consumos de cimento estão, de forma geral, associados a maiores valores de resistência. A inclinação positiva da linha de tendência fornece evidência visual compatível com a afirmação de que, à medida que aumenta o consumo de cimento, a resistência do concreto tende a aumentar. No entanto, os pontos não se encontram perfeitamente alinhados, indicando que a resistência também pode ser influenciada por outras características da mistura, além do consumo de cimento.

  • Relação entre a relação água/cimento e a resistência à compressão

Para investigar a relação entre a relação água/cimento e a resistência à compressão, foi elaborado um gráfico de dispersão. Também foi adicionada uma linha de tendência linear para facilitar a identificação do comportamento geral entre as duas variáveis.

Código
ggplot(
  dados_limpos,
  aes(
    x = relacao_a_c,
    y = resistencia_mpa
  )
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    se = FALSE
  ) +
  labs(
    title = "Relação entre água/cimento e resistência à compressão",
    x = "Relação água/cimento",
    y = "Resistência à compressão (MPa)"
  ) +
  theme_minimal()

A partir do gráfico, observa-se uma tendência decrescente entre a relação água/cimento e a resistência à compressão, indicando que menores valores da relação a/c tendem a estar associados a maiores valores de resistência. A inclinação negativa da linha de tendência reforça visualmente esse comportamento. Entretanto, verifica-se dispersão dos pontos ao redor da linha, indicando que a resistência não depende exclusivamente da relação água/cimento.

4.9 Questões relacionadas à prática profissional

Após as etapas de inspeção, tratamento e exploração da base de dados, as informações obtidas podem ser utilizadas na análise de situações mais próximas da prática profissional da Engenharia Civil. Nesta etapa, busca-se interpretar os dados do controle tecnológico do concreto e verificar se eles fornecem suporte a determinadas afirmações relacionadas ao desempenho dos materiais empregados na obra.

Uma das questões levantadas refere-se ao desempenho dos concretos utilizados nos diferentes blocos da obra, mais especificamente à afirmação de que os concretos empregados no Bloco C apresentam desempenho superior aos utilizados nos demais blocos.

Para realizar uma primeira avaliação dessa afirmação, será calculada a resistência média à compressão dos corpos de prova de cada bloco.

Código
resumo_obras <- dados_limpos |>
  group_by(obra) |>
  summarise(
    resistencia_media = mean(resistencia_mpa),
    .groups = "drop"
  ) |>
  arrange(desc(resistencia_media))

resumo_obras |>
  knitr::kable(
    caption = "Resistência média à compressão por bloco da obra",
    col.names = c(
      "Bloco",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média à compressão por bloco da obra
Bloco Resistência média (MPa)
Bloco B 35.68
Bloco A 33.43
Bloco D 33.35
Bloco C 32.62
Código
ggplot(
  resumo_obras,
  aes(
    x = obra,
    y = resistencia_media
  )
) +
  geom_col() +
  labs(
    title = "Resistência média à compressão por bloco",
    x = "Bloco da obra",
    y = "Resistência média (MPa)"
  ) +
  theme_minimal()

Entretanto, como a base apresenta diferentes classes de concreto, a análise também será realizada considerando separadamente cada tipo de concreto, evitando que diferenças na composição dos grupos influenciem diretamente a comparação entre os blocos.

Código
resumo_obra_concreto <- dados_limpos |>
  group_by(obra, tipo_concreto) |>
  summarise(
    resistencia_media = mean(resistencia_mpa),
    .groups = "drop"
  )

resumo_obra_concreto |>
  knitr::kable(
    caption = "Resistência média por bloco e tipo de concreto",
    col.names = c(
      "Bloco",
      "Tipo de concreto",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média por bloco e tipo de concreto
Bloco Tipo de concreto Resistência média (MPa)
Bloco A C25 28.28
Bloco A C30 30.53
Bloco A C35 35.78
Bloco A C40 42.58
Bloco B C25 27.82
Bloco B C30 33.24
Bloco B C35 38.48
Bloco B C40 42.00
Bloco C C25 27.03
Bloco C C30 32.02
Bloco C C35 35.10
Bloco C C40 44.98
Bloco D C25 26.15
Bloco D C30 32.28
Bloco D C35 37.00
Bloco D C40 44.95
Código
ggplot(
  resumo_obra_concreto,
  aes(
    x = tipo_concreto,
    y = resistencia_media,
    group = obra,
    linetype = obra
  )
) +
  geom_line() +
  geom_point() +
  labs(
    title = "Resistência média por tipo de concreto e bloco",
    x = "Tipo de concreto",
    y = "Resistência média (MPa)",
    linetype = "Bloco"
  ) +
  theme_minimal()

A comparação inicial entre os blocos mostra que o Bloco C não apresenta a maior resistência média à compressão. Sua média geral foi de 32,62 MPa, sendo a menor entre os quatro blocos analisados. O Bloco B apresentou a maior média, com aproximadamente 35,68 MPa, enquanto os Blocos A e D apresentaram valores intermediários.

Entretanto, como os blocos apresentam diferentes proporções de classes de concreto, também foi realizada uma comparação considerando separadamente os tipos de concreto, de forma a detalhar melhor a distribuição das médias por resistência dentro de cada bloco.

Considerando os resultados obtidos, não há suporte descritivo suficiente para afirmar que os concretos utilizados no Bloco C apresentam desempenho superior aos utilizados nos demais blocos. A superioridade do Bloco C é observada apenas para o concreto C40.

A idade do corpo de prova também é uma variável relevante na avaliação da resistência do concreto, uma vez que o material tende a desenvolver resistência ao longo do tempo em função da continuidade das reações de hidratação do cimento.

Para verificar esse comportamento na base analisada, será calculada a resistência média à compressão para cada idade dos corpos de prova. A comparação entre esses valores permitirá identificar se existe uma tendência de aumento da resistência com o avanço da idade.

Código
media_por_idade <- dados_limpos |>
  group_by(idade_dias) |>
  summarise(
    resistencia_media = mean(resistencia_mpa),
    .groups = "drop"
  ) |>
  arrange(idade_dias)

media_por_idade |>
  knitr::kable(
    caption = "Resistência média à compressão por idade",
    col.names = c(
      "Idade (dias)",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média à compressão por idade
Idade (dias) Resistência média (MPa)
7 28.78
14 35.83
28 34.96
56 33.12
Código
ggplot(
  media_por_idade,
  aes(
    x = idade_dias,
    y = resistencia_media
  )
) +
  geom_line() +
  geom_point() +
  labs(
    title = "Resistência média à compressão em função da idade",
    x = "Idade (dias)",
    y = "Resistência média (MPa)"
  ) +
  theme_minimal()

Observa-se um aumento expressivo da resistência média entre 7 e 14 dias, passando de aproximadamente 28,78 MPa para 35,83 MPa. Entretanto, nas idades seguintes ocorre uma pequena redução, com médias de 34,96 MPa aos 28 dias e 33,12 MPa aos 56 dias.

Dessa forma, considerando apenas as médias gerais por idade, os dados não apresentam um crescimento contínuo da resistência ao longo do tempo. Porém, avaliando este resultado de forma técnica, deve-se levar em consideração que as classes de concreto existentes na base de dados podem estar distribuídas de forma diferente entre as idades analisadas.

Portanto, a seguir, serão apresentados os comportamentos de resistência ao longo do tempo considerando as diferentes classes de concreto.

Código
media_idade_concreto <- dados_limpos |>
  group_by(tipo_concreto, idade_dias) |>
  summarise(
    resistencia_media = mean(resistencia_mpa),
    .groups = "drop"
  ) |>
  arrange(tipo_concreto, idade_dias)

media_idade_concreto |>
  knitr::kable(
    caption = "Resistência média por idade e tipo de concreto",
    col.names = c(
      "Tipo de concreto",
      "Idade (dias)",
      "Resistência média (MPa)"
    ),
    digits = 2
  )
Resistência média por idade e tipo de concreto
Tipo de concreto Idade (dias) Resistência média (MPa)
C25 7 25.26
C25 14 28.30
C25 28 28.12
C25 56 27.30
C30 7 28.96
C30 14 30.87
C30 28 33.30
C30 56 33.88
C35 7 30.00
C35 14 35.92
C35 28 37.89
C35 56 42.85
C40 7 42.50
C40 14 41.33
C40 28 44.14
Código
ggplot(
  media_idade_concreto,
  aes(
    x = idade_dias,
    y = resistencia_media,
    group = tipo_concreto,
    linetype = tipo_concreto
  )
) +
  geom_line() +
  geom_point() +
  labs(
    title = "Resistência média por idade e tipo de concreto",
    x = "Idade (dias)",
    y = "Resistência média (MPa)",
    linetype = "Tipo de concreto"
  ) +
  theme_minimal()

Ao analisar os resultados separadamente para cada tipo de concreto, o comportamento fica mais evidente. Para os concretos C30 e C35, observa-se um aumento da resistência média conforme a idade avança. No concreto C25, ocorre um aumento inicial até os 14 dias, seguido de pequenas variações. Já o C40 apresenta uma pequena redução aos 14 dias e volta a aumentar aos 28 dias, não havendo resultados aos 56 dias.

Dessa forma, a análise por classe de concreto representa melhor o comportamento dos dados, pois evita que as diferenças de resistência existentes entre C25, C30, C35 e C40 interfiram na comparação entre as idades. De modo geral, observa-se uma tendência de ganho de resistência com o avanço da idade, embora esse comportamento não ocorra de maneira uniforme em todas as classes.

4.9.1 Resumo dos dados obtidos

Para a elaboração da tabela-resumo, foram selecionadas informações que permitem avaliar o comportamento geral da resistência dos concretos, a variabilidade dos resultados e a ocorrência de valores abaixo da referência.

A quantidade de ensaios informa o número de corpos de prova avaliados em cada classe. A média e a mediana permitem analisar o comportamento central dos resultados, enquanto o desvio-padrão indica o grau de dispersão e a uniformidade dos valores obtidos.

A resistência mínima observada permite identificar o menor resultado registrado em cada classe. Já o percentual de ensaios que atingiram a resistência mínima estabelecida mostra qual parcela dos corpos de prova alcançou o valor mínimo esperado para sua respectiva classe.

Código
tabela_resumo <- dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    quantidade = n(),
    resistencia_media = mean(resistencia_mpa),
    mediana = median(resistencia_mpa),
    desvio_padrao = sd(resistencia_mpa),
    resistencia_minima = min(resistencia_mpa),
    percentual_atende = mean(
      classificacao == "Atende ou supera a referência"
    ) * 100,
    .groups = "drop"
  )

tabela_resumo |>
  knitr::kable(
    caption = "Resumo estatístico para acompanhamento da qualidade do concreto",
    col.names = c(
      "Tipo de concreto",
      "N",
      "Média (MPa)",
      "Mediana (MPa)",
      "Desvio-padrão (MPa)",
      "Resistência mínima observada (MPa)",
      "Ensaios que atingiram a resistência mínima (%)"
    ),
    digits = 2
  )
Resumo estatístico para acompanhamento da qualidade do concreto
Tipo de concreto N Média (MPa) Mediana (MPa) Desvio-padrão (MPa) Resistência mínima observada (MPa) Ensaios que atingiram a resistência mínima (%)
C25 26 27.45 27.95 2.77 21.3 76.92
C30 32 32.08 31.80 3.57 24.5 71.88
C35 21 37.14 36.90 3.77 29.9 76.19
C40 16 43.33 43.15 3.08 37.3 81.25

4.10 Desafio Final

Como etapa final, busca-se reunir em um único script os principais procedimentos realizados ao longo da análise da base de dados. O processamento contempla a importação e inspeção inicial dos dados, identificação e tratamento de valores ausentes e inconsistências, criação de novas variáveis, obtenção de estatísticas descritivas e elaboração de representações gráficas.

# DESAFIO FINAL - PROCESSAMENTO E ANÁLISE DOS DADOS

library(tidyverse)


# 1. IMPORTAÇÃO E INSPEÇÃO DOS DADOS

# Importação da base de dados
dados <- read_delim(
  "base_processamento_dados_engenharia_civil.csv",
  delim = ";",
  locale = locale(decimal_mark = "."),
  show_col_types = FALSE
)

# Inspeção da estrutura da base
dados |>
  glimpse()

# Verificação das dimensões da base
dados |>
  summarise(
    observacoes = n(),
    variaveis = ncol(dados)
  )

# Identificação dos tipos das variáveis
dados |>
  summarise(
    across(
      everything(),
      ~ class(.x)
    )
  ) |>
  pivot_longer(
    cols = everything(),
    names_to = "Variável",
    values_to = "Tipo reconhecido pelo R"
  )


# 2. IDENTIFICAÇÃO DE PROBLEMAS E VALORES AUSENTES

# Contagem dos valores ausentes por variável
dados |>
  summarise(
    across(
      everything(),
      ~ sum(is.na(.x))
    )
  ) |>
  pivot_longer(
    cols = everything(),
    names_to = "Variável",
    values_to = "Valores ausentes"
  )

# Localização das observações com valores ausentes
registros_ausentes <- dados |>
  mutate(
    possui_na = if_any(
      everything(),
      is.na
    )
  ) |>
  filter(possui_na) |>
  select(-possui_na)

registros_ausentes

# Definição das variáveis quantitativas reconhecidas como texto
variaveis_quantitativas_texto <- c(
  "resistencia_mpa",
  "cimento_kg_m3",
  "relacao_a_c"
)

# Identificação de inconsistências nas variáveis quantitativas
problemas_identificados <- dados |>
  select(
    id_corpo_prova,
    all_of(variaveis_quantitativas_texto)
  ) |>
  pivot_longer(
    cols = all_of(variaveis_quantitativas_texto),
    names_to = "Variável",
    values_to = "Valor"
  ) |>
  mutate(
    valor_teste = parse_double(Valor)
  ) |>
  filter(
    !is.na(Valor) & is.na(valor_teste)
  ) |>
  select(
    id_corpo_prova,
    Variável,
    Valor
  )

problemas_identificados

# Verificação das categorias da variável obra
dados |>
  count(obra)

# Verificação das categorias da variável tipo_concreto
dados |>
  count(tipo_concreto)


# 3. LIMPEZA E TRATAMENTO DOS DADOS

# Criação de uma cópia da base original
dados_limpos <- dados

# Remoção das observações com valores ausentes
dados_limpos <- dados_limpos |>
  drop_na()

# Correção das inconsistências de formatação e digitação
dados_limpos <- dados_limpos |>
  mutate(
    resistencia_mpa = resistencia_mpa |>
      str_replace_all(",", ".") |>
      str_replace("3O.4", "30.4"),

    relacao_a_c = relacao_a_c |>
      str_replace_all(",", "."),

    cimento_kg_m3 = cimento_kg_m3 |>
      str_replace("390O", "390")
  )

# Conversão das variáveis quantitativas para formato numérico
dados_limpos <- dados_limpos |>
  mutate(
    resistencia_mpa = parse_double(resistencia_mpa),
    cimento_kg_m3 = parse_double(cimento_kg_m3),
    relacao_a_c = parse_double(relacao_a_c)
  )

# Investigação estatística dos valores após a primeira correção
dados_limpos |>
  summary()

# Correção dos valores numéricos inconsistentes
dados_limpos <- dados_limpos |>
  mutate(
    idade_dias = if_else(
      id_corpo_prova == "CP-085", 28, idade_dias
    ),
    abatimento_mm = if_else(
      id_corpo_prova == "CP-032", 125, abatimento_mm
    ),
    densidade_kg_m3 = if_else(
      id_corpo_prova == "CP-053", 2380, densidade_kg_m3
    ),
    absorção_agregado_pct = if_else(
      id_corpo_prova == "CP-072", 1.84, absorção_agregado_pct
    )
  )

# Padronização das variáveis qualitativas
dados_limpos <- dados_limpos |>
  mutate(
    obra = str_trim(obra),
    tipo_concreto = str_to_upper(tipo_concreto)
  )


# 4. CRIAÇÃO DE VARIÁVEIS DERIVADAS

# Criação da resistência relativa
dados_limpos <- dados_limpos |>
  mutate(
    resistencia_referencia = case_when(
      tipo_concreto == "C25" ~ 25,
      tipo_concreto == "C30" ~ 30,
      tipo_concreto == "C35" ~ 35,
      tipo_concreto == "C40" ~ 40
    ),
    resistencia_relativa =
      resistencia_mpa / resistencia_referencia
  )

# Remoção da variável auxiliar de resistência de referência
dados_limpos <- dados_limpos |>
  select(-resistencia_referencia)

# Classificação dos resultados em relação à resistência de referência
dados_limpos <- dados_limpos |>
  mutate(
    classificacao = case_when(
      resistencia_relativa < 1 ~ "Abaixo da referência",
      resistencia_relativa >= 1 ~ "Atende ou supera a referência"
    )
  )

# Identificação dos resultados acima da média da própria classe
dados_limpos <- dados_limpos |>
  group_by(tipo_concreto) |>
  mutate(
    acima_media = resistencia_mpa > mean(resistencia_mpa)
  ) |>
  ungroup()


# 5. ESTATÍSTICAS DESCRITIVAS

# Estatísticas da resistência por tipo de concreto
dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    quantidade = n(),
    media = mean(resistencia_mpa),
    mediana = median(resistencia_mpa),
    desvio_padrao = sd(resistencia_mpa),
    minimo = min(resistencia_mpa),
    maximo = max(resistencia_mpa)
  )

# Cálculo das médias das propriedades por tipo de concreto
medias_tipo_concreto <- dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    across(
      c(
        resistencia_mpa,
        cimento_kg_m3,
        relacao_a_c,
        abatimento_mm,
        densidade_kg_m3
      ),
      mean
    ),
    .groups = "drop"
  )

medias_tipo_concreto

# Cálculo da resistência média por idade e tipo de concreto
media_idade_concreto <- dados_limpos |>
  group_by(
    tipo_concreto,
    idade_dias
  ) |>
  summarise(
    resistencia_media = mean(resistencia_mpa),
    .groups = "drop"
  ) |>
  arrange(
    tipo_concreto,
    idade_dias
  )

media_idade_concreto


# 6. REPRESENTAÇÕES GRÁFICAS

# Gráfico da relação entre consumo de cimento e resistência
ggplot(
  dados_limpos,
  aes(
    x = cimento_kg_m3,
    y = resistencia_mpa
  )
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    se = FALSE
  ) +
  labs(
    title = "Relação entre consumo de cimento e resistência à compressão",
    x = "Consumo de cimento (kg/m³)",
    y = "Resistência à compressão (MPa)"
  ) +
  theme_minimal()

# Gráfico da relação entre água/cimento e resistência
ggplot(
  dados_limpos,
  aes(
    x = relacao_a_c,
    y = resistencia_mpa
  )
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    se = FALSE
  ) +
  labs(
    title = "Relação entre água/cimento e resistência à compressão",
    x = "Relação água/cimento",
    y = "Resistência à compressão (MPa)"
  ) +
  theme_minimal()


# 7. INFORMAÇÕES PARA O ENGENHEIRO RESPONSÁVEL

# Construção da tabela-resumo para acompanhamento da qualidade
tabela_resumo <- dados_limpos |>
  group_by(tipo_concreto) |>
  summarise(
    quantidade = n(),
    resistencia_media = mean(resistencia_mpa),
    mediana = median(resistencia_mpa),
    desvio_padrao = sd(resistencia_mpa),
    resistencia_minima = min(resistencia_mpa),
    percentual_atende = mean(
      classificacao == "Atende ou supera a referência"
    ) * 100,
    .groups = "drop"
  )

# Apresentação da tabela final
tabela_resumo |>
  knitr::kable(
    caption = "Resumo estatístico para acompanhamento da qualidade do concreto",
    col.names = c(
      "Tipo de concreto",
      "N",
      "Média (MPa)",
      "Mediana (MPa)",
      "Desvio-padrão (MPa)",
      "Resistência mínima observada (MPa)",
      "Ensaios que atingiram a resistência mínima (%)"
    ),
    digits = 2
  )

5 💭 Reflexão Final

A principal diferença entre a utilização da Base R e das ferramentas da família tidyverse está na forma de organizar o processamento dos dados. No tidyverse, funções como filter(), select(), mutate(), group_by() e summarise(), associadas ao operador |>, permitem construir uma sequência de etapas mais padronizada e de fácil leitura. Além disso, funções como across() reduzem a repetição de comandos ao permitir a aplicação de uma mesma operação a diferentes variáveis.

O processamento e a limpeza dos dados são fundamentais antes da aplicação de métodos estatísticos, pois inconsistências podem alterar médias, medidas de dispersão, gráficos e, consequentemente, a interpretação dos resultados. Na Engenharia Civil, trabalhar com uma base corretamente estruturada contribui para que as análises representem de forma mais confiável a situação estudada.

O principal problema encontrado na base foi a presença de diferentes inconsistências, incluindo valores ausentes, erros de digitação e formatação, variáveis quantitativas reconhecidas como texto e categorias sem padronização. Para o tratamento, foram removidas as cinco observações com valores ausentes e corrigidos os demais problemas identificados. Foram utilizadas funções como drop_na(), mutate(), if_else(), str_replace(), parse_double(), str_trim() e str_to_upper(), além de funções empregadas na investigação, como filter(), summarise() e across().

A solução foi considerada adequada porque os problemas foram identificados antes das alterações e tratados de acordo com cada tipo de inconsistência, mantendo-se também uma cópia da base original. Após o processamento, obteve-se uma base com 95 observações completas, variáveis quantitativas em formato numérico e categorias padronizadas, adequada para a realização das análises estatísticas propostas.

6 🧠 Considerações finais

A realização da atividade permitiu aplicar as ferramentas do tidyverse em diferentes etapas do processamento de uma base de dados, desde sua organização e tratamento até a obtenção de estatísticas e representações gráficas. A partir dessas ferramentas, foi desenvolvida a organização e a exploração das diferentes informações presentes na base.

Em relação aos dados analisados, os resultados permitiram observar diferenças de resistência entre as classes de concreto, além das relações existentes com características como idade, consumo de cimento e relação água/cimento. Também foi possível verificar que a análise apenas de valores médios nem sempre é suficiente, sendo necessário considerar a distribuição e a variabilidade dos resultados.

Dessa forma, foi possível associar as ferramentas de processamento de dados à interpretação técnica dos resultados aplicados à Engenharia Civil, nas quais as informações obtidas podem contribuir para a avaliação e o acompanhamento do comportamento dos materiais.

7 📖 Referências

ASSOCIAÇÃO BRASILEIRA DE NORMAS TÉCNICAS. ABNT NBR 8953:2015: concreto para fins estruturais - classificação pela massa específica, por grupos de resistência e consistência. Rio de Janeiro: ABNT, 2015. ISMAY, Chester; KIM, Albert Y.; VALDIVIA, Arturo. Statistical inference via data science: a ModernDive into R and the Tidyverse. 2. ed., 2026. Disponível em: https://moderndive.com/v2/.