Anchor Article

Anchor article: Baumgaertner Nunn, E. (2026, Sept. 8). Can geneticists fight disease by targeting “nurture” instead of “nature”? The New York Times.https://www.nytimes.com/2026/09/08/science/epigenetic-editing.html?smid=url-share.

This article describes epigenome editing which is where chemical tags (i.e. methylation, acetylation) are added or removed in certain regions of the genome to up or down regulate gene expression. One example in the article is a single injection that would down regulate a liver gene involved in clearing low density lipoprotein (LDL) or “bad” cholesterol, as a possible replacement for a daily medication. This description fits the gene PCSK9, which is the target in the scientific articles that I used for this analysis.

This analysis test three research questions:

  1. Does a single dose of an epigenome-editing injection lower PCSK9 in the blood?
  2. Does the effect last, or does it fade over time?
  3. Does cholesterol go down as a result of down regulated PCSK9?
  4. Is there more DNA methylation at the Pcsk9 gene in treated mice.

About the data:

I used the Source Data from Cappelluti et al. (2024), which is open access. It comes as Excel files with one sheet per figure panel. I saved each sheet I needed as a CSV file. For the mouse experiments, each mouse got one injection of nanoparticles carrying either placebo or an epigenome editor.

CSV file Paper figure What it holds
pcsk9_330days.csv Fig. 3B PCSK9 in blood, 17 time points up to 330 days
pcsk9_43days.csv Fig. 5B PCSK9 in blood, first 43 days
total_cholesterol.csv Fig. 5C Total cholesterol before treatment and 30 days after
ldl_cholesterol.csv Fig. 5D LDL-cholesterol before treatment and 30 days after
methylation_cpg.csv Fig. 5E Percent methylation at 45 CpG sites in the Pcsk9 promoter
ldl_earlier_cohort.csv Extended Data Fig. 3E LDL-cholesterol at day 30 in an earlier cohort of mice
Group What the mice received
Vehicle Salt solution only (baseline control)
Mock Nanoparticles carrying a harmless message (controls for the injection)
Cas9 Classic CRISPR gene editor of Pcsk9
ZFP-ETRs First epigenetic editor (three parts)
EvoETR-8 Improved all-in-one epigenetic editor

PCSK9 values are normalised to the vehicle group, so 1.0 means “same as baseline” and 0.4 means “60% lower then baseline.”

Tidying the data

The CSV files are laid out for people, not for R: one mouse per column, group names only in the top cell of each block, title rows above the numbers, and time points down the rows. For each file I (1) skip the title rows with skip =, (2) give the columns clear names with col_names =, (3) treat the blank cells and the text na as missing with na =, and (4) pivot_longer() into tidy format (one row per mouse per time point). Every file has an empty first column (column A in Excel), which I name blank and drop.

First, the column names for each layout (group, underscore, mouse number):

# load packages
library(tidyverse)
library(lme4)
# create column names for pcsk9_330days.csv, this file contains 7 ZFP-ETR mice, 5 Mock mice, 4 Vehicle mice, and 3 Cas9 mice.
# "blank" represents the empty first column in the CSV.
# "day" represents the time point for each measurement.

names_long <- c(
  "blank", "day",
  
  # Name the 7 ZFP-ETR mouse columns
  paste0("ZFP-ETRs_", 1:7),
  
  # Name the 5 Mock mouse columns
  paste0("Mock_", 1:5),
  
  # Name the 4 Vehicle mouse columns
  paste0("Vehicle_", 1:4),
  
  # Name the 3 Cas9 mouse columns
  paste0("Cas9_", 1:3)
)


# create column names for pcsk9_43days.csv,, total_cholesterol.csv, and ldl_cholesterol.csv.
# each file contains 6 mice in each of four groups: ZFP-ETR, EvoETR-8, Vehicle, and Cas9.

names_short <- c(
  "blank", "day",
  
  # Name the 6 ZFP-ETR mouse columns
  paste0("ZFP-ETRs_", 1:6),
  
  # Name the 6 EvoETR-8 mouse columns
  paste0("EvoETR-8_", 1:6),
  
  # Name the 6 Vehicle mouse columns
  paste0("Vehicle_", 1:6),
  
  # Name the 6 Cas9 mouse columns
  paste0("Cas9_", 1:6)
)


# create column names for methylation_cpg.csv., this file contains 3 mice in each of three groups: ZFP-ETR, EvoETR-8, and Vehicle.
# "position" identifies the CpG site or genomic position associated with each methylation measurement.

