リファクタリングをPythonで実践!基本手法とコード例・注意点まで
1.Pythonのzip関数とは?
Pythonのzip関数は、2つ以上あるリスト型や辞書型、タプル型などの要素を集約することができるPython標準の関数です。zip関数内で使用できる引数としては、
・リスト型
・タプル型
・辞書型
・セット型
などが挙げられます。
zip関数を用いれば配列データが複数ある場合でも、効率のいいデータ処理が可能です。しかし、zip関数を知らない場合は余計なfor文などを用いることになり非効率となる可能性があります。
統計的なデータをPythonで処理する場合にzip関数は役立ちますので、ここで基本的な情報をおさえておきましょう。
2.Pythonのzip関数の基本的な使い方
まずはPythonのzip関数の基本的な使い方を解説します。
①複数のリストのインデックスを同時に取得する
2つのリスト型データを使って、リスト内の要素を取得する例をご紹介します。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20 , 18]
zipped = zip(names, ages)
print(zipped)
print(list(zipped))
結果
<zip object at 0x7fc08c8406e0>
[('桃太郎', 16), ('浦島太郎', 20), ('かぐや姫', 18)]
配列:namesと対になる配列:agesがあると仮定してください。zip関数で2つの配列を処理すると、zipオブジェクトが返されます。そのzipオブジェクトに対してリスト関数を用いると、両配列内の要素が順番に取り出されていることが確認できます。
キーワードの補足:リストと配列
本稿では「リスト」と「配列」、似た意味を持つキーワードが登場します。文調による使い分けだけであり、意味は同じです。
②インデックス数が異なる場合
zip関数は、2つ以上の配列等を処理すると解説しました。この時、配列内の要素数が異なる場合は、短い要素数の配列が優先されます。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20 ]
zipped = zip(names, ages)
print(list(zipped))
結果
[('桃太郎', 16), ('浦島太郎', 20)]
配列:agesにかぐや姫の値はないため、zip関数の処理後にかぐや姫の項目がないことがわかります。
③for文で繰り返し処理をする
zip関数で複数の配列等を処理した場合、zipオブジェクトとして値が返されます。このままでは処理内容がわからないですが、for文を用いることでzip関数の処理内容を判断可能です。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, 18 ]
zipped = zip(names, ages)
for name, age in zipped:
print( '名前:' + name + ' 年齢:' + str(age) )
結果
名前:桃太郎 年齢:16
名前:浦島太郎 年齢:20
名前:かぐや姫 年齢:18
④タプルとして取得する
zip関数で処理した値をまとめて取得する場合は、以下のように記述し、タプル型としてデータを取得できます。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, 18 ]
zipped = zip(names, ages)
for value in zipped:
print(value)
print(type(value))
結果
('桃太郎', 16)
('浦島太郎', 20)
('かぐや姫', 18)
<class 'tuple'>
⑤dictを使いリストから辞書を作る
zip関数で得た結果にdict関数を加えると、2つの配列内要素を辞書型で管理できます。配列内要素に対してキーを指定してもう一方の値を取得できますので、データ活用の幅が一気に広がりそうですね。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, 18 ]
zipped = zip(names, ages)
dictionary = dict(zipped)
print(type(dictionary))
print(dictionary)
print(dictionary['かぐや姫'])
結果
<class 'dict'>
{'桃太郎': 16, '浦島太郎': 20, 'かぐや姫': 18}
18
3.Pythonのzip関数の応用的な使い方
続いて、Pythonのzip関数の応用的な使い方を4つご紹介します。
①zip関数の処理結果にインデックス番号をふる
処理内容によっては、zip関数で得た結果にインデックス番号を加えたい場合はどうすればよいでしょうか。enumerate関数を加えることで処理結果にインデックス番号を加えられます。
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, 18 ]
for i, (name, age) in enumerate(zip(names, ages)):
print('No. ' + str(i) + ' 名前:' + name + ' 年齢:' + str(age) )
結果
No. 0 名前:桃太郎 年齢:16
No. 1 名前:浦島太郎 年齢:20
No. 2 名前:かぐや姫 年齢:18
②配列の長さを合わせる方法
zip関数は配列等の長さが異なる場合、短い要素数のデータが優先されます。しかし、要素数が異なる場合も同じ要素数として処理したい場合もあるでしょう。その場合は、zip_longest()関数を用いれば同じ要素数のデータとして処理できます。
from itertools import zip_longest
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, ]
print('ケース1')
for name, age in zip_longest(names, ages):
print(name, age)
print('\nケース2')
for name, age in zip_longest(names, ages, fillvalue=50):
print(name, age)
結果
ケース1
桃太郎 16
浦島太郎 20
かぐや姫 None
ケース2
桃太郎 16
浦島太郎 20
かぐや姫 50
zip_longest関数の引数にfillvalueを加えると要素不足時にfillvalueの値が加えられます。ただし、fillvalueは1つの値しか設定できません。
③2次元配列の転置
1つの配列内に複数配列データがある場合の、zip関数の使用例をご紹介します。
array_data = [ ['桃太郎', '16歳', '男性'], ['かぐや姫', '18歳', '女性'] ]
print(list(zip(*array_data)))
zipped = map(list, zip(*array_data))
print(zipped)
print(list(zipped))
結果
[('桃太郎', 'かぐや姫'), ('16歳', '18歳'), ('男性', '女性')]
<map object at 0x7fc08c7ad390>
[['桃太郎', 'かぐや姫'], ['16歳', '18歳'], ['男性', '女性']]
配列内の配列データを整形したい場合にzip関数を知っておくと、上記のように要素の種類毎にデータを集めることが可能です。
map関数を使うことでタプル型をリスト型に一括変換しています。
④pandasのデータフレームに変換する
zip関数で得た結果をpandas(Pythonライブラリ)のデータ形式に変換する方法をご紹介します。
import pandas as pd
names = ['桃太郎', '浦島太郎', 'かぐや姫']
ages = [16, 20, 18]
zipped = zip(names, ages)
df = pd.DataFrame(zipped, columns = ['名前', '年齢'])
display(df)
結果
名前 | 年齢 | |
0 | 桃太郎 | 16 |
1 | 浦島太郎 | 20 |
2 | かぐや姫 | 18 |
行と列が置換していることが確認できます。
プログラムの補足:display()
pandasのデータフレームは、print()よりdisplay()の方が結果を見やすいため、上記ではdisplay()を用いています。Pythonの実行環境によってdisplay関数のインポートが必要になります。
4.Pythonのzip関数でありがちなエラーやトラブル
zip関数の引数にはリスト型がよく用いられますが、辞書型も使用できます。辞書型をzip関数で処理した場合は、リスト型と異なる結果を出力しますので注意が必要です。
リスト型をzip関数で処理した場合 | 辞書型をzip関数で処理した場合 |
|
|
元データがリスト型の場合は対になる値が保持されていますが、辞書型の場合は対になる値が保持されていないことが分かります。