見出し画像

Python基礎1:配列(List型)


概要

pythonの配列(List)に関する全般的な内容です。リスト使用時によく使う操作を記載しました。

1.リストのイメージ

 データ(数字・文字列・リスト・辞書など何でもよい)を順番に保持できる箱であり、イテラブルでありfor文で使用可能です。

2.リストの作成方法

2-1.一般的な作成

一般的な方法:[]の中に,カンマで区切って要素を入れます。

[In]
nums = [1, 2, 3, 4, 5] #要素が数値型
nums_str = ['1','2','3','4','5'] #要素が文字列型

[Out]
[1, 2, 3, 4, 5]
['1', '2', '3', '4', '5']

2-2.イテラブルをリスト化

 イテラブル(for文で回せる要素)をlistに入れます。

[In]
nums = list(range(5)) #list()の中にfor文で使用するrange()を使用
words_str = list('ABCDE') #文字列はイテラブル

[Out]
[0, 1, 2, 3, 4]
['A', 'B', 'C', 'D', 'E']

2-3.内包表記

 イテラブルを使って[]内にfor文を記載します。if文やネストも可能であり、基本的には左から右に見れば通常通りの記載方法と同じです。

[In]
nums_comp = [i for i in range(5)] #[]の中にfor文を記載
nums_comp_if = [i for i in range(10) if i%2==0] #if文も可能:参考例は偶数(2で割ると余りが0)

[Out]
[0, 1, 2, 3, 4]
[0, 2, 4, 6, 8]

 より複雑な記載もできますが可読性が下がるため推奨しません。

[In]
nums_comp_next = [(i, j) for i in [1,2] for j in [10,20]] #ネスト:通常は見にくいため使用しない
nums_comp_elif = [i if i % 2==0 else None for i in range(10)] #if文で偶数はそのまま、奇数はNoneを返す

[Out]
[(1, 10), (1, 20), (2, 10), (2, 20)]
[0, None, 2, None, 4, None, 6, None, 8, None]

2-4.参考:文字列のリスト化

 文字列を処理(メソッドを使用)してリストに変換します。なお文字列もイテラブルのため2-2, 2-3の処理も可能です。

[In]
list_iroha1 = 'い ろ は に ほ へ と'.split() #split()内に何も入れないと空白文字(スペースやタブなど)を区切り文字と判定
list_iroha2 = 'ち,り,ぬ,る,を'.split(',') #split()内に区切り文字を入力

[Out]
['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と']
['ち', 'り', 'ぬ', 'る', 'を']

【参考】
アルファベットの作成:ライブラリstringを使用して作成できます。

[In]
import string
alphabets_lower = [alphabet for alphabet in string.ascii_lowercase] #小文字
alphabets_upper = [alphabet for alphabet in string.ascii_uppercase] #大文字

[Out]
['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']

3.リストの計算

 リストはデータを保持する箱とすると、リストの計算は箱をつなぎ合わせるイメージとなります。

[In]
a = [1,2,3]
b = [10,20,30]
a*3
a+b

[Out]
[1, 2, 3, 1, 2, 3, 1, 2, 3] 
[1, 2, 3, 10, 20, 30]

 数値として計算する場合は内包表記や組み込み関数のmap()を使用します。map(関数、イテラブル)とすると、イテラブル内の要素を関数で処理してくれるためlist()に入れると関数処理後のリストを出力します。

[In]
nums = [1,2,3]

nums_double = [2*num for num in nums] #内包表記で値を2倍にして出力
f =lambda x:3*x #値を3倍にする関数f
nums_tri =list(map(f, nums)) #リスト内の値を3倍にして出力

print(f'値を2倍:{nums_double}')
print(f'値を3倍:{nums_tri}')

[Out]
値を2倍:[2, 4, 6]
値を3倍:[3, 6, 9]

4.リスト内のデータ確認

リスト内データを確認する場合は in リスト で確認できbool値を返します。

[In]
nums = [1,2,3]
print(1 in nums, 4 in nums)
[Out]
True False

[In]
words = ['A', 'B', 'C', 'DEFGH']
print('B' in words, 'D' in words, 'DEFGH' in words,)
[Out]
True False True

5.要素の抽出

5-1.単要素の抽出

リストからデータを抽出(要素へアクセス)は、リスト[インデックス]で記載します。インデックス番号は下図のとおり0から始まります。またマイナスで指定することで逆順で指定できます。

note1_List説明用 (1)
[In]
words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
words[0], words[1], words[-1], words[-2]

[Out]
('A', 'B', 'H', 'G')

 参考までにイテラブルな要素を1個ずつ抽出したい場合はnext(iter(イテラブル))を使用します。(リストでは使用しませんがpytorchのDataloaderの1個目を簡易に確認したいときに使います。)

[In]
words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
iterable= iter(words)
next(iterable)

[Out]
'A'

5-2.複数要素の抽出(スライス)

 複数要素をまとめて抽出するときはスライス機能を使用します。一般的にはリスト[開始インデックス:終了インデックス]と記載します※1。要素を飛ばして抽出も可能です※2。
※1 終了インデックス=ストップさせるインデックス番号のためデータは抽出されません。
※2 複数の記載方法がありますのでコード参照のこと。

[In]
print(words[0:3]) #index1-2まで抽出※3は終了するインデックス番号のため抽出しない
print(words[:3]) #index3より前の要素をまとめて抽出(index3は含まない)
print(words[3:]) #index3以降の要素をまとめて抽出(index3は含む)
print(words[:]) #全部抽出:ここではwordsと同じ。
print(words[0:-1]) #スライスで-1を使用する場合は最後の要素は抽出されない。
print(words[0:6:2]) #リスト[開始:終了:インデックスを飛ばす順]

[Out]
['A', 'B', 'C']
['A', 'B', 'C']
['D', 'E', 'F', 'G', 'H']
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
['A', 'B', 'C', 'D', 'E', 'F', 'G']
['A', 'C', 'E']

5-3.Index番号の抽出:index(<要素>)

 リスト内の要素の位置(インデックス番号)を取得する場合は、index()メソッドの中に要素を指定します。

[In]
words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

num_index1 = words.index('A')
print(f'Aのindex: {num_index1}')
num_index2 = words.index('E')
print(f'Eのindex: {num_index2}')

[Out]
Aのindex: 0
Eのindex: 4

6.要素の追加・更新・削除

6-1.要素の追加:append, extend, insert

  • Listの足し算:リスト同士をくっつける。※あまり見ない方法

  • appendメソッド:渡した値をそのままリストに追加する->一つの値をリストに追加するときに使用する。

  • extendメソッド:渡したリストを一つずつ指定リストに追加する(一つずつappendする)->リストの中の値を別のリストに個々に追加するときに使用する。

  • insertメソッド:挿入する位置を指定して値を追加

[In]
n1 = [1, 2, 3] 
n2 = [4, 5, 6]
n3 = [7, 8, 9]

#リストの足し算
nums = n1 + n2
print('nums:', nums)
#appendメソッド:
n1.append(n2) #n1は上書き
print('n1(n2追加後):', n1)
#extendメソッド:
n2.extend(n3) #n2は上書き
print('n2(n3追加後):', n2)
#insertメソッド:
n2.insert(0, 3) #n2の頭(index=0)に3を追加
print('n2(insert後):', n2)

[Out]
nums: [1, 2, 3, 4, 5, 6]
n1(n2追加後): [1, 2, 3, [4, 5, 6]]
n2(n3追加後): [4, 5, 6, 7, 8, 9]
n2(insert後): [3, 4, 5, 6, 7, 8, 9]

6-2.要素の更新

 抽出した要素にデータを代入することで更新できます。

[In]
nums = [1,2,3,4,5,6]
nums[0] = 100 #index0に100を代入
nums[1:3] = [200, 300] #index1-2に200, 300を代入
print(nums)

[Out]
[100, 200, 300, 4, 5, 6]

6-3.要素の削除:pop, del, clear

 リスト内からデータを削除するメソッドは下記があります。

  • pop:要素から抜く時に指定インデックスの数値を取得できます。

  • del:指定したインデックスの値をリストから削除します。

  • clear:リストの中身を空にします。

[In]
nums1 = [1,2,3,4,5]
nums2 = [6,7,8,9,10]

num_pop= nums1.pop(1) #nums1リストのindex1の値を抜き出す。この時、変数に入れることが可能
print(nums1) #pop抜出後のリスト
print(num_pop) #popで抜き出した値

[Out]
[1, 3, 4, 5]
2
[In]
del nums2[1] #delで削除:削除した要素を取得することはできない。

