schedule2019-01-17

Python3の配列でよく使う小技のまとめ

Pythonの配列list)はたくさんできることがあって他の言語よりも扱いやすいな〜と思います。 なんとなく配列は使えるけど、もっと良い方法を探している方向けに書きます。 配列操作がわかってくるとPythonのコードもスッキリしてきますので、是非使いこなしていきましょう。

以下はPython3.6で動作確認しました。 出力や操作後の配列の中身は# >> もしくは"""出力"""で表現しています。

配列の宣言

空(から)の配列の宣言

arr = []

内包表記

# 月
month = [m for m in range(1, 13)]
# >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

全て同じ値

zero = [0 for m in range(10)]
# >> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

こっちでも大丈夫

zero = [0] * 10
# >> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

奇数/偶数の配列を作る

奇数

odd = [i for i in range(1, 12, 2)]
# >> [1, 3, 5, 7, 9, 11]

偶数

even = [i for i in range(0, 12, 2)]
# >> [0, 2, 4, 6, 8, 10]

結合 +, extend()

arr1 = ['a', 'b', 'c']
arr2 = ['D', 'E', 'F']

# 結合
arr3 = arr1 + arr2
print(arr3)
# >> ['a', 'b', 'c', 'D', 'E', 'F']

arr1.extend(arr2)
print(arr1)
# >> ['a', 'b', 'c', 'D', 'E', 'F']

四則演算

内包表記を使って要素に対し四則演算できます。

arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

[i + 100 for i in arr]
# => [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]


[i * 3 for i in arr]
# => [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

numpy

numpyを使うと直感的に四則演算できます。 行列のベクトルスカラーを理解してからだとより自在に扱えるようになるので、学習がオススメです。

import numpy as np

arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

arr = np.array(arr)
arr
# >> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr + 1
# >> array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

arr / 10
# >> array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

arr + arr
# >> array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

要素を取得

[]に数値を入れるとそこの要素を取得できます。 先頭の要素は0番目で末尾はlen(arr) - 1番目です。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

# 先頭の要素
arr[0]
# >> 1

# 末尾の要素
arr[len(arr) - 1]
# >> 12
# これもOK
arr[-1]
# >> 12

配列のサイズを越えるとエラーがでます。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
arr[100]

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

最大 max/最小 min

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

max(arr)
# >> 12

min(arr)
# >> 1

何番目の要素か index

arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
arr1.index(3)
# >> 2

arr2 = ['苺', 'バナナ', 'apple']
arr2.index('apple')
# >> 2

要素がないときはエラー。

arr1.index(13)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 13 is not in list

部分配列 [start:end:step]

部分配列は[start:end:step]で簡単に分けられます。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
arr[6:]
# >> [7, 8, 9, 10, 11, 12]

arr[:7]
# >> [1, 2, 3, 4, 5, 6, 7]

arr[::2]
# >> [1, 3, 5, 7, 9, 11]

arr[1::2]
# >> [2, 4, 6, 8, 10, 12]

条件判定

要素を含むか in / not in

if文などの条件式で手軽に判定できます。

# 奇数
odd = [e for e in range(1, 12, 2)]
# >> [1, 3, 5, 7, 9, 11]

print( 5 in odd )
# >> True
print( 2 in odd )
# >> False

print( 2 not in odd )
# >> True

順序の変更

昇順/降順 list.sort() sorted()

要素の値によって昇順/降順できます。 数値だけでなく文字(文字列)や日付(datetime)もソートできます。

昇順

arr = [12, 9, 3, 2, 5, 1, 10, 7, 6, 8, 4, 11]

sorted(arr)
# >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
arr
# >> [12, 9, 3, 2, 5, 1, 10, 7, 6, 8, 4, 11]

arr.sort()
arr
# >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

sorted()は新しくソートした配列を作成するのに対し、.sort()は対象の配列をソートして更新します。s

降順 reverse=Trueを指定する。

arr = [12, 9, 3, 2, 5, 1, 10, 7, 6, 8, 4, 11]

sorted(arr, reverse=True)
# >> [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
arr
# >> [12, 9, 3, 2, 5, 1, 10, 7, 6, 8, 4, 11]

arr.sort(reverse=True)
arr
# >> [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

逆順 list.reverse()

arr.reverse()
arr
# >> [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

シャッフル

標準のrandomモジュールを使う。

import random

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
random.shuffle(arr)
arr
# >> [11, 2, 5, 12, 9, 3, 10, 7, 6, 4, 8, 1]

# 毎回異なる順序
random.shuffle(arr)
arr
# >> [8, 3, 2, 11, 9, 7, 12, 6, 5, 4, 1, 10]

値とインデックスを同時に扱う enumerate()

arr = ['a', 'b', 'c']

for i, val in enumerate(arr):
  print(i, val)

"""出力
0 a
1 b
2 c
"""

同時に複数の配列を扱う zip()

zip()は同時に2つ以上の配列が扱えます。

lower = ['a', 'b', 'c']
upper = ['A', 'B', 'C']

for l, u in zip(lower, upper):
  print(l, u)

"""出力
a A
b B
c C
"""

配列の長さが異なる場合は、短い方に合わせられます。

lower = ['a', 'b', 'c']
upper = ['A', 'B', 'C', 'D']

for l, u in zip(lower, upper):
  print(l, u)

"""出力
a A
b B
c C
"""

要素のフィルタリング

filter()を使うと条件に合った要素だけ取り出せる。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

under_5 = list(filter(lambda x: x < 5, arr))
print(under_5)
# >> [1, 2, 3, 4]

odd = list(filter(lambda x: x%2 == 1, arr))
print(odd)
# >> [1, 3, 5, 7, 9, 11]

lambda xxは要素。 : の後にくる条件式が正となるものが取り出せる。

内包表記でフィルタリング

内包表記もフィルタリングできる。 ifを加えて真となる要素だけ抽出。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

even = [x for x in arr if x%2 == 0]
# >> [2, 4, 6, 8, 10, 12]

空文字の要素を取り除く

ファイルを読み込んでからリスト化すると改行コード空文字が含まれていたりします。 文字列の前後の空文字などを除去するstrip()filter()を使えば2行でかけました。

# 空文字などが混ざった配列
arr = ['2019\n','  \n', '01\n', ' suzu6\n', '\n', ' ']

dst = [x.strip() for x in arr]
# >> ['2019', '', '01', 'suzu6', '', '']

dst =  list(filter(None, dst))
# >> ['2019', '01', 'suzu6']

空文字に値するものだけ取り除く場合はこちらもできる。

arr = ['2019\n','  \n', '01\n', ' suzu6\n', '\n', ' ']

dst = [x for x in arr if x.strip() is not '']
# >> ['2019\n', '12\n', ' suzu\n']

重複する要素の削除

集合set)にすると重複する要素を削除できます。

upper = ['A', 'B', 'C', 'D', 'B', 'D', 'B', 'C', 'D']
result = list(set(upper))
result
# >> ['A', 'B', 'D', 'C']

要素の数を求める

upper = ['A', 'B', 'C', 'D', 'B', 'D', 'B', 'C', 'D']
upper.count('A')
# >> 1
upper.count('B')
# >> 3

要素の数を全て求めてdictにする

result = {}
upper = ['A', 'B', 'C', 'D', 'B', 'D', 'B', 'C', 'D']

for c in set(upper):
  result[c] = upper.count(c)

print(result)
# >> {'A': 1, 'B': 3, 'D': 3, 'C': 2}

標準モジュールのcollectionsを使うともっと簡単です。

import collections

upper = ['A', 'B', 'C', 'D', 'B', 'D', 'B', 'C', 'D']
result = collections.Counter(upper)

print(result)
# >> Counter({'B': 3, 'D': 3, 'C': 2, 'A': 1})
print(dict(result))
# >> {'A': 1, 'B': 3, 'C': 2, 'D': 3}

collectionsについてはPythonのCounterでリストの各要素の出現個数をカウント - note.nkmk.meが詳しい。

区切り文字 <-> 配列

CSVやExcelのデータを扱うときに便利。

区切り文字 -> 配列

split()を使う。カンマ区切りは','で分けられます。

string = "a,b,c,d,e"

string.split(',')
# >> ['a', 'b', 'c', 'd', 'e']

区切り文字 <- 配列

"".join()を使う。 カンマ区切りへは','で変換できます。

arr = ['a', 'b', 'c', 'd', 'e']

"".join(arr)
# >> 'abcde'

",".join(arr)
# >> 'a,b,c,d,e'

文字列 <-> 配列

文字列(str型)も配列のように[:]で扱えますが、配列の関数をlist()に入れ込めばlist型にできます。

string = 'Hello World'

print(string.split())
# >> ['Hello', 'World']

arr = list(string)
# >> ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']

''.join(arr)
# >> 'Hello World'

要素をそれぞれ処理する

配列の要素をそれぞれ処理したいときは結構あります。 for文よりもmapを使うと簡潔に早く処理できます。 内包表記でも構いません。

例えば、要素を全て文字にする場合

for文

arr = [1, 2, 3, 4, 5]

# for
for i, n in enumerate(arr):
  arr[i] = str(n)

print(arr)
# >> ['1', '2', '3', '4', '5']

内包表記

arr = [1, 2, 3, 4, 5]

arr = [str(i) for i in arr]
print(arr)
# >> ['1', '2', '3', '4', '5']

map

arr = [1, 2, 3, 4, 5]

arr = list(map(str, arr))
print(arr)
# >> ['1', '2', '3', '4', '5']

自作の関数で処理をする

ファイルのリストを作成して中身を配列に格納したいときなど、自作の関数を処理したいこともあります。 内包表記mapでやってみる。

def read_csv(filename):
  # ファイルの中身を読み込んで返す関数
  with open(filename, mode='r') as file:
    return file.read()

file_list = ['./2017/01.csv', './2017/02.csv', './2017/03.csv']

# 内包表記
arr = [read_csv(filename) for filename in file_list]

# map
arr = list(map(read_csv, file_list))

その他

参考

以下はこの記事を書く上で参考にした記事や参考書です。

オンライン学習

Udemy Python3の入門オンライン講座 Udemy Pythonコース