names_meth <- c(
  "blank", "position",
  
  # Name the 3 ZFP-ETR mouse columns
  paste0("ZFP-ETRs_", 1:3),
  
  # Name the 3 EvoETR-8 mouse columns
  paste0("EvoETR-8_", 1:3),
  
  # Name the 3 Vehicle mouse columns
  paste0("Vehicle_", 1:3)
)

PCSK9 over 330 days

# load the pcsk over 330 days csv, skip the four title lines, add column names
pcsk9_wide <- read_csv("pcsk9_330days.csv", skip = 4, col_names = names_long,
                       na = c("", "na")) %>%
  select(-blank)

head(pcsk9_wide)
## # A tibble: 6 × 20
##     day `ZFP-ETRs_1` `ZFP-ETRs_2` `ZFP-ETRs_3` `ZFP-ETRs_4` `ZFP-ETRs_5`
##   <dbl>        <dbl>        <dbl>        <dbl>        <dbl>        <dbl>
## 1    -1       1.70         1.54          1.33         1.19        1.15  
## 2     1       0.160        0.0888        0.182        0.172       0.161 
## 3     3       0.305        0.153         0.275        0.381       0.284 
## 4     6       0.660        0.170         0.227        0.434       0.199 
## 5    10       0.0260      -0.0505        0.243        0.236       0.0394
## 6    15       0.346        0.209         0.423        0.362       0.190 
## # ℹ 14 more variables: `ZFP-ETRs_6` <dbl>, `ZFP-ETRs_7` <dbl>, Mock_1 <dbl>,
## #   Mock_2 <dbl>, Mock_3 <dbl>, Mock_4 <dbl>, Mock_5 <dbl>, Vehicle_1 <dbl>,
## #   Vehicle_2 <dbl>, Vehicle_3 <dbl>, Vehicle_4 <dbl>, Cas9_1 <dbl>,
## #   Cas9_2 <dbl>, Cas9_3 <dbl>
# above is in a wide format with one column per mouse I want to reshape it so that it is in a long format where the columns are Id,  group, and mouse number

pcsk9_long <- pcsk9_wide %>%
  pivot_longer(-day, names_to = "id", values_to = "pcsk9") %>%
  separate(id, into = c("group", "mouse"), sep = "_", remove = FALSE) %>%
  mutate(group = factor(group, levels = c("Vehicle", "Mock", "Cas9", "ZFP-ETRs")))

# I'm only showing the first few rows here but I looked at the table in the R environment to make sure it worked properly

# check the group names
table(pcsk9_long$group, useNA = "ifany")
## 
##  Vehicle     Mock     Cas9 ZFP-ETRs 
##       68       85       51      119
head(pcsk9_long)
## # A tibble: 6 × 5
##     day id         group    mouse pcsk9
##   <dbl> <chr>      <fct>    <chr> <dbl>
## 1    -1 ZFP-ETRs_1 ZFP-ETRs 1      1.70
## 2    -1 ZFP-ETRs_2 ZFP-ETRs 2      1.54
## 3    -1 ZFP-ETRs_3 ZFP-ETRs 3      1.33
## 4    -1 ZFP-ETRs_4 ZFP-ETRs 4      1.19
## 5    -1 ZFP-ETRs_5 ZFP-ETRs 5      1.15
## 6    -1 ZFP-ETRs_6 ZFP-ETRs 6      1.33

PCSK9, cholesterol and LDL-C in the second experiment

# load data for PCSK9 over 43 days (same as above)
pcsk9_short <- read_csv("pcsk9_43days.csv", skip = 3, col_names = names_short,
                        na = c("", "na")) %>%
  select(-blank) %>%
  pivot_longer(-day, names_to = "id", values_to = "pcsk9")

chol <- read_csv("total_cholesterol.csv", skip = 4, col_names = names_short,
                 na = c("", "na")) %>%
  select(-blank) %>%
  pivot_longer(-day, names_to = "id", values_to = "chol")

ldl <- read_csv("ldl_cholesterol.csv", skip = 5, col_names = names_short,
                na = c("", "na")) %>%
  select(-blank) %>%
  pivot_longer(-day, names_to = "id", values_to = "ldl")

head(pcsk9_short)
## # A tibble: 6 × 3
##     day id         pcsk9
##   <dbl> <chr>      <dbl>
## 1     1 ZFP-ETRs_1 0.173
## 2     1 ZFP-ETRs_2 0.295
## 3     1 ZFP-ETRs_3 0.257
## 4     1 ZFP-ETRs_4 0.373
## 5     1 ZFP-ETRs_5 0.373
## 6     1 ZFP-ETRs_6 0.221
head(chol)
## # A tibble: 6 × 3
##     day id          chol
##   <dbl> <chr>      <dbl>
## 1    -1 ZFP-ETRs_1    83
## 2    -1 ZFP-ETRs_2    76
## 3    -1 ZFP-ETRs_3    75
## 4    -1 ZFP-ETRs_4    79
## 5    -1 ZFP-ETRs_5    72
## 6    -1 ZFP-ETRs_6    89
head(ldl)
## # A tibble: 6 × 3
##     day id           ldl
##   <dbl> <chr>      <dbl>
## 1    -1 ZFP-ETRs_1    19
## 2    -1 ZFP-ETRs_2    17
## 3    -1 ZFP-ETRs_3    17
## 4    -1 ZFP-ETRs_4    20
## 5    -1 ZFP-ETRs_5    18
## 6    -1 ZFP-ETRs_6    22
# for the cholesterol models I need one row per mouse with its "before" (day -1) and "after" (day 30) values side by side. I get this by filtering each table and joining them

# start with LDL baseline 

base <- ldl %>% filter(day == -1) %>% select(id, ldl_base = ldl)  

# combine each mouse's baseline and day 30 cholesterol, LDL, and PCSK9 measurements into one dataset, and organize mice by treatment group.
mice <- base %>%
  left_join(ldl  %>% filter(day == 30) %>% select(id, ldl_30  = ldl),  by = "id") %>%
  left_join(chol %>% filter(day == -1) %>% select(id, chol_base = chol), by = "id") %>%
  left_join(chol %>% filter(day == 30) %>% select(id, chol_30 = chol),  by = "id") %>%
  left_join(pcsk9_short %>% filter(day == 30) %>% select(id, pcsk9_30 = pcsk9), by = "id") %>%
  separate(id, into = c("group", "mouse"), sep = "_", remove = FALSE) %>%
  mutate(group = factor(group, levels = c("Vehicle", "Cas9", "ZFP-ETRs", "EvoETR-8")))

head(mice)
## # A tibble: 6 × 8
##   id         group    mouse ldl_base ldl_30 chol_base chol_30 pcsk9_30
##   <chr>      <fct>    <chr>    <dbl>  <dbl>     <dbl>   <dbl>    <dbl>
## 1 ZFP-ETRs_1 ZFP-ETRs 1           19     14        83      78    0.383
## 2 ZFP-ETRs_2 ZFP-ETRs 2           17     21        76      90    0.699
## 3 ZFP-ETRs_3 ZFP-ETRs 3           17     15        75      88    0.749
## 4 ZFP-ETRs_4 ZFP-ETRs 4           20     17        79      85    0.717
## 5 ZFP-ETRs_5 ZFP-ETRs 5           18     13        72      66    0.436
## 6 ZFP-ETRs_6 ZFP-ETRs 6           22     15        89      78    0.710

Methylation and the earlier cohort

#load methylation csv (same as above)
meth <- read_csv("methylation_cpg.csv", skip = 4, col_names = names_meth,
                 na = c("", "na")) %>%
  select(-blank) %>%
  pivot_longer(-position, names_to = "id", values_to = "meth") %>%
  separate(id, into = c("group", "mouse"), sep = "_", remove = FALSE) %>%
  mutate(group = factor(group, levels = c("Vehicle", "ZFP-ETRs", "EvoETR-8")))

# load earlier cohort csv and put groups side by side, there are gaps because of the differing sample size for each group so "NA" fills those gaps
cohort1 <- read_csv("ldl_earlier_cohort.csv", skip = 5,
                    col_names = c("blank", "ZFP-ETRs", "Cas9", "Vehicle"), na = c("", "na")) %>%
  select(-blank) %>%
  pivot_longer(everything(), names_to = "group", values_to = "ldl") %>%
  drop_na(ldl) %>%
  mutate(group = factor(group, levels = c("Vehicle", "Cas9", "ZFP-ETRs")))

