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

可視化

新型コロナウイルスが流行ってきています。

ありがたいことに
データをまとめて頂いている方が
いらっしゃるので
そのデータを用いて可視化をしてみます。


解説動画はこちら




データの提供元は東洋経済新聞社の方のようです。
めちゃくちゃ感謝したいと思います。


新型コロナウイルス国内感染の状況
https://toyokeizai.net/sp/visual/tko/covid19/

制作:荻原 和樹(東洋経済オンライン編集部)
データソース:厚生労働省の報道発表資料。

更新履歴・ソースコード:GitHub
https://github.com/kaz-ogiwara/covid19/

さてそれではデータを可視化をしていきましょう。

まずは
ライブラリの読み込みです。

今回は下記のライブラリを用います。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline


次に
データの読み込みです。
直接データを読み込み指定します。

file_path = 'https://toyokeizai.net/sp/visual/tko/covid19/csv/data.csv'
df = pd.read_csv(file_path)
df.head()


新No.
旧No.確定日年代性別居住地周囲の患者の発生※濃厚接触者\nの状況
0111/1530代神奈川県なし38名特定、健康観察終了
1221/2440代中国(武漢市)なし32名特定、健康観察終了
2331/2530代中国(武漢市)なし7名特定、健康観察終了
3441/2640代中国(武漢市)No.192名特定、健康観察終了
4551/2840代中国(武漢市)なし3名特定、健康観察終了

こんな感じのローデータですね。

年代や性別などで
日別の感染者の状況をまとめてくれています。


はじめに
性年代別で集計してみましょう。

年代の並びがアレなので先に指定しておきます。
# 年代の並びを指定
order = ['10歳未満', '10代', '20代','30代','40代','50代','60代','70代','80代','90代']
data = pd.crosstab(df['性別'],df['年代'])[order]
data
年代10歳未満10代20代30代40代50代60代70代80代90代
性別
01941112151231
4161017283318120

これをヒートマップにしてみましょう。
ヒートマップはseabornライブラリで可視化できます。
data = pd.crosstab( df['性別'],df['年代'])[order]
ax = sns.heatmap(data,annot=True, fmt="d")
ax.set_ylim(len(data), 0)
plt.title('性年代別感染者数')
plt.show()
download-4

棒グラフにもしてみます。
棒グラフはseabornでは
countplotで可視化できます。
sns.countplot(y="年代", hue="性別",data=df,order=order)
plt.title('性年代別感染者数')
plt.show()

download-3
50-60代の男性の感染者が多いですね。

全体的には女性に比べると
男性の方が感染する方が多いようです。

男性の方がかかりやすいのかどうかは
このデータからは分かりませんね。



次に日別です。

日の並びも先に指定します。
date_order = ['1/15', '1/24', '1/25', '1/26', '1/28', '1/29', '1/30', '1/31','2/4', '2/5', '2/11',
       '2/13', '2/14', '2/15', '2/16', '2/17', '2/18', '2/19', '2/20', '2/21',
       '2/22', '2/23', '2/24', '2/25', '2/26', '2/27', '2/28', '調査中']
plt.figure(figsize=(16,9))
sns.countplot(x="確定日", hue="性別",data=df,order=date_order)
plt.title('日別感染者数')
plt.show()
download-2

日別の性別での可視化です。

男女を積み上げ棒グラフにしてみます。
data = pd.crosstab(df['性別'],df['確定日'])[date_order].T

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.bar(date_order, data['男'], label='男')
ax.bar(date_order, data['女'], bottom=data['男'], label='女')
ax.legend()
plt.title('日別感染者数')
plt.show()
download-1
2/22,2/27に感染者数が激増していますね。

グラフを累積にしてみます。
累積のデータを先に作って
それを可視化しています。

numpyのcumsumで累積データを作ることができます。
data = pd.crosstab(df['性別'],df['確定日'])[date_order]
data2 = np.cumsum(data.T)

fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.bar(date_order, data2['男'], label='男')
ax.bar(date_order, data2['女'], bottom=data2['男'], label='女')
ax.legend()
plt.title('日別累積感染者数')
plt.show()
download


最後に都道府県別でみてみましょう。
data = pd.crosstab(df['居住地'],df['性別'])
data2 = data.sort_values('男',ascending=True)
data2


fig = plt.figure(figsize=(16,9))
ax = fig.add_subplot(1, 1, 1)
ax.barh(data2.index, data2['男'], label='男')
ax.barh(data2.index, data2['女'], left=data2['男'], label='女')
ax.legend()
plt.title('都道府県別感染者数')
plt.show()
download-5

北海道がすごいことになっていますね。

2番目に東京と愛知です。

地域差はかなりあると思いますが
そもそも目には見えないので
どこに潜んでいるかは
正直感じることもできないと思います。

企業の中には
在宅勤務を始める会社も出てきていて

人混みの中に出ないことが
感染予防につながると思いますので
リモートワークはどんどん広がっていくと
いーなーと思っています。

まあ
我々エンジニアは
客先に行くことなんて稀なので
全然在宅でも仕事できちゃいますよね。

2,3月は
大人しくしていた方が
良さそうな気はしますね。

この機会に
Pythonプログラミングを
覚えてみてはいかがでしょうか?

Youtubeなどでも
講座を公開していますので
ぜひみてみてください。

それでは。

グランメゾン東京をみているので
ミシュランの三つ星店が気になってしまいました。

ミシュランの星のついたお店群を
いい感じのライブラリがあったので
可視化してみました。

解説動画はこちら



さて今回使用する可視化のライブラリは
Foliumです。

緯度経度を使用して
いい感じに地図にマッピングできます。

foliumを使うにはインストールしないといけません

まずはインストールしておきましょう。

pip install folium

可視化するにはデータが必要です。

次のようなデータを作りました。
スクリーンショット 2019-12-29 20.31.33

必要な項目としては
お店の名前、星の数、ジャンル、住所、緯度経度などが必要です。

データを集めたら
Foliumを使って可視化です。

地図にマッピングした
HTMLファイルを作成することができます。

実際に作成したコードを貼っておきます。
import folium
from folium.features import CustomIcon
import pandas as pd

stars = {
'一つ星'        :'star1.png',
'二つ星'        :'star2.png',
'三つ星'        :'star3.png',
'ビブグルマン'  :'starbib.png'
}

def makeicon(icon_image):
    icon = CustomIcon(
    icon_image=icon_image,
    icon_size=(30, 30),
    icon_anchor=(30, 30),
    shadow_image='',
    shadow_size=(5, 5),
    shadow_anchor=(4, 4),
    popup_anchor=(-3, -3))
    return icon

def make_maker(r):
    tmp = folium.Marker(
        location = [r['longitude'],r['latitude']], 
        popup    = r['name'] + ' : ' + r['genre'],
        tooltip  = r['name'] + ' : ' + r['genre'] + ' : ' + r['address'].replace('東京都',''),
        icon     = makeicon(stars[r['star']]))
    return tmp

michelin_data = pd.read_table('michelin_data_2020.csv')

plot_map = folium.Map(location=[35.6725175,139.7210145] ,  zoom_start=12)

star1_group = folium.FeatureGroup(name="一つ星").add_to(plot_map)
star2_group = folium.FeatureGroup(name="二つ星").add_to(plot_map)
star3_group = folium.FeatureGroup(name="三つ星").add_to(plot_map)
bib_group   = folium.FeatureGroup(name="ビブグルマン").add_to(plot_map)

for i, r in michelin_data[michelin_data['star']=='一つ星'].iterrows():
    star1_group.add_child(make_maker(r))
for i, r in michelin_data[michelin_data['star']=='二つ星'].iterrows():
    star2_group.add_child(make_maker(r))
for i, r in michelin_data[michelin_data['star']=='三つ星'].iterrows():
    star3_group.add_child(make_maker(r))
for i, r in michelin_data[michelin_data['star']=='ビブグルマン'].iterrows():
    bib_group.add_child(make_maker(r))

folium.LayerControl().add_to(plot_map)
plot_map.save("mishelin_all_maps.html")

コードの説明ですが
まず星を出し分けるためにカスタムアイコンを用います。

CostomIconをインポートしておきます。

folium.Mapで初期マップを作ります。
緯度経度とズーム値を指定して
どこの地点から始めるのかを指定できます。

この初期マップにお店を一軒ずつ加えていきます。

グループ化しておくと
アイコンを点けたり消したり出来るので
星の種別ごとにグループ化しておきます。

グループごとにお店を加えて
LayerControlで出し分けをするための
パーツを作れます。

最後saveでHTMLファイルに出力をして
おしまいです。

出来上がりはこんな感じです。
スクリーンショット 2019-12-29 20.39.52

3つ星のお店だけ表示させました。

Foliumでは星ごとに出し入れ出来るので
右上のパーツを押してみてください。


出来上がりをリンクしておきますね。
ミシュランの3つ星のお店

400店舗ほどあるので
行きたい方はぜひ使ってみてください。

さて
緯度経度を用いた可視化も
Pythonを使えば簡単にできます。

ぜひ試してみてくださいね

グランメゾン東京の最終回も楽しみですね!!!

それでは
 

さて
最近話題に上がった

食べログ分布

に関して
そんなものが存在するのかどうか
検証してみました。

解説動画はこちら



食べログ分布を手元で見たい方は
まずは
データを集めてください。

食べログさんのサイトへ行って
コピペしていただければ
集まるかなーと思います。

集めるのは大変だと思うので
こちらで集めてみました。

手元のデータは
東京の店舗で
約36千件です。

これを可視化してみましょう。

評点で3-4だけに絞っていますが
全店舗だとこうなりました。

zenken

縦が店舗数
横は評点です

3.0-3.1あたりがボリュームが多いですね

おそらくですが
加盟したばかりの店舗というのは
3.0から始まるのではないかと思います。

有る程度コメントなどが加味されて
評点が決まっていくのではないかな?
と思われます。

ということで
有る程度コメントがある店舗に絞って
みてみましょう。

コメント100より多い店舗だけに
絞って可視化してみると・・・

100ken

どうでしょうか

分布が変わりましたね。

有る程度
コメント数などで
評点が定まってくるのだと思います。

この場合はボリュームゾーンが
3.6あたりになりますね。

自然界のデータを無作為に抽出すると
正規分布を取ることが多いので
正規分布も重ねてみましょう。

seiki

赤線は平均を3.6とした際の正規分布です。

どうでしょう
正規分布と比べると
ややスカスカしてる部分が有りますね!!!!

seiki2

これがそのスカスカ部分を囲ってみたものですが

評点に対する店舗数は
正規分布に収束するという仮定だと

もう少し3.6-3.7と
3.8-3.85くらいまでの店舗が
存在していてもおかしくはないのですが
少ないんですねーーー

このやや
歪な感じの分布というのが
「食べログ分布」
ということになります。

有りましたね!!

正規分布などに比べると
いびつさが際立つ分布になるという
結果になりました。

さて
いかがでしたでしょうか?

あと余談ですが
色々みてみると
食べログさんは

「3.0から5.0までの20段階評価である」

ということが分かりました。

なので
3.5と言っても20段階で見れば
それほどでも無いのです。

ただし、店舗の大半は
3.5に到達しないので

3.5も有れば相対的には
良く見えるよ、ということなのでしょう。

単純にコメント数の大小などでは
この評点は決まらない仕組みなのでしょうね

そもそも仕組みを公開してないと思いますし

この評点を信じるのか
自分の舌を信じるのかは
あなた次第です!!

日本で一番有名なマークである

安産祈願マークを
プログラムで作ってみましょう!!!


動画はこちら



さて
作り方は
numpyとmatplotlibを用いて
マークを描いていきます。

widgetも用いて
うねうね変形できるようにもします。

ソースはこちら
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider, IntSlider,Select
%matplotlib inline

r = IntSlider(min=1, max=8, step=1, value=5)
t = FloatSlider(min=1.0, max=1.5, step=0.1, value=1.0)
c = Select(options=['black', 'red', 'blue','green'],
    value='black',description='coler : ',disabled=False)

@interact(r=r,t=t,c=c)
def plot_man(r,t,c):
    plt.figure(figsize=(10,9))
    plt.axes().set_aspect('equal', 'datalim')
    
    # circle 1
    x = [np.sin(np.radians(_x))*r for _x in np.linspace(-180,180,721)]
    y = [np.cos(np.radians(_y))*r*t for _y in np.linspace(-180,180,721)]
    plt.plot(x, y, c)
    
    # circle 2
    x2 = [i*0.7 for i in x]
    y2 = [i*0.7 for i in y]
    plt.plot(x2, y2, c)
    
    # line
    x3 = [0,0]
    y3 = [min(y)*1.25,max(y)*1.25]
    plt.plot(x3, y3, c)
    
    # lines
    x4 = [i*1.2 for i in x]
    y4 = [i*1.2 for i in y]
    for i in range(16):
        x5 = [x[i*45],x4[i*45]]
        y5 = [y[i*45],y4[i*45]]
        plt.plot(x5, y5, c)
    
    plt.xlim([-20,20])
    plt.ylim([-20,20])
    plt.show()



結果はこうなります。

スクリーンショット 2019-07-28 15.47.34


rで円の半径を変更
tで楕円の倍率を変更
colorで色を変更です。

楕円は
円の縦横方向を n 倍にすることで実現しています。

なので
縦方向であれば
xの値はそのままに
yの値の倍率を変えてあげると
縦方向の楕円になります。

今回のやり方では
まず半径を決めて円を用意します。
内側の円は1つめの円の倍率を変えただけです。

縦棒はxの値が0でyの値を変化させることで
実現させ、外側の棒たちは

大きな円を用意し
1つめの円から外側の円に向かう値で
描いています。

一応16本になるみたいなので
22.5度の角度になるような計算で
座標を求めています。

全部足すと
安産マークになります!!!!!!!




せっかくなので
GIFも作ってみましょう


matplotlibではアニメーション機能で
mp4やgifも作れますが
環境によっては動かないこともあり

今回は
画像をたくさん生成して
無理くりGIFに落とし込みます。


ソースはこちら
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider, IntSlider,Select
import os

data_dir = 'anzan_data/'
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

for a in range(30):
    plt.figure(figsize=(3,3))
    plt.axes().set_aspect('equal', 'datalim')
    plt.tick_params(labelbottom=False,
                    labelleft=False,
                    labelright=False,
                    labeltop=False)
    plt.tick_params(bottom=False,
                    left=False,
                    right=False,
                    top=False)
    plt.xlim([-20,20])
    plt.ylim([-20,20])
    c ='black'
    n = (np.abs(np.sin(a))+1)
    # circle 1
    x = [np.sin(np.radians(_x))*8 for _x in np.linspace(-180,180,721)]
    y = [np.cos(np.radians(_y))*8*n for _y in np.linspace(-180,180,721)]
    plt.plot(x, y, c)

    # circle 2
    x2 = [i*0.7 for i in x]
    y2 = [i*0.7 for i in y]
    plt.plot(x2, y2, c)

    # line
    x3 = [0,0]
    y3 = [min(y)*1.25,max(y)*1.25]
    plt.plot(x3, y3, c)

    # lines
    x4 = [i*1.2 for i in x]
    y4 = [i*1.2 for i in y]
    for i in range(16):
        x5 = [x[i*45],x4[i*45]]
        y5 = [y[i*45],y4[i*45]]
        plt.plot(x5, y5, c)
    file_name = data_dir + 'tmp_{0:02}.png'.format(a)
    plt.savefig(file_name)

images = []
for a in range(30):
    file_name = data_dir + 'tmp_{0:02}.png'.format(a)
    img = Image.open(file_name)
    images.append(img)

gif_name = 'anzan.gif'
images[0].save(gif_name,save_all=True, append_images=images[1:], optimize=False, duration=2, loop=0)





結果はこうなります。


anzan
びろんびろん動くのが
気持ちいいですよねーーーー


はい
周りのに安産祈願の方がいたら
是非送ってあげましょう!!

きっと喜ばれること
間違いなし




今回はここまでです。

それでは


 

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

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

動画はこちら




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


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をいい感じの太さにしてくださいねーー

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

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

はーーい
クソですねー

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

それでは

このページのトップヘ