rggplot2plotregressiontapply

Plot bin averaged values with error bars in R


I have a dataframe with three columns "DateTime", "T_ET", and "LAI". I want to plot T_ET (on y-axis) against LAI (on x-axis) along with 0.1-bin LAI averaged values of T_ET on the same plot something like below (Wei et al., 2017):enter image description here

In above figure, y-axis is T_ET or T/(E+T), x-axis is LAI, red open diamonds with error bars are 0.1-bin LAI averaged of black points and the standard deviation, solid line is a regression of the individual data points (estimated from the bin averages), n is available data points. Dash lines are 95% confidence bounds.

How can I obtain the plot similar to above plot? Please find the sample data using the following link: file

or use following sample data:

df <- structure(list(DateTime = structure(c(1478088000, 1478347200, 1478692800, 1478779200, 1478865600, 1478952000, 1479124800, 1479211200, 1479297600, 1479470400), class = c("POSIXct", "POSIXt"), tzone = "GMT"), 
                     T_ET = c(0.996408350852751, 0.904748351479432, 0.28771236118773, 0.364402232484906, 0.452348409759872, 0.415408041501318, 0.629291202120187, 0.812083112145703, 0.992414777441755, 0.818032913071265), 
                     LAI = c(1.3434, 1.4669, 1.6316, 1.6727, 1.8476, 2.0225, 2.3723, 2.5472, 2.7221, 3.0719)), 
                row.names = c(NA, 10L), 
                class = "data.frame")

Solution

  • You can do this directly while plotting via stat_summary_bin(). By default, the geom associated with this would be the pointrange geom and uses mean_se(). bins= controls the number of bins, but you can also supply binwidth=. Note that with the pointrange geom, fatten controls the size of the central point:

    ggplot(df, aes(LAI, T_ET)) + geom_point() + theme_classic() +
      stat_summary_bin(bins=3, color='red', shape=5, fatten=5)
    

    enter image description here

    Your sample data is a little light, so here's another example via the diamonds dataset. Here, I'm constructing the same look as the example plot you show by combining the errorbar and poing geom. Please note that apparently setting the width of the errorbar doesn't work correctly with stat_summary_bin().

    ggplot(diamonds, aes(carat, price)) + geom_point(size=0.3) +
      stat_summary_bin(geom='errorbar', color='red', bins=12, width=0.001) +
      stat_summary_bin(geom='point', size=3, shape=5, color='red', bins=12) +
      theme_classic()
    

    enter image description here

    EDIT: Showing Regression for Binned Data

    As indicated in the comments, drawing a regression line based on the binned data and not the original data is possible, but not through the stat_summary_bin() function unless you are okay to use loess. If you're looking for linear regression, you'll need to bin the data outside of ggplot, then plot the regression on the binned data.

    The reason for this is probably by design. It's inherently not a good idea to draw a regression line (a way of summarizing data) that is based on summarized data. Regardless, here's one way to do this via the diamonds dataset. We can use the cut() function to cut into separate bins, then summarize the data on those binned values. Due to the way the cut() function labels the output, we have to create our own labels. Since we're cutting into 12 equal pieces in this example, I'm creating 12 evenly-spaced positions on the x axis for our data values to sit into - this may be different in your case, just take care you label according to what the data represents and what makes the most statistical sense.

    df <- diamonds
    
    # setting interval labeling
    bin_width <- diff(range(df$carat)/12)
    bin_labels <- c((range(df$carat)[1] + (bin_width/2))+(0:11*bin_width))
    
    # cutting the data
    df$bins <- cut(df$carat, breaks=12, labels=bin_labels)
    df$bins <- as.numeric(levels(df$bins)[df$bins])    # convert factor to numeric
    
    ggplot(diamonds, aes(carat, price)) + geom_point(size=0.3) +
      stat_summary_bin(geom='errorbar', color='red', bins=12, width=0.001) +
      stat_summary_bin(geom='point', size=3, shape=5, color='red', bins=12) +
      geom_smooth(data=df, aes(x=bins), method='lm', color='blue') +
      theme_classic()
    

    enter image description here

    Note that the regression line above is weighting all binned values equally. This is generally not a good idea unless your data is spaced evenly among the dataset. I'd still recommend if you're going to draw a regression line, have it linked to the original data, which is much more representative of the reality within your data. That would look like this:

    ggplot(diamonds, aes(carat, price)) + geom_point(size=0.3) +
      stat_summary_bin(geom='errorbar', color='red', bins=12, width=0.001) +
      stat_summary_bin(geom='point', size=3, shape=5, color='red', bins=12) +
      geom_smooth(method='lm', color='green') +
      theme_classic()
    

    enter image description here

    When it comes down to it, drawing a regression line for binned data is summarizing the summarized data rather than summarizing your original data. It's statistical heresay, so use at your own risk. But if you simply must for whatever strange reason... I can't stop you. ;)