head(meth)
## # A tibble: 6 × 5
##    position id         group    mouse  meth
##       <dbl> <chr>      <fct>    <chr> <dbl>
## 1 106463706 ZFP-ETRs_1 ZFP-ETRs 1      12.3
## 2 106463706 ZFP-ETRs_2 ZFP-ETRs 2      11.0
## 3 106463706 ZFP-ETRs_3 ZFP-ETRs 3      13.5
## 4 106463706 EvoETR-8_1 EvoETR-8 1      17.6
## 5 106463706 EvoETR-8_2 EvoETR-8 2      14.4
## 6 106463706 EvoETR-8_3 EvoETR-8 3      13.0
head(cohort1)
## # A tibble: 6 × 2
##   group      ldl
##   <fct>    <dbl>
## 1 ZFP-ETRs    11
## 2 Cas9        12
## 3 Vehicle     20
## 4 ZFP-ETRs    13
## 5 Cas9        14
## 6 Vehicle      8

Looking at the data

Does the PCSK9 drop last?

# calculate the mean PCSK9 level and standard error for each group at each time point 
time_summary <- pcsk9_long %>%
  group_by(group, day) %>%
  summarise(mean = mean(pcsk9), se = sd(pcsk9) / sqrt(n()))

# create a line graph to compare PCSK9 levels over time between treatment groups
ggplot(time_summary, aes(x = day, y = mean, colour = group)) +
  geom_line() +
  geom_pointrange(aes(ymin = mean - se, ymax = mean + se), size = 0.3) +
  labs(x = "Days after injection", y = "PCSK9 in blood (1.0 = baseline)",
       colour = "Group",
       title = "Does a single dose lower PCSK9 in the blood and for how long",
       caption = "Points = group mean, bars = +/- 1 standard error")

Ananlysis: PCSK9 falls in a few days in the ZFP-ETR and Cas9 mice. Mock mice spike to 2 early and then wander back toward 1. The ZFP-ETR curve rises over the first weeks but stays below the control line. Because the data are normalised to the vehicle mean at each time point, the vehicle line is at 1, and the treated groups were already higher than vehicle on day -1, before treatment.

Cholesterol before and after

# separate mouse IDs into treatment group and mouse number, and label the time points 
ldl_plot <- ldl %>%
  separate(id, into = c("group", "mouse"), sep = "_", remove = FALSE) %>%
  mutate(group = factor(group, levels = c("Vehicle", "Cas9", "ZFP-ETRs", "EvoETR-8")),
         time = factor(day, levels = c(-1, 30), labels = c("Before", "30 days after")))

# create a line graph showing changes in LDL cholesterol for each mouse, separated by treatment group
ggplot(ldl_plot, aes(x = time, y = ldl, group = id, colour = group)) +
  geom_line() + geom_point() +
  facet_wrap(~ group, nrow = 1) +
  labs(x = NULL, y = "LDL-cholesterol (mg/dL)", 
       title = "LDL Cholesterol Levels Before and After Teatments",
       caption = "Each line is one mouse") + 
  theme(legend.position = "none") #delete legend as its repetitive

Where does the methylation happen?

# calculate the mean DNA methylation for each treatment group at each CpG position, then convert the positions to relative distances in base pairs
meth_summary <- meth %>%
  group_by(group, position) %>%
  summarise(mean_meth = mean(meth)) %>%
  mutate(bp = position - min(position))

# create a line graph comparing average DNA methylation across the Pcsk9 promoter between treatment groups
ggplot(meth_summary, aes(x = bp, y = mean_meth, colour = group)) +
  geom_line() + geom_point() +
  labs(x = "Position along the Pcsk9 promoter (base pairs)", y = "% of DNA methylated",
       colour = "Group",
       title = "Percentage of DNA Methylation on the Pcsk9 promoter")

Analysis: the treatment groups both have more methylation accross the promotor, with the EvoETR-8 being the highest

Linear models

Does one dose lower PCSK9? (day 330)

# select PCSK9 measurements at day 330 and join each mouse's baseline PCSK9 level
d330 <- pcsk9_long %>%
  filter(day == 330) %>%
  left_join(pcsk9_long %>% filter(day == -1) %>% select(id, baseline = pcsk9), by = "id")

# linear model to compare PCSK9 levels at day 330 between treatments
m1 <- lm(pcsk9 ~ group, data = d330)
summary(m1)
## 
## Call:
## lm(formula = pcsk9 ~ group, data = d330)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.11019 -0.07220 -0.01005  0.05695  0.17710 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    1.00000    0.05042  19.833 3.56e-12 ***
## groupMock      0.02226    0.06765   0.329    0.747    
## groupCas9     -0.52255    0.07702  -6.785 6.15e-06 ***
## groupZFP-ETRs -0.37861    0.06321  -5.990 2.48e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1008 on 15 degrees of freedom
## Multiple R-squared:  0.8601, Adjusted R-squared:  0.8321 
## F-statistic: 30.73 on 3 and 15 DF,  p-value: 1.193e-06
# calculate confidence interval
confint(m1)
##                    2.5 %     97.5 %
## (Intercept)    0.8925311  1.1074689
## groupMock     -0.1219258  0.1664434
## groupCas9     -0.6867112 -0.3583884
## groupZFP-ETRs -0.5133264 -0.2438878
# generate diagnostic plots to check whether the linear model meets its assumptions
par(mfrow = c(2, 2))
plot(m1)

# extract the estimated coefficients from the linear model and store them in b1
b1 <- coef(m1)

Analysis: The intercept (1) is the Vehicle mean. ZFP-ETR mice mean is 0.38 lower, which is about 38% lower than baseline mice almost a year after one injection. Cas9 mice mean is about 52% lower. At day 330, Mock mice are not different from Vehicle, so the injection alone does not explain the drop. This model explains 86% of the variation (R-squared).

Does the effect fade over time?

# for ZFP-ETR mice only, I model PCSK9 against time from day 30 onward (after the first drop). 
# I express time in units of 100 days so the slope is for change per 100 days.
pcsk9_long <- pcsk9_wide %>%
  pivot_longer(-day, names_to = "id", values_to = "pcsk9") %>%
  separate(id, into = c("group", "mouse"), sep = "_", remove = FALSE) %>%
  mutate(group = factor(group, levels = c("Vehicle", "Mock", "Cas9", "ZFP-ETRs")))

zfp <- pcsk9_long %>%
  filter(group == "ZFP-ETRs", day >= 30) %>%
  mutate(day100 = day / 100)

# linear model for PCSK9 levels over time in ZFP-ETRs mice
m2 <- lm(pcsk9 ~ day100, data = zfp)
summary(m2)
## 
## Call:
## lm(formula = pcsk9 ~ day100, data = zfp)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.30397 -0.13085 -0.02923  0.10715  0.60778 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.49866    0.04324  11.533   <2e-16 ***
## day100       0.03285    0.02227   1.475    0.145    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1876 on 68 degrees of freedom
## Multiple R-squared:  0.03099,    Adjusted R-squared:  0.01674 
## F-statistic: 2.175 on 1 and 68 DF,  p-value: 0.1449
confint(m2)
##                   2.5 %     97.5 %
## (Intercept)  0.41237710 0.58494287
## day100      -0.01159727 0.07729631
# plot the second linear model with ZFP-ETRs mice's PCSK9 levels over time
ggplot(zfp, aes(x = day, y = pcsk9)) +
  geom_point() +
  geom_smooth(method = "lm") +
  geom_hline(yintercept = 1, linetype = "dashed") +
  labs(x = "Days after injection", y = "PCSK9 (1.0 = untreated)",
       title = "PCSK9 levels in ZFP-ETR mice after day 30")

# generate diagnostic plots to check whether the linear model meets its assumptions
par(mfrow = c(2, 2))
plot(m2)

Analysis: The slope from day 30 is 0.03 per 100 days (95% CI). The interval includes zero, so there is no clear evidence of a steady upward drift after day 30. The group mean moved from at day 30 to at day 330, but the plot shows that most of this happens in the first ~50 days and the values then stay around 0.5.

A problem with this model. The same 7 mice are measured at many time points, so the observations are not independent: a mouse that is high in PCSK9 at one time point tends to be high at the others. A linear model treats the 70 values as if they came from 70 different mice. A linear mixed model fixes this by giving each mouse its own baseline level, which is done bellow.

# linear mixed model of the above data
m2_mixed <- lmer(pcsk9 ~ day100 + (1 | id), data = zfp)
summary(m2_mixed)
## Linear mixed model fit by REML ['lmerMod']
## Formula: pcsk9 ~ day100 + (1 | id)
##    Data: zfp
## 
## REML criterion at convergence: -37.7
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -1.74945 -0.75872 -0.07708  0.45187  2.89238 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  id       (Intercept) 0.01080  0.1039  
##  Residual             0.02564  0.1601  
## Number of obs: 70, groups:  id, 7
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)  0.49866    0.05391   9.250
## day100       0.03285    0.01902   1.727
## 
## Correlation of Fixed Effects:
##        (Intr)
## day100 -0.586
confint(m2_mixed, parm = "day100", method = "Wald")
##               2.5 %     97.5 %
## day100 -0.004425286 0.07012432
# create two diagnostic plots to check the assumptions of the linear mixed model
par(mfrow = c(1, 2))

# plot residuals against fitted values to check for patterns and equal variance
plot(fitted(m2_mixed), resid(m2_mixed),
     xlab = "Fitted", ylab = "Residuals")
# add a horizontal reference line at zero
abline(h = 0, lty = 2)
# create a Q-Q plot to check whether the residuals are normally distributed
qqnorm(resid(m2_mixed))
qqline(resid(m2_mixed))

Both models have almost the same slope (0.033 from both). The mixed model’s 95% interval is -0.004 to 0.07, slightly narrower than the linear model’s interval (-0.012 to 0.077), but this can be accounted for by mouse to mouse differences.The interval still includes zero, so there is still no clear evidence of a steady upward drift after day 30.

Does cholesterol go down?

Each mouse has a before and an after value. Instead of only comparing “after” values, I compare groups at the same starting level.

# linear model of the LDL data to compare LDL cholesterol levels at day 30 between treatment groups
m3_ldl <- lm(ldl_30 ~ group + ldl_base, data = mice)
summary(m3_ldl)
## 
## Call:
## lm(formula = ldl_30 ~ group + ldl_base, data = mice)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -3.553 -1.365  0.087  1.307  4.750 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   20.02936    3.52635   5.680 1.78e-05 ***
## groupCas9     -5.20454    1.35064  -3.853  0.00107 ** 
## groupZFP-ETRs  0.08323    1.39898   0.059  0.95318    
## groupEvoETR-8 -4.61361    1.35392  -3.408  0.00295 ** 
## ldl_base      -0.22722    0.19968  -1.138  0.26932    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.339 on 19 degrees of freedom
## Multiple R-squared:  0.5729, Adjusted R-squared:  0.483 
## F-statistic: 6.373 on 4 and 19 DF,  p-value: 0.00199
# calculate 95% confidence interval
confint(m3_ldl)
##                    2.5 %     97.5 %
## (Intercept)   12.6486236 27.4101047
## groupCas9     -8.0314651 -2.3776074
## groupZFP-ETRs -2.8448709  3.0113351
## groupEvoETR-8 -7.4473949 -1.7798226
## ldl_base      -0.6451627  0.1907277
# generate diagnostic plots to check whether the linear model meets its assumptions
par(mfrow = c(2, 2))
plot(m3_ldl)

# the same model as above for total cholesterol
m3_chol <- lm(chol_30 ~ group + chol_base, data = mice)
summary(m3_chol)
## 
## Call:
## lm(formula = chol_30 ~ group + chol_base, data = mice)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -16.775  -4.180   0.147   5.153  13.287 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   101.7793    18.1821   5.598 2.13e-05 ***
## groupCas9     -22.2675     4.8519  -4.589   0.0002 ***
## groupZFP-ETRs   0.9622     4.8414   0.199   0.8446    
## groupEvoETR-8 -25.1471     4.8560  -5.179 5.34e-05 ***
## chol_base      -0.2773     0.2309  -1.201   0.2446    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 8.359 on 19 degrees of freedom
## Multiple R-squared:  0.7187, Adjusted R-squared:  0.6595 
## F-statistic: 12.14 on 4 and 19 DF,  p-value: 4.575e-05
veh_ldl <- predict(m3_ldl, newdata = data.frame(group = "Vehicle",
                                                ldl_base = mean(mice$ldl_base)))
coef(m3_ldl)["groupEvoETR-8"] / veh_ldl * 100   # % difference for EvoETR-8
## groupEvoETR-8 
##     -28.65525
coef(m3_ldl)["groupCas9"]     / veh_ldl * 100   # % difference for Cas9
## groupCas9 
## -32.32552

Analysis: After adjusting for each mouse’s starting LDL-C, mice that received the improved epigenetic editor (EvoETR-8) had LDL-C about 4.6 mg/dL lower than untreated mice 30 days after treatment (95% CI −7.5 to −1.8; p = 0.003). Mice that received Cas9 gene editing had LDL-C about 5.2 mg/dL lower (95% CI −8.0 to −2.4; p = 0.001). These values are roughly 29% and 32% lower than the untreated mice LDL-C levels (about 16 mg/dL), which match the values in the paper. In contrast, the ZFP-ETR editor produced no detectable change in LDL-C (95% CI −2.8 to 3.0; p = 0.95), even though it lowers PCSK9. Thus, the improved editor (EvoETR-8) matches the results of the Cas9 treatment, which matches what the news article mentions about the use of epigenetic editing being a comperable way to treat disease. This model compares each group only to untreated mice, so I cannot say whether EvoETR-8 and Cas9 differ from each other. However, with only six mice per group and cholesterol measured only up to day 30, these data cannot show how long the cholesterol drop lasts.

