[Python][Matplotlib] sin(x)のマクローリン展開のGIFアニメーションを作る Making GIF animation of Maclaurin series of sin(x)

sin(x)のマクローリン展開 Maclaurin series of sin(x)

 \displaystyle
\sin (x) = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots

GIFアニメーション GIF animation

f:id:cyanatlas:20191211005345g:plain

\sin(x)マクローリン展開には奇数番目の項しかないため、nが2増えるごとに近似がよくなっていることが、アニメーションからも見て取れる。

Since Maclaurin series of \sin(x) contains only even terms, GIf animation shows that the approximate accuracy improves when n increases 2.

ソースコード Source code

実行にはImageMagickのインストールが必要。

Execution of the following code is required to install ImageMagick.

import math

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from tqdm import tqdm


def calc_maclaurin_series_of_expx(x, n):
    result = 0
    for k in range(n + 1):
        if k % 2 == 0:
            continue
        elif k % 4 == 1:
            result = result + x**k / math.factorial(k)
        else:
            result = result - x**k / math.factorial(k)
    return result


def main():
    n_max = 40
    fig = plt.figure()
    x = np.linspace(-4 * math.pi, 4 * math.pi, 100)

    def plot(n):
        plt.cla()
        y = np.ones_like(x) * np.nan
        for i in range(len(x)):
            y[i] = calc_maclaurin_series_of_expx(x=x[i], n=n)
        plt.plot(x, y, color='C0', zorder=5)
        plt.plot(x, np.sin(x), color='darkgray', linestyle='dashed', zorder=4)
        plt.ylim(-1.5, 1.5)
        plt.text(x=0.0, y=1.2, s='n={:>2d}'.format(n), fontsize=16)

    anim = animation.FuncAnimation(
        fig, plot, interval=300, repeat_delay=2000, frames=n_max
    )
    anim.save('anim2.gif', writer="imagemagick")
    return


if __name__ == '__main__':
    main()

[Python][Matplotlib] e^xのマクローリン展開のGIFアニメーションを作る

ソースコード

実行にはImageMagickのインストールが必要。

import math

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from tqdm import tqdm


def calc_maclaurin_series_of_expx(x, n):
    result = 0
    for k in range(n + 1):
        result = result + x**k / math.factorial(k)
    return result


def main():
    n_max = 20
    fig = plt.figure()
    x = np.linspace(-10, 10, 100)

    def plot(n):
        plt.cla()
        y = np.ones_like(x) * np.nan
        for i in range(len(x)):
            y[i] = calc_maclaurin_series_of_expx(x=x[i], n=n)
        plt.plot(x, y, color='C0', zorder=5)
        plt.plot(x, np.exp(x), color='darkgray', linestyle='dashed', zorder=4)
        plt.ylim(-5000, 23000)
        plt.text(x=-5.0, y=10000, s='n={:>2d}'.format(n), fontsize=16)

    anim = animation.FuncAnimation(
        fig, plot, interval=300, repeat_delay=2000, frames=n_max
    )
    anim.save('anim1.gif', writer="imagemagick")
    return


if __name__ == '__main__':
    main()

[Python] product関数を使って多重ループを1つのループにする

多重ループを書くとネストが深くなり、読みにくさが倍増する。

関数を使うなど、ループを浅くする方法はいくつかあるが、ここではPython標準ライブラリのitertoolsライブラリを使う方法を紹介する。

例えば次のようなコードがあったとしよう。

コード 1:

for i in range(3):
    for j in range(2):
        print([i, j])

出力 1:

[0, 0]
[0, 1]
[1, 0]
[1, 1]
[2, 0]
[2, 1]

このコードには二重ループが含まれている。二重ループであればそれほど読みにくさは感じないが、他の処理と組み合わさって三重以上のループになってしまった場合、読みにくさを感じる人も多いのではないか。

そこで、Python標準ライブラリであるitertoolsライブラリに用意されている、product関数を使って、この二重ループを一重ループにする。
docs.python.org

コード 2:

from itertools import product

for i, j in product(range(3), range(2)):
    print([i, j])

出力 2 (出力1と同じ):

[0, 0]
[0, 1]
[1, 0]
[1, 1]
[2, 0]
[2, 1]

product関数は、複数のリストまたはジェネレータを受け取り、それらのすべての組み合わせを生成するジェネレータを返す。これにより、シンプルな二重ループを1つのループに落とすことができる。

同様にして、三重ループも1つのループに落とすことができる。

コード 3(出力結果は省略):

from itertools import product

for i, j in product(range(3), range(2), range(4)):
    print([i, j, k])

product関数のrepeat引数に整数を指定すれば、与えたリストまたジェネレータを何個分組み合わせるかを指定できる。下の例では、0から2までを生成するジェネレータを二つ組み合わせて、合計9通りの組み合わせを生成するジェネレータを返している。

コード 4:

from itertools import product

for i, j in product(range(3), repeat=2):
    print([i, j])

出力 4:

[0, 0]
[0, 1]
[0, 2]
[1, 0]
[1, 1]
[1, 2]
[2, 0]
[2, 1]
[2, 2]


itertoolsライブラリにはproduct関数の他にも有用な関数が用意されているので、興味を持った方は一度ドキュメントを覗いてみるのがよいと思う。
docs.python.org

[Python][Matplotlib] Matplotlibのデフォルトカラーセット

f:id:cyanatlas:20191207000644j:plain

引数 16進数コード RGBコード(0~255)
'C0' blue #1f77b4 (31, 119, 180)
'C1' orange #ff7f0e (255, 127, 14)
'C2' green #2ca02c (44, 160, 44)
'C3' red #d62728 (214, 39, 40)
'C4' purple #9467bd (148, 103, 189)
'C5' brown #8c564b (140, 86, 75)
'C6' pink #e377c2 (227, 119, 194)
'C7' gray #7f7f7f (127, 127, 127)
'C8' olive #bcbd22 (188, 189, 34)
'C9' light blue #17becf (23, 190, 207)

以下は上の図を作成するソースコード

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(10):
    ax.plot([0, 1], [-i, -i], color='C{}'.format(i), linewidth=5)
    ax.text(
        x=1.05, y=-i, s='C{}'.format(i), color='C{}'.format(i),
        fontsize=14, verticalalignment='center'
    )
ax.set_xlim(0, 1.2)
ax.set_axis_off()
fig.savefig('default_colors.jpg', dpi=600)

[Python][Matplotlib] 枠・軸を削除する・目盛りのトゲを削除する(Removing frames, axes and ticks)

目次 Contents

ふつうに図を描く Plot with no options

Matplotlibを使ってふつうに図を描くと

  1. 枠・軸が四方に表示される
  2. 目盛りにはその位置を示すためのトゲ(tick)が表示される

この二つを消す方法を下で説明する.

When we plot a figure with no options using Matplotlib package,

  1. the frame and axes are shown, and
  2. the ticks are shown.

Here, I'll explain how to remove the above two parts.

import os.path

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.linspace(-3, 3, 100)
    fig, ax = plt.subplots()
    y = (x - 2) * (x + 2) * x
    ax.plot(x, y)

    fig.savefig(os.path.splitext(os.path.basename(__file__))[0] + '.jpg')
    return


if __name__ == '__main__':
    main()

f:id:cyanatlas:20200411195335j:plain

枠・軸を消す Remove the frame and axes

部分的に消す Partially remove the frame and axes

import os.path

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.linspace(-3, 3, 100)
    fig, ax = plt.subplots()
    y = (x - 2) * (x + 2) * x
    ax.plot(x, y)

    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['left'].set_visible(False)

    fig.savefig(os.path.splitext(os.path.basename(__file__))[0] + '.jpg')
    return


if __name__ == '__main__':
    main()

f:id:cyanatlas:20200411195555j:plain

すべて消す Entirely remove the frame and axes

import os.path

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.linspace(-3, 3, 100)
    fig, ax = plt.subplots()
    y = (x - 2) * (x + 2) * x
    ax.plot(x, y)

    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)

    fig.savefig(os.path.splitext(os.path.basename(__file__))[0] + '.jpg')
    return


if __name__ == '__main__':
    main()

f:id:cyanatlas:20200411195557j:plain

目盛りのトゲを消す Remove the ticks

部分的に消す Partially remove the ticks

import os.path

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.linspace(-3, 3, 100)
    fig, ax = plt.subplots()
    y = (x - 2) * (x + 2) * x
    ax.plot(x, y)

    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.tick_params('y', length=0, which='major')

    fig.savefig(os.path.splitext(os.path.basename(__file__))[0] + '.jpg')
    return


if __name__ == '__main__':
    main()

f:id:cyanatlas:20200411195600j:plain

すべて消す Entirely remove the ticks

import os.path

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.linspace(-3, 3, 100)
    fig, ax = plt.subplots()
    y = (x - 2) * (x + 2) * x
    ax.plot(x, y)

    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.tick_params('x', length=0, which='major')
    ax.tick_params('y', length=0, which='major')

    fig.savefig(os.path.splitext(os.path.basename(__file__))[0] + '.jpg')
    return


if __name__ == '__main__':
    main()

f:id:cyanatlas:20200411195604j:plain

[数学][位相] 位相に関連する定義と定理のざっくりまとめ ~連続性と連結性とコンパクト性~

注意

  • 私(=この記事の著者)は数学徒ではありません.基本的に定理の証明は追ってません.
  • 「位相」に関連する定義と定理を(私の主観で)まとめました.
  • 主に松坂『集合・位相入門』の後半をざっと読んでまとめました.
  • 「定義」「定理」以外の部分では,私(=この記事の執筆者)独自の考えを記述している場合があります(その部分では不正確な用語法を用いている可能性があります).

この記事の主な対象

  • 数学を道具として使うために,数学を理解したいと考えている人.
  • 解析学(いわゆる微分積分学)を一通り勉強した人(「近傍」や「連続」の定義を理解した人).

(に役に立ったら嬉しいです)

目次

1 「位相」を考える目的は何か?

私は数学徒ではないので,数学のある概念を勉強するにあたって,まずはその概念が何の役に立つのかを知りたくなります(ここでの「役に立つ」は必ずしも工学的な応用を意味しません).数学のいろいろな場所で「位相」という概念が出てきますが,「位相」を用いることにどのような利点があるのでしょうか?そもそもなぜ「位相」を考えようという試みが為されたのでしょうか?

解析学(いわゆる微分積分学)では,実数の上で「収束」や「連続」といった性質を定義し,中間値の定理や最大値・最小値の定理を証明しました.これらの便利な性質や定理は,実は実数に固有の理論ではなく,「位相空間」の理論として認識することができます.おおざっぱに言えば,解析学の定義や定理を,実数に限らない一般の《ある構造》の上で展開しようというのが,位相空間論のねらいであり,その《ある構造》というのが「位相空間」であるわけです.

例えば松坂(1968, p.137)には次の記述があります.

解析学の展開される’場’としての実数の体系には,四則の演算が自由におこなわれるという代数的な性質とともに今一つ位相的な性質があり,収束や連続などの概念は,その後者の性質にもとづいて定義されるのである.一般の集合においても,そこに適当な’構造’を与えれば,連続写像その他の概念が定義され,それについて解析学で通常考えられるような理論が展開される.そのような’構造’がいわゆる’位相的構造’であって,[...]

位相空間論が解析学とどのような関係にあるのかは分かりました.では,解析学の理論を「位相空間」に拡張すると,一体どのような利点があるのでしょうか?

ここでは私の思う利点を二つ説明します.

一つ目の利点は,解析学でやや唐突に登場する「コンパクト性」がどのような役割を持つかが理解できる点です.位相空間論では,最大値・最小値の定理が成り立つためには「コンパクトな位相空間S上で定義された実連続関数f:S\to \mathbf{R}」が必要であることが分かります.解析学では「コンパクトな集合\Leftrightarrow有界閉集合」と言い換えられてしまうので,コンパクト性の役割が分かりづらいのですが,一方で,位相空間論においては,コンパクト性は最大値・最小値の定理を成り立たせるために必要であることが示され,コンパクト性の役割がはっきりします.

二つ目の利点は,位相幾何学などへの応用可能性です.例えば田村(1972, p.24)は,

位相空間はここで連続性の本質を示すものとして導入されたが,ユークリッド空間の図形に話をかぎるとしても位相空間を持ち出す方が具合がいい場合が少なくない.

と述べ,「位相空間を持ち出す方が具合がいい場合」の例として,四角形とトーラス面が同相であることの証明をこの後に挙げています.この他にも,位相空間を用いることで便利になる例があるはずです.

以上で,位相空間論がどのようなねらいを持つのか,そして位相空間を考えるとどのような利点があるのかが分かったと思います.では早速,位相空間の定義に移ります.


2 位相空間と連続性

2.1 位相空間の定義

位相空間の定義にはいろいろな流儀があるようですが,主流なのは開集合系を用いた定義のようです.以下の内容は主に松坂(1968)に基づいています.また,集合を大文字のアルファベット(S, Oなど),集合系(集合の集合)を大文字の筆記体\mathcal{O}など)で表します.

定義:空でない集合Sがあり,Sの部分集合系\mathcal{O}が次の三つの条件を満たすとき,\mathcal{O}Sにおける一つの位相であるという.
(i) S \in \mathcal{O}および\emptyset \in \mathcal{O}
(ii) O_1 \in \mathcal{O}, O_2 \in \mathcal{O}ならばO_1 \cap O_2 \in \mathcal{O}
(iii) \{O_{\lambda} | \lambda \in \Lambda \}\mathcal{O}の元からなる任意の集合系(無限集合でもよい)とすれば,\cup_{\lambda \in \Lambda} O_{\lambda} \in \mathcal{O}

定義:集合Sとその位相\mathcal{O}の組(S,\mathcal{O})位相空間という.

定義位相空間(S, \mathcal{O})が与えられたとき,\mathcal{O}の元をこの位相空間開集合という.

定義位相空間(S, \mathcal{O})が与えられたとき,補集合が開集合であるようなSの部分集合を閉集合という.

以上で「位相」「位相空間」「開集合」「閉集合」が定義されました.位相の条件(i)は全体と空集合を含むこと,条件(ii)は積集合をとる操作\capについて\mathcal{O}は閉じていること,条件(iii)は和集合をとる操作\cupについて\mathcal{O}は閉じていることを表しています.さらに,条件(iii)では無限集合に対する操作\cupについても閉じていることを約束しています.ただし,条件(ii)では有限集合のみにしか約束していません.

なぜ条件(ii)では有限集合のみ扱われているのでしょうか?ここから先は想像なので間違っているかもしれませんが,実数空間の開集合をもとに類推してみます.1以上の自然数nに対して開区間I_n = (0, 1 + 1/n)を考え,その積集合J_n = \bigcap_{k=1}^{k=n} I_kを考えます.すると,\lim_{n \to \infty} J_n = (0, 1]となるはずです.つまり,無限個の開区間の積集合は,開区間ではなくなってしまうわけです.位相空間の定義においても,無限個の開集合に対して積集合をとる操作を許してしまうと,同様の問題が発生するのではないでしょうか.そこで,無限個の開集合に対して積集合をとる操作を許さなように位相空間を定義したのではないかと思います.

簡単な集合に対して,位相の具体例を考えてみます.

:集合S=\{a, b\}における位相には次の4つがある:
\mathcal{O}_1 = \{\emptyset, S\}
\mathcal{O}_2 = \{\emptyset, \{a\}, S\}
\mathcal{O}_3 = \{\emptyset, \{b\}, S\}
\mathcal{O}_4 = \{\emptyset, \{a\}, \{b\}, S\}


2.2 近傍

次に,近傍の定義をざっとまとめます.以下では,位相を明示する必要のない場合は,位相空間(S, \mathcal{O})を単に位相空間Sと書くことがあります.

定義位相空間Sと任意の部分集合Mが与えられたとき,Mに含まれるような開集合全体の和集合(すなわちMに含まれる最大の開集合)をM内部といい,M^\circと書く.

定義位相空間SSの部分集合Mに対して,Mの内部M^\circに属する点をM内点という.

定義位相空間Sx \in Sが与えられたとき,Sの部分集合Vx近傍であるとは,xVの内点であること,すなわちx \in V^\circであることをいう.

2.3 連続写像と実連続関数

ここまでの準備をもとに,位相空間における連続写像を定義します.連続写像とは,非常に大雑把に言えば,近くの点を近くに移すような写像(”近さ”を保存する写像)であると言えます.

定義SS'を二つの位相空間とし,fSからS'への写像とする.このとき,次の条件(*)が成り立つとき,fSからS'への連続写像であるという.

(*)xSの任意の点とし,f(x)=x'とする.このときx'の(S')における任意の近傍V'に対して,f^{-1}(V')xの(Sにおける)近傍となる.

これで連続写像を定義できたわけですが,驚くべきことに,ここまで「実数」や「収束」の概念を全く使っていません.解析学において連続関数は「実数」と「収束」を用いて定義されましたが,実はこの二つの概念は連続性の定義には必ずしも必要ではないことが,上の連続写像の定義で主張されているわけです.

ちなみに連続写像の条件に関して,次の定理が成り立ちます.

定理:条件(*)は次の二つの条件のいずれとも同値である.
(i) S'の任意の開集合O'に対して,f^{-1}(O')Sの開集合となる.
(ii) S'の任意の閉集合A'に対して,f^{-1}(A')S閉集合となる.

最後に,実連続関数を定義します.

定義位相空間(S, \mathcal{O})が与えられたとき,Sから実数\mathbf{R}への連続写像実連続関数という.


3 連結性と中間値の定理

実は,連結性のある位相空間では,実連続関数は中間値の定理を満たすことが分かります.これを次に説明します.

「連結」を定義するために,今一度,閉集合の定義を確認します.閉集合は補集合が開集合となる集合として定義されました.実はこの定義に基づけば,「開かつ閉集合」という一見不思議な(?)集合が存在する可能性があると分かります.詳しく言えば,定義からS\emptysetは「開かつ閉集合」であることが分かるのですが,S\emptyset以外に「開かつ閉集合」が存在する場合もあり得るわけです.しかし,このようなケースは非直観的であって扱いにくいため,このような特殊なケースを取り除くことを考えます.そこで定義されるのが「連結」です.

定義(S, \mathcal{O})位相空間とする.開かつ閉集合であるようなSの部分集合がS\emptysetのみであるとき,位相空間S連結であるという.

実は,連続写像は連結性を保存します.

定理(S, \mathcal{O})(S', \mathcal{O}')位相空間とし,(S, \mathcal{O})は連結であるとする.f:S \to S'連続写像であるならば,f(S)S'の連結な部分空間である.

さらに,連結性から解析学における中間値の定理が導かれます.

定理位相空間\mathbf{R}の連結部分集合は,\mathbf{R}全体または区間である.

系(中間値の定理)(S, \mathcal{O})を連結な位相空間とし,f:S \to \mathbf{R}S上で定義された実連続関数とする.さらに,Sの二点 x_1, x_2におけるfの値をf(x_1)=\alpha, f(x_2)=\beta, \alpha < \betaとする.このとき,\alpha < \gamma < \betaを満たす任意の実数\gammaに対して,f(x)=\gammaとなるx\in\mathbf{R}が存在する.

以上から,連結性のある位相空間では,実連続関数は中間値の定理を満たすことが分かりました.


4 コンパクト性と最大値・最小値の定理

実は,コンパクトな位相空間上で定義された実連続関数は,最大値・最小値の定理を満たします.このことを説明します.

まずはコンパクトを定義します.定義自体は解析学で用いられていたものとほとんど同じです.

定義位相空間(S, \mathcal{O})があり,\mathcal{U}Sの部分集合系で,その和集合\bigcup \mathcal{U}S全体と一致するとき,\mathcal{U}S被覆であるという.

定義Sの被覆\mathcal{U}の各元がSの開集合であるとき,\mathcal{U}S開被覆という.

定義Sの被覆\mathcal{U}が有限個の集合からなるとき,\mathcal{U}S有限被覆という.

定義位相空間Sコンパクトであるとは,Sの任意の開被覆が必ずSの有限被覆を部分集合として含むことである.

実は,連続写像はコンパクト性を保存します.

定理Sをコンパクトな位相空間とし,fSから位相空間S'への連続写像とする.このときf(S)もコンパクトである.

実連続関数について扱うために,実数空間の性質を調べます.解析学で学んだように,実数空間ではコンパクトな集合=有界閉集合であることが分かります(ただし,実数空間全体の集合\mathbf{R}^n自体は開集合なのでコンパクトではないことに注意).

定義\mathbf{R}^nの部分集合Mについて,M\mathbf{R}^nのある球体B(a;\epsilon)に含まれるとき,有界であるという.

定理:Euclid空間\mathbf{R}^nの部分集合Mがコンパクトであるための必要十分条件は,M\mathbf{R}^n有界閉集合であることである.

最後に,以上の定理から,実連続関数について最大値・最小値の定理が成り立つことが分かります.

M位相空間\mathbf{R}のコンパクトな部分集合とすれば,\max M, \min Mが存在する.

系(最大値・最小値の定理)f:S \to \mathbf{R}をコンパクトな位相空間S上で定義された実連続関数とすれば,その値域f(S)には最大値および最小値が存在する.すなわち,Sの中に点x_{\mathrm{argmax}}, x_{\mathrm{argmin}}が存在して,すべてのx \in Sに対してf(x_{\mathrm{argmin}}) \leq f(x) \leq f(x_{\mathrm{argmax}})が成り立つ.

以上で,コンパクトな位相空間から実数空間への連続写像に関しては,最大値・最小値の定理が成り立つことが分かりました.


5 まとめ

  1. 位相空間を用いれば,「実数」や「収束」を用いなくても,連続写像が定義できる.
  2. 連結な位相空間から実数への連続写像に関して,中間値の定理が成り立つ.
  3. コンパクトな位相空間から実数への連続写像に関して,最大値・最小値の定理が成り立つ.

参考文献

松坂和夫(1968)『集合・位相入門』岩波書店
内田伏一(2009)『集合と位相』(第25版)裳華房
田村一郎(1972)『トポロジー岩波書店
小平邦彦(2003)『解析入門I』岩波書店

[Python][Matplotlib] Matplotlibのデフォルト設定を取得する方法(rcParamsから値を取得する方法)

環境:Windows10 + Anaconda + Python 3.6.4 + Matplotlib 2.1.2

Pythonの Matplotlibライブラリには、図のサイズや線の色といった、デフォルトの設定がある。デフォルトの設定の一部は、rcParamsというMatplotlibオブジェクトに格納されている。rcParamsの値は、getメソッドを用いて取得することができる。

コード例

import matplotlib.pyplot as plt

plt.rcParams.get('figure.figsize')   # -> [6.4, 4.8]
plt.rcParams.get('lines.color')      # -> 'C0'