How to modify letter overlap axis map ggplot2 [duplicate] - r

I have a plot where the x-axis is a factor whose labels are long. While probably not an ideal visualization, for now I'd like to simply rotate these labels to be vertical. I've figured this part out with the code below, but as you can see, the labels aren't totally visible.
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
q <- qplot(cut,carat,data=diamonds,geom="boxplot")
q + opts(axis.text.x=theme_text(angle=-90))

Change the last line to
q + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
By default, the axes are aligned at the center of the text, even when rotated. When you rotate +/- 90 degrees, you usually want it to be aligned at the edge instead:
The image above is from this blog post.

Use coord_flip()
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
qplot(cut, carat, data = diamonds, geom = "boxplot") +
coord_flip()
Add str_wrap()
# wrap text to no more than 15 spaces
library(stringr)
diamonds$cut2 <- str_wrap(diamonds$cut, width = 15)
qplot(cut2, carat, data = diamonds, geom = "boxplot") +
coord_flip()
In Ch 3.9 of R for Data Science, Wickham and Grolemund speak to this exact question:
coord_flip() switches the x and y axes. This is useful (for example), if you want horizontal boxplots. It’s also useful for long labels: it’s hard to get them to fit without overlapping on the x-axis.

ggplot 3.3.0 fixes this by providing guide_axis(angle = 90) (as guide argument to scale_.. or as x argument to guides):
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(angle = 90)) +
# ... or, equivalently:
# guides(x = guide_axis(angle = 90)) +
NULL
From the documentation of the angle argument:
Compared to setting the angle in theme() / element_text(), this also
uses some heuristics to automatically pick the hjust and vjust that
you probably want.
Alternatively, it also provides guide_axis(n.dodge = 2) (as guide argument to scale_.. or as x argument to guides) to overcome the over-plotting problem by dodging the labels vertically. It works quite well in this case:
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
NULL

To make the text on the tick labels fully visible and read in the same direction as the y-axis label, change the last line to
q + theme(axis.text.x=element_text(angle=90, hjust=1))

I'd like to provide an alternate solution, a robust solution similar to what I am about to propose was required in the latest version of ggtern, since introducing the canvas rotation feature.
Basically, you need to determine the relative positions using trigonometry, by building a function which returns an element_text object, given angle (ie degrees) and positioning (ie one of x,y,top or right) information.
#Load Required Libraries
library(ggplot2)
library(gridExtra)
#Build Function to Return Element Text Object
rotatedAxisElementText = function(angle,position='x'){
angle = angle[1];
position = position[1]
positions = list(x=0,y=90,top=180,right=270)
if(!position %in% names(positions))
stop(sprintf("'position' must be one of [%s]",paste(names(positions),collapse=", ")),call.=FALSE)
if(!is.numeric(angle))
stop("'angle' must be numeric",call.=FALSE)
rads = (angle - positions[[ position ]])*pi/180
hjust = 0.5*(1 - sin(rads))
vjust = 0.5*(1 + cos(rads))
element_text(angle=angle,vjust=vjust,hjust=hjust)
}
Frankly, in my opinion, I think that an 'auto' option should be made available in ggplot2 for the hjust and vjust arguments, when specifying the angle, anyway, lets demonstrate how the above works.
#Demonstrate Usage for a Variety of Rotations
df = data.frame(x=0.5,y=0.5)
plots = lapply(seq(0,90,length.out=4),function(a){
ggplot(df,aes(x,y)) +
geom_point() +
theme(axis.text.x = rotatedAxisElementText(a,'x'),
axis.text.y = rotatedAxisElementText(a,'y')) +
labs(title = sprintf("Rotated %s",a))
})
grid.arrange(grobs=plots)
Which produces the following:

The ggpubr package offers a shortcut that does the right thing by default (right align text, middle align text box to tick):
library(ggplot2)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
q <- qplot(cut, carat, data = diamonds, geom = "boxplot")
q + ggpubr::rotate_x_text()
Created on 2018-11-06 by the reprex package (v0.2.1)
Found with a GitHub search for the relevant argument names: https://github.com/search?l=R&q=element_text+angle+90+vjust+org%3Acran&type=Code

OUTDATED - see this answer for a simpler approach
To obtain readable x tick labels without additional dependencies, you want to use:
... +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) +
...
This rotates the tick labels 90° counterclockwise and aligns them vertically at their end (hjust = 1) and their centers horizontally with the corresponding tick mark (vjust = 0.5).
Full example:
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
q <- qplot(cut,carat,data=diamonds,geom="boxplot")
q + theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
Note, that vertical/horizontal justification parameters vjust/hjust of element_text are relative to the text. Therefore, vjust is responsible for the horizontal alignment.
Without vjust = 0.5 it would look like this:
q + theme(axis.text.x = element_text(angle = 90, hjust = 1))
Without hjust = 1 it would look like this:
q + theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
If for some (wired) reason you wanted to rotate the tick labels 90° clockwise (such that they can be read from the left) you would need to use: q + theme(axis.text.x = element_text(angle = -90, vjust = 0.5, hjust = -1)).
All of this has already been discussed in the comments of this answer but I come back to this question so often, that I want an answer from which I can just copy without reading the comments.

An alternative to coord_flip() is to use the ggstance package.
The advantage is that it makes it easier to combine the graphs with other graph types and you can, maybe more importantly, set fixed scale ratios for your coordinate system.
library(ggplot2)
library(ggstance)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
ggplot(data=diamonds, aes(carat, cut)) + geom_boxploth()
Created on 2020-03-11 by the reprex package (v0.3.0)

Also with ggplot2 3.3+, we can make horizontal plots without coord_flip() as it supports bi-directional geoms, simply swapping x and y axis. https://cmdlinetips.com/2020/03/ggplot2-2-3-0-is-here-two-new-features-you-must-know/

Related

Is there a way to wrap text on an axis? or Lengthen the graph? [duplicate]

I have a plot where the x-axis is a factor whose labels are long. While probably not an ideal visualization, for now I'd like to simply rotate these labels to be vertical. I've figured this part out with the code below, but as you can see, the labels aren't totally visible.
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
q <- qplot(cut,carat,data=diamonds,geom="boxplot")
q + opts(axis.text.x=theme_text(angle=-90))
Change the last line to
q + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
By default, the axes are aligned at the center of the text, even when rotated. When you rotate +/- 90 degrees, you usually want it to be aligned at the edge instead:
The image above is from this blog post.
Use coord_flip()
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
qplot(cut, carat, data = diamonds, geom = "boxplot") +
coord_flip()
Add str_wrap()
# wrap text to no more than 15 spaces
library(stringr)
diamonds$cut2 <- str_wrap(diamonds$cut, width = 15)
qplot(cut2, carat, data = diamonds, geom = "boxplot") +
coord_flip()
In Ch 3.9 of R for Data Science, Wickham and Grolemund speak to this exact question:
coord_flip() switches the x and y axes. This is useful (for example), if you want horizontal boxplots. It’s also useful for long labels: it’s hard to get them to fit without overlapping on the x-axis.
ggplot 3.3.0 fixes this by providing guide_axis(angle = 90) (as guide argument to scale_.. or as x argument to guides):
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(angle = 90)) +
# ... or, equivalently:
# guides(x = guide_axis(angle = 90)) +
NULL
From the documentation of the angle argument:
Compared to setting the angle in theme() / element_text(), this also
uses some heuristics to automatically pick the hjust and vjust that
you probably want.
Alternatively, it also provides guide_axis(n.dodge = 2) (as guide argument to scale_.. or as x argument to guides) to overcome the over-plotting problem by dodging the labels vertically. It works quite well in this case:
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
NULL
To make the text on the tick labels fully visible and read in the same direction as the y-axis label, change the last line to
q + theme(axis.text.x=element_text(angle=90, hjust=1))
I'd like to provide an alternate solution, a robust solution similar to what I am about to propose was required in the latest version of ggtern, since introducing the canvas rotation feature.
Basically, you need to determine the relative positions using trigonometry, by building a function which returns an element_text object, given angle (ie degrees) and positioning (ie one of x,y,top or right) information.
#Load Required Libraries
library(ggplot2)
library(gridExtra)
#Build Function to Return Element Text Object
rotatedAxisElementText = function(angle,position='x'){
angle = angle[1];
position = position[1]
positions = list(x=0,y=90,top=180,right=270)
if(!position %in% names(positions))
stop(sprintf("'position' must be one of [%s]",paste(names(positions),collapse=", ")),call.=FALSE)
if(!is.numeric(angle))
stop("'angle' must be numeric",call.=FALSE)
rads = (angle - positions[[ position ]])*pi/180
hjust = 0.5*(1 - sin(rads))
vjust = 0.5*(1 + cos(rads))
element_text(angle=angle,vjust=vjust,hjust=hjust)
}
Frankly, in my opinion, I think that an 'auto' option should be made available in ggplot2 for the hjust and vjust arguments, when specifying the angle, anyway, lets demonstrate how the above works.
#Demonstrate Usage for a Variety of Rotations
df = data.frame(x=0.5,y=0.5)
plots = lapply(seq(0,90,length.out=4),function(a){
ggplot(df,aes(x,y)) +
geom_point() +
theme(axis.text.x = rotatedAxisElementText(a,'x'),
axis.text.y = rotatedAxisElementText(a,'y')) +
labs(title = sprintf("Rotated %s",a))
})
grid.arrange(grobs=plots)
Which produces the following:
The ggpubr package offers a shortcut that does the right thing by default (right align text, middle align text box to tick):
library(ggplot2)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
q <- qplot(cut, carat, data = diamonds, geom = "boxplot")
q + ggpubr::rotate_x_text()
Created on 2018-11-06 by the reprex package (v0.2.1)
Found with a GitHub search for the relevant argument names: https://github.com/search?l=R&q=element_text+angle+90+vjust+org%3Acran&type=Code
OUTDATED - see this answer for a simpler approach
To obtain readable x tick labels without additional dependencies, you want to use:
... +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) +
...
This rotates the tick labels 90° counterclockwise and aligns them vertically at their end (hjust = 1) and their centers horizontally with the corresponding tick mark (vjust = 0.5).
Full example:
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
q <- qplot(cut,carat,data=diamonds,geom="boxplot")
q + theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
Note, that vertical/horizontal justification parameters vjust/hjust of element_text are relative to the text. Therefore, vjust is responsible for the horizontal alignment.
Without vjust = 0.5 it would look like this:
q + theme(axis.text.x = element_text(angle = 90, hjust = 1))
Without hjust = 1 it would look like this:
q + theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
If for some (wired) reason you wanted to rotate the tick labels 90° clockwise (such that they can be read from the left) you would need to use: q + theme(axis.text.x = element_text(angle = -90, vjust = 0.5, hjust = -1)).
All of this has already been discussed in the comments of this answer but I come back to this question so often, that I want an answer from which I can just copy without reading the comments.
An alternative to coord_flip() is to use the ggstance package.
The advantage is that it makes it easier to combine the graphs with other graph types and you can, maybe more importantly, set fixed scale ratios for your coordinate system.
library(ggplot2)
library(ggstance)
diamonds$cut <- paste("Super Dee-Duper", as.character(diamonds$cut))
ggplot(data=diamonds, aes(carat, cut)) + geom_boxploth()
Created on 2020-03-11 by the reprex package (v0.3.0)
Also with ggplot2 3.3+, we can make horizontal plots without coord_flip() as it supports bi-directional geoms, simply swapping x and y axis. https://cmdlinetips.com/2020/03/ggplot2-2-3-0-is-here-two-new-features-you-must-know/

How do I use facetting correctly in ggplot geom_tile, while keeping the aspect ratio intact?

I am trying to create a 'likeliness plot' intended to quickly show an items likeliness vs other items in a table.
A quick example:
'property_data.csv' file to use:
"","Country","Town","Property","Property_value"
"1","UK","London","Road_quality","Bad"
"2","UK","London","Air_quality","Very bad"
"3","UK","London","House_quality","Average"
"4","UK","London","Library_quality","Good"
"5","UK","London","Pool_quality","Average"
"6","UK","London","Park_quality","Bad"
"7","UK","London","River_quality","Very good"
"8","UK","London","Water_quality","Decent"
"9","UK","London","School_quality","Bad"
"10","UK","Liverpool","Road_quality","Bad"
"11","UK","Liverpool","Air_quality","Very bad"
"12","UK","Liverpool","House_quality","Average"
"13","UK","Liverpool","Library_quality","Good"
"14","UK","Liverpool","Pool_quality","Average"
"15","UK","Liverpool","Park_quality","Bad"
"16","UK","Liverpool","River_quality","Very good"
"17","UK","Liverpool","Water_quality","Decent"
"18","UK","Liverpool","School_quality","Bad"
"19","USA","New York","Road_quality","Bad"
"20","USA","New York","Air_quality","Very bad"
"21","USA","New York","House_quality","Average"
"22","USA","New York","Library_quality","Good"
"23","USA","New York","Pool_quality","Average"
"24","USA","New York","Park_quality","Bad"
"25","USA","New York","River_quality","Very good"
"26","USA","New York","Water_quality","Decent"
"27","USA","New York","School_quality","Bad"
Code:
prop <- read.csv('property_data.csv')
Property_col_vector <- c("NA" = "#e6194b",
"Very bad" = "#e6194B",
"Bad" = "#ffe119",
"Average" = "#bfef45",
"Decent" = "#3cb44b",
"Good" = "#42d4f4",
"Very good" = "#4363d8")
plot_likeliness <- function(town_property_table){
g <- ggplot(town_property_table, aes(Property, Town)) +
geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
strip.text.y = element_text(angle = 0)) +
scale_fill_manual(values = Property_col_vector) +
coord_fixed()
return(g)
}
summary_town_plot <- plot_likeliness(prop)
Output:
This is looking great!
Now I've created a plot that looks nice because I used the coord_fixed() function, but now I want to create the same plot, facetted by Country.
To do this I created the following function:
plot_likeliness_facetted <- function(town_property_table){
g <- ggplot(town_property_table, aes(Property, Town)) +
geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
strip.text.y = element_text(angle = 0)) +
scale_fill_manual(values = Property_col_vector) +
facet_grid(Country ~ .,
scale = 'free_y')
return(g)
}
facetted_town_plot <- plot_likeliness_facetted(prop)
facetted_town_plot
Result:
However, now my tiles are stretched and if i try to use '+ coords_fixed()' I get the error:
Error: coord_fixed doesn't support free scales
How can I get the plot to facet, but maintain the aspect ratio ? Please note that I'm plotting these in a series, so hardcoding the heights of the plot with manual values is not a solution I'm after, I need something that dynamically scales with the amount of values in the table.
Many thanks for any help!
Edit: Although the same question was asked in slightly different context elsewhere, it had multiple answers with none marked as solving the question.
theme(aspect.ratio = 1) and space = 'free' seems to work.
plot_likeliness_facetted <- function(town_property_table){
g <- ggplot(town_property_table, aes(Property, Town)) +
geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
strip.text.y = element_text(angle = 0), aspect.ratio = 1) +
scale_fill_manual(values = Property_col_vector) +
facet_grid(Country ~ .,
scale = 'free_y', space = 'free')
return(g)
}
This might not be a perfect answer, but I'm going to give it a spin anyway. Basically, it is going to be difficult to do this with base ggplot because -as you mentioned- coord_fixed() or theme(aspect.ratio = ...) don't play nice with facets.
The first solution I'll propose, is to use gtables to programatically set the width of panels to match the number of variables on your x-axis:
plot_likeliness_gtable <- function(town_property_table){
g <- ggplot(town_property_table, aes(Property, Town)) +
geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
strip.text.y = element_text(angle = 0)) +
scale_fill_manual(values = Property_col_vector) +
facet_grid(Country ~ .,
scale = 'free_y', space = "free_y")
# Here be the gtable bits
gt <- ggplotGrob(g)
# Find out where the panel is stored in the x-direction
panel_x <- unique(gt$layout$l[grepl("panel", gt$layout$name)])[1]
# Set that width based on the number of x-axis variables, plus 0.2 because
# of the expand arguments in the scales
gt$widths[panel_x] <- unit(nlevels(droplevels(town_property_table$Property)) + 0.2, "null")
# Respect needs to be true to have 'null' units match in x- and y-direction
gt$respect <- TRUE
return(gt)
}
Which would work in the following way:
library(grid)
x <- plot_likeliness_gtable(prop)
grid.newpage(); grid.draw(x)
And gives this plot:
This all works reasonably well but at this point, it would probably be good to discuss some of the drawbacks of having gtables instead of ggplot objects. First, you can't edit it anymore with ggplot, so you can't add another + geom_myfavouriteshape() or anything of the sort. You could still edit parts of the plot in gtable/grid though. Second, it has the quirky grid.newpage(); grid.draw() syntax, which needs the grid library. Third, we're kind of relying on the ggplot facetting to set the y-direction panel heights correctly (2.2 and 1.2 null-units in your example) while this might not be appropriate in all cases. On the upside, you're still defining dimensions in flexible null-units, so it'll scale pretty well with whatever plotting device you're using.
The second solution I'll propose could be a bit hacky for many a taste, but it'll take away the first two drawbacks of using gtables. Some time ago, I had similar issues with the weird panel size behaviour when facetting, so I wrote these functions to set panel sizes. The essence of what is does is to copy the panel drawing function from whatever plot you're making and wrap it inside a new function that sets the panel sizes to some pre-defined numbers. It has to be called after any facetting function though. It would work like this:
plot_likeliness_forcedsizes <- function(town_property_table){
g <- ggplot(town_property_table, aes(Property, Town)) +
geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
strip.text.y = element_text(angle = 0)) +
scale_fill_manual(values = Property_col_vector) +
facet_grid(Country ~ .,
scale = 'free_y', space = "free_y") +
force_panelsizes(cols = nlevels(droplevels(town_property_table$Property)) + 0.2,
respect = TRUE)
return(g)
}
myplot <- plot_likeliness_forcedsizes(prop)
myplot
It still relies on ggplot setting the y-direction heights correctly though, but you could override these within force_panelsizes() if things go awry.
Hope this helped, good luck!

Spacing between groups of bars in histogram

When I produce histograms in ggplot2 where the bar positions are dodge, I expect something like this where there is space between the groups of bars (i.e. notice the white space between each groups of red/green pairs):
I'm having a hard time producing the same effect when I build a histogram with continuous data. I can't seem to add space between the groups of bars, and instead, everything gets squashed together. As you can see, it makes it visually difficult to compare the red/green pairs:
To reproduce my problem, I created a sample data set here: https://www.dropbox.com/s/i9nxzo1cmbwwfsa/data.csv?dl=0
Code to reproduce:
data <- read.csv("https://www.dropbox.com/s/i9nxzo1cmbwwfsa/data.csv?dl=1")
ggplot(data, aes(x = soldPrice, fill = month)) +
geom_histogram(binwidth=1e5, position=position_dodge()) +
labs(x="Sold Price", y="Sales", fill="") +
scale_x_continuous(labels=scales::comma, breaks=seq(0, 2e6, by = 1e5)) +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
How can I add white space between the groups of red/green pairs?
Alternative 1: overlapping bars with geom_histogram()
From ?position_dodge():
Dodging preserves the vertical position of an geom while adjusting the horizontal position
This function accepts a width argument that determines the space to be created.
To get what I think you want, you need to supply a suitable value to position_dodge(). In your case, where binwidth=1e5, you might play with e.g. 20% of that value: position=position_dodge(1e5-20*(1e3)).
(I left the rest of your code untouched.)
You could use the following code:
ggplot(data, aes(x = soldPrice, fill = month)) +
geom_histogram(binwidth=1e5, position=position_dodge(1e5-20*(1e3))) + ### <-----
labs(x="Sold Price", y="Sales", fill="") +
scale_x_continuous(labels=scales::comma, breaks=seq(0, 2e6, by = 1e5)) +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
yielding this plot:
Alternative 2: use ggplot-object and render with geom_bar
geom_histogram() was not designed to produce what you want. geom_bar() on the other hand provides the flexibility you need.
You can generate the histogram with geom_histogram and save it in an ggplot-object. Then, you generate the plotting information with ggplot_build(). Now,
you may use the histogram plotting information in the object to generate a bar plot with geom_bar()
## save ggplot object to h
h <- ggplot(data, aes(x = soldPrice, fill = month)) +
geom_histogram(binwidth=1e5, position=position_dodge(1e5-20*(1e3)))
## get plotting information as data.frame
h_plotdata <- ggplot_build(h)$data[[1]]
h_plotdata$group <- as.factor(h_plotdata$group)
levels(h_plotdata$group) <- c("May 2018", "May 2019")
## plot with geom_bar
ggplot(h_plotdata, aes(x=x, y=y, fill = group)) +
geom_bar(stat = "identity") +
labs(x="Sold Price", y="Sales", fill="") +
scale_x_continuous(labels=scales::comma, breaks=seq(0, 2e6, by = 1e5)) +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5))
yielding this graph:
Please, let me know whether this is what you want.

Left-aligned axis labels when using cowplot to switch x axis to top

I'm trying to make a correlation heatmap where the x axis is moved to the top using cowplot::switch_axis_position. I have axis labels of varying length and I want the labels to be left-aligned (or rather bottom-aligned, because they are rotated 90 degrees). Although I manage to align the labels, they are moved up far above the plot.
library(reshape2)
library(ggplot2)
library(cowplot)
# some toy data
set.seed(1)
mydata <- mtcars[, c(1, 3, 4, 5, 6, 7)]
# to show difference in justification better, make names of unequal length
names(mydata) = paste0(sample(c("mtcars_", ""), 6, replace = TRUE), names(mydata))
cormat <- round(cor(mydata), 2)
melted_cormat <- melt(cormat)
head(melted_cormat)
First a plot where the x axis is moved to the top, and the labels are centered vertically:
plot <- ggplot(data = melted_cormat, aes(x=Var1, y=Var2, fill=value)) +
geom_tile() +
theme_bw(base_size=20) + xlab("") + ylab("") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 0.5))
ggdraw(switch_axis_position(plot, 'x'))
Then I use the same code as above but with hjust = 0 instead to left-align the x axis text. It indeed aligns the text, but the text is moved weirdly far from graph so variable names are cut off:
Any ideas of how to fix this?
Please note: this bug is no longer present in the latest version of cowplot on CRAN.
Old answer:
It seems this is a bug for the special case of angle = 90. We can circumvent this by adding an arbitrarily small value to angle.
plot <- ggplot(data = melted_cormat, aes(x=Var1, y=Var2, fill=value)) +
geom_tile() + theme_bw(base_size=20) + xlab("") + ylab("")+
theme(axis.text.x=element_text(angle=90 + 1e-09, hjust = 0, vjust=1)) +
coord_equal(expand = 0)
ggdraw(switch_axis_position(plot, 'x'))

Rotation of labels to follow x-axis in ggplot2, polar projection?

How might the angle of x-axis labels follow the same angle as the outer x-axis using the coor_polar projection in ggplot? This is similar to rotate x-axis text in ggplot2 when using coord_polar(), but I don't understand the math well enough to adapt it. I found a trial-and-error solution below that sort of works, where angle = c(c(1:3)*c(-14,-22.3,-22),-90,c(3:1)*c(22,22.3,14),c(1:3)*c(-14,-22.3,-22),90,c(3:1)*c(22,22.3,14)). It would also be okay for the x-axis labels to rotate in the same direction all the way around. If all else fails, I might give up on rotating the labels and just add a second legend, as in Two legends for polar ggplot (with one customized). Thanks for the help!
require(ggplot2)
df.test <- data.frame(Names=c("name01", "name02", "name03", "name04", "name05", "name06", "name07", "name08", "name09", "name10", "name11", "name12", "name13", "name14"),Values=rep(1,24))
p <- ggplot(df.test, aes(Names, fill=Values))
p + coord_polar(theta="x", direction=1) +
geom_bar(stat="bin", colour="gray", alpha=.7) +
theme(axis.text.x = element_text(angle = c(c(1:3)*c(-14,-22.3,-22),-90,c(3:1)*c(22,22.3,14),c(1:3)*c(-14,-22.3,-22),90,c(3:1)*c(22,22.3,14))))
I'm not exactly clear what your goal is but see if this answers the question:
+ theme(axis.text.x = element_text(angle =
360/(2*pi)*rev( seq( pi/14, 2*pi-pi/14, len=14))))
That would make the names "tangential" to the splits. If you wanted them "perpendicular" (as in the illustration in the linked answer) you just add pi/2 radians to the angle. (Didn't we all take geometry in high school?)
+ theme(axis.text.x = element_text(angle =
360/(2*pi)*rev( pi/2 + seq( pi/14, 2*pi-pi/14, len=14))))
(BTW: The data argument that you specified had an error. I changed "24" to "14".)
Let me know if the first 7 labels look right, but you want the ones on the LHS of the figure flipped.
So you want the bottom 6 rotated by pi radians (=pi*360/2*pi degrees):
+theme(axis.text.x = element_text(angle = 360/(2*pi)*rev(
seq(pi/14,2*pi-pi/14, len=14))+
360/(2*pi)*c( rep(0, 4),rep(pi,6), rep(0,4)) ))
# the rotation "back" of the lower items
(I really do not understand the decision to use degrees rather than radians.)
Here's a small example how the solution of #42- could be implemented. Would have been useful for me.
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
ax <- length(unique(mtcars$gear))
ggplot(mtcars, aes(x = gear, y = cyl)) +
geom_point() +
coord_polar() +
theme(
axis.text.x = element_text(
angle = 360/(2*pi)*rev(seq(pi/ax,2*pi-pi/ax, len=ax)) + 360/(2*pi)*c(rep(0, round(ax/3)),rep(pi,ax-2*round(ax/3)), rep(0,round(ax/3)))
)
) +
scale_y_discrete(
breaks = levels(mtcars$cyl),
limits = c(" ", levels(mtcars$cyl))
)

Resources