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
