挑选出符合模式的峰

Mik*_*ike 6 r ggplot2

我得到了 x 轴上的时间(秒)和 y 轴上的强度(以相对荧光单位或 rfu 表示)的数据。它是通过观察 DNA 片段通过相机而生成的——DNA 片段越大,时间就越长。有 23 个已知大小的片段(以 DNA 碱基对单位 bp 为单位),因此应该有 23 个峰。由于我知道 DNA 片段的大小(以 bp 为单位),因此我想使用线性模型重新校准 x 轴,从时间(秒)到碱基对 (bp)。

不幸的是,数据中存在大量噪声,会产生虚假峰值。自信地区分真假的唯一方法是假碱基与 DNA 碱基对中的预期模式不符。

我已在此链接中的名为 demo 的数据框中提供了一个示例的数据。不幸的是它太大了,无法粘贴到下面。

https://1drv.ms/t/s!AvBi5ipmBYfrhf0v_kvWuN2foLyBgg?e=RWfdXZ

我可以如下选出所有峰值。

library(ggplot2)
library(ggpmisc)
ggplot(demo, aes(x=time, y=rfu)) +
geom_line(size=0.1) +
stat_peaks(col = "red", span=5, ignore_threshold=0.1) +
theme_bw()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

然而,这些是预期的 DNA 片段大小(以 bp 为单位):

ladder <- c(50, 75, 100, 125, 150, 200, 250, 300, 350, 400, 450, 475, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000)
Run Code Online (Sandbox Code Playgroud)

这是一个选出了正确峰值的图:

在此输入图像描述

有没有办法让峰值查找器(例如 stat_peak 或 ggpmisc:::find_peaks)在选择峰值以实现第二个图时考虑峰值模式?

附录:假峰可能与真峰处于相同的 rfu 范围内,因此峰高不能用于排除它们(请参阅下面不同示例中的示例)

在此输入图像描述

Jon*_*ing 6

对于给定的数据,我们可以通过查找 1000 到 6000 之间的局部最大值来隔离峰值。这会识别 23 个峰值。

我使用该slider包来识别 21 bp(时间 - 10 到时间 +10)范围内的最大值,然后排除rfu1000-6000 范围之外的点或与之前的点匹配的点。

library(tidyverse); library(slider)
demo %>%
  mutate(peak = rfu == slider::slide(
    rfu, max, .before = 10, .after = 10) & 
      rfu > 1000 & rfu < 6000 & rfu != lag(rfu)) %>%
  ggplot(aes(time, rfu, color = peak, alpha = peak)) +
  geom_point() 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我探索了是否有可能找到一个强力的“最佳拟合”,这可能会使它更加自动化,并且对与底层信号处于相同范围内的噪声更加鲁棒。我确信这是可能的,但它比我预期的更困难,因为“时间拉伸”在整个范围内变化,因此具有两个参数的简单偏移+缩放模型是不够的。梯子和数据之间的“时间延伸”逐渐变化,从开始时略高于 10 倍(即梯子上从 50 到 75 的 25 diff 转换为数据 1578 到 1831 中的 253 diff)到结束时的 5 倍以下。

在这种情况下,看起来二次拟合可能效果很好,但这可能无法转化为时间扭曲不同的其他数据。

在此输入图像描述

在此输入图像描述

如果运行之间的畸变完全一致,则使用内置畸变定义梯子可能更有用,如下面的“ladder_scaled”列。那么问题将简化为找到最适合数据的单个偏移值,在您的示例中为+1528。

ladder_scaled <- tibble::tribble(
      ~ladder, ~ladder_scaled,
   50 ,          50,
   75 ,         303,
  100 ,         557,
  125 ,         811,
  150 ,        1068,
  200 ,        1580,
  250 ,        2104,
  300 ,        2630,
  350 ,        3159,
  400 ,        3684,
  450 ,        4204,
  475 ,        4451,
  500 ,        4705,
  550 ,        5203,
  600 ,        5683,
  650 ,        6143,
  700 ,        6574,
  750 ,        6983,
  800 ,        7357,
  850 ,        7701,
  900 ,        8010,
  950 ,        8283,
  1000,         8523
  )
Run Code Online (Sandbox Code Playgroud)

如果我们可以依赖运行之间一致的时间对齐,我们就可以解决时序偏移问题,从而提供与数据的最佳对齐,即使我们的数据中存在重叠的噪声峰值。关于这个特定示例的一个棘手的事情是,梯子“签名”并不是非常独特 - 梯子上大多数步骤之间的间距相似,因此即使您偏移了一个或一个,您也可以很好地适应大多数丢失的功能。梯子的两个“台阶”。

这是一种方法,我可以强制拟合一堆偏移值。我依赖于这样的假设(也许没有根据):梯子具有一致的时间长度,并且唯一需要解决的变量是时间偏移。我相信,如果不能假设这一点,问题就会变得更加困难。

首先,我使用了更广泛的合理值,其中除了 23 个真实峰值之外,还包括 6 个噪声峰值:

demo %>%
  mutate(peak = rfu == slider::slide(
    rfu, max, .before = 10, .after = 10) & 
      rfu > 500 & rfu < 30000 & rfu != lag(rfu)) -> demo_labels
# includes 29 "peaks": 23 real + 6 noisy
Run Code Online (Sandbox Code Playgroud)

然后,我从 0:5000 开始交叉一系列可能的偏移,并将每个峰值模糊连接到 200 个时间单位内可能的阶梯峰值。对于每个可能的偏移和峰值,我选择最接近的拟合,然后对于每个可能的偏移,我选择 23 个最接近的拟合。最后,我绘制了每个偏移值的最差对齐情况。这表明在原始数据中,大约 1528 的偏移量提供了最佳拟合。但这个特殊的“阶梯签名”的一个困难是,1976 年的偏移量大约高出 450,通过这种衡量标准看起来并没有特别糟糕,尽管它肯定是错误的。因此,可能需要更多的领域知识来识别比“最差拟合”更好的函数来选择良好的匹配。

library(fuzzyjoin)
demo_labels %>%
  filter(peak) %>%
  crossing(offset = seq(0, 5000, by = 2)) %>%
  mutate(time_adj = time - offset) %>%
  distance_left_join(ladder_scaled, 
                  by = c("time_adj" = "ladder_scaled"),
                  max_dist = 200) %>%
  mutate(time_error = time_adj - ladder_scaled) %>% 
  group_by(offset, time_adj) %>%
  slice_min(abs(time_error)) %>%
  group_by(offset) %>%
  slice_min(abs(time_error), n = 23) %>%  ### pick the 23 best fits %>%
  summarise(worst_error = max(abs(time_error)),
            matching_peaks = n()) %>%
  arrange(worst_error) %>% 
  ggplot(aes(offset, worst_error, alpha = matching_peaks == 23)) +
  geom_point()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述