読者です 読者をやめる 読者になる 読者になる

IT技術者のAIに関する雑記

AI関連のMicrosoft技術やデータサイエンスの話をメインに。

Microsoft Translatorを使ったテキスト機械翻訳 -基本編

Microsoft Translatorは機械翻訳のためのSaaS APIです。現在はAzure Cognitive Servicesという人工知能サービスAPI群の一つとして提供されています。

以前はAzure DataMarketというサービスで提供されていましたが、DataMarketは2017年3月31日で提供終了、DataMarketでのMicrosoft Translatorへのアクセスも2017年4月30日で終了します。

このサービスはテキスト翻訳のためのTranslator Text APIと音声翻訳のためのTranslator Speech APIを提供していますが、ここではTranslator Text APIの特徴と基本的な使い方を紹介しようと思います。

特徴

  • クラウドベースのAPIとして提供されているため、様々なアプリケーションに容易に組み込むことが可能
  • Bing翻訳やOffice、Skype Translatorにも組み込まれており、アプリケーションとしてすぐ使えるものとしても提供している
  • 日本語を含む60言語 (2017年2月) に対応している

統計的機械翻訳は辞書登録だけで翻訳品質を上げることは難しく、対訳文 (日本語の文章と英語の文章のペアのような) で学習させることが必要と言われていますが、深層学習型機械翻訳は統計的機械翻訳よりもはるかに高い翻訳品質を実現するものとして期待されています。

まずは試してみる

Microsoft Translatorを簡単に試してみるには、Bing翻訳を使うのがてっとり早いです。「category」GETパラメータで「generalnn」と指定するだけで、アルゴリズムが切り替わります。

例としてMicrosoft Translatorの製品ページにある以下の文章を訳してみます。

Microsoft Translator Text API, part of the Microsoft Cognitive Services API collection, is a cloud-based machine translation service supporting multiple languages that reach more than 95% of world's gross domestic product (GDP). Translator can be used to build applications, websites, tools, or any solution requiring multilanguage support.


統計的機械翻訳の場合:
Microsoft Translator テキスト APIMicrosoft 認知サービス API コレクションの一部は、世界の国内総生産 (GDP) の 95% 以上に達する複数の言語のサポート クラウド ベースの機械翻訳サービスです。アプリケーション、web サイト、ツール、または複数言語のサポートを必要とする任意のソリューションを構築するトランスレーターを使用することができます。

f:id:masakykato:20170214164711p:plain

個人目的として使う分にはよさそうですが、正式文書で使うには学習したほうがよさそうです。


深層学習型機械翻訳の場合:
microsoft 認知サービス api コレクションの一部である Microsoft Translator テキスト api は、世界の国内総生産 (gdp) の 95% 以上に達する複数の言語をサポートするクラウドベースの機械翻訳サービスです。トランスレータは、アプリケーション、web サイト、ツール、または多言語サポートを必要とする任意のソリューションを構築するために使用できます

f:id:masakykato:20170214165013p:plain

自然な文書になってますね。アルゴリズムを変えるだけで、ここまで訳に変化が出ています。

Translator Text APIを使ってみる

それでは本題であるTranslator Text APIの使い方を紹介していきます。Translator Text APIでもアルゴリズムの変更は簡単にできますので、合わせて紹介していきます。
なお、Translator Text APIにはREST、SOAPAJAXの3つの種類のAPIがありますが、ここではREST APIを使用します。

Translator Text APIを使用するには、以下の3つの手順が必要になります。

  • Subscription Keyの取得
    • 認証トークンを取得するために必要になります。認証トークンの取得方法はAzure DataMarketで提供されていた時点のものから変更されていますので、ご注意ください。
  • 認証サービスの呼出
    • 認証サービスを呼び出し、Subscription Keyから認証トークンを生成します。
  • 翻訳サービスの呼出
    • 認証サービスを呼び出し、文章を翻訳します。

以下では、これらの基本的な使用方法について記述していきます。

Subscription Keyの取得

まずはAzureが使用できるようにしてください。こちらについては各種情報があると思いますので、そちらをご参考に。

Azureポータルにログインしたら、左のメニューから「新規」→「Intelligence + analytics」→「Cognitive Services APIs」を選択します。
f:id:masakykato:20170223175502p:plain

Cognitive Servicesアカウントの作成画面で、以下の項目を入力し、アカウントを作成します。

  • Account name:アカウントの表示名を入力
  • サブスクリプション:利用可能なサブスクリプションを選択
  • API Type:Translator Text API
  • 価格レベル:任意の価格レベル(F0を選択すると、200万文字まで利用可能な無料版となります)
  • Resource Group:任意で作成もしくは既存のリソースグループを選択
  • Resource group location:自分のいる場所に近いロケーションを選択

f:id:masakykato:20170223180853p:plain

アカウントが作成されたら、作成したアカウントを選択し、「Keys」メニューを選択して「KEY 1」を確認します。こちらがSubscription Keyになりますので、記録しておいてください。
f:id:masakykato:20170223180746p:plain

Subscription Keyの取得方法については、以下のドキュメントを参考にしてください。
https://www.microsoft.com/ja-jp/translator/getstarted.aspx

認証サービスの呼出

Translator Text APIの呼出は、認証トークンによる認証が必要です。認証トークンは複数の認証サービスの呼出に対して再利用できますが、有効時間は10分となっているため、認証サービスを呼び出す際には有効時間を考慮する必要があります。

認証トークンは、認証サービスに対して「Ocp-Apim-Subscription-Key」ヘッダーにSubscription Keyをセットしてリクエストするか、「Subscription-Key」GETパラメータにSubscription Keyをセットしてリクエストすることで取得できます。認証サービスのURLは以下になります。
認証サービス:https://api.cognitive.microsoft.com/sts/v1.0/issueToken


翻訳サービスの呼出時には、認証トークンをBearトークンとしてヘッダーにセットして渡します。


以下のコードは、認証サービスを呼び出すためのC#のサンプルクラスになります。

