Skip to content

Instantly share code, notes, and snippets.

@Cartman0
Last active April 21, 2016 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cartman0/5d24bdc3234a97bfc7c9fed4355a1580 to your computer and use it in GitHub Desktop.
Save Cartman0/5d24bdc3234a97bfc7c9fed4355a1580 to your computer and use it in GitHub Desktop.
NumPyTutorial メモ5(Fancy indexing and index tricks)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {
"toc": "true"
},
"cell_type": "markdown",
"source": "# Table of Contents\n <p><div class=\"lev1\"><a href=\"#NumPy-Tutorial-5-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>NumPy Tutorial 5</a></div><div class=\"lev2\"><a href=\"#Fancy-indexing-and-index-tricks(変わったindexと、index付け小技)-1.1\"><span class=\"toc-item-num\">1.1&nbsp;&nbsp;</span>Fancy indexing and index tricks(変わったindexと、index付け小技)</a></div><div class=\"lev3\"><a href=\"#Indexing-with-Arrays-of-Indices(index配列によるindex付け)-1.1.1\"><span class=\"toc-item-num\">1.1.1&nbsp;&nbsp;</span>Indexing with Arrays of Indices(index配列によるindex付け)</a></div><div class=\"lev3\"><a href=\"#Indexing-with-Boolean-Arrays(真偽値配列によるindex付け)-1.1.2\"><span class=\"toc-item-num\">1.1.2&nbsp;&nbsp;</span>Indexing with Boolean Arrays(真偽値配列によるindex付け)</a></div><div class=\"lev4\"><a href=\"#bool行列の反転-1.1.2.1\"><span class=\"toc-item-num\">1.1.2.1&nbsp;&nbsp;</span>bool行列の反転</a></div><div class=\"lev3\"><a href=\"#The-ix_()-function(ix関数)-1.1.3\"><span class=\"toc-item-num\">1.1.3&nbsp;&nbsp;</span>The ix_() function(ix関数)</a></div><div class=\"lev3\"><a href=\"#Indexing-with-strings(文字列によるindex付け)-1.1.4\"><span class=\"toc-item-num\">1.1.4&nbsp;&nbsp;</span>Indexing with strings(文字列によるindex付け)</a></div><div class=\"lev2\"><a href=\"#参考リンク-1.2\"><span class=\"toc-item-num\">1.2&nbsp;&nbsp;</span>参考リンク</a></div>"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "# NumPy Tutorial 5"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "- [NumPy Tutorial メモ1(Basic)](http://nbviewer.jupyter.org/gist/Cartman0/9fa48b89664dc08ef82a55877767b5a0)\n- [NumPy Tutorial メモ2(ShapeManipulation)](http://nbviewer.jupyter.org/gist/Cartman0/bc062d1a162589e40a02c21e0ada7776)\n- [NumPy Tutorial メモ3(Copies and Views)](http://nbviewer.jupyter.org/gist/Cartman0/cd39c1bfc88f5edccd8f4523fbd33b91)\n- [NumPy Tutorial メモ4(Less Basic)](http://nbviewer.jupyter.org/gist/Cartman0/e9f50b0f0dbc28f40938b11088ac211d)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Fancy indexing and index tricks(変わったindexと、index付け小技)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "NumPy offers more indexing facilities than regular Python sequences. \n(NumPyは、普通のPythonシーケンスよりも多くのindex付け機能を用意している。)\n\nIn addition to indexing by integers and slices, as we saw before, \narrays can be indexed by arrays of integers and arrays of booleans.\n(整数やスライスによるindex付けに加え、\nこれまでに見てきたように、\n整数配列や真偽値配列による配列のindex付けも可能である。)\n"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Indexing with Arrays of Indices(index配列によるindex付け)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "import numpy as np\na = np.arange(12) ** 2 # the first 12 square numbers\na",
"execution_count": 1,
"outputs": [
{
"execution_count": 1,
"data": {
"text/plain": "array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "i = np.array([1, 1, 3, 8, 5]) # an array of indices indexの配列\na[i] # the elements of a at the positions i",
"execution_count": 2,
"outputs": [
{
"execution_count": 2,
"data": {
"text/plain": "array([ 1, 1, 9, 64, 25])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "j = np.array([[3, 4], [9, 7]])# a bidimensional array of indices(indexの2次元配列)\na[j] # the same shape as j (2*2)",
"execution_count": 3,
"outputs": [
{
"execution_count": 3,
"data": {
"text/plain": "array([[ 9, 16],\n [81, 49]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "When the indexed array a is multidimensional, \na single array of indices refers to the first dimension of a. \n(index付けされた配列aが多次元配列の場合、\nindexのsingle配列は a の最初の次元を参照する。)\n\nThe following example shows this behavior by converting an image of labels into a color image using a palette.\n(以下の例では、ラベル画像をパレットを用いてカラー画像に変換することでこの振舞いを示す。)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "palette = np.array([\n [0, 0, 0], # black\n [255, 0, 0], # red\n [0, 255, 0], # green\n [0, 0, 255], # blue\n [255,255,255] # white\n ])\n\nimage = np.array([\n [0, 1, 2, 0], # each value corresponds to a color in the palette(各値が、パレット内の1つの色に対応する)\n [0, 3, 4, 0] \n ])\n\npalette[image] # the (2,4,3) color image (2*4*3のカラーimage)",
"execution_count": 4,
"outputs": [
{
"execution_count": 4,
"data": {
"text/plain": "array([[[ 0, 0, 0],\n [255, 0, 0],\n [ 0, 255, 0],\n [ 0, 0, 0]],\n\n [[ 0, 0, 0],\n [ 0, 0, 255],\n [255, 255, 255],\n [ 0, 0, 0]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "We can also give indexes for more than one dimension. \n(2次元以上のindexを与えることも可能。\n)\n\nThe arrays of indices for each dimension must have the same shape.\n(各次元のindex配列は同じshapeでなければならない。)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(12).reshape(3, 4)\na",
"execution_count": 5,
"outputs": [
{
"execution_count": 5,
"data": {
"text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "i = np.array( [ \n [0, 1], # indices for the first dim of a\n [1, 2] \n ])\n\nj = np.array([ \n [2, 1], # indices for the second dim\n [3, 3] \n ])\na[i] # 2 * 2 * 4行列",
"execution_count": 6,
"outputs": [
{
"execution_count": 6,
"data": {
"text/plain": "array([[[ 0, 1, 2, 3],\n [ 4, 5, 6, 7]],\n\n [[ 4, 5, 6, 7],\n [ 8, 9, 10, 11]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[i, j] # i and j must have equal shape",
"execution_count": 7,
"outputs": [
{
"execution_count": 7,
"data": {
"text/plain": "array([[ 2, 5],\n [ 7, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[i, 2]",
"execution_count": 8,
"outputs": [
{
"execution_count": 8,
"data": {
"text/plain": "array([[ 2, 6],\n [ 6, 10]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[:, j] # 3 * 2 * 2, i.e., a[ : , j]",
"execution_count": 9,
"outputs": [
{
"execution_count": 9,
"data": {
"text/plain": "array([[[ 2, 1],\n [ 3, 3]],\n\n [[ 6, 5],\n [ 7, 7]],\n\n [[10, 9],\n [11, 11]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Naturally, we can put `i` and `j` in a sequence (say a list) and then do the indexing with the list.\n(普通は、`i` と `j` をシーケンス(というかリスト)に入れた上で、\nリストでindex付けを行う。)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "l = [i, j]\na[l] # equivalent to a[i,j]",
"execution_count": 10,
"outputs": [
{
"execution_count": 10,
"data": {
"text/plain": "array([[ 2, 5],\n [ 7, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "However, we can not do this by putting `i` and `j` into an array, \nbecause this array will be interpreted as indexing the first dimension of a.\n(しかし、`i` と `j` を配列に入れて同じことができるわけではない。\nなぜなら、この配列は a の1次元目をindex付けするものとして解釈されるためである。)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "s = np.array([i, j])\ns",
"execution_count": 11,
"outputs": [
{
"execution_count": 11,
"data": {
"text/plain": "array([[[0, 1],\n [1, 2]],\n\n [[2, 1],\n [3, 3]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[s] # not what we want,",
"execution_count": 12,
"outputs": [
{
"evalue": "index 3 is out of bounds for axis 0 with size 3",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-12-77dec03ac05c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ma\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;31m# not what we want,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m: index 3 is out of bounds for axis 0 with size 3"
],
"ename": "IndexError"
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[tuple(s)] # same as a[i,j]",
"execution_count": 13,
"outputs": [
{
"execution_count": 13,
"data": {
"text/plain": "array([[ 2, 5],\n [ 7, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Another common use of indexing with arrays is the search of the maximum value of time-dependent series \n(もう1つの配列でのindex付けの一般的な使い方は、\n時間に依存した連続データの中からの最大値の検索である):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "time = np.linspace(20, 145, 5) # time scale\ntime",
"execution_count": 14,
"outputs": [
{
"execution_count": 14,
"data": {
"text/plain": "array([ 20. , 51.25, 82.5 , 113.75, 145. ])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "data = np.sin(np.arange(20)).reshape(5, 4) # 4 time-dependent series (4つの時間依存な連続データ)\ndata",
"execution_count": 15,
"outputs": [
{
"execution_count": 15,
"data": {
"text/plain": "array([[ 0. , 0.84147098, 0.90929743, 0.14112001],\n [-0.7568025 , -0.95892427, -0.2794155 , 0.6569866 ],\n [ 0.98935825, 0.41211849, -0.54402111, -0.99999021],\n [-0.53657292, 0.42016704, 0.99060736, 0.65028784],\n [-0.28790332, -0.96139749, -0.75098725, 0.14987721]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "ind = data.argmax(axis=0) # index of the maxima for each series(各連続データで最大値をとるindex)\nind",
"execution_count": 16,
"outputs": [
{
"execution_count": 16,
"data": {
"text/plain": "array([2, 0, 3, 1], dtype=int64)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "time_max = time[ind] # times corresponding to the maxima 最大値に対応する時刻\ntime_max",
"execution_count": 17,
"outputs": [
{
"execution_count": 17,
"data": {
"text/plain": "array([ 82.5 , 20. , 113.75, 51.25])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...\ndata_max",
"execution_count": 18,
"outputs": [
{
"execution_count": 18,
"data": {
"text/plain": "array([ 0.98935825, 0.84147098, 0.99060736, 0.6569866 ])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "np.all(data_max == data.max(axis=0))",
"execution_count": 19,
"outputs": [
{
"execution_count": 19,
"data": {
"text/plain": "True"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "You can also use indexing with arrays as a target to assign to\n(代入対象を表すために配列でindex付けすることも可能):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(5)\na",
"execution_count": 20,
"outputs": [
{
"execution_count": 20,
"data": {
"text/plain": "array([0, 1, 2, 3, 4])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[[1, 3, 4]] = 0\na",
"execution_count": 21,
"outputs": [
{
"execution_count": 21,
"data": {
"text/plain": "array([0, 0, 2, 0, 0])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "However, when the list of indices contains repetitions, \nthe assignment is done several times, leaving behind the last value\n(しかし、indexリストに繰り返しが含まれる場合、\n代入は複数回行われ、最後の値が残る):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(5)\na[[0, 0, 2]] = [1, 2, 3] # 2が残る\na",
"execution_count": 22,
"outputs": [
{
"execution_count": 22,
"data": {
"text/plain": "array([2, 1, 3, 3, 4])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "This is reasonable enough, \nbut watch out if you want to use Python’s` +=` construct, \nas it may not do what you expect\n(これは十分納得がいくものであるが、\nPythonの `+=` 構造を使いたい場合、期待している動作をしないかもしれないので注意):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(5)\na",
"execution_count": 23,
"outputs": [
{
"execution_count": 23,
"data": {
"text/plain": "array([0, 1, 2, 3, 4])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[[0, 0, 2]] += 1\na # a[0] は2にならない",
"execution_count": 24,
"outputs": [
{
"execution_count": 24,
"data": {
"text/plain": "array([1, 1, 3, 3, 4])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Even though 0 occurs twice in the list of indices, \nthe 0th element is only incremented once. \n(indexのリストに 0 が2回現れるにもかかわらず、\n0番目の要素は1回しかインクリメントされていない。)\n\n\nThis is because Python requires `“a+=1”` to be equivalent to `“a=a+1”`.\n(これはPythonが \"a+=1\" に \"a=a+1\" と等価であるよう求めるためである。\n)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Indexing with Boolean Arrays(真偽値配列によるindex付け)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "When we index arrays with arrays of (integer) indices, we are providing the list of indices to pick. \n(整数の)indexの配列を使って配列をindex付けする時、\n我々は拾いたいindexのリストを提供している。\n\nWith boolean indices the approach is different; we explicitly choose which items in the array we want and which ones we don’t.\n(bool配列を使う場合には異なるアプローチをとる;\n配列の要素のうちどれを選び、どれを選ばないのかを明示的に選択する。)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "The most natural way one can think of for boolean indexing is to use boolean arrays that have the same shape as the original array\n(boolのindex付けについて考えつく最も自然なやり方は、\n元の配列と同じshapeのbool配列を用いることである):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(12).reshape(3, 4)\na",
"execution_count": 25,
"outputs": [
{
"execution_count": 25,
"data": {
"text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "b = a > 4\nb # b is a boolean with a's shape",
"execution_count": 26,
"outputs": [
{
"execution_count": 26,
"data": {
"text/plain": "array([[False, False, False, False],\n [False, True, True, True],\n [ True, True, True, True]], dtype=bool)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[b] # 1d array with the selected elements",
"execution_count": 27,
"outputs": [
{
"execution_count": 27,
"data": {
"text/plain": "array([ 5, 6, 7, 8, 9, 10, 11])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "This property can be very useful in assignments\n(この性質は、代入の際にとても便利):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[b] = 0 # All elements of 'a' higher than 4 become 0\na",
"execution_count": 28,
"outputs": [
{
"execution_count": 28,
"data": {
"text/plain": "array([[0, 1, 2, 3],\n [4, 0, 0, 0],\n [0, 0, 0, 0]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "#### bool行列の反転"
},
{
"metadata": {
"collapsed": false,
"trusted": true,
"scrolled": true
},
"cell_type": "code",
"source": "a = np.arange(12).reshape(3, 4)\nb = a > 4\n~b",
"execution_count": 29,
"outputs": [
{
"execution_count": 29,
"data": {
"text/plain": "array([[ True, True, True, True],\n [ True, False, False, False],\n [False, False, False, False]], dtype=bool)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[~b] = 0 # 4以下を0\na",
"execution_count": 30,
"outputs": [
{
"execution_count": 30,
"data": {
"text/plain": "array([[ 0, 0, 0, 0],\n [ 0, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "You can look at the following example to see how to use boolean indexing to generate an image of the **[Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set)**:\n(以下の例を見れば、**Mandelbrot集合** の画像の生成にbool index付けをどのように用いているか分かるだろう。\n\n[マンデルブロ集合 wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%B3%E3%83%87%E3%83%AB%E3%83%96%E3%83%AD%E9%9B%86%E5%90%88)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "import numpy as np\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\ndef mandelbrot(h, w, maxit=20):\n \"\"\"Returns an image of the Mandelbrot fractal of size (h, w).\"\"\"\n (y, x) = np.ogrid[1:0:h * 1j, 0:1:w * 1j]\n c = x + y * 1j\n z = c\n print('z.shape', z.shape)\n divtime = np.zeros(z.shape, dtype=int) # h * w の0行列\n \n for i in range(maxit):\n z = z ** 2 + c # z_{n+1} = z_{n}^2 + c, z^2 = (x+yj)^2 = x^2 - y^2 + 2xyj\n diverge = (z * np.conj(z)) > (2 ** 2) # who is diverging zの大きさが4より大きければTrue\n div_now = diverge & (divtime == 0) # who is diverging now, Bool行列, divtime == 0はまだ更新されていない行列を表す\n #print('div_now', div_now)\n divtime[div_now] = i + 1 # note when, divegeの高いところに今のiterateを代入\n z[diverge] = 2 # avoid diverging too much, 大きすぎるdivergeは避ける\n #print(i, divtime)\n\n return divtime\n",
"execution_count": 31,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "[numpy.conj 複素共役を返す](http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.conj.html)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "$x = 0$ のとき:\n\n$$ \nz^2 +c = (x+yj)^2 \n= x^2 - y^2 + 2xyj \n= - y^2 + 0j + c\n$$\n\n$$\n|z^2 + c| = (-y^2)^2 + c_y^2 = y^4 + c_y^2\n$$\n\n$y = 0$のとき:\n\n$$ \nz^2 + c = (x+yj)^2 \n= x^2 - y^2 + 2xyj \n= x^2 + 0j + c \n$$\n\n$$\n|z^2 + c| = (x^2 + c_x)^2 = x^4 + 2c_x x^2 + c_x^2\n$$\n\n"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "plt.imshow(mandelbrot(500, 500, maxit=20))",
"execution_count": 32,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "z.shape (500, 500)\n"
},
{
"execution_count": 32,
"data": {
"text/plain": "<matplotlib.image.AxesImage at 0x15314d50048>"
},
"output_type": "execute_result",
"metadata": {}
},
{
"data": {
"text/plain": "<matplotlib.figure.Figure at 0x153132e11d0>",
"image/png": "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\n"
},
"output_type": "display_data",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "The second way of indexing with booleans is more similar to integer indexing; \nfor each dimension of the array, we give a 1D boolean array selecting the slices we want.\n(bool値によるindex付けの2つ目の方法は、\nもっと整数での添字付けに似ている;\n配列の各次元について、欲しいスライスを選択して、1次元のbool配列を与える。)"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.arange(12).reshape(3, 4)\na",
"execution_count": 33,
"outputs": [
{
"execution_count": 33,
"data": {
"text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "b1 = np.array([False, True, True]) # first dim selection\nb2 = np.array([True, False, True, False]) # second dim selection",
"execution_count": 34,
"outputs": []
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[b1, :] # selecting rows",
"execution_count": 35,
"outputs": [
{
"execution_count": 35,
"data": {
"text/plain": "array([[ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[b1] #same thing 2*4",
"execution_count": 36,
"outputs": [
{
"execution_count": 36,
"data": {
"text/plain": "array([[ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[:, b2] # selecting columns 3*2",
"execution_count": 37,
"outputs": [
{
"execution_count": 37,
"data": {
"text/plain": "array([[ 0, 2],\n [ 4, 6],\n [ 8, 10]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[b1, b2] # a weird thing to do",
"execution_count": 38,
"outputs": [
{
"execution_count": 38,
"data": {
"text/plain": "array([ 4, 10])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "Note that the length of the 1D boolean array must coincide with the length of the dimension (or axis) you want to slice. \n(1D bool配列の長さは、スライスしたい次元(あるいは軸)の長さと合致していなければならないことに注意。)\n\nIn the previous example,\n`b1` is a 1-rank array with length 3 (the number of rows in a), \nand `b2` (of length 4) is suitable to index the 2nd rank (columns) of `a`.\n(前の例では、`b1` はランク1で長さが3の配列(a に含まれる行数)であり、\n`b2`(長さ4)は `a` の第2ランク(列)をindex付けするのにふさわしい。)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### The ix_() function(ix関数)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "The [`ix`](https://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.ix_.html#numpy.ix_) function can be used to combine different vectors so as to obtain the result for each **n-uplet**. \n(`ix_` 関数は、各 n-uplet の結果を得るために、異なるベクトルを結合するのに用いられる。)\n\nFor example, \nif you want to compute all the `a+b*c` for all the triplets taken from each of the vectors `a`, `b` and `c`(例えば、ベクトル a, b, c から取ってきた3つ組全てに対し `a+b*c` をくまなく計算したいのであれば):"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a = np.array([2, 3, 4, 5])\nb = np.array([8, 5, 4])\nc = np.array([5, 4, 6, 8, 3])",
"execution_count": 39,
"outputs": []
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a + b * c",
"execution_count": 40,
"outputs": [
{
"evalue": "operands could not be broadcast together with shapes (3,) (5,) ",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-40-2fc63b0a8d79>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ma\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mb\u001b[0m \u001b[1;33m*\u001b[0m \u001b[0mc\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mValueError\u001b[0m: operands could not be broadcast together with shapes (3,) (5,) "
],
"ename": "ValueError"
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "(ax, bx, cx) = np.ix_(a, b, c)\nax",
"execution_count": 41,
"outputs": [
{
"execution_count": 41,
"data": {
"text/plain": "array([[[2]],\n\n [[3]],\n\n [[4]],\n\n [[5]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "ax.shape # 4 * 1 * 1",
"execution_count": 42,
"outputs": [
{
"execution_count": 42,
"data": {
"text/plain": "(4, 1, 1)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "bx",
"execution_count": 43,
"outputs": [
{
"execution_count": 43,
"data": {
"text/plain": "array([[[8],\n [5],\n [4]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "bx.shape # 1 * 3 * 1",
"execution_count": 44,
"outputs": [
{
"execution_count": 44,
"data": {
"text/plain": "(1, 3, 1)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "cx # 1 * 1 * 5",
"execution_count": 45,
"outputs": [
{
"execution_count": 45,
"data": {
"text/plain": "array([[[5, 4, 6, 8, 3]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "cx.shape",
"execution_count": 46,
"outputs": [
{
"execution_count": 46,
"data": {
"text/plain": "(1, 1, 5)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "bx * cx # 直積 1 * 3 * 5",
"execution_count": 47,
"outputs": [
{
"execution_count": 47,
"data": {
"text/plain": "array([[[40, 32, 48, 64, 24],\n [25, 20, 30, 40, 15],\n [20, 16, 24, 32, 12]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "result = ax + bx * cx # 4 * 3 * 5\nresult",
"execution_count": 48,
"outputs": [
{
"execution_count": 48,
"data": {
"text/plain": "array([[[42, 34, 50, 66, 26],\n [27, 22, 32, 42, 17],\n [22, 18, 26, 34, 14]],\n\n [[43, 35, 51, 67, 27],\n [28, 23, 33, 43, 18],\n [23, 19, 27, 35, 15]],\n\n [[44, 36, 52, 68, 28],\n [29, 24, 34, 44, 19],\n [24, 20, 28, 36, 16]],\n\n [[45, 37, 53, 69, 29],\n [30, 25, 35, 45, 20],\n [25, 21, 29, 37, 17]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "result.shape",
"execution_count": 49,
"outputs": [
{
"execution_count": 49,
"data": {
"text/plain": "(4, 3, 5)"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "result[3, 2, 4]",
"execution_count": 50,
"outputs": [
{
"execution_count": 50,
"data": {
"text/plain": "17"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a[3] + b[2] * c[4]",
"execution_count": 51,
"outputs": [
{
"execution_count": 51,
"data": {
"text/plain": "17"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "You could also implement the `reduce` as follows:\nreduceを以下のように実装することもできるだろう"
},
{
"metadata": {
"collapsed": true,
"trusted": true
},
"cell_type": "code",
"source": "def ufunc_reduce(ufct, *vectors):\n vs = np.ix_(*vectors)\n r = ufct.identity\n for v in vs:\n print('r', r)\n print('v', v)\n r = ufct(r, v)\n return r\n",
"execution_count": 52,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "and then use it as:"
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "a, b, c",
"execution_count": 53,
"outputs": [
{
"execution_count": 53,
"data": {
"text/plain": "(array([2, 3, 4, 5]), array([8, 5, 4]), array([5, 4, 6, 8, 3]))"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {
"collapsed": false,
"trusted": true
},
"cell_type": "code",
"source": "ufunc_reduce(np.add, a, b, c)",
"execution_count": 54,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": "r 0\nv [[[2]]\n\n [[3]]\n\n [[4]]\n\n [[5]]]\nr [[[2]]\n\n [[3]]\n\n [[4]]\n\n [[5]]]\nv [[[8]\n [5]\n [4]]]\nr [[[10]\n [ 7]\n [ 6]]\n\n [[11]\n [ 8]\n [ 7]]\n\n [[12]\n [ 9]\n [ 8]]\n\n [[13]\n [10]\n [ 9]]]\nv [[[5 4 6 8 3]]]\n"
},
{
"execution_count": 54,
"data": {
"text/plain": "array([[[15, 14, 16, 18, 13],\n [12, 11, 13, 15, 10],\n [11, 10, 12, 14, 9]],\n\n [[16, 15, 17, 19, 14],\n [13, 12, 14, 16, 11],\n [12, 11, 13, 15, 10]],\n\n [[17, 16, 18, 20, 15],\n [14, 13, 15, 17, 12],\n [13, 12, 14, 16, 11]],\n\n [[18, 17, 19, 21, 16],\n [15, 14, 16, 18, 13],\n [14, 13, 15, 17, 12]]])"
},
"output_type": "execute_result",
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "The advantage of this version of `reduce` compared to the normal `ufunc.reduce` is that it makes use of the [Broadcasting Rules](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html#head-c43f3f81719d84f09ae2b33a22eaf50b26333db8) in order to avoid creating an argument array the size of the output times the number of vectors.\nreduceのこのバージョンを通常の `ufunc.reduce` と比較した場合の利点は、\n出力サイズ×ベクトル数の引数配列の作成を回避するために Broadcasting Rules を用いている事にある。"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### Indexing with strings(文字列によるindex付け)"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "See [RecordArrays](http://docs.scipy.org/doc/numpy-1.10.1/user/basics.rec.html#record-arrays). "
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## 参考リンク"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "- [QuickStartTutorial](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html)\n- [私訳「暫定的 NumPy チュートリアル」](http://naoyat.hatenablog.jp/entry/2011/12/29/021414)"
}
],
"metadata": {
"hide_input": false,
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
},
"language_info": {
"pygments_lexer": "ipython3",
"mimetype": "text/x-python",
"nbconvert_exporter": "python",
"codemirror_mode": {
"version": 3,
"name": "ipython"
},
"version": "3.5.1",
"name": "python",
"file_extension": ".py"
},
"toc": {
"toc_number_sections": true,
"toc_window_display": false,
"toc_threshold": "6",
"toc_cell": true
},
"gist": {
"id": "",
"data": {
"description": "NumPyTutorial メモ5(Fancy indexing and index tricks)",
"public": true
}
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment