裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

Julia の小ネタ--026 R の rle(), Run Length Encoding

2021年06月03日 | ブログラミング

同じ文字列が何個続くか勘定する。

aabbbcccc なら,a が 2個,b が 3個,c が 4個なので,([2, 3, 4], ["a", "b", "c"]) のようなタプルを返す。

function rle(x)
    """ Run Length Encoding
    Compute the lengths and values of runs of equal values in a vector – or the reverse operation.
    
    rle("001111000000011")
    # ([2, 4, 7, 2], ["0", "1", "0", "1"])

    rle("aabcccbbbaab")
    # ([2, 1, 3, 3, 2, 1], ["a", "b", "c", "b", "a", "b"])
    """
    s = split(x, "")
    n = length(x)
    n == 0 && return (Int[], "")
    y = s[2:end] .!= s[1:end-1]
    i = vcat((1:length(y))[y], n)
    diff(vcat(0, i)), string.(s[i])rle(
end

rle("001111000000011") # ([2, 4, 7, 2], ["0", "1", "0", "1"])
rle("aabcccbbbaab")    # ([2, 1, 3, 3, 2, 1], ["a", "b", "c", "b", "a", "b"])

StatsBase にも rle() という関数がある。全く同じことをやるのだが,引数はベクトルである。返すタプルも,(種類, 個数) の順。

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Julia で多項式,求解,多項式近似

2021年06月03日 | ブログラミング

using Polynomials
using Plots
gr(xlabel="\$x\$", ylabel="\$y\$", size=(400, 300))

多項式の定義,多項式の値,多項式の解

Polynomial() の目的は,1つには与えた係数ベクトルから「多項式 Polynomial{Float64, :x}」を作ることである。
係数ベクトルは定数項(0 次)から,1,2,3 次... の順に指定する。該当次数がないときは 0 を指定する。

p = Polynomial([1, 0, 3, 4]) # 1 + 3∙x^2 + 4∙x^3
typeof(p)                     # Polynomial{Float64, :x}

戻り値 p は関数でもあるので,plot() の第1引数(関数)としても使える。

plot(p, tick_direction=:out, title="\$1 + 3x^2 + 4x^3\$", label="")
hline!([0])
savefig("fig1.png")

また,関数なので当然ながら,指定した値に対応する多項式の値を求めることもできる。

p(-1)    # 0
p(-0.75) # 1.0
p(0)     # 1

特に,多項式=0 の解を求めるには,roots() を使う。複素解も求まる。

roots(p)
#=
3-element Vector{ComplexF64}:
                -1.0 + 0.0im
 0.12500000000000006 - 0.4841229182759272im
 0.12500000000000006 + 0.4841229182759272im
=#

多項式は,多項式 = 0 の解を a1, a2, ...としたとき,解のベクトル [a1, a2, ...] を指定して fromroots() で作成することもできる。

(x + 1.5) * (x - 2.2) * (x - 3.7) = 0 のとき,以下のように与える。

p =  fromroots([-1.5, 2.2, 3.7]) # 12.21 - 0.7099999999999995∙x - 4.4∙x^2 + 1.0∙x^3
plot(p, tick_direction=:out, title="\$12.21 - 0.71^x - 4.4x^2 + 1.0 x^3\$", label="")
hline!([0])
savefig("fig2.png")

p(-1.5) # -3.552713678800501e-15
p(2.2)  # -1.7763568394002505e-15
p(3.7)  # 0.0

Machine.double.epsilon は eps() = 2.220446049250313e-16 なので,8eps()~16eps() 程度の誤差である。

eps()   # 2.220446049250313e-16
8eps()  # 1.7763568394002505e-15
16eps() # 3.552713678800501e-15

roots(p)
#=
3-element Vector{Float64}:
 -1.4999999999999998
  2.200000000000002
  3.7
=#
p(-1.4999999999999998) # 1.7763568394002505e-15
p(2.200000000000002)   # -1.2434497875801753e-14
p(3.7)                 # 0.0

多項式に用いる変数の指定

第二引数に「シンボル」で与えることができる(デフォルトは,前述の通り :x とするのと同じ)。

r = Polynomial([1,2,3], :a) # 1 + 2∙a + 3∙a^2

多項式の四則演算

四則演算の一部が使える。ただし,多項式に使われる変数が同じでなければならない。

p = Polynomial([1, 2])     # 1 + 2∙x
q = Polynomial([1, 0, -1]) # 1 - x^2

定数倍

2p  # 2 + 4∙x
q/2 # 0.5 - 0.5∙x^2

定数和

3 + p # 4 + 2∙x

p +4q # 5 + 2∙x - 4∙x^2

p - q # 2∙x + x^2

p * q # 1 + 2∙x - x^2 - 2∙x^3

割り算

q ÷ p        # 0.25 - 0.5∙x
div(q, p)    # 0.25 - 0.5∙x
rem(q, p)    # 0.75
divrem(q, p) # (Polynomial(0.25 - 0.5*x), Polynomial(0.75))

不定積分

y = Polynomial([1, 0, -1]) # 1 - x^2
z = integrate(y)           # 1.0∙x - 0.3333333333333333∙x^3

微分

derivative(z) # 1.0 - 1.0∙x2 --> y に戻る

y = (x + 1) * (x - 2) * (x -4) の極値を求める。

多項式の定義

y = fromroots([-1, 2, 4])
plot(y, tick_direction=:out, title="\$8 + 2x - 5x^2 + x^3\$", label="")
savefig("fig3.png")

y の導関数を求めて(derivative),0 になるときの値 x を求める(roots)。

x = roots(derivative(y))
#=
2-element Vector{Float64}:
 0.2137003521531088
 3.1196329811802244
=#

vline!([x])
savefig("fig4.png")

多項式近似(多項式補間)

前報で述べたように,滑らかな単調増加・単調減少関数は多項式によって有効に近似できる。

正規分布の上側確率を求める近似式を作ってみる。

using Rmath

z値 に対する p 値の 9 対のデータに基づく。

z = 1:0.5:5          # z 値, 1.0:0.5:5.0
p = pnorm.(z, false) # p 値
#=
9-element Vector{Float64}:
 0.15865525393145705
 0.06680720126885807
 0.022750131948179212
 0.006209665325776135
 0.0013498980316300946
 0.00023262907903552494
 3.1671241833119924e-5
 3.39767312473006e-6
 2.866515718791939e-7
=#

理論的に 8 次の近似式まで可能。

scatter(z, p, tick_direction=:out, title="pnorm(x)", label="sample point")
approx = fit(z, p); # length(z) - 1 次式(8 次式)で近似

見た目では差がわからないほどよく近似されているように見えるが。

plot!(approx, [1,5]..., label="approximation")
savefig("fig5.png")

近似式を求めたのと同じ範囲の z をさらに細かく(0.01刻み)にして,真値と近似値の差をプロットしてみる。

z2 = collect(1:0.01:5);
truevalue = similar(z2);
approximatevalue = similar(z2);
for (i, z3) in enumerate(z2)
    truevalue[i] = pnorm(z3, false)
    approximatevalue[i] = approx(z3)
end

少なくとも小数点以下 4 桁程度まで正確であることがわかる。

plot(z2, (truevalue .- approximatevalue),
    xlabel="\$z\$", ylabel="truevalue - approximatevalue", label="")
savefig("fig6.png")

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Julia で多項式回帰,Polynomials

2021年06月03日 | ブログラミング

x = range(0, 5, length=6)
y = @. exp(-x)
#=
6-element Vector{Float64}:
 1.0
 0.36787944117144233
 0.1353352832366127
 0.049787068367863944
 0.01831563888873418
 0.006737946999085467
 =#

というデータがある。y を x の多項式 a0 + a1*x + a2*x^2 + ... で予測すること(多項式回帰)を考える。

多項式回帰は x^2, x^3, ... を前もって用意しておけば重回帰プログラムを使えばできる。
理論上は x の次数は length(y) - 1 までなので,例の場合は 5 次の項まで使える。

using DataFrames
df = DataFrame(:x => x, :x2 => x.^2, :x3 => x.^3, :x4 => x.^4, :x5 => x.^5, :y => y)
#=
6 rows × 6 columns

 x x2 x3 x4 x5 y
 Float64 Float64 Float64 Float64 Float64 Float64
1 0.0 0.0 0.0 0.0 0.0 1.0
2 1.0 1.0 1.0 1.0 1.0 0.367879
3 2.0 4.0 8.0 16.0 32.0 0.135335
4 3.0 9.0 27.0 81.0 243.0 0.0497871
5 4.0 16.0 64.0 256.0 1024.0 0.0183156
6 5.0 25.0 125.0 625.0 3125.0 0.00673795
=#

重回帰プログラムは GLM にある lm() を使う。

using GLM

lmans2 = lm(@formula(y ~ x + x2), df); # 2 次式
coef(lmans2)
#= 0, 1, 2 次の係数(以下同様)
3-element Vector{Float64}:
  0.9313226293358078
 -0.5231411796668056
  0.0697679508663814
=#

lmans3 = lm(@formula(y ~ x + x2 + x3), df); # 3 次式
coef(lmans3)
#=
4-element Vector{Float64}:
  0.9917995957122201
 -0.7993193261190775
  0.22096036680740455
 -0.0201589887921363
=#

lmans4 = lm(@formula(y ~ x + x2 + x3 + x4), df); # 4 次式
coef(lmans4)
#=
5-element Vector{Float64}:
  0.9995995032132226
 -0.9293177844691326
  0.36070870953370876
 -0.0656584492146525
  0.0045499460422515035
=#

lmans5 = lm(@formula(y ~ x + x2 + x3 + x4 + x5), df); # 5 次式
coef(lmans5)
#=
6-element Vector{Float64}:
  0.9999999999998723
 -0.9762026083014735
  0.4413086878624456
 -0.11144858183069759
  0.015062986693944548
 -0.0008410432521384522
=#

5次多項式による予測値

predict(lmans5)
#=
6-element Vector{Float64}:
 0.9999999999998723
 0.36787944117195287
 0.13533528323580923
 0.04978706836849145
 0.018315638888491637
 0.006737946999125999
=#

多項式回帰を行う fit() が Polynomials にある。
次数が高い項を使うと,正規方程式は不安定になるが,Polynomials.fit() だと,不安定性を軽減する計算アルゴリズムが採用される。

using Plots, Polynomials

注:以下では Polynomials.fit() として使っているが,これは先に使った GLM の fit() と識別するためなので,通常は fit() だけでよい。

f2 = Polynomials.fit(x, y, 2); # 2次式で予測
# 0.9313226293358072 - 0.5231411796668045∙x + 0.06976795086638117∙x2

f3 = Polynomials.fit(x, y, 3); # 3次式で予測
# 0.9917995957122127 - 0.7993193261190567∙x + 0.22096036680739517∙x2 - 0.02015898879213521∙x3

f4 = Polynomials.fit(x, y, 4); # 4次式で予測
# 0.9995995032131947 - 0.9293177844687606∙x + 0.360708709533328∙x2 - 0.06565844921453219∙x3 + 0.004549946042239714∙x4

f5 = Polynomials.fit(x, y); # 最高次数 = length(x) - 1 = 5次式で予測
# 1.0 - 0.9762026083107394∙x + 0.4413086878778398∙x2 - 0.11144858183923873∙x3 + 0.015062986695871163∙x4 - 0.0008410432522905108∙x5

データにある x の範囲内だと次数が高いほど予測が上手くできるように見えるが,


scatter(x, y, grid=false, tick_direction=:out,

        markerstrokewidth=0, size=(400, 300),
        xlabel="\$x\$", ylabel="\$y\$", label="Data")
plot!(f2, extrema(x)..., label="Order2")
plot!(f3, extrema(x)..., label="Order3")
plot!(f4, extrema(x)..., label="Order4")
plot!(f5, extrema(x)..., label="Order5")
savefig("fig1.png")

範囲外では全くダメなのがよく分かる。


scatter(x, y, grid=false, tick_direction=:out,
        markerstrokewidth=0, size=(400, 300),
        xlims=(-3,8),
        xlabel="\$x\$", ylabel="\$y\$", label="Data")
plot!(f2, [-3,8]..., label="Order2")
plot!(f3, [-3,8]..., label="Order3")
plot!(f4, [-3,8]..., label="Order4")
plot!(f5, [-3,8]..., label="Order5")
savefig("fig2.png")

また,単調減少や単調増加の場合は目立たないが,そうでない場合にはデータ範囲内でもデータ点は通っても,それ以外の所は凸凹で,予測とはとてもいえないことが明らかである。

x2 = 0:5
y2 = [2,6,9,4,11,7]
f52 = Polynomials.fit(x2, y2); # degree = length(x) - 1 = 5
scatter(x2, y2, grid=false, tick_direction=:out,
        markerstrokewidth=0, size=(400, 300),
        xlabel="\$x\$", ylabel="\$y\$", label="")
plot!(f52, [-0.3,5.3]..., label="")
savefig("fig3.png")

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村