Clay is a minimalistic Clojure tool for data visualization and literate programming, compatible with the Kindly convention. It allows to conduct visual data explorations and create documents (HTML pages like this one, books, blog posts, reports, slideshows) from source code and comments.
Source:
Artifact:
Status: The project has moved into Beta stage (March 2024).
Clay is developed by Timothy Pratley & Daniel Slutsky in parallel and in coordination with Claykind, a tool with similar goals which is build in a more thoughtful process, aiming at a more modular structure.
Goals
Easily explore & share data visualizations and notebooks for others to easily pick & use.
Encourage writing Kindly-compatible notes for future compatiblity with other tools.
Flow with the REPL: encourage user interactions that flow naturally with the typical use of Clojure in editors and REPLs.
Getting started
Add Clay to your project dependencies:
(If you like to use aliases, you may add under it to the extra dependencies under an alias. See, for example, the deps.edn file of Noj. If you do not know what this means, just add it under the main :deps section of your deps.edn file.)
To render a given Clojure namespace, say "notebooks/index.clj", you may run the following in the REPL:
Evaluate and render the namespace in "notebooks/index.clj" as a Quarto qmd file then, using Quarto, render that file as HTML and show it at the browser:
Evaluate and render the namespace in "notebooks/slides.clj" as a Quarto qmd file (using its namespace-specific config from the ns metadata) then, using Quarto, render that file as HTML and show it at the browser:
Evaluate and render the namespace in "notebooks/slides.clj" as a Quarto qmd file (using its namespace-specific config from the ns metadata) then, using Quarto, render that file as a reveal.js slideshow and show it at the browser:
Evaluate and render the namespace in "notebooks/index.clj" as a Quarto qmd file with a custom Quarto config then, using Quarto, render that file as HTML and show it at the browser:
Evaluate and render the namespace in "notebooks/index.clj" as a Quarto qmd file with a custom Quarto config where the higlight style is fetched from the scicloj.clay.v2.quarto.highlight-styles namespace, and the theme is fetched from the scicloj.clay.v2.quarto.themes namespace, then, using Quarto, render that file as HTML and show it at the browser:
Plain data structures (lists and sequnces, vectors, sets, maps) are pretty printed if there isn’t any value inside which needs to be displayed in special kind of way.
Linear Model:Residuals: Min 1Q Median 3Q Max -0.8282 -0.22020.01910.19920.8457Coefficients: Estimate Std. Error t value Pr(>|t|)Intercept 1.85600.25087.40100.0000 ***sepal_width 0.65080.06669.76540.0000 ***petal_width -0.55650.1275 -4.36290.0000 ***petal_length 0.70910.056712.50250.0000 ***---------------------------------------------------------------------Significance codes: 0'***'0.001'**'0.01'*'0.05'.'0.1 ' ' 1Residual standard error: 0.3145 on 146 degrees of freedomMultiple R-squared: 0.8586, Adjusted R-squared: 0.8557F-statistic: 295.5391 on 4and146 DF, p-value: 8.588e-62
Observable visualizations are supported when rendering through Quarto.
The following is adapted from the Penguins example in Quarto’s documentation.
Note that you can save your Clojure data as a csv file and refer to it from within your Observable code. See Referring to files for more information. In this case, we are using the local file, "notebooks/datasets/palmer-penguins.csv", which is transparently copied by Clay alongside the target HTML.
(kind/reagent ['(fn [] [:div {:style {:height"200px"}:ref (fn [el] (let [m (-> js/L (.map el) (.setView (clj->js [51.505 -0.09])13))] (-> js/L .-tileLayer (.provider "OpenStreetMap.Mapnik") (.addTo m)) (-> js/L (.marker (clj->js [51.5 -0.09])) (.addTo m) (.bindPopup "A pretty CSS popup.<br> Easily customizable.") (.openPopup))))}])];; Note we need to mention the dependency: {:html/deps [:leaflet]})
D3
The following example is adapted from hiccup-d3. The code is a bit different, e.g. (.scaleOrdinal js/d3 (.-schemeCategory10 js/d3)) instead of (d3/scaleOrdinal d3/schemeCategory10). We still need to figure out how to make hiccup-d3’s examples work as they are.
Clay supports rendering plots through the JS client side of ggplotly - an R package offering a Plotly fronted for ggplot2’s grammar of graphics implementation. This package is part of the htmlwidgets ecosystem, and we represent that in the kind’s name.
The following is a work-in-progress attempt to generate JSON specs of the kind consumed by ggplotly’s client side.
The following spec function was generaged by mimicking R’s ggplotly(ggplot(mtcars, aes(wt, mpg)) + geom_point()). Therefore, some parts are hard-coded and require generalization. Other parts are missing (e.g., specifying colours).
(kind/reagent ['(fn [{:keys [data-pdb]}] [:div {:style {:height"400px":width"400px":position:relative}:class"viewer_3Dmoljs":data-pdb data-pdb:data-backgroundcolor"0xffffff":data-style"stick":data-uitrue}]) {:data-pdb"2POR"}];; Note we need to mention the dependency: {:html/deps [:three-d-mol]})
Using 3Dmol within your code (inspired by these examples):
Clojure Delays are a common way to define computations that do not take place immediately. The computation takes place when dereferencing the value for the first time.
Clay makes sure to dererence Delays when passing values for visualization.
This is handy for slow example snippets and explorations, that one would typically not like to slow down the evaluation of the whole namespace, but would like to visualize them on demand and also include in them in the final document.
(delay (Thread/sleep 500) (+12))
3
Referring to files
In data visualizations, one can directly refrer to files places under "notebooks/" or "src/". By default, all files except of these directories, except for Clojure files, are copied alongside the HTML target.
This default can be overridden using the :subdirs-to-sync config option. E.g., :subdirs-to-sync ["notebooks" "data"] will copy files from the "notebooks" and "data" directories, but not from "src".
By default, a Clay notebook shows both the code and the result of an evaluated form. Here are a few ways one may hide the code:
Add the metadata :kindly/hide-code true to the form (e.g., by preceding it with ^:kindly/hide-code).
Add the metadata :kindly/hide-code true to the value.
Globally define certain kinds (e.g., :kind/md, :kind/hiccup) to always hide code (on project level or namespace level) by adding thme as a set to the project config or namespace config, e.g., :kindly/options {:kinds-that-hide-code #{:kind/md :kind/hiccup}}.