# First we can massage the default co2 timeseries data into a data frame co2frame = data.frame(year = floor(time(co2)), month = cycle(co2), value = co2) # now calculate min and max values per year co2summary = data.frame(aggregate(value~year,co2frame,min)[c("year","value")],aggregate(value~year,co2frame,max)["value"]) # add a column with the decade for each year co2summary = cbind(co2summary,floor(co2summary$year/10)*10) # make sure the names of the columns make sense names(co2summary)=c("year","min","max","decade") # set the size of the graph dev.new(width=3.5,height=3.5) # set the margins to make the graph look a little better par( oma=c(0,0,0,0), mar=c(4,4,1,1) ) lineswarm( max ~ decade, ## This formula defines the first independent variable that will be plotted as a point, and the grouping variable. data = subset(co2summary,decade>1950), pwdropline = min, ## this is the second independent variable, which defines the endpoint of the line. This is not plotted as a point. pch = NaN, # This turns off plotting the first point because it isn't neccesary in this graph. The default is a open circle. cex = 0.5, # This is point size. It controls packing density as well, which is why I modify the default here lwd = 2, # line width. col = "gray35", # dark grey lines ylim = c(315, 370), ylab = "CO2 Uptake Range", xlab = "Decade" )