今から始めるPython その5 文字列、リスト、numpyスライシング
さて、前回までにPythonの研究向けディストリビューションであるPython(x,y)をインストールし、付属するIDEであるSpyderをつかってpylabの仕組みを説明、ネームスペースについて触れました。
本ブログでは普通のチュートリアルではカヴァーされていないような初心者がつまづきそうなトピックにフォーカスしながらやっていきたいとおもっているので、今のところかなり変なPython入門シリーズになっています(とくにその3,4)。
さて、いよいよnumpyを使い始めていきます。numpyは高速な配列を提供するライブラリですが、これがなかったら私はPythonは使っていなかったとおもいます。それほど重要なライブラリです。
今回は、numpyを使うにあたって重要なスライシングについて見ていきます。
Pythonで文字列、リストのスライシング
シリーズのタイトルが「今から始めるPython」なので、一応Pythonの基本を見て、numpyアレイを扱うまでの基礎を見て行きましょう。
巷にPythonチュートリアルはたくさんありますが、Python公式のPython チュートリアルはとてもオススメです。私がPythonの基本を覚えたのも公式サイトのチュートリアルでした。
ただで読めるPython公式サイトの日本語チュートリアルがコスパと質のバランスでやっぱりいちばんいい気がしますので、これを一緒に見て行きましょう。
それではまず、3.1. Python を電卓として使うでPythonのインタプリタを計算機代わりにつかって、簡単な計算をしてみましょう。
まあ、3+5としたければ3+5と打てばいいという、とても当たりまえな内容で問題ないとおもいますが、剰余の計算が、MATLABだとmod(X,Y)みたいにして関数が必要なので、思わずPythonではどうやるんだっけと調べてしまうことがありますが、実はオペレーターがあって%だけでいいのが、ちょっと注意ですね。
5 % 3
とすると2が得られます。
あとは、
5/3
とすると答えがざっくり1になるのが驚いてしまうと思います。これは、もともとPythonではデータ型が保たれるような仕様であったため、Python 2系まではデフォルトでは整数同士の割り算は整数の答えになります。
MATLABから来た人は気持ち悪いし、初心者がびっくりする部分なのでPythonのバージョン3系ではちゃんと小数点のついたフロートのデータ型で答えが出るようになりました。でも、Python(x,y)をおすすめしている当ブログではPython2系をつかっていますので、整数型の答えがデフォルトです。しかし、この仕様は実は変えられるので、
from __future__ import division
と未来からのモジュールをインポートすると、先程の結果が、1.6666666666666667に変わります。私はこれ便利でよく使っています。
あとは、Pythonを起動してすぐでインポートが面倒なときは、
5/3.0
として、フロートの計算なのでフロートの結果になるようにしたりもします。
便利なのは、たまに割り算の結果の整数部だけ欲しいので、floor(5/3)なんてやることがあります。実はこれは、
5 // 3
としても同じことで、スラッシュが二重になると小数点が切り捨てになります。ちょっと便利ですね。
あとはMATLABだと階乗はハット(^)で2^10みたいにやったように記憶していますが、Pythonでは2**10のようにアスタリスクを二重にします。
あとはMATLABのansに相当するのがアンダースコア(_)で、計算機代わりに使うときは割りと便利ですね。
3.1.2. 文字列のところは、文字列のスライスのあたりまでnumpyにはあまり関係ないので説明は飛ばしますが、難しくないのでざっと読むといいと思います。"hellow" * 3とかで"hellowhellowhellow"になるのは結構斬新です。
ここで、スライスの考え方はMATLABから来たらほぼ同じなのですんなり行くと思いますが、多少違いもあるので、気をつけましょう。
word = 'Python'
として、word[0]でPにアクセスできるからわかるようにPythonではインデックスは0から始まります。MATLABは1からだったりするのでややこしいですね。
で、重要なのは
word[-1]
ですね。MATLABだとword[end-1]みたいに書くところが、endがない形でスッキリみえるけども、ひと目で初心者が分かりやすいコードはMATLABの方ですね。私はnumpyに慣れたので、スッキリしていいと思います。
word[2:5]
のように、部分的にスライスしたり、
word[2:]
のように、こっから全部という風にかけたり、
word[2::2]
ステップサイズを2に指定したりと出来ます。MATLABだとインデックスがずれて、[3:2:end]という感じでしょうか(もうMATLAB忘れている)。
このスライスはnumpyでも全く同じなので、覚えましょう。これを多次元に拡張するとnumpyのスライスになります。
3.1.3. Unicode 文字列は飛ばします。今はざっと読んでください。
3.1.4. リスト さあ、いよいよリストです。リストは、MATLABでいうCELLみたいな感じですが、リストのほうがめちゃ便利です。Pythonにはあとはタプルとディクショナリがありますが、リストが一番よく使うもので、リストを覚えたらタプルは簡単です。
リストも文字列のときと同じようにスライスが出来ます。
squares = [1, 4, 9, 16, 25] squares[-3:]
とすると最後から3つをスライスして、
[9, 16, 25]
という結果になります。
意外と忘れるのが、リストを繋げる操作(コンカテネーション)です。リストは実は+オペレーターであっさりとつなげられます。
なので、
squares + [36, 49, 64, 81, 100]
とすると
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
という結果になります。
なんでこれが忘れがちになるかというと、リストの後ろに要素を増やそうとするときには、普通appendを使うからです。
ちょっとSpyderなどのコンソール上で、
print dir(list)
としてみるとアンダースコアが付いたものの他に
'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'
というのが出てきます。ここでのlistはグローバルスコープにあるリストのタイプを規定しているオブジェクトで、dirでもってリストが持っているメソッドを表示させているわけです。
a = []
または
a = list()
として変数aを初期化したリストにして、
a.append(3)
とするとaの中身が[3]になります。
こういう感じに最後に要素を継ぎ足していくときに使うappendはむっちゃ使いますので、発想としてはさっきのsquares + [36, 49, 64, 81, 100]をしようと思うより先に
squares.append([36, 49, 64, 81, 100])
としてしまいたくなるわけですが、これだと結果が違って
[1, 4, 9, 16, 25, [36, 49, 64, 81, 100]]
となってしまいます。 リストはCELLのようにいろんなオブジェクトを要素として持てるので、リストの中にリストというのも可能なんですね。で、appendしたときに渡すものは一要素として追加されるので、上の様に6番目の要素はリストとしてひとまとまりで入ってしまうわけです。
どうしてもappendでやろうとおもったら一つ一つ追加する必要があるので
for element in [36, 49, 64, 81, 100]: squares.append(element)
という形になります。これは、フォーループですが、PythonではMATLABのようにendは使いません。その代わりループする内容のまとまりを表すのにインデンテーションを使います。インデンテーションとは、左側のスペースのことです。普通はタブか、スペースを4つつかいます。これが同一ブロックでスペースか一つでもずれるとエラーになるという、非常に厳しいルールです。多少窮屈ですが、このルールのおかげで他人が書いたコードを読むときにスラスラ読めてしまう効果を実感すると、利点の方が大きいことがすぐ理解できると思います。
リストには文字列やタプル、ディクショナリ、関数、クラスオブジェクト、などなどなんでも格納出来ます。
numpy超基本その1
さて、ながなかと準備してきましたが、いよいよnumpyを見ていきます。
MATLABから来た人はぜひ、NumPy for MATLAB usersというチートシートを活用しましょう。
Numpyの基本については、個人的に良くまとまっていると思った、J.R. Johanssonさんという方が作ったiPython notebookが参考になるので、これを一緒に見て行きましょう。
まず、
%pylab inline
としていますが、これはIPythonのマジック(特別機能)である%ランチャー機能をつかっています。我々はインタプリタにはIPythonではなくSpyderを使っている前提なので、かわりに
from pylab import *
の呪文を唱えましょう。基本的に同じことがおこります。
つぎに、
from numpy import *
としていますが、これはpylabと同じネームスペースですね。はっきりいって、上でpylabを呼んでいるので、必要ないのですが、まあ害はありません。
pylabのネームスペースは、トップレベルにnumpyとmatplotlibの関数をもってくるので、気持ち悪い人は気持ち悪いかもしれません。pylabはさらにnumpyをnpのネームスペースにもインポートします。numpyは普通 import numy as npとして使われるのでそのスタイルに慣れている人向けですね。
pylabはインタラクティブな解析でMATLAB代わりにするならタイピングが少ないのでいいと思いますが、長いプログラミングを書くときはPylabじゃなくmatplotlibからpyplotを掘り出して来て使ってネームスペースを分けるのが普通です。
そして、リストを渡してnumpy arrayをつくり、dtypeやsize、shapeを説明しています。このへんは簡単ですね。
注意すべきは
M[0,0] = "hello"
でエラーになっていることですね。これは、numpy arrayはリストとはちがって同じデータタイプのものしか入れられないという重要な制限があるからなんですね。これは、numpy arrayのメモリ上のレイアウトを最適化し、必要最小のメモリー領域をつかい、最小のオーバーヘッドでもってアレイの要素にアクセスするための仕様なのです。numpyの速さの秘密の一つです。
なので、たとえば自分のデータが0から255までの整数しかないとわかっている場合は、numpy arrayのデータタイプとして最適なnumpy.uint8を選んでやると一番パフォーマンスがでます。なんだかC言語をやっているみたいですね。
データタイプはアレイを作るときに
M = array([[1, 2], [3, 4]], dtype=np.uint8)
のように指定できます。我々はpylab環境にいるのでnpを省いて
M = array([[1, 2], [3, 4]], dtype=uint8)
でもオッケーです。ちなみにarray自体もnumpy.arrayのことですが、pylabがトップレベルにインポートしているのでnumpyを省いてもかけますし、np.arrayにもエイリアスがあるので、これでも同じです。
さらには、
M = array([[1, 2], [3, 4]], dtype='uint8')
のようにクォーテーションでくくってもよく、これはpylabでなくってもオッケーです。私はいつもnp.uint8ですが、この辺りは好みです。
さて、このMを浮動小数点型にしたいとおもったら
M = M.astype(np.float)
M = M.astype(float)
M = M.astype(np.float32)
などとして、データタイプのキャストができます。
そして、arangeやlinspaceですね。linspaceはMATLABでおなじみですね。x = arange(0, 10, 1) でarangeに渡している2番めのパラメータ10は最初のいらない要素を意味するので、0から9までを1刻みでとったアレイになるんですが、これはnumpyアレイのスライスと同じと覚えるといいと思います。あと、3つめのパラメータは省くと1になるのでこの場合省けます。
さっきの
squares = [1, 4, 9, 16, 25]
をつかってnumpyアレイaを作ってみましょう。
a = array(squares)
ここで、a[:2]とすると2は最初のいらない要素のインデックスなので、欲しいのは0,1になって、結果は
array([1, 4])
となるのですね。arangeはnumpyならではの関数なのでpython/numpyのインデックスを踏襲しているわけです。でも、linspaceはおそらくMATLABとの互換性を高めるためにつくられたかなり機能のかぶっている関数なので、動作がMATLABの様になっているわけです。
その後はざっとながして、次に便利なのは
M.nbytes
ですね。これで配列のメモリ上の大きさがバイトで分かります。MATLABではwhosというコマンドがあってよく使っていたと思いますが、numpyにはwhosみたいなのはないです。勘違いnp.who()というそっくりなコマンドがありました。pylab環境ならwho()です。whosだとそんなのないと怒られるので、随分の間ないものと思っていた。。。
numpyアレイに関してはwho()が一番いいですが、listやdictは表示されませんので、Pythonにもglobalsという関数をつかってグローバルなスコープにある関数・変数をディクショナリとして返してくれますが、ちょっと情報量多すぎで、必要な情報を探すにはフィルターしないと大変です。例えばディクショナリだけ表示とか
[ k for k,v in globals().items() if type(v) == dict ]
またはリストだけ表示なら
[ k for k,v in globals().items() if type(v) == list ]
とできます。
もしくは'A'で終わる変数名だけなら
[ k for k,g in globals().items() if k.endswith('A') ]
とかです。
Spyderにはオブジェクトヴュアーがあってwhosの代わりになります。また、IPythonにはwhosがあったりします。IPythonは便利なのは便利ですが、Python初心者には混乱するかもしれないので、まだ説明してませんが、慣れた頃にぜひ試してみるべきツールです。
さあ、とうとうインデックシングのところまできました。リストと文字列のスライスの説明をしてきたので、かなり分かりやすいとおもいます。Fancy indexingがMATLABから来た人なら当然のことですが、そうでないと咀嚼するのに時間がかかりますね。
66行目に
[n for n in range(5)]
とありますが、これはリストコンプリヘンションというもので、とっても便利です。これは、ちゃんとした説明の記事が必要なくらい重要なテクニックなので、ここでは簡単にフォーループに読み替えたときに出てくる要素がリストの中身になるような感じと思ってもらえばOKです。ここではrangeというPythonのビルトイン関数をつかって0から4までの要素のリストを作っています。
ただし、
array(range(5))
とやってもarray([n for n in range(5)])と同じ結果が得られるので、リストコンプリヘンションを使う意味があまりありません。(突っ込んでばかりですみません。。)
同じことをするなら、おすすめはNumpyのarangeを使って
B = arange(5)
で、結果は同じです。(pylabはトップレベルとnpにnumpyをインポートしているのでnp.arangeでもarangeだけでも同じ結果。)
リストコンプリヘンションの便利な使い方を一例だけ紹介しておくと、
[n for n in range(100) if n % 11 == 5]
なんてやると100までの整数で11で割った時のあまりが5になるような数を選んでリストを作れます。
次の、
row_mask = array([True, False, True, False, False]) B[row_mask]
のマスクの使い方はとても便利です。ぜひマスターしましょう。
応用としては、
B>3
とすると
array([False, False, False, False, True], dtype=bool)
という結果が得られるとおもいますが、これをつかって
B[B>3]
という風にするとアレイBの中から3より大きい要素を抜き出すことが簡単に出来ます。
array([4])
takeとかchooseって使ったことがない。Fancy indexingで十分な気がします。
さて、Linear algebraのところでたどり着きました。 これで大体半分消化しましたが、長くなったので、続きは次回 その6 numpyの基本 スライシングの続き にしましょう。
« 2の倍数がプログラマー心をくすぐる「2048」が面白い件。パズルゲームの著作権? | トップページ | BRIAN OKKEN氏のWhy Most Unit Testing is Wasteを読む。 »
「パソコン・インターネット」カテゴリの記事
- Linuxのデスクトップ環境にログインした時にxmodmapコマンド実行(2023.12.30)
- youtube-dlでrumbleをダウンロード(2023.12.06)
- Pop!_OSのアプデがfcitxを繰り返し葬る件(2023.05.22)
- vue.jsで遊んでみる(2023.01.02)
- 『将棋神やねうら王』のオープンソース版MyShogiをLinux Mint 19.1でビルドしてみた件(2019.03.24)
「学問・資格」カテゴリの記事
- ウイルスを殺すのは簡単。人に無害でやるのが難しいの!! 二酸化塩素「こう?えい!」(2020.03.11)
- 老化はエピジェネティクスな影響が積り、スプライシングに影響を与えるのかも(2017.11.20)
- シネッセンス細胞がアツい。不老不死とハゲの治療が同時に可能か?(2017.11.18)
- 古めの論文でテキスト選択がおかしい時の対処法(2017.09.08)
- Juliaやってみよう。五日目。Pythonと速度比較。(2017.08.01)
「Python」カテゴリの記事
- Noteの記事をPythonでバックアップしといた。(2021.05.05)
- JupyterLabでも好きな外部エディターを使いたい!(2018.05.02)
- ローカルエリア内のJupyterLabサーバーにLAN経由で接続する。(2018.05.02)
- Juliaやってみよう。五日目。Pythonと速度比較。(2017.08.01)
- Juliaやってみよう。四日目。@timeでプロファイリング(2017.07.16)
「今から始めるPython」カテゴリの記事
- 今から始めるPython その6 numpyの基本 スライシングの続き(2014.10.03)
- 今から始めるPython その5 文字列、リスト、numpyスライシング(2014.08.02)
- 今から始めるPython その4 pylabネームスペースの解説(2014.07.26)
- 今から始めるPython その3 Site-Packagesってなに。モデュールのインストール・アンインストール(2014.03.17)
- 今から始めるPython その2 Spyderを使う(2014.03.11)
« 2の倍数がプログラマー心をくすぐる「2048」が面白い件。パズルゲームの著作権? | トップページ | BRIAN OKKEN氏のWhy Most Unit Testing is Wasteを読む。 »
コメント