Does lower PCSK9 lead to lower LDL-C?

# linear model of LDL at day 30 and pcsk9 at day 30
m4a <- lm(ldl_30 ~ pcsk9_30, data = mice)
# add treatment group to see if lower pcsk9 still means lower LDL in each treatment
m4b <- lm(ldl_30 ~ pcsk9_30 + group, data = mice)
# coefficient table for first model
summary(m4a)$coefficients
##              Estimate Std. Error   t value     Pr(>|t|)
## (Intercept) 10.063604  0.9241692 10.889352 2.506169e-10
## pcsk9_30     6.774719  1.4784730  4.582241 1.455179e-04
# 95% confidence interval for first model
confint(m4a)
##                2.5 %    97.5 %
## (Intercept) 8.146994 11.980214
## pcsk9_30    3.708554  9.840884
# coefficient table for second model
summary(m4b)$coefficients
##                 Estimate Std. Error     t value   Pr(>|t|)
## (Intercept)   10.5757592   4.220072  2.50606109 0.02146429
## pcsk9_30       5.5909065   4.113587  1.35913172 0.19002020
## groupCas9     -1.0913673   3.281092 -0.33262319 0.74306064
## groupZFP-ETRs  1.8150545   2.067232  0.87801209 0.39090522
## groupEvoETR-8 -0.2539152   3.396309 -0.07476211 0.94118553
# 95% confidence interval for second model
confint(m4b)
##                   2.5 %    97.5 %
## (Intercept)    1.743046 19.408472
## pcsk9_30      -3.018930 14.200743
## groupCas9     -7.958772  5.776037
## groupZFP-ETRs -2.511711  6.141820
## groupEvoETR-8 -7.362472  6.854641
# plot comparing PCSK9 levels to LDL levels in mice 
ggplot(mice, aes(x = pcsk9_30, y = ldl_30, colour = group)) +
  geom_point(size = 3) +
  geom_smooth(aes(group = 1), method = "lm", se = FALSE, colour = "black") +
  labs(x = "PCSK9 at day 30 (1.0 = untreated)", y = "LDL-C at day 30 (mg/dL)",
       colour = "Group", title = "LDL-C and PCSK9 levels in each mouse")

Ananlysis: Across all 24 mice, higher PCSK9 means higher LDL-C (slope 6.8 mg/dL per 1.0 unit; 95% CI 3.7 to 9.8). However, this relationship could be explained by treatment groups. After accounting for treatment groups, the slope was similar (slop 5.6 mg/dL per 1.0 unit; 95% CI −3.0 to 14.2). So the data are consistent with PCSK9 lowering leading to lower LDL-C. Treatment lowers both PCSK9 and LDL-C, so the two levels are confounded with which treatment a mouse got.

Is there more methylation at Pcsk9?

# average methylation across the 45 CpG sites for each mouse (n = 3 per group) and then compare groups.
meth_mouse <- meth %>%
  group_by(group, id) %>%
  summarise(mean_meth = mean(meth))

# linear model for methylation
m5 <- lm(mean_meth ~ group, data = meth_mouse)
summary(m5)
## 
## Call:
## lm(formula = mean_meth ~ group, data = meth_mouse)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3.5394 -0.2749  0.0494  1.1338  3.2806 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      2.434      1.523   1.599  0.16102    
## groupZFP-ETRs   11.553      2.153   5.365  0.00172 ** 
## groupEvoETR-8   23.553      2.153  10.937 3.47e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.637 on 6 degrees of freedom
## Multiple R-squared:  0.9522, Adjusted R-squared:  0.9363 
## F-statistic: 59.82 on 2 and 6 DF,  p-value: 0.0001089
# 95% confidence interval
confint(m5)
##                   2.5 %    97.5 %
## (Intercept)   -1.291734  6.160235
## groupZFP-ETRs  6.284083 16.822759
## groupEvoETR-8 18.283688 28.822364
# mean methylation (%) of each group and average methylation of the mice in that group
mm <- meth_mouse %>% group_by(group) %>% summarise(m = mean(mean_meth))
get_m <- function(g) mm$m[mm$group == g]
mm
## # A tibble: 3 × 2
##   group        m
##   <fct>    <dbl>
## 1 Vehicle   2.43
## 2 ZFP-ETRs 14.0 
## 3 EvoETR-8 26.0

Vehicle mice average about 2.43% methylation across this region, ZFP-ETR mice 14% and EvoETR-8 mice 26%. the EvoETR-8 group has about 1.9 times as much methylation as the ZFP-ETR group, close to the number reported in the paper. Since three are 3 mice per group the confidence intervals are wide.

Summary

Question Model Answer
1. Does one dose lower PCSK9? pcsk9 to group (day 330) Yes, ZFP-ETRs about 38% lower than vehicle
2. Does the effect last? pcsk9 to day (ZFP-ETR mice, day 30 onward) Partly, PCSK9 stays well below normal but no clear steady drift (slope CI includes 0)
3. Does cholesterol fall? LDL at day 30 to group and LDL baseline EvoETR-8 -29%, Cas9 -32%; ZFP-ETRs no clear change
Mechanism mean methylation per group Yes, more methylation in treated mice, most in EvoETR-8

Connection to NY Times article: In these mice, one injection of an epigenetic editor lowered PCSK9 for almost a year and changed methylation levels at the Pcsk9 gene. The improved editor (EvoETR-8) also lowered cholesterol about as much as the Cas9 approach. There were two caveats: the silencing was partial rather than complete, and cholesterol was only followed in this study for 30 days.

Limitations

  • small sample size: 3 to 7 mice per group
  • Normalization: PCSK9 is relative to vehicle at each time point
  • Short cholesterol follow-up: Only measured til day 30.

References

Cappelluti, M. A., Mollica Poeta, V., Valsoni, S., Quarato, P., Merlin, S., Merelli, I., & Lombardo, A. (2024). Durable and efficient gene silencing in vivo by hit-and-run epigenome editing. Nature, 627(8003), 416–423. https://doi.org/10.1038/s41586-024-07087-8 * data from this article was used for analysis Ivashchenko, V., de Groot, M., Derks, R., den Ouden, A., Khazeeva, G., van den Heuvel, S., Timmermans, R., Corominas Galbany, J., Pfundt, R., Hofste, T., Yntema, H., Vissers, L., Hoischen, A., Hampstead, J., & Gilissen, C. (2026). Genome-wide methylation detection and episignature analysis using PacBio long-read sequencing. Genome medicine, 18(1), 11. https://doi.org/10.1186/s13073-025-01506-9 Nuñez, J. K., Chen, J., Pommier, G. C., Cogan, J. Z., Replogle, J. M., Adriaens, C., Ramadoss, G. N., Shi, Q., Hung, K. L., Samelson, A. J., Pogson, A. N., Kim, J. Y. S., Chung, A., Leonetti, M. D., Chang, H. Y., Kampmann, M., Bernstein, B. E., Hovestadt, V., Gilbert, L. A., & Weissman, J. S. (2021). Genome-wide programmable transcriptional memory by CRISPR-based epigenome editing. Cell, 184(9), 2503–2519.e17. https://doi.org/10.1016/j.cell.2021.03.025 Tay, Y. L., Thomson, S. B., Hnatova, S., Ng, S., Teo, S. R., McCallum, R., Sim, B., Tarantini, L., Tai, F. L., Bollati, V., Loh, M., Hayden, M. R., Leavitt, B. R., & Pouladi, M. A. (2026). Silencing of human HTT by targeted CRISPR/dCas9-mediated epigenetic editing. Journal of Huntington’s disease, 15(3), 399–407. https://doi.org/10.1177/18796397251415368 Tremblay, F., Xiong, Q., Shah, S. S., Ko, C. W., Kelly, K., Morrison, M. S., Giancarlo, C., Ramirez, R. N., Hildebrand, E. M., Voytek, S. B., El Sebae, G. K., Wright, S. H., Lofgren, L., Clarkson, S., Waters, C., Linder, S. J., Liu, S., Eom, T., Parikh, S., Weber, Y., … Jaffe, A. B. (2025). A potent epigenetic editor targeting human PCSK9 for durable reduction of low-density lipoprotein cholesterol levels. Nature medicine, 31(4), 1329–1338. https://doi.org/10.1038/s41591-025-03508-x