A lightweight, dependency-free data engine for R that provides a grammar for tabular and time-series manipulation. Built entirely on Base R, 'm61r' offers a fluent, chainable API inspired by modern data tools while prioritizing memory efficiency and speed. It includes optimized versions of common data verbs such as filtering, mutation, grouped aggregation, and approximate temporal joins, making it an ideal choice for environments where external dependencies are restricted or where performance in pure R is required.
m61r provides dependency-free tabular and time-series manipulation for R. It is designed for the Base R purist who demands modern syntax and high performance.
Zero Dependencies: No dplyr, no tidyverse. It runs on any vanilla R installation.
Unified Logic: Treat tabular and time series as one.
High Performance: Optimized Base R verbs for tabular accounting.
Fluent API: A clean, object-oriented API inspired by the best modern data tools
Transparent: Written in 100% Base R
library(m61r)
# Initialize with the built-in CO2 dataset
co2 <- m61r(CO2)
co2$filter(~uptake > 30)$
select(~c(Plant, Type, uptake))$
group_by(~Type)$
summarise(mean_uptake = ~mean(uptake))
print(co2[])
ts_data <- data.frame(
time = as.POSIXct("2025-01-01 00:00:00") + runif(50, 0, 86400),
consumption = rnorm(50, 500, 100)
)
p_agg <- m61r(ts_data)
p_agg$mutate(hour_bin = ~format(time, "%Y-%m-%d %H:00"))
p_agg$group_by(~hour_bin)
p_agg$summarise(
n_obs = ~length(consumption),
avg_load = ~mean(consumption)
)
p_agg$head(5)
library(microbenchmark)
library(m61r)
set.seed(42)
n <- 1e6
df_large <- data.frame(
id = 1:n,
group = sample(letters, n, replace = TRUE),
val = rnorm(n)
)
p <- m61r(df_large)
results <- microbenchmark(
# Approche Base R classique
base_r = {
tmp <- df_large[df_large$val > 0, ]
tmp$new_val <- tmp$val * 2
head(tmp)
},
# Approche m61r
m61r_pipe = {
p$filter(~val > 0)
p$mutate(new_val = ~val * 2)
p$head()
},
times = 50
)
print(results)
Unit: milliseconds
expr |
min |
lq |
mean |
median |
uq |
max |
neval |
|---|---|---|---|---|---|---|---|
| base_r | 37.79 | 42.08 | 49.75 | 44.39 | 59.48 | 92.38 | 50 |
| m61r_pipe | 20.11 | 21.94 | 27.10 | 23.78 | 26.19 | 55.45 | 50 |
results_group <- microbenchmark(
# Base R avec aggregate
base_aggregate = {
aggregate(val ~ group, data = df_large, FUN = mean)
},
# m61r
m61r_grouped = {
p$group_by(~group)
p$summarise(avg = ~mean(val))
p[]
},
times = 20
)
print(results_group)
Unit: milliseconds
expr |
min |
lq |
mean |
median |
uq |
max |
neval |
|---|---|---|---|---|---|---|---|
| base_aggregate | 329.80 | 350.84 | 361.70 | 354.05 | 372.74 | 442.04 | 20 |
| m61r_grouped | 146.57 | 147.70 | 154.79 | 149.88 | 165.77 | 172.25 | 20 |
install.packages("m61r")
setwd("~")
download.file("https://github.com/pv71u98h1/m61r/archive/0.1..zip",destfile="m61r-0.1.0.zip")
unzip("m61r-0.1.0.zip")
# install
install.packages("m61r", repos=NULL, type='source')
unlink(file.path("~","m61r-0.1.0"),recursive=TRUE)
file.remove(file.path("~","m61r-0.1.0.zip"))