Add wordcounts plot script
This commit is contained in:
@@ -16,3 +16,7 @@ format:
|
||||
|
||||
clean-format:
|
||||
rm thesis.fmt thesis.log thesis.tex.bbl thesis.tex.blg
|
||||
|
||||
# Plot wordcounts.svg with ticks every n days (default: 7).
|
||||
plot-wordcounts days="7":
|
||||
Rscript plot_wordcounts.R {{quote(days)}}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env Rscript
|
||||
# Usage: Rscript plot_wordcounts.R [tick_days] [input.csv] [output.svg]
|
||||
# By default, label every seven days, independently of the data points.
|
||||
|
||||
library(ggplot2)
|
||||
|
||||
args <- commandArgs(trailingOnly = TRUE)
|
||||
if (length(args) > 3L) {
|
||||
stop("Usage: Rscript plot_wordcounts.R [tick_days] [input.csv] [output.svg]")
|
||||
}
|
||||
tick_days <- if (length(args) >= 1L) {
|
||||
as.numeric(args[1])
|
||||
} else {
|
||||
7
|
||||
}
|
||||
|
||||
script_arg <- grep("^--file=", commandArgs(), value = TRUE)[1]
|
||||
script_dir <- dirname(normalizePath(sub("^--file=", "", script_arg)))
|
||||
input <- if (length(args) >= 2L) {
|
||||
args[2]
|
||||
} else {
|
||||
file.path(script_dir, "wordcounts.csv")
|
||||
}
|
||||
output <- if (length(args) >= 3L) {
|
||||
args[3]
|
||||
} else {
|
||||
file.path(script_dir, "wordcounts.svg")
|
||||
}
|
||||
|
||||
counts <- read.csv(input, colClasses = "character")
|
||||
|
||||
timezone <- Sys.getenv("TZ", unset = "Europe/Berlin")
|
||||
if (!nzchar(timezone)) {
|
||||
timezone <- "Europe/Berlin"
|
||||
}
|
||||
normalized <- sub("([+-][0-9]{2}):([0-9]{2})$", "\\1\\2", counts$date)
|
||||
counts$timestamp <- as.POSIXct(
|
||||
normalized,
|
||||
format = "%Y-%m-%dT%H:%M:%S%z",
|
||||
tz = timezone
|
||||
)
|
||||
date_only <- grepl("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", counts$date)
|
||||
counts$timestamp[date_only] <- as.POSIXct(
|
||||
counts$date[date_only],
|
||||
format = "%Y-%m-%d",
|
||||
tz = timezone
|
||||
)
|
||||
counts$wordcount <- as.numeric(counts$wordcount)
|
||||
counts <- counts[order(counts$timestamp), ]
|
||||
|
||||
# Fixed time intervals
|
||||
start <- as.POSIXct(
|
||||
format(min(counts$timestamp), "%Y-%m-%d", tz = timezone),
|
||||
tz = timezone
|
||||
)
|
||||
interval <- tick_days * 86400
|
||||
span <- as.numeric(difftime(max(counts$timestamp), start, units = "secs"))
|
||||
ticks <- start + seq(0, max(1, ceiling(span / interval))) * interval
|
||||
|
||||
plot <- ggplot(counts, aes(x = timestamp, y = wordcount)) +
|
||||
scale_x_datetime(
|
||||
breaks = ticks,
|
||||
limits = range(ticks),
|
||||
date_labels = "%Y-%m-%d",
|
||||
timezone = timezone,
|
||||
minor_breaks = NULL
|
||||
) +
|
||||
scale_y_continuous(labels = scales::label_comma()) +
|
||||
labs(
|
||||
title = "Thesis Word Count",
|
||||
x = "Date",
|
||||
y = "Words"
|
||||
) +
|
||||
theme_minimal(base_size = 12) +
|
||||
theme(
|
||||
axis.text.x = element_text(angle = 45, hjust = 1),
|
||||
panel.grid.minor = element_blank()
|
||||
)
|
||||
if (nrow(counts) > 1L) {
|
||||
plot <- plot + geom_line(linewidth = 0.5)
|
||||
}
|
||||
|
||||
ggsave(output, plot, width = 10, height = 5, dpi = 180, bg = "white")
|
||||
message("Saved ", output)
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
date,wordcount
|
||||
2026-06-23T12:00:00+02:00,496
|
||||
2026-06-30T12:00:00+02:00,2210
|
||||
2026-07-01T10:00:00+02:00,3745
|
||||
2026-07-01T12:00:00+02:00,4587
|
||||
2026-07-02T12:00:00+02:00,5058
|
||||
2026-07-05T12:00:00+02:00,5783
|
||||
2026-07-07T12:00:00+02:00,5538
|
||||
2026-09-11T11:24:33+02:00,8603
|
||||
2026-09-11T11:26:13+02:00,8603
|
||||
2026-09-11T11:31:20+02:00,8596
|
||||
2026-09-11T11:42:48+02:00,8727
|
||||
2026-09-11T11:43:08+02:00,8727
|
||||
2026-09-11T11:47:47+02:00,8746
|
||||
2026-09-11T12:01:42+02:00,8812
|
||||
2026-09-11T12:03:40+02:00,8860
|
||||
2026-09-11T12:07:01+02:00,8887
|
||||
2026-09-11T12:07:14+02:00,8888
|
||||
2026-09-11T12:07:31+02:00,8884
|
||||
2026-09-11T12:08:51+02:00,8892
|
||||
2026-09-11T12:09:39+02:00,8894
|
||||
2026-09-11T12:11:27+02:00,8913
|
||||
2026-09-11T12:12:29+02:00,8924
|
||||
2026-09-11T12:13:22+02:00,8920
|
||||
2026-09-11T12:13:42+02:00,8927
|
||||
2026-09-11T12:14:31+02:00,8927
|
||||
2026-09-11T12:15:46+02:00,8946
|
||||
2026-09-11T12:20:47+02:00,9070
|
||||
2026-09-11T12:23:46+02:00,9099
|
||||
2026-09-11T12:24:02+02:00,9102
|
||||
2026-09-11T12:24:42+02:00,9109
|
||||
2026-09-11T12:26:15+02:00,9109
|
||||
2026-09-11T12:26:25+02:00,9109
|
||||
2026-09-11T12:26:38+02:00,9109
|
||||
2026-09-11T12:30:20+02:00,9137
|
||||
2026-09-11T12:30:35+02:00,9137
|
||||
2026-09-11T12:31:24+02:00,9115
|
||||
2026-09-11T12:34:33+02:00,9124
|
||||
2026-09-11T12:36:17+02:00,9129
|
||||
2026-09-11T12:36:44+02:00,9113
|
||||
2026-09-11T12:42:09+02:00,9167
|
||||
2026-09-11T12:42:35+02:00,9167
|
||||
2026-09-11T12:53:02+02:00,9235
|
||||
2026-09-11T12:53:46+02:00,9234
|
||||
2026-09-11T12:54:27+02:00,9235
|
||||
2026-09-11T12:55:42+02:00,9235
|
||||
2026-09-11T12:56:42+02:00,9250
|
||||
2026-09-11T12:58:59+02:00,9379
|
||||
2026-09-11T12:59:54+02:00,9389
|
||||
2026-09-11T13:00:27+02:00,9389
|
||||
2026-09-11T13:00:43+02:00,9389
|
||||
2026-09-11T13:04:39+02:00,9427
|
||||
2026-09-11T13:04:55+02:00,9485
|
||||
2026-09-11T13:06:04+02:00,9488
|
||||
2026-09-11T13:07:00+02:00,9496
|
||||
2026-09-11T13:09:11+02:00,9494
|
||||
2026-09-11T13:10:20+02:00,9504
|
||||
2026-09-11T13:10:37+02:00,9509
|
||||
2026-09-11T13:10:45+02:00,9508
|
||||
2026-09-11T13:12:12+02:00,9510
|
||||
2026-09-11T13:17:28+02:00,9639
|
||||
2026-09-11T13:20:11+02:00,9686
|
||||
2026-09-11T13:20:35+02:00,9686
|
||||
2026-09-11T13:30:23+02:00,9704
|
||||
2026-09-11T13:31:28+02:00,9713
|
||||
2026-09-11T13:34:02+02:00,9740
|
||||
2026-09-11T13:34:11+02:00,9738
|
||||
2026-09-11T13:36:51+02:00,9771
|
||||
2026-09-11T13:37:45+02:00,9768
|
||||
2026-09-11T13:39:55+02:00,9778
|
||||
2026-09-11T13:40:19+02:00,9779
|
||||
2026-09-11T13:40:55+02:00,9779
|
||||
2026-09-11T13:41:22+02:00,9779
|
||||
2026-09-11T13:44:40+02:00,9800
|
||||
2026-09-11T13:44:47+02:00,9801
|
||||
2026-09-11T13:45:09+02:00,9801
|
||||
2026-09-11T13:47:18+02:00,9801
|
||||
2026-09-11T13:47:55+02:00,9801
|
||||
2026-09-11T13:49:17+02:00,9769
|
||||
2026-09-11T13:50:04+02:00,9906
|
||||
2026-09-11T16:31:06+02:00,9952
|
||||
2026-09-11T16:32:21+02:00,9967
|
||||
2026-09-11T16:40:24+02:00,10123
|
||||
2026-09-11T16:40:56+02:00,10123
|
||||
2026-09-11T16:41:08+02:00,10127
|
||||
2026-09-11T16:43:54+02:00,10166
|
||||
2026-09-11T16:44:23+02:00,10170
|
||||
2026-09-11T16:44:54+02:00,10170
|
||||
2026-09-11T16:45:46+02:00,10170
|
||||
2026-09-11T16:46:07+02:00,10170
|
||||
2026-09-12T11:57:09+02:00,10170
|
||||
2026-09-12T12:05:20+02:00,10170
|
||||
2026-09-12T12:05:36+02:00,10169
|
||||
2026-09-12T13:09:05+02:00,10172
|
||||
2026-09-12T13:10:19+02:00,10188
|
||||
2026-09-12T13:10:53+02:00,10197
|
||||
2026-09-12T13:12:10+02:00,10201
|
||||
2026-09-12T13:14:00+02:00,10242
|
||||
2026-09-12T13:14:23+02:00,10228
|
||||
2026-09-12T13:15:51+02:00,10227
|
||||
2026-09-12T13:17:15+02:00,10230
|
||||
2026-09-12T13:18:49+02:00,10224
|
||||
2026-09-12T13:25:07+02:00,10225
|
||||
2026-09-12T13:26:35+02:00,10236
|
||||
2026-09-12T13:27:44+02:00,10236
|
||||
2026-09-12T13:28:26+02:00,10237
|
||||
2026-09-12T13:58:35+02:00,10237
|
||||
|
Reference in New Issue
Block a user