Skip to content

Instantly share code, notes, and snippets.

@fschuindt
Last active September 21, 2020 01:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fschuindt/e008bd05c9dda4ab050dcf0c7b824198 to your computer and use it in GitHub Desktop.
Save fschuindt/e008bd05c9dda4ab050dcf0c7b824198 to your computer and use it in GitHub Desktop.
defmodule SunLightExperiment do
@moduledoc false
alias Decimal, as: D
require Logger
@doc false
def perform do
with data <- read_data("data.csv"),
starts_growth <- compute_growth(data, :starts),
ends_growth <- compute_growth(data, :ends),
size_growth <- compute_growth(data, :size),
starts_growth_average <- average(starts_growth),
ends_growth_average <- average(ends_growth),
size_growth_average <- average(size_growth) do
Logger.info("Printing results...")
IO.inspect(data)
IO.puts("\n'starts' growth: #{inspect(starts_growth)}\n")
IO.puts("'ends' growth: #{inspect(ends_growth)}\n")
IO.puts("'size' growth: #{inspect(size_growth)}\n")
IO.puts("'starts' growth average: #{inspect(starts_growth_average)}\n")
IO.puts("'ends' growth average: #{inspect(ends_growth_average)}\n")
IO.puts("'size' growth average: #{inspect(size_growth_average)}")
end
end
@spec read_data(String.t()) :: Enumerable.t()
defp read_data(file) do
file
|> File.stream!()
|> CSV.decode()
|> Stream.take(10)
|> Stream.map(&get_valid_row/1)
|> Stream.drop(1)
|> Stream.map(&trim_columns/1)
|> Stream.map(&to_entry/1)
|> Enum.filter(fn entry -> entry != %{} end)
end
@spec compute_growth(list(map()), atom()) :: list(D.t())
defp compute_growth(results, v) do
Enum.reduce(results, [], fn result, acc ->
case result == Enum.at(results, 0) do
true ->
acc
_any ->
acc ++ [D.sub(Map.get(result, v), tnm1(results, v, Enum.count(acc) + 1))]
end
end)
end
@spec average(list(D.t())) :: D.t()
defp average(list) do
with count <- Enum.count(list),
count <- D.new(count),
sum <- Enum.reduce(list, D.new(0), fn e, acc -> D.add(e, acc) end) do
D.div(sum, count)
end
end
@spec tnm1(list(map()), atom(), integer()) :: D.t()
defp tnm1(results, variable, n) do
results
|> Enum.at(n - 1)
|> Map.get(variable)
end
@spec get_valid_row({:ok, list(String.t())} | any()) :: list(String.t())
defp get_valid_row(result) do
case result do
{:ok, row} -> row
_any -> []
end
end
@spec to_entry(list(String.t())) :: {:ok, Entry.t()} | {:error, Error.t()}
defp to_entry([id, filename, taken_at, starts, ends]) do
with {:ok, taken_at, _offset} <- DateTime.from_iso8601(taken_at),
{starts, _any} <- D.parse(starts),
{ends, _any} <- D.parse(ends),
entry <- do_to_entry(id, filename, taken_at, starts, ends) do
entry
end
end
defp to_entry(_any) do
%{}
end
@spec do_to_entry(String.t(), String.t(), DateTime.t(), D.t(), D.t()) :: map()
defp do_to_entry(id, filename, taken_at, starts, ends) do
%{
id: id,
filename: filename,
taken_at: taken_at,
starts: starts,
ends: ends,
size: D.sub(ends, starts)
}
end
@spec trim_columns(list(String.t())) :: list(String.t())
defp trim_columns(row) do
Enum.map(row, fn
column when is_binary(column) -> String.trim(column)
column -> column
end)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment