Bogumił Kamiński, Przemysław Szufel:Julia プログラミングクックブック

作成日 : 2021-03-20
最終更新日:

概要

原題は「Julia 1.0 Programming Cookbook」。 サポートページには、本書のコードが置かれている。
https://github.com/oreilly-japan/julia-programming-cookbook

感想

1章 Julia のインストールと設定

レシピ 1.1 では Julia をバイナリパッケージでインストールする方法について書かれている。 わたしはひねくれものだから、本書に書いてある方法ではなく、Ubuntu 20.04 (WSL2) で、
$ sudo apt install julia
でインストールした。しかし、このバージョンは 1.4.1 だったので、新しいのがいいだろうということで、 本書の方法にしたがってバイナリをインストールすることにした。 ただし、本に書かれている通りではない。まず Julia のダウンロードページ
https://julialang.org/downloads/
に行って、Generic Linux on x86 [help] の行にある 64bit の (glibc) を選んでダウンロードした。 以下は本書と同様である。このバージョンは 1.6.0-rc3 (2021-03-16) である。 rc とは Release Candidate 、すなわちリリース候補のことだ。正式な 1.6.0 というには足りないものらしい。

レシピ 1.6 では Julia 起動時の動作を変更する方法について書かれている。 必ず実行されるスタートアップスクリプトは、
~/.julia/config/startup.jl
に書けばいいようだ。本書ではスタートアップスクリプトに using Random という、Random モジュールを使うための指示をした例が出ている。さて、このスタートアップをすると、 本書によれば次のようになるという。

$ julia --banner=no
Setup successful
julia> RandomDevice()
RandomDevice(UInt128[(省略)])

julia>

ここで(省略)としてあるのは、128 ビットの符号なし整数で、最初が 0x で始まっているから16進数を表している。 しかし私の環境ではこうなった。

$ julia --banner=no
Setup successful
julia> RandomDevice()
RandomDevice(true)

julia>

レシピ 1.10 では、パッケージの管理が述べられている。p.52 で、パッケージの管理をやってみた。

(@v1.6) pkg>
(@v1.6) pkg> status
    Status `~/.julia/environments/v1.6/Project.toml` (empty project)
(@v1.6) pkg> add BenchmarkTools.jl
Installing known registries into `~/.julia`
     Added registry `General` to `~/.julia/registries/General`
 Resolving package versions...
 Installed Parsers ──────── v1.1.0
 Installed JSON ─────────── v0.21.1
 Installed BenchmarkTools ─ v0.6.0
  Updating `~/.julia/environments/v1.6/Project.toml`
[6e4b80f9] + BenchmarkTools v0.6.0
  Updating `~/.julia/environments/v1.6/Manifest.toml`
[6e4b80f9] + BenchmarkTools v0.6.0
[682c06a0] + JSON v0.21.1
[69de0a69] + Parsers v1.1.0
[ade2ca70] + Dates
[8f399da3] + Libdl
[37e2e46d] + LinearAlgebra
[56ddb016] + Logging
[a63ad114] + Mmap
[de0858da] + Printf
[9a3f8284] + Random
[ea8e919c] + SHA
[9e88b42a] + Serialization
[2f01184e] + SparseArrays
[10745b16] + Statistics
[cf7118a7] + UUIDs
[4ec0a83e] + Unicode
Progress [========================================>]  3/3
3 dependencies successfully precompiled in 14 seconds

(@v1.6) pkg> status
      Status `~/.julia/environments/v1.6/Project.toml`
  [6e4b80f9] BenchmarkTools v0.6.0
 
(@v1.6) pkg> precompile

(@v1.6) pkg> (BackSpace キーを入力する)

julia>

julia> using BenchmarkTools

julia> @btime rand()
  13.413 ns (0 allocations: 0 bytes)
0.1341729413635535

julia>

ここまでは大丈夫だ。パッケージマネージャーモードに移行するときには]キーを、 パッケージマネージャーモードを抜けるときにはBackSpaceキーを使う。

以下の記述は特記なき限り WSL の Julia 1.6.0-rc3 での結果である。

2章 データ構造とアルゴリズム

p.68 で、2つのargmin 関数の実行速度の比較がある。私も試した。

julia> x = rand(1:10, 1000);

julia> using BenchmarkTools

julia> @btime randargmin1($x);
  3.825 μs (2 allocations: 8.92 KiB)

julia> @btime randargmin2($x);
  4.943 μs (0 allocations: 0 bytes)

本書では、randargmin1 の結果が、34.056 μs (602 allocations: 18.05 KiB)、 randargmin2 の結果が、2.022 μs (0 allocations: 0 bytes) となっている。 私の環境では、randargmin1 の方が速い。おそらく、Julia のバージョンがアップしたときに、 アルゴリズムが高速になるよう見直されたのだろう。何事も試してみなければわからないものだ。

p.74 のコードを見たとき、妙な記号に驚いた。
state ⊻= state << 13
= の左側にある記号 ⊻ は、どう読むのだろう。そして、どうタイプするのだろう。 そもそも、どんな意味だろう。Julia はこんな記号を受け付けるのだろうか。 なお、私がこのように表示できているのは、タイプしたからではない。 上記サポートページのコードからコピーしてペーストしただけだ。

まあ、いい。本書でどこか、記載があるだろうか。まず 0 章 Julia言語入門(日本語版補遺)を見た。 ⊻ は演算子だろうから演算子の説明があればよかったのだが、ない。索引を探してもない。 調べると、⊻ は排他的論理和だった。
https://docs.julialang.org/en/v1/base/math/
Julia の REPL で、 \xor とタイプしてタブキーを打てば入力できるようだ。

3章 Julia によるデータエンジニアリング

「レシピ 3.9 Microsoft Excel ファイルを読み書きする」の項では、 p.129 の 9 項で 「Excel ファイルの行や列に関して繰り返し実行することもできる」とある。 私の環境では、実行した結果に多くの nothing が出てしまった。なぜだろう。

4章 Julia による数値演算

Julia 1.6.0-rc3 で「レシピ 4.2 条件文のあるループの効率的な実行」で、pp.144-145 にある function possum2a(x) を実行すると次のようなエラーが生じる:

julia> @btime possum2a(x)
ERROR: MethodError: no method matching +(::Vector{Float64}, ::Float64)
For element-wise addition, use broadcasting with dot syntax: array .+ scalar
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
  +(::Array, ::Array...) at arraymath.jl:43
  +(::Array, ::SparseArrays.AbstractSparseMatrixCSC) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/SparseArrays/src/sparsematrix.jl:1745
  ...

このエラーは次の行
x += ifelse(v > 0, v, zero(s))
で起きている。メッセージを見ると、.+ を使えばよさそうだ。早速次のように
x .+= ifelse(v > 0, v, zero(s))
に変えてみた。エラーは出なくなったが、今度は julia> プロンプトが返ってこなくなった。 何かがいけないことはわかるが、何がいけないかがわからない。

その後 Windows 11 の Julia 1.8.2 で行なったところ、本書記載の通りの結果が得られた(2023-04-30)。そのときのデータである。

julia> @btime sum(v for v in x if v > 0)
  3.757 ms (3 allocations: 48 bytes)
398954.3436691946
julia> function possum1(x)
      s = zero(eltype(x))
      for v in x
        if v > 0
          s +=v
        end
      end
      s
    end
      
possum1 (generic function with 1 method)

julia> @btime possum1(x)
  1.372 ms (1 allocation: 16 bytes)
398954.3436691946

julia> function possum2a(x)
        s = zero(eltype(x))
        for v in x
          s += ifelse(v>0,v,zero(s))
        end
        s
      end
possum2a (generic function with 1 method)

julia> @btime possum2a(x)
  911.400 μs (1 allocation: 16 bytes)
398954.3436691946

julia> function possum2b(x)
        s = zero(eltype(x))
        @simd for v in x
          s += ifelse(v>0,v,zero(s))
        end
        s
      end
possum2b (generic function with 1 method)

julia> @btime possum2b(x)
  110.100 μs (1 allocation: 16 bytes)
398954.3436692079

5章 変数、型、関数

「レシピ5.2 多重ディスパッチで動作を切り替える」を確かめている。p.194 の例でエラーが発生した。

julia> simpledescribe(v::CategoricalArray) = "categorical"
ERROR: UndefVarError: CategoricalArray not defined
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

検索エンジンの DuckDuckGo (duckduckgo.com) で調べたが、 理由がわからない。ひょっとして CategoricalArrays.jl というパッケージをインストールする必要があるのかもしれない。やっておく。

(@v1.6) pkg> add CategoricalArrays
   Resolving package versions...
    Updating `~/.julia/environments/v1.6/Project.toml`
  [324d7699] + CategoricalArrays v0.9.3
  No Changes to `~/.julia/environments/v1.6/Manifest.toml`

julia> simpledescribe(v::CategoricalArray) = "categorical"
ERROR: UndefVarError: CategoricalArray not defined
Stacktrace:
 [1] top-level scope
   @ REPL[11]:1

julia> using CategoricalArrays

julia> simpledescribe(v::CategoricalArray) = "categorical"
simpledescribe (generic function with 3 methods)

期待している結果が得られた。もう一度復習しよう。p.193 の次の行

julia> using DataFrames

は次のように変更する必要がある。

julia> using DataFrames, CategoricalArrays

なお、add CategoricalArrays を実施する必要があったのかどうかはわからない。

6章 メタプログラミングと高度な型

「レシピ6.3 ユーザ定義型を作ってみる―連結リスト」を確かめている。p.231 の出力が多少異なっている。

julia> collect(charlist)
5-element Vector{Char}:
 '1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
 '2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
 '3': ASCII/Unicode U+0033 (category Nd: Number, decimal digit)
 '4': ASCII/Unicode U+0034 (category Nd: Number, decimal digit)
 '5': ASCII/Unicode U+0035 (category Nd: Number, decimal digit)

本書では次の通りだった:

julia> collect(charlist)
q5-element Array{Char,1}:
 '1'
 '2'
 '3'
 '4'
 '5'

Vector{Char} と Array{Char,1} の違いは何か。調べていない。


7章 Julia によるデータ分析

「レシピ 7.1 データフレームと行列を変換する」で早速失敗した。

julia> df = DataFrame(mat)
ERROR: UndefVarError: DataFrame not defined

最初に REPL で using DataFrames を宣言する必要があった。本書にはないが、 サポートページの ipynb にはある。

データフレームから行列に戻したときのデータ型が異なる。私の環境ではこうなった:

julia> Matrix(df)
3×4 Matrix{Int64}:
 1  2  3   4
 2  4  6   8
 3  6  9  12

本書では次のように記載されている:

julia> Matrix(df)
3×4 Array{Int64,2}:
 1  2  3   4
 2  4  6   8
 3  6  9  12


8章 Julia ワークフロー

「レシピ8.1 Revise.jl を用いてモジュールを開発する」の項を Julia 1.6.0-rc3 で試してみたが、 期待した結果は得られなかった。本書の通り行えば、問題のあるモジュールを問題を解消するように更新すれば、 Julia を再起動する必要もなく、更新したモジュールをリロードする必要もなく正しい結果が得られるはずである。 しかし、私の環境では本書の通りモジュールを更新しても、更新した結果は反映されなかった。

1.8.2 で再度試してみた。なお、本書掲載のプログラムサンプルによれば JSON.jl がインストールされていることが前提となっているが、 本文には明示的に書かれていないので下記の手順ではそれを追記している。

(@v1) pkg> add Revise
      (中略)
(@v1.8) pkg> add HTTP
      (中略)
(@v1.8) pkg> add JSON
      (中略)
julia> push!(LOAD_PATH, ".")
4-element Vector{String}:
 "@"
 "@v#.#"
 "@stdlib"
 "."    

julia> using Revise
julia> Module1
julia> getcoinprices("2018-06-20", "2018-06-22")
Dict{String, Any} with 3 entries:
  "2018-06-22" => 6053.9
  "2018-06-20" => 6758.38
  "2018-06-21" => 6717.2

julia> getcoinprices("2018-06-23", "2018-06-22")
  ERROR: HTTP.Exceptions.StatusError(404, "GET", "/v1/bpi/historical/close.json?currency=USD&start=2018-06-23&end=2018-06-22", HTTP.Messages.Response:
"""
HTTP/1.1 404 Not Found
(以下省略)
(ここで Module1.jl ファイルを変更し、不当な日付の範囲が指定されていた場合にエラーを返すのではなく空の Dict を返すようにする)
julia> getcoinprices("2018-06-23", "2018-06-22")
Dict{Any, Any}()

無事本書の通り動作していることがわかった。


9章 データサイエンス

「レシピ9.4 Plots.jl を用いて複雑なプロットを描く」の項を試してみた。 サンプルコードに ρ とか √ とか書いてあるので戸惑った。 Julia の REPL であれば、ρ は \rho とタイプしてタブキーを打つと変換できる。 また、√ は \sqrt とタイプしてタブキーを打つと変換できる。


10章 分散処理

「レシピ10.2 リモートの Julia プロセスと通信する」の項を試してみた。 本書での出力はきれいなオートマトン図形だが、私の環境では、正しい出力行の間に次の警告が4行出てしまい、 まったくもってみっともない表示になってしまった。理由は不明である。

┌ Warning: Cannot transfer global variable caa; it already has a value.
└ @ Distributed /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Distributed/src/clusterserialize.jl:179

これは v 1.8.2でもほぼ同じ結果となる。

誤植

p.6 の下から8行目「確実に開放させる」は正しくは、「確実に解放させる」だろう。
p.12 中ほど、「@を冒頭に付けて呼び出。」とあるが、正しくは、「@を冒頭に付けて呼び出す。」だろう。
p.31 6. の項目の「MKL に関するしては、」は、正しくは 「MKL に関しては、」だろう。
p.75 下から3行目「設計されいている。」は、正しくは「設計されている。」だろう。
p.107 「Web サービスを一から構築する」の項 2. で「8000 番ポート」とあるが、後のコードを見る限り、 正しくは「8080 番ポート」である。
p.120 のレシピ3.7 「オブジェクトをシリアライズしよう」の節の中ほどで、 JDL2.jl とあるが、JLD2.jl が正しい。同じページの 「準備しよう」の節で、「パッケージ JL.Djl」とあるが、正しくは 「パッケージ JLD2.jl」である。

書誌情報

書名 Julia プログラミングクックブック
著者 Bogumił Kamiński, Przemysław Szufel
訳者 中田 秀基
発行日 2019 年 10 月 18 日(初版第1刷)
発行所 オライリー・ジャパン
発売元 オーム社
定 価 3800 円(本体)
サイズ A5 変形判
ISBN 978-4-87311-889-5
その他 越谷市立図書館で借りて読む

まりんきょ学問所コンピュータの部屋コンピュータの本Julia > Bogumił Kamiński, Przemysław Szufel:Julia プログラミングクックブック


MARUYAMA Satosi