本稿はNCC Advent Calender 2019の14日目記事です.
はじめに
プログラム書くとき,配列って大事です.
しかし,Python 配列っぽいの多すぎ問題があります.
Pythonの標準機能だけでも
list
,dict
,set
,tuple
があります.
加えてnumpy
などのライブラリを使い始めるとさらに似たようなものが増えます.
なので,今回はそいつらを以下の4点でまとめて使い分けができるように解説していきます.
- 概説
- いいとこ
- わるいとこ
- 所感
あくまで使い分けのための記事なので,細かい使い方などには触れません.
また,配列が何かはなんとなくわかってる前提で書きます.
さらになるべく簡単な言葉を使って説明します.
- 説明の易化のために,本質とは異なる説明をしている場合があります.
対象読者
- Pythonを使い始めたけど,配列多すぎて困ってる
- 他の言語やってたけど,Pythonでの各配列の立ち位置がわからない
- 実装中,ここどれ使うべきかなってなった人
- numpyなどのライブラリを使い始めた人
とりあげるもの
Python標準機能
list
dict
set
tuple
ライブラリ
numpy
-
numpy.array
/numpy.ndarray
numpy.matrix
本編
list
概説
list
は簡単に言えば一番の基本のやつです.
他言語でいう一般的な配列と同じで,Pythonでは「リスト」と呼ぶのが一般的です.
記号としては[]
なので,これで囲まれて,中に,
がいっぱいあったらリストだと思ってください.
Pythonではリスト内の型が複数でも構いません.
また,同じ値を何個でも入れることができます.
その他,世間一般に言う配列の機能を持っています.
あとで,dict
以下の説明をするときに,よくlist
と比較すると思うので,それでlist
の性質はそれを見ながら把握してもらえるといいかなと思います.
(普通のやつなのであまり説明がないです.)
いいとこ
- 扱いが簡単
- 難しいこと考えず,とりあえず
list
にしとけば実装はできる
わるいとこ
- 次元が多くなるとわかりにくくなる
- 一個一個の配列の長さをまとめて見れない(
numpy
のshape
みたいなのがない) - 参照が0から続く数字のみ
所感
良くも悪くも原点.
numpy
とかpandas
でも一旦list
に変換してから使うこともザラですね.
1次元で長さが不定な場合や,順序にい意味があるが番号に意味がない時もlist
です.
ただ多次元ではあまり向かないです.
(そもそも次元が多すぎるような設計をするな)
数値であればnumpy
,複合的なデータであればdict
と組み合わせるとスッキリします.
dict
概説
いわゆる連想配列ってやつです.
Pythonでは辞書型またはdictionary
(略してdict
)です.
記号としては{}
で,{key: value}
にように,一つ一つの要素が:
で結ばれます.
例に及んで詳しくは説明しません.レファレンスなどを参照してください.
dict
のkey
の型はなんでもよかった気がしますが,文字列か整数を使った方がわかりやすいです.
あと中身がいっぱいある時はkey
ごと改行すると見やすいです.またインデントは揃えましょう.(jsonみたいに)
例を載せておきます.
ncc = { 'name': 'ncc',
'full name': 'nakano computer club',
'estimate': 2015,
'web site': 'https://meiji-ncc.tech/'
}
*インデントに関してはいくつか宗派があるのでお好みのものを使用しましょう.
また一行一要素で長すぎる場合は,3個ずつなどにしたほうが見やすい場合もあります.
この辺は内容に合わせて臨機応変に対応しましょう.
いいとこ
- わかりやすい
- jsonとの相互利用がしやすい
- 参照に文字列が使える
わるいとこ
- 慣れないとエラーのもと
- 深くなると取り出しづらい
-
dic['first']['second']['third']
みたいになっていく
所感
値とその名前が重要になる時はこいつで決まり!
数字でも間隔が不規則な時はdict
のほうが扱いやすいです.
あと,pandas
(ライブラリ)のDataFrame
に変換する前に一旦dict
でまとめることも多いです.そのほうが扱いやすいので.
key
とvalue
を結びつけるイメージで使うといいかと思います.
なんやかんやjsonとの対応が便利だったり..(jsonライブラリ使えば読み書き簡単)
set
概説
set
は**被り禁止のlist
**というのが端的な表し方です.
記号は{}
でdict
と同じですが,:
は使いません.
list
と同じように,
で値をポンポン並べていきます.
なので,{}
で囲まれて,,
がいっぱいあったらset
です
厳密には集合を表すものです.
なので集合演算もできます.(ここでは触れませんが)
list
は[0, 1, 2, 1, 0]
のように同じ値を持つことができますがset
ではできません.
上記のlist
をset
に変換すると{0, 1, 2}
となります.
これは数字以外でも同じです.
逆を言えば,被りをなくたい時はset
に変換するといけます.
この場合,リストとしてまた扱いたい場合はset
の上からlist
に変換してリストに戻す必要があります.
例を書いておきます.
list_duplicate = [0, 1, 2, 2, 1, 0, 3]
list_non_duplicate = list(set(list_duplicate))
print(list_non_duplicate) # out: [0, 1, 2, 3]
いいとこ
- 被りなく入れられる
- 集合演算ができる
わるいとこ
- 要素の順番にルーズ
- (
list
から変換した場合,被りが除かれてその分前につめます) - 記号が
dict
と一緒でわかりづらい(若干)
所感
最初からset
で使うことはあまりないです.
被りをなくしたいときや,複数のリストの要素の共通部分などを抽出する時などにリストから変換することが多いです.
なので,実装時に集合的アプローチをとるときに使うものと思っておけばいいと思います.
tuple
概説
tuple
は**ちょっとお堅いlist
**と言うべきでしょうか.
少しクセがあります.
記号は()
です.
基本はlist
のようなものですが,色々違います.
大まかに言えば,一度作ったものをいじくることができません.
後ろに別のタプルを追加することはできます.(1)
タプル自体を丸々別のものに変えることもできます.(2)
その他,要素の書き換えなどはできません.
やや難しいので,実例で示します.
t = (0, 1, 2)
# 後ろにタプルを追加
t += (3, 4) # OK(1)
# タプル自体の書き換え
t = (0, 1, 2) # OK(2)
# 要素の書き換え
t[0] = 1 # Error
代入や,削除などにはそもそもメソッドが用意されてません.
また,要素の書き換えができないので,特定の順番を変えることもできません.
この辺をやるには,list
に変換する必要があります.
いいとこ
- 一回作ったら書き換えできない
- 順番も作成時のものが常に保証される
- 挙動を確定させられる
- 定数として使える
わるいとこ
- 柔軟性が皆無
- 扱いづらい
- エラーのもと
所感
Pythonには定数を表す型(jsでいうconst)がないので,それを擬似的にtuple
で行うことができます.
ただ,動的なことはいっさいできないので,あまり使いません.
ライブラリのメソッドの返り値がtuple
のことがあるので,そこでつかうぐらいです.
続いてPythonのライブラリであるnumpy
における配列系の説明に移ります.
その前に,numpy
の説明をさっくりします.
(補足)numpy
とは
numpy
は線形代数でやる行列演算を行えるライブラリです.
配列の要素同士の足し算,引き算.
配列全体に数値をかける時などに使えます.
もっと高度なこともできますが,配列の数値計算が便利になると思っておけば大丈夫です.
numpy.array
/numpy.ndarray
概説
numpy
の1次元の配列がnumpy.array
です.
多次元はnumpy.ndarray
になります.
1次元でも多次元でも扱いはあまり変わりません.
こいつはライブラリなので,特定の記号で表すことはできません.
list
などをnumpy.array()
で囲んであげると変換されます.
- 多次元でも
numpy.array()
で囲みます,numpy.ndarray
では囲めません.
list
との違いは何と言っても,配列同士の演算ができることです.
ただ,空のnumpy.array
を作成することはできません.
list
から変換しましょう.
import numpy as np
# 通常のlistを定義
list_num0 = [0, 1, 2, 3, 4]
# numpy配列に変換
np_num0 = np.array(list_num0)
print(np_num0) # out: [0 1 2 3 4]
# 直接numpy配列を生成
np_num1 = np.array([5, 6, 7, 8, 9])
print(np_num1) # out: [5 6 7 8 9]
# numpy配列をlistに変換
list_num1 = list(np_num1)
print(list_num1) # out: [5, 6, 7, 8, 9]
# list,numpy配列それぞれを2倍してみる
list_num0_twice = 2*list_num0
print(list_num0_twice) # out: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
np_num0_twice = 2*np_num0
print(np_num0_twice) # out: [0 2 4 6 8]
# list, numpy配列でそれぞれ足し算してみる
list_num_add = list_num0 + list_num1
print(list_num_add) # out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
np_num_add = np_num0 + np_num1
print(np_num_add) # out: [ 5 7 9 11 13]
このように,配列の要素内の演算をnumpy.array
では簡単にやることができます.
いいとこ
- 配列の要素同士の演算を簡単にできる
- 多次元でも扱いやすい
- 計算が早い(ことが多い)
わるいとこ
- 慣れないと扱いにくい
- 1つ1つの長さが違う多次元配列には向かない
- 数値以外は使いづらい
所感
数学的なことをやるなら絶対numpy
です.
scipy
という応用的な計算(積分とか)ができるライブラリとも相性がいいです.
計算をいっぱいやるときに慣れてきたら,使ってみるといいと思います.
list
との違いを把握すれば,使いやすいです.
numpy.matrix
- 使ったことない
- 色々調べましたが,
ndarray
使えばいいみたいです - m×nの行列を使う時は便利みたい
- 以上.
まとめ
それぞれを一言でまとめるとこうなります.
list
: 基本
dict
: 名前と値の結びつきが強い
set
: 被りなしのlist
tuple
: 書き換え不可のlist
numpy.array
/numpy.ndarray
: 数値計算特化list
numpy.matrix
:numpy.ndarray
とこんがらがるから使わない
フローチャート
個人的にどれを使うかを決める時のなんとなくのフローを図で示すとこうなります.
実際はもう少し複雑ですが,慣れるまではこんな感じで考えるといいかと思います.
tuple
は使わないので入ってません.
(本当はset
もあまり使わない)
終わりに
今回はPythonの配列っぽいやつらを比べました.
細かいところやライブラリを含めるともっとあります.
しかし,この辺が基本となるので,ここが理解できれば,他の理解も捗るかと思います.