sinhrks / ggfortify

Define fortify and autoplot functions to allow ggplot2 to handle some popular R packages.
Other
527 stars 65 forks source link

ggbiplot(): add support for user-settable arrow heads for loadings and user-settable position for loading labels #213

Closed aphalo closed 3 years ago

aphalo commented 3 years ago

Constraints in current version ggbiplot() and code changes done to remove them: 1) grid::arrow() definition used for plotting loadings is hard coded, and the large-sized arrow heads can clutter the plots, solution: add parameter loadings.arrow and set its default to the previously hard-coded value. 2) it is not possible to override the default position "identity" in geom_text() or geom_text_repel(), however nudging with ggpmisc::position_nudge_center() can be very effective way of better positioning of labels; add new parameter label.position and set its default to "identity", which is the geoms' default; in edit geom_factory() to treat position as a special case as the implicitly called match() failed to correctly handle functions as arguments to position. 3) a vector of colour definitions passed to label.colour is silently ignored because of how geom_factory() is coded as a test for the presence a column named "colour" always exists in the data seen by geoms irrespective of a mapping provided by the user or not for the aesthetic. Solution: change test in geom_factory() so that vectors are always considered as options rather than mappings

This also required some non-code-breaking edits to plot_label() adding the new parameters and passing them to geom_factory().

Note: I have also made a pull request in 'ggrepel' about adding wider support for position functions. These are likely to make it to the next release. In the current version of 'ggrepel' no label segments are drawn when using ggpmisc::position_nudge_center() or 'ggplot2' nudge functions, but otherwise they still work as expected.

Dependencies: Dependencies and imports from other packages remain unchanged.

I needed these features for a couple of figures I had to do this afternoon, and as these edits to 'ggfortify' seem not to have added any failures to checks or tests, I am making this pull request.

New examples compared to an example from the current vignette:

library(ggfortify)
df <- iris[1:4]
pca_res <- prcomp(df, scale. = TRUE)

## example from vignette
autoplot(pca_res, data = iris, colour = 'Species',
         loadings = TRUE, loadings.colour = 'blue',
         loadings.label = TRUE, loadings.label.size = 3)

## now possible to use nudging

autoplot(pca_res, data = iris, 
         colour = 'Species',
         label.position = 
           ggpmisc::position_nudge_center(x = 0.02, y = 0.007,
                                          center_x = 0, center_y = 0,
                                          direction = "radial"),
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3)

autoplot(pca_res, data = iris, 
         colour = 'Species',
         label.position = 
           ggpmisc::position_nudge_center(x = 0.02,
                                          center_x = 0, center_y = 0,
                                          direction = "split"),
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3)

## now possible to control arrows

# smaller closed arrow heads
autoplot(pca_res, 
         data = iris, 
         colour = 'Species',
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3,
         loadings.arrow =
           grid::arrow(angle = 20, length = unit(6, "point"), type = "closed"))

# no arrow heads
autoplot(pca_res, 
         data = iris, 
         colour = 'Species',
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3,
         loadings.arrow =
           grid::arrow(angle = 20, length = unit(0, "point"), type = "closed"))

## now we can use vectors to set the colour of loading arrows

part.colours <- c("red", "blue")[grepl("Length", rownames(pca_res$rotation)) + 1]

autoplot(pca_res, 
         data = iris, 
         colour = 'Species',
         loadings = TRUE, 
         loadings.colour = part.colours,
         loadings.label = TRUE, 
         loadings.label.size = 3,
         loadings.label.colour = part.colours)

## everything together

part.colours <- c("red", "blue")[grepl("Length", rownames(pca_res$rotation)) + 1]

autoplot(pca_res, 
         data = iris, 
         colour = 'Species',
         label.position = 
           ggpmisc::position_nudge_center(x = 0.02,
                                          center_x = 0, center_y = 0,
                                          direction = "split"),
         loadings = TRUE, 
         loadings.colour = part.colours,
         loadings.label = TRUE, 
         loadings.label.size = 3,
         loadings.label.colour = part.colours,
         loadings.arrow =
           grid::arrow(angle = 20, length = unit(4, "point"), type = "closed"))

Plot for the example immediately above:

Rplot

Plot for the first example above taken from the PCA vignette (plot_pca.Rmd, line 52) and used as starting point:

Rplot01

Additional note: I created this branch starting from the most recent commit in "master" (912ba6d39feb1537b30997632b1d125500c1058d), which is triggering some warnings and errors during checks. As far as I can see, there are no additional problems added by this pull request). I also commented out a few lines of code in the last chuck of of the maps vignette because of an error was triggered due to a change in 'ggmap' preventing access to OSM data.

aphalo commented 3 years ago

@terrytangyuan The pull request is now ready for review. I updated the title and the comment text itself to make clear what are the changes I made and why they were needed. I added code examples showing how the new features can be used, and included a bitmap for the last code example that shows what is possible to achieve compared to what was earlier possible.

aphalo commented 3 years ago

One additional example. The example in the vignette can be improved quite a lot without using the new features by setting `loadings.label.hjust = "outward". Adding nudging away from the plot origin improves it further.

library(ggfortify)
df <- iris[1:4]
pca_res <- prcomp(df, scale. = TRUE)

## example from vignette
autoplot(pca_res, 
         data = iris, 
         colour = 'Species',
         loadings.label.hjust = "outward",
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3)

## but adding nudging improves the plot even a bit more

autoplot(pca_res, data = iris, 
         colour = 'Species',
         label.position = 
           ggpmisc::position_nudge_center(x = 0.002, y = 0.001,
                                          center_x = 0, center_y = 0,
                                          direction = "radial"),
         loadings.label.hjust = "outward",
         loadings = TRUE, 
         loadings.colour = 'blue',
         loadings.label = TRUE, 
         loadings.label.size = 3)

This is the output for the second example.

Rplot

terrytangyuan commented 3 years ago

LGTM. Thanks!