A Clojure dataframe library for larger-than-memory datasets. Process millions of rows with lazy evaluation and parallel execution—all with familiar dataframe operations.
| Feature | Description |
|---|---|
| Larger than memory | Stream data from disk—no need to fit everything in RAM |
| Lazy & parallel | Operations are pipelined and executed across multiple threads |
| Relational operations | Filter, transform, group-by, aggregate, join, with arbitrary Clojure functions |
| File-to-file | Read CSV in, write CSV out, with built-in IO |
| Native types | Works with standard Clojure and Java types |
| Faster than Dask | Benchmarks show significant speedups on large datasets |
Add to project.clj:
[com.github.clojure-finance/clojask "2.1.2"]
Or deps.edn:
com.github.clojure-finance/clojask {:mvn/version "2.1.2"}
Basic example (using Employees-example.csv):
(require '[clojask.dataframe :as ck])
;; Load a CSV
(def df (ck/dataframe "Employees-example.csv"))
;; Preview the data (the second row shows each column's type)
(ck/print-df df)
;; | Employee | EmployeeName | Department | Salary | UpdateDate |
;; |------------------+------------------+------------------+------------------+------------------|
;; | java.lang.String | java.lang.String | java.lang.String | java.lang.String | java.lang.String |
;; | 1 | Alice | 11 | 300 | 2020/12/12 |
;; | 2 | Bob | 11 | 600 | 2020/12/01 |
;; ...
;; Set column types
(ck/set-type df "Salary" "double")
(ck/set-type df "UpdateDate" "date:yyyy/MM/dd")
;; Transform data: give Bob a raise, as a new column
;; (an operation over several columns must write to a new column)
(ck/operate df
(fn [name salary]
(if (= name "Bob") (+ salary 100) salary))
["EmployeeName" "Salary"]
"NewSalary")
;; Compute with 8 threads, output to file
(ck/compute df 8 "results.csv"
:select ["Employee" "EmployeeName" "Department" "NewSalary" "UpdateDate"])
;; results.csv:
;; Employee,EmployeeName,Department,NewSalary,UpdateDate
;; 1,Alice,11,300.0,2020/12/12
;; 2,Bob,11,700.0,2020/12/01
;; ...

Solid arrows show required sequence; dotted arrows show optional paths.
Available operations:
dataframeset-type, set-parser, set-formatteroperate, rename-col, select-col, delete-col, reorder-col, get-col-namesfiltergroup-by, aggregateinner-join, left-join, right-join, outer-join, rolling-join-forward, rolling-join-backwardcompute (with options such as :select, :exclude, :melt, :header), print-df, previewsort stands outside this pipeline: it sorts the dataframe's source file on disk and writes the result directly to an output file.
Upgrading from 2.0.x: clojask no longer uses Onyx, so the JVM flags it needed (--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED, --enable-native-access=ALL-UNNAMED) and the clojask.zookeeper.* / clojask.aeron.* / CLOJASK_* port and directory settings can be removed.
Operations are collected lazily and executed together when you call compute, streaming data through your transformation pipeline without loading the full dataset into memory. Each compute runs in your JVM: one thread reads the input in batches, a pool of worker threads (the number you pass to compute) applies the operations and filters, and the calling thread writes the results in input order. Group-by and joins first sort the rows into groups by key, kept in temporary files under .clojask/ (or in memory with :in-memory true); group-by and outer joins write their results in group order.
Found a bug or have a question? Check the existing issues or open a new one.
Can you improve this documentation? These fine people already did:
Yuchen Liu, Matt Buehlmaier, awoo424, clojure-finance, c-sungho & Angel WooEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |