Reorder ggplot axis by one value, display labels from another - r

Situation is as follows:
I have many names and many corresponding codes for those names.
All different names have a unique code, but not all different codes have a unique name.
This has created an issue when plotting the data, as I need to group_by(code), and reorder(name,code) when plotting, but the codes are nonsense and I want to display the names. Since some codes share names, this creates a bit of an issue.
Example to illustrate below:
library(tidyverse)
set.seed(1)
# example df
df <- tibble("name" = c('apple','apple','pear','pear','pear',
'orange','banana','peach','pie','soda',
'pie','tie','beer','picnic','cigar'),
"code" = seq(1,15),
"value" = round(runif(15,0,100)))
df %>%
ggplot(aes(x=reorder(name,value)))+
geom_bar(aes(y=value),
stat='identity')+
coord_flip()+
ggtitle("The axis labels I want, but the order I don't")
df %>%
ggplot(aes(x=reorder(code,value)))+
geom_bar(aes(y=value),
stat='identity')+
coord_flip()+
ggtitle("The order I want, but the axis labels I don't")
Not quite sure how to get ggplot to keep the display and order of the second plot while being able to replace the axis labels with the names from the first plot.

What about using interaction to bind names and code and in scale_x_discrete replace labels by appropriate one such as follow:
df %>%
ggplot(aes(x=interaction(reorder(name, value),reorder(code,value))))+
geom_bar(aes(y=value),
stat='identity')+
scale_x_discrete(labels = function(x) sub("\\..*$","",x), name = "name")+
coord_flip()
is it what you are looking for ?

Related

Summarising data before plotting with geom_tile() renders different results

I have noticed that when plotting with ggplot2's geom_tile(), summarising the data before plotting renders a completely different result than when it is not pre-summarised. I don't understand why.
For a dataframe with three columns, year (character), state (character) and profit (numeric), consider the following examples:
# Plot straight away
data %>%
ggplot(aes(x=year, y=state)) + geom_tile(aes(fill=profit))
# Summarise before plotting
data %>% group_by(year, state) %>% summarize(profit_mean = mean(profit)) %>%
ungroup() %>%
ggplot(aes(x=year, y=state)) + geom_tile(aes(fill=profit_mean))
These two examples render two different tile plots - the values are quite different. I thought that these two methods of plotting would be analogous and that ggplot2 would take a mean automatically - is that not so?
I tried reproducing this error on a smaller subset of data, but it didn't appear. What could be going on here?
OP, this was a very interesting question.
First, let's get this out of the way. It is clear what plotting the summary of your data is plotting just that: the summary. You are summarizing via mean, so what is plotted equals the mean of the values for each tile.
The actual question here is: If you have a dataset containing more than one value per tile, what is the result of plotting the "non-summarized" dataset?
User #akrun is correct: the default stat used for geom_tile is stat="identity", but it might not be clear what that exactly means. It says it "leaves the data unchanged"... but that's not clear what that means here.
Illustrative Example Dataset
For purposes of demonstration, I'll create an illustrative dataset, which will answer the question very clearly. I'm creating two individual datasets df1 and df2, which each contain 4 "tiles" of data. The difference between these is that the values themselves for the tiles are different. I've include text labels on each tile for more clarity.
library(ggplot2)
library(cowplot)
df1 <- data.frame(
x=rep(paste("Test",1:2), 2),
y=rep(c("A", "B"), each=2),
value=c(5,15,20,25)
)
df2 <- data.frame(
x=rep(paste("Test",1:2), 2),
y=rep(c("A", "B"), each=2),
value=c(10,5,25,15)
)
tile1 <- ggplot(df1, aes(x,y, fill=value, label=value)) +
geom_tile() + geom_text() + labs(title="df1")
tile2 <- ggplot(df2, aes(x,y, fill=value, label=value)) +
geom_tile() + geom_text() + labs(title="df2")
plot_grid(tile1, tile2)
Plotting the Combined Data Frame
Each of the data frames df1 and df2 contain only one value per tile, so in order to see how that changes when we have more than one value per tile, we need to combine them into one so that each tile will contain 2 values. In this example, we are going to combine them in two ways: first df1 then df2, and the other way is df2 first, then df1.
df12 <- rbind(df1, df2)
df21 <- rbind(df2, df1)
Now, if we plot each of those as before and compare, the reason for the discrepancy the OP posted should be quite obvious. I'm including the value for each tile for each originating dataset to make things super-clear.
tile12 <- ggplot(df12, aes(x,y, fill=value, label=value)) +
geom_tile() + labs(title="df1, then df2") +
geom_text(data=df1, aes(label=paste("df1:",value)), nudge_y=0.1) +
geom_text(data=df2, aes(label=paste("df2:",value)), nudge_y=-0.1)
tile21 <- ggplot(df21, aes(x,y, fill=value, label=value)) +
geom_tile() + labs(title="df2, then df1") +
geom_text(data=df1, aes(label=paste("df1:",value)), nudge_y=0.1) +
geom_text(data=df2, aes(label=paste("df2:",value)), nudge_y=-0.1)
plot_grid(tile12, tile21)
Note that the legend colorbar value does not change, so it's not doing an addition. Plus, since we know it's stat="identity", we know this should not be the case. When we use the dataset that contains first observations from df1, then observations from df2, the value plotted is the one from df2. When we use the dataset that contains observations first from df2, then from df1, the value plotted is the one from df1.
Given this piece of information, it can be clear that the value shown in geom_tile() when using stat="identity" (default argument) corresponds to the last observation for that particular tile represented in the data frame.
So, that's the reason why your plot looks odd OP. You can either summarize beforehand as you have done, or use stat_summary(geom="tile"... to do the transformation in one go within ggplot.

How to assign unique title and text labels to ggplots created in lapply loop?

I've tried about every iteration I can find on Stack Exchange of for loops and lapply loops to create ggplots and this code has worked well for me. My only problem is that I can't assign unique titles and labels. From what I can tell in the function i takes the values of my response variable so I can't index the title I want as the ith entry in a character string of titles.
The example I've supplied creates plots with the correct values but the 2nd and 3rd plots in the plot lists don't have the correct titles or labels.
Mock dataset:
library(ggplot2)
nms=c("SampleA","SampleB","SampleC")
measr1=c(0.6,0.6,10)
measr2=c(0.6,10,0.8)
measr3=c(0.7,10,10)
qual1=c("U","U","")
qual2=c("U","","J")
qual3=c("J","","")
df=data.frame(nms,measr1,qual1,measr2,qual2,measr3,qual3,stringsAsFactors = FALSE)
identify columns in dataset that contain response variable
measrsindex=c(2,4,6)
Create list of plots that show all samples for each measurement
plotlist=list()
plotlist=lapply(df[,measrsindex], function(i) ggplot(df,aes_string(x="nms",y=i))+
geom_col()+
ggtitle("measr1")+
geom_text(aes(label=df$qual1)))
Create list of plots that show all measurements for each sample
plotlist2=list()
plotlist2=lapply(df[,measrsindex],function(i)ggplot(df,aes_string(x=measrsindex, y=i))+
geom_col()+
ggtitle("SampleA")+
geom_text(aes(label=df$qual1)))
The problem is that I cant create unique title for each plot. (All plots in the example have the title "measr1" or "SampleA)
Additionally I cant apply unique labels (from qual columns) for each bar. (ex. the letter for qual 2 should appear on top of the column for measr2 for each sample)
Additionally in the second plot list the x-values aren't "measr1","measr2","measr3" they're the index values for those columns which isn't ideal.
I'm relatively new to R and have never posted on Stack Overflow before so any feedback about my problem or posting questions is welcomed.
I've found lots of questions and answers about this sort of topic but none that have a data structure or desired plot quite like mine. I apologize if this is a redundant question but I have tried to find the solution in previous answers and have been unable.
This is where I got the original code to make my loops, however this example doesn't include titles or labels:
Looping over ggplot2 with columns
You could loop over the names of the columns instead of the column itself and then use some non-standard evaluation to get column values from the names. Also, I have included label in aes.
library(ggplot2)
library(rlang)
plotlist3 <- purrr::map(names(df)[measrsindex],
~ggplot(df, aes(nms, !!sym(.x), label = qual1)) +
geom_col() + ggtitle(.x) + geom_text(vjust = -1))
plotlist3[[1]]
plotlist3[[2]]
The same can be achieved with lapply as well
plotlist4 <- lapply(names(df)[measrsindex], function(x)
ggplot(df, aes(nms, !!sym(x), label = qual1)) +
geom_col() + ggtitle(x) + geom_text(vjust = -1))
I would recommend putting your data in long format prior to using ggplot2, it makes plotting a much simpler task. I also recoded some variables to facilitate constructing the plot. Here is the code to construct the plots with lapply.
library(tidyverse)
#Change from wide to long format
df1<-df %>%
pivot_longer(cols = -nms,
names_to = c(".value", "obs"),
names_sep = c("r","l")) %>%
#Separate Sample column into letters
separate(col = nms,
sep = "Sample",
into = c("fill","Sample"))
#Change measures index to 1-3
measrsindex=c(1,2,3)
plotlist=list()
plotlist=lapply(measrsindex, function(i){
#Subset by measrsindex (numbers) and plot
df1 %>%
filter(obs == i) %>%
ggplot(aes_string(x="Sample", y="meas", label="qua"))+
geom_col()+
labs(x = "Sample") +
ggtitle(paste("Measure",i, collapse = " "))+
geom_text()})
#Get the letters A : C
samplesvec<-unique(df1$Sample)
plotlist2=list()
plotlist2=lapply(samplesvec, function(i){
#Subset by samplesvec (letters) and plot
df1 %>%
filter(Sample == i) %>%
ggplot(aes_string(x="obs", y = "meas",label="qua"))+
geom_col()+
labs(x = "Measure") +
ggtitle(paste("Sample",i,collapse = ", "))+
geom_text()})
Watching the final plots, I think it might be useful to use facet_wrap to make these plots. I added the code to use it with your plots.
#Plot for Measures
ggplot(df1, aes(x = Sample,
y = meas,
label = qua)) +
geom_col()+
facet_wrap(~ obs) +
ggtitle("Measures")+
labs(x="Samples")+
geom_text()
#Plot for Samples
ggplot(df1, aes(x = obs,
y = meas,
label = qua)) +
geom_col()+
facet_wrap(~ Sample) +
ggtitle("Samples")+
labs(x="Measures")+
geom_text()
Here is a sample of the plots using facet_wrap.

GGplot2: plotting values per year of specific columns in a dataframe

I am trying to plot values on the y axis against years on the x axis with ggplot2.
This is the dataset: https://drive.google.com/file/d/1nJYtXPrxD0xvq6rBz2NXlm4Epi52rceM/view?usp=sharing
I want to plot the values of specific countries.
It won't work by just specifying year as the x axis and a country's values on the y axis. I'm reading I need to melt the data frame, so I did that, but it's now in a format that doesn't seem convenient to get the job done.
I'm assuming I haven't correctly melted, but I have a hard time finding what I need to specifically do.
What I did beforehand is manually transpose the data and make the years a column, as well as all the countries.
This is the dataset transposed:
https://drive.google.com/file/d/131wNlubMqVEG9tID7qp-Wr8TLli9KO2Q/view?usp=sharing
Here's how I melted:
inv_melt.data <- melt(investments_t.data, id.vars="Year")
ggplot() +
geom_line(aes(x=Year, y=value), data = inv_melt.data)
The plot shows the aggregated values of all countries per year, but I want them per country in such a manner that I can also select to plot certain countries only.
How do I utilize melt in such a manner? Could someone walk me through this?
There are no columns named "Year" in the linked to data set, there are columns per year. So it need to be melted by "country" and then the "variable" edited with sub.
inv_melt.data <- reshape2::melt(investments_t.data, id.vars="country")
inv_melt.data$variable <- as.integer(sub("^X", "", inv_melt.data$variable))
ggplot(inv_melt.data, aes(variable, value, color = country)) +
geom_line(show.legend = FALSE)
Edit.
The following code keeps only some countries, filtering out the ones with more missing values.
i <- sapply(investments_t.data[-1], function(x) sum(is.na(x)) == 0)
i <- c(1, which(i))
inv_melt.data <- reshape2::melt(investments_t.data[i], id.vars = "Year")
ggplot(inv_melt.data, aes(Year, value, color = variable)) +
geom_line(show.legend = FALSE)

How to plot bar charts with 'n' number of columns and group by another column?

I am learning r currently and I have an r data-frame containing data I have scraped from a football website.
There are 58 columns(Variables,attributes) for each row. Out of these variables, I wish to plot 3 in a single bar chart.I have 3 important variables 'Name', 'Goals.with.right.foot', 'Goals.with.left.foot'.
What I want to build is a bar chart with each 'Name' appearing on the x-axis and 2 independent bars representing the other 2 variables.
Sample row entry:
{......., RONALDO, 10(left), 5(right),............}
I have tried playing around a lot with ggplot2 geom_bar with no success.
I have also searched for similar questions however I cannot understand the answers. Is anyone able to explain simply how do I solve this problem?
my data frame is called 'Forwards' who are the strikers in a game of football. They have attributes Name, Goals.with.left.foot and Goals.with.right.foot.
barplot(counts, main="Goals",
xlab="Goals", col=c("darkblue","red"),
legend = rownames(counts))
You could try it this way:
I simulated a frame as a stand in for yours, just replace it with a frame containing the columns you're interested in:
df <- data.frame(names = letters[1:5], r.foot = runif(5,1,10), l.foot = runif(5,1,10))
# transform your df to long format
library(reshape2)
plotDf <- melt(df, variable.name = 'footing', value.name = 'goals')
# plot it
library(ggplot2)
ggplot(plotDf, aes(x = names, y = goals, group = footing, fill = footing)) +
geom_col(position = position_dodge()) #does the same as geom_bar, but uses stat_identity instead of stat_count
Results in this plot:
your plot
This works, because ggplot expects one variable containing the values needed for the y-axis and one or more variable containing the grouping factor(s).
with the melt-function, your data.frame is merged into the so called 'long format' which is exactly the needed orientation of data.

deselect subplots in GGally::ggpairs by rows / columns in the ggmatrix: how to color by a factor without showing its related plots?

I want to create a ggpairs plot colored by a factor, in order to do that I have to keep the factor column in the data frame that goes into ggpairs().
The problem with that is that it adds plots that are done with the factor (the last column and last line in the plot), which I do not want to have in the ggpairs plot (they only add a limited amount of information and make the plot messy).
Is there a way to not show them in the plot, or alternatively color by a factor which is in a separate dataframe?
I was able to remove the whole top part of the plot by using: upper = 'blank' but it doesn't really help as I cannot remove by columns or rows of the ggmatrix.
Is there a way to do this?
I searched for solutions but I didn't find anything relevant
here is an example using the gapminder dataset:
library(dplyr)
library(ggplot2)
library(GGally)
library(gapminder)
gapminder %>%
filter(year == 2002 & continent != 'Oceania') %>%
transmute(lifeExp = lifeExp, log_pop = log(pop), log_gdpPercap = log(gdpPercap), continent = continent) %>%
ggpairs(aes(color = continent, alpha = 0.5))
I get this:
ggpairs with the factor
and I would like to get something like this:
ggpairs colored by factor but without its related plots
You can use the columns argument for this.
From the documentation:
which columns are used to make plots. Defaults to all columns.
In your example output you want only columns 1:3.
... %>%
ggpairs(aes(color = continent, alpha = 0.5), columns = 1:3)

Resources