8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

はじめに

この記事は機械学習のエッセンスのPythonによる数値計算の章の NumPyと PyPlotのコードをNxとVega Liteで書き換えてみた内容になります。

ライブラリインストール

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

Mix.install([
  {:nx, "~> 0.2"},
  {:kino_vega_lite, "~> 0.1.1"}
])
:ok
alias VegaLite, as: Vl
VegaLite

04-02 NumPyの基本

# np.exp(2)
Nx.exp(2)
#Nx.Tensor<
  f32
  7.389056205749512
>
# np.log(np.e)
Nx.exp(1) |> Nx.log()
#Nx.Tensor<
  f32
  0.9999999403953552
>

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

# np.sin(np.pi)

:math.pi() |> Nx.sin() |> IO.inspect()
:math.pi() |> :math.sin() |> Nx.tensor()
#Nx.Tensor<
  f32
  -8.742277657347586e-8
>
#Nx.Tensor<
  f32
  1.2246468525851679e-16
>
# np.sqrt(3)
Nx.sqrt(3)
#Nx.Tensor<
  f32
  1.7320507764816284
>

NumPyの配列

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

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

#Nx.Tensor<
  s64
  2
>

Rangeでも取得できます

# a[1:3]
IO.inspect(a)
a[1..2]
#Nx.Tensor<
  s64[5]
  [2, 3, 5, 7, 8]
>

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

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

# a[2:-1]
IO.inspect(a)
a[2..-2//1]
#Nx.Tensor<
  s64[5]
  [2, 3, 5, 7, 8]
>

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

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

# b = np.arange(5)
b = Nx.iota({5})
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>

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

# np.arange(1,3,0.2)
c = Nx.iota({10}) |> Nx.multiply(0.2) |> Nx.add(1) |> IO.inspect()
#Nx.Tensor<
  f32[10]
  [1.0, 1.2000000476837158, 1.399999976158142, 1.600000023841858, 1.7999999523162842, 2.0, 2.200000047683716, 2.4000000953674316, 2.5999999046325684, 2.8000001907348633]
>

関数化するとこんな感じになります

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)

#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になります

Nx.type(a)
{:s, 64}

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

Nx.tensor([1, 2, 4], type: :s64)
#Nx.Tensor<
  s64[3]
  [1, 2, 4]
>

2次元配列

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

# a = np.array([[2, 3, 4], [5 ,6, 7]], dtype=np.float64)
a = Nx.tensor([[2, 3, 4], [5, 6, 7]])
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>

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

# a[0, 1]
IO.inspect(a)
a[0][1]
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
#Nx.Tensor<
  s64
  3
>

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

