numpyとpyplotのコード翻訳 NxとVegaでどう書くの？

<!-- livebook:{"persist_outputs":true} -->

# はじめに
この記事は[機械学習のエッセンス](https://www.sbcr.jp/product/4797393965/)のPythonによる数値計算の章の NumPyと PyPlotのコードをNxとVega Liteで書き換えてみた内容になります。

https://www.sbcr.jp/product/4797393965/

# ライブラリインストール
以下LivebookのExportにちょこちょこコメントを追加した内容になります
最初にnxとkino_veta_liteをインストールします

```elixir
Mix.install([
  {:nx, "~> 0.2"},
  {:kino_vega_lite, "~> 0.1.1"}
])
```

<!-- livebook:{"output":true} -->

```
:ok
```

```elixir
alias VegaLite, as: Vl
```

<!-- livebook:{"output":true} -->

```
VegaLite
```


## 04-02 NumPyの基本

```elixir
# np.exp(2)
Nx.exp(2)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  7.389056205749512
>
```

```elixir
# np.log(np.e)
Nx.exp(1) |> Nx.log()
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  0.9999999403953552
>
```

ここが問題です
Elixirの浮動小数点の問題なのかNx.sinが通常のsinと違うのかはわかりません
erlangの:mathモジュールを使えば正常な値になります

```elixir
# np.sin(np.pi)

:math.pi() |> Nx.sin() |> IO.inspect()
:math.pi() |> :math.sin() |> Nx.tensor()
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  -8.742277657347586e-8
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  1.2246468525851679e-16
>
```

```elixir
# np.sqrt(3)
Nx.sqrt(3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  1.7320507764816284
>
```

## NumPyの配列
Elixirだと特定のインデックスの値を取り出すにはEnum.at等を使用しないといけないのですが、
Nxですと添字で値が取得できます

```elixir
# a = np.array([2,3,5,7,8])
a = Nx.tensor([2, 3, 5, 7, 8]) |> IO.inspect()
a[0]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [2, 3, 5, 7, 8]
>

#Nx.Tensor<
  s64
  2
>
```

Rangeでも取得できます

```elixir
# a[1:3]
IO.inspect(a)
a[1..2]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [2, 3, 5, 7, 8]
>

#Nx.Tensor<
  s64[2]
  [3, 5]
>
```

配列の最後を表すのはちょっと特殊で `-1//1`でアクセスします
以下は後ろから２番めです

```elixir
# a[2:-1]
IO.inspect(a)
a[2..-2//1]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [2, 3, 5, 7, 8]
>

#Nx.Tensor<
  s64[2]
  [5, 7]
>
```

Nx.iotaで指定した数の０からの行列を作成します

```elixir
# b = np.arange(5)
b = Nx.iota({5})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

ここでまた問題です
Nxはnp.arangeの引数１から引数２まで第３引数刻みで配列を作成するに該当する関数がありません
ですが安心してください、iotaと他の関数を組み合わせてnp.arrangeと同等の値が得ることができます

```elixir
# np.arange(1,3,0.2)
c = Nx.iota({10}) |> Nx.multiply(0.2) |> Nx.add(1) |> IO.inspect()
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[10]
  [1.0, 1.2000000476837158, 1.399999976158142, 1.600000023841858, 1.7999999523162842, 2.0, 2.200000047683716, 2.4000000953674316, 2.5999999046325684, 2.8000001907348633]
>
```
関数化するとこんな感じになります

```elixir
arange = fn s, e, step ->
  Kernel.-(e, s)
  |> Kernel./(step)
  |> Kernel.trunc()
  |> then(&Nx.iota({&1}))
  |> Nx.multiply(step)
  |> Nx.add(s)
end

arange.(1, 3, 0.2)

```
<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[10]
  [1.0, 1.2000000476837158, 1.399999976158142, 1.600000023841858, 1.7999999523162842, 2.0, 2.200000047683716, 2.4000000953674316, 2.5999999046325684, 2.8000001907348633]
>
```

type関数はテンソルのデータ型を表します
この場合は　signed float 64になります

```elixir
Nx.type(a)
```

<!-- livebook:{"output":true} -->

```
{:s, 64}
```

データタイプは作成時に指定することもできます

```elixir
Nx.tensor([1, 2, 4], type: :s64)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [1, 2, 4]
>
```

## ２次元配列

作成方法はnumpyとかわりありません

```elixir
# a = np.array([[2, 3, 4], [5 ,6, 7]], dtype=np.float64)
a = Nx.tensor([[2, 3, 4], [5, 6, 7]])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

数値に対するアクセスは、他言語(Ruby,JS)でよくある多重配列へのアクセスと同じです

```elixir
# a[0, 1]
IO.inspect(a)
a[0][1]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  3
>
```

縦方向で値を取得するときは、numpyに近い書き方になります

```elixir
# a[:, 1]
IO.inspect(a)
a[[0..-1//1, 1]]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2]
  [3, 6]
>
```

横方向を取得するのはシンプルにできます

```elixir
# a[1, :]
IO.inspect(a)
a[1]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [5, 6, 7]
>
```

横方向のn番目の2~最後こんな感じ

```elixir
# a[0, 2:]
IO.inspect(a)
a[0][2..-1//1]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1]
  [4]
>
```

```elixir
# a[0, :2]
IO.inspect(a)
a[0][0..1]
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2]
  [2, 3]
>
```

## 配列のデータ属性

iotaで生成したテンソルにreshapeを実行して変形します

```elixir
# a = np.arange(15.).reshape(3, 5)
a = Nx.iota({15}) |> Nx.reshape({3, 5})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][5]
  [
    [0, 1, 2, 3, 4],
    [5, 6, 7, 8, 9],
    [10, 11, 12, 13, 14]
  ]
>
```

shapeは行列の形を返します

```elixir
# a.shape
Nx.shape(a)
```

<!-- livebook:{"output":true} -->

```
{3, 5}
```

rankは行列の深さを返します

```elixir
# a.ndim
Nx.rank(a)
```

<!-- livebook:{"output":true} -->

```
2
```

sizeは要素数を返します

```elixir
# a.size
Nx.size(a)
```

<!-- livebook:{"output":true} -->

```
15
```

## reshapeメソッドと形状の変更

```elixir
# a = np.arange(16.)
# c = a.reshape(4, -1)
a = Nx.iota({16}) |> IO.inspect()
c = Nx.reshape(a, {4, 4})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[16]
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4][4]
  [
    [0, 1, 2, 3],
    [4, 5, 6, 7],
    [8, 9, 10, 11],
    [12, 13, 14, 15]
  ]
>
```

```elixir
# c.ravel()
IO.inspect(c)
Nx.flatten(c)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4][4]
  [
    [0, 1, 2, 3],
    [4, 5, 6, 7],
    [8, 9, 10, 11],
    [12, 13, 14, 15]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[16]
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
```

縦方向に変換

```elixir
# b = np.arange(4.)
b = Nx.iota({4}) |> IO.inspect()
Nx.reshape(b, {4, 1}) |> IO.inspect()
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4][1]
  [
    [0],
    [1],
    [2],
    [3]
  ]
>
```


## その他の配列操作

Nxにはnp.zerosに該当する関数がないので
random_normalで形状と初期値と偏差を 0.0にして作成しています

```elixir
# a = np.zeros((3,4))
Nx.random_normal({3, 4}, 0.0, 0.0)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3][4]
  [
    [0.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 0.0]
  ]
>
```

np.onesも同様

```elixir
# b = np.ones((2,2))
Nx.random_normal({2, 2}, 1.0, 0.0)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[2][2]
  [
    [1.0, 1.0],
    [1.0, 1.0]
  ]
>
```


## 行列の連結

```elixir
a = Nx.iota({2, 3})　|> IO.inspect
b = Nx.iota({2, 3})|> Nx.add(6)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>

#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
```

```elixir
# 縦方向に連結
# np.r_[a, b]
IO.inspect(a)
IO.inspect(b)
Nx.concatenate([a, b])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [9, 10, 11]
  ]
>
```

```elixir
# 横方向に連結
# np._c(a,b)
IO.inspect(a)
IO.inspect(b)
Nx.concatenate([a, b], axis: 1)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][6]
  [
    [0, 1, 2, 6, 7, 8],
    [3, 4, 5, 9, 10, 11]
  ]
>
```

```elixir
c = Nx.iota({3}) |> IO.inspect()
d = Nx.iota({3}) |> Nx.add(3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [3, 4, 5]
>
```

```elixir
IO.inspect(c)
IO.inspect(d)
# np.r_[c,d]
Nx.concatenate([c, d]) |> IO.inspect()

# np.c_[c,d]
Nx.concatenate(
  [
    Nx.reshape(c, {4,1}),
    Nx.reshape(d,{4,1})
  ],
  axis: 1
)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
#Nx.Tensor<
  s64[3]
  [3, 4, 5]
>
#Nx.Tensor<
  s64[6]
  [0, 1, 2, 3, 4, 5]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][2]
  [
    [0, 3],
    [1, 4],
    [2, 5]
  ]
