Do you remember the time when you were watching Star Wars and were breath-taken by starships crossing the lightspeed and instantly travelling to another galaxy? If so, I would like to show you how to play with Star Wars API (SWAPI) using Elixir Livebook. You can follow the link to learn more about SWAPI.
What is the Livebook?
You've probably heard about Jupyter which is a notebook for Python which is willingly used by data scientists and ML engineers. Inspired by its success and based on powerful Phoenix Live View this kind of notebook was created for the Elixir ecosystem
Preparing our environment
Let's clone the official Github repo and then install dependencies:
git clone https://github.com/elixir-nx/livebook.git
cd livebook
mix deps.get --only prod
# Run the Livebook server
MIX_ENV=prod mix phx.server
Create a new notebook and name it whatever you want. Did everything go smoothly? Now, we have to start by installing the required dependencies.
Mix.install([
{:tesla, "~> 1.4"},
{:jason, ">= 1.0.0"},
{:vega_lite, "~> 0.1.0"},
{:kino, "~> 0.2.0"}
])
Tesla and Jason let us play with an external API and Vega Lite together with Kino are required to use charts.
Creating a module with a client function will simplify our requests later on. We can obtain it by using Tesla.client/2 function with the middlewares list inside.
defmodule ForceClient do
require Tesla
def client(url) do
Tesla.client([
{Tesla.Middleware.BaseUrl, url},
Tesla.Middleware.JSON
])
end
end
We can fetch our data now. For this example, I will get https:/swapi.dev/api/vehicles
using previously created client and Tesla.get/3 function.
url = "https://swapi.dev/api"
{:ok, %{body: body}} = ForceClient.client(url) |> Tesla.get("/vehicles")
The fastest one is the coolest? Let's try to get name of the fastest vehicle. We have to get the object with the highest "max_atmosphering_speed"
and then get its name.
body["results"]
|> Enum.reduce(fn x, acc ->
if String.to_integer(x["max_atmosphering_speed"]) <
String.to_integer(acc["max_atmosphering_speed"]) do
acc
else
x
end
end)
|> Map.get("name")
Now we would like to see our data with diagrams. We will start with adding an alias:
alias VegaLite, as: Vl
After that, we can create our first chart. It's going to be a line chart of max atmospheric speed and length relationship. First, we have to parse our data to provide a suitable format.
data =
body["results"]
|> Enum.map(
&%{
"length" => elem(Float.parse(&1["length"]), 0),
"max_atmosphering_speed" => elem(Float.parse(&1["max_atmosphering_speed"]), 0)
}
)
[
%{"length" => 36.8, "max_atmosphering_speed" => 30.0},
%{"length" => 10.4, "max_atmosphering_speed" => 1.2e3},
%{"length" => 3.4, "max_atmosphering_speed" => 250.0},
...
]
And then we can create a graph using Vl.new/1 and passing our data.
Vl.new(width: 750, height: 400)
|> Vl.data_from_values(data)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "length")
|> Vl.encode_field(:y, "max_atmosphering_speed")
The next thing we will prepare is a pie chart of the count of vehicles having a specific class. As previously, we have to prepare data:
data =
body["results"]
|> Enum.frequencies_by(&Map.get(&1, "vehicle_class"))
|> Enum.map(fn {k, v} -> %{"Class" => k, "value" => v} end)
[
%{"Class" => "airspeeder", "value" => 1},
%{"Class" => "assault walker", "value" => 1},
%{"Class" => "repulsorcraft", "value" => 3},
...
]
And then generate pie chart:
Vl.new(width: 600, height: 400)
|> Vl.data_from_values(data)
|> Vl.mark(:arc)
|> Vl.encode_field(:theta, "value", type: :quantitative)
|> Vl.encode_field(:color, "Class", type: :nominal)
|> Vl.config(view: [stroke: nil])
The last thing we would like to do is a pretty table generated with markdown. Kino library provides an easy way to deal with it, so don't waste time and prepare it using Kino.Markdown.new/1 function.
"""
## Pretty table
| Name | Vehicle class | Manufacturer |
| -----| ------------- | ------------ |
#{Enum.map(body["results"], &("| #{&1["name"]} | #{&1["vehicle_class"]} | #{&1["manufacturer"]} |\n"))}
"""
|> Kino.Markdown.new()
Summary
Did you enjoy it? Livebook provides much more interesting features, which you can find in the official examples. Here you can get the whole notebook file.