移除局部最小值左右两侧的数据

Remove data to the left and right of local minima

我有很多测量结果,我得到的数据看起来像这样:

# Generate example data
x <- 1:100
y <- 100*(1-exp(-0.3*x))
x2 <- 101:200
y2 <- rev(y)
df <- data.frame("x" = c(x, x2),
                 "y" = c(y, y2))
df$x <- df$x + 50
rm(x, x2, y, y2)
x <- 1:50
y <- 25.91818
x2 <- 251:300
y2 <- 25.91818
df2 <- data.frame("x" = c(x, x2),
                  "y" = c(y, y2))
rm(x, x2, y, y2)
df <- rbind(df, df2)
rm(df2)

如果我绘制它,我可以看到有最左边和最右边的局部最小值。

library(ggplot2)
p <- ggplot(df, aes(x,y))+
  geom_line()+
  geom_point(data = data.frame("x" = c(50, 250), "y" = c(25.91818, 25.91818)),
             mapping = aes(x, y), colour = "red")+
  scale_y_continuous(limits = c(0, 101))
p + annotate("text", label = "minimum 1", x = 50, y = 20) +
  annotate("text", label = "minimum 2", x = 250, y = 20)

我想做的是trimminimum 1左边和minimum 2右边的那些数据。这不是非常简单,因为这两点之间也可能存在局部最小值,因为真实数据看起来并不理想。我还需要将这个过程应用于许多样本,但我认为这可能是微不足道的,因为我可以使用例如dplyrgroup_by().

我很幸运地使用 ggpmisc 包绘制了局部最小值,但我不确定如何使用它来实际对我的数据进行子集化。为了清楚起见,我在下面包含了执行此操作的代码,并且使用真实数据看起来更好一些:

library(ggpmisc)
p2 <- ggplot(df, aes(x, y))+
    geom_line()+
    ggpmisc::stat_peaks(col="red", span=3)
p2

我希望这是清楚的,我很乐意澄清任何问题。提前谢谢你。

您可以按照以下步骤执行此操作:

  1. 根据 x 对数据进行排序 co-ordinates
  2. 在您排序的数据上,找到 y co-ordinates 的 diff,对于两端的平坦部分(以及任何平坦部分),它将为 0(或接近 0)之间)
  3. 从左边开始,找到 diff 不为零(或至少高于最小阈值)的第一个点。将该索引存储为名为 left
  4. 的变量
  5. 从右边开始,找到 diff 不为零(或至少高于最小阈值)的第一个点。将该索引存储为名为 right
  6. 的变量
  7. 对您的数据框进行子集化,使其仅包含行之间的数据 left:right

因此,在您的示例中,我们将:

# Define a minimal threshold above which we are not at the minimum line
minimal_change <- 1e-6

df    <- df[order(df$x),]                                           # Step 1
left  <- which(diff(df$y) > minimal_change)[1]                      # Step 2
right <- nrow(df) - which(diff(rev(df$y)) > minimal_change)[1] + 1  # Step 3
df    <- df[left:right, ]                                           # Step 4

现在我们可以绘制结果:

ggplot(df, aes(x, y)) +
  geom_line()+
  geom_point(data = data.frame("x" = c(50, 250), "y" = c(25.91818, 25.91818)),
             mapping = aes(x, y), colour = "red") +
  scale_y_continuous(limits = c(0, 101)) +
  scale_x_continuous(limits = c(0, 300))