[Out]
[6, 8, 9, 10]
[In]
del nums2.clear() #clearでリストが空になる

[Out]
[]

 6-3-1.指定文字をリストから削除

 指定文字をリストから削除したい場合、メソッドはおそらくないため自作関数で対応します。

[IN]
from string import ascii_uppercase #'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
nums = [1,2,3,4]

def delitem_inList(listdata, value):
    val2idx = {v:k for k,v in enumerate(listdata)}
    
    out = [i for i in listdata if not value == i]
    return out

print(delitem_inList(nums, 2))
print(delitem_inList(ascii_uppercase, 'A'))

[OUT]
[1, 3, 4]
['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

7.ソート(並び替え)

7-1.数値のソート

 リストは作成時の指定通りにデータが保持されているため、最初とは別順に取得したい場合は並び替えが必要です。並び順が比較できるものであれば、直接sorted()を使用します。

[In]
import random
random.seed(0) #random値を固定:同じ引数を入れることで値を再現できる
nums = [1,2,3,4,5,6,7,8,9,10]
random.shuffle(nums) #値をshuffle

print(f'shuffle後:{nums}')
nums = sorted(nums)
print(f'sort後:{nums}')

[Out]
shuffle後:[8, 9, 2, 6, 4, 5, 3, 1, 10, 7]
sort後:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

7-2.文字列のソート

 数値以外の文字列も可能です。文字列型の数字は数値型とは異なる並び替えになります。

[In]
import string

nums_int = ['7','10','2','6','5','9','3','1','8','4']
print(f'sort前_nums_int:{nums_int}')
print(f'sort後_nums_int:{sorted(nums_int)}')

words = [word for word in string.ascii_lowercase][:10] #アルファベット10文字
random.seed(0) #random値を固定:同じ引数を入れることで値を再現できる
random.shuffle(words)
print(f'sort前_words:{words}', end="") #sort前:出力を改行しないためにオプションend=""追加
print(f'sort後_words:{sorted(words)}', end="")

[Out]
sort前_nums_int:['7', '10', '2', '6', '5', '9', '3', '1', '8', '4']
sort後_nums_int:['1', '10', '2', '3', '4', '5', '6', '7', '8', '9']

sort前_words:['h', 'i', 'b', 'f', 'd', 'e', 'c', 'a', 'j', 'g']
sort後_words:['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

数値と文字列が混ざっている場合はエラーが発生します。文字列を含む数値をソートしたい場合は、sorted(リスト、key=リスト内の要素を処理する関数)を使用します。
個人的なイメージとしては、keyに入れた関数で一度リスト内の要素を処理して、処理した値を比較することでソートします。よって関数をうまく作れば自由にソート可能です。

[In]
nums_strint = ['8','9','2',6,'4',5,'3',1,10,'7']
# sorted(nums_int) #これはエラーが発生する。
sorted(nums_strint, key=lambda x:int(x)) #defで関数式を書くのは仰々しいためlambdaを使用

#下記はsorted(nums_strint, key=lambda x:int(x))と同じ
def strtoint(x):
   return int(x)
sorted(nums_strint, key=strtoint) 

[Out]
[1, '2', '3', '4', 5, 6, '7', '8', '9', 10]

7-3.逆順

 順番に並び替えとは異なりますが、逆順にする場合はリスト[::-1]のように記載します。理解として、「スライスの抽出法としてリスト[開始:終了:ステップ]がありますが、開始終了に何も入れないと全選択でステップ-1->逆順に一個ずつ取ってください。」 と認識しています。

[In]
nums = [1,2,3,4,5,6,7,8,9,10]
nums_reverse = nums[::-1]
nums_rev2 = nums[::-2]

print(nums_reverse)
print(nums_rev2)

[Out]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[10, 8, 6, 4, 2]

7-4.シャッフル(ランダムに配置):random

 並び替えるのではなくバラバラにする場合、リストそのものにはそのような機能はなさそうなので"random"ライブラリを使用します。 
 処理方法として、重複無しの場合:①ランダムにサンプリング、②リストそのものをシャッフル(inplace方式) 重複ありの場合:内包表記で一つずつ抽出したものをリスト化します。

【重複無し】

[IN]
import random
from string import ascii_uppercase

random.seed(0) # 乱数のシードを固定

words = [i for i in ascii_uppercase] # A-Z
w_randomall = random.sample(words, k=len(words)) # A-Zをランダムに並び替え(全数)
w_random5 = random.sample(words, k=5) # A-Zをランダムに並び替え(5文字)

print(w_randomall) # 全数
print(w_random5) # 5文字

[OUT]
['M', 'Y', 'N', 'B', 'I', 'Q', 'P', 'Z', 'J', 'T', 'L', 'R', 'D', 'V', 'C', 'E', 'U', 'W', 'K', 'H', 'F', 'G', 'S', 'X', 'A', 'O']
['V', 'K', 'P', 'R', 'D']
[IN]
random.seed(0) # 乱数のシードを固定

words = [i for i in ascii_uppercase] # A-Z
random.shuffle(words) # A-Zをランダムに並び替え(全数)※shuffleは返り値がない
print(words)

[OUT]
['O', 'A', 'X', 'S', 'G', 'F', 'H', 'K', 'W', 'U', 'E', 'C', 'V', 'D', 'R', 'L', 'T', 'J', 'Z', 'P', 'Q', 'I', 'B', 'N', 'Y', 'M']

【重複あり】

[IN]
random.seed(0) # 乱数のシードを固定

words = [i for i in ascii_uppercase] # A-Z
print(f'random.choice()-> {random.choice(words)}') # A-Zからランダムに1文字選択

word_random5 = [random.choice(words) for i in range(5)] # A-Zからランダムに5文字選択
word_random5

[OUT]
random.choice()-> M
['Y', 'N', 'B', 'I', 'Q']

8.集合(set)

 集合はリストやタプルとは異なるイテラブルなオブジェクトの一つであり、特徴としては”要素の重複はできず、順番も保持しない”です。
 重複させたくない場合などに使用できます。なおTupleのようなset型を使用したい場合はfrozensetを使用します(紹介のみ)。

9-1.setの基本動作確認

 set型は{要素1, 要素2,・}のように記載します。リストと動作を比較するとわかるように重複した要素は削除されます。また簡単にlist型へ変換できます。

[IN]
nums = [1,2,3,3,3,4,4,5] #重複があるリスト
nums_set = {1,2,3,3,3,4,4,5} #重複がある集合(set)

print(type(nums), nums)
print(type(nums_set), nums_set)

print(list(nums_set))

[OUT]
<class 'list'> [1, 2, 3, 3, 3, 4, 4, 5]
<class 'set'> {1, 2, 3, 4, 5}
[1, 2, 3, 4, 5]

9-2.空setの作成・追加

 空のset型を作成するにはset()を使用します。

[IN]
numsets = set() #空の集合を作成
print(type(numsets), len(numsets), numsets)

[OUT]
<class 'set'> 0 set()

 データの追加はadd(), 削除はremove()またはpop()を使用します。

[IN]
numsets = set() #空の集合を作成
numsets.add(1) #集合に1を追加
numsets.add(2) 
numsets.add(3) 
numsets.add(4) 
numsets.add(4) #重複して追加しても追加されない
print(len(numsets), numsets)

numsets.remove(4) #集合から4を削除
print(len(numsets), numsets)

[OUT]
4 {1, 2, 3, 4}
3 {1, 2, 3}

9-3.値の抽出

 set型は順番を保持しないためindexで取得するとエラーとなります。

[IN]
nums_set = {1,2,3,3,3,4,4,5} #重複がある集合(set)
nums_set[0]

[OUT]
TypeError: 'set' object is not subscriptable

 データを取得する場合は(イテラブルのため)for文を使用します。

[IN]
nums_set = {1,2,3,3,3,4,4,5} #重複がある集合(set)
for i in nums_set:
    print(i)

[OUT]
1
2
3
4
5

9-4.集合の演算

 集合の特徴として「和集合、差集合、積集合、対称差などの数学的演算」を実施できます。ベン図を使用するとそれぞれの意味が理解しやすくなります。

[IN]
nums1 = {1,2,3,4}
nums2 = {3,4,5,6}

print('和集合:', nums1 | nums2) #和集合:num1とnum2の要素を含む集合
print('差集合:', nums1 - nums2) #差集合:num1の要素をnum2に含まない要素を含む集合
print('差集合:', nums2 - nums1) #差集合:num2の要素をnum1に含まない要素を含む集合
print('積集合:', nums1 & nums2) #積集合:num1とnum2の要素を含む集合
print('対象差:', nums1 ^ nums2) #対象差:num1とnum2の要素を含まない要素を含む集合

[OUT]
和集合: {1, 2, 3, 4, 5, 6}
差集合: {1, 2}
差集合: {5, 6}
積集合: {3, 4}
対象差: {1, 2, 5, 6}

9.ライブラリ紹介1:deque(データ数制御)

 リスト内に保持できるデータ数を制限してFIFOFirst in, Fisrt outで制御できるリストとしてdequeがあります。リスト内の数を制限したい時に便利です。

[In]
from collections import deque

nums= [] #通常のリスト
nums_deque = deque(maxlen=3) #maxlenで最大長を指定できる。

for i in range(10): #10回繰り返す
    nums.append(i) #通常のリストに追加
    nums_deque.append(i) #dequeに追加

print(nums)
print(nums_deque)
[Out]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
deque([7, 8, 9], maxlen=3)

10.ライブラリ紹介2:bisect(index取得)

 リスト内の指定箇所にデータを挿入したりindex番号を取得したい場合はbisectライブラリを使用します。

【bisectのメソッド】
●bisect():値のindex番号(挿入時の順序)をリスト内から探す
●insort():リスト内の順序を保持して値を挿入(※戻り値は無し)

[IN]
import bisect

lst = [1,3,5,7,9]
idx = bisect.bisect(lst, 2) #5が入るべき位置を返す
print(f'idx:{idx}')

bisect.insort(lst, 2) #メソッドのため、戻り値はなし
print(lst)

[OUT]
idx:1
[1, 2, 3, 5, 7, 9]

参考:参照渡しと値渡し

 リスト操作ではないですがリスト内のデータ取り扱いの注意点です。リスト内データのメモリ位置は数値や文字列とは異なります。
 参考例として数値を入れた変数1を変数2に代入して、変数1を更新したときの動きは下記のとおりです。

[In]
var1 = 10
var2 = var1 #変数1の値を変数2に渡している
print(id(var1), id(var2)) #オブジェクトが同じものか確認

var1 = 100
print(f'var1:{var1}  var2:{var2}')
print(id(var1), id(var2)) #オブジェクトが同じものか確認

[Out]
140703662876736 140703662876736
var1:100  var2:10
140703662879616 140703662876736

 変数1を変数2に代入時は同じものとして扱われるが、変数1に値を代入したときに変数1と変数2は別物として扱われます。
 次に上記をリストで同様に実行すると下記のとおりとなります。

[In]
nums1 = [1,2,3,4,5]
nums2 = nums1 #num1とnum2は同じものとして扱われる
print(id(nums1), id(nums2)) #オブジェクトが同じものか確認

nums1[0] = 100 #num1の更新はnum2にも反映される。
nums2[1] = 300 #num2の更新はnum1にも反映される。
print(f'nums1:{nums1}  nums2:{nums2}')
print(id(nums1), id(nums2)) #オブジェクトが同じものか確認

[Out]
2036420973120 2036420973120
nums1:[100, 300, 3, 4, 5]  nums2:[100, 300, 3, 4, 5]
2036420973120 2036420973120

 num1をnum2に代入する時、値を渡してコピーしているのではなく変数を参照しているだけです。よってリスト内の要素を更新すると連動して数値が変わるため、予期せぬ動作が起こる可能性があります。
 参照元を変えたくない場合はcopy()した値を使用するのがよいです。

[In]
nums1 = [1,2,3,4,5]
nums2 = nums1.copy() #num1とnum2は別物として扱われる
print(id(nums1), id(nums2)) #オブジェクトが同じものか確認

nums1[0] = 100 #num1の更新はnum2にも反映される。
nums2[1] = 300 #num2の更新はnum1にも反映される。
print(f'nums1:{nums1}  nums2:{nums2}')
print(id(nums1), id(nums2)) #オブジェクトが同じものか確認

[Out]
2036420768320 2036421172096
nums1:[100, 2, 3, 4, 5]  nums2:[1, 300, 3, 4, 5]
2036420768320 2036421172096

参考資料

あとがき

 少しずつリスト用ライブラリ追加します。


この記事が気に入ったらサポートをしてみませんか?