本稿はNCC Advent Calender 2019の14日目記事です.
はじめに
プログラム書くとき,配列って大事です.
しかし,Python 配列っぽいの多すぎ問題があります.
Pythonの標準機能だけでも
list,dict,set,tuple
があります.
加えてnumpyなどのライブラリを使い始めるとさらに似たようなものが増えます.
なので,今回はそいつらを以下の4点でまとめて使い分けができるように解説していきます.
- 概説
- いいとこ
- わるいとこ
- 所感
あくまで使い分けのための記事なので,細かい使い方などには触れません.
また,配列が何かはなんとなくわかってる前提で書きます.
さらになるべく簡単な言葉を使って説明します.
- 説明の易化のために,本質とは異なる説明をしている場合があります.
対象読者
- Pythonを使い始めたけど,配列多すぎて困ってる
- 他の言語やってたけど,Pythonでの各配列の立ち位置がわからない
- 実装中,ここどれ使うべきかなってなった人
- numpyなどのライブラリを使い始めた人
とりあげるもの
Python標準機能
listdictsettuple
ライブラリ
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の配列っぽいやつらを比べました.
細かいところやライブラリを含めるともっとあります.
しかし,この辺が基本となるので,ここが理解できれば,他の理解も捗るかと思います.