>
```

```elixir
# 形状が違うものを連結
# numpyではエラー
IO.inspect(a)
IO.inspect(c)
Nx.concatenate([a, c])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

new_axisで軸を足すか reshape(c, {1,3})を実行して合わせれば正常に連結できます


```elixir
c = Nx.new_axis(c, 0) |> IO.inspect()
Nx.concatenate([a, c])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1][3]
  [
    [0, 1, 2]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [0, 1, 2]
  ]
>
```

## 04-03 配列操作の基本

```elixir
a = Nx.iota({5})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

合計

```elixir
Nx.sum(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  10
>
```

平均

```elixir
Nx.mean(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32
  2.0
>
```

最大値

```elixir
Nx.reduce_max(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  4
>
```

最小値

```elixir
Nx.reduce_min(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  0
>
```

## ２次元配列の合計

```elixir
b = Nx.iota({3, 3})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

```elixir
Nx.sum(b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  36
>
```

縦軸で合計

```elixir
IO.inspect(b)
Nx.sum(b, axes: [0])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [9, 12, 15]
>
```

横軸で合計

```elixir
IO.inspect(b)
Nx.sum(b, axes: [1])
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [3, 12, 21]
>
```

## ブロードキャスト

```elixir
a = Nx.iota({5}) |> Nx.add(3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [3, 4, 5, 6, 7]
>
```

```elixir
Nx.exp(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[5]
  [20.08553695678711, 54.598148345947266, 148.4131622314453, 403.4288024902344, 1096.6331787109375]
>
```

```elixir
Nx.log(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[5]
  [1.0986123085021973, 1.3862943649291992, 1.6094379425048828, 1.7917594909667969, 1.945910096168518]
>
```

```elixir
Nx.sqrt(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[5]
  [1.7320507764816284, 2.0, 2.2360680103302, 2.4494898319244385, 2.6457512378692627]
>
```

```elixir
b = Nx.iota({3, 3})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

```elixir
Nx.exp(b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 2.7182817459106445, 7.389056205749512],
    [20.08553695678711, 54.598148345947266, 148.4131622314453],
    [403.4288024902344, 1096.6331787109375, 2980.9580078125]
  ]
>
```

## 配列とスカラの演算

```elixir
a = Nx.iota({5})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

```elixir
Nx.add(a, 3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [3, 4, 5, 6, 7]
>
```

```elixir
Nx.multiply(a, 3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 3, 6, 9, 12]
>
```

```elixir
Nx.power(a, 2)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 4, 9, 16]
>
```

```elixir
# ２以上の値を1　未満を０
IO.inspect(a)
Nx.greater_equal(a, 2)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  u8[5]
  [0, 0, 1, 1, 1]
>
```

```elixir
# 一致するもののみ１
IO.inspect(a)
Nx.not_equal(a, 3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  u8[5]
  [1, 1, 1, 0, 1]
>
```

```elixir
b = Nx.iota({3, 3})
IO.inspect(b)
Nx.greater(b, 3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  u8[3][3]
  [
    [0, 0, 0],
    [0, 1, 1],
    [1, 1, 1]
  ]
>
```

ブール値を要素として持つ配列の演算はNxにないというかT/Fがないので一度リストにして無理やりやるしかなさそうだった

## ブール値を要素として持つ配列の演算

```elixir
# a = np.array([10, 20, 30, 40])
# b = np.array([False, True, True, False])
# a[b]
# array([20, 30])
a = Nx.tensor([10, 20, 30, 40])
b = Nx.tensor([0, 1, 1, 0])
Nx.multiply(a, b) |> Nx.to_flat_list() |> Enum.reject(&(&1 == 0)) |> Nx.tensor()
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[2]
  [20, 30]
>
```

## 配列の演算

```elixir
u = Nx.iota({4})
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
```

```elixir
v = Nx.iota({4}) |> Nx.add(3)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
```

```elixir
IO.inspect(u)
IO.inspect(v)
Nx.add(u, v)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [3, 5, 7, 9]
>
```

```elixir
IO.inspect(u)
IO.inspect(v)

Nx.subtract(u, v)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [-3, -3, -3, -3]
>
```

```elixir
IO.inspect(u)
IO.inspect(v)

Nx.multiply(u, v)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 4, 10, 18]
>
```

```elixir
# ベクトルの内積
IO.inspect(u)
IO.inspect(v)

Nx.dot(u, v)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64
  32
>
```

## ２次元配列の演算

```elixir
a = Nx.iota({3, 3})
b = Nx.iota({3, 3}) |> Nx.add(4)
```


足し算

```elixir
IO.inspect(a)
IO.inspect(b)
Nx.add(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [4, 6, 8],
    [10, 12, 14],
    [16, 18, 20]
  ]
>
```

引き算

```elixir
IO.inspect(a)
IO.inspect(b)

Nx.subtract(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [-4, -4, -4],
    [-4, -4, -4],
    [-4, -4, -4]
  ]
>
```

掛け算

```elixir
IO.inspect(a)
IO.inspect(b)

Nx.multiply(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 5, 12],
    [21, 32, 45],
    [60, 77, 96]
  ]
>
```

割り算

```elixir
IO.inspect(a)
IO.inspect(b)

Nx.divide(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3][3]
  [
    [0.0, 0.20000000298023224, 0.3333333432674408],
    [0.4285714328289032, 0.5, 0.5555555820465088],
    [0.6000000238418579, 0.6363636255264282, 0.6666666865348816]
  ]
>
```

内積

```elixir
IO.inspect(a)
IO.inspect(b)

Nx.dot(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [27, 30, 33],
    [90, 102, 114],
    [153, 174, 195]
  ]
>
```

## 形状の違う行列の積

```elixir
a = Nx.iota({3, 3})
v = Nx.iota({3}) |> Nx.add(1)
```

```elixir
IO.inspect(a)
IO.inspect(v)

Nx.dot(a, v)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3]
  [1, 2, 3]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [8, 26, 44]
>
```

```elixir
IO.inspect(v)
IO.inspect(a)

Nx.dot(v, a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [1, 2, 3]
>

#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3]
  [24, 30, 36]
>
```

```elixir
u = Nx.new_axis(v, 0) |> Nx.transpose()
IO.inspect(a)
IO.inspect(u)
Nx.dot(a, u)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][1]
  [
    [1],
    [2],
    [3]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][1]
  [
    [8],
    [26],
    [44]
  ]
>
```

```elixir
# error
Nx.dot(u, a)
```

```elixir
w = Nx.new_axis(v, 0)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1][3]
  [
    [1, 2, 3]
  ]
>
```

```elixir
IO.inspect(w)
IO.inspect(a)

Nx.dot(w, a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1][3]
  [
    [1, 2, 3]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1][3]
  [
    [24, 30, 36]
  ]
>
```


## 04-04疎行列 

該当する関数なし

## 04-05 Numpy/SciPyによる線形代数

Nxには線形代数関連のLinAlgモジュールがあるので興味がある方はhexdocを覗いてみてください

## 逆行列を求める

```elixir
a = Nx.tensor([[3, 1, 1], [1, 2, 1], [0, -1, 1]])
Nx.LinAlg.invert(a)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3][3]
  [
    [0.4285714328289032, -0.2857142686843872, -0.1428571343421936],
    [-0.1428571343421936, 0.4285714328289032, -0.2857142686843872],
    [-0.1428571343421936, 0.4285714030265808, 0.714285671710968]
  ]
>
```

## slove関数を使う

```elixir
a = Nx.tensor([[3, 1, 1], [1, 2, 1], [0, -1, 1]])
b = Nx.tensor([1, 2, 3])
Nx.LinAlg.solve(a, b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3]
  [-0.5714285373687744, -0.14285710453987122, 2.857142686843872]
>
```

## LU分解により連立方程式を解く

```elixir
a = Nx.tensor([[3, 1, 1], [1, 2, 1], [0, -1, 1]])
b = Nx.tensor([1, 2, 3])
{_p, l, u} = Nx.LinAlg.lu(a)
Nx.dot(l, u) |> Nx.LinAlg.solve(b)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3]
  [-0.5714285969734192, -0.1428571492433548, 2.857142925262451]
>
```

## 04-06 乱数

## numpy.randomモジュールを使う

Nxにrandom_normalとは別に randorm_uniformがあります
こちらは第２引数から第３引数までの間の値をランダムに返します
マイナスの範囲にしたい場合は第２引数をマイナス値にすれば大丈夫です

```elixir
Nx.random_uniform({1}, 0.0, 1.0)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[1]
  [0.20401528477668762]
>
```

```elixir
Nx.random_uniform({3, 2}, 0.0, 1.0)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[3][2]
  [
    [0.20480242371559143, 0.5718821287155151],
    [0.7167996168136597, 0.3234789967536926],
    [0.8850403428077698, 0.806730329990387]
  ]
>
```

整数のランダム値が欲しい場合は上限値を整数にすればいけます

```elixir
Nx.random_uniform({1}, 0, 4)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[1]
  [3]
>
```

```elixir
Nx.random_uniform({3, 3}, 0, 5)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  s64[3][3]
  [
    [0, 2, 1],
    [3, 0, 2],
    [2, 4, 3]
  ]
>
```

## 乱数の種を指定する 

Nxにはseedを指定する関数はなさそうでした

## 04-07 データの可視化

## 折れ線グラフ

Vega Liteの値はNxは対応していないのでリストで渡す必要があります

```elixir
x = [0, 1, 2, 3]
y = [3, 7, 4, 8]

Vl.new(width: 200, height: 200)
|> Vl.data_from_values(x: x, y: y)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
```

<!-- livebook:{"output":true} -->

![fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/d0bdc4e6-b153-e338-fc3e-c586e5bb2aed.png)

## 散布図

線ではなく点で描画したい場合はmarkを':point'にしてください

```elixir
x = [0, 1, 2, 3]
y = [3, 7, 4, 8]

Vl.new(width: 200, height: 200)
|> Vl.data_from_values(x: x, y: y)
|> Vl.mark(:point)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
```

<!-- livebook:{"output":true} -->
![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/35782e0d-e918-abba-7c7a-a9a269415e08.png)

## 曲線のグラフ

階差数列(linspace)がないので値から逆算してiotaで作ります
べき乗はpowerになります

```elixir
# np.linspace(-5, 5, 300)
x = Nx.iota({1, 300}) |> Nx.multiply(10 / 300) |> Nx.subtract(5) |> IO.inspect()
y = Nx.power(x, 2) |> IO.inspect()

Vl.new(width: 200, height: 200)
|> Vl.data_from_values(x: Nx.to_flat_list(x), y: Nx.to_flat_list(y))
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
```

<!-- livebook:{"output":true} -->

```
#Nx.Tensor<
  f32[1][300]
  [
    [-5.0, -4.9666666984558105, -4.933333396911621, -4.900000095367432, -4.866666793823242, -4.833333492279053, -4.800000190734863, -4.766666412353516, -4.733333110809326, -4.699999809265137, -4.666666507720947, -4.633333206176758, -4.599999904632568, -4.566666603088379, -4.5333333015441895, -4.5, -4.4666666984558105, -4.433333396911621, -4.400000095367432, -4.366666793823242, -4.333333492279053, -4.300000190734863, -4.266666412353516, -4.233333110809326, -4.199999809265137, -4.166666507720947, -4.133333206176758, -4.099999904632568, -4.066666603088379, -4.0333333015441895, -4.0, -3.9666666984558105, -3.933333396911621, -3.9000000953674316, -3.866666555404663, -3.8333332538604736, -3.799999952316284, -3.7666666507720947, -3.733333110809326, -3.6999998092651367, -3.6666665077209473, -3.633333206176758, -3.5999999046325684, -3.566666603088379, -3.5333333015441895, -3.5, -3.4666666984558105, -3.433333396911621, -3.3999998569488525, -3.366666555404663, ...]
  ]
>
#Nx.Tensor<
  f32[1][300]
  [
    [25.0, 24.66777801513672, 24.337778091430664, 24.010000228881836, 23.684446334838867, 23.361112594604492, 23.040000915527344, 22.72110939025879, 22.404441833496094, 22.089998245239258, 21.77777671813965, 21.467777252197266, 21.15999984741211, 20.85444450378418, 20.551111221313477, 20.25, 19.95111083984375, 19.65444564819336, 19.360000610351562, 19.067779541015625, 18.77777862548828, 18.490001678466797, 18.204442977905273, 17.921110153198242, 17.639997482299805, 17.36111068725586, 17.084444046020508, 16.809999465942383, 16.537776947021484, 16.267778396606445, 16.0, 15.734444618225098, 15.471111297607422, 15.210000991821289, 14.951109886169434, 14.694443702697754, 14.4399995803833, 14.187777519226074, 13.937776565551758, 13.689998626708984, 13.444443702697754, 13.201109886169434, 12.959999084472656, 12.721110343933105, 12.484444618225098, 12.25, 12.017778396606445, 11.7877779006958, 11.559999465942383, 11.334444046020508, ...]
  ]
>
```

<!-- livebook:{"output":true} -->

![fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/eb231d51-fed2-19a1-afd7-c253d97e5fff.png)


## 複数の線を表示する

複数の線を１つのグラフに描画する場合は layersを使用してください
線の色や形状を変えたい場合も markにオプションを追加していってください

```elixir
# np.linspace(-5, 5, 300)
x = Nx.iota({1, 300}) |> Nx.multiply(10 / 300) |> Nx.subtract(5)
y1 = Nx.power(x, 2) |> Nx.to_flat_list()
y2 = Nx.subtract(x, 2) |> Nx.power(2) |> Nx.to_flat_list()

Vl.new(width: 200, height: 200)
|> Vl.data_from_values(x: Nx.to_flat_list(x), y1: y1, y2: y2)
|> Vl.layers([
  Vl.new()
  |> Vl.mark(:line, color: :red)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y1", type: :quantitative),
  Vl.new()
  |> Vl.mark(:line, color: :black, stroke_dash: [6, 4])
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y2", type: :quantitative)
])
```

<!-- livebook:{"output":true} -->

![fig4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/786237d1-181a-9361-b0a8-691357c02ef5.png)


## ヒストグラム

binsでx軸の上限を２０にして
encode_fieldのaggregateで集計しています

```elixir
# サイコロを10回振って目の合計を計算するを1000回行う
x =
  Nx.iota({1000})
  |> Nx.map(fn _i -> Nx.random_uniform({10}, 1, 6) |> Nx.sum() end)
  |> Nx.to_flat_list()

y = Nx.iota({1000}) |> Nx.to_flat_list()

Vl.new(width: 200, height: 200)
|> Vl.data_from_values(x: x, y: y)
|> Vl.mark(:bar, color: :gray)
|> Vl.encode_field(:x, "x", type: :quantitative, bin: %{maxbins: 20})
|> Vl.encode_field(:y, "y", type: :quantitative, aggregate: :count)
```

<!-- livebook:{"output":true} -->

![fig5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/c2a082c0-b68e-ba03-2958-d6f44409f4dd.png)

## 複数のグラフを並べて表示する

複数のグラフを並べて表示する場合はconcatになります
デフォルトは横方向で、:verticalを指定すると縦方向になります

```elixir
# np.linspace(-5, 5, 300)
x = Nx.iota({1, 300}) |> Nx.multiply(10 / 300) |> Nx.subtract(5)
sin_x = Nx.sin(x) |> Nx.to_flat_list()
cos_x = Nx.cos(x) |> Nx.to_flat_list()

# 縦方向
Vl.new(width: 400, height: 200)
|> Vl.data_from_values(x: Nx.to_flat_list(x), y: sin_x, y2: cos_x)
|> Vl.transform(filter: %{field: "y", range: [-1.5, 1.5]})
|> Vl.transform(filter: %{field: "y2", range: [-1.5, 1.5]})
|> Vl.concat(
  [
    Vl.new(width: 600)
    |> Vl.mark(:line, color: :red)
    |> Vl.encode_field(:x, "x", type: :quantitative)
    |> Vl.encode_field(:y, "y", type: :quantitative),
    Vl.new(width: 600)
    |> Vl.mark(:line, color: :black)
    |> Vl.encode_field(:x, "x", type: :quantitative)
    |> Vl.encode_field(:y, "y2", type: :quantitative)
  ],
  :vertical
)
```

<!-- livebook:{"output":true} -->

![fig6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/18c104be-baa6-e390-e88a-35c495145d36.png)


```elixir
# np.linspace(-5, 5, 300)
x = Nx.iota({1, 300}) |> Nx.multiply(10 / 300) |> Nx.subtract(5)
sin_x = Nx.sin(x) |> Nx.to_flat_list()
cos_x = Nx.cos(x) |> Nx.to_flat_list()

# 横方向
Vl.new(width: 400, height: 200)
|> Vl.data_from_values(x: Nx.to_flat_list(x), y: sin_x, y2: cos_x)
|> Vl.transform(filter: %{field: "y", range: [-1.5, 1.5]})
|> Vl.transform(filter: %{field: "y2", range: [-1.5, 1.5]})
|> Vl.concat([
  Vl.new(width: 600)
  |> Vl.mark(:line, color: :red)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y", type: :quantitative),
  Vl.new(width: 600)
  |> Vl.mark(:line, color: :black)
  |> Vl.encode_field(:x, "x", type: :quantitative)
  |> Vl.encode_field(:y, "y2", type: :quantitative)
])
```

![fig7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/1bd137fe-b510-e887-b0f5-767c7018a8a2.png)

# 最後に
書籍ではこのあとは実際に数値最適化や実際にモデルを実装しますが、一旦ここまでになります
ありがとうございました

# 参考サイト

https://www.sbcr.jp/product/4797393965/
https://hexdocs.pm/nx/Nx.html
https://hexdocs.pm/elixir/1.12/Kernel.html
https://hexdocs.pm/vega_lite/VegaLite.html
https://vega.github.io/vega-lite/examples/
https://vega.github.io/vega-lite/docs/

# github

https://github.com/thehaigo/livebook_samples