Within R, it is possible to generate graphics that are composed from two or more separate visuals. Let us build a graphic that displays several pieces of information about our fire attack strategy simultaneously:
> #use par(mfcol) to prepare the graphic window to display multiple graphics simultaneously > #the mfcol argument receives a vector indicating the number of rows and columns to reserve for separate graphics in the graphics window > #here, we want 4 total graphics, so use a 2x2 vector > par(mfcol = c(2,2)) > #note that a blank graphic window will open > #if this window is closed, your graphic window will default back to displaying a single visual > #if it remains open, your graphic window will continue to add visuals to the 2x2 grid as they are created
> #create the first graphic by duplicating the steps taken in the Building a graphic activity > #this scatterplot depicted the number of Shu and Wei soldiers engaged in past fire attacks > plot(x = 0, y = 0, main = buildFireSoldiersMain, xlab = buildFireSoldiersLabX, ylab = buildFireSoldiersLabY, xlim = buildFireSoldiersLimX, ylim = buildFireSoldiersLimY, type = buildFireSoldiersType, xaxt = buildFireSoldiersAxtX, yaxt = buildFireSoldiersAxtY) > axis(side = 1, at = c(0:30), las = 0) > axis(side = 2, graphicscreating, with multiple visualsat = c(1000, 5000, 10000, 20000, 30000, 40000, 50000), las = 1) > points(x = pointsFireShuSoldiersDataX, y = pointsFireShuSoldiersDataY, type = pointsFireShuSoldiersType, col = pointsFireShuSoldiersColor) > points(x = pointsFireWeiSoldiersDataX, y = pointsFireWeiSoldiersDataY, type = pointsFireWeiSoldiersType, col = pointsFireWeiSoldiersColor, pch = pointsFireWeiSoldiersPch) > legend(x = 0, y = 50000, legend = c("Shu", "Wei"), fill = c(pointsFireShuSoldiersColor, pointsFireWeiSoldiersColor))
> #add a second chart that depicts the duration of past battles > #create new variables where necessary > #otherwise reuse the variables from our initial graphic > #basic parameters > buildFireDurationMain <- "Duration in Days" > buildFireDurationLabY <- "Days" > buildFireDurationLimY <- c(0, 14) > #use the plot(...) function to create a foundation for the graphic > plot(x = 0, y = 0, main = buildFireDurationMain, xlab = buildFireSoldiersLabX, ylab = buildFireDurationLabY, xlim = buildFireSoldiersLimX, ylim = buildFireDurationLimY, type = buildFireSoldiersType, xaxt = buildFireSoldiersAxtX, yaxt = buildFireSoldiersAxtY) > #axes > #x axis > axis(side = 1, at = c(0:30), las = 0) > #y axis > axis(side = 2, at = c(0:14), las = 1) > #use lines(...) to add data to the graphic graphicscreating, with multiple visuals> #add a line representing the duration in days for each battle > lineFireDurationDataX <- c(1:30) > lineFireDurationDataY <- subsetFire$DurationInDays > lineFireDurationType <- "o" > lineFireDurationWidth <- 1 > lineFireDurationColor <- "red" > lines(x = lineFireDurationDataX, y = lineFireDurationDataY, type = lineFireDurationType, lwd = lineFireDurationWidth, col = lineFireDurationColor) > #use abline(...) to add a horizontal line to the chart > #add a line representing the mean duration > lineFireDurationMeanWidth <- 3 > lineFireDurationMeanColor <- "blue" > abline(h = mean(lineFireDurationDataY), lwd = lineFireDurationMeanWidth, col = lineFireDurationMeanColor)
> #add a third chart that depicts the percentage of victorious fire attacks when the strategy is executed successfully > #basic parameters > buildFireResultMain <- "Result When Successfully Executed" > buildFireResultSlices <- c(length(subset(numericResultFire, numericExecutionFire == 1 & numericResultFire == 1)), length(subset(numericExecutionFire, numericExecutionFire == 1 & numericResultFire == 0))) > buildFireResultLabels <- paste(buildFireResultSlices / sum(buildFireResultSlices) * 100, "%", sep = "") > buildFireResultColors <- c("red", "blue") > #use the pie(...) function to create and display the pie chart > pie(x = buildFireResultSlices, labels = buildFireResultLabels, main = buildFireResultMain, col = buildFireResultColors) > #legend > legend(x = "topright", legend = c("Victory", "Defeat"), fill = buildFireResultColors, cex = 0.85)
> #add a fourth chart that compares the gold cost (in thousands) of the fire attack with the other battle methods > #get the raw cost of the various methods using comparable resources > goldCostFire <- functionGoldCost(2500, 225, 7) > goldCostAmbush <- functionGoldCost(meanShuSoldiersAmbush, 225, meanDurationAmbush) > goldCostHeadToHead <- functionGoldCost(meanShuSoldiersHeadToHead, 225, meanDurationHeadToHead) > goldCostSurround <- functionGoldCost(meanShuSoldiersSurround, 225, meanDurationSurround) > #basic parameters > #note that the bar heights are divided by 1000 so they are represented in thousands of gold > #presenting larger numbers in this manner is one way to keep our axes cleaner and our graphics more readable > buildCostHeight <- c(goldCostFire, goldCostAmbush, goldCostHeadToHead, goldCostSurround) / 1000 > buildCostMain <- "Cost Comparison by Method" > buildCostLabX <- "Gold Cost (in thousands)" > buildCostLimX <- c(0, 400) > buildCostLimY <- c(0, 5) > buildCostNames <- c("Fire", "Amb", "Head", "Sur") > buildCostColors <- rainbow(length(buildCostHeight)) > buildCostHoriz <- TRUE > #use the barplot(...) function to create and display the bar chart graphicscreating, with multiple visuals> barplot(height = buildCostHeight, main = buildCostMain, xlab = buildCostLabX, xlim = buildCostLimX, ylim = buildCostLimY, names = buildCostNames, col = buildCostColors, horiz = buildCostHoriz) > #legend > legend(x = 275, y = 2, legend = round(buildCostHeight * 1000, 0), fill = buildCostColors, title = "Exact Cost", cex = 0.75)
We built a custom visual that was composed from a set of four individual graphics.
Note that this section will only highlight the new or unique features that were encountered during this process. You should already be familiar with generating individual graphics and customizing their parameters from our previous work.
The par(mfcol)
command modifies the number of visuals that are displayed in the graphic window. By default, the graphic window displays a single visual. The mfcol
argument accepts a vector indicating the number of rows and columns of visuals to be displayed in the graphic window. For example:
> par(mfcol = c(3, 3))
The code would reserve space in the graphic window for nine visuals that would be displayed in a 3-row by 3-column grid. Note that the mfcol
vector does not have to be symmetrical. For instance, a 5 by 1 or 2 by 10 vector would also be acceptable.
Our par(mfcol)
command told our graphic window to display our visuals in a 2-row by 2-column grid:
> par(mfcol = c(2,2))
When par(mfcol)
is executed, a blank graphic window will open. It is important to keep this window open. As long as it remains open, all graphics generated by R will be added to the grid defined by par(mfcol)
. Once the graphic window is closed, it will default back to generating single visuals. At that point, par(mfcol)
can be used again to redefine the space of the graphic window.
Once the space of our graphic window was defined, we simply added new visuals one by one. Notice that this process is identical to creating individual graphics. The difference is that the graphic window will continue to add new visuals to the same space, rather than replacing the previous visual each time a new one is created. Thus, we are able to combine multiple visuals into a single graphic.
We started by building two charts from scratch, one depicting the soldiers engaged in fire attacks on a scatterplot, and one displaying the duration of fire attacks in a line chart. We then generated two highly customized charts, one depicting the result of fire attacks when successfully executed as a pie, and one comparing the cost of the battle methods on a bar chart. Ultimately, we arrived at a single graphic containing information from four separate visuals.
While creating the charts that composed our combined graphic, we encountered two notable items that deserve an explanation here.
The first occurred while making our fire attack duration line chart. You may have noticed that we drew a flat, horizontal line across the chart at the mean duration. To accomplish this, we used the abline(...)
function in a new way. Previously, we used abline(...)
to draw best fit lines on scatterplots in the Customizing a scatterplot section of this chapter. Here, we used the h
argument to define a point where a horizontal line should be drawn across our chart. By setting h
to the mean duration, we were able to visualize the average fire attack duration amidst the fluctuations experienced across each individual battle:
> abline(h = mean(lineFireDurationDataY), lwd = lineFireDurationMeanWidth, col = lineFireDurationMeanColor)
Note that the abline(...)
function also has a v
argument, which can be used to define a vertical line at any point along the chart. If h
and v
are defined together, an intersecting pair of horizontal and vertical lines will be drawn.
A complex code segment that we encountered while making our pie chart involved a series of nested functions:
> buildFireResultSlices <- c(length(subset(numericResultFire, numericSuccessfullyExecutedFire == 1 & numericResultFire == 1)), length(subset(numericSuccessfullyExecutedFire, numericSuccessfullyExecutedFire == 1 & numericResultFire == 0)))
Here, we created our pie's slices using a combination of the c(...), length(...)
, and subset(...)
functions. Individually, these are all familiar. When combined, they can look confusing at first glance. The key to reading nested functions is to work from the innermost function to the outermost function. The key to creating nested functions is to remember to close your parenthesis in the opposite order that they are opened. For example, while the c(...)
function was the first opened in our code, it was the last one that was closed. The following example illustrates this principle:
> function1(function2(function3()))
In nested functions, the innermost function is always executed first, followed by its surrounding function, and so on.
As with other programming languages, functions in R can be nested at virtually unlimited levels. On one hand, nesting makes our code more compact and efficient. On the other hand, excessive nesting makes our code unreadable and undesirably complex. Take these points into consideration when nesting functions.
par(mfcol)
command?a. When executed, par(mfcol)
will launch a new graphic window.
b. Closing the graphic window will cancel the effects of the most recently executed par(mfcol)
command.
c. The mfcol
argument only accepts symmetrical vectors.
d. The par(mfcol)
can be executed again to redefine the space of the graphic window.
> abline(h = 4, v = 10)
a. A horizontal line would be drawn at 4 on the y-axis.
b. A vertical line would be drawn at 10 on the x-axis.
c. A horizontal line would be drawn at 4 on the y-axis and a vertical line would be drawn at 10 on the x-axis.
d. No lines would be drawn.
c(...)
function inside the sum(data)
function inside the mean(data)
function?a. c(sum(data), mean(data))
b. mean(sum(c(...)))
c. c(sum(mean(data)))
d. mean(sum(data), c(...))