みなさんこんにちは、ZeroTerasu(@ZeroTerasu)です。
今回は、Pythonでのデータ加工で役立つスライスについて解説致します。
文字列に対しては、エクセルのLEFT, MID, RIGHT関数のように指定した部分を選択してくれます。
リスト・タプルに対しても同様に、[]でインデックスを指定して特定の要素を抽出することが可能です。
スライスの基本的使い方:オブジェクト[start:stop:step]
・スライスでは、開始位置と終了位置のインデックスをコロンを挟んで四角括弧で囲みます。
・start <= スライス範囲 < stop の範囲が選択されることになります。
・stop未満となることに注意が必要。stopは対象外となるので、stopには、終点+1を指定する。
stepを記述した場合、stepの数値分のインデックスを飛ばしながら抽出することになります。
尚、stepは省略可能で、省略時は「step = 1」として扱われます。
文字列に対するスライス
str = 'string'
str[1:3]
# 実行結果
'tr'
上記の例では、’string’という文字列の2番目(インデックス番号=1)から4番目(インデックス番号=3)の文字を抽出するようにスライスを記述しています。
リストに対するスライス
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[1:3]
# 実行結果
[1, 2]
上記の例では、0-9の数字のlistに対して、2番目(インデックス番号=1)から4番目(インデックス番号=3)の数字を抽出するようにスライスを記述しています。
続いて、stepを指定した場合の挙動を確認します。
stepを指定した場合の挙動
str = 'string'
str[1:6:2]
# 実行結果
'tig'
上記の例では、’string’という文字列の2番目(インデックス番号=1)から6番目(インデックス番号=5)の文字を抽出するようにスライスを記述しています。
尚、冒頭での説明の通り、スライスではstopに指定したインデックス番号は抽出範囲に含まれないため、上記コードでは、「終端インデックス番号の5 + 1 = 6」をstopに指定しています。
このように、スライスでは範囲外のインデックスを指定してもエラーになりません。スライスが丁度良いところを忖度して抽出してくれます。
また、念のためにリストでの挙動について下記に記述します。
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[1:6:2]
# 実行結果
[1, 3, 5]
stepのみ指定した場合
str = 'string'
str[::2]
# 実行結果
'srn'
startとstopを省略し、コロン2つを続けてstepのみ指定することも可能です。
その場合、start = 0 と見なされ、0から始まりstepで指定した増分のインデックス番号が抽出されます。
上記コードの例では、インデックス番号=0,2,4 の文字が抽出されています。
マイナスの値で後ろからスライス
startとstopをマイナスの値で指定すると、オブジェクトの後ろからスライスすることが出来ます。
マイナス指定は少し取っつきにくいと思いますので、公式ドキュメントに記載されているスライス指定の考え方についての記事を参考にして下さい。
スライスされる文字列 = between start and stop
下記の説明分のように、startで指定したインデックスを始点として、stopで指定したインデックスを終点とした間の範囲が選択されるということを意識して頂ければイメージが湧きやすいかと思います。
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
マイナス指定については、複数パターンありますので、それぞれ例示します。
str = 'string'
str[-1]
# 'g'
str = 'string'
str[-5:-1]
# 'trin'
str = 'string'
str[-2:]
# 'ng'
stepをマイナスで指定した場合
・stepをマイナスで指定すると、逆向きにスライスされます。
・stop => start の向きにスライスされます。
・stop < start で指定する必要があります。
*stop > startとすると、下記の2番目の例のように空になります。
str = 'string'
str[::-1]
# 'gnirts'
str = 'string'
str[2:5:-1]
# '' <= start(インデックス番号=2='r')を始点として左方向に文字列抽出するプログラムになっているが、始点から左方向は指定範囲外のため空になってしまいます。
str = 'string'
str[5:1:-1]
# 'gnir'
スライスを使った代入
スライスで選択した範囲に値を代入することが出来ます。(既に値がある場合は、置換になります。)
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = [300, 400, 500]
list
# 実行結果 [0, 1, 2, 300, 400, 500, 6, 7, 8, 9] インデックス番号=3-5の要素がそれぞれ置換されています。
#(stop = 6 のため、インデックス番号=6 は範囲外になることに注意)
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = [300, 400]
list
# 実行結果 [0, 1, 2, 300, 400, 6, 7, 8, 9] インデックス番号=3-5の要素が[300, 400]に置換されています。
# このように、大きさの異なるオブジェクトで置換することも可能です。
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = [300]
list
# 実行結果 [0, 1, 2, 300, 6, 7, 8, 9] 上記と同様に異なる大きさのオブジェクトで置換されています。
代入出来るオブジェクトは、文字列・リスト等のイテラブルオブジェクトのみです。
下記の「例3」のスカラーを代入するとエラーとなります。
# 例1
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = 'string'
list
# 実行結果 [0, 1, 2, 's', 't', 'r', 'i', 'n', 'g', 6, 7, 8, 9]
# 例2
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = ['string']
list
# 実行結果 [0, 1, 2, 'string', 6, 7, 8, 9]
# 例3
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = 300
list
# 実行結果 TypeError: can only assign an iterable
空配列を代入すると、要素の削除が出来ます。
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list[3:6] = []
list
# 実行結果 [0, 1, 2, 6, 7, 8, 9]
二次元配列の例
二次元配列については、リストの各要素がリストであり、スライスが適用されるのは各要素であるリストになります。
下記の例では、二次元配列の各要素である各リストの中の各要素(最初のリストでは、「1」,「2」,「3」)からインデックス番号=0, 1 (最初のリストでは、「1」、「2」)のみを新たな配列に格納しています。すなわち、要素の削除を実行しています。
import numpy as np
list = []
list = np.arange(1,10).reshape(3,3).tolist()
list
#実行結果 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
list2 = []
list2 = [i[:2] for i in list]
#実行結果 [[1, 2], [4, 5], [7, 8]]
コメント