乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

matplotlib

ある番組でとあるクイズ王の
出身高校の話が出ていて
気になって調べてしまいました。

解説動画はこちら



さて今回はこちらのサイトさんの
データを参考にさせていただいております。

参考:みんなの高校情報



さて
データはCSVなどにして
pandasのデータフレームに取り込みます。

こんな感じのデータになりました。
偏差値ランク学校名住所形態評判
0791灘高等学校兵庫県神戸市東灘区私立4.51
1782お茶の水女子大学附属高等学校東京都文京区国立4.14
2782開成高等学校東京都荒川区私立4.96
3782筑波大学附属高等学校東京都文京区国立4.27
4782筑波大学附属駒場高等学校東京都世田谷区国立4.56

これを使って行きたいと思います。

まず最初は統計情報です。
df['偏差値'].describe()
count    7765.000000
mean       50.005151
std         9.265878
min        35.000000
25%        42.000000
50%        48.000000
75%        57.000000
max        79.000000
Name: 偏差値, dtype: float64

全部で7765校分のデータで
平均偏差値は50ですが
中央値は48とやや低いので
低めにデータが偏っていそうですね。

と言うことで
偏差値ごとの学校数で
グラフ化してみましょう。

偏差値を横軸に
学校数を縦軸にとった
ヒストグラムを生成します。
divine_df  = pd.DataFrame(df['偏差値'].value_counts().sort_index())
divine_df['累積'] = divine_df['偏差値'].cumsum()/sum(divine_df['偏差値'])
divine_df.head()
偏差値累積
3570.000901
36930.012878
371810.036188
382560.069156
393500.114231

fig, ax1 = plt.subplots(figsize=(16,10))
ax2 = ax1.twinx()
ax1.bar(divine_df.index, divine_df['偏差値'], label="偏差値")
ax2.plot(divine_df.index, divine_df['累積'],label="累積",c='orange')
ax1.xaxis.set_ticks(pd.np.arange(35, 80, 1))
for i,d in enumerate(divine_df['偏差値']):
    ax1.text(divine_df.index[i], d, "{0}".format(d), size=10)
plt.show()
download

中央値は48でしたが
一番高校が多い偏差値は42ですね。

大体偏差値57位で
全体の80%の高校がおさまります。


ここからは偏差値TOPの学校を
みていきましょう。
df[df['ランク']<=10]
偏差値ランク学校名住所形態評判
0791灘高等学校兵庫県神戸市東灘区私立4.51
1782お茶の水女子大学附属高等学校東京都文京区国立4.14
2782開成高等学校東京都荒川区私立4.96
3782筑波大学附属高等学校東京都文京区国立4.27
4782筑波大学附属駒場高等学校東京都世田谷区国立4.56
5782東大寺学園高等学校奈良県奈良市私立4.44
6782ラ・サール高等学校鹿児島県鹿児島市私立4.09
7778慶應義塾女子高等学校東京都港区私立4.09
8778東京学芸大学附属高等学校東京都世田谷区国立4.49
9778神戸高等学校兵庫県神戸市灘区公立4.10


偏差値70以上の学校をみるには
こんなコードになりますが
数は218校あるようです。
df[df['偏差値']>=70]

逆にげべはどうなんだって
話になると思うので
げべはこんな感じでした。

df[df['ランク']==max(df['ランク'])]
偏差値ランク学校名住所形態評判
7758357759田口高等学校愛知県北設楽郡設楽町公立3.30
7759357759真壁高等学校茨城県桜川市公立2.77
7760357759京都国際高等学校京都府京都市東山区私立2.19
7761357759西山学院高等学校宮城県刈田郡七ヶ宿町私立2.75
7762357759東大阪大学柏原高等学校大阪府柏原市私立2.83
7763357759耶麻農業高等学校福島県喜多方市公立2.75
7764357759霞城学園高等学校山形県山形市公立2.91

次に都道府県別の偏差値を見てみましょう。

都道府県別などにしたい場合は
カテゴリとして都道府県が必要です。

住所から都道府県を抜き出す関数を作って
うまくデータ化します。
def function(se):
    if '北海道' in se:
        return '北海道'
    elif '東京都' in se:
        return '東京都'
    elif '大阪府' in se or '京都府' in se:
        return '大阪府'
    elif '京都府' in se:
        return '京都府'
    else:
        return se.split('県')[0]+'県'

df['都道府県'] = df['住所'].apply(function)
df[['都道府県','偏差値']].groupby("都道府県").describe().sort_values(('偏差値',  'mean'),ascending=False)
偏差値
countmeanstdmin25%50%75%max
都道府県
埼玉県352.053.9119329.41754940.045.053.061.076.0
東京都585.053.8581209.35388938.046.053.060.078.0
千葉県304.051.9802639.67387038.043.050.059.076.0
大阪府713.051.7082759.40246535.044.050.058.076.0
兵庫県334.051.5838328.87909336.044.050.058.079.0

平均偏差値だと埼玉がTOPになるようです。

偏差値70以上の高校だけに絞るコードは
次のようになりました。
df[df['偏差値']>=70][['都道府県','偏差値']].groupby("都道府県").describe().sort_values(('偏差値',  'count'),ascending=False)

40以下をみる場合は条件を40以下に
変更すれば見られます。

最後に都道府県別での箱ヒゲ図を
見ていたいと思います。

都道府県ごとの分布のような
ものを見たい場合にこの箱ヒゲ図は
非常に役立ちます。

seabornと言うライブラリを用いると
箱ヒゲ図を作ることができます。
import seaborn as sns

plt.figure(figsize=(16,10))
idx = df[['都道府県','偏差値']].groupby("都道府県").agg(np.median).sort_values('偏差値',ascending=False).index
sns.boxplot(x="都道府県", y="偏差値", data=df,order=idx)
plt.xticks(rotation=90)
plt.show()
download-1


まとめですが
埼玉県 , 東京都 , 千葉県 , 大阪府 , 兵庫県
この5県は偏差値の平均が高く

この近隣に住むと偏差値の高い高校に
通いやすいでしょうね。

自分は近所の高校で
卒業時の偏差値20くらいなので
今だとどの高校にも入れなさそうwww

今回はこれまでです
それでは



今回はウラムの螺旋についてです。 
ドラマ「危険なビーナス」を見ていて
気になってしまったので
これを作るプログラムを
作成してみようと思います。

解説動画はこちら



まずウラムの螺旋とは
数学者のウラムさんが発見と言うか作った
素数をとある法則に従って並べた図形です。

uram
1から渦巻状に数値を配置する際に
素数だけ残すと、対角線状に素数が残ります。

まずは1から数値を並べるところを
考えていきましょう。


def uram(N=7):
    # 箱を用意
    data = [ [0 for n1 in range(N)] for n2 in range(N)]

    # 入れる値を生成
    nums,tmp,c = [],[],1
    for num in range(1,N*N+1):
        tmp.append(num)
        if num==c*c:
            nums.append(tmp)
            tmp,c=[],c+1
            #c+=1

    # 起点位置決め
    c_y,c_x = N//2,N//2 if N%2==0 else N//2+1

    # 値の代入
    for i,tmp in enumerate(nums,start=1):
        for j,n in enumerate(tmp):
            sub = 1 if i%2==0 else -1
            if j==0:
                c_y,c_x=c_y,c_x + sub*1
            elif j>len(tmp)//2:
                c_y,c_x=c_y,c_x - sub*1
            else:
                c_y,c_x=c_y - sub*1,c_x
            data[c_y][c_x]=n
    return data

data = uram(5)
for row in data:
    for t in row:
        print('{0:}'.format(t),end='\t')
    print()
17 16 15 14 13
18 5 4 3 12
19 6 1 2 11
20 7 8 9 10
21 22 23 24 25


最初にN*Nの箱を作ります。

次に入れる数値の生成ですね。
こんな感じでN*Nの数値のリストを作ります。
N = 5
nums,tmp,c = [],[],1
for num in range(1,N*N+1):
    tmp.append(num)
    if num==c*c:
        nums.append(tmp)
        tmp,c=[],c+1
print(nums)
[[1],
[2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24, 25]]