# a[:, 1]
IO.inspect(a)
a[[0..-1//1, 1]]
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
#Nx.Tensor<
  s64[2]
  [3, 6]
>

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

# a[1, :]
IO.inspect(a)
a[1]
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
#Nx.Tensor<
  s64[3]
  [5, 6, 7]
>

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

# a[0, 2:]
IO.inspect(a)
a[0][2..-1//1]
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
#Nx.Tensor<
  s64[1]
  [4]
>
# a[0, :2]
IO.inspect(a)
a[0][0..1]
#Nx.Tensor<
  s64[2][3]
  [
    [2, 3, 4],
    [5, 6, 7]
  ]
>
#Nx.Tensor<
  s64[2]
  [2, 3]
>

配列のデータ属性

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

# a = np.arange(15.).reshape(3, 5)
a = Nx.iota({15}) |> Nx.reshape({3, 5})
#Nx.Tensor<
  s64[3][5]
  [
    [0, 1, 2, 3, 4],
    [5, 6, 7, 8, 9],
    [10, 11, 12, 13, 14]
  ]
>

shapeは行列の形を返します

# a.shape
Nx.shape(a)
{3, 5}

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

# a.ndim
Nx.rank(a)
2

sizeは要素数を返します

# a.size
Nx.size(a)
15

reshapeメソッドと形状の変更

# a = np.arange(16.)
# c = a.reshape(4, -1)
a = Nx.iota({16}) |> IO.inspect()
c = Nx.reshape(a, {4, 4})
#Nx.Tensor<
  s64[16]
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
#Nx.Tensor<
  s64[4][4]
  [
    [0, 1, 2, 3],
    [4, 5, 6, 7],
    [8, 9, 10, 11],
    [12, 13, 14, 15]
  ]
>
# c.ravel()
IO.inspect(c)
Nx.flatten(c)
#Nx.Tensor<
  s64[4][4]
  [
    [0, 1, 2, 3],
    [4, 5, 6, 7],
    [8, 9, 10, 11],
    [12, 13, 14, 15]
  ]
>
#Nx.Tensor<
  s64[16]
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>

縦方向に変換

# b = np.arange(4.)
b = Nx.iota({4}) |> IO.inspect()
Nx.reshape(b, {4, 1}) |> IO.inspect()
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4][1]
  [
    [0],
    [1],
    [2],
    [3]
  ]
>

その他の配列操作

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

# a = np.zeros((3,4))
Nx.random_normal({3, 4}, 0.0, 0.0)
#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も同様

# b = np.ones((2,2))
Nx.random_normal({2, 2}, 1.0, 0.0)
#Nx.Tensor<
  f32[2][2]
  [
    [1.0, 1.0],
    [1.0, 1.0]
  ]
>

行列の連結

a = Nx.iota({2, 3}) |> IO.inspect
b = Nx.iota({2, 3})|> Nx.add(6)
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>

#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
# 縦方向に連結
# np.r_[a, b]
IO.inspect(a)
IO.inspect(b)
Nx.concatenate([a, b])
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
#Nx.Tensor<
  s64[4][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [9, 10, 11]
  ]
>
# 横方向に連結
# np._c(a,b)
IO.inspect(a)
IO.inspect(b)
Nx.concatenate([a, b], axis: 1)
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[2][3]
  [
    [6, 7, 8],
    [9, 10, 11]
  ]
>
#Nx.Tensor<
  s64[2][6]
  [
    [0, 1, 2, 6, 7, 8],
    [3, 4, 5, 9, 10, 11]
  ]
>
c = Nx.iota({3}) |> IO.inspect()
d = Nx.iota({3}) |> Nx.add(3)
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
#Nx.Tensor<
  s64[3]
  [3, 4, 5]
>
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
)
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
#Nx.Tensor<
  s64[3]
  [3, 4, 5]
>
#Nx.Tensor<
  s64[6]
  [0, 1, 2, 3, 4, 5]
>
#Nx.Tensor<
  s64[3][2]
  [
    [0, 3],
    [1, 4],
    [2, 5]
  ]
>
# 形状が違うものを連結
# numpyではエラー
IO.inspect(a)
IO.inspect(c)
Nx.concatenate([a, c])
#Nx.Tensor<
  s64[2][3]
  [
    [0, 1, 2],
    [3, 4, 5]
  ]
>
#Nx.Tensor<
  s64[3]
  [0, 1, 2]
>
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>

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

c = Nx.new_axis(c, 0) |> IO.inspect()
Nx.concatenate([a, c])
#Nx.Tensor<
  s64[1][3]
  [
    [0, 1, 2]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [0, 1, 2]
  ]
>

04-03 配列操作の基本

a = Nx.iota({5})
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>

合計

Nx.sum(a)
#Nx.Tensor<
  s64
  10
>

平均

Nx.mean(a)
#Nx.Tensor<
  f32
  2.0
>

最大値

Nx.reduce_max(a)
#Nx.Tensor<
  s64
  4
>

最小値

Nx.reduce_min(a)
#Nx.Tensor<
  s64
  0
>

2次元配列の合計

b = Nx.iota({3, 3})
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
Nx.sum(b)
#Nx.Tensor<
  s64
  36
>

縦軸で合計

IO.inspect(b)
Nx.sum(b, axes: [0])
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3]
  [9, 12, 15]
>

横軸で合計

IO.inspect(b)
Nx.sum(b, axes: [1])
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3]
  [3, 12, 21]
>

ブロードキャスト

a = Nx.iota({5}) |> Nx.add(3)
#Nx.Tensor<
  s64[5]
  [3, 4, 5, 6, 7]
>
Nx.exp(a)
#Nx.Tensor<
  f32[5]
  [20.08553695678711, 54.598148345947266, 148.4131622314453, 403.4288024902344, 1096.6331787109375]
>
Nx.log(a)
#Nx.Tensor<
  f32[5]
  [1.0986123085021973, 1.3862943649291992, 1.6094379425048828, 1.7917594909667969, 1.945910096168518]
>
Nx.sqrt(a)
#Nx.Tensor<
  f32[5]
  [1.7320507764816284, 2.0, 2.2360680103302, 2.4494898319244385, 2.6457512378692627]
>
b = Nx.iota({3, 3})
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
Nx.exp(b)
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 2.7182817459106445, 7.389056205749512],
    [20.08553695678711, 54.598148345947266, 148.4131622314453],
    [403.4288024902344, 1096.6331787109375, 2980.9580078125]
  ]
>

配列とスカラの演算

a = Nx.iota({5})
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
Nx.add(a, 3)
#Nx.Tensor<
  s64[5]
  [3, 4, 5, 6, 7]
>
Nx.multiply(a, 3)
#Nx.Tensor<
  s64[5]
  [0, 3, 6, 9, 12]
>
Nx.power(a, 2)
#Nx.Tensor<
  s64[5]
  [0, 1, 4, 9, 16]
>
# 2以上の値を1 未満を0
IO.inspect(a)
Nx.greater_equal(a, 2)
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
#Nx.Tensor<
  u8[5]
  [0, 0, 1, 1, 1]
>
# 一致するもののみ1
IO.inspect(a)
Nx.not_equal(a, 3)
#Nx.Tensor<
  s64[5]
  [0, 1, 2, 3, 4]
>
#Nx.Tensor<
  u8[5]
  [1, 1, 1, 0, 1]
>
b = Nx.iota({3, 3})
IO.inspect(b)
Nx.greater(b, 3)
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  u8[3][3]
  [
    [0, 0, 0],
    [0, 1, 1],
    [1, 1, 1]
  ]
>

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

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

# 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()
#Nx.Tensor<
  s64[2]
  [20, 30]
>

配列の演算

u = Nx.iota({4})
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
v = Nx.iota({4}) |> Nx.add(3)
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
IO.inspect(u)
IO.inspect(v)
Nx.add(u, v)
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
#Nx.Tensor<
  s64[4]
  [3, 5, 7, 9]
>
IO.inspect(u)
IO.inspect(v)

Nx.subtract(u, v)
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
#Nx.Tensor<
  s64[4]
  [-3, -3, -3, -3]
>
IO.inspect(u)
IO.inspect(v)

Nx.multiply(u, v)
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
#Nx.Tensor<
  s64[4]
  [0, 4, 10, 18]
>
# ベクトルの内積
IO.inspect(u)
IO.inspect(v)

Nx.dot(u, v)
#Nx.Tensor<
  s64[4]
  [0, 1, 2, 3]
>
#Nx.Tensor<
  s64[4]
  [3, 4, 5, 6]
>
#Nx.Tensor<
  s64
  32
>

2次元配列の演算

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

足し算

IO.inspect(a)
IO.inspect(b)
Nx.add(a, b)
#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]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [4, 6, 8],
    [10, 12, 14],
    [16, 18, 20]
  ]
>

引き算

IO.inspect(a)
IO.inspect(b)

Nx.subtract(a, b)
#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]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [-4, -4, -4],
    [-4, -4, -4],
    [-4, -4, -4]
  ]
>

掛け算

IO.inspect(a)
IO.inspect(b)

Nx.multiply(a, b)
#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]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [0, 5, 12],
    [21, 32, 45],
    [60, 77, 96]
  ]
>

割り算

IO.inspect(a)
IO.inspect(b)

Nx.divide(a, b)
#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]
  ]
>
#Nx.Tensor<
  f32[3][3]
  [
    [0.0, 0.20000000298023224, 0.3333333432674408],
    [0.4285714328289032, 0.5, 0.5555555820465088],
    [0.6000000238418579, 0.6363636255264282, 0.6666666865348816]
  ]
>

内積

IO.inspect(a)
IO.inspect(b)

Nx.dot(a, b)
#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]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [27, 30, 33],
    [90, 102, 114],
    [153, 174, 195]
  ]
>

形状の違う行列の積

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

Nx.dot(a, v)
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3]
  [1, 2, 3]
>
#Nx.Tensor<
  s64[3]
  [8, 26, 44]
>
IO.inspect(v)
IO.inspect(a)

Nx.dot(v, a)
#Nx.Tensor<
  s64[3]
  [1, 2, 3]
>

#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3]
  [24, 30, 36]
>
u = Nx.new_axis(v, 0) |> Nx.transpose()
IO.inspect(a)
IO.inspect(u)
Nx.dot(a, u)
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[3][1]
  [
    [1],
    [2],
    [3]
  ]
>
#Nx.Tensor<
  s64[3][1]
  [
    [8],
    [26],
    [44]
  ]
>
# error
Nx.dot(u, a)
w = Nx.new_axis(v, 0)
#Nx.Tensor<
  s64[1][3]
  [
    [1, 2, 3]
  ]
>
IO.inspect(w)
IO.inspect(a)

Nx.dot(w, a)
#Nx.Tensor<
  s64[1][3]
  [
    [1, 2, 3]
  ]
>
#Nx.Tensor<
  s64[3][3]
  [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8]
  ]
>
#Nx.Tensor<
  s64[1][3]
  [
    [24, 30, 36]
  ]
>

04-04疎行列

該当する関数なし

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

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

逆行列を求める

a = Nx.tensor([[3, 1, 1], [1, 2, 1], [0, -1, 1]])
Nx.LinAlg.invert(a)
#Nx.Tensor<
  f32[3][3]
  [
    [0.4285714328289032, -0.2857142686843872, -0.1428571343421936],
    [-0.1428571343421936, 0.4285714328289032, -0.2857142686843872],
    [-0.1428571343421936, 0.4285714030265808, 0.714285671710968]
  ]
>

slove関数を使う

a = Nx.tensor([[3, 1, 1], [1, 2, 1], [0, -1, 1]])
b = Nx.tensor([1, 2, 3])
Nx.LinAlg.solve(a, b)
#Nx.Tensor<
  f32[3]
  [-0.5714285373687744, -0.14285710453987122, 2.857142686843872]
>

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

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)
#Nx.Tensor<
  f32[3]
  [-0.5714285969734192, -0.1428571492433548, 2.857142925262451]
>

04-06 乱数

numpy.randomモジュールを使う

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

Nx.random_uniform({1}, 0.0, 1.0)
#Nx.Tensor<
  f32[1]
  [0.20401528477668762]
>
Nx.random_uniform({3, 2}, 0.0, 1.0)
#Nx.Tensor<
  f32[3][2]
  [
    [0.20480242371559143, 0.5718821287155151],
    [0.7167996168136597, 0.3234789967536926],
    [0.8850403428077698, 0.806730329990387]
  ]
>

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

Nx.random_uniform({1}, 0, 4)
#Nx.Tensor<
  s64[1]
  [3]
>
Nx.random_uniform({3, 3}, 0, 5)
#Nx.Tensor<
  s64[3][3]
  [
    [0, 2, 1],
    [3, 0, 2],
    [2, 4, 3]
  ]
>

乱数の種を指定する

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

04-07 データの可視化

折れ線グラフ

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

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)

fig1.png

散布図

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

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)

fig2.png

曲線のグラフ

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

# 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)
#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, ...]
  ]
>

fig3.png

複数の線を表示する

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

# 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)
])

fig4.png

ヒストグラム

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

# サイコロを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)

fig5.png

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

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

# 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
)

fig6.png

# 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://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

8
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?