Explorer

CIDocumentationPackage

Explorer brings series (one-dimensional) and dataframes (two-dimensional) for fast data exploration to Elixir.

Getting started

Inside an Elixir script or Livebook:

Mix.install([
  {:explorer, "~> 0.5.0"}
])

Or in the mix.exs file of your application:

def deps do
  [
    {:explorer, "~> 0.5.0"}
  ]
end

A glimpse of the API

We have two ways to represent data with Explorer:

A series can be created from a list:

fruits = Explorer.Series.from_list(["apple", "mango", "banana", "orange"])

Your newly created series is going to look like:

#Explorer.Series<
  Polars[4]
  string ["apple", "mango", "banana", "orange"]
>

And you can, for example, sort that series:

Explorer.Series.sort(fruits)

Resulting in the following:

#Explorer.Series<
  Polars[4]
  string ["apple", "banana", "mango", "orange"]
>

Dataframes

Dataframes can be created in two ways:

You can pass either series or lists to it:

mountains = Explorer.DataFrame.new(name: ["Everest", "K2", "Aconcagua"], elevation: [8848, 8611, 6962])

Your dataframe is going to look like this:

#Explorer.DataFrame<
  Polars[3 x 2]
  name string ["Everest", "K2", "Aconcagua"]
  elevation integer [8848, 8611, 6962]
>

It's also possible to see a dataframe like a table, using the Explorer.DataFrame.table/2 function:

Explorer.DataFrame.table(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
|        name         |      elevation      |
|      <string>       |      <integer>      |
+=====================+=====================+
| Everest             | 8848                |
+---------------------+---------------------+
| K2                  | 8611                |
+---------------------+---------------------+
| Aconcagua           | 6962                |
+---------------------+---------------------+

And now I want to show you how to filter our dataframe. But first, let's require the Explorer.DataFrame module and give a short name to it:

require Explorer.DataFrame, as: DF

The "require" is needed to load the macro features of that module. We give it a shorter name to simplify our examples.

Now let's go to the filter. I want to filter the mountains that are above the mean elevation in our dataframe:

DF.filter(mountains, elevation > mean(elevation))

You can see that we can refer to the columns using their names, and use functions without define them. This is possible due the powerful Explorer.Query features, and it's the main reason we need to "require" the Explorer.DataFrame module.

The result is going to look like this:

#Explorer.DataFrame<
  Polars[2 x 2]
  name string ["Everest", "K2"]
  elevation integer [8848, 8611]
>

There is an extensive guide that you can play with Livebook: Ten Minutes to Explorer

You can also check the Explorer.DataFrame and Explorer.Series docs for further details.

Features and design

Explorer high-level features are:

The API is heavily influenced by Tidy Data and borrows much of its design from dplyr. The philosophy is heavily influenced by this passage from dplyr's documentation:

The aim here isn't to have the fastest dataframe library around (though it certainly helps that we're building on Polars, one of the fastest). Instead, we're aiming to bridge the best of many worlds:

That means you can expect the guiding principles to be 'Elixir-ish'. For example, you won't see the underlying data mutated, even if that's the most efficient implementation. Explorer functions will always return a new dataframe or series.

Contributing

Explorer uses Rust for its default backend implementation, and while Rust is not necessary to use Explorer as a package, you need Rust tooling installed on your machine if you want to compile from source, which is the case when contributing to Explorer.

We require Rust Nightly, which can be installed with Rustup.

rustup toolchain install nightly-2023-02-14

You can also use asdf:

asdf install rust nightly-2023-02-14

Once you have made your changes, run mix ci, to lint and format both Elixir and Rust code.

Sponsors

<img src="sponsors/amplified.png" width=100 alt="Amplified">