Packages

Dataset provides a simple abstraction for managing tabular sets of data.

Current section

Files

Jump to
dataset lib dataset.ex
Raw

lib/dataset.ex

defmodule Dataset do
defstruct rows: [], labels: {}
@moduledoc ~S"""
Datasets represent labeled tabular data. Datasets are enumerable:
iex> Dataset.new([{:a, :b, :c},
...> {:A, :B, :C},
...> {:i, :ii, :iii},
...> {:I, :II, :III}],
...> {"one", "two", "three"})
...> |> Enum.map(&elem(&1, 2))
[:c, :C, :iii, :III]
Datasets are also collectable:
iex> for x <- 0..10, into: Dataset.empty({:n}), do: x
%Dataset{labels: {:n}, rows: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
"""
@doc ~S"""
Construct a new dataset. A dataset is a list of tuples. With no
arguments, an empty dataset with zero columns is constructed. Withf
one argument a dataset is constructed with the passed object
interpreted as rows and labels beginning with `0` are generated, the
number of which are determined by size of the first tuple in the
data.
iex> Dataset.new()
%Dataset{rows: [], labels: {}}
iex> Dataset.new([{:foo, :bar}, {:eggs, :ham}])
%Dataset{rows: [foo: :bar, eggs: :ham], labels: {0, 1}}
iex> Dataset.new([{0,0}, {1, 1}, {2, 4}, {3, 9}],
...> {:x, :x_squared})
%Dataset{labels: {:x, :x_squared}, rows: [{0, 0}, {1, 1}, {2, 4}, {3, 9}]}
"""
def new(rows \\ [], labels \\ nil)
def new(rows, nil) do
labels = default_labels(rows)
new(rows, labels)
end
def new(rows, labels) do
%Dataset{rows: rows, labels: labels}
end
def empty(labels \\ nil) do
new([], labels)
end
defp default_labels([]), do: {}
defp default_labels([h | _t]) do
0..(tuple_size(h) - 1) |> Enum.to_list() |> List.to_tuple()
end
def to_map_list(_ds = %Dataset{rows: rows, labels: labels}) do
for row <- rows do
Map.new(tuple_zip(labels, row))
end
end
defp tuple_zip(t1, t2) do
for i <- 0..(tuple_size(t1) - 1) do
{elem(t1, i), elem(t2, i)}
end
end
@doc ~S"""
iex> Dataset.new([{:a,:b,:c},
...> {:A, :B, :C},
...> {:i, :ii, :iii},
...> {:I, :II, :III}])
...> |> Dataset.rotate()
%Dataset{
labels: {0, 1, 2, 3},
rows: [{:a, :A, :i, :I},
{:b, :B, :ii, :II},
{:c, :C, :iii, :III}]
}
"""
def rotate(%Dataset{rows: in_rows}) do
for i <- 0..(tuple_size(List.first(in_rows)) - 1) do
List.to_tuple(for in_row <- in_rows, do: elem(in_row, i))
end
|> Dataset.new()
end
end
defimpl Enumerable, for: Dataset do
def reduce(_list, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(ds = %Dataset{}, {:suspend, acc}, fun) do
{:suspended, acc, &reduce(ds, &1, fun)}
end
def reduce(%Dataset{rows: []}, {:cont, acc}, _fun) do
{:done, acc}
end
def reduce(%Dataset{rows: [h | t], labels: ls}, {:cont, acc}, fun) do
reduce(%Dataset{rows: t, labels: ls}, fun.(h, acc), fun)
end
def count(%Dataset{rows: []}), do: {:ok, 0}
def count(%Dataset{rows: rows}), do: {:ok, Enum.count(rows)}
def member?(_ds = %Dataset{}, _el), do: {:error, __MODULE__}
def slice(_ds = %Dataset{}), do: {:error, __MODULE__}
end
defimpl Collectable, for: Dataset do
def into(o) do
collector = fn
%Dataset{rows: rows, labels: labels}, {:cont, el} ->
Dataset.new([el | rows], labels)
%Dataset{rows: rows, labels: labels}, :done ->
%Dataset{rows: Enum.reverse(rows), labels: labels}
_set, :halt ->
:ok
end
{o, collector}
end
end