近所のスーパーの野菜価格を簡単に比較できるWEBサービスの開発

キャベツ・レタス・トマトなど、価格の変動が大きい野菜について、

近所のスーパー5店舗、オンラインスーパー3店舗、去年の平均価格、今後の価格の見通し、などを比較することができます

使用言語・技術

以下で説明しています。

github.com

外出目的・帰宅予定時刻を管理するWEBサービスの開発

概要

玄関に端末が設置してあります

  1. 名前を選択します

  2. 外出目的を選択します

  3. 帰宅予定時刻を選択します

  4. 結果がLINEグループに送信されます

使用言語・技術

項目 言語・技術
フロントエンド HTML, CSS
バックエンド Ruby on Rails
サーバ Heroku

実装の手順

  1. Ruby on Rails, HerokuでWebアプリを実装

    RailsのViewに複数のsubmitボタンを設置して押されたボタンの値を取得する - Qiita

  2. Ruby on Rails, LINEBot APIでボタン入力内容をLINEグループに送信

    【Rails】LINEBotでPush送信 - Qiita

ベイジアンネットで遅刻の原因推定

[やること]
ベイズの定理、条件付確率を使ったベイジアンネットで、遅刻の原因を推定します

[ベイジアンネットについて]
事象をノード、条件付確率をエッジにして、ネットワークを構築して、原因推定、意思決定モデルなどを行う手法です。
参考文献[1]がわかりやすいです。

[結果1]
条件付確率からベイジアンネットを構築しました
以下がそれぞれの事象の説明です
B:BeLate:遅刻する
O:OverSleep:寝坊する
Ti:Tired:昨日疲れてた
S:StayUpLate:昨日夜更かしした
Tw:TweetALot:昨日のツイート数が多い

f:id:wada0421514:20200329222528p:plainf:id:wada0421514:20200329222531p:plain

[結果2]
遅刻した時に、昨日夜更かしした確率を計算します
P(S=1|B=1)です

P(S=1|B=1) = P(S=1,B=1)/P(B=1)です。

まず、分母P(B=1)から計算します
P(B=1) = P(B=1|Ov=1)P(Ov=1)+P(B=1|Ov=0)P(Ov=0)

P(B=1)の計算には、P(Ov=1)の値が必要です。
P(Ov=1)=P(Ov=1|Ti=[1,0],S=[1,0])P(Ti=[1,0])P(S=[1,0])
=0.8*0.5*0.4+0.5*0.4*0.5+0.6*0.6*0.5+0.4*0.6*0.5
=0.16+0.1+0.18+0.12
=0.56

分母P(B=1)が求まります。
P(B=1)=0.9*0.56+0.1*0.44=0.548

分子P(S=1,B=1)を計算します
P(S=1,B=1)
=P(S=1,B=1,Ti=[1,0],Ov=[1,0])
=P(B=1|Ov=[1,0])P(S=1,Ti=[1,0],Ov=[1,0])
=P(B=1|Ov=[1,0])P(Ov=[1,0]|Ti=[1,0],S=1)P(Ti=[1,0])P(S=1)
=0.9*0.8*0.4*0.5+0.8*0.6*0.6*0.5+0.4*0.2*0.4*0.5+0.4*0.4*0.6*0.5
=0.352

遅刻した時に、昨日夜更かしした確率が求まります。64%です。
P(S=1|B=1)=0.352/0.548=0.64

[結果3]
まず、寝坊した時に、夜更かしした確率を計算します。
その後、「昨日のツイート数が多い」という事象が加わった場合を計算します
P(S=1|O=1)、P(S=1|O=1,Tw=1)です

P(S=1|Ov=1)=P(S=1,Ov=1)/P(Ov=1)です。

分母P(Ov=1)は結果2で求まっています。
P(Ov=1)=0.56

分子P(S=1,Ov=1)を計算します
P(S=1,Ov=1)=P(Ov=1|S=1,Ti=[1,0])P(S=1)P(Ti=[1,0])
=0.8*0.5*0.4+0.6*0.5*0.6
=0.16+0.18
=0.34

寝坊した時に、夜更かしした確率が求まりました。61%です。
P(S=1|Ov=1)=0.34/0.56
=0.61

次に、「昨日のツイート数が多い」という事象が加わった場合を計算します

P(S=1|Tw=1,Ov=1)=P(S=1,Tw=1,Ov=1)/P(Tw=1,Ov=1)です。

分母P(Tw=1,Ov=1)を計算します
P(Tw=1,Ov=1)
=P(Tw=1,Ov=1,Ti=[1,0],S=[1,0])
=P(Ov=1|Ti=[1,0],S=[1,0])P(Tw=1,Ti=[1,0],S=[1,0])
=P(Ov=1|Ti=[1,0],S=[1,0])P(Ti=[1,0])P(Tw=1|S=[1,0])P(S=[1,0])
=0.8*0.4*0.7*0.5+0.5*0.4*0.5*0.5+0.6*0.6*0.7*0.5+0.4*0.6*0.5*0.5
=0.348

分子P(S=1,Tw=1,Ov=1)を計算します
P(S=1,Tw=1,Ov=1)
=P(S=1,Tw=1,Ov=1,Ti=[1,0])
=P(Ov=1|Ti=[1,0],S=1)P(Ti=[1,0])P(Tw=1|S=1)P(S=1)
=0.8*0.4*0.7*0.5+0.6*0.6*0.7*0.5
=0.238

「昨日のツイート数が多い」という事象が加わった場合の、寝坊した時に、夜更かしした確率が求まりました。68%です。少し確率が上がっています。直感と一致する結果です。
P(S=1|Tw=1,Ov=1)=0.238/0.348
=0.68

[今後の展望]
ベイジアンネットによる計算を自動化したいです

[参考文献]
[1]ベイジアンネットワーク
https://www.sist.ac.jp/~kanakubo/research/reasoning_kr/bayesiannetwork.html

最適なラーメンハシゴルート

[やること]
駅とラーメンのお店をネットワークで表現して、一番満足度の高いラーメンハシゴルートを計算します。
知識グラフを扱う練習が目的です。

[結果1]
青ノードが駅、赤ノードがラーメンのお店です。実際にあるお店です。
赤ノードの大きさが、ラーメンのお店の個人的なスコアです
エッジの数字はラーメンの値段、電車賃などを表します
f:id:wada0421514:20200329201246p:plain

[結果2]
以下のプログラムで、以下の設定で、スコアが一番高いルートを探索しました
設定:東京駅スタート、ラーメン2店舗ハシゴが上限
結果:['TOKYO', 'Rokurinsha', 'SHINJUKU', 'Konjiki']、1980円

[プログラム]

import networkx as nx
import matplotlib.pyplot as plt
import itertools

class Noodle():

    def __init__(self):
        #ネットワーク読み込み
        self.G = nx.read_edgelist('nodelist.txt', nodetype=str)
        nx.set_node_attributes(self.G, name='score', values=200)
        #スコア
        self.G.nodes["Rokurinsha"]["score"]=850
        self.G.nodes["Konjiki"]["score"]=900
        self.G.nodes["Ozeki"]["score"]=800
        self.G.nodes["TOKYO"]["score"]=0
        self.G.nodes["SHINJUKU"]["score"]=0
        self.G.nodes["SHIBUYA"]["score"]=0
        #コスト
        self.edge_labels={}
        self.edge_labels[("SHINJUKU","TOKYO")]=200
        self.edge_labels[("TOKYO","SHINJUKU")]=200
        self.edge_labels[("SHIBUYA","TOKYO")]=200
        self.edge_labels[("TOKYO","SHIBUYA")]=200
        self.edge_labels[("SHIBUYA","SHINJUKU")]=160
        self.edge_labels[("SHINJUKU","SHIBUYA")]=160
        self.edge_labels[("TOKYO","Rokurinsha")]=830
        self.edge_labels[("TOKYO","Hyottoko")]=670
        self.edge_labels[("TOKYO","Oboroduki")]=850
        self.edge_labels[("TOKYO","Hachigo")]=850
        self.edge_labels[("SHINJUKU","Gonokami")]=800
        self.edge_labels[("SHINJUKU","Funji")]=700
        self.edge_labels[("SHINJUKU","Konjiki")]=950
        self.edge_labels[("SHINJUKU","Sho")]=700
        self.edge_labels[("SHINJUKU","Hayashida")]=800
        self.edge_labels[("SHIBUYA","Kiraku")]=700
        self.edge_labels[("SHIBUYA","Hayashi")]=800
        self.edge_labels[("SHIBUYA","Ozeki")]=830

    #ネットワークをプロットする
    def plot(self):
        #色、サイズなど
        pos = nx.spring_layout(self.G, k=0.8)
        nx.draw_networkx_edges(self.G, pos, edge_color='y')
        c = ['blue' if n.isupper() else 'red' for n in self.G.nodes()]
        score_size = [1000 if n.isupper() else self.G.nodes[n]["score"] for n in self.G.nodes()]
        nx.draw_networkx_nodes(self.G, pos, node_color=c, alpha=0.5, node_size=score_size)
        nx.draw_networkx_edge_labels(self.G, pos, edge_labels=self.edge_labels, font_size=24)
        nx.draw_networkx_labels(self.G, pos, font_size=24)
        plt.figure(figsize=(10, 8))
        plt.axis('off')
        plt.show()

    #ルートのコスト計算
    def costscore_cal(self,route):
        score = 0
        cost = 0
        for a in range(len(route) - 1):
            if route[a] == route[a + 1]:
                continue
            else:
                if not (route[a].isupper()) and route[a + 1].isupper():
                    continue
                else:
                    cost = cost + self.edge_labels[(route[a], route[a + 1])]
                    score = score + self.G.nodes[route[a + 1]]["score"]
        return score, cost

    #店の一番近い駅
    def store2station(self,storename):
        station = {'Rokurinsha': 'TOKYO', 'Hyottoko': 'TOKYO', 'Oboroduki': 'TOKYO', 'Hachigo': 'TOKYO',
                   'Gonokami': 'SHINJUKU', 'Funji': 'SHINJUKU', 'Konjiki': 'SHINJUKU', 'Sho': 'SHINJUKU',
                   'Hayashida': 'SHINJUKU',
                   'Kiraku': 'SHIBUYA', 'Hayashi': 'SHIBUYA', 'Ozeki': 'SHIBUYA'}
        return station[storename]

    #全ルート生成
    def allroutes(self, noodle_count):
        noodle_shop = ('Rokurinsha', 'Hyottoko', 'Oboroduki', 'Hachigo',
                       'Gonokami', 'Funji', 'Konjiki', 'Sho', 'Hayashida',
                       'Kiraku', 'Hayashi', 'Ozeki')
        all_routes = ()
        for a in range(noodle_count):
            all_routes = all_routes + tuple(itertools.permutations(noodle_shop, a + 1))

        return all_routes

    #一番スコアが高いルート探索
    def allroutes_costscore_cal(self,allroutes, start_station):
        result_route = []
        for noodle_route in allroutes:
            list_kari = []
            for a in noodle_route:
                list_kari.append(start_station)
                list_kari.append(self.store2station(a))
                list_kari.append(a)
                list_kari.append(self.store2station(a))
            result_route.append(list_kari)

        result_score = []
        result_cost = []

        for a in result_route:
            score, cost = self.costscore_cal(a)
            result_score.append(score)
            result_cost.append(cost)

        result_route=result_route[result_score.index(max(result_score))]
        result_cost=result_cost[result_score.index(max(result_score))]

        return list(dict.fromkeys(result_route)),result_cost

if __name__=='__main__':

    n=Noodle()
    #2店舗はしごが限界
    allroutes=n.allroutes(2)
    #東京駅スタート
    result_route,cost=n.allroutes_costscore_cal(allroutes,"TOKYO")
    #結果:['TOKYO', 'Rokurinsha', 'SHINJUKU', 'Konjiki']
    print(result_route)
    #結果:1980円
    print(str(cost)+"円")
    n.plot()

[参考文献]
[1]Pythonでネットワークを分析・可視化しよう!必要手順まとめ
https://www.sejuku.net/blog/91371

[2]【Python】NetworkX 2.0の基礎的な使い方まとめ
https://qiita.com/kzm4269/items/081ff2fdb8a6b0a6112f

カオスの軌跡のプロット

[やること]
微分方程式で表現されるカオスをPythonで解いて、軌跡をプロットします。

[カオスについて]
カオスは式で明確に定義されているのに、初期値の選び方で将来の状態が予測できない現象のことです。
参考文献[1][2]がわかりやすいです

[結果1]
ローレンツ方程式

f:id:wada0421514:20200329140013p:plain

p,r,b=10,28,8/3

初期値 x,y,z=1,1,1
f:id:wada0421514:20200329135713p:plain
初期値 x,y,z=1.0001,1,1
f:id:wada0421514:20200329135717p:plain
初期値 x,y,z=1.001,1,1
f:id:wada0421514:20200329135721p:plain
初期値 x,y,z=1.01,1,1
f:id:wada0421514:20200329135727p:plain

横軸に時間、縦軸にxを取ったプロットです。
初期値が少し変わるだけで軌跡が大きく変わることがわかります。

f:id:wada0421514:20200329140511p:plain


[結果2]
レスラー方程式
f:id:wada0421514:20200329140830g:plain

a,b,c=0.2,1.0,5.7

初期値 x,y,z=1.0,0.0,0.0
f:id:wada0421514:20200329140958p:plain
初期値 x,y,z=8.01,0.0,0.0
f:id:wada0421514:20200329141003p:plain
初期値 x,y,z=8.02,0.0,0.0
f:id:wada0421514:20200329141007p:plain

[プログラム]
微分方程式を解くライブラリodeintについては、参考文献[3]がわかりやすいです

import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#レスラー方程式
def rossler(var,t,a,b,c):
    x_next=-var[1]-var[2]
    y_next=var[0]+a*var[1]
    z_next=b*var[0]-(c-var[0])*var[2]
    return np.array([x_next,y_next,z_next])

#ローレンツ方程式
def lorentz(var,t,p,r,b):
    x_next=-p*var[0]+p*var[1]
    y_next=-var[0]*var[2]+r*var[0]-var[1]
    z_next=var[0]*var[1]-b*var[2]
    return np.array([x_next,y_next,z_next])

if __name__ == "__main__":

    #レスラー方程式のパラメータと初期値
    #abc=(0.2,1.0,5.7)
    #xyz_first=[1.0,0.0,0.0]

    #ローレンツ方程式のパラメータと初期値
    prb=(10,28,8/3)
    xyz_first=[1.01,1,1]

    #ステップ数
    t=np.linspace(0,1000,100000)

    #微分方程式を解く
    #var=odeint(chaos,xyz_first,t,args=abc)
    var=odeint(lorentz,xyz_first,t,args=prb)

    #グラフの枠を作っていく
    fig = plt.figure()
    ax = Axes3D(fig)

    #軸にラベルを付ける
    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.set_zlabel("Z")

    #プロットして表示
    ax.plot(var[:, 0], var[:, 1], var[:, 2], marker="o", linestyle='None')
    plt.show()


[参考文献]
[1]カオスアトラクタとは
http://www.gifu-nct.ac.jp/elec/deguchi/sotsuron/hisaki/node9.html#eqresura
[2]カオス
http://www.aoni.waseda.jp/yuuka/Sim/Chaos.html
[3]Python運動方程式を解く(odeint)
https://qiita.com/binaryneutronstar/items/ad5efa27fd626826846f

自己組織化マップで色マップ画像作成

[やること]

自己組織化マップ(Self-Organizing Maps)で、RGBで表現された色データを、教師なし学習して、色マップを作成します。

 

[自己組織化マップの説明]

教師なし学習の手法です。

入力データに関して、近いデータを近くに配置するマップを作成します。

参考資料[1]がわかりやすいです

  

[結果1]

黒、緑、黄、赤、紫、白、水、青の8色を学習させた結果です。

青と水、赤と紫、など、近い色が近くに配置されています。

f:id:wada0421514:20200329114954p:plain

 

[結果2]

以下の12色相環の12色を学習させた結果です。

12色相環を再現できています。

f:id:wada0421514:20200329115153p:plain

f:id:wada0421514:20200329115156p:plain

 

[プログラムについて]

参考資料[2]をかなり参考にしました

import numpy as np
import cv2

class SOM:
def __init__(self, n_side, n_learn=1000, c=0.5):
#n_side:マップのサイズ,n_learn:学習回数,c:更新率
self.n_side = n_side
self.n_learn = n_learn
self.c = c
self.n_weight = self.n_side * self.n_side

def fit(self, input_vector):

input_vector = np.array(input_vector)
n_input = len(input_vector)
n_vector = input_vector.shape[1]

# pointsにはそれぞれの重みの(x,y)座標が入っている(範囲は[0,1))
points = np.array([[i // self.n_side, i % self.n_side] for i in range(self.n_weight)])
points = points / (1.0 * self.n_side)

# 重みベクトルの初期化
self.weight = np.zeros((self.n_weight, n_vector))

# ランダムなインデックス
random_index = np.arange(n_input)
np.random.shuffle(random_index)

for t in range(self.n_learn):

# ランダムに一つ抽出
vec = input_vector[random_index[t % n_input]]

# 勝ちニューロン決定
winner_index = np.argmin(np.linalg.norm(diff, axis=1))
winner_point = points[winner_index]

# 近傍関数の計算
alpha = 1.0 - float(t) / self.n_learn#α=1-t/T:学習回数に応じて単調に減少
delta_point = points - winner_point
dist = np.linalg.norm(delta_point, axis=1)#勝ちニューロンとの距離
h = self.c * alpha * np.exp(- (dist / alpha) ** 2)

# 重みを更新
diff = vec - self.weight# 入力ベクトルと重みの差
self.weight += np.atleast_2d(h).T * diff


if __name__ == "__main__":

    #8色のRGB
    input_vector=[[255,255,255],[255,255,0],[255,0,255],[0,255,255],
                  [0,0,255],[0,255,0],[255,0,0],[0,0,0]]

    #12色相環のRGB
    color_wheel=[[255, 0, 0],[255, 127, 0],[255, 255, 0],[127, 255, 0],
                 [0, 255, 0],[0, 255, 127],[0, 255, 255],[0, 127, 255],
                 [0, 0, 255],[127, 0, 255],[255, 0, 255],[255, 0, 127]]

    # SOMクラスの作成・学習
    n_side = 500 # 一辺の長さ
    som = SOM(n_side, n_learn=1000,c=0.75)
    som.fit(color_wheel)

    # 重みベクトルの取得
    output_imgs = som.weight

    # 重みベクトルを並べて、画像作成
    output_imgs = output_imgs.reshape(n_side, n_side, 3)
    tile = np.zeros((n_side, n_side,3))
    for x in range(n_side):
    for y in range(n_side):
    tile[(x):(x+1), (y):(y+1)] = output_imgs[x, y]

    # 画像の保存
    print(tile)
    cv2.imwrite("tile.png", tile)

  

[参考資料]

[1]自己組織化特徴マップ(SOM)

http://www.sist.ac.jp/~kanakubo/research/neuro/selforganizingmap.html

[2]MNISTの自己組織化マップ(SOM)を作った[Python]-もりとーにのブログ

https://tony-mooori.blogspot.com/2016/01/mnistpython.html

山手線を徒歩で一周した

山手線を徒歩で一周しました。

新宿がスタート、ゴールで外回りで歩きました。

約40キロ、8時間21分かかりました。

f:id:wada0421514:20191120194455p:plain

1. 新宿駅:スタート

大きな駅。なんでもある。雑多。

f:id:wada0421514:20191120194950j:plain

2. 新大久保駅

韓国料理のお店がたくさんある。

新大久保ー高田馬場:きれいなマンション。落ち着いた感じ。

f:id:wada0421514:20191120195930j:plain

3. 高田馬場駅

豚カツ。全体的に低めの建物。とん太。

高田馬場ー目白:住宅街。

f:id:wada0421514:20191120195927j:plain

4. 目白駅

駅前オシャレ。住宅街。住みやすそう。落ち着いた感じ。ケーキ。

f:id:wada0421514:20191120195924j:plain

5. 池袋駅

大きな駅。雑多。なんでもある。飲み屋街。SEIBUビル。

池袋ー大塚:キレイめ。タワマンがあった。

 f:id:wada0421514:20191120195922j:plain

6. 大塚駅

路面電車。落ち着いた感じ。緑多め。カッチャルバッチャル。

f:id:wada0421514:20191120195918j:plain

7. 巣鴨

地味な感じ。

f:id:wada0421514:20191120195914j:plain

8. 駒込駅

地味な感じ。

f:id:wada0421514:20191120195912j:plain

9. 田端駅

地味な感じ。

f:id:wada0421514:20191120195910j:plain

10. 西日暮里駅

神田っぽい。栄えている。

f:id:wada0421514:20191120195907j:plain

11. 日暮里駅

駅前がオシャレ。タワマンがある。

f:id:wada0421514:20191120195902j:plain

12. 鶯谷

駅前にラブホたくさんある。鶯谷苑。

f:id:wada0421514:20191120195859j:plain

13. 上野駅

上野動物園。公園。不忍池。博物館。

f:id:wada0421514:20191120195856j:plain

14. 御徒町駅

神田っぽい。

f:id:wada0421514:20191120195853j:plain

15. 秋葉原駅

大きい駅。オタク。なんでもある。

f:id:wada0421514:20191120195850j:plain

16. 神田駅

線路下に店色々。飲み屋色々。低めの建物多い。

f:id:wada0421514:20191120195848j:plain

17. 東京駅

大きな駅。駅前かっこいい。なんでもある。皇居。

中間地点。ちょっと休憩した。

f:id:wada0421514:20191120195844j:plain

18. 有楽町駅

東京駅の続き見たいな感じ。

f:id:wada0421514:20191120195841j:plain

19. 新橋駅

大きな駅。なんでもある感じ。

この辺りで、脚を痛めた。ラスト10kmくらい、かなりしんどかった。

f:id:wada0421514:20191120195838j:plain

20. 浜松町駅

品川っぽい。

f:id:wada0421514:20191120195836j:plain

21. 田町駅

品川っぽい。大きな駅。

f:id:wada0421514:20191120195833j:plain

22. 品川駅

大きな駅。なんでもある。結構きれい。

f:id:wada0421514:20191120195831j:plain

23. 大崎駅

オシャレ。新しい。キレイ。

f:id:wada0421514:20191120195828j:plain

24. 五反田駅

雑多な感じ。品川っぽい。

f:id:wada0421514:20191120195826j:plain

25. 目黒駅

色々ある感じ。ちょっとオシャレ。

f:id:wada0421514:20191120195823j:plain

26. 恵比寿駅

オシャレ。色々ある。蕃 YORONIKU。

f:id:wada0421514:20191120195821j:plain

27. 渋谷駅

大きな駅。なんでもある。若い。人多い。

f:id:wada0421514:20191120195818j:plain

28. 原宿駅

竹下通り。若い。人多い。

f:id:wada0421514:20191120195816j:plain

29. 代々木駅

新宿駅の続きっぽい。

f:id:wada0421514:20191120195814j:plain

30. 新宿駅:ゴール

f:id:wada0421514:20191120195811j:plain