Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - coherentpath/explorer: Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir · GitHub
Skip to content

Repository files navigation

Explorer

CIDocumentationPackage

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

Features and design

Explorer high-level features are:

  • Simply typed series: :binary, :boolean, :category, :date, :datetime, :duration, floats of 32 and 64 bits ({:f, size}), integers of 8, 16, 32 and 64 bits ({:s, size}, {:u, size}), :null, :string, :time, :list, and :struct.

  • A powerful but constrained and opinionated API, so you spend less time looking for the right function and more time doing data manipulation.

  • Support for CSV, Parquet, NDJSON, and Arrow IPC formats

  • Integration with external databases via ADBC and direct connection to file storages such as S3

  • Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on remote databases or even Spark dataframes.

  • The first (and default) backend is based on NIF bindings to the blazing-fast polars library.

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:

  • By constraining your options, it helps you think about your data manipulation challenges.

  • It provides simple “verbs”, functions that correspond to the most common data manipulation tasks, to help you translate your thoughts into code.

  • It uses efficient backends, so you spend less time waiting for the computer.

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:

  • the elegance of dplyr
  • the speed of polars
  • the joy of Elixir

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.

Getting started

Inside an Elixir script or Livebook:

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

Or in the mix.exs file of your application:

defdepsdo[{:explorer,"~> 0.11.1"}]end

Explorer will download a precompiled version of its native code upon installation. You can force a local build by setting the environment variable EXPLORER_BUILD=1 and including :rustler as a dependency:

{:explorer,"~> 0.11.1",system_env: %{"EXPLORER_BUILD"=>"1"}},{:rustler,">= 0.0.0"}

If necessary, clean up before rebuilding with mix deps.clean explorer.

A glimpse of the API

We have two ways to represent data with Explorer:

  • using a series, that is similar to a list, but is guaranteed to contain items of one data type only - or one dtype for short. Notice that nil values are permitted in series of any dtype.

  • using a dataframe, that is just a way to represent one or more series together, and work with them as a whole. The only restriction is that all the series share the same size.

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:

  • by reading from files or memory using the IO functions. This is by far the most common way to load dataframes in Explorer. We accept Parquet, IPC, CSV, and NDJSON files.

  • by using the Explorer.DataFrame.new/2 function, that is neat for small experiments. We are going to use this function here.

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 s64 [8848, 8611, 6962]
>

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

Explorer.DataFrame.print(mountains)

Prints:

+-------------------------------------------+
| Explorer DataFrame: [rows: 3, columns: 2] |
+---------------------+---------------------+
| name | elevation |
| <string> | <s64> |
+=====================+=====================+
| 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:

requireExplorer.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 defining them. This is possible due to 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 s64 [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.

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. If you already have Rustup and a recent version of Cargo installed, then the correct version of Rust is going to be installed in the first compilation of the project. Otherwise, you can manually install the correct version:

rustup toolchain install nightly-2025-06-23

You can also use asdf:

asdf install rust nightly-2025-06-23

It's possible that you may need to install CMake in order to build the project, if that is not already installed.

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

Our integration tests require the AWS CLI to be installed, and also a container engine that can be Podman or Docker.

Once these dependencies are installed, you need to run the mix localstack.setup command, and then run the cloud integration tests with mix test --only cloud_integration.

Just to recap, here is the combo of commands you need to run:

mix ci
mix localstack.setup
mix test --only cloud_integration

Precompilation

Explorer ships with the NIF code precompiled for the most popular architectures out there. We support the following:

  • aarch64-apple-darwin - MacOS running on ARM 64 bits CPUs.
  • aarch64-unknown-linux-gnu - Linux running on ARM 64 bits CPUs, compiled with GCC.
  • aarch64-unknown-linux-musl - Linux running on ARM 64 bits CPUs, compiled with Musl.
  • x86_64-apple-darwin - MacOS running on Intel/AMD 64 bits CPUs.
  • x86_64-pc-windows-msvc - Windows running on Intel/AMD 64 bits CPUs, compiled with Visual C++.
  • x86_64-pc-windows-gnu - Windows running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-gnu - Linux running on Intel/AMD 64 bits CPUs, compiled with GCC.
  • x86_64-unknown-linux-musl - Linux running on Intel/AMD 64 bits CPUs, compiled with Musl.
  • x86_64-unknown-freebsd - FreeBSD running on Intel/AMD 64 bits.

This means that Explorer is going to work without the need to compile it from source.

This currently only works for Hex releases. For more information on how it works, please check the RustlerPrecompiled project.

Legacy CPUs

We ship some of the precompiled artifacts with modern CPU features enabled by default. But in case your computer is not compatible with them, you can set an application environment that is going to be read at compile time, enabling the legacy variants of artifacts.

config:explorer,use_legacy_artifacts: true

If you see the error message "Illegal instruction" after your project compiles, you need to enable the legacy artifacts.

Features disabled

Some of the features cannot be compiled to some targets, because one of the dependencies don't work on it.

This is the case for the NDJSON reads and writes, that don't work for the RISCV target. We also disable the AWS S3 reads and writes for the RISCV target, because one of the dependencies of ObjectStore does not compile on it.

Sponsors

Amplified

About

Series (one-dimensional) and dataframes (two-dimensional) for fast and elegant data exploration in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages