CL-Sci is a Common Lisp library designed to provide numerical computing capabilities similar to NumPy and data manipulation functionalities akin to Pandas. It includes a minimal numerical library numcl
and a column-oriented dataframe
structure backed by the numerical library, making it suitable for scientific computing and data analysis.
This library includes a minimal numerical library numcl
and a column-oriented dataframe
that is backed by the numerical library. The library is designed for ease of use and flexibility in handling numerical data and performing mathematical operations.
There is a lot to do to get to creating machine learning models, but right now I left off with broadcasting numcl arrays.
This README is incomplete and needs to be updated. The code has complete documentation strings.
This project is quicklisp installable but is not on quicklisp. Instead, download to your local projects.
(ql:quickload :cl-sci)
For manual installation, clone the repository and load it using ASDF:
(asdf:load-system :cl-sci)
Style can be one of :none, :spec or :dot.
(ql:quickload :cl-sci/tests)
(rove::run :cl-sci/tests)
OR
(ql:quickload :cl-sci/tests)
(asdf:test-system :cl-sci)
The basic data structure of NumCL is an array that is always displaced to a row-major array.
sci:pp-ndarray
is a pretty printing function for NumCL arrays.
(sci:pp-ndarray (sci:make-ndarray '((1 2 3) (4 5 6) (7 8 9)) :element-type 'double-float))
#2A((1.0d0 2.0d0 3.0d0)
(4.0d0 5.0d0 6.0d0)
(7.0d0 8.0d0 9.0d0))
(sci:dtype (sci:make-ndarray '((1 2 3) (4 5 6) (7 8 9)) :element-type 'double-float))
DOUBLE-FLOAT
(sci:pp-ndarray (sci:make-ndarray '((1.0 2.0 3.0) (4.0 5.0 6.0) (7.0 8.0 9.0))))
#2A((1.0 2.0 3.0)
(4.0 5.0 6.0)
(7.0 8.0 9.0))
(sci:dtype (sci:make-ndarray '((1.0 2.0 3.0) (4.0 5.0 6.0) (7.0 8.0 9.0))))
SINGLE-FLOAT
;; Generate 5 values from 0 to 10, including the endpoint.
(sci:linspace 0 10 :num 5)
#(0 2.5 5.0 7.5 10.0)
;; Generate 3 values from 1.0 to 2.0 without including 2.0.
(sci:linspace 1.0 2.0 :num 3 :endpoint nil)
#(1.0 1.3333333 1.6666667)
;; Generate 5 integer values from 1 to 5.
(sci:linspace 1 5 :num 5 :dtype 'integer)
#(1 2 3 4 5)
; Zeros (default type bit)
(sci:pp-ndarray (sci:zeros '(2 3)))
#2A((0 0 0)
(0 0 0))
; Ones (default type bit)
(sci:pp-ndarray (sci:ones '(2 3)))
#2A((1 1 1)
(1 1 1))
; Random (default type single-float)
(sci:pp-ndarray (sci:rand '(2 3)))
#2A((0.58396804 0.6492634 0.86330223)
(0.7186488 0.03097415 0.632234))
; Range creation
(sci:arange 5)
#(0 1 2 3 4)
; Range step and stop
(sci:arange 1 :stop 10 :step 2)
#(1 3 5 7 9)
There is a deftype of numcl-array
this is satisfied by not being a simple array and being backed by a displaced array and passing numcl-array-p
; Numcl Array
(sci:numcl-array-p (sci:make-ndarray '((1.0 2.0 3.0) (4.0 5.0 6.0) (7.0 8.0 9.0))))
T
; Regular Lisp Array
(sci:numcl-array-p (make-array 10 :element-type 'double-float))
NIL
; Shape
(sci:shape (sci:rand '(2 3)))
(2 3)
; Datatype
(sci:dtype (sci:rand '(2 3)))
SINGLE-FLOAT
; Reshape
(sci:pp-ndarray (sci:reshape (sci:rand '(2 3)) '(3 2)))
#2A((0.8456712 0.12792969)
(0.09574175 0.19673777)
(0.88547397 0.26244366))
; Number of Dimensions
(sci:ndim (sci:rand '(2 3)))
2
; Item size
(sci:itemsize (sci:rand '(2 3)))
4
; Bytes consumed
(sci:nbytes (sci:rand '(2 3)))
24
; Base (do the arrays share the same memory space)
(defvar x (sci:rand '(2 3)))
X
(defvar y x)
Y
(sci:base x y)
T
; Transpose
(sci:pp-ndarray x)
#2A((0.06520164 0.12508643 0.12239957)
(0.26312304 0.7637329 0.17990577))
(sci:pp-ndarray (sci:transpose x))
#2A((0.06520164 0.26312304)
(0.12508643 0.7637329)
(0.12239957 0.17990577))
NumCL provides several functions for normalizing arrays and calculating different norms. Below are the key functions and usage examples:
Normalize the values in an array to a range of [0, 1]:
(defparameter *normalized-array* (sci:min-max *your-array*))
Compute the Z-score normalization of an array:
(defparameter *zscore-normalized* (sci:zscore *your-array*))
Perform max normalization on an array:
(defparameter *max-normalized* (sci:max-scale *your-array*))
Compute the norm of an array with optional axis and order:
(defparameter *norm-value* (sci:norm *your-array* :axis 0 :ord 2))
The norm
function is versatile, allowing for the computation of norms for vectors and matrices, with the ability to specify the axis along which to compute the norm. By default, computes the L2 (Euclidean) norm. Supports L1 and L2 norms for vectors and matrices.
The DataFrame functionality allows for easy manipulation of tabular data. Below are some examples of how to create and manipulate dataframes.
You can create a DataFrame by importing data from a CSV file using the df-from-csv
function:
(defparameter *df* (df-from-csv "path/to/your/file.csv"))
- Get a Column: Retrieve a specific column by its name:
(df-get-column *df* "Column1")
- Get a Row: Retrieve a specific row by its index:
(df-get-row *df* 0) ; Gets the first row
To drop a column from the DataFrame:
(df-drop-column *df* "ColumnToDrop")
- Display the First N Rows: Use the
head
function to display the first N rows:
(head *df* 5) ; Displays the first 5 rows
- Display the Last N Rows: Use the
tail
function to display the last N rows:
(tail *df* 5) ; Displays the last 5 rows
Contributions are welcome! Please feel free to submit a pull request or open an issue for any enhancements or bug fixes.
- Andrew Medeiros (andrew@amedeiros.com)
Copyright (c) 2025 Andrew Medeiros (andrew@amedeiros.com)
Lice 4A5A nsed under the MIT License. For more details, see the LICENSE file.