Introducción

En este análisis, se realiza la curva estándar de RT-PCR en tiempo real para determinar la relación entre la concentración de ADN y el valor del ciclo umbral (Cq). Se ajusta un modelo de regresión lineal y se muestra su ecuación junto con el coeficiente de determinación (\(R^2\)).


Carga de Paquetes

# Instalar y cargar paquetes si es necesario
if(!require(ggplot2)) install.packages("ggplot2", dependencies=TRUE)
if(!require(tidyverse)) install.packages("tidyverse", dependencies=TRUE)
if(!require(tidyverse)) install.packages("dplyr", dependencies=TRUE)
library(ggplot2,
        tidyverse,
        dpyr)
# Definir valores de Ct de ambas muestras
Ct_Cx <- 25.8
Ct_Tx<- 22.2
# Crear un data frame
df_ct <- data.frame(
  Muestra = c("Control", "Tratamiento"),
  Ct = c(Ct_Cx, Ct_Tx)
)

# Mostrar el data frame
df_ct
##       Muestra   Ct
## 1     Control 25.8
## 2 Tratamiento 22.2
#Datos RT-PCR (Concentración vs. Cq)
datos_rt_pcr <- data.frame(
  Concentracion = c(100, 10, 1, 0.1, 0.01, 0.001),  # Diluciones seriadas
  Cq = c(15.2, 18.5, 22.1, 26.5, 30.3, 34.5)  # Valores simulados de Ct
)

# Transformar concentración a log10
datos_rt_pcr$logConc <- log10(datos_rt_pcr$Concentracion)

datos_rt_pcr
##   Concentracion   Cq logConc
## 1         1e+02 15.2       2
## 2         1e+01 18.5       1
## 3         1e+00 22.1       0
## 4         1e-01 26.5      -1
## 5         1e-02 30.3      -2
## 6         1e-03 34.5      -3
# Ajustar modelo de regresión lineal
modelo_pcr <- lm(Cq ~ logConc, data = datos_rt_pcr)

modelo_pcr
## 
## Call:
## lm(formula = Cq ~ logConc, data = datos_rt_pcr)
## 
## Coefficients:
## (Intercept)      logConc  
##      22.570       -3.894
# Obtener coeficientes de la ecuación
coeficientes <- coef(modelo_pcr)
pendiente <- round(coeficientes[2], 3)
intercepto <- round(coeficientes[1], 3)
# Crear puntos de predicción para la línea ajustada
predicciones <- data.frame(logConc = seq(min(datos_rt_pcr$logConc), max(datos_rt_pcr$logConc), length.out = 100))
predicciones$Cq <- predict(modelo_pcr, newdata = predicciones)

# Obtener resumen del modelo
resumen_modelo <- summary(modelo_pcr)
resumen_modelo
## 
## Call:
## lm(formula = Cq ~ logConc, data = datos_rt_pcr)
## 
## Residuals:
##        1        2        3        4        5        6 
##  0.41905 -0.17524 -0.46952  0.03619 -0.05810  0.24762 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  22.5695     0.1493  151.16 1.15e-08 ***
## logConc      -3.8943     0.0839  -46.41 1.29e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.351 on 4 degrees of freedom
## Multiple R-squared:  0.9981, Adjusted R-squared:  0.9977 
## F-statistic:  2154 on 1 and 4 DF,  p-value: 1.289e-06
# Extraer el valor de R²
R2 <- resumen_modelo$r.squared
# Crear la ecuación en formato de texto

ecuacion_texto <- paste0("y = ", pendiente, "x + ", intercepto, "\nR² = ", round(R2, 4))

# Mostrar el valor de R²
cat("y = ", pendiente, "x + ", intercepto, "\nR² = ", round(R2, 4),"\n")
## y =  -3.894 x +  22.57 
## R² =  0.9981
# Graficar con ggplot2 e incluir la ecuación a la derecha
grafica <- ggplot(datos_rt_pcr, aes(x = logConc, y = Cq)) +
  geom_point(color = "#900C3F", size = 5) +  # Puntos de datos reales
  geom_line(data = predicciones, aes(x = logConc, y = Cq), color = "#FFC300", size = 1.5) +  # Ajuste lineal
  labs(
    title = "Curva Estándar de RT-PCR en Tiempo Real",
    x = "Log10(Concentración de ADN [pg/µL])",
    y = "Cq (Ct)",
    caption = "Simulación de datos de RT-PCR"
  ) +
  theme_classic(base_size = 15) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.title.x = element_text(face = "bold"),
    axis.title.y = element_text(face = "bold")
  ) +
  #geom_smooth(method = "lm", se = FALSE, color="#FFC300") +  # Línea de regresión
  annotate("text", 
           x = max(datos_rt_pcr$logConc) -1.5,  # Posición hacia la derecha
           y = min(datos_rt_pcr$Cq) + 18,         # Ajustar para que no se sobreponga
           label = ecuacion_texto, 
           size = 5, color = "#581845", fontface = "bold", hjust = 0)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