ここでは螺旋状に数値を入れる際に
折り返しがあるので
入れる数値のリストの半分のところで
折り返すようにしています。

Nが偶数の時はその前の数値から
右に一つずれ、そこから上
真ん中のところで左に折り返し

Nが奇数の時はその前の数値から
左に一つずれ、そこから下
真ん中のところで右に折り返す
ように数値を入れていくと
螺旋状に数値が並んだ
リストが生成できます。

次に素数だけを
残すようにしてみましょう。

sympyのisprimeと言うメソッドで
素数判定ができます。

これを使って素数の部分だけを
残すように変えてみます。


from sympy import isprime

def uram2(N=7):
    # 箱を用意
    data = [ [0 for n1 in range(N)] for n2 in range(N)]

    # 入れる値を生成
    nums,tmp,c = [],[],1
    for num in range(1,N*N+1):
        tmp.append(num)
        if num==c*c:
            nums.append(tmp)
            tmp=[]
            c+=1

    # 起点位置決め
    c_y,c_x = N//2,N//2 if N%2==0 else N//2+1

    # 値の代入
    for i,tmp in enumerate(nums,start=1):
        for j,n in enumerate(tmp):
            text = ' | '
            sub = 1 if i%2==0 else -1
            if j==0:
                c_y,c_x=c_y,c_x + sub*1
            elif  j>len(tmp)//2:
                text = ' - '
                c_y,c_x=c_y,c_x - sub*1
            else:
                c_y,c_x=c_y - sub*1,c_x
            # 素数判定
            if isprime(n):
                data[c_y][c_x]=n
            else:
                data[c_y][c_x]=text
    return data

data = uram2(5)
for row in data:
    for t in row:
        print('{0:}'.format(t),end='\t')
    print()

17 - - - 13
 | 5 - 3 |
19 | | 2 11
 | 7 - - |
 | - 23 - -


はいこれで素数だけ残りましたね。

次は塗りつぶしですが
数値を文字列に変えるだけでできます。

     
     
   
       
       

これだと使いづらいので
画像にするようにしてみましょう。

matplotlibではリストを投入すると
そのまま画像化できます。

素数を0,他は255を入れた
リストを作ってplotするだけです。

せっかくなので1000x1000
100万までの素数で
ウラムの螺旋を作ってみましょう。

from sympy import isprime
import matplotlib.pyplot as plt
%matplotlib inline

def uram4(N=7):
    # 箱を用意
    data = [ [255 for n1 in range(N)] for n2 in range(N)]

    # 入れる値を生成
    nums,tmp,c = [],[],1
    for num in range(1,N*N+1):
        tmp.append(num)
        if num==c*c:
            nums.append(tmp)
            tmp=[]
            c+=1

    # 起点位置決め
    c_y,c_x = N//2,N//2 if N%2==0 else N//2+1

    # 値の代入
    for i,tmp in enumerate(nums,start=1):
        for j,n in enumerate(tmp):
            sub = 1 if i%2==0 else -1
            if j==0:
                c_y,c_x=c_y,c_x + sub*1
            elif  j>len(tmp)//2:
                c_y,c_x=c_y,c_x - sub*1
            else:
                c_y,c_x=c_y - sub*1,c_x
            if isprime(n):
                data[c_y][c_x]=0

    return data

data = uram4(1000)
plt.figure(figsize=(10,10))
plt.imshow(data)
plt.axis("off")
plt.show()
download

メモリとCPUが強力であれば
もっと大きな螺旋も描けると思います。

これ以上はなかなかに痺れますね。

素数の法則は見つかっていないと
思いますが、なんかロマンがある図形ですね。

お暇な方は試してみてください。
それでは。


最近見ているドラマでフラクタル図形が
取り上げられていたので
気になっちゃいました。

今回は色々なフラクタル図形を作図します。

解説動画はこちら




さてフラクタル図形と言うものは
どう言う図形でしょうか?

フランスの数学者ブノワ・マンデルブロが
導入した幾何学の概念で
図形の一部が全体と
自己相似な構造を持っている
図形のことです。

結構たくさんの種類があります。

Python言語では作図の方法が沢山あります。

最もメジャーな方法としては
matplotlibを用いた作図です。