public class AzureAuthToken
    {
        // 認証サービスのURL
        private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
        // Subscription Keyを渡すときの要求ヘッダー
        private const string OcpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key";
        // 認証トークンの有効時間:5分
        private static readonly TimeSpan TokenCacheDuration = new TimeSpan(0, 5, 0);
        // 有効な認証トークンを格納
        private string storedTokenValue = string.Empty;
        // 有効な認証トークンの取得時間
        private DateTime storedTokenTime = DateTime.MinValue;

        /*
          Subscription Keyの取得
        */
        public string SubscriptionKey { get; private set; } = string.Empty;

        /*
         認証サービスへのリクエスト時のHTTPステータスコードの取得
        */
        public HttpStatusCode RequestStatusCode { get; private set; }

        /*
          認証トークンを取得するためのクライアント作成
          <param name="key">Subscription Key</param>
        */
        public AzureAuthToken(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key", "Subscription Keyが必要です。");
            }

            this.SubscriptionKey = key;
            this.RequestStatusCode = HttpStatusCode.InternalServerError;
        }

        /*
          Subscriptionに紐づいた認証トークンの取得 (非同期)
        */
        public async Task<string> GetAccessTokenAsync()
        {
            if (SubscriptionKey == string.Empty) return string.Empty;

            // 認証トークンが有効な場合は有効な認証トークンを返す
            if ((DateTime.Now - storedTokenTime) < TokenCacheDuration)
            {
                return storedTokenValue;
            }

            // 認証トークンを取得
            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage())
            {
                request.Method = HttpMethod.Post;
                request.RequestUri = ServiceUrl;
                request.Content = new StringContent(string.Empty);
                request.Headers.TryAddWithoutValidation(OcpApimSubscriptionKeyHeader, this.SubscriptionKey);
                client.Timeout = TimeSpan.FromSeconds(2);
                var response = await client.SendAsync(request);
                this.RequestStatusCode = response.StatusCode;
                response.EnsureSuccessStatusCode();
                var token = await response.Content.ReadAsStringAsync();
                storedTokenTime = DateTime.Now;
                storedTokenValue = "Bearer " + token;
                return storedTokenValue;
            }
        }

        /*
          Subscriptionに紐づいた認証トークンの取得 (同期)
        */
        public string GetAccessToken()
        {
            // 認証トークンが有効な場合は有効な認証トークンを返す
            if ((DateTime.Now - storedTokenTime) < TokenCacheDuration)
            {
                return storedTokenValue;
            }

            // 認証トークンを取得
            string accessToken = null;

            var task = Task.Run(async () =>
            {
                accessToken = await GetAccessTokenAsync();
            });

            while (!task.IsCompleted)
            {
                System.Threading.Thread.Yield();
            }

            if (task.IsFaulted)
            {
                throw task.Exception;
            }
            else if (task.IsCanceled)
            {
                throw new Exception("トークンの取得がタイムアウトしました。");
            }

            return accessToken;
        }
    }


認証サービスの使用方法については、以下のドキュメントを参考にしてください。
http://docs.microsofttranslator.com/oauth-token.html

翻訳サービスの呼出

翻訳サービスは
http://api.microsofttranslator.com/v2/Http.svc/
こちらのURLで提供されています。複数のサービスが使用できますが、最も単純な翻訳サービスは以下になります。
http://api.microsofttranslator.com/v2/http.svc/translate

このサービスに対して以下のGETパラメータを指定してリクエストすることで、翻訳が実行されます。

  • text
    • 翻訳対象文書
  • appId
  • from
    • 翻訳元言語
  • to
    • 翻訳先言語
  • category

以下のコードは、翻訳サービスを呼び出すためのC#のサンプルクラスになります。

public class CallTranslator
    {
        // Subscription Key
        private const string SubscriptionKey = "<Subscription Keyを入力>";
        // TranslateサービスのURL
        private static readonly Uri ServiceUrl = new Uri("http://api.microsofttranslator.com/v2/Http.svc/");

        /*
          翻訳の実施
          <param name="textToTransform">翻訳対象文書</param>
          <param name="fromLangCode">翻訳元言語</param>
          <param name="toLangCode">翻訳先言語</param>
          <param name="domain">翻訳ドメイン</param>
        */
        public string TranslateMethod(string textToTransform, string fromLangCode, string toLangCode, string domain)
        {
            // トークンの取得
            var authTokenSource = new AzureAuthToken(SubscriptionKey);
            var token = string.Empty;
            token = authTokenSource.GetAccessToken();

            // 翻訳サービスの呼出
            string uri = ServiceUrl + "Translate?text=" + System.Web.HttpUtility.UrlEncode(textToTransform) 
                + "&appid=" + token + "&from=" + fromLangCode + "&to=" + toLangCode + "&category=" + domain;
            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
            WebResponse response = null;
            string translatedText = null;

            try
            {
                response = httpWebRequest.GetResponse();
                using (Stream stream = response.GetResponseStream())
                {
                    System.Runtime.Serialization.DataContractSerializer dcs = new System.Runtime.Serialization.DataContractSerializer(Type.GetType("System.String"));
                    translatedText = (string)dcs.ReadObject(stream);
                }
            } catch (Exception ex){
                Console.WriteLine("message: " + ex.Message);
                Console.WriteLine("stack trace: ");
                Console.WriteLine(ex.StackTrace);
                Console.ReadLine();
            }

            return translatedText;
        }


翻訳サービスの使用方法については、以下のドキュメントを参考にしてください。こちらではその他のサービスの使用方法についても記載されています。
http://docs.microsofttranslator.com/text-translate.html#!/default/get_Translate


上記のコードを使用したサンプルコードをGitHubで公開していますので、そちらも参考にしてみてください。サンプルコードでは、翻訳対象文書から翻訳元言語を自動識別するサービスも使っています。
https://github.com/masakykato0526/SimpleMicrosoftTranslator

私訳:NumPy Quickstart tutorial

機械学習 深層学習 Python NumPy

Pythonの数学計算ライブラリであるNumPyは深層学習を含めた機械学習に必須のライブラリですが、日本語情報があまり多くないため、Quickstart tutorialを翻訳してみました。

あくまで私訳ですので、原文と合わせてみて頂ければと思います。

原文:

https://docs.scipy.org/doc/numpy-dev/user/quickstart.html

前提

このチュートリアルを読む前に、Pythonについてある程度知っておいたほうがよいです。記憶を蘇らせたいのであれば、Python tutorial を参照してください。

このチュートリアルのサンプルを実行したい場合は、自分のコンピューターにソフトウエアをインストールしておく必要があります。手順については、http://scipy.org/install.htmlを参照してください。

基礎

NumPy の主要なオブジェクトは、同次の多次元配列です。これは(通常は数字の)要素のテーブルであり、全て同じ型、正の整数のタプルでインデックス付けされています。NumPyでは、次元は軸、軸の数はランクと呼ばれます。

例えば、三次元空間における点の座標「[1, 2, 1]」は、軸が1つであるためランク1の配列になります。軸の長さは3になります。以下の図の例では、配列はランク2 (2次元) となります。最初の次元 (軸) の長さは2、2つ目の次元の長さは3になります。

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

NumPyの配列クラスは「ndarray」と呼ばれています。これは「array」というエイリアスとしても知られています。標準のPythonライブラリの「array.array」クラスと同じではないということに注意してください。「array.array」クラスは一次元配列しか扱えませんし、機能も不足しています。「ndarray」クラスの重要な属性を以下に挙げておきます:

ndarray.ndim

配列の軸 (次元) 数です。Pythonの世界では、次元数はランクと呼ばれます。

ndarray.shape

配列に含まれる次元です。これは各次元の配列の大きさを表す整数のタプルになります。n行m列の行列の場合「shape」は「(n,m)」となります。「shape」タプルの長さはランクもしくは次元数である「ndim」の値となります。

ndarray.size

配列に含まれる要素の総数です。これは「shape」の要素の積と等しくなります。

ndarray.dtype

配列に含まれる要素の型を記述するオブジェクトです。dtypeは標準Pythonの型を用いて作成もしくは指定することができます。それに加えて、NumPyはnumpy.int32, numpy.int16, numpy.float64等の独自の型を提供しています。

ndarray.itemsize

配列に含まれる各要素のバイト単位の大きさです。例えば、「float64」型の要素からなる配列では、「itemsize」は8 (=64/8) となり、「complex32」型では4 (=32/8) となります。これはndarray.dtype.itemsizeと同値になります

ndarray.data

配列の実際の要素を収容しているバッファです。配列の要素にはインデックス機能を用いてアクセスするので、通常この属性を使う必要はないでしょう。

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<type 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<type 'numpy.ndarray'>

配列の作成

配列を作成するには、何通りかの方法があります。

例えば、通常のPythonのリストや「array」関数を使用たタプルから配列を作成することができます。作成された配列の型は、シーケンスの要素の型から推定されます。

>>> import numpy as np
>>> a = np.array([2,3,4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')

「array」関数を呼ぶときに、引数として一つの数値のリストを渡すのでなく複数の数値の引数を渡してエラーになるというのは、よくある間違いです。

>>> a = np.array(1,2,3,4)    # WRONG
>>> a = np.array([1,2,3,4])  # RIGHT

「array」は、シーケンスのシーケンスを2次元配列に、シーケンスのシーケンスのシーケンスを3次元配列に、といった形で変換します。

>>> b = np.array([(1.5,2,3), (4,5,6)])
>>> b
array([[ 1.5,  2. ,  3. ],
       [ 4. ,  5. ,  6. ]])

 配列の作成時に、明示的に型を指定することもできます:

>>> c = np.array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[ 1.+0.j,  2.+0.j],
       [ 3.+0.j,  4.+0.j]])

配列の要素が最初は分からず、大きさだけが分かっているということはよくあることです。そのためNumPyは、場所取り用の初期値を入れて配列を作成する関数を提供しています。これにより、コストのかかる演算である配列の拡張の必要性を最小限に抑えられます。

「zeros」関数はすべて0の値を持つ配列を、「ones」関数はすべて1の値を持つ配列を、「empty」はメモリ状態に依存したランダムな初期値を持つ配列を作成します。デフォルトでは、作成された配列のdtypeは「float64」になります。

>>> np.zeros( (3,4) )
array([[ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.]])
>>> np.ones( (2,3,4), dtype=np.int16 )                # dtype can also be specified
array([[[ 1, 1, 1, 1],
        [ 1, 1, 1, 1],
        [ 1, 1, 1, 1]],
       [[ 1, 1, 1, 1],
        [ 1, 1, 1, 1],
        [ 1, 1, 1, 1]]], dtype=int16)
>>> np.empty( (2,3) )                                 # uninitialized, output may vary
array([[  3.73603959e-262,   6.02658058e-154,   6.55490914e-260],
       [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]])

数値のシーケンスを作成するために、NumPyは、「range」に似た、リストの代わりに配列を返す関数を提供しています。

>>> np.arange( 10, 30, 5 )
array([10, 15, 20, 25])
>>> np.arange( 0, 2, 0.3 )                 # it accepts float arguments
array([ 0. ,  0.3,  0.6,  0.9,  1.2,  1.5,  1.8])

「arange」が浮動小数点数を引数として使用する場合、浮動小数の精度が有限であるために、一般的には得られる要素数を予測することはできません。このため、stepの代わりに必要な要素数を引数として渡せる「linespace」関数を使うほうが通常望ましいです:

>>> from numpy import pi
>>> np.linspace( 0, 2, 9 )                 # 9 numbers from 0 to 2
array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ,  1.25,  1.5 ,  1.75,  2.  ])
>>> x = np.linspace( 0, 2*pi, 100 )        # useful to evaluate function at lots of points
>>> f = np.sin(x)
関連項目

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.rand, numpy.random.randn, fromfunction,fromfile 

配列の表示

配列を表示する時、NumPyはネストされたリストと同じような方法で表示しますが、以下のレイアウトになります:

  • 最後の軸は左から右へ表示されます。
  • 最後から2番目の軸は、上から下へ表示されます。
  • その他の軸も上から下へ表示されますが、各スライスは空行で区切られます。

したがって、1次元配列は行のように、2次元配列は行列のように、3次元配列は行列のリストのように表示されます。

>>> a = np.arange(6)                         # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4,3)           # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2,3,4)         # 3d array
>>> print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]
 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

「reshape」の詳細については、「形状の操作」の項目を参照してください。

配列が大きすぎて表示できない場合には、NumPyは自動的に配列の中心部分を省略して端部分のみ表示します。

>>> print(np.arange(10000))
[   0    1    2 ..., 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100,100))
[[   0    1    2 ...,   97   98   99]
 [ 100  101  102 ...,  197  198  199]
 [ 200  201  202 ...,  297  298  299]
 ...,
 [9700 9701 9702 ..., 9797 9798 9799]
 [9800 9801 9802 ..., 9897 9898 9899]
 [9900 9901 9902 ..., 9997 9998 9999]]

この動作を無効にしてNumPyに配列全体を表示させたい場合には、「set_printoptions」を使用して表示オプションを変更することができます。

>>> np.set_printoptions(threshold='nan')

基本的な演算

配列に対する算術演算は要素ごとに適用されます。新しい配列が作成され、演算結果が入力されます。

>>> a = np.array( [20,30,40,50] )
>>> b = np.arange( 4 )
>>> b
array([0, 1, 2, 3])
>>> c = a-b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10*np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a<35
array([ True, True, False, False], dtype=bool)

多くの行列言語と異なり、NumPyでは乗算演算子「*」は要素ごとの演算を行います。行列の積は「dot」関数もしくはメソッドを使用して実行することができます:

>>> A = np.array( [[1,1],
...             [0,1]] )
>>> B = np.array( [[2,0],
...             [3,4]] )
>>> A*B                         # elementwise product
array([[2, 0],
       [0, 4]])
>>> A.dot(B)                    # matrix product
array([[5, 4],
       [3, 4]])
>>> np.dot(A, B)                # another matrix product
array([[5, 4],
       [3, 4]])

「+=」や「*=」のようないくつかの演算では、新しい行列を作成せずに、既存の配列を変更します。

>>> a = np.ones((2,3), dtype=int)
>>> b = np.random.random((2,3))
>>> a *= 3
>>> a
array([[3, 3, 3],
       [3, 3, 3]])
>>> b += a
>>> b
array([[ 3.417022  ,  3.72032449,  3.00011437],
       [ 3.30233257,  3.14675589,  3.09233859]])
>>> a += b                  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

 型が異なる行列に対して演算する場合、演算結果の配列の型は、より一般度の高い型もしくはより精度の高い型(アップキャストとして知られている動作)になります。

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0,pi,3)
>>> b.dtype.name
'float64'
>>> c = a+b
>>> c
array([ 1.        ,  2.57079633,  4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c*1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
       -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'

配列に含まれる全要素の合計計算のような単項演算子の多くは、「ndarray」クラスのメソッドとして実装されています。

>>> a = np.random.random((2,3))
>>> a
array([[ 0.18626021,  0.34556073,  0.39676747],
       [ 0.53881673,  0.41919451,  0.6852195 ]])
>>> a.sum()
2.5718191614547998
>>> a.min()
0.1862602113776709
>>> a.max()
0.6852195003967595

これらの演算子は、デフォルトでは配列の形状にかかわらず、数値のリストのように配列に適用されます。しかし、「axis」パラメータを指定することで、指定された配列の軸に沿って演算を実施することができます:

>>> b = np.arange(12).reshape(3,4)
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)                            # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)                            # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)                         # cumulative sum along each row
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])

ユニバーサル関数

NumPyは、sin、cos、expのようなよく知られている数学関数も提供しています。NumPyでは、これらは「ユニバーサル関数(ufunc)」と呼ばれています。Numpyでは、これらの関数は配列の要素ごとに演算を行い、出力として新規の行列が作成されます。

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([ 1.        ,  2.71828183,  7.3890561 ])
>>> np.sqrt(B)
array([ 0.        ,  1.        ,  1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([ 2.,  0.,  6.])
関連項目

all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff,dot, floor, inner, inv, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace,transpose, var, vdot, vectorize, where

インデックス付け、スライス、イテレート

一次元配列に対しては、リストや他のPythonのシーケンスと同じように、インデックス付けやスライス、イテレートを行うことができます。

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
>>> a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729])
>>> a[ : :-1]                                 # reversed a
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000])
>>> for i in a:
...     print(i**(1/3.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

多次元配列は、軸ごとに一つのインデックスを持つことができます。これらのインデックスは、カンマ区切りのタプルで与えられます:

>>> def f(x,y):
...     return 10*x+y
...
>>> b = np.fromfunction(f,(5,4),dtype=int)
>>> b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
>>> b[2,3]
23
>>> b[0:5, 1]                       # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[ : ,1]                        # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, : ]                      # each column in the second and third row of b
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

軸数より少ないインデックスが与えられた場合には、不足分のインデックスは完全なスライス「:」と見なされます。

>>> b[-1]                                  # the last row. Equivalent to b[-1,:]
array([40, 41, 42, 43])

「b[i]」の括弧の中の式は、残りの軸を表現するために必要な分の「:」で埋められているものとして扱われます。NumPyでは、これを「b[i,...]」のようにドットを用いて記述することもできます。

ドット「...」は、完全にインデックス付けされたタプルを生成するのに必要な分のコロンを表しています。例えば、「x」がランク5の配列(つまり5軸)の場合、以下の配列は等価になります。

  • x[1,2,...]はx[1,2,:,:,:]
  • x[...,3]はx[:,:,:,:,3]
  • x[4,...,5,:]はx[4,:,:,5,:]
>>> c = np.array( [[[  0,  1,  2],               # a 3D array (two stacked 2D arrays)
...                 [ 10, 12, 13]],
...                [[100,101,102],
...                 [110,112,113]]])
>>> c.shape
(2, 2, 3)
>>> c[1,...]                                   # same as c[1,:,:] or c[1]
array([[100, 101, 102],
       [110, 112, 113]])
>>> c[...,2]                                   # same as c[:,:,2]
array([[  2,  13],
       [102, 113]])

多次元配列に対するイテレートは、最初の軸に対して行われます:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

一方で、配列の各要素に対して演算を行いたい場合には、配列の全要素に対して適用されるイテレータとなる「flat」属性を使用することができます:

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43
関連項目

Indexing, Indexing (reference), newaxis, ndenumerate, indices

形状の操作

配列の形状の変更

配列は、各軸に沿った要素数によって与えられる形状を持ちます:

>>> a = np.floor(10*np.random.random((3,4)))
>>> a
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
>>> a.shape
(3, 4)

配列の形状は、様々なコマンドで変更することができます:

>>> a.ravel()  # returns the array, flattened
array([ 2.,  8.,  0.,  6.,  4.,  5.,  1.,  1.,  8.,  9.,  3.,  6.])
>>> a.reshape(6,2)  # returns the array with a modified shape
array([[ 2.,  8.],
       [ 0.,  6.],
       [ 4.,  5.],
       [ 1.,  1.],
       [ 8.,  9.],
       [ 3.,  6.]])
>>> a.T  # returns the array, transposed
array([[ 2.,  4.,  8.],
       [ 8.,  5.,  9.],
       [ 0.,  1.,  3.],
       [ 6.,  1.,  6.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)

ravel()の結果として得られる配列の要素の順番は、通常は”Cスタイル”つまり一番右のインデックスが”最初の変わる”ので、a[0,0]の次の要素がa[0,1]になります。配列が別の形状に再整形されても、配列は"Cスタイル"として扱われます。NumPyは、通常はこの順番で格納される配列を作成するため、ravel()は普通は引数をコピーする必要がありませんが、配列が別の配列をスライスして作成されたり、一般的ではないオプションを使って作成された場合には、引数をコピーする必要があることがあります。ravel()とreshape() 関数 は、オプションの引数を使うことでFORTRANスタイルの配列、つまり一番左のインデックスが最初に変わるように指定することもできます。

reshape関数は、変更後の形状で引数を返す一方で、ndarray.resizeメソッドは配列そのものを変更します:

>>> a
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
>>> a.resize((2,6))
>>> a
array([[ 2.,  8.,  0.,  6.,  4.,  5.],
       [ 1.,  1.,  8.,  9.,  3.,  6.]])

再整形の演算時に次元が-1で与えられた場合、他の次元は自動的に計算されます。

>>> a.reshape(3,-1)
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
関連項目

ndarray.shape, reshape, resize, ravel

配列の積み上げ

複数の配列を、さまざまな軸に沿って積み上げることができます:

>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 8.,  8.],
       [ 0.,  0.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1.,  8.],
       [ 0.,  4.]])
>>> np.vstack((a,b))
array([[ 8.,  8.],
       [ 0.,  0.],
       [ 1.,  8.],
       [ 0.,  4.]])
>>> np.hstack((a,b))
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])

column_stack関数は、1次元配列を2次元配列に積み上げます。1次元配列の場合は、vstackと等価になります:

>>> from numpy import newaxis
>>> np.column_stack((a,b))   # With 2D arrays
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])
>>> a = np.array([4.,2.])
>>> b = np.array([2.,8.])
>>> a[:,newaxis]  # This allows to have a 2D columns vector
array([[ 4.],
       [ 2.]])
>>> np.column_stack((a[:,newaxis],b[:,newaxis]))
array([[ 4.,  2.],
       [ 2.,  8.]])
>>> np.vstack((a[:,newaxis],b[:,newaxis])) # The behavior of vstack is different
array([[ 4.],
       [ 2.],
       [ 2.],
       [ 8.]])

2次元以上の配列の場合には、hstackは2つ目の軸に沿って積み上げ、vstackは最初の軸に沿って積み上げます。concatenateは連結する軸の番号をオプションの引数として指定することができます。

注釈

複雑な場合においては、ある軸に沿って数字を積み上げることで配列を作成するr_とc_が役に立ちます。これは、範囲を表すリテラルである「:」を使うことができます:

>>> np.r_[1:4,0,4]
array([1, 2, 3, 0, 4])

配列を引数として使う場合、r_とc_はvstackとhstackのデフォルトの動作に似てますが、連結する軸の番号をオプションの引数として指定することができます。

関連項目

hstack, vstack, column_stack, concatenate, c_, r_

配列を複数の小さな配列に分割

hsplitを使用すると、同じ形状の配列を返す数を指定することで、もしくは分割させる後ろの列を指定することで、配列を水平軸に沿って分割することができます:

>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[ 9.,  5.,  6.,  3.,  6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
       [ 1.,  4.,  9.,  2.,  2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])
>>> np.hsplit(a,3)   # Split a into 3
[array([[ 9.,  5.,  6.,  3.],
       [ 1.,  4.,  9.,  2.]]), array([[ 6.,  8.,  0.,  7.],
       [ 2.,  1.,  0.,  6.]]), array([[ 9.,  7.,  2.,  7.],
       [ 2.,  2.,  4.,  0.]])]
>>> np.hsplit(a,(3,4))   # Split a after the third and the fourth column
[array([[ 9.,  5.,  6.],
       [ 1.,  4.,  9.]]), array([[ 3.],
       [ 2.]]), array([[ 6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
       [ 2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])]

vsplitは垂直軸に沿って分割し、array_splitはどの軸に沿って分割するかを指定することができます。

コピーとビュー

配列の演算や操作時には、データを新しい配列にコピーする場合もありますし、コピーしない場合もあります。これは時として初心者を混乱させる元となります。これには3つのケースがあります:

コピー無し

単純な代入では、配列オブジェクトもデータもコピーされません。

>>> a = np.arange(12)
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True
>>> b.shape = 3,4    # changes the shape of a
>>> a.shape
(3, 4)

Pythonは変更可能なオブジェクトを参照渡しするため、関数呼び出しではコピーは行いません。

>>> def f(x):
...     print(id(x))
...
>>> id(a)                           # id is a unique identifier of an object
148293216
>>> f(a)
148293216

ビューとシャローコピー

異なる配列オブジェクトが、同じデータを共有することができます。「view」メソッドは、同じデータを参照する新しい配列オブジェクトを作成します。

>>> c = a.view()
>>> c is a
False
>>> c.base is a                        # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6                      # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0,4] = 1234                      # a's data changes
>>> a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

配列をスライスすると、ビューが返されます:

>>> s = a[ : , 1:3]     # spaces added for clarity; could also be written "s = a[:,1:3]"
>>> s[:] = 10           # s[:] is a view of s. Note the difference between s=10 and s[:]=10
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

ディープコピー

「copy」メソッドは、配列とそのデータの完全なコピーを作ります。

>>> d = a.copy()                          # a new array object with new data is created
>>> d is a
False
>>> d.base is a                           # d doesn't share anything with a
False
>>> d[0,0] = 9999
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

関数とメソッド概要

ここでは、役に立つNumPyの関数とメソッドをカテゴリ順にリストします。完全なリストはhttps://docs.scipy.org/doc/numpy-dev/reference/routines.html#routinesを参照してください。

Array Creation

arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r,zeros, zeros_like

Conversions

ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

Manipulations

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape,resize, squeeze, swapaxes, take, transpose, vsplit, vstack

Questions

all, any, nonzero, where

Ordering

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

Operations

choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

Basic Statistics

cov, mean, std, var

Basic Linear Algebra

cross, dot, outer, linalg.svd, vdot

応用

ブロードキャストルール

ブロードキャストにより、ユニバーサル関数は、正確には同じ形状ではない入力でも意味のある方法で扱うことができます。

ブロードキャストの1つ目のルールは、すべての入力は配列が同じ次元数を持たない場合、すべての配列が同じ次元数を持つまで、配列の先頭に「1」を穴埋めするものです。

ブロードキャストの2つ目のルールでは、特定の次元に沿った大きさが1である配列は、その次元に沿って最大の形状を持つ配列の大きさを持つものとして振舞うことを保証します。配列の要素の値は、「ブロードキャスト」配列の次元に沿って同じ値であるとみなされます。

ブロードキャストルールが適用されると、すべての配列の大きさは一致することになります。詳細は https://docs.scipy.org/doc/numpy-dev/user/basics.broadcasting.htmlに記載されています。

役に立つインデックス付けとインデックスのコツ

NumPyは、通常のPythonシーケンスよりも多くのインデックス機能を提供しています。整数やスライスによるインデックス付けに加えて、これまでに見てきたように、整数配列やブーリアン配列でも配列にインデックス付けすることができます。

インデックス配列によるインデックス付け

>>> a = np.arange(12)**2                       # the first 12 square numbers
>>> i = np.array( [ 1,1,3,8,5 ] )              # an array of indices
>>> a[i]                                       # the elements of a at the positions i
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] )      # a bidimensional array of indices
>>> a[j]                                       # the same shape as j
array([[ 9, 16],
       [81, 49]])

インデックス配列「a」が多次元配列の時、単一配列のインデックスは「a」の最初の次元を参照します。以下の例は、ラベルの画像をパレットを用いてカラー画像に変換することでこの振る舞いを表します。

>>> palette = np.array( [ [0,0,0],                # black
...                       [255,0,0],              # red
...                       [0,255,0],              # green
...                       [0,0,255],              # blue
...                       [255,255,255] ] )       # white
>>> image = np.array( [ [ 0, 1, 2, 0 ],           # each value corresponds to a color in the palette
...                     [ 0, 3, 4, 0 ]  ] )
>>> palette[image]                            # the (2,4,3) color image
array([[[  0,   0,   0],
        [255,   0,   0],
        [  0, 255,   0],
        [  0,   0,   0]],
       [[  0,   0,   0],
        [  0,   0, 255],
        [255, 255, 255],
        [  0,   0,   0]]])

1次元以上の配列に対してインデックスを与えることもできます。各次元に対するインデックス配列は、同じ形状である必要があります。

>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> i = np.array( [ [0,1],                        # indices for the first dim of a
...                 [1,2] ] )
>>> j = np.array( [ [2,1],                        # indices for the second dim
...                 [3,3] ] )
>>>
>>> a[i,j]                                     # i and j must have equal shape
array([[ 2,  5],
       [ 7, 11]])
>>>
>>> a[i,2]
array([[ 2,  6],
       [ 6, 10]])
>>>
>>> a[:,j]                                     # i.e., a[ : , j]
array([[[ 2,  1],
        [ 3,  3]],
       [[ 6,  5],
        [ 7,  7]],
       [[10,  9],
        [11, 11]]])

通常は、「i」と「j」をシーケンス (つまりリスト) に入れて、リストでインデックス付けを行います。

>>> l = [i,j]
>>> a[l]                                       # equivalent to a[i,j]
array([[ 2,  5],
       [ 7, 11]])

しかし、「i」と「j」を配列に入れると、同じことはできません。なぜならこの配列は、「a」の最初の次元をインデックス付けするものとして解釈されるからです。

>>> s = np.array( [i,j] )
>>> a[s]                                       # not what we want
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: index (3) out of range (0<=index<=2) in dimension 0
>>>
>>> a[tuple(s)]                                # same as a[i,j]
array([[ 2,  5],
       [ 7, 11]])

配列でインデックス付けする他のよくある使い方は、時間依存連続データの最大値検索です。

>>> time = np.linspace(20, 145, 5)                 # time scale
>>> data = np.sin(np.arange(20)).reshape(5,4)      # 4 time-dependent series
>>> time
array([  20.  ,   51.25,   82.5 ,  113.75,  145.  ])
>>> data
array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
>>>
>>> ind = data.argmax(axis=0)                   # index of the maxima for each series
>>> ind
array([2, 0, 3, 1])
>>>
>>> time_max = time[ ind]                       # times corresponding to the maxima
>>>
>>> data_max = data[ind, xrange(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
>>>
>>> time_max
array([  82.5 ,   20.  ,  113.75,   51.25])
>>> data_max
array([ 0.98935825,  0.84147098,  0.99060736,  0.6569866 ])
>>>
>>> np.all(data_max == data.max(axis=0))
True

代入する対象として、配列でインデックス付けすることにも使用できます。

>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])

一方で、インデックスのリストに繰り返しが含まれている場合、代入は複数回行われ、最後の値が残ります:

>>> a = np.arange(5)
>>> a[[0,0,2]]=[1,2,3]
>>> a
array([2, 1, 3, 3, 4])

これは十分に合理的なのですが、Pythonの「+=」構造を使いたいのであれば、期待している動作とは異なる場合があるので注意してください:

>>> a = np.arange(5)
>>> a[[0,0,2]]+=1
>>> a
array([1, 1, 3, 3, 4])

インデックスのリストに0が2回現れていますが、0番目の要素は1度しか増分されていません。これは、Pythonが「a+=1」を「a=a+1」と等価であるとしているためです。

ブール型配列によるインデックス付け

(整数の) インデックス配列で配列をインデックス付けする場合には、使用したいインデックスのリストを用意しています。ブール型インデックスを使う場合には、アプローチは異なります;使用したい配列の要素と使用したくない配列の要素を、明示的に選択します。

ブール型インデックス付けを検討する最もよくある方法は、元の配列と同じ形状を持つブール型配列を使うことです:

>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> b                                          # b is a boolean with a's shape
array([[False, False, False, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True]], dtype=bool)
>>> a[b]                                       # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11])

この性質は、代入のときに非常に便利です:

>>> a[b] = 0                                   # All elements of 'a' higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
       [4, 0, 0, 0],
       [0, 0, 0, 0]])

以下の例を見ると、「マンデルブロ集合」のイメージを生成するためにブール型インデックス付けをどのように使うかがわかります:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot( h,w, maxit=20 ):
...     """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
...     c = x+y*1j
...     z = c
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + c
...         diverge = z*np.conj(z) > 2**2            # who is diverging
...         div_now = diverge & (divtime==maxit)  # who is diverging now
...         divtime[div_now] = i                  # note when
...         z[diverge] = 2                        # avoid diverging too much
...
...     return divtime
>>> plt.imshow(mandelbrot(400,400))
>>> plt.show()

f:id:masakykato:20170202200535p:plain

ブール型インデックス付けを使う2つ目の方法は、整数型インデックス付けにより似ています;配列の各次元に対して、欲しいスライスを選択して1次元のブール型配列を得ます。

>>> a = np.arange(12).reshape(3,4)
>>> b1 = np.array([False,True,True])             # first dim selection
>>> b2 = np.array([True,False,True,False])       # second dim selection
>>>
>>> a[b1,:]                                   # selecting rows
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                     # same thing
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[:,b2]                                   # selecting columns
array([[ 0,  2],
       [ 4,  6],
       [ 8, 10]])
>>>
>>> a[b1,b2]                                  # a weird thing to do
array([ 4, 10])

1次元ブール型配列の長さはスライスしたい次元(もしくは軸)の長さと一致している必要があることに注意してください。前の例では、「b1」は長さが3 (「a」の行数) のランク1配列で、「b2」(長さが4) は「a」の2番目のランク(列)のインデックスに一致しています。

ix_()関数

「ix_」関数は、異なるベクトルを結合して各n-upletの結果を得るために使用することができます。例えば、ベクトルa、b、cから取得したすべての3つ組の値に対してa+b*cを計算したいのであれば:

>>> a = np.array([2,3,4,5])
>>> b = np.array([8,5,4])
>>> c = np.array([5,4,6,8,3])
>>> ax,bx,cx = np.ix_(a,b,c)
>>> ax
array([2,
       3,
       4,
       5])
>>> bx
array([[[8],
        [5],
        [4]]])
>>> cx
array([5, 4, 6, 8, 3])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],
        [27, 22, 32, 42, 17],
        [22, 18, 26, 34, 14]],
       [[43, 35, 51, 67, 27],
        [28, 23, 33, 43, 18],
        [23, 19, 27, 35, 15]],
       [[44, 36, 52, 68, 28],
        [29, 24, 34, 44, 19],
        [24, 20, 28, 36, 16]],
       [[45, 37, 53, 69, 29],
        [30, 25, 35, 45, 20],
        [25, 21, 29, 37, 17]]])
>>> result[3,2,4]
17
>>> a[3]+b[2]*c[4]
17

以下のようにreduceを実装することもできます:

>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r,v)
...    return r

そして、このように使用します:

>>> ufunc_reduce(np.add,a,b,c)
array([[[15, 14, 16, 18, 13],
        [12, 11, 13, 15, 10],
        [11, 10, 12, 14,  9]],
       [[16, 15, 17, 19, 14],
        [13, 12, 14, 16, 11],
        [12, 11, 13, 15, 10]],
       [[17, 16, 18, 20, 15],
        [14, 13, 15, 17, 12],
        [13, 12, 14, 16, 11]],
       [[18, 17, 19, 21, 16],
        [15, 14, 16, 18, 13],
        [14, 13, 15, 17, 12]]])

 

標準のufunc.reduceと比べたこのバージョンのreduceの利点は、出力xベクトル数の大きさの引数配列の作成を避けるために、ブロードキャストルールを使用するところにあります。

文字列によるインデックス付け

Recordarraysを参照してください。(参照先不明)

線形代数

執筆中です。基本的な線形代数がここに入ります。

簡単な配列演算

詳細はnumpyフォルダにあるlinalg.pyを参照してください。

>>> import numpy as np
>>> a = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> print(a)
[[ 1.  2.]
 [ 3.  4.]]

>>> a.transpose()
array([[ 1.,  3.],
       [ 2.,  4.]])

>>> np.linalg.inv(a)
array([[-2. ,  1. ],
       [ 1.5, -0.5]])

>>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I"
>>> u
array([[ 1.,  0.],
       [ 0.,  1.]])
>>> j = np.array([[0.0, -1.0], [1.0, 0.0]])

>>> np.dot (j, j) # matrix product
array([[-1.,  0.],
       [ 0., -1.]])

>>> np.trace(u)  # trace
2.0

>>> y = np.array([[5.], [7.]])
>>> np.linalg.solve(a, y)
array([[-3.],
       [ 4.]])

>>> np.linalg.eig(j)
(array([ 0.+1.j,  0.-1.j]), array([[ 0.70710678+0.j        ,  0.70710678-0.j        ],
       [ 0.00000000-0.70710678j,  0.00000000+0.70710678j]]))
Parameters:
    square matrix
Returns
    The eigenvalues, each repeated according to its multiplicity.
    The normalized (unit "length") eigenvectors, such that the
    column ``v[:,i]`` is the eigenvector corresponding to the
    eigenvalue ``w[i]`` .

ヒントとコツ

ここでは、ちょっとした役立つTipsを提供します。

「自動的な」再整形

配列の次元を変更する場合、配列のサイズを省略し、自動的に推定させることができます。

>>> a = np.arange(30)
>>> a.shape = 2,-1,3  # -1 means "whatever is needed"
>>> a.shape
(2, 5, 3)
>>> a
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11],
        [12, 13, 14]],
       [[15, 16, 17],
        [18, 19, 20],
        [21, 22, 23],
        [24, 25, 26],
        [27, 28, 29]]])

ベクトルの積み上げ

同じサイズを持つ行ベクトルのリストから、どのように2次元配列を作成するのでしょうか。MATLABでは、これは非常に簡単です:「x」と「y」が同じ長さを持つ2つのベクトルの場合、「m=[x;y]」とするだけです。NumPyでは、 積み上げを実行する次元に従って「column_stack」や「dstack」、「hstack」、「vstack」といった関数で実施します。例えば:

x = np.arange(0,10,2)                     # x=([0,2,4,6,8])
y = np.arange(5)                          # y=([0,1,2,3,4])
m = np.vstack([x,y])                      # m=([[0,2,4,6,8],
                                          #     [0,1,2,3,4]])
xy = np.hstack([x,y])                     # xy =([0,2,4,6,8,0,1,2,3,4])

2次元以上でのこれらの関数のロジックは、奇妙ではあります。

関連項目

Numpy for Matlab users

ヒストグラム

配列に適用されるNumPyの「histgram」関数は、ベクトルのペア、つまり配列のヒストグラムとビンのベクトルを返します。

注意:matplotlibにもヒストグラムを生成する関数 (Matlabでは「hist」と呼ばれます) がありますが、NumPyとは異なります。主な違いは、「pylab.hist」は自動的にヒストグラムを描画し、「numpy.histgram」はデータを生成するのみであることです。

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
>>> mu, sigma = 2, 0.5
>>> v = np.random.normal(mu,sigma,10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, normed=1)       # matplotlib version (plot)
>>> plt.show()

f:id:masakykato:20170202201220p:plain

>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, normed=True)  # NumPy version (no plot)
>>> plt.plot(.5*(bins[1:]+bins[:-1]), n)
>>> plt.show()

f:id:masakykato:20170202201251p:plain

参考情報

 

データサイエンスにおけるETL vs ELT

データサイエンス全般

ETLといえば、昔からBIの領域で様々なマスター/トランザクションデータを収集するために使われている技術です。すなわち、Extract (抽出) - Transform (変換) - Load (読み込み) という3つのプロセスで、BI側に役立つ形にデータを加工して取り込むことです。

 

これに対してELTという技術もあります。これはTransformとLoadの順番が逆のプロセスになっているもので、ETLアーキテクチャでは中間層がボトルネックになりやすいという問題を解消させる考え方です。例えばOracle社のData Integratorは、ELTアーキテクチャを採用してデータ変換をOracle Databaseのエンジン内で行うことにより、中間層がボトルネックになることを避けています。

 

このようにETLとELTという言葉はアーキテクチャの視点でのみ使われていると思っていたのですが、以下のブログから、データサイエンスの領域では異なった視点からELTという言葉が使われていることに気が付きました。

https://buckwoody.wordpress.com/2016/11/18/data-wrangling-elt-not-etl/

データサイエンスの目的は、BIのようにデータを可視化することではなくデータから答えを見つけ出すことにあるため、オリジナルのデータが非常に重要になります。このため、データ変換は最後にすべきことからETLプロセスではなくELTプロセスであるべきだということです。

 

機械学習のみでなく統計処理でもデータ処理のプロセスは確かにELTだったのですが、この言葉が使われているということには、うかつにも気が付きませんでした。

従来の統計処理と機械学習

AI 機械学習 深層学習

最近様々な新聞や雑誌に、毎日のように機械学習(マシンラーニング)や深層学習(ディープラーニング)といった言葉がでてきますが、先日あるお客様より

「今までの統計処理と何が違うの?人工知能とはどう違うの?」

という質問をされました。

 

機械学習の使用してる数学的な手法やアルゴリズムは従来の統計処理(統計解析やデータマイニング)と非常に近いため、何が違うのかがよく分からないという人が多いような気がするので、このブログの最初のネタにしようと思います。

 

なおここで書いている内容は、あくまで解釈の一つとして理解ください。視点によっては違う解釈のほうが適合しやすいことがあります。

 

まず、

機械学習、深層学習 = 人工知能

ではないですよね。機械学習や深層学習は、人工知能のための技術、もしくは研究テーマの一つであるという考え方です。

 

機械学習と深層学習についてですが、別々のもののように扱われている記事をよく見かけますが、深層学習は多層ニューラルネットワークを用いた機械学習の手法の一つです。ただし、深層学習は従来の機械学習の手法では難しかった画像、音声、言語を対象とする人工知能のテーマに対して優れた性能を発揮するため、独自のテーマとして脚光を浴びているという背景があるのだと思います。

 

ここまでで、先ほどのお客様からの質問に対して

人工知能 ← (適用技術/研究テーマ) - 機械学習(深層学習含む)」

という回答になりました。

 

次に従来の統計処理との違いについてですが、先ほども書いたように機械学習と従来の統計処理の数学的な手法やアルゴリズムは非常に近いのですが、目的が異なります。つまり

  • 統計解析は、まず仮説を立て、その仮説を標本データから推定、評価していくことで、仮説の正しさを検証するものです。つまり、データの意味や仮説を説明することに重きを置いています。
  • データマイニングは、膨大なデータからルールやパターンを発見し、検証するものです。つまり事実の発見と説明に重きを置いています。
  • 機械学習は、膨大なデータからルールやパターンを発見し、それを基に予測することを目的とするものです。つまり事実から予測をすることに重きを置いています。

 

なお、一般的に「予測」というと「未来を予測する」ことと思われますが、機械学習分野においては、「未来を予測する」ことだけではなく「見えないものを見えるようにする」ことにも予測という言葉を使います。例えばある企業が提供しているサービスを利用している顧客に対して、「3か月後にサービス利用を停止する顧客を予測する」ことも予測ですし、「今サービス利用を停止しようと考えている顧客を探し出す」ことも予測と言えます。

 

以上で、先ほどのお客様からの質問に対して、

「従来の統計処理との違いは目的」

という回答になりますね。

 

なお、別の解釈がある、こういう回答のほうがお客様が分かりやすい、という話がありましたら、是非ともコメント頂ければと思います。