grafica

# Ct (Cq) observado
Ct_Control <- Ct_Cx

# Calcular log10(Concentración)
logConc_calculado_Cx <- (Ct_Control - intercepto) / pendiente

# Obtener concentración real
Conc_calculada_Cx <- 10^logConc_calculado_Cx

# Crear un data frame con el nuevo punto
nuevo_punto_Cx <- data.frame(logConc = logConc_calculado_Cx, Cq = Ct_Control)

# Mostrar resultado
cat("La concentración estimada para el control es:", round(Conc_calculada_Cx, 4), "pg/µL\n")
## La concentración estimada para el control es: 0.1481 pg/µL
interpolacion <- grafica+
  geom_point(data = nuevo_punto_Cx, 
             aes(x = logConc, y = Cq), 
             color = "black", 
             size = 6, shape = 20) +  # Nuevo punto en verde
  geom_segment(aes(x = min(datos_rt_pcr$logConc), 
                   xend = logConc_calculado_Cx, 
                   y = Ct_Control, 
                   yend = Ct_Control), 
               linetype = "dotted", 
               color = "#2f2d2c", 
               size = 1.5) +  # Línea horizontal hasta el punto
  geom_segment(aes(x = logConc_calculado_Cx, xend = logConc_calculado_Cx, 
                   y = min(datos_rt_pcr$Cq), yend = Ct_Control),
               linetype = "dotted", 
               color = "#2f2d2c", 
               size = 1.5)+
   annotate("text", 
           x = logConc_calculado_Cx, 
           y = Ct_Control - 1,  
           label = paste0("  [Cx]= ", 
                          round(Conc_calculada_Cx, 4), " pg/µL"), 
           color = "black", size = 5, 
           fontface = "bold", 
           hjust = -0.007, vjust = -1.7)

interpolacion
## Warning: Use of `datos_rt_pcr$logConc` is discouraged.
## ℹ Use `logConc` instead.
## Warning in geom_segment(aes(x = min(datos_rt_pcr$logConc), xend = logConc_calculado_Cx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?
## Warning: Use of `datos_rt_pcr$Cq` is discouraged.
## ℹ Use `Cq` instead.
## Warning in geom_segment(aes(x = logConc_calculado_Cx, xend = logConc_calculado_Cx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?

# Ct (Cq) observado
Ct_Tratamiento <- Ct_Tx

# Calcular log10(Concentración)
logConc_calculado_Tx <- (Ct_Tratamiento - intercepto) / pendiente

# Obtener concentración real
Conc_calculada_Tx  <- 10^logConc_calculado_Tx 

# Crear un data frame con el nuevo punto
nuevo_punto_Tx <- data.frame(logConc = logConc_calculado_Tx , Cq = Ct_Tx )

# Mostrar resultado
cat("La concentración estimada para el tratamiento es:", round(Conc_calculada_Tx , 4), "pg/µL\n")
## La concentración estimada para el tratamiento es: 1.2446 pg/µL
interpolacion_Tx <- interpolacion+
  geom_point(data = nuevo_punto_Tx, 
             aes(x = logConc, y = Cq), 
             color = "black", 
             size = 6, shape = 20) +  # Nuevo punto en verde
  geom_segment(aes(x = min(datos_rt_pcr$logConc), 
                   xend = logConc_calculado_Tx, 
                   y = Ct_Tratamiento, 
                   yend = Ct_Tratamiento), 
               linetype = "dotted", 
               color = "#2f2d2c", 
               size = 1.5) +  # Línea horizontal hasta el punto
  geom_segment(aes(x = logConc_calculado_Tx, xend = logConc_calculado_Tx, 
                   y = min(datos_rt_pcr$Cq), yend = Ct_Tratamiento),
               linetype = "dotted", 
               color = "#2f2d2c", 
               size = 1.5)+
   annotate("text", 
           x = logConc_calculado_Tx, 
           y = Ct_Tratamiento - 1,  
           label = paste0("  [Tx]= ", 
                          round(Conc_calculada_Tx, 4), " pg/µL"), 
           color = "black", size = 5, 
           fontface = "bold", 
           hjust = -0.007, vjust = -1.7)

interpolacion_Tx
## Warning: Use of `datos_rt_pcr$logConc` is discouraged.
## ℹ Use `logConc` instead.
## Warning in geom_segment(aes(x = min(datos_rt_pcr$logConc), xend = logConc_calculado_Cx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?
## Warning: Use of `datos_rt_pcr$Cq` is discouraged.
## ℹ Use `Cq` instead.
## Warning in geom_segment(aes(x = logConc_calculado_Cx, xend = logConc_calculado_Cx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?
## Warning: Use of `datos_rt_pcr$logConc` is discouraged.
## ℹ Use `logConc` instead.
## Warning in geom_segment(aes(x = min(datos_rt_pcr$logConc), xend = logConc_calculado_Tx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?
## Warning: Use of `datos_rt_pcr$Cq` is discouraged.
## ℹ Use `Cq` instead.
## Warning in geom_segment(aes(x = logConc_calculado_Tx, xend = logConc_calculado_Tx, : All aesthetics have length 1, but the data has 6 rows.
## ℹ Did you mean to use `annotate()`?

Comparacion <- data.frame(Condicion=c("Control", "Tratamiento"),
           Concentracion= c(Conc_calculada_Cx, Conc_calculada_Tx))
Comparacion
##     Condicion Concentracion
## 1     Control     0.1480876
## 2 Tratamiento     1.2445661
#Gráfica analítica

Barras <- ggplot(Comparacion, aes(x = Condicion, y = Concentracion, fill = Condicion)) +
  geom_bar(stat = "identity", color = "black", width = 0.9) +  # Barras con borde negro
  labs(
    title = "Comparación de Concentraciones",
    x = "Condición",
    y = "Concentración (pg/µL)"
  ) +
  theme_classic(base_size = 14) +
  scale_fill_manual(values = c("Control" = "#f8c471", "Tratamiento" = "#2980b9")) +  # Colores personalizados
  #geom_text(aes(label = round(Concentracion, 2)), vjust = -0.5, size = 5) +  # Etiquetas de valores
  scale_y_continuous(limits = c(0, (max(Comparacion$Concentracion))), 
                     breaks = seq(0, (max(Comparacion$Concentracion)+0.2), 
                                  by = round(((max(Comparacion$Concentracion))/10),1))) +  # Ajuste del eje Y
  theme(
    axis.title = element_text(face = "bold", size = 16),  # Títulos de los ejes en negrita
    axis.text = element_text(face = "bold", size = 14),  # Valores de los ejes en negrita
    axis.line = element_line(size = 1.5, color = "black"),  # Líneas de los ejes más gruesas
    legend.position = "none")
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Barras

Porcentaje <- Comparacion %>%
  mutate(Porcentaje = (Concentracion ) / Concentracion[Condicion == "Control"]) %>% 
  select(1,3)
  
Porcentaje
##     Condicion Porcentaje
## 1     Control   1.000000
## 2 Tratamiento   8.404255
#Gráfica analítica

Barras2 <- ggplot(Porcentaje, aes(x = Condicion, y = Porcentaje, fill = Condicion)) +
  geom_bar(stat = "identity", color = "black", width = 0.9) +  # Barras con borde negro
  labs(
    title = "Comparación de Concentraciones",
    x = "Condición",
    y = "Tasa de cambio (u. a.)"
  ) +
  theme_classic(base_size = 14) +
  scale_fill_manual(values = c("Control" = "#f8c471", 
                               "Tratamiento" = "#2980b9")) +  # Colores personalizados
  #geom_text(aes(label = round(Porcentaje, 2)), vjust = -0.5, size = 5) +  # Corrección del nombre de columna
  scale_y_continuous(limits = c(0, (max(Porcentaje$Porcentaje)+1)), 
                     breaks = seq(0, (max(Porcentaje$Porcentaje)+1), 
                                  by = round((max(Porcentaje$Porcentaje)/10),0)))+  # Ajuste del eje Y
  theme(axis.title = element_text(face = "bold", 
                                  size = 16),  # Títulos de los ejes en negrita
        axis.text = element_text(face = "bold", 
                                 size = 14),  # Valores de los ejes en negrita
        axis.line = element_line(size = 1.5, 
                                 color = "black"),  # Líneas de los ejes más gruesas
        legend.position = "none"  ) #Quita la barra de descripción
Barras2