x,y座標を計算してプロットするものです。


マンデルブロ集合
まず最初はマンデルブロ集合です。
数学者ブノワ・マンデルブロの
名に因む集合体の図形です。

次のようなコードで試すことができます。
from ipywidgets import interact,IntSlider
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

h,w = 1000,1000 
y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
c = x+y*1j

def mandelbro_plt(rate=30):
    z = c
    times = rate + np.zeros(z.shape, dtype=int)
    for i in range(rate):
        z = z**2 + c
        diverge = z*np.conj(z) > 2**2
        div = diverge & (times==rate)
        times[div] = i
        z[diverge] = 2
    return times

mdb_rate = IntSlider(min=1, max=30, step=1, value=1)
@interact(mdb_rate=mdb_rate)
def plot(mdb_rate):
    plt.figure(figsize=(10,10))
    plt.imshow(mandelbro_plt(mdb_rate))
    plt.show()
download-2

スライダーを上げていくと
より細かい感じになっていきます。

一部を見ると貝のような感じにも
見受けられる図形です。


シェルピンスキーのギャスケット
ポーランドの数学者
ヴァツワフ・シェルピンスキに
因んで名づけられた

自己相似的な無数の
三角形からなる図形です。

matplotlib.patchesの
Polygonで三角形を描画できるので
これを使っていきます。

patches.Polygon(xy = [座標1, 座標2, 座標2])
少し複雑なプログラムですが
スライダーで三角形の
深さを変えることができます。
from ipywidgets import interact,IntSlider
import matplotlib.pyplot as plt
import matplotlib.patches as pat
import numpy as np
%matplotlib inline

def dist(p1,p2):
    return np.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)

def points(p, tri):
    d1,d2,d3 = dist(p, tri[0]),dist(p, tri[1]),dist(p, tri[2])
    if d1 > d2:
        return [p, tri[1], tri[2]] if d1 > d3 else [p, tri[0], tri[1]]
    else:
        return [p, tri[0],tri[2]] if d2 > d3 else [p, tri[0],tri[1]]

def make_tri(tri):
    x1,y1 = (tri[0][0] + tri[1][0])/2,(tri[0][1] + tri[1][1])/2
    x2,y2 = (tri[1][0] + tri[2][0])/2,(tri[1][1] + tri[2][1])/2
    x3,y3 = (tri[2][0] + tri[0][0])/2,(tri[2][1] + tri[0][1])/2
    return [(x1,y1),(x2,y2),(x3,y3)]

def make_fractal(ax,bef, ite):
    if ite == 0: return 0
    p1,p2,p3 = bef[0],bef[1],bef[2]
    tri = make_tri(bef)
    ax.add_patch(pat.Polygon(xy = tri,fc = "white", ec = "black"))
    make_fractal(ax,points(p1,tri), ite-1)
    make_fractal(ax,points(p2,tri), ite-1)
    make_fractal(ax,points(p3,tri), ite-1)

tri_rate = IntSlider(min=0, max=7, step=1, value=0)
@interact(tri_rate=tri_rate)
def plot(tri_rate):
    tri = [(0.2, 0.2), (0.8, 0.2), (0.5, 0.8)]
    fig = plt.figure(figsize=(10, 10))
    ax = fig.add_subplot(1,1,1)
    ax.add_patch(pat.Polygon(xy = tri,fc = "white", ec = "black"))
    make_fractal(ax,tri,tri_rate)
    plt.show()
download-1


コッホ曲線
スウェーデンの数学者
ヘルゲ・フォン・コッホが考案した
線分を3等分し分割した2点を
頂点とする正三角形の作図を
無限に繰り返すことによって
得られる図形です。

PythonのPILのImage.drawを用いて
座標を結ぶ直線を描くことができるので
これを使っていきます。

最初は直線を引き
それを分割するような点を求め
三角形の頂点を求め・・・

と言うのを繰り返すことで
描画する座標を求めて
その点同士を直線で結んでいきます。

from PIL import Image,ImageDraw
from math import cos,sin,radians
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

def divine(num,tp):
     if num>0:
        (Ax,Ay),ind=tp[0],1
        for (Bx,By) in tp[1:]:
            ABx,ABy= Bx - Ax , By - Ay
            ACx,ACy= ABx / 3,ABy / 3
            Cx,Cy = Ax + ACx,Ay + ACy
            Dx,Dy = Ax + ACx*2,Ay + ACy*2
            Ex,Ey = cos(r)*ACx - sin(r)*ACy + Cx,sin(r)*ACx + cos(r)*ACy + Cy
            tp.insert(ind,  (Cx,Cy))
            tp.insert(ind+1,(Ex,Ey))
            tp.insert(ind+2,(Dx,Dy))
            ind,Ax,Ay=ind+4,Bx,By
        divine(num-1,tp)

def calc_cell(n,t):
    divine(n,t)
    return t

n1_rate = IntSlider(min=0, max=5, step=1, value=0)
n2_rate = IntSlider(min=3, max=12, step=1, value=3)
@interact(n1_rate=n1_rate,n2_rate=n2_rate)
def plot(n1_rate,n2_rate):
    white,black=(255,255,255),(0,0,0)
    w,h = 2000,2000
    img=Image.new('RGB',(h,w), white)
    draw=ImageDraw.Draw(img)
    x,y,r=[],[],w//2
    for _x in np.linspace(-180,180,361):
        if _x%(360//n2_rate)==0:
            x.append(int(sin(radians(_x))*r*3//4)+r)
            y.append(int(cos(radians(_x))*r*3//4)+r)
    x.append(x[0])
    y.append(y[0])
    poslist = []
    xys = [(xx,yy) for xx,yy in zip(x,y)]
    tmps = [[xys[b],xys[b+1]]for b in range(len(xys)-1)]
    for tmp in tmps:
        poslist += calc_cell(n1_rate,tmp)
    (Ax,Ay)=poslist[0]
    for (Bx,By) in poslist[1:]:
        draw.line((Ax,Ay, Bx,By),fill=black,width=5)
        Ax,Ay = Bx,By
    plt.figure(figsize=(10,10))
    plt.imshow(img)
    plt.show()

download

スライダーで分割数と
多角形の辺の数を定義できます。

より深くしていくと
曲線に近くなりますね。

ドラマ
「危険なビーナス」では
このフラクタル図形と言うのが
物語の鍵になっているようです。

人間が描こうとすると
かなり至難の技ですが
コンピューターであれば
描く点の座標さえ計算できれば
どんな複雑な図形であっても
描くことができます。

ドラマのフラクタル図形は
もっと複雑でしたね。

これを作るのは大変そう・・・
興味がある方は
作ってみてください。

それでは。

前回作った関数を進化させてみました。


解説動画はこちら




前回はこちら
動くおっぱい関数

さて
Python言語ではmatplotlibというライブラリで
作図、可視化を行うことができます。

ということなので
曲線を描くことなどが出来てしまうわけです。

前回は黒い曲線で可視化しましたが
それだと味気ないので
色を塗ってみました。

ただし、これは静止画ではありません。
動くんです!!!

なので毎回色ぬりする範囲を
計算しないといけません。

うまく塗る範囲を考えてみました。

出来上がった関数はこちら
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from ipywidgets import interact, FloatSlider, IntSlider
import warnings
warnings.simplefilter('ignore')
%matplotlib inline

def oppai(y,t):
    x_1 = (1.5 * np.exp((0.12*np.sin(t)-0.5) * (y + 0.16 *np.sin(t)) ** 2)) / (1 + np.exp(-20 * (5 * y + np.sin(t))))
    x_2 = ((1.5 + 0.8 * (y + 0.2*np.sin(t)) ** 3) * (1 + np.exp(20 * (5 * y +np.sin(t)))) ** -1)
    x_3 = (1+np.exp(-(100*(y+1)+16*np.sin(t))))
    x_4 = (0.2 * (np.exp(-(y + 1) ** 2) + 1)) / (1 + np.exp(100 * (y + 1) + 16*np.sin(t)))
    x_5 = (0.1 / np.exp(2 * (10 * y + 1.2*(2+np.sin(t))*np.sin(t)) ** 4))
    x = x_1 + (x_2 / x_3) + x_4 + x_5
    return x
 
t = FloatSlider(min=0.1, max=5.0, step=0.1, value=0)
y = np.arange(-3, 3.01, 0.01)
 
@interact(t=t)
def plot_oppai(t):
    x = oppai(y,t)
    plt.figure(figsize=(10,9))
    plt.axes().set_aspect('equal', 'datalim')
    plt.grid()
    b_chiku = (0.1 / np.exp(2 * (10 * y + 1.2*(2+np.sin(t))*np.sin(t)) ** 4))
    b_index = [i for i ,n in enumerate(b_chiku>3.08361524e-003) if n]
    x_2,y_2 = x[b_index],y[b_index]
    plt.axes().set_aspect('equal', 'datalim')
    plt.plot(x, y, '#F5D1B7')
    plt.fill_between(x, y, facecolor='#F5D1B7', alpha=1)
    plt.plot(x_2, y_2, '#F8ABA6')
    plt.fill_between(x_2, y_2, facecolor='#F8ABA6', alpha=1)
    plt.show()
スクリーンショット 2020-07-18 14.54.46

いやー素晴らしい曲線ですよね。

x,y座標に描く曲線
これは前回と一緒です。

色ぬりの指定は
#F5D1B7
のように16進数のwebカラー表記で
書いて指定します。

デフォルトは黒いようなので
肌色を指定しています。

webで検索すれば好みの色に該当する
数値がわかるはずです。

次のポイントは
色ぬりの範囲指定です。

plt.fill_between

これで色ぬりができるんですが
範囲はxの最小値からxの最大値のようですね。

まず全体を肌色で塗り込みます。

後はB地区の計算です。

B地区の範囲を計算している部分は
def oppaiのx_5を計算している部分です。

これが曲線全体における
一番の盛り上がりの部分を
計算しているところです。

このdef oppaiではyの値を元に
x座標を計算して出力していますので
盛り上がり部分の数値がわかれば
塗る範囲も決まります。

この数値を計算すると盛り上がり部分が
ある一定の数以上の値になる部分でした。

ここを
x,yそれぞれをインデックスで取り
その部分だけピンク色を指定して
塗っています。

ようやく完成です。

tの値を変えると
プルンプルン動きます。
タイトルなし

いやー今年一番の感動です。
暇でよかった!!!!

matplotlibであれば
あなたの夢も可視化できるかもしれませんね

今回はこれまでです
それでは。

可視化の授業には使えない

クソの役にも勃たない関数です。

動画はこちら




はーい
それでは関数の作り方をみていきましょう。


xの値からyを作る数式を元に
関数を作ります。

可視化の元となる関数は以下の通りです。
npはnumpyです。

y = np.abs(t*np.sin(x)) + ((5*np.e)**(-x**k)) * s * np.cos(x)

はい
これを上手いこと
Pythonを使って
再現していきます。

出来上がったものはコレ
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider, IntSlider
%matplotlib inline

s = FloatSlider(min=0.8 , max=10,step=0.1,value=2.1)
t = FloatSlider(min=0.1 , max=2,step=0.1,value=0.2)
k = IntSlider(min=2,max=20,step=2,value=4)
@interact(s=s,t=t,k=k)
def plot(s,t,k):
    plt.figure(figsize=(10,9))
    x = np.arange(-3,3,0.01)
    y = np.abs(t*np.sin(x)) + ((5*np.e)**(-x**k)) * s * np.cos(x)
    plt.ylim(0,10)
    plt.xlim(-5,5)
    plt.plot(x,y)
    plt.show()

こいつを実行していただくと
出ます。!!
画像がね(汗)

ここには載せないようにしときますねーー
モザイク必要なんでwww

一応使い方としては
スクリーンショット 2019-07-27 16.05.38

S:サオ
T:タマ
K:カリ

になっているので
大きさを変えれます。

まずはTを変更してもらい
次にSで伸ばしてもらい
Kをいい感じの太さにしてくださいねーー

太くて
長くて
たくましい
・・・

まるで戦艦大和やないかーーい

はーーい
クソですねー

みなさん楽しんでくださいませ

それでは

このページのトップヘ