if(!require("pacman"))
  install.packages("pacman")
## Loading required package: pacman
library("pacman")

p_load("vroom",
       "ggplot2",
       "dplyr",
       "ggrepel",
       "tidyverse",
       "scales")
Datos_PCR <- vroom("https://raw.githubusercontent.com/ManuelLaraMVZ/Metabolomica_2026_1/refs/heads/main/Amplificacion_ambos%20grupos.csv")
## Rows: 51 Columns: 9
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (9): Cycle, 50 ng, 10 ng, 5 ng, 1 ng, 0.5 ng, 0.1 ng, G1-M, G2-M
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Datos_PCR
## # A tibble: 51 × 9
##    Cycle `50 ng` `10 ng` `5 ng` `1 ng` `0.5 ng` `0.1 ng` `G1-M`  `G2-M`
##    <dbl>   <dbl>   <dbl>  <dbl>  <dbl>    <dbl>    <dbl>  <dbl>   <dbl>
##  1     1    21.3   0.124 -2.25    2.06   -1.78    -2.32    22.1 -0.184 
##  2     2    27.9   1.39   2.61    8.66    5.96     2.18    92.0  8.29  
##  3     3    34.5  -0.660 11.4    12.2     5.94     2.34   132.   3.73  
##  4     4    56.6   1.000  1.87    6.49    5.20     1.05   164.   3.20  
##  5     5    65.2  -0.484 -1.18    1.77   -5.23     0.606  184.   0.557 
##  6     6    72.3   1.44  -0.477   2.35   -0.958    4.81   214.   0.0300
##  7     7    89.1  -5.10  -3.87   -4.93   -3.60    -4.87   249.  -7.37  
##  8     8   101.   -1.67  -3.52   -3.31   -2.34     0.403  277.  -6.98  
##  9     9   114.    2.66   5.35   -2.11   -1.40    -0.659  302.  -3.63  
## 10    10   123.   13.6   10.4    -5.98    2.27     1.13   332.  -1.16  
## # ℹ 41 more rows
Datos_curva <- Datos_PCR %>%
  pivot_longer(
    cols = -Cycle,
    names_to = "Curva",
    values_to = "Fluorescencia"
  ) %>%
  mutate(
    Concentracion = as.numeric(gsub(" ng", "", Curva)),
    LogConc = log10(Concentracion)
  )
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Concentracion = as.numeric(gsub(" ng", "", Curva))`.
## Caused by warning:
## ! NAs introduced by coercion
Datos_curva
## # A tibble: 408 × 5
##    Cycle Curva  Fluorescencia Concentracion LogConc
##    <dbl> <chr>          <dbl>         <dbl>   <dbl>
##  1     1 50 ng         21.3            50     1.70 
##  2     1 10 ng          0.124          10     1    
##  3     1 5 ng          -2.25            5     0.699
##  4     1 1 ng           2.06            1     0    
##  5     1 0.5 ng        -1.78            0.5  -0.301
##  6     1 0.1 ng        -2.32            0.1  -1    
##  7     1 G1-M          22.1            NA    NA    
##  8     1 G2-M          -0.184          NA    NA    
##  9     2 50 ng         27.9            50     1.70 
## 10     2 10 ng          1.39           10     1    
## # ℹ 398 more rows
umbral <- 130

Ct <- Datos_curva %>%
  filter(!is.na(LogConc)) %>%   # solo estándares
  group_by(Curva, LogConc) %>%
  filter(Fluorescencia >= umbral) %>%
  slice(1) %>%
  ungroup()

Grafica <- ggplot(Ct,
  aes(x = LogConc,
      y = Cycle)) +
  geom_point(color = "#900C3F", size = 4) +
  theme_classic() +
  labs(
    title = "Curva estándar qPCR",
    x = "log10(Concentración)",
    y = "Ct"
  )

Grafica

modelo_lineal <- lm(Cycle ~ LogConc, data = Ct)

modelo_lineal
## 
## Call:
## lm(formula = Cycle ~ LogConc, data = Ct)
## 
## Coefficients:
## (Intercept)      LogConc  
##      18.321       -4.257
coeficientes <- coef(modelo_lineal)

coeficientes
## (Intercept)     LogConc 
##   18.320919   -4.256508
m <- round(coeficientes["LogConc"], 2)

m
## LogConc 
##   -4.26
y0 <- round(coeficientes["(Intercept)"], 2)

y0
## (Intercept) 
##       18.32
prediccion <- data.frame(
  LogConc = seq(min(Ct$LogConc),
                max(Ct$LogConc),
                length.out = 100)
)

prediccion$Ct <- predict(modelo_lineal, newdata = prediccion)

head(prediccion)
##      LogConc       Ct
## 1 -1.0000000 22.57743
## 2 -0.9727377 22.46138
## 3 -0.9454754 22.34534
## 4 -0.9182130 22.22930
## 5 -0.8909507 22.11326
## 6 -0.8636884 21.99722
resumen_modelo <- summary(modelo_lineal)

R2 <- resumen_modelo$r.squared

R2
## [1] 0.9652785
ecuacion_recta <- paste0("Ct = ", m, "x + ", y0, "\nR² = ", round(R2, 3))

cat(ecuacion_recta)
## Ct = -4.26x + 18.32
## R² = 0.965
Grafica_ajuste <- Grafica +
  geom_line(data = prediccion,
            aes(x = LogConc, y = Ct),
            color = "#FFC300",
            linewidth = 1.5) +
  labs(title = "Curva estándar de RT-PCR",
       subtitle = "Equipo: LSD",
       caption = "Diseño: SZV",
       x = "Log10 (Concentración) [pg/µL]",
       y = "Cycle threshold (Ct)") +
  theme_classic(base_size = 15) +
  scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        axis.title.x = element_text(face = "bold"),
        axis.title.y = element_text(face = "bold")) +
  annotate("text",
           x = max(Ct$LogConc) - 1.5,
           y = min(Ct$Cycle) + 2,
           label = ecuacion_recta,
           color = "#581845",
           size = 5,
           fontface = "bold",
           hjust = 0)

Grafica_ajuste

Dato_muestra <- Datos_curva %>%
  filter(Curva == "G1-M") %>%
  filter(Fluorescencia >= umbral) %>%
  slice(1) %>%
  select(Curva, Ct = Cycle)

Dato_muestra
## # A tibble: 1 × 2
##   Curva    Ct
##   <chr> <dbl>
## 1 G1-M      3
Ct_muestra <- round(Dato_muestra$Ct, 2)

Ct_muestra
## [1] 3
LogConc_calculado <- (Ct_muestra - y0) / m

LogConc_calculado
## (Intercept) 
##    3.596244
Valor_real <- 10^LogConc_calculado

Valor_real
## (Intercept) 
##    3946.791
cat("La concentración de la muestra es:", round(Valor_real, 2), "pg/µL")
## La concentración de la muestra es: 3946.79 pg/µL
Datos_muestra <- data.frame(
  Concentracion_real = round(Valor_real, 2),
  LogConc = round(LogConc_calculado, 2),
  Ct = Ct_muestra
)

Datos_muestra
##             Concentracion_real LogConc Ct
## (Intercept)            3946.79     3.6  3
x_min <- min(Ct$LogConc)
y_min <- min(Ct$Cycle)

Grafica_muestra <- Grafica_ajuste +
  geom_point(data = Datos_muestra,
             aes(x = LogConc, y = Ct),
             color = "black",
             size = 6,
             shape = 20,
             inherit.aes = FALSE) +
  geom_segment(data = Datos_muestra,
               aes(x = x_min, xend = LogConc,
                   y = Ct, yend = Ct),
               linetype = "dotted",
               color = "#2f2d2c",
               linewidth = 1.5,
               inherit.aes = FALSE) +
  geom_segment(data = Datos_muestra,
               aes(x = LogConc, xend = LogConc,
                   y = y_min, yend = Ct),
               linetype = "dotted",
               color = "#2f2d2c",
               linewidth = 1.5,
               inherit.aes = FALSE) +
  geom_text(data = Datos_muestra,
            aes(x = LogConc,
                y = Ct + 2,
                label = paste0("[ Muestra ] = ", Concentracion_real, " pg/µL")),
            color = "black",
            fontface = "bold",
            hjust = 1.1,
            vjust = 0,
            inherit.aes = FALSE)

Grafica_muestra