プロクラシスト

今日の寄り道 明日の近道

【Day-14】株価や仮想通貨で使える、5つのテクニカル分析を解説&Pythonで実装してみた

f:id:imslotter:20171214165640p:plain

データ分析ガチ勉強アドベントカレンダー 14日目。

時系列データでまず思いつくのは、株価のチャートですよね。 また、最近はやっている仮想通貨。私も最近coincheckに入金しました。

やっぱ、実際にお金が絡むとちゃんと勉強しようって言う気になる!笑

せっかくチャートを見るわけだし、その見方について勉強しておこうと思いました。 そしてせっかくなので、自分で実装してどういう仕組みなのかまで知っておこうと思いました。 理系だからね、分からないものを使うのは嫌だからね。

というわけで、Python(主にPandasとMatplotlibを用いながら)でテクニカル指標についてやっていきます。扱うデータは三年分の日経平均株価

指標について知りたい人も、自分で実装してみたいという人もどうぞ。

テクニカル分析ファンダメンタル分析

大きく相場にはテクニカル分析ファンダメンタル分析の2つの分析手段がある。

  • ファンダメンタル分析 : 情報を集め、価格がどうなるかを判断する。株で言えば会社の情報とか、仮想通貨で言えば技術的な要素とか方針とかそういうのかな?
  • テクニカル分析 : チャート情報から、価格がどうなるかを判断する。チャートだけ見れば良いが、大変動などの予測は難しい。

一般的に、テクニカルのほうがとっつきやすいけど、判断を誤ることも多いと言われている。

トレーダーが良く使うテクニカル指標より、主要なテクニカル分析の指標を並べてみた。

順位 テクニカル指標 割合
1位 移動平均線 36.6%
2位 ボリンジャーバンド 17.1%
3位 MACD 13.2%
4位 RSI 8.4%
5位 一目均衡表 8.4%
6位 ストキャスティクス 7.1%
7位 フィボナッチ 2.9%
8位 その他 6.3%

これの、1位から5位までの指標について簡単に説明&Python実装する。

実装において

実装を見る方は、まず下記でデータを読み込んで置いてください。また、自分の好きなデータでもOKです。

いつものごとく、githubのノートブックに上げています。(day14)

github.com

コードをみる
# データの読み込み
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
def str2time(string):
    return datetime.strptime(string, '%Y/%m/%d')
df_orig = pd.read_csv("stock_nikkei_3yrs.csv")
df_orig.index = df_orig.date.apply(str2time)
df_orig = df_orig.sort_index(ascending=True)

ローソク足

説明

チャートの代表。一つ一つのローソクを見ると、ひと目でその期間の動きが分かるようになっている。

  • 始値・高値・安値・終値をまとめて表現
  • 終わりのほうが始まりより高いと青色 。逆だと赤色
  • 上下に伸びた線はその期間の最高値と最低値。ヒゲという
  • https://www.fxbroadnet.com/techs/rousokuahi.gif
    テクニカルチャート講座:ローソク足より引用

    実装

    コードをみる
    import matplotlib.finance as mpf
    from matplotlib.dates import date2num
    df = df_orig[df_orig.index >= datetime(2017,7,1)]
    df_ohlc = pd.DataFrame(df[["open","high","low","close"]])
    xdate = [x.date() for x in df_ohlc.index] #Timestamp -> datetime
    ohlc = np.vstack((date2num(xdate), df_ohlc.values.T)).T #datetime -> float
    
    fig = plt.figure(figsize=(15,5))
    ax = plt.subplot()
    mpf.candlestick_ohlc(ax, ohlc, width=0.7, colorup='b', colordown='r')
    ax.grid() #グリッド表示
    ax.set_xlim(xdate[-60], xdate[-1]) #x軸の範囲
    fig.autofmt_xdate() #x軸のオートフォーマット
    

    1位 : 移動平均線

    説明

    そのままのチャートはノイズ等も多く、価格が乱高下するので、その日からある期間分の平均を取るという操作を良くする。英語で言うとMoving Averageで、25MAだと25日分の移動平均線ということになる。平均を取ると、ノイズが無くなり、そのチャートのトレンドが明確に現れる。

    また、2つの移動平均線を組み合わせることも多く、各線の交差するところをゴールデンクロス/デッドクロスと呼ぶ。

  • ゴールデンクロス : 株価が下落した後に短期移動平均線が、長期移動平均線下から上に抜ける現象。買いのサイン
  • デッドクロス : 株価が上昇した後に短期移動平均線が、長期移動平均線上から下に抜ける現象。売りのサイン
  • (わかる株式用語より)
    • 日足の場合、短期が25日、長期が75日
    • 週足の場合、短期が13週、長期が26週

    が基本らしい。

    実装

    コードをみる
    df1 = df_orig.sort_index()
    df1["ma25"] = df1.close.rolling(window=25).mean()
    df1["ma75"] = df1.close.rolling(window=75).mean()
    df1["diff"] = df1.ma25-df1.ma75
    # df1 = df.date.apply(datetime.timestamp())
    df1["unixtime"] = [datetime.timestamp(t) for t in df_orig.index]
    plt.figure(figsize=(15,5))
    
    
    xdate = [x.date() for x in df1.index]
    # line and Moving Average
    plt.plot(xdate, df1.close,label="original")
    plt.plot(xdate, df1.ma75,label="75days")
    plt.plot(xdate, df1.ma25,label="25days")
    plt.xlim(xdate[0],xdate[-1])
    plt.grid()
    
    #Cross
    for i in range(1, len(df1)):
        if df1.iloc[i-1]["diff"] < 0 and df1.iloc[i]["diff"] > 0:
            print("{}:GOLDEN CROSS".format(df1.iloc[i]["date"]))
            plt.scatter(df1.iloc[i]["date"],df1.iloc[i]["ma25"],marker="o",s=100,color="b")
            plt.scatter(df1.iloc[i]["date"],df1.iloc[i]["close"],marker="o",s=50,color="b",alpha=0.5)
    
        if df1.iloc[i-1]["diff"] > 0 and df1.iloc[i]["diff"] < 0:
            print("{}:DEAD CROSS".format(df1.iloc[i]["date"]))
            plt.scatter(df1.iloc[i]["date"],df1.iloc[i]["ma25"],marker="o",s=100,color="r")
            plt.scatter(df1.iloc[i]["date"],df1.iloc[i]["close"],marker="o",s=50,color="r",alpha=0.5)
    plt.legend()
    

    2位 : ボリンジャーバンド

    説明

    移動平均線移動標準偏差を用いて分析を行う。

    {1\sigma, 2\sigma, 3\sigma}線を移動平均に引く

    • {1\sigma} : 68%の確率で株価がそこに収まる
    • {2\sigma} : 95%の確率で株価がそこに収まる
    • {3\sigma} : 99%の確率で株価がそこに収まる

    均衡状態の相場での正規分布を仮定しているので、 ここから外れた = 釣り合いが破れているということ。

    不安定だが、売買のチャンスともいえる。

    株の売買サインを簡単に見極めるボリンジャーバンドの使い方によると

  • バンドが狭いときは様子見(スクイーズ状態)
  • バンドが上下に開いているときはトレンドに入り気味。上昇のときはチャンス
  • +2σのラインを価格がぶち破ればめちゃくちゃ強いトレンド
  • 実装

    コードをみる
    def Bollinger(df,window=25):
        df1 = df.copy()
        df1["ma"] = df1.close.rolling(window=window).mean()
        df1["sigma"] =  df1.close.rolling(window=window).std()
        df1["ma+2sigma"] = df1.ma + 2*df1.sigma
        df1["ma-2sigma"] = df1.ma - 2*df1.sigma
        df1["diffplus"] = df1.close - df1["ma+2sigma"] 
        df1["diffminus"] = df1["ma-2sigma"] - df1.close
        s_up = df1[df1["diffplus"]>0]["close"]
        s_down = df1[df1["diffminus"]>0]["close"]
    
        plt.figure(figsize=(15,5))
        plt.grid()
        plt.scatter(s_up.index, s_up.values,marker="x",s=100,color="blue")
        plt.scatter(s_down.index, s_down.values,marker="x",s=100,color="red")
        xdate = [x.date() for x in df1.index]
        plt.plot(xdate, df1.close.values,label="close",color="b",alpha=0.8)
        plt.plot(xdate,df1.ma.values,label="{}ma".format(window))
        plt.fill_between(xdate, df1.ma-df1.sigma,df1.ma+df1.sigma,color="red", alpha=0.7, label="$1\sigma$")
        plt.fill_between(xdate, df1.ma-2*df1.sigma,df1.ma+2*df1.sigma,color="red", alpha=0.3, label="$2\sigma$")
    #     plt.fill_between(xdate, df1.ma-3*df1.sigma,df1.ma+3*df1.sigma,color="red", alpha=0.3, label="$3\sigma$")    
        plt.legend()
        plt.xlim(xdate[0],xdate[-1])
    Bollinger(df_orig,window=25)
    

    3位 : MACD(Moving Average Convergence/Divergence)

    説明

    移動平均線を少し改良している。時系列は直近のデータのほうが昔のデータよりも価値が高い傾向にあるので、そういう平均のとり方をして、移動平均線を引いている(指数平滑移動平均線(EMA)という)。下記の2本の移動平均線を見ることで、そのトレンドを把握する。

    • MACD : 12日EMA - 26日EMA
    • シグナル : MACDの9日EMA
  • シグナルをMACD下から上へクロス。買いのサイン
  • シグナルをMACD上から下へクロス。売りのサイン
  • 実装

    コードをみる
    df_orig.head()
    df_orig["MACD"] = df_orig.close.ewm(span=12, min_periods=1).mean() - df_orig.close.ewm(span=26, min_periods=1).mean()
    df_orig["signal"] = df_orig.MACD.ewm(span=9, min_periods=1).mean()
    df_orig["macd_diff"] = df_orig["MACD"]-df_orig["signal"]
    xdate = [x.date() for x in df_orig.index]
    
    plt.figure(figsize=(15,10))
    plt.subplot(211)
    plt.plot(xdate, df_orig.close,label="original")
    plt.xlim(xdate[0],xdate[-1])
    plt.legend()
    plt.grid()
    plt.subplot(212)
    plt.title("MACD")
    plt.plot(xdate, df_orig.MACD,label="MACD")
    plt.plot(xdate, df_orig.signal,label="signal")
    plt.xlim(xdate[0],xdate[-1])
    plt.legend()
    plt.grid(True)
    #Cross
    for i in range(1, len(df_orig)):
        if df_orig.iloc[i-1]["macd_diff"] < 0 and df_orig.iloc[i]["macd_diff"] > 0:
            print("{}:GOLDEN CROSS".format(df_orig.iloc[i]["date"]))
            plt.scatter(xdate[i],df_orig.iloc[i]["MACD"],marker="o",s=100,color="b")
    
        if df_orig.iloc[i-1]["macd_diff"] > 0 and df_orig.iloc[i]["macd_diff"] < 0:
            print("{}:DEAD CROSS".format(df_orig.iloc[i]["date"]))
            plt.scatter(xdate[i],df_orig.iloc[i]["MACD"],marker="o",s=100,color="r")
    

    4位 : RSI (Relative Strength Index)

    買われすぎ売られすぎな相場では、投資家が不安になってくる。その心理状況を定量化しようとした指標。 値上がりと値下がりのバランスを見ている。

  • RSIの数値が70%以上になると買われ過ぎゾーン
  • RSIの数値が30%以下になると売られ過ぎゾーン
  • それぞれのゾーンに入った後に反転した動きになったところで買われ過ぎゾーンの時は“売り”を、売られ過ぎゾーンの時は“買い”を考える。
  • カブドットコム証券より

    実装

    コードをみる
    def plot_RSI(df,window):
        diff = df_orig.close.diff(periods=1).values
        xdate = [x.date() for x in df_orig.index]
        RSI = []
        for i in range(window+1,len(xdate)):
            neg = 0
            pos = 0
            for value in diff[i-window:i+1]:
                if value > 0:
                    pos += value
                if value < 0:
                    neg += value
            pos_ave = pos/window
            neg_ave = np.abs(neg/window)
            rsi = pos_ave/(pos_ave+neg_ave)*100
            RSI.append(rsi)
        #RSIのグラフ描画
        plt.plot(xdate[window+1:], RSI,label="RSI {}".format(window),lw=2.5,alpha=0.6)
        plt.xlim(xdate[window+1],xdate[-1])
        plt.ylim(0,100)
        plt.legend()
    xdate = [x.date() for x in df_orig.index]
    plt.figure(figsize=(15,10))
    plt.subplot(211)
    plt.plot(xdate, df_orig.close,label="original")
    plt.xlim(xdate[0],xdate[-1])
    plt.legend()
    plt.grid()
    plt.subplot(212)
    plt.grid()
    plt.title("RSI")
    plot_RSI(df_orig,window=14)
    plot_RSI(df_orig,window=28)
    plot_RSI(df_orig,window=105)
    plt.fill_between(xdate,np.ones(len(xdate))*30,color="blue",alpha=0.2)
    plt.fill_between(xdate,np.ones(len(xdate))*70,np.ones(len(xdate))*100,color="red",alpha=0.2)
    plt.plot(xdate,np.ones(len(xdate))*30,color="blue",linestyle="dotted")
    plt.plot(xdate,np.ones(len(xdate))*70,color="red",linestyle="dotted")
    plt.show()
    

    5位 : 一目均衡表

    説明

    これもローソク足と並ぶ、国産のテクニカル分析方法

    性質
    転換線(Conversion Line) (9日間の高値+9日間の安値)÷2
    基準線(Base Line) (26日間の高値+26日間の安値)÷2
    先行スパン1(Leading Span 1) (転換線+基準線)÷2を26日先にプロット
    先行スパン2(Leading Span 2 (52日間の高値+52日間の安値)÷2を26日先にプロット
    遅行スパン(Lagging Span) 当日の終値を26日前にプロット
  • 転換線と基準線の交差ポイントは、トレンドの傾向。転換線が下から上好転上から下逆転
  • 先行スパン1と先行スパン2の間をという。株価が雲よりも上にいるときは上昇基調、株価が雲より下にいるときは下落基調
  • 実装

    コードをみる
    df_orig = pd.read_csv("stock_nikkei_3yrs.csv")
    df_orig.index = df_orig.date.apply(str2time)
    df_orig = df_orig.sort_index(ascending=True)
    
    max_9 = df_orig.high.rolling(window=9).max()
    min_9 = df_orig.high.rolling(window=9).min()
    df_orig["tenkan"] = (max_9+min_9)/2
    df_orig["base"] = (df_orig.high.rolling(window=26).max()+df_orig.high.rolling(window=26).min())/2
    xdate = [x.date() for x in df_orig.index]
    
    plt.figure(figsize=(15,5))    
    plt.grid()
    plt.plot(xdate, df_orig.close,color="b",lw=1,linestyle="dotted",label="Close")
    plt.plot(xdate, df_orig.tenkan,label="Conversion line")
    plt.plot(xdate, df_orig.base,label="Base line")
    senkou1 = ((df_orig.tenkan+df_orig.base)/2).iloc[:-26]
    senkou2 = ((df_orig.high.rolling(window=52).max()+df_orig.high.rolling(window=52).min())/2).iloc[:-26]
    plt.fill_between(xdate[26:], senkou1, senkou2, color="blue",alpha=0.2,label="Cloud")
    plt.legend(loc=4)
    
    plt.xlim(xdate[0],xdate[-1])
    

    まとめ

    けっこういろいろな指標が考えられているのですね。 株価だけじゃなくて、他の時系列データへのヒントにもならないかと思い、調べましたが、とてもいい勉強になりました。

    ちょっと一つ移動平均線で試したいことがあるので、明日はそれをしようかなと思います。テーマは『ベイズ最適化』 それでは明日もお楽しみに!

    【Day-13】『Prophet入門』簡単に高精度を実現するFacebook謹製の時系列予測ライブラリ

    f:id:imslotter:20171213111429p:plain

    データ分析ガチ勉強アドベントカレンダー 13日目。

    仮想通貨がはやり始めて、チャートを見るようになった人も多いのではないでしょうか?

    チャートから予測をしたい

    という思いを持ちつつも、結構ハードルの高いのが時系列予測。 それをできるだけ簡単にできるツールがProphet

    自分の持っているドメイン知識を導入しながら、簡単に時系列データ予測を行うことができます。

    prophetとは

    資料

    • Completely automatic forecasting techniques can be brittle and they are often too inflexible to incorporate useful assumptions or heuristics.
    • Analysts who can produce high quality forecasts are quite rare because forecasting is a specialized data science skill requiring substantial experience.
    • 完全な予測は柔軟さに欠ける
    • 時系列予測は特別なスキルがいる

    から、良い予測が全然スケールしない。 なので、簡潔な記述必要な柔軟性を残し作ったのがProphet

    設計思想

    公式の設計思想がこちら

    • 簡潔な記述 : 正しい形で入力をすると、デフォルトで良い予測ができるようになっている
    • 柔軟性 : ユーザーの持つドメイン知識をいれることで、精度向上を測れる

    必要のないものは裏でやっちゃって、必要な部分だけ自由度を残すという感じですね。

    Prophetでできないこと

    これだけ言えば超絶便利だと思われるが、もちろんできないこともある。

    (認識間違っていたら教えてください。)

    • いろいろなアルゴリズムを試して比べるようなものではない*2
    • 予測のためのツール。時系列クラスタリングなどはできない。
    • 他変数を用いた予測(issueにあげられているので、そのうち対応されるかも。されてほしい)

    根底のアルゴリズム

    additive regression modelを利用している。具体的には下記のアルゴリズムを組み合わせている。

    • トレンドをlinearかlogistic曲線で近似する。また、Prophetではトレンドの変化点も自動で検知するようになっている
    • Fourier級数によって、年周期を抽出している
    • ダミー変数をもちいて週周期を抽出している
    • 重要な休日やイベントのリストをわれわれユーザー側で与えられる(ドメイン知識)

    インストール

    切り分けのため、別々にインストールするといいかも。*3

    # numpyとmatplotlibを最新版にしておくといい
    pip install -U numpy matplotlib
    # mcmc等サンプリング用ライブラリ
    pip install pystan
    # fbprophet
    pip install fbprophetc

    Prophet tutorial

    型の変換

    • pythonAPIでは、sklearnのモデルを踏襲しているため、fitpredictで学習と予測を行うことができる。
    • inputは必ず下記の型で行う
    変数 用途
    ds datetime 時系列情報(タイムスタンプ)
    y 数値 そのときの値
    %matplotlib inline
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    from datetime import datetime
    from fbprophet import Prophet
    df = pd.read_csv("AirportPassengers.csv",delimiter=";").dropna()
    def str2time(string):
        #datetimeに変換
        return datetime.strptime(string, '%Y-%m')
    # 読み込んだデータをdsとyに変える
    df["ds"] = df.Month.apply(str2time)
    df = df.drop(["Month"],axis=1)
    df["y"] = np.log(df.Passengers) #logをとる
    df.head()
    

    出力。dsyの形で持っている

       Passengers         ds         y
    0       112.0 1949-01-01  4.718499
    1       118.0 1949-02-01  4.770685
    2       132.0 1949-03-01  4.882802
    3       129.0 1949-04-01  4.859812
    4       121.0 1949-05-01  4.795791

    将来の予測

    下記の手順で、めちゃくちゃ簡単に予測までできる。面倒なパラメータ調整なんていらない。めちゃくちゃ簡単。

    • m = Prophet()で初期化(ドメイン知識はここで入れる!)
    • m.fit(df)で学習
    • make_future_dataframeで、dsのデータフレームを追加
      • freq : 予測する間隔(freq="M"ならMonthly。詳しくは公式 Sub-daily data )
      • periods : 何個dataframeを作るか
    • m.predct(future)で予測 : 出力はdataframe型
    • m.plot()でかなりオシャレなプロット

    今回は、下記要領で予測する

    • 1949年1月から1960年12年までのデータを入力にする
    • 1961年1月から5年間を予測する
    m = Prophet()
    # prophetに入れるには、dsとyのペアが必要
    m.fit(df)
    future = m.make_future_dataframe(periods=60,freq="M")
    forecast = m.predict(future)
    forecast[["ds","trend","yhat"]].tail()
    m.plot(forecast);
    plt.legend()
    

    出力がコレ

    かなりきれいな予測が出来ていそう。また、予測の誤差見積もりまでplotされている。色使いがFacebookっぽくてこれまたオシャレ感がある。

    Components

    アルゴリズムで紹介したように、トレンドや周期性についての分析結果は、m.plot_components()にて見ることができる。

    m.plot_components(forecast)
    

    特別なイベントなど

    ドメイン知識を入力し、精度向上を図ることができる。↓が参考になる。

    https://facebook.github.io/prophet/docs/seasonality_and_holiday_effects.htmlfacebook.github.io

    help

    その他使い方はhelp(Prophet)と打つ。

    まとめ

    一次元のデータを予測するなら、本当に簡単に分析が行えるので、オススメ。 株価でも突っ込んで分析をしてみようかな。

    株価といえば、明日はテクニカル分析で使われる指標について調べて実装してみます。オレオレ分析ツールを作りたいと思っているので。ではでは、また明日!

    *1:ドメイン知識の入れ方や、時系列予測に対する評価方法も書かれていて、良かった。

    *2:設計思想上その必要はない気がする

    *3:単にpip install fbprophetのみでもOKではありそう。

    【Day-12】時系列分析の良リソースまとめ&基礎チュートリアル

    データ分析ガチ勉強アドベントカレンダー 12日目。

    今までは、時間に依存しないデータについて取り扱ってきました。 しかし、世の中のデータは時間に依存したデータも多いのが事実です。

    時間に依存しないデータは、その分各データを独立に扱うことができますが、時系列データはそういうわけにはいきません。なので、なかなか難しいのです。

    今日は時系列のさわりをまとめて、また、時系列予測のチュートリアルをしていきます。

    参考にできるサイト

    時系列を学ぶにあたって、参考にした&参考にしたいサイトを列挙しておきます。 どれも充実した内容なので、一度は目を通しておくといいかもしれません。

    メタ的な記事

    pythonの実装も含めて

    ディープラーニング

    読んでる本、読みたい本を列挙しておく

    内容
    時系列解析入門 クラシカルな時系列データ分析が載っている本。数式をベースにきちんとした説明なのでよい。状態空間モデルのあたりは結構難しいが、たどり着きたいところ
    経済・ファイナンスデータの計量時系列分析 (統計ライブラリー) 未読。いいらしい。
    岩波データサイエンス Vol.6 時系列に関する最新トピックを集めた本。かなり面白いが、それなりの素養が必要
    詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理~ KerasとTensorflowを使ってRNNを実装する本。LSTMの説明などもされていて、ディープラーニングのモデルを実装しつつ勉強ができる。

    読みたい論文(積読)

    (見出しを押せば論文にとびます)

    Toeplitz Inverse Covariance-Based Clustering of Multivariate Time Series Data

    2017年KDDのBestPaperを受賞している。時系列を分析して状態をクラスタリングするという旨の論文。

    www.youtube.com

    WAVENET: A GENERATIVE MODEL FOR RAW AUDIO

    CNNで時系列データ分析を実現した論文。Google音声合成などにも使われていて、高い性能を実現している

    日本語の輪読スライドも、ぜひとも参考にしたいところ。

    www.slideshare.net

    基礎チュートリアル

    上記で紹介した記事A comprehensive beginner’s guide to create a Time Series Forecast (with Codes in Python) を参考にしながら、実際に時系列の予測をしてみる。

    ところどころライブラリのバージョンが古かったりしたので、修正&大事そうなところは加筆した。内容は↓

    • データの用意
    • 定常性チェック
    • 定常状態のデータに変換
    • 時系列の予測

    いつものようにgithubにあげている(day12)

    github.com

    データの用意

    pandasを用いてデータを用意する 使うデータは International Airport Passengers

    定常性のチェック

    • 平均や分散がずっと一定ならstarionary(定常)といえる
    • 多くの時系列モデルは定常性を仮定しているので、定常性のチェックは重要。

    使用しているデータを見ると、増加傾向にあることがわかる(上図)。これだと平均や分散が一定とならないように見える。 ちゃんと調べるために、下記の処理を施してみる

    1. moving averageやmoving distanceをとってみる(sliding window)今回は、季節変動的なのも吸収したいから12か月分
    2. Dickkey Fuller Testを用いる。定常性の統計検定である。詳細

    移動平均や移動標準偏差には、pandasの関数rollingを用いる。(pandas rolling) windowは窓幅で、window=12はこのデータでは一年分の平均となる。これで、簡単に移動平均をとることができる。

    rolmean = timeseries.rolling(window=12).mean()
    

    移動平均/標準偏差をとった結果は↓

    DF検定には、statsmodelsという時系列用統計ライブラリを使う。statsmodelsは時系列を取り扱うときに何かと便利なので、扱えるようにしておきたい(statsmodels)

    from statsmodels.tsa.stattools import adfuller

    で、検定をしてくれる。検定結果は↓

    Results of Dickey-Fuller Test:
    Test Statistic                   0.815369
    p-value                          0.991880
    #Lags Used                      13.000000
    Number of Observations Used    130.000000
    Critical Value (1%)             -3.481682
    Critical Value (5%)             -2.884042
    Critical Value (10%)            -2.578770

    この表の見方だが、Test StatisticとCritical Valueを見比べる Test Statisticの値が、Critical Valueを下回らないと定常とはいえない。

    たとえば、Test Statisticが-3.0だったとすると、95%以上の信頼を持って定常といえると結論付けることができる。ともかく、このままだとこいつはまったく持って定常ではないデータなのである。

    時系列データを定常状態にしたい

    変動要因にはいろいろある

    • トレンド
    • 季節変動
    • ノイズ
    • 小変動(周期性あり)

    定常性に響くトレンドをうまく取り除き、定常状態にしてやりたい。 下記の要領で行っていく

    1. positiveトレンドのときは、大きい値にペナルティを与えるような変換をかける。さまざまな変換がある。

      • logをとる
      • logit変換{$z_n = \log{\frac{y_n}{1-y_n}}$}
      • 差分
      • ルート、三重根をとる
    2. トレンド成分を取り出す

      • Aggregation : 週/月ごとにまとめた平均を取る
      • Rolling : windowをスライドさせながら平均を取る(移動平均)
      • Polynomial Fitting : 回帰でフィッティングさせる
    3. Originalからトレンド成分を引く
    4. テストをしてみる。

    の要領でやっていく。今回はlogをとって、移動平均にかけて、Originalから引いてみた。 移動平均には、普通の平均をとるのと、最近の時間ほど重みを強くかけるexpweighted moving average**の二種類をとった。後者は時間幅を厳格に設定しなくてよいため、クリティカルな窓幅が想像できないときなどに、柔軟に扱える。

    moving averageによってトレンドを求めた結果がこちら

    このトレンド成分(weighted moving average)をOriginalから引いたのがこちら

    この結果を検定にかけると、下記の結果になる

    Results of Dickey-Fuller Test:
    Test Statistic                  -3.601262
    p-value                          0.005737
    #Lags Used                      13.000000
    Number of Observations Used    130.000000
    Critical Value (1%)             -3.481682
    Critical Value (5%)             -2.884042
    Critical Value (10%)            -2.578770

    99%以上定常状態といえる。このようにして定常性を消すと良い。

    時系列の予測

    最後に予測。 予測によく使われるARIMA(Auto-Regressive Integrated Moving Average)の説明をしながら、実際に使ってみる。 ARIMAは、ARモデルと、MAモデルを統合したモデルとなっている。

    ARIMAには、下記のパラメータを用いる

    • p (number of AR terms) : 自己回帰の窓幅を何個前まで取るか
    • q (number of moving average) : 移動平均の窓幅を何個前まで取るか
    • d (number of differences) : 一次の差分か二次の差分か

    p=0だとMA、q=0だとARモデルとできる。

    pとqを決めるための大事な量がACF(Autoorrelation FunctionPACF(Partial ACF)

    • p : ACFのupper confidential intervalがクロスするラインを選ぶ
    • q : PACFupper confidential intervalがクロスするラインを選ぶ

    と良いらしい。これもstatsmodelsで利用可能。

    from statsmodels.tsa.stattools import acf, pacf
    

    *1 よさげなpとqがわかったら、ARIMAにかけてみる。 これもstatsmodelsで利用ができる。

    • モデル初期化はARIMA(p,d,q)の順番
      • p=0でMAモデル
      • q=0でARモデル
    • sklearnのように、fitでモデル作成
    ts #時系列データ
    from statsmodels.tsa.arima_model import ARIMA
    model = ARIMA(ts, order=(2, 1, 0))  
    model.fit()
    

    いろいろとわちゃわちゃしながら*2の結果がこちら

    定量評価には、RMSE(Root Mean Square Error)を使ってみた。自分で簡単に実装できるが、復習もかねてsklearn.metricsを使ってみる。

    from sklearn.metrics import mean_squared_error
    print("ARIMA:", np.sqrt(mean_squared_error(df.Passengers, df.ARIMA)))
    print("MA", np.sqrt(mean_squared_error(df.Passengers, df.MA)))
    print("AR", np.sqrt(mean_squared_error(df.Passengers, df.AR)))
    

    今回はARが最も良かった模様?

    ARIMA: 50.2956404427
    MA 43.9652476837
    AR 20.4183022135

    まとめ

    時系列分析事始ということで、調べたいリソースを集約するのと、はじめのチュートリアルまでを行った。 statsmodelsをとりあえず知っておけば、ぱぱっとした分析には良いなという感じなので、習得しておきたいところ。

    しかし最近、facebookが時系列ツールを出したので、そちらのキャッチアップもしておきたい。 なので、明日はfacebook謹製の時系列予測ツールprophetについて触れたい。

    明日もお楽しみに!

    *1:でも結局pとqは試行錯誤で選ぶ気もしている

    *2:詳しくはgithbをご覧ください

    【Day-11】機械学習のチートシートを眺めたり、比べてみたり

    f:id:imslotter:20171210225755p:plain

    データ分析ガチ勉強アドベントカレンダー 11日目。

    モデルを選び、試行錯誤しながら作っていく、そんな過程まで勉強してきました。 実装寄りの内容になったので、ここで一度機械学習界隈を俯瞰してみようと思いまして、調べると出てくる有名なチートシート

    について、ここでまとめておきます。

    用途ベースのMicrosoft Azure Machine Learning Cheat Sheet

    Microsoftが提供しているチートシート用途別にできるだけ分岐を行って、その後でデータの性質別に分けている。

    用途 アルゴリズム
    データの隠れた構造を見つけたい クラスタリング
    カテゴリを予測したい 分類
    値を予測したい 回帰
    普通じゃないデータを予測したい 異常検知

    データベースのMachine Learning Cheat Sheet (for scikit-learn)

    こちらはpython機械学習を行うときには必須のツールsklearnチートシート

    どういうデータかというところに着目してチートシートが描かれている。

    その中で、データの種類によって4種類に分けている。ニュアンスは下表な感じ

    データの種類 アルゴリズム
    カテゴリのラベルを持っている 分類
    カテゴリラベルを持っていない クラスタリング
    値のラベルを持っている 回帰
    どうしようもないデータを見やすくしたい 次元削減

    両者の違い

    上の説明を見ればわかるが、Microsoftチートシートは用途ベースで話を進めている。なので、どういうことをしたいかって言うのがわかりやすい。また、ほとんど定性的な説明で書かれているので、イメージがしやすい。

    一方でsklearnはデータの中身を見ながら分岐している。チェーンソーで紙を切るような無駄を起さないために、また良い妻で立っても計算が終わらないなどの悲劇を起さないために、なるべく簡単な学習器から初めてうまくいかなかったら高度な学習器をっていう感じになっている。

    ぱっと見タスクは何かを把握するにはMicrosoftチートシートだが、データ分析従事者にとってはsklearnのチートシートを見るのがいいのかなっていう気がした。各アルゴリズムの中身を見ていると

    • (回帰)一部の特徴量が効いているっぽい : LASSOなどによるスパース正則化を導入
    • (分類)テキストデータ : ナイーブベイズ
    • サンプルデータが大きい非線形分類 : カーネル近似

    など、割と細かく分かれている。これをベースにアルゴリズムを勉強して、「なぜそういう分岐になっているのか」を理解していくのもよさそう。

    見て判断できるようにしておく

    上記チートシートアルゴリズムを選ぶ際の参考になる。コレに加えて、自分で実験しながら視覚的&数値的にその学習器の性質を判断できると良いのかなと思う。Day-9で少し触れたが、評価したり可視化したりできていると、たとえば分類ならどんな分類面の引き方をするのかな~がわかる。

    www.procrasist.com

    いつものGithubに、データを投げるといろんな学習器を調べるスクリプトを作ってみた。

    github.com

    まだ改良中だが、これからもう少し整備したい。

    分類

    今回は簡単な分類問題と、三日月形の分類で、その識別境界を色付けしてみた。濃いほど自信があるように色をつけてある。

    blobs

    線形分離がほぼできる分布 LogisticRegressionやLinearSVMなどのシンプルな分類器が一番わかりやすそう。 一方で、RBFカーネルはちょっとぐにゃぐにゃとした分離選を作りすぎかなというようにも感じられる。 加えて、アンサンブル学習系(Decision Tree/ Adaboost/ RandomForest)なども斜めに選を引かずかくかくとした線になっていて少し苦しそう*1

    三日月形

    三日月形になると、線形分類器たちは太刀打ち不能になってしまう。一方で、GaussianProcessなどの非線形分類器は、かなりきれいな分離面を引いているのがわかる。ただGaussianProcess等は計算コストが大きいので、そのあたりも考慮しながらの運用になりそう。

    クラスタリング

    クラスタリングも、パラメータの有無だったり、クラスタ形成の仕方だったりで、いろいろと結果に違いが生じる

    blobs

    簡単なクラスタリング問題では、ほとんどのアルゴリズムが正しく機能している。 一方で、DBSCANだけはクラスタが多くなってしまっているようだ。

    三日月形

    じゃあDBSCANが悪いのかといわれると、実はそんなことはない。非線形な分布クラスタリングでは威力を発揮することも多い。下図だと、DBSCANだけが狙ったクラスタリングを達成できている。 閾値の調整が結構めんどくさいが、パラメータ次第では協力に機能する。

    まとめ

    きょうはチートシートを眺めたり、アルゴリズムを俯瞰したりしてみた。 結局時と場合によることが多いので、そのときにより良い判断ができるような基準を自分の中でもっておくことが必要かなと思う。

    などを総合的に判断できるようになると、データサイエンティストに近づけるのかなと思う。

    結構長い間sklearnメインで話を進めてきたけど、データ分析の基礎ができたところで、明日からは少し別の話題に触れようと思う。明日は時系列データについて書いていきたい。それではまた明日。

    *1:座標変換すれば何とかなるのかな?

    【Day-10】Cross Validationとパラメータサーチでモデルの調整

    f:id:imslotter:20171210183652p:plain

    データ分析ガチ勉強アドベントカレンダー 10日目。

    • データを集め、前処理を行い、学習をする。
    • どういう学習器が良いのかの評価基準

    の勉強までできた。でも、データがあって、評価基準がわかっていても、どうやって評価すればいいかについてはまだあまり触れていない。

    もうちょっと具体的に言うと、データと学習器があって、どういう風に教師データとテストデータを分ければいいのか。この方法について、実はまだ何も言っていない。

    そこで登場するのが、sklearn.model_selection

    いよいよ最後の仕上げ。良い学習器、良いパラメータの探り方について学ぶ

    Validationとは

    モデルを作ってデータを分析する際、データをtrain data, validation data, test dataに分ける。 最終的なテストをする前に、訓練データの中を分割してテストを回すことで、パラメータ調整を行うために用いられる。

    sklearnでは、originalデータをtrainとtestに分けるところをsklearn.model_selection.test_train_splitで行い、Validationデータに分けるところにもsklearn.model_selection.cross_val_scoreなどが用意されている。

    また、それにしたがって学習器のパタメータを変えながら評価していく仕組みも用意されていて、sklearn.model_selection.GridSearchCVで実現される。(後述)

    skleanのCross Validation

    cross_val_score

    sklearnで最も簡単にCross Validationをするには、cross_val_scoreという関数を用いる。

    `cross_val_score(clf, data, target, cv=5, scoring="accuracy")`
    
    変数 役割
    clf 学習器
    data データ
    target ラベル
    cv クロスバリデーションの回数
    scoring 評価する指標(参照:metrics)

    scoringには、下記の値を用いることができる。

    ['accuracy', 'adjusted_rand_score', 'average_precision', 
    'f1', 'f1_macro', 'f1_micro', 'f1_samples', 'f1_weighted', 'neg_log_loss', 
    'neg_mean_absolute_error', 'neg_mean_squared_error', 'neg_median_absolute_error', 
    'precision', 'precision_macro', 'precision_micro', 'precision_samples', 'precision_weighted', 
    'r2', 'recall', 'recall_macro', 'recall_micro', 'recall_samples', 'recall_weighted', 
    'roc_auc']
    

    各評価値が何なのかを詳しく知りたい方は、Day9を参照

    出力は、cvの回数分の、評価値の配列となっている。例として、digitsデータをRandomForestで取り扱ってみる。

    from sklearn.datasets import load_digits
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.model_selection import cross_val_score
    digits = load_digits()
    clf = RandomForestClassifier()
    scoring = "f1_macro"
    scores = cross_val_score(clf, digits.data, digits.target, cv=5, scoring=scoring)
    print("{}:{:.3f}+/-{:.3f}".format(scoring, scores.mean(), scores.std()))
    

    結果

    f1_macro:0.901+/-0.031

    pipline

    cross validationを行う際に、sklearn.pipelineを使うと、処理が簡潔に書ける。(参考)

    例 : 正規化した後の効果をcross_validationしたい

    # 通常
    from sklearn import preprocessing
    X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, train_size=0.8)
    scaler = preprocessing.StandardScaler().fit(X_train)
    X_train_transformed = scaler.transform(X_train)
    clf = SVC(C=1).fit(X_train_transformed, y_train)
    X_test_transformed = scaler.transform(X_test)
    print(clf.score(X_test_transformed, y_test))
    # pipeline
    from sklearn.pipeline import make_pipeline
    # 処理を流す順に関数を書いていく
    scoring = "f1_macro"
    clf = make_pipeline(preprocessing.StandardScaler(), SVC(kernel="linear",C=1))
    scores = cross_val_score(clf, digits.data, digits.target, cv=5, scoring=scoring)
    print("{}:{:.3f}+/-{:.3f}".format(scoring, scores.mean(), scores.std()))
    

    pipelineを使うと、複数処理を一括でCVにかけられるので、とてもよい。

    cross_validate 関数

    sklearn 0.19.1から、cross_validateが用意された。

    さっきまでのがcross_val_scores関数。ややこしいが少し違う

    • 評価に複数の指標を考慮できる
    • テストスコアに加えて、学習の時のスコア、学習時間、テストの時間などを算出してくれる。

    つまり、より強力なcross validationを行える。

    from sklearn.model_selection import cross_validate
    from sklearn.metrics import recall_score
    from sklearn.svm import SVC
    scoring = ["f1_macro", "recall_macro"]
    clf = SVC(C=1)
    scores = cross_validate(clf, digits.data, digits.target, scoring=scoring, cv=5)
    for key,value in scores.items():
        print("{}:{:.2f}+/-{:.2f}".format(key, value.mean(), value.std()))
    

    結果はこちら

    fit_time:0.51+/-0.01
    score_time:0.12+/-0.00
    test_f1_macro:0.50+/-0.04
    train_f1_macro:1.00+/-0.00
    test_recall_macro:0.45+/-0.04
    train_recall_macro:1.00+/-0.00

    Validation iterators

    CrossValidationを行うときの、データセットの分け方にも、いろんなバリエーションがある。 sklearn.model_selection内には、いろんなCVの分け方の方法が載っている。

    • i.i.d data
      • K-fold
      • Repeated K-Fold
      • Leave One Out (LOO)
      • Leave P out (LPO)
      • Shuffle & Split
    • iterators with stratification based on class labels(サンプリングとか)
      • Stratified k-fold
      • Stratified Shuffle Split
    • Grouped data i.i.dデータと基本おなじ

    代表的なのが、K-Fold法とLeave One Out法。両者を図にするとこんな感じ

    今回は、K-Fold法でのCrossValidationを実装してみる

    import numpy as np
    from sklearn.model_selection import KFold
    from sklearn.datasets import load_digits
    from sklearn.metrics import accuracy_score
    # K-Fold交差検定
    iterater = KFold(n_splits=5)
    results = []
    for train_indexes, test_indexes in iterater.split(digits.data):
    #     print(train_indexes, test_indexes)
        X = digits.data[train_indexes]
        y = digits.target[train_indexes]
        clf = RandomForestClassifier()
        clf.fit(X,y)
        pred_y = clf.predict(digits.data[test_indexes])
        ac = accuracy_score(digits.target[test_indexes], pred_y)
        results.append(ac)
    results = np.array(results)
    print("KFold accuracy: {:.2f}+/-{:.2f}".format(results.mean(),results.std()))
    

    出力

    KFold accuracy: 0.90+/-0.04

    パラメータを調整する

    機械学習は複雑なので、モデルの持つパラメータをどのように調節すべきか難しい。

    sklearnでは、下記の方法でパラメータ探索が行えるようになっている(参考 : Tuning the hyper-parameters of an estimator)

    • グリッドサーチ : パラメータを総当り式で調べる
    • ランダム選択 : ランダムにパラメータをサンプリングして試す
    • モデルに合わせた賢いCV*1

    他に、変数最適化の問題として、ある指標を評価関数にして解くというのもよさそう。Gaussian Process Optimizationなどがよさげ (参考)

    今回は、グリッドサーチでの方法を載せる

    パラメータ

    SVCのパラメータである、C, kernel, その他変数について調整する。調整する パラメータは下記のように、キーとリストで保持する。

    param_grid = [
        {'C': [1, 10, 100, 1000], 'kernel': ['linear']},
        {'C': [1, 10, 100, 1000], 'kernel': ['rbf'], 'gamma': [0.001, 0.0001]},
        {'C': [1, 10, 100, 1000], 'kernel': ['poly'], 'degree': [2, 3, 4], 'gamma': [0.001, 0.0001]},
        {'C': [1, 10, 100, 1000], 'kernel': ['sigmoid'], 'gamma': [0.001, 0.0001]}
        ]
    

    関数にはsklearn.model_selection.GridSearchCVを用いる。引数は

    • 学習器のインスタンス(今回はSVC)
    • パラメータグリッド(上記param_grid)
    • cv : cross validationの回数
    • scoring : 評価する指標

    実装

    実装は下記の通り

    from sklearn.model_selection import GridSearchCV
    import pandas as pd
    clf.get_params()
    svc = SVC()
    scoring = "f1_macro"
    param_grid = [
        {'C': [1, 10, 100, 1000], 'kernel': ['linear']},
        {'C': [1, 10, 100, 1000], 'kernel': ['rbf'], 'gamma': [0.001, 0.0001]},
        {'C': [1, 10, 100, 1000], 'kernel': ['poly'], 'degree': [2, 3, 4], 'gamma': [0.001, 0.0001]},
        {'C': [1, 10, 100, 1000], 'kernel': ['sigmoid'], 'gamma': [0.001, 0.0001]}
        ]
    clf = GridSearchCV(svc, param_grid,cv=4)
    clf.fit(digits.data, digits.target)
    df = pd.DataFrame(clf.cv_results_)
    df_scored = df.sort_values(by=["rank_test_score"])[["params","mean_test_score","std_test_score","mean_fit_time"]]
    

    結果を見る

    結果は、pandasのデータフレームに保管可能になっていて、簡単に好きな値でソートができる。今回はテストスコアの良い順にソートを行ってみた。(pandasの扱いが苦手な人はDay4をご覧ください)

    こんな風に、どういうパラメータがどういう結果だったかが保存されているので、より良いパラメータを選ぶことができる。

    まとめ

    今日は学習器の調整に重要なCross Validationとパラメータサーチの方法について触れた。 実際の業務の場では、パラメータを調整する機会もとても多いので、知っておくと良い。また、少し述べたが、もう少し効率的なパラメータ調整方法についても勉強しておきたい。

    機械学習の一連の流れがコレでざっと洗えたと思う。明日はscikit-learnのチートシートについてでも触れようかな。明日もお楽しみに!

    *1:ドキュメントによればあるらしい...?

    【Day-9】機械学習で使う指標まとめ(実装編)

    f:id:imslotter:20171209195625p:plain データ分析ガチ勉強アドベントカレンダー 9日目。

    データを学習器に入れるところまではできた。後は学習させるだけ! だが、学習器といってもたくさんある。どういう学習器を選べばよいのだろうか。

    そのためにはモデルをうまく評価するしくみを作らなければならない。 今回は、sklearn.metricsを見ながら、学習の評価をどのように行うかについて学ぶ

    機械学習に使う指標総まとめ(教師あり学習編)

    学習器の評価には、さまざまな指標が用意されている。 以前、教師あり学習については全力でまとめている。なので、そちらを見ていただければと思う。

    www.procrasist.com

    しかし、こちらの記事では、数式も多く、実感もわかないだろう。なので今回はこの中の指標の実装も行ってみる。

    sklearn.metricsによる定量指標

    sklearn.metricsには、たくさんの指標が用意されている

    分類の定量指標

    先ほどの分布を分類した結果を評価したい。参考にしたのはこちらの公式ページ。 用意するものは、下記のものである。カッコ内は私が実装で用いた変数

    • テストデータの正解ラベル(y_test )
    • 学習器が判定した結果(pred_y )
    • そのときの判定スコア(pred_y_score)※

    ※判定スコアは、必要な指標と必要でない指標がある。使わない指標は、ただ分類結果だけをみて判定するのでわかりやすい。一方で判定スコアを使う場合は、指標がすこしわかりにくくなる一方で、いわゆる分類器の自信を考慮しながらの算出となるので、より詳細に分類器を評価している。

    ここで、sklearn.metricsに入っている主要な分類指標を見てみる

    クラス 分類指標 判定スコアの
    使用
    備考
    confusion matrix -- なし 実際と予測のずれ具合を見る
    accuracy Accuracy なし どのくらいあたったか
    classification_report f値, recall, precision なし よく使う指標をまとめてくれてて良い
    hamming_loss hamming loss なし f:id:imslotter:20171209182300p:plain
    jaccard_similarity jaccard類似度 なし f:id:imslotter:20171209182351p:plain
    log_loss logarithm loss あり 使い方あんまり自信がない公式
    precision_recall_curve precision-recall曲線 あり トレードオフ具合をみる
    roc_auc ROC曲線 あり 閾値判定とか

    よく使いそうだなと思ったのが、accuracy. classification_report. roc_auc。主要な指標がきれいにまとまっている

    sklearn.datasets.load_breast_cancer()の学習を簡易的に行い、上記の3つの指標の入力の仕方と出力の仕方を学ぶ。

    コードをみる
    import matplotlib.pyplot as plt
    from sklearn.datasets import load_breast_cancer
    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import train_test_split
    from sklearn import metrics
    breast_cancer = load_breast_cancer()
    clf = LogisticRegression()
    X_train, X_test, y_train, y_test = train_test_split(breast_cancer.data, breast_cancer.target,train_size=0.8)
    clf.fit(X_train,y_train)
    # 結果を返す
    pred_y = clf.predict(X_test)
    # スコアを出すための関数。
    # ref : [decision_functionとの違いは?](https://stackoverflow.com/questions/36543137/whats-the-difference-between-predict-proba-and-decision-function-in-sklearn-py)
    pred_y_score = clf.predict_proba(X_test)[:,1]
    print("accuracy\n", metrics.accuracy_score(y_test, pred_y))
    print("classification report\n",metrics.classification_report(y_test,pred_y))
    # Compute ROC curve and ROC area for each class
    fpr, tpr, _ = metrics.roc_curve(y_test, pred_y_score,pos_label=1)
    roc_auc = metrics.auc(fpr, tpr)
    print("auc area", roc_auc)
    # ROC
    plt.figure(figsize=(5,5))
    plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve')
    plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC curve: \nAUC={:.3f}'.format(roc_auc))
    plt.show()
    

    結果はこんな感じ

    f:id:imslotter:20171209184215p:plain

    回帰の定量指標

    正解が数値で与えられる回帰にも、いくつか指標がある。これもsklearn.metrics内で実装されている。

    sklearn.datasets,load_bostonデータセットを使って、試してみる

    コードをみる
    from sklearn.datasets import load_boston
    from sklearn.linear_model import LinearRegression
    from sklearn import model_selection, metrics
    import numpy as np
    boston = load_boston()
    reg = LinearRegression()
    X_train, X_test, y_train, y_test = model_selection.train_test_split(boston.data, boston.target,train_size=0.8)
    reg.fit(X_train,y_train)
    pred_y = reg.predict(X_test)
    print("Variance score", metrics.explained_variance_score(y_test, pred_y))
    print("MAE : Mean absolute error", metrics.mean_absolute_error(y_test, pred_y))
    print("MSE : Mean squared error", metrics.mean_squared_error(y_test, pred_y))
    print("RMSE: Root mean squared error", np.sqrt(metrics.mean_squared_error(y_test, pred_y)))
    print("R2 score", metrics.r2_score(y_test, pred_y))
    

    出力は

    Variance score 0.637022922972
    MAE : Mean absolute error 3.16480595297
    MSE : Mean squared error 19.7557043856
    RMSE: Root mean squared error 4.44473895584
    R2 score 0.633791811542

    クラスタリング定量指標

    Evaluating the performance of a clustering algorithm is not as trivial as counting the number of errors or the precision and recall of a supervised classification algorithm. (clustering performance evaluation)

    とあるように、クラスタリングこそ絶対的な定量指標が存在しない。分け方なので、用途や意味によって変わってくるから。 けれど、一応指針みたいなのはあって、クラスタ内の散らばりの少なさクラスタ間の距離の長さをうまく定式化することで、定量化する。

    上記サイトにはクラスタリングの指標も多数実装されている。*1

    デモ実装(分類学習器ごとの差)

    最後にデモを作る。

    この図は、三日月形に分布するデータを分類しようとした結果を分類器別に分けてみたものである。 *2

    線形、非線形、アンサンブル学習など、学習器の性質によってかなり異なることが見て取れて面白い。

    こうやって分離状況を可視化していくと、大体どういう雰囲気で学習器が分布しているかはわかる。 でも、それでは定量的な指標とはいえない。ので、先ほどまでの指標を見て、定量化してみた

    詳細は例のごとくgithubにあげている。

    github.com

    結果1(表)

    大きく結果の順位が変わるようなことはないが、精度だけじゃなく、precisionとrecallなどを見ると開きがあるもの、ないものなど違いがあったりして、分類器の個性を詳細に知ることができる。

    Algorithm accuracy precision recall f1-score ham. loss jaccard sim AUC
    LogisticRegression 0.83 0.82 0.83 0.82 0.17 0.83 0.92
    Nearest Neighbors 0.95 0.91 1 0.95 0.05 0.95 0.95
    Linear SVM 0.8 0.8 0.77 0.79 0.2 0.8 0.92
    RBF SVM 0.94 0.89 1 0.94 0.06 0.94 0.98
    Gaussian Process 0.95 0.91 1 0.95 0.05 0.95 0.98
    Decision Tree 0.92 0.92 0.92 0.92 0.08 0.92 0.92
    Random Forest 0.89 0.88 0.9 0.89 0.11 0.89 0.93
    Neural Net 0.83 0.82 0.83 0.82 0.17 0.83 0.92
    AdaBoost 0.86 0.84 0.88 0.86 0.14 0.86 0.92
    Naive Bayes 0.82 0.81 0.81 0.81 0.18 0.82 0.92
    QDA 0.83 0.82 0.83 0.82 0.17 0.83 0.92

    結果2(図)

    代表的な3種の分類器でROC曲線とPrecision-Recall曲線を引いた。 両者とも、トレードオフのグラフになっているので、覆われた領域の面積が大きいほど、いい分類器であるといえる。 今回のデータに対しては、非線形分類器に軍配が上がりそうだ。

    まとめ

    以前まとめていた指標、理論ではわかっていても実際に使って動きをみてみると、知識として定着する。 いろいろと指標はあるが、sklearn.metricsを用いれば簡単に実装が可能なので、勉強したい方は是非一度手を動かしてみてはいかがでしょうか。

    ちょっと長くなりましたが、今日はこの辺で。 明日はモデル選択(Cross Validation)とパラメータサーチ!

    *1:余裕があれば加筆します...w

    *2:Classifier Comparisonを一部改変してつくった

    【Day-8】絶望的なデータを前処理で何とかする。(pandas/sklearn)

    f:id:imslotter:20171208163632p:plain データ分析ガチ勉強アドベントカレンダー 8日目。

    Day-7の記事で、データを取り扱えるようになりました。 しかし、データがいつもきれいで美しいものだとは限りません。なかには絶望的なデータもたくさんあります。

    機械学習等の学習器に投げ入れるには、もうひと工夫いることのほうが多いです。 pandassklearnで、できる工夫、前処理についてまとめて行きます

    前処理とは

    学習の流れを簡単な図にまとめてみる。

    データ分析の労力の7~8割は、↑図の赤の部分、前処理といわれている。 適当に学習器に投げ入れたデータよりも、きちんと温かみをもって処理をすることが大事。*1

    データをうまく整形することで精度が大きく変わってくるので、試行錯誤で調整を繰り返すことが多い。

    ひとくちに前処理といってもデータの質、型(数値、カテゴリ変数、画像、自然言語)、仮定する分布などで大きく変わるので、一概には言えない。下記スライド(英語)は前処理について詳しく書かれていて、大変勉強になった。

    絶望的なデータの入手

    たとえば、avent-calendar-2017リポジトリにある、day8-sample.csvを読み込んでみる。

    import pandas as pd
    df = pd.read_csv("day8-sample.csv", index_col="name")
    df
    

    すると、絶望的なデータを手に入れることができる。

         height weight
    name              
    A     160cm   49kg
    B     170cm   37kg
    C     1.81m     80
    D       NaN   50kg
    E     1.90m     80
    F     170cm    NaN
    G     150cm     40

    単位が違う、、単位がない、、、NaN。。。 実際、こんなデータがたくさん入っていたりする。本当はオペレーションのレベルで何とかしておけばこんなことは起こらないはずだが、データを集めているヒトが必ずしも情報技術者というわけではない。めんどくさいが、せむかたなし。

    データを統一的な型(数値等)に変換(df.apply)

    こういうデータを、学習器に投げるための形にもっていくのが、まず始めに行う前処理である。pandasを使えば、比較的楽に処理が可能。

    【Day-4】都道府県のデータをいじりながら、pandasを学ぶにて、pandasについて触れた。 ここで、緯度経度を60度法から数値に変換するという処理をしたが、これも前処理のひとつである。 自分で関数を書いてpandasのapply関数を使うと、処理が簡単に行えるので良い。(詳しくはDay-4)

    データを眺めると、単位で場合わけしながら、cmとkgに統一した数値データにするのがよさげ。なので、関数を作る。少し雑だが、cm,m,kgなどをうまく除去するように場合わけをする。

    def height_to_num(height):
        if type(height)==float:
            return height
        if "cm" in height:
            height = float(height[:-2])
        if (type(height)!=float) and ("m" in height):
            height = float(height[:-1])
            height *= 100
        return height
    
    def weight_to_num(weight):
        if type(weight)==float:
            return weight
        if  (type(weight)!=float) and ("kg" in weight):
            weight = weight[:-2]
        return float(weight)
    

    この関数を全部のindexに適用するのが、apply関数である。次のようにすると、各カラムにさっき作った関数が実行される

    df["height"] = df.height.apply(height_to_num)
    df["weight"] = df.weight.apply(weight_to_num)
    print(df)
    

    出力

          height weight
    name               
    A      160.0     49
    B      170.0     37
    C      181.0     80
    D        NaN     50
    E      190.0     80
    F      170.0    NaN
    G      150.0     40

    数字がきれいに扱えるようになった。

    NaNの除去

    これで全部統一的なデータ形式にできた。あと厄介なのがNaN(空白)の取り扱い。これをそのまま学習器に代入するとエラーが出る。sklearnでもpandasでも可能である。両方やってみる pandasの場合、下記の三種類がある

    • df.dropna() : NaNのある行を除去
    • df.fillna() : NaNをある値で埋める
    • df.interpolate() : NaNを前後の値から補間

    df.dropna()

    print(df.dropna(how="any"))
    

    実行すると、

          height  weight
    name                
    A      160.0    49.0
    B      170.0    37.0
    C      181.0    80.0
    E      190.0    80.0
    G      150.0    40.0

    このように、ひとつでもNaNが入っているindexは消える。 dropna()内の引数で、消し方を決められる。how="all"だと、すべてNaNのindexだけ消すといった具合になる。 詳しくはこちら

    df.fillna()

    値を埋める。0で埋めてもいいが、たとえばそのほかの値の統計値を使って埋める。なども可能。今回は平均(mean)で埋めてみる

    df.height = df.height.fillna(df.height.mean())
    df.weight = df.weight.fillna(df.weight.mean())
    print(df)
    

    出力は

    name                    
    A     160.000000    49.0
    B     170.000000    37.0
    C     181.000000    80.0
    D     170.166667    50.0
    E     190.000000    80.0
    F     170.000000    56.0
    G     150.000000    40.0

    ふむふむ、無事平均値で埋まったようだ。ほかにも中央値で埋めるなど、無難な値で埋めることができる。

    df.interpolate()

    値を近似して補間してしまう方法。indexが意味を持つとき(たとえば時系列などにはよく用いられるが、名前のような、カテゴリカルなときは用いてもあまり意味を成さない。けど、練習のためやってみる。

    print("orig\n",df.height.values)
    print("interpolate\n",df.height.interpolate(method='linear').values)
    

    interpolateを行った後は、

    orig
     [ 160.  170.  181.   nan  190.  170.  150.]
    interpolate
     [ 160.   170.   181.   185.5  190.   170.   150. ]

    となり、method="linear"では線形補間が行われたことがわかる。

    なお、methodには、いろんな補間形式がある。公式ページに補間の方法とまとめが載っているので、参考にするといいだろう。

    また、sklearn.preprocessing.imputerでも、欠損値補間はできるようだ。(Imputer)

    その他

    そのほかにも、回帰代入法など、NaNを埋める方法はたくさんある。データの特徴をみながら、どういう埋め方が良いのかを判断すると良い。

    スケーリング

    これですべて数値で埋まった値が帰ってきたが、身長と体重で絶対値が違う。こういうデータの大きさ、形状を調節して同じような大きさにするほうが、うまくいく(ことが多い)。これをスケーリングという。

    sklearn.preprpcessingには、スケーリングを行う関数が多く実装されている。 sklearn.preprocessing(公式ページ)

    StandardScaler

    各カラムの値を、平均0、標準偏差1の分布に変えてしまう。データが普通の分布(正規分布っぽい分布)をしているときなどはコレを用いることが多い。skelarn.preprocessing.StandardScalerを用いる。

    # standard scaler
    # スケールの取り出し(新しいデータが来たときに、固定された正規化定数で対応が可能)
    scaler = preprocessing.StandardScaler().fit(df)
    # スケールを出す
    print("mean:{} std:{}".format(scaler.mean_,scaler.scale_))
    # スケーリングを行う
    df_scaled = scaler.transform(df)
    print(df_scaled)
    

    出力

    mean:[ 170.16666667   56.        ] std:[ 12.07614729  16.27443218]
    [[-0.84187998 -0.43012253]
     [-0.01380131 -1.16747545]
     [ 0.89708523  1.47470583]
     [ 0.         -0.36867646]
     [ 1.64235603  1.47470583]
     [-0.01380131  0.        ]
     [-1.66995865 -0.98313722]]

    基本的には、

    • scaler = preprocessing.hogehoge().fit(df) で、それぞれのアルゴリズムによるデータのスケーリング定数を算出
    • scaler.hoge_で、スケーリング定数を取り出す
    • sceler.transform(df) で、スケーリングした配列を出力

    という設計になっている。hogehoge,hogeは各アルゴリズムによって変わってくる

    MinMaxScaler

    これも良く使うスケーリング方法で、最低が0、最高が1にスケーリングする。StandardScalerとほとんど同じだが、係数が少し変わってくる

    # min-max scaling
    scaler = preprocessing.MinMaxScaler().fit(df)
    print("max:{}, min:{}".format(scaler.data_max_, scaler.data_min_))
    print("SCALED")
    print(scaler.transform(df))
    

    出力

    max:[ 190.   80.], min:[ 150.   37.]
    SCALED
    [[ 0.25        0.27906977]
     [ 0.5         0.        ]
     [ 0.775       1.        ]
     [ 0.50416667  0.30232558]
     [ 1.          1.        ]
     [ 0.5         0.44186047]
     [ 0.          0.06976744]]

    その他

    preprocessingには、カーネル向けの前処理(KernelCenterer)や、多項式化(PolynomialFeatures)、バイナリ化(Binarizer)そのほかにもさまざまな前処理方法が用意されている。詳しくは公式ページを参照し、いろいろ試してみるのがいい。使い方は基本的に上記で示したとおり。

    また、FunctionTransformerという関数を用いれば、sklearnの設計どおりに自分で特徴量変換関数を作ることができる。たとえば、データにlogをつけることも結構あったりするので、そういう変換関数を作る。

    from sklearn.preprocessing import FunctionTransformer
    # logを当てはめる
    transformer = FunctionTransformer(np.log1p)
    transformer.transform(df)
    

    出力は

    array([[ 5.08140436,  3.91202301],
           [ 5.14166356,  3.63758616],
           [ 5.20400669,  4.39444915],
           [ 5.14263774,  3.93182563],
           [ 5.25227343,  4.39444915],
           [ 5.14166356,  4.04305127],
           [ 5.01727984,  3.71357207]])

    オレオレ特徴変換をしたいときは、こんなのもよさそう。

    まとめ

    手に入るデータがいつもきれいだとは限らない。というか、汚いぐちゃぐちゃなデータのほうが多い。 うまく変換する方法を知っておくことで、効率的に学習器にデータを投げることができる。

  • pandasのapply関数で、カラムごと一括処理
  • pandasのdropna, fillna, interpolateで、欠損値に対応
  • sklearn.preprocessingをうまく使って、データをいい具合に変換
  • 近年ではよく使うものは、前処理を済ませたデータを配ろうとする動きもあるみたいで、オープンソースとして提供しようとする試みもある。 このdataset.jpというところなどがそう。開発が滞っているっぽいので、活発になればうれしいな(面倒が減るな)とか思ってる。

    データ分析や機械学習に欠かせない「前処理」の共通化を目指したオープンソースが国内で発足|アイザック株式会社のプレスリリース

    以上、これで学習器に投げられるところまできた気がする。 明日は、良い学習器を選ぶためには?というお題で書きたい。

    *1:ディープラーニングの対等で、適当に投げてもかまわんという風潮もあるが、個人的には前処理は結果の説明のために必要だと思っている。

    【Day-7】sklearnで機械学習用データの作り方/使い方をまとめる(sklearn.datasets)

    f:id:imslotter:20171206182003p:plain データ分析ガチ勉強アドベントカレンダー7日目。 今日からはscikit-learnを取り扱う。

    機械学習の主要ライブラリであるscikit-learn(sklearn)。機械学習のイメージをつかみ練習するにはコレが一番よいのではないかと思われる。

    今日はデータを作って、(必要ならば)変形し、モデルに入力するまでをまとめる。

    • 既存データへのアクセスの仕方
    • ある程度狙ったランダムデータを作る方法
    • modelに入力する際、どういう構造でデータを入れるかについて。

    基本的にはDataset loading utilitiesを参考にしながら。 いくつかデータセットをピックアップして、実際に扱ってみる。

    なお、今回はsklearn.datasetsをふんだんに用いるので、はじめに下記コマンドを読み込んでおく。

    from sklearn import datasets
    

    Toy Dataset

    datasets.load_hogehoge()という関数により、データを使うことができる。

    感覚をつかむために用意されたデータセット。簡単に使えるが、デー多数は少なく、学習器の性能をテストするには完全な量ではない。

    スペック

    dataset どういうデータか データ数 次元 特徴量の型 正解ラベル
    load_boston ボストンの家賃 506 13 正の実数 実数
    load_iris アヤメの分類 50×3クラス 4 正の実数 3クラス
    load_diabetes 糖尿病患者の進行状況 442 10 -2<x<2 25~346の整数
    load_digits 8×8ピクセルの数字データ (約)180×10クラス 64 0~16の整数 0から9まで
    load_linnerud 男性の生理学的特徴と運動能力の関係 20 3 整数 整数
    load_wine ワインを3種類に分類 [59,71,48]=178 13 正の実数 3クラス
    load_breast_canser 乳がんの陰性/陽性判定 [212,357]=569 30 正の実数 2クラス

    データの形式

    正解ラベルが実数かクラスかによって、正解データの利用の仕方が回帰か分類かに分かれる。

    ざっくり回帰は、次の値を予測する、分類は、そのデータがどのクラスに属するかを判定する。正解ラベルが実数ならばその値自身に意味があるが、クラス(1,2,3など)ならば、その値自身には意味がない(カテゴリ変数とも呼ばれる)

    先ほどの表で、正解ラベル次第でデータの保持形式が変わる

    正解ラベルが実数

    下記データを保持している

    • data : データに入っているベクトルの値
    • feature_names : 各要素の名前
    • target : 実数値

    (例:boston)

    data =  datasets.load_boston()
    print("data shape:", data.data.shape)
    print("feature name:",data.feature_names)
    print("target value:", data.target)
    

    出力

    data shape: (506, 13)
    feature name: ['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT']
    target value: [ 24.   21.6  34.7  ...  22.   11.9]

    正解ラベルがクラス

    下記データを保持

    • data : データに入っているベクトルの値
    • feature_names : 各要素の名前
    • target : クラス(カテゴリ変数)
    • target_names : クラスの名前
    data = datasets.load_iris()
    print("data shape:",data.data.shape)
    print("feature name:",data.feature_names)
    print("target class", data.target)
    print("class name:",data.target_names)
    

    出力

    data shape: (150, 4)
    feature name: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
    target class [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
    class name: ['setosa' 'versicolor' 'virginica']

    シャッフル

    ちなみにこのままだと、データが0,1,2ときれいに並びすぎている。逐次学習のアルゴリズムのときなどは影響を受けてしまうので、sklearn.util.shuffleを用いるとよい

    from sklearn.utils import shuffle
    X, y = shuffle(data.data,data.target, random_state=0)
    print("target class", y)
    

    出力

    target class: [2 1 0 2 0 2 0 1 1 1 2 1 1 1 1 0 1 1 0 0 2 1 0 0 2 0 0 1 1 0 2 1 0 2 2 1 0 1 1 1 2 0 2 0 0 1 2 2 2 2 1 2 1 1 2 2 2 2 1 2 1 0 2 1 1 1 1 2 0 0 2 1 0 0 1 0 2 1 0 1 2 1 0 2 2 2 2 0 0 2 2 0 2 0 2 2 0 0 2 0 0 0 1 2 2 0 0 0 1 1 0 0 1 0 2 1 2 1 0 2 0 2 0 0 2 0 2 1 1 1 2 2 1 1 0 1 2 2 0 1 1 1 1 0 0 0 2 1 2 0]

    狙ったランダムデータを生成する

    ラベル付データ生成

    make_hogeを使えば、簡単にデータを生成することができる。hogeで表内囲まれているところは、パラメータで調節できるところ。

    dataset どういうデータか データ数 次元 クラス数 データ分布の調整
    make_blobs ガウス分布のデータ生成
    (重ならない)
    n_samples n_features centers cluster_std
    center_box
    make_classification n-class分類用データ生成
    (詳細設計可能)
    n_samples n_features n_classes 結構複雑
    詳細
    make_gaussian_quantiles 同心円状の分布を生成 n_samples n_features n_classes mean
    cov
    make_hastie_10_2 Hastie Exampleで用いられた分布 n_samples 10 2 -
    make_circles 円の分布 n_samples 2 2 factor
    make_moons 三日月形の分布 n_samples 2 2 -

    その他

    • shuffle(bool) : サンプルをシャッフルするかどうか。

    それぞれのデータの分布(イメージ)はこんな感じ

    f:id:imslotter:20171206190749p:plain

    マルチラベルデータ生成

    さっきのが、正解ラベルがひとつのラベルのとき。正解ラベルを複数持つデータについてはmake_multilabel_classificationにて生成が可能(詳細)。重要なパラメータは下記

    • n_samples, n_features : ↑と一緒
    • n_classes : クラスの数
    • n_labels : ひとつのデータに対するラベル付けするクラスの平均

    ひとつのデータに対して出てくる出力は下記のようになる

    data, target = datasets.make_multilabel_classification(n_samples=100, n_features=10, n_classes=5, n_labels=3)
    print("data:",data[0])
    print("label:",target[0])
    

    出力

    data: [  4.   2.   9.   3.   4.   5.   3.   6.   1.  10.]
    label: [0 1 1 0 0]

    1がついているのがそのデータについているラベル。

    その他

    そのほかにもbiclustering用のデータや、metrics learning用のデータなどもある

    • make_biclustering
    • make_s_curvemake_swiss_roll(xz平面に射影)

    fetchしてくる

    datasets.fetch_hoghogeで、外部リポジトリにあるデータを保持することも可能。普通にダウンロードに時間がかかるので、必要なときだけ保存するようになっている。下記のようなものがある。

    関数 説明
    datasets.fetch_20newsgroups() 自然言語のデータ、20個のニュースカテゴリに分ける。データ数は11314
    datasets.fetch_20newsgroups_vectorized() 上記の文章を単語に分けてベクトル化(13万次元くらい)
    datasets.fetch_california_housing() カリフォルニアの家の評価(正解値は実数)
    datasets.fetch_kddcup99() KDD99の侵入検知のコンペで用いられたデータ。詳細
    datasets.fetch_lfw_pairs() labeld face in the wild の略。同じ人か違う人かの二値分類詳細
    datasets.fetch_lfw_people() 人の名前を当てる分類問題
    datasets.fetch_mldata("hoge") "hoge"を変えれば、ここにあるデータを良しなにとってこれる
    datasets.fetch_olivetti_faces() AT&Tが作った、CV用データセット
    datasets.fetch_rcv1() Reuters Corpus Volumeの略。80万のデータ、約5万のワードベクトル、13のカテゴリ。マルチラベル
    datasets.fetch_species_distributions() species distributionのデータセット参考

    これも基本的には

    • data : データに入っているベクトルの値
    • feature_names : 各要素の名前
    • target : クラス(カテゴリ変数の場合)
    • target_names : クラスの名前

    がデータとして入っている。自然言語ベクトル化する前とかだったら微妙に違うこともあるが、だいたいはこういう配列なんだと思っていればOK。そんなに難しい構造していないので、ちょっと自分が作った学習器を試したいときに、データだけ呼ぶってのもいいと思う。

    学習させる

    データを用意できたので、最後にデータを投げるところまでまとめて終わりにする。 処理の仕方とか、まあ種々のデータのいじり方は後に回すとして、どうやって入力するかだけ。

    1. データをシャッフルして分割する(sklearn.model_selection.train_test_split)
    2. 学習させる(sklearn.ensemble.RandomForestClassifier)
    3. テストして評価する(accuracy)(sklearn.metrics.accuracy_score)

    取ってきたデータを いろんな学習器があるけれど、sklearnは入出力の関数/型が決まっている

    • model.fit(X,y) : モデルを学習する(教師なしの場合は、ラベルはいらないので、model.fit(X))
    • model.predict(X) : モデルからテストを行う

    irisのデータを読み込んで学習を行うところまでをやってみる

    from sklearn.model_selection import train_test_split
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.metrics import accuracy_score
    
    # データの読み込み
    iris = datasets.load_iris()
    data, target = iris.data, iris.target
    # 学習とテストに分ける
    data_train, data_test, target_train, target_test = train_test_split(data, target, train_size=0.8, random_state=1) 
    # RandomForestを使う
    model = RandomForestClassifier()
    # 学習
    model.fit(data_train, target_train)
    # テストと評価
    target_pred = model.predict(data_test)
    print(accuracy_score(target_test, target_pred))
    

    出力

    0.966666666667

    できた。ランダムフォレスト強い

    まとめ

    今日はデータについてと、一度学習するところまでをまとめてみた。 sklearnのデータセットの形を知り、をうまく使いこなせれば、いろいろと幅が広がりそう。

    今日のコードも、上げているので是非試してみてください。

    github.com

    明日もsklearnの話題!最後に取り扱った学習のところをもうちょっと詳しく書く予定です。お楽しみに~

    【Day-6】ゼロからJupyterの達人に!使い方の総まとめ。

    f:id:imslotter:20171205234021p:plain

    こんにちは、ほけきよです。

    データ分析ガチ勉強アドベントカレンダー6日目。

    当ブログもJupyterに関するメモをたくさん記してきました。 今回は保存版ということで、Jupyterの基礎事項から、ちょっとしたTipsなどを総まとめしておきます。この記事を読めばJupyterの使い心地もかなりUPするでしょう。

    事始

    インストール方法、実際に一通り実行してみるところまで行える。

    www.procrasist.com

    コレだけは知っておくべきチートシート

    以前自分のQiitaで書いたものを+αして再掲。最低限知っておくとよいもの。

    起動

    jupyter notebook

    実行

    shift+Enter

    補完

    然るべきところでtab。そこまで強力でもない

    ショートカット

    最低限覚えておくべきショートカット

    ショートカット 役割
    esc+M マークダウンモード
    esc+Y コードモード
    esc+L 行番号表示
    esc+A セルを上に挿入
    esc+B セルを下に挿入
    esc+DD セルを削除

    % : マジックコマンド

    Jupyterをpythonインタプリタとしてではなくシェルとして扱えるもの。詳しくは公式を。

    Built-in magic command

    マジックコマンドには二種類あって、下記の通り。

    • % : 一行を対象とする
    • %% : セル全体を対象とする

    たとえば、

    • %pdoc, %pdef. %psource, %pfile その後につづくもの(関数、クラスなど)の情報を表示
    • %timeit : 実行時間を表示
    • %run : 指定したpythonスクリプトを読み込んで実行
    • �bug : pdbデバッガを走らせる
    • %quickref : その他マジックコマンドのラインナップを表示

    あたりを覚えておけば便利だろう*1

    ? : イントロスペクション

    オブジェクトの前か後ろに?をつけると、jupyterがその説明をしてくれて、とても便利。(下図)

    なお、escを押せば、この情報は閉じられる。

    エディタとしてのJupyterをかっこよく

    Jupyter themesというライブラリによって、簡単にJupyterのテーマを変えられる。故にモチベがあがる。

    www.procrasist.com

    グラフのインタラクティブな操作

    もともとインタラクティブなJupyterをさらにインタラクティブにする。 パラメータをバーでいじればグラフが変わったりすると、素敵やん?

    下記二点を使えば、おしゃれなグラフの描画が可能!

    • Bokeh : オシャレなグラフ描画ライブラリ
    • ipywidget : インタラクティブにパラメータをぐりぐりする

    www.procrasist.com

    Jupyterでイケてるプレゼンをする

    ここまでくれば相当なJupyter使い。プレゼンもJupyterで作っちゃいましょう。 RISEというライブラリを紹介して、実際にプレゼン資料を作っています。

    www.procrasist.com

    種々のこと

    その他Jupyter界隈で気になることをさらっとまとめておく

    Colaboratory

    Colaboratory

    Googleが作ったクラウド型Jupyter - クラウド上でのJupyterの保管 - 複数人での編集

    を可能にする(参考: 日本語版最速!? jupyter notebookをgoogleが神改造 colaboratoryについてまとめてみた。 - Aidemy Tech Blog)

    Jupyterhub

    Jupyterhub documentation

    複数人でひとつのjupyter notebookを使うときに便利 (JupyterHubの構築 - Qiita)

    まとめ

    Jupyter、使いこなせばpythonの専用エディタとして大活躍できそう。 関数の情報や実行時間まで測れるし、かなり優秀だと思う。

    さて、明日からはscikit-klearnのお勉強。どこからやろうかな。考え中。。。

    *1:最悪最後のquickrefだけでも...w

    【Day-5】Jupyterでできる!イケてるプレゼンスライドの作り方

    f:id:imslotter:20171205031053p:plain

    こんにちは、ほけきよです。

    MSのツールの中でも、パワポだけは優れたツールだと思っているんですよ。 けれど、せっかく技術者ならば、そこら辺も新しいツールを使ってみたいものです。

    最近エンジニア界隈では、reveal.jsを使ってHTMLでプレゼン資料を作っているのをチラホラと見かけます。 あれ、かっこよい。。。

    なので、今回は、Jupyterでプレゼン資料を作る方法`をまとめておきます。

    jupyterをスライドにする(利用編)

    RISEをつかう。

    インストールと設定

    コマンド側はたったこれだけ。簡単、超簡単

    pip install RISE
    jupyter-nbextension install rise --py --sys-prefix
    jupyter-nbextension enable rise --py --sys-prefix

    Jupyterを起動して、 赤枠で囲んだ箇所が現れていれば成功

    使い方

    view->Cell Toolbar->slideshowを選択。スライドショー用の設定画面になる。

    下図のように、プルダウンメニューが出ていればOK、

    メニュー 用途
    Slide スライドで新しいページに切り替える
    Subslide サブスライドを作る(→のページ送りじゃなくて↓のページ送り)
    Flagment 同一スライドで、Enterを押してからスライド表示
    Skip スライドに反映しない
    Note スピーカーノート

    また、そのままではダサいので、体裁を整えたい。 %%HTML<style></style>とかでcssをべた書きがめんどくさいけど一番シンプル。その際は、図のように.rise-enabled .revealをつけておくと、スライドにのみ反映となる。つけないと、jupyter編集画面まで変わってしまう。

    実行

    実際にRISEでプレゼンするとこんな感じになる。題材は昨日のアドベントカレンダー

    出力

    htmlに出力 下記コマンドを入力すると、スライドの形式を保ったまま、htmlファイルが出来上がる

    jupyter nbconvert "day5-jupyter-slide.ipynb" --to slides --reveal-prefix "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.1.0"

    出力されたhtmlファイルは、デフォルトのスライドデザインなので、jupyterの冒頭に書いたcssをコピーして、htmlのスライドが始まる直前の<style></style>の間にはめ込んでやる(はじめのスライドのタイトルなどで検索すると見つかりやすい)

    その際に、enable-riseセレクタは消去する。.revealは残す

    アップロード

    github.ioでは、htmlのスライドも見られるようにできる。 参考記事を見ながら、github pagesにあげてみる

    以下参考記事から抜粋した手順

    • reveal.jsを自分のgithubページにforkして、適当な名前に変更(settingで変更、そのままでもOK)
    • ローカルにforkしたreveal.jsページをcloneする
    • gh-pagesブランチを削除
    git branch -rd origin/gh-pages
    git push origin --delete gh-pages
    • 自分で新しくgh-pagesブランチを作成
    git checkout -b gh-pages
    • index.htmlというhtmlファイルを先ほど出力した自分のスライドに差し替える
    • 上記で作ったブランチにpush
    git push origin gh-pages

    ごり押しではあるが、スライドが作られている (demo page )

    ブログ等htmlで埋め込む

    最後、github.ioのページを何とかしてhtmlで埋め込みたい。

    こういうときは<iframe>タグを使う。

    <center>
    <iframe allowfullscreen="true" allowtransparency="true" frameborder="0" height="596"  mozallowfullscreen="true" src="https://hokekiyoo.github.io/jupyter-slide-demo/#" style="border:0; padding:0; margin:0; background:transparent;" webkitallowfullscreen="true" width="800"></iframe>
    </center>
    

    出力はこんな感じ

    まとめ

    いかがでしたか? Jupyter使いはぜひ使ってみてはいかがでしょうか

    試行錯誤を繰り返しながら色々と作ってみたので、改善点があれば教えていただければ幸いです。

    明日はJupyterネタがだいぶブログ内でまとまってきているので、それを踏まえてJupyterの総ざらいをします。ではでは!

    明日はjupyter!!

    【Day-4】都道府県のデータをいじりながら、pandasを学ぶ

    f:id:imslotter:20171204215003p:plain

    データ分析ガチ勉強アドベントカレンダー4日目。 今日はpandasを取り扱う。

    機械学習系の本にも、numpy、scipy, matplotlibの使い方は載っていても、pandasを載せている本って意外と少ない。 けれど、実際numpyの次くらいによく使う。データを取り扱ったり、計算したりするときにとても便利。一方で、癖があって慣れるまでに時間がかかる。なので、基礎的な事項をまとめておこうと思う。

    今回も、githubにコードや扱うデータを配置している。

    github.com

    都道府県の重心データを使いながら、pandasのお勉強をしようという試みである。

    pandasとは

    配列データの取扱が便利に行える

    • データの取り出し
    • データの操作
    • 統計計算
    • 時系列データの処理

    などを簡単に行えるため、重宝する。

    基礎事項は、公式ページの10 minutes to pandasを参考にすると良い。しかしこれ、確実に10分で終わらない内容となっている。その分濃いのでまあOKとする。

    また、リファレンスとしては下記の本も有用である。pandasの製作者が作っているので、信頼性は高い。

    Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

    Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

    基本事項

    データの読み込み/書き込み

    pandasの使い所としては、自分でデータ配列を作るよりかは、読み込んだcsv等のファイルを取り扱う時のほうが多いので、まずはソレ。pythonの組み込み関数であるcsvを使ってもいいが、pandasのほうがより手軽である。

    # 読み込み(csv)
    df = pd.read_csv("japan.csv", index_col="prefecrue")
    # 書き込み(csv)
    df.to_csv("japan2.csv")
    

    index_colは、インデックスにしたい列のこと。行の指定をするときに最もわかりやすいモノ(専門的に言えば主キーのようなもの?)を指定する。指定しなければ通し番号になる。

    Tips : データの文字コードがアレだと、下記のようなエラーが出るときもある。

    UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8e in position 0: invalid start byte

    そういうときは、

    df = pd.read_csv("japan.csv", encoding="shift-jis")
    

    とすると、解決する

    SeriesとDataFrameの作成

    読み込んでしまうと、そのまま表がDataFrameとして保管されるが、時には自ら作る場合もあるだろう。 pandasで用意されているデータ構造は下記のようなものがある。

    種類 特徴
    Series 1次元配列。要素をindexに保管
    DataFrame 2次元配列。行をindex、列をcolumnとして保存する
    Panel 3次元配列 参考

    正直、SeriesとDataFrameだけで良い。Panelは初めて知った。

    下記が、データフレームの作り方である

    # seriesが1次元配列、dataframeが多次元配列
    se = pd.Series([1,3,5,np.nan,6,8])
    print(se)
    # dataframe
    dates = pd.date_range("20130101",periods=6) 
    df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
    

    indexは指定しなければ、通し番号になる。datetimeなどで指定してやると後々時刻の範囲検索などもできるので便利である。

    データの基本的な情報をGetする

    (基本的にDataFrameの話を中心にすすめる)

    こうしてGETしたDataFrameの、基本的な情報は、下記のようにしてアクセスできる

    • df.head(3) # 初めの3つを見る
    • df.tail(3)) # 後の3つを見る
    • df.index # インデックスを確認
    • df.columns # カラムを表示
    • df.values # 中の値をarray形式で表示
    • df.describe() # 統計量(頻度、uniqueな値の個数、max,min,平均、25%点etc...)を表示

    df.valuesまでは、は中身を見るのにどれもよく使う。describeは初めて知った。便利そうだから今後使ってみる。

    データ内部へのアクセス

    DataFrame型へのアクセスは、様々用意されている。が、様々用意されているが故に、どれがどのようなアクセスの仕方だったのか、わかりにくくなっているのも事実である。ここで、どういうアクセスの仕方が可能なのか、まとめておく。

    • 基本
      • df["longtitude] # カラムの値(df.longtitudeでも可能)
      • df[0:3] # indexの指定
    • ラベルで指定 : df.loc
      • df.loc["東京都"] # indexの指定
      • df.loc[:,["longtitude","latitude"]] # columnのみ指定
      • df.loc["東京都",["longtitude","latitude"]] #両方指定
      • df.loc["東京都","longtitude"] # セル一つ分の指定
      • df.at["東京都","longtitude"] # セル一つ分の指定(より高速なアクセスが可能)
    • 位置による指定 ; df.iloc
      • df.iloc[3] # ilocによる行指定
      • df.iloc[3:5, 0:2] # ilocによる行列範囲指定
      • df.iat[1,1] # scalarの値(高速版)
    • 条件検索
      • df = df[(df.index!="沖縄県")&(df.index!="北海道")] # 沖縄と北海道は除外
      • 数値的な条件(>, == など)も可能

    新しいcolumnの追加、計算の適用。

    japanデータは、緯度経度が136:08:07のように、数値で入っていない。なので、この60度法で書かれたものをfloat型に変換し、新しいcolumnを追加したい。columnの追加は以下のような方法で出来る。辞書の追加のようなものだ。

    df["new_column"] = (新しい配列 or Series)
    

    変数の変換には、calc_numberという関数を用意した。

    def calc_number(d_num):
        """
        度であらわされてるのを、小数に変換
        """
        d = d_num.split(":")
        n = int(d[0])+float(d[1])/60+float(d[2])/3600
        return n
    

    さて、問題は、これをlongtitude,latitude関数の全てのindexに適用させるにはどうしたら良いかだ。 方法は2つある。

    • 一度配列を作り直して、新しいcolumnとして追加する
    • apply関数を適用する

    二番目の方法のほうが、シンプル。apply関数をつかうと、指定したcolumn全体に所与の関数を適用してくれる。 さっきのcalc_number関数を適用する

    df["latitude_num"]=df.latitude.apply(calc_number)
    df["longtitude_num"]=df.longtitude.apply(calc_number)
    print(df[df["latitude_num"]>135].index)
    

    出力

    Index(['山梨県', '静岡県', '長野県', '岐阜県', '富山県', '新潟県', '石川県', '栃木県', '群馬県', '埼玉県',
           '福島県', '北海道', '山形県', '福井県', '岩手県', '東京都', '奈良県', '宮城県', '秋田県', '三重県',
           '神奈川県', '青森県', '愛知県', '和歌山県', '滋賀県', '大阪府', '茨城県', '京都府', '千葉県'],

    ちゃんと西の方にある県が取り出せているよう。

    イテレーションを回す

    pandasはイテレーション用のメソッドを持っている。 が、なんかややこしいのでなかなか覚えられない。コチラのサイトを無限回見ている。何度もググって何度もこのページを開くのも流石にアレなので、ここにメモしておく。

    DataFrameなら、下記の3種類のイテレーション方法がある

    http://cdn-ak.f.st-hatena.com/images/fotolife/s/sinhrks/20150618/20150618222231.png
    (StatsFragmentsより引用)

    イテレーションの練習に、各都道府県の重心をプロットしてみる。*1

    plt.figure(figsize=(8,8))
    for key, row in df.loc[:,["longtitude_num","latitude_num"]].iterrows():
        plt.scatter(row["latitude_num"],row["longtitude_num"],
                    marker="x",color="blue")
    

    f:id:imslotter:20171204211523p:plain

    日本地図っぽい点群が出てきた。

    統計計算をしてみる

    今回の最後に、統計計算をpandasでしてみる。 pandasでは、簡単に統計計算を行うことが出来る。例えば以下の様なものが用意されている

    • df.mean() : 平均
    • df.std() : 標準偏差
    • df.var() : 分散
    • df.min() : 最小値
    • df.max() : 最大値

    全国の緯度と経度の平均を取ってみて、先程の都道府県重心プロットに重ね合わせる。どこになると思います?予想してみてください。

    longtitude_mean = df["longtitude_num"].mean()
    latitude_mean = df["latitude_num"].mean()
    print("latitude;{:.2f}, longtitude_mean:{:.2f}".format(latitude_mean, longtitude_mean))
    # >>> latitude;136.01, longtitude_mean:35.34
    

    さっきの日本地図に重ね合わせると

    f:id:imslotter:20171204212326p:plain

    こんな感じ。値をgooglemapで確認すると... f:id:imslotter:20171204212724p:plain

    滋賀県に!

    まとめ

    というわけで、よく使ったり調べたりする事柄を、まとめておいた。*2 しかし、ここに載せているのはpandasの機能のほんの一部分に過ぎない。 もともと金融データを取り扱う用に作られている*3ため、特に時系列データの取扱いは得意なようである*4。そのあたりもどこかでまとめられたらまとめる。

    明日はjupyter!!

    *1:pandasでもplotの関数はあるが、調べるのもめんどくさいので、matplotlibのものを使っている。

    *2:10 minutes to pythonは途中で飽きたので中座。。。

    *3:って書いていた気がする

    *4:rolling関数とか

    【Day-3】知らないコトがいっぱい...!『numpy 100 exercise』を全部やってみる (上級編)

    f:id:imslotter:20171203122846p:plain

    データ分析ガチ勉強アドベントカレンダー3日目。 今日も引き続き、100 numpy exercise をしていく。

    github.com

    今日は上級編。初級、中級でさえかなり難しかったので、不安ではあるが...とりあえずやっていく!

    また、問題の和訳と難易度も掲載しているので、自分の実力を試したい人はどうぞ。 自分が書いたコードともこちらに載せた(day3のやつ)

    github.com

    問題

    以下に、初級中級の問題を掲載する。長いので、ボタンを押すと見られるようにしている。

    • ○ : 模範解答どおり
    • △ : もっと効率的なやり方があった
    • ✕ : わからなかった
    実際の問題をみる
    番号 問題 難易度 結果
    64 2つのベクトルa,bがあって、aベクトルに、bで与えられたインデックスに箇所に1を加える ★★★
    65 aベクトルの値を用いて新しいベクトルを、bのインデックスに従って作る ★★★
    66 dtype=ubyte)の(w,h,3)画像全体を定義し、uniqueな値の数計算する(色はRGB256階調) ★★★
    67 4次元配列において、最後の2軸だけ一気に足す方法は? ★★★
    68 1次元ベクトルaを用意する。aと同じサイズで用意されたインデックスベクトル(min:0, max:9) s に従って構成されるsubsetベクトルの平均 ★★★
    69 内積の対角成分だけを取り出す ★★★
    70 [1,2,3,4,5]に対し、各値の間に3つの連続したゼロが挿入された新しいベクトルを作成する方法は? ★★★
    71 (5,5,3)の行列に対し、(5,5)の行列を、(5,5)の要素の箇所だけ掛け合わせる方法は? ★★★
    72 行列の特定の行を入れ替える ★★★
    73 三角形を記述する10個のtripletsがある。全ての三角形を作る線分の中で、uniqueなモノを書き出す。 ★★★
    74 bincountされた配列Cに対して、np.bincount(A)==CとなるようなAをどう作るか ★★★
    75 array上でsliding windowを使った平均算出法 ★★★
    76 一次元ベクトルaに対して一行目が(a[0],a[1],a[2])となって、以下の行はインデックスを1ずつずらした値が入るような行列をつくる ★★★
    77 配列のbooleanをひっくり返す、floatの記号をひっくり返す ★★★
    78 2つの点p0,p1がある。ある点pにおいて、p0,p1が作る直線と点pの距離を求めよ。 ★★★
    79 点群の集合P0,P1,Pがあって、Pの各要素の点からP0,P1が作る直線の距離を計算して配列にせよ。 ★★★
    80 配列Zに対し、与えられたpositionを配列の中心とし、与えられた形(shape)をくり抜いて新しい配列を作る(ないところは0で埋める) ★★★
    81 a=[1,2,3,4,5,...,14]とした時、R = [[1,2,3,4],[2.3.4.5].[3.4.5.6],...,[11,12,13,14]]を作る方法 ★★★
    82 行列のランクを計算する ★★★
    83 行列の中で最も使われている値を見つける ★★★
    84 (10,10)のランダム行列から全ての連続した(3,3)行列を取り出す ★★★
    85 Z[i,j]==Z[j,i]となるような、2次元配列のサブクラスを作る ★★★
    86 pセットの(n,n)の配列( size=(p,n,n) )と、pセットの(n,1)のベクトル( size = (p,n,1) )を用意する。p個の配列の掛け算を一度に実行する ★★★
    87 (16,16)の配列について、4×4のブロック和を求める ★★★
    88 ライフゲームをnuypyで実装する ★★★
    89 配列の中でn番目に大きな値を取る ★★★
    90 数値ベクトルについて、直積を取る。 ★★★
    91 普通の配列からrecord arrayを作る ★★★
    92 ベクトルaに対し、aの要素を3乗する計算方法を3つ答えよ ★★★
    93 A:(8,3)の配列、B:(2,2)の配列がある。Bの要素の順序は無視して、Bのそれぞれの行の要素を含むAの行を抜き出す ★★★
    94 (10,3)の行列に対し、全ての値が異なるrowとなるmatrix ★★★
    95 整数行列をバイナリ表現で表す ★★★
    96 2次元配列において、uniqueなrowを抽出 ★★★
    97 2つのベクトルa,bを考える。inner、outer、sum、mulの関数を求める ★★★
    98 2つのベクトル(x,y)によって作られるpathにおいて、等間隔サンプリングを用いてサンプリングする ★★★
    99 整数nと二次元配列Xがある。整数のみを含み、また和が4になる行を抜き出す ★★★
    100 1次元配列Xの平均のbootstrapped samplingにおける95%区間を計算する ★★★

    結果

    ★★★になると、題意がわからない問題も増えた。あと、普通に難しい、日本語が*1想像以上に解くのに時間がかかった。

    また、ゴリ押しでなんとかなる問題も結構あった*2。でも美しい回答を心がけたいところである。

    全問題
    37 15 7 15

    活躍したeinsum

    上級で印象に残ったのは、einsumを使う問題が結構多かった。 einsum()というのは、アインシュタインの縮約記号をpythonで使える、便利なやつ。以前記事にもしているので、そちらが詳しい。

    とにかく、**多次元配列や、複雑な配列演算に超絶威力を発揮する。

    下記の問題でeinsum()が使われている

    • (69). 内積の対角成分だけを取り出す
    • (71). (5,5,3)の行列に対し、(5,5)の行列を、(5,5)の要素の箇所だけ掛け合わせる方法は?
    • (84). pセットの(n,n)の配列( size=(p,n,n) )と、pセットの(n,1)のベクトル( size = (p,n,1) )を用意する。p個の配列の掛け算を一度に実行する
    • (90). 数値ベクトルについて、直積を取る。
    • (92). 大きなベクトルaに対し、aの要素を3乗する計算方法を3つ答えよ
    • (97). 2つのベクトルa,bを考える。inner、outer、sum、mulの関数を求める

    更に、実行時間が早くなるという恩恵も受けられる。92.の実行時間を確かめてみる

    x = np.random.rand(5e7)
    %timeit np.power(x,3)
    %timeit x*x*x
    %timeit np.einsum('i,i,i->i',x,x,x)
    

    計算結果

    2.42 s ± 79.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    762 ms ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    364 ms ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

    数倍早い。

    物理学徒以外の人は余り馴染みがないと思うので、初めは苦労するかもだが、慣れるととても良い。

    勉強になった問題(抜粋)

    例のごとく、勉強になった問題を抜粋する。

    全問題とちょっとした調べ物のリンクなどは、前と同様にgithubに残した。

    github.com

    81. a=[1,2,3,4,5,...,14]とした時、R = [[1,2,3,4],[2.3.4.5].[3.4.5.6],...,[11,12,13,14]]を作る方法

    普通にゴリ押しでも行けたが、関数を知っていると一発で終わりとても良いので、メモしておく。

    自分の回答

    a = np.arange(1,15,dtype=np.int32)
    b = np.zeros((11,4),dtype=np.int32)
    for i in range(11):
        b[i] = a[i:i+4]
    print(b)
    

    模範解答

    np.lib.stride_tricksを使う stride_trickの説明ページ

    Z = np.arange(1,15,dtype=np.uint32)
    R = np.lib.stride_tricks.as_strided(Z,(11,4),(4,4))
    print(R)
    

    共に出力は以下

    [[ 1  2  3  4]
     [ 2  3  4  5]
     [ 3  4  5  6]
     [ 4  5  6  7]
     [ 5  6  7  8]
     [ 6  7  8  9]
     [ 7  8  9 10]
     [ 8  9 10 11]
     [ 9 10 11 12]
     [10 11 12 13]
     [11 12 13 14]]

    82. 行列のランクを計算

    線形代数パッケージnp.linalgを使う。linalgの説明ページ

    linalgを使うと、例えば以下の様なことが出来る

    • 特異値分解 : np.linalg.svd(A)
    • ノルムを出す : np.linalg.norm(a)
    • 行列式を出す : np.linalg.det(A)
    • 方程式を解く : np.linalg.solve(a,b)
    • 逆行列を出す : np.linalg.inv(A)
    • 固有値を出す : np.linalg.eig(A)
    Z = np.random.uniform(0,1,(10,10))
    U, S, V = np.linalg.svd(Z) # Singular Value Decomposition
    #np.linalg.matrix_rank(Z) も可
    rank = np.sum(S > 1e-10)
    

    83. 行列の中で最も使われている値を見つける

    このbincount()というやつが結構便利。どの値が何個あるかを数えてくれる。 また、重複を避けた配列を返すものはnp.unique(a)というのが便利だったりする

    A = np.random.randint(0,5,10)
    print(A)
    print(np.bincount(A).argmax())
    #unique
    print(np.unique(A))
    

    出力

    [1 1 2 0 3 2 2 1 4 0]
    1
    [0 1 2 3 4]

    95. 整数行列をバイナリ表現で表す

    2進数に直す方法。ゴリ押しでもいけるが、模範解答がエレガントだったので、採用。

    &演算子がバイナリの演算子なのとreshap(-1,1)で縦ベクトルに出来るのを利用する。

    I = np.array([0, 1, 2, 3, 15, 16, 32, 64, 127])
    print(I)
    # print(I.reshape(-1,1))
    # print([3]&(2**np.arange(8))!=0)
    B = ((I.reshape(-1,1) & (2**np.arange(8))) != 0).astype(int)
    print(B[:,::-1])
    

    出力

    [  0   1   2   3  15  16  32  64 127]
    [[0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 0 1]
     [0 0 0 0 0 0 1 0]
     [0 0 0 0 0 0 1 1]
     [0 0 0 0 1 1 1 1]
     [0 0 0 1 0 0 0 0]
     [0 0 1 0 0 0 0 0]
     [0 1 0 0 0 0 0 0]
     [0 1 1 1 1 1 1 1]]

    まとめ

    2日に渡って、numpyの勉強をしてきた。 結構網羅的だったように思う。知らない関数もだいぶ多かったし、いい勉強になった。

    何よりも、数時間配列を作ってはいじり作ってはいじりを繰り返したお陰で、ささっと配列を作れるよぅになった気がする。これはもう訓練なので、一度こういう時間を儲けられたのはとても良かった。

    というわけでとりあえずnumpyの総ざらいは終わり。次回以降はpandasscikit-learnjupyterの復習・勉強をしていく。それではまた明日!

    *1:和訳が意味不明のものもあると思います、スミマセン。実行してみて何がいいたいか確かめてくださいw

    *2:88.ライフゲームとか

    PROCRASIST