Create bar plot with logarithmic scale in R - r

I am trying to create a bar plot with a logarithmic scale as my data varies from 3.92 to 65700.
This is the code i have used so far:
beach <- c(PlasticsBlue=3.92, PlasticsGrey=65700, FoamsOrange=17.9, FoamsWhite=51300, RopesGreen=9.71, RopesGreen=3140)
beach
par(mar = c(10, 5, 10, 5))
barplot(beach, names.arg=c("Plastics/Blue", "Plastics/Grey", "Foams/Orange", "Foams/White", "Ropes/Green", "Ropes/Green"), col=c("red2", "slateblue4", "red2", "slateblue4", "red2", "slateblue4", "red2"), legend.text = c("Lowest", "Highest"), args.legend=list(cex=0.75,x="topright"), ylim=c(1,100000), log = ("y"), las=2, ylab = expression("mg g"^-1))
Which has given me this graph graph
This is exactly what I'm looking for apart from the log function used means that the next tick mark would be 1000000 which is far too large and therefore currently the y axis is only numbered up to 10000 which does not incorporate my largest values. Is there any way around this to have the y axis numbered up to 100000 whilst still using the log function as this seemed to work when I first made the graph in excel (see graph2 link) graph2
Thanks in advance, Alistair

You can always get what if you are willing to fiddle with the details in R. In this case it is easier to bypass R's helpful log axis and construct your own:
options(scipen=8)
out <- barplot(log10(beach), names.arg=c("Plastics/Blue", "Plastics/Grey", "Foams/Orange",
"Foams/White", "Ropes/Green", "Ropes/Green"), col=c("red2", "slateblue4", "red2",
"slateblue4", "red2", "slateblue4", "red2"), legend.text = c("Lowest", "Highest"),
args.legend=list(cex=0.75,x="topright"), ylim=c(0, 5), las=2, yaxt="n",
ylab = expression("mg g"^-1))
yval <- c(1, 10, 100, 1000, 10000, 100000)
ypos <- log10(yval)
axis(2, ypos, yval, las=1)
text(out, log10(beach), beach, pos=3, xpd=NA)
The first line just keeps R from switching to scientific notation for the 100000 value. The barplot differs in that we convert the raw data with log10() set the ylim based on the log10 values, and suppress the y-axis. Then we create a vector of the positions on the y axis we want to label and get their log10 positions. Finally we print the axis. The last line uses the value out from barplot which returns the positions of the bars on the x axis so we can print the values on the tops of the bars.

Using ggplot2 and company could look like:
library(dplyr)
library(ggplot2)
library(tibble)
library(scales)
beach <- c(PlasticsBlue = 3.92, PlasticsGrey = 65700, FoamsOrange = 17.9, FoamsWhite = 51300, RopesGreen = 9.71, RopesGreen = 3140) %>%
enframe() %>%
mutate(colorID = rep(c('Lowest', 'Highest'), 3))
plot <- beach %>%
ggplot(aes(x = 1:nrow(beach), y = value, label = value, fill = colorID)) +
geom_col(stat = 'identity') +
scale_y_continuous(trans = "log10", labels = label_number(), breaks = c(1, 10, 100, 1000, 10000, 100000)) +
scale_x_discrete(labels = beach$name, breaks = 1:nrow(beach), limits = 1:nrow(beach)) +
geom_text(vjust = -1) +
theme_minimal() +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line.y = element_line(colour = 'black'),
legend.position = 'right',
legend.title = element_blank()) +
labs(y = expression("mg g"^-1),
x = 'Category/Sample colour') +
scale_fill_manual(values = rep(c('slateblue4', 'red2'), 3))
This gives us:

Related

ggplotly display ggplot choropleth map ugly

I am trying to make my beautiful ggplot map interactive with a tooltip using ggplotly. But the map rendered with ggploty is not beautiful.
Here is a picture of my map with only ggplot:
Here is a picture of my map when using ggplotly. It removes the legend and make the map ugly:
Is there another way of making my ggplot map interactive with a tooltip? And also ggplotly takes some time to render the interactive map:
Here is my sample code for my ggplot:
ggplot(data = sdpf_f, aes( fill = n,x = long, y = lat, group = group, text = tooltip)) +
geom_polygon(color = "white") +
theme_void() +
scale_fill_continuous(low="#c3ffff", high="#0291da",
guide = guide_legend(title.position = "top", label.position = "bottom", keywidth = 2,
keyheight = 0.5,
title = "Number of agreements"),na.value="lightgrey"
) +
theme(legend.position="bottom") +
coord_map()
Thanks & kind regards,
Akshay
I don't have your data and this isn't exactly the same, but it's fairly close to what I think you're expecting.
The libraries:
I called tidyverse for the plotting and piping. I called maps for the data I used and plotly for the Plotly graph.
I used a function that is derived from one of the ways ggplot sets the aspect ratio. I know I found this function on SO, but I don't remember who wrote it.
library(tidyverse)
library(maps)
library(plotly)
map_aspect = function(x, y) {
x.center <- sum(range(x)) / 2
y.center <- sum(range(y)) / 2
x.dist <- ggplot2:::dist_central_angle(x.center + c(-0.5, 0.5), rep(y.center, 2))
y.dist <- ggplot2:::dist_central_angle(rep(x.center, 2), y.center + c(-0.5, 0.5))
y.dist / x.dist
}
I had to create data as your question is not reproducible. I figured I would include it, so that my answer was reproducible.
ms <- map_data("state") %>%
mutate(n = ifelse(str_detect(region, "^a"), 1.0,
ifelse(str_detect(region, "^o"), 1.5,
ifelse(str_detect(region, "^t"), 2.0,
ifelse(str_detect(region, "^s"), 2.5,
ifelse(str_detect(region, "^w"),
3.0,
NA))))))
I modified your ggplot call. The only change is coord_fixed instead of coord_map. I did this so that Plotly could interpret the aspect ratio correctly. Within coord_fixed, I used the function map_aspect.
gp <- ggplot(data = ms, aes(fill = n, x = long, y = lat,
group = group, text = tooltip)) +
geom_polygon(color = "white") +
theme_void() +
scale_fill_continuous(low="#c3ffff", high="#0291da",
guide = guide_legend(title.position = "top",
label.position = "bottom",
keywidth = 2,
keyheight = 0.5,
title = "Number of agreements"),
na.value="lightgrey"
) +
theme(legend.position="bottom") +
coord_fixed(with(ms, map_aspect(long, lat)))
Then I created a Plotly object. I set some requirements for the layout, as well (horizontal legend at the bottom, with the legend title above the elements—similar to the legend in your ggplot call).
pp <- ggplotly(gp) %>%
layout(legend = list(orientation = "h", valign = "bottom",
title = list(side = "top"),
x = .02),
xaxis = list(showgrid = F), yaxis = list(showgrid = F),
showlegend = T)
Next, I needed to add the information for the legend. I chose to name the traces (which are split by color). I started by creating a vector of the names of the traces (which is what you see in the legend). I added a "" at the end, so that the NA-colored states wouldn't have a named trace (so they won't show in the legend).
This is likely something you'll need to change for your data.**
# the color values and the last one, which is for NAs, no legend
nm <- seq(1, 3, by = .5) %>% sprintf(fmt = "%.1f") %>% append(., "")
Then I wanted to ensure that showlegend was true for all but the NA (light gray) states. I created a vector of T and F.
legs <- c(rep(TRUE, 5), FALSE)
Then I added these to the plot.
invisible(lapply(1:length(pp$x$data),
function(i){
pp$x$data[[i]]$name <<- nm[i]
pp$x$data[[i]]$showlegend <<- legs[i]
}))

How do I add the degree symbol and letters to each value along the x- and y-axis of a graph

So I am trying to add the degree symbol and some letters to the axis values of my graph to make them look like longitude and latitudes.
My current graph:
Want to make the axis look like this graph (with e.g., 90°N etc.)
This is the code I am using to generate my current graph:
image.plot(lon_baseline_temp, lat_baseline_temp, dat_baseline_temp,
col=rev(brewer.pal(11,"RdBu")), xlab="",
ylab="",
main="Global surface temperature (Baseline)", sub="Year 1970 ~ 1999", font.sub=2,
legend.lab="K", legend.line=2.5, legend.mar=7,
xaxp=c(-180, 180, 6), yaxp=c(-90, 90, 6), las=1)
title(ylab = expression(paste("Latitude "(degree))), line = 2, cex.lab = 1)
title(xlab = expression(paste("Longitude "(degree))), line = 2.5, cex.lab = 1)
minor.tick(nx = 5, ny = 5, tick.ratio = 0.5)
map(database = 'world', add = T, lwd=1.5)
I would really appreciate any help on this soon, thank you very much!
I cant use your data but I think you just need to use a specify your labels as follows:
#some example plot
g <- ggplot() + geom_point(aes(50,50)) + ylim(0,100) + xlim(0,100) + labs(y = "Latitude",x = "Longitude")
#plot it
g
#add a new scale with specific labels
g + scale_y_continuous(breaks = c(0,25,50,75,100),
limits = c(0,100),
labels = c(expression(0~degree),
expression(25~degree),
expression(50~degree),
expression(75~degree),
expression(100~degree)
)
) +
labs(y = "Latitude",x = "Longitude")
#plot
g

How to assign dates on x-axis to the barplot in R?

I have multiple dates data set that I would like to plot using barplot functions in R. The data is for two different periods so I want to have its respective dates on the x-axis for ease of comparison. Here is my code so far. A_Date is for dataset in A while B_Date is for dataset contain in B.
A= runif(24, min = 25, max = 45)
B=runif(24, min = 35, max = 100)
DF=rbind(A,B)
A_Date= as.data.frame(seq(as.Date("1987-01-01"), to= as.Date("1988-12-31"),by="months"))
names(A_Date)= "Dates"
A_Date$year=as.numeric(format(A_Date$Dates, "%Y"))
A_Date$month=as.numeric(format(A_Date$Dates, "%m"))
A_Date=A_Date[,-1]
A_Date = as.character(paste(month.abb[A_Date$month], A_Date$year, sep = "_" ))
B_Date= as.data.frame(seq(as.Date("2010-01-01"), to= as.Date("2011-12-31"),by="months"))
names(B_Date)= "Dates"
B_Date$year=as.numeric(format(B_Date$Dates, "%Y"))
B_Date$month=as.numeric(format(B_Date$Dates, "%m"))
B_Date=B_Date[,-1]
B_Date = as.character(paste(month.abb[B_Date$month], B_Date$year, sep = "_" ))
barplot(DF, beside = T, col = c("red","darkblue"), legend.text =c("1987-88", "2010-11"), args.legend =list(x="topleft", cex = 1.2, bty="n", x.intersp=0.2),
ylab = "Precipitation (mm)", cex.axis = 1.2, cex.lab=1.5)
Also, I would like to have x-axis line (just like the line on y-axis.
Thank you
barplot also throws a coordinate matrix, which we may catch by assignment, here by b <-. Now we can make an axis with ticks at the right places. To avoid that the plot becomes too crowded, we could unify the redundant month information and just split the different years in mtextlines. I've used here built-in month.abbs.
b <- barplot(DF, beside=T, col=c("red","darkblue"),
legend.text=c("1987-88", "2010-11"),
args.legend=list(x="topleft", cex=1.2, bty="n", x.intersp=0.2),
ylab="Precipitation (mm)", cex.axis=1.2, cex.lab=1.5, ylim=c(0, 130))
axis(1, at=b[1, ], labels=FALSE)
axis(1, at=b[2, ], labels=FALSE)
mtext(rep(c(1987, 1988), each=12), 1, 1, at=b[1, ], cex=.8, las=2)
mtext(rep(c(2010, 2011), each=12), 1, 1, at=b[2, ], cex=.8, las=2)
mtext(rep(month.abb, 2), 1, 3, at=colMeans(b), las=2)
 
Result
If you'd also like to close the gap between y and x axis, you could add this line:
abline(h=0, cex=1.3)
I feel like it's going to be hard to fit all 4 dates into one spot on the axis. Here is the best I could come up with. I also rearranged your data so it fits all in one dataframe and used ggplot2.
library(tidyverse)
new_df <- tibble(precip = runif(48, c(25, 25), c(45,100)),
dates = c(seq(as.Date("1987-01-01"), as.Date("1988-12-31"), by = "months"),
seq(as.Date("2010-01-01"), as.Date("2011-12-31"), by = "months")),
group = ifelse(lubridate::year(dates) %in% c(1987,1988), "1987-88", "2010-11"),
month = lubridate::month(dates))
ggplot(new_df, aes(x = month, y = precip, fill = group)) +
geom_bar(stat = 'identity', position = position_dodge()) +
scale_x_continuous(labels = paste0(1:12, "/1987 - 1988", "\n", 1:12, "/2010 - 2011"),
breaks = 1:12) +
scale_fill_manual(values = c("red", "navy")) +
theme_classic() +
theme(legend.title = element_blank(),
axis.text = element_text(size = 10))

How to show plotted data with big value differences?

I have the data car_crashes that I am plotting using ggplot. It has 3 different data sets as seen below
but since Average of Cars is huge, the other values do not show even bit because they are in the range of 100. If I remove the average of cars data, the plot actually looks like this
Is there a way I can show all the data in one plot so that at least I can see the num of crashes plot?
The code I used is below:
carcrashes_figure <- ggplot()+geom_area(aes(YEAR_WW,AverageofCars,group = 1,colour = 'Average of cars'),car_crashes,fill = "dodgerblue1",alpha = 0.4)+
geom_line(aes(YEAR_WW,averageofcars,group = 1,linetype ='num of crashes'),car_crashes,fill = "dodgerblue3",colour = "dodgerblue3",size = 1.6) +
geom_line(aes(car_crashes$YEAR_WW,constantline,group = 1, size = 'constant line' ),car_crashes1,fill = "green4",colour = "green4")+
theme_bw() +
theme(axis.text.x = element_text(angle=70, vjust=0.6, face = 'bold'))+
theme(axis.text.y = element_text(angle=0, vjust=0.2, face = 'bold'))+
scale_colour_manual('', values = "dodgerblue1")+
scale_size_manual('',values = 1.4)+
scale_linetype_manual('',values = 1)+
scale_y_continuous()+
theme(legend.text = element_text(size = 8, colour = "black", angle = 0))
carcrashes_figure
I agreed the idea, using a separate y-axis by #Jim Quirk. As far as I know, ggplot2 isn't very good at doing it, so I used basic plot.
# making example ts_data
set.seed(1); data <- matrix(c(rnorm(21, 1000, 100), rnorm(21, 53, 10), rep(53, 21)), ncol=3)
ts_data <- ts(data, start = 1980, frequency = 1)
par(mar=c(4, 4.2, 1.5, 4.2)) # enlarge a right margin
# plot(ts_data[,1]) # check y-range
plot(ts_data[,2:3], plot.type = "single", ylab="num of crashes & constant line",
col=c(2,3), ylim=c(35,100), lwd=2) # draw "num of crashes" and "constant line"
par(usr = c(par("usr")[1:2], 490, 1310)) # set the second y coordinates
axis(4) # write it on the right side
polygon(x = c(1980:2000, rev(1980:2000)), y = c(ts_data[,1], rep(0,21)),
col="#0000FF20", border = "blue") # paint "Average of cars"
mtext(side=4, "Average of cars", line=2.5)
legend("topright",paste(c("num of crashes","constant line","Average of cars")),
pt.cex=c(0,0,3), lty=c(1,1,0), pch=15, cex=0.9, col=c(2, 3, "#0000FF20"), bty="n",
inset=c(0.02,-0.02), y.intersp=1.5)

How to invert the y-axis on a plot

I would like to know how to make a plot in R where the y-axis is inverted such that the plotted data appears in what would be the fourth quadrant (IV) of a cartesian plane, as opposed to the first (I) quadrant.
For reference, the plot I am trying to make looks very similar to the following (source):
I have found a number of questions online pertaining to reversing the numbering on the y-axis, but these all still plot the data in the first quadrant. Can anyone suggest how I might produce a plot similar to that shown above?
Just to provide a worked out answer, following the comments of #timriffe and #joran...
Use the function for minor log ticks from this answer:
minor.ticks.axis <- function(ax,n,t.ratio=0.5,mn,mx,...){
lims <- par("usr")
if(ax %in%c(1,3)) lims <- lims[1:2] else lims[3:4]
major.ticks <- pretty(lims,n=5)
if(missing(mn)) mn <- min(major.ticks)
if(missing(mx)) mx <- max(major.ticks)
major.ticks <- major.ticks[major.ticks >= mn & major.ticks <= mx]
labels <- sapply(major.ticks,function(i)
as.expression(bquote(10^ .(i)))
)
axis(ax,at=major.ticks,labels=labels,...)
n <- n+2
minors <- log10(pretty(10^major.ticks[1:2],n))-major.ticks[1]
minors <- minors[-c(1,n)]
minor.ticks = c(outer(minors,major.ticks,`+`))
minor.ticks <- minor.ticks[minor.ticks > mn & minor.ticks < mx]
axis(ax,at=minor.ticks,tcl=par("tcl")*t.ratio,labels=FALSE)
}
Make some reproducible example data:
x <- 1:8
y <- 10^(sort(runif(8, 1, 10), decreasing = TRUE))
Plot without axes:
plot(x, log10(y), # function to plot
xlab="", # suppress x labels
type = 'l', # specify line graph
xlim = c(min(x), (max(x)*1.3)), # extend axis limits to give space for text annotation
ylim = c(0, max(log10(y))), # ditto
axes = FALSE) # suppress both axes
Add fancy log axis and turn tick labels right way up (thanks #joran!):
minor.ticks.axis(2, 9, mn=0, mx=10, las=1)
Add x-axis up the top:
axis(3)
Add x-axis label (thanks for the tip, #WojciechSobala)
mtext("x", side = 3, line = 2)
And add an annotation to the end of the line
text(max(x), min(log10(y)), "Example", pos = 1)
Here's the result:
Answering the question in the title, the best/easiest way to invert the axis is to flip the limit variables around:
> plot(1:10, xlim=c(1,10));
> plot(1:10, xlim=c(10,1));
> plot(1:10, ylim=c(10,1));
Doing it this way means that you don't need to mess around with axes that are different from the image coordinates.
This can be combined with the 'xaxt="n"' parameter and an additional axis command to place an axis on another side:
> plot(1:10, ylim=c(10,1), xaxt="n"); axis(3);
It's now quite easy to reverse the y-axis using scale_y_reverse and specify position = "top" for the x-axis in ggplot2
Example
library(ggplot2)
library(scales)
set.seed(99)
Date <- seq(from = as.Date("2017-12-01"), to = as.Date("2017-12-15"),
by = "days")
Flux <- runif(length(Date), 1, 10000)
Flux_df <- data.frame(Date, Flux)
p1 <- ggplot(Flux_df, aes(Date, Flux)) +
geom_col() +
xlab("") +
scale_x_date(position = "top", breaks = pretty_breaks(), expand = c(0, 0)) +
scale_y_reverse(expand = expand_scale(mult = c(0.2, 0))) +
theme_bw(base_size = 16) +
theme(panel.border = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line()) +
theme(legend.position = "none")
p1
If we want both logarithmic and reverse axis, we need a workaround suggested here as ggplot2 does not have that option atm
reverselog_trans <- function(base = exp(1)) {
trans <- function(x) -log(x, base)
inv <- function(x) base^(-x)
scales::trans_new(paste0("reverselog-", format(base)), trans, inv,
scales::log_breaks(base = base), domain = c(1e-100, Inf))
}
p1 + scale_y_continuous(trans = reverselog_trans(10),
breaks = scales::trans_breaks("log10", function(x) 10^x),
labels = scales::trans_format("log10", scales::math_format(10^.x)),
expand = expand_scale(mult = c(0.2, 0))) +
annotation_logticks()

Resources