When I plot my data without explicitly setting the x coordinate range (the fourth and lowest plot in the screenshot, "Sonntag"), par("usr")
tells me what range R uses by default:
> par("usr")
[1] -68.912 1797.112 0.000 87.400
But when I set the x coordinate range to those values using xlim = c(-68.912, 1797.112)
, the plot is narrower (the third plot in the screenshot, "Samstag"). And when I set a range that is narrower than the default, the x-axis extends beyond the plot window (the first and second plot in the screenshot).
The data on the x-axis is a factor:
steps$time <- factor(format(steps$start_time, "%H:%M"),
levels = format(seq(as.POSIXct("2020-02-20 00:00:00"),
as.POSIXct("2020-02-20 23:59:00"),
by = "1 min"),
"%H:%M"
)
)
I want to limit the plot to the "time" between 6:00 and 23:00, which should correspond to the factor levels 360 and 1380 (used in the top two plots). How can I achieve that?
Here is the full plot code:
par(mfrow = c(4, 1), mar = c(5.1, 2.1, 4.1, 2.1))
barplot(count ~ time, dat[dat$group == "A",], xlim = c(360, 1380), ylim = c(0, max(dat$count)), col = "violet", border = NA, xlab = "", ylab = "", yaxt = "n", main = "Montag bis Donnerstag")
barplot(count ~ time, dat[dat$group == "B",], xlim = c(360, 1380), ylim = c(0, max(dat$count)), col = "violet", border = NA, xlab = "", ylab = "", yaxt = "n", main = "Freitag")
barplot(count ~ time, dat[dat$group == "C",], xlim = c(-68.912, 1797.112), ylim = c(0, max(dat$count)), col = "violet", border = NA, xlab = "", ylab = "", yaxt = "n", main = "Samstag")
barplot(count ~ time, dat[dat$group == "D",], ylim = c(0, max(dat$count)), col = "violet", border = NA, xlab = "", ylab = "", yaxt = "n", main = "Sonntag")
Data is here: https://pastebin.com/tnNwFywX
You're facing the problem, that barplot
also plots unused levels, which is why @DaveArmstrong used droplevels
. Here is a small example to illustrate this:
foo <- data.frame(a=1:6, b=replace(rep_len(0, 6), 3:4, 100)) |>
transform(a1=as.factor(a))
par(mfrow=1:2)
barplot(b ~ a1, data=subset(foo, a > 1 & a < 6))
barplot(b ~ droplevels(a1), data=subset(foo, a > 1 & a < 6))
Note, that in barplot
, the x-axis denotes bar indices {1, 2, ..., n}, and positions are returned invisibly which you might exploit here. To get nice time labels, coerce factor times to character that are easily sub-settable using relational operators like >= '06:00'
and this also works alphabetically. You won't need xlim
then which might be tedious to use in this case.
Crucial is using ylim
on the entire range of count to make bar charts comparable, though.
You also might turn group into a factor with the corresponding day labels, which will help when using a for
loop.
dat <- dat |>
transform(
time_chr=as.character(time),
days=factor(group, labels=c("Montag bis Donnerstag", "Freitag", "Samstag",
"Sonntag"))
)
Make sure, that there are no "holes" in the data, i.e. times really are within 6–23 o'clock and otherwise have equal intervals, as specified in the OP, e.g., via a check like:
stopifnot(with(dat, min(time_chr) <= '06:00' & max(time_chr) >= '23:00')) ## for safety
stopifnot(all(diff(strptime(unique(dat$time), '%H:%M')) == 1))
Finally, just use endsWith()
to find the full hours then.
png('foo.png', 800, 800)
par(mfrow=c(4, 1), mar=c(5, 2, 4, 2) + 0.1)
for (g in unique(dat$group)) {
pdat <- subset(dat, group == g & time_chr >= '06:00' & time_chr <= '23:00')
b <- barplot(count ~ time_chr, pdat, xaxt='n', col='violet', border=NA,
ylim=range(dat$count), xlab="", ylab="", yaxt="n", main=unique(pdat$days))
full_h <- endsWith(pdat$time_chr, '00')
axis(1, at=b[full_h], labels=pdat$time_chr[full_h])
}
dev.off()