1
votes

I am writing a function in order to filter out datapoints out of my plot based on a linear equation. I currently have the following function (with a different function within it):

 MD_filter<- function(dataframe, mz_col){ 
    #In-function MD calculation
    MZ<- mz_col
    MZR<- trunc(mz_col, digits = 0)#Either floor() or trunc() can be used for this part.
    MD<- as.numeric(MZ-MZR)
    dataframe<- dataframe%>%
      dplyr::mutate(MD)%>%
      dplyr::select(MD, everything())
    #fit data to m/z defect maxima equation
    f<- function(x){#This could be problem `1`, maybe resolved by leaving x.... 
      y<-0.00112*x + 0.01953
      return(y)}
    fit<-as.data.frame(t(apply(dataframe,1,f)))# t() transforms df to matrix...?
    filtered<-dataframe[which((dataframe$MD<= fit$MZ)),]
    #keep rows in dataframe if MD is less than or equal to fitted value (mz after equation)
    #As "fit" calculated the maximum MD value for each MZ value in the MZ column, we subset fit$MZ, as this contains the dataframe MZ values.
    #The MD calculated at the very start, needs to be lower than the equivalent MZ value of the fitted dataframe.
    filtered<-write.table(filtered,"feature_list_mz_defect_filtered.txt",sep="\t",col.names=NA)
    #Now we have pre filter dataframe (dataframe) and post filter df (filtered)
    #2 Different plots: (highlight to be removed as well, so we need a 3rd eventually)
    MD_plot<- ggplot(dataframe, aes(x= MZ, y = MD)) +
      geom_point() +#THE FOLLOWING PART DOES NOT WORK YET
      ggtitle(paste("Unfiltered MD data - ", dataframe))
    #stat_smooth(method="lm", se=FALSE)-> For linear line through the plot, but may not be necessary to show
    
    return(MD_plot)#While I do get a plot, I have not yet gotten the equation. I could use my earlier approach maybe.
    
    MD_plot_2<- ggplot(filtered, aes(x= MZ, y = MD)) +#Filtered is basically the second dataframe, 
      #which subsets datapoints with an Y value (which is the MD), below the linear equation MD...
      geom_point() +#THE FOLLOWING PART DOES NOT WORK YET
      ggtitle(paste("Filtered MD data - ", dataframe))
    #stat_smooth(method="lm", se=FALSE) -> For linear line through the plot, but may not be necessary to show
    
    return(MD_plot_2)
    
  }

The function works as follows: The argument inputs are a dataframe and a specific column inside that dataframe which I call the mz_col. From this column a second column, the MD column, is generated.

From here on out I want to make two plots: ggplot 1: A plot with the mz_col (MZ) values on the X axis and the MD values on the Y axis

ggplot 2: EXACTLY the same as ggplot 1, but I want to filter out the datapoints if MD exceeds the linear equation y<-0.00112*x + 0.01953 (as is visible in the code). This linear line is basically my maximum filter limit in the plot, everything above this I want gone.

I've tried many different solutions. I swapped the "x" argument with mz_col among many other solutions such as trying to use plot() instead of ggplot. Currently I'm getting no plot, but I do get this:

No plot is plotted, but there is a plot of numbers visible.

Basically my question is: How do I solve my function, so I can get my two plots? The first plot is no real issue, this already works, but the second plot just won't filter out datapoints based on my linear equation.

Thanks in advance! I'm quite new to SO and R, so I apologize if anything is unclear. Please let me know if any clearification is needed and thanks in advance for all the help!

Reproducable sample data:

structure(list(mz = c(446.0394, 346.043, 199.0446, 199.0464, 97.057, 657.0595, 879.0606, 978.0631, 199.0684, 199.0707, 199.0724, 86.0748, 199.0761, 196.0789, 199.0819, 199.0852, 199.0878, 199.089, 908.0935, 147.0958, 199.0999,199.1299, 199.1322, 199.1384, 199.1398, 199.1434, 124.1475, 199.1513, 187.156, 199.1686, 199.1766, 199.1797, 199.1879, 199.1924, 187.1959, 479.1981, 169.1999, 109.2049, 399.2092, 299.2125, 159.2146, 199.2242, 356.2405, 69.2423, 956.4337, 978.5537, 199.5695, 676.5769, 199.5851, 500.6021, 260.6039, 270.6081, 200.6114, 200.6131, 200.6172, 200.6221, 
200.6315, 200.6402, 200.6476, 200.766, 200.8591, 200.8732, 200.8768, 
200.89, 200.8937, 200.8972, 200.9067, 200.9127, 200.9147, 200.9231, 
200.9253, 200.9288, 200.9324, 200.935, 200.9468, 200.9515, 200.9536, 
200.9557, 200.9568, 200.9594, 200.9661, 200.968, 200.9729, 200.9745, 
200.9819, 200.9837, 200.9858, 200.9937)), row.names = c(NA, -88L), class = c("tbl_df", "tbl", "data.frame"))
1
Can you share a small reproducible example so we have something to run you function on? dput() is helpful to make a copy/pasteable version of an R object - or use a built-in dataset, or share code to simulate a little bit of fake data. The simpler you can make your example data, the clearer the problem will be. - Gregor Thomas
But also, at a glance - R functions end as soon as there is a return(). Instead of returning the first plot as soon as it's created, wait until the end and then return(list(MD_plot, MD_plot_2)) - Gregor Thomas
Voting to close for no response (after 2 hours) to cogent request for working example data. Posters are expected to monitor for comments and respond in a timely fashion. - IRTFM
I apologize for my late reply, it was already 2 AM where I lived when I posted this question and did not expect an answer this quickly. I am currently figuring out the use of dput I'll post my example data within 15 minutes. - Ama
structure(list(mz = c(446.0394, 346.043, 199.0446, 199.0464, 97.057, 657.0595, 879.0606, 978.0631, 199.0684, 199.0707, 199.0724, 86.0748, 199.0761, 196.0789, 199.0819, 199.0852, 199.0878, 199.089, 908.0935, 147.0958, 199.0999,199.1299, 199.1322, 199.1384, 199.1398, 199.1434, 124.1475, 199.1513, 187.156, 199.1686, 199.1766, 199.1797, 199.1879, 199.1924, 187.1959, 479.1981, 169.1999, 109.2049, 399.2092, 299.2125, 159.2146, 199.2242, 356.2405, 69.2423, 956.4337, 978.5537, 199.5695, 676.5769, 199.5851)), row.names = c(NA, -49L), class = c("tbl_df", "tbl", "data.frame")) - Ama

1 Answers

1
votes

I got a bit lost trying to follow your code, but based on your description, does the following work for you?

library(dplyr)
library(ggplot2)

MD_filter <- function(dataframe, mz_col, a = 0.01953, b = 0.00112){ 
  
  # rename column so that rest of function doesn't depend on inputted column name
  dataframe[["MZ"]] <- dataframe[[mz_col]] 
  
  # process dataframe
  dataframe <- dataframe %>%
    select(MZ) %>%
    mutate(MD = MZ - trunc(MZ, digits = 0),
           MD.limit = a + b*MZ)
  
  p1 <- ggplot(dataframe,
               aes(x = MZ, y = MD)) +
    geom_point() +
    geom_smooth(method = "lm", se = F) +
    ggtitle("Unfiltered MD data")
  
  p2 <- p1 %+% filter(dataframe, MD <= MD.limit) +
    expand_limits(y = range(dataframe[["MD"]])) + # optional (if you want same 
                                                  # y-axis range for both plots)
    ggtitle("Filtered MD data")

  cowplot::plot_grid(p1, p2, nrow = 1)
}

Data & usage

dd <- structure(list(mz = c(
  446.0394, 346.043, 199.0446, 199.0464, 97.057, 657.0595, 879.0606, 
  978.0631, 199.0684, 199.0707, 199.0724, 86.0748, 199.0761, 196.0789, 
  199.0819, 199.0852, 199.0878, 199.089, 908.0935, 147.0958, 199.0999,
  199.1299, 199.1322, 199.1384, 199.1398, 199.1434, 124.1475, 199.1513, 
  187.156, 199.1686, 199.1766, 199.1797, 199.1879, 199.1924, 187.1959, 
  479.1981, 169.1999, 109.2049, 399.2092, 299.2125, 159.2146, 199.2242,
  356.2405, 69.2423, 956.4337, 978.5537, 199.5695, 676.5769, 199.5851,
  500.6021, 260.6039, 270.6081, 200.6114, 200.6131, 200.6172, 200.6221, 
  200.6315, 200.6402, 200.6476, 200.766, 200.8591, 200.8732, 200.8768, 
  200.89, 200.8937, 200.8972, 200.9067, 200.9127, 200.9147, 200.9231, 
  200.9253, 200.9288, 200.9324, 200.935, 200.9468, 200.9515, 200.9536, 
  200.9557, 200.9568, 200.9594, 200.9661, 200.968, 200.9729, 200.9745, 
  200.9819, 200.9837, 200.9858, 200.9937)),
  row.names = c(NA, -88L),
  class = c("tbl_df", "tbl", "data.frame"))

MD_filter(dd, "mz")
# MD_filter(dd, "mz", a = 0.02, b = 0.001) # if you want to change the limit

plot