python `itertools.zip_longest`で長さの異なるイテラブルを組み合わせる

## はじめに

Pythonでリストやタプルなどの複数のイテラブルを組み合わせる際、`zip()`関数がよく使われます。しかし、`zip()`は最も短いイテラブルの長さに合わせて処理を終了するため、長さの異なるイテラブルを扱う場合に不便なことがあります。そこで今回は、`itertools.zip_longest()`を使って、長さの異なるイテラブルを効果的に組み合わせる方法を紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/da631c81-a117-01b1-a4c0-d8d1b7fcda5d.png)

## `zip()`の制限

まず、通常の`zip()`関数の動作を確認してみましょう。

```python
list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']

for item in zip(list1, list2):
    print(item)
```

出力:
```
(1, 'a')
(2, 'b')
(3, 'c')
```

ご覧のように、`zip()`は短い方のリスト（この場合は`list2`）の長さに合わせて処理を終了しています。

## `itertools.zip_longest()`の使用

`itertools.zip_longest()`を使うと、最も長いイテラブルの長さまで処理を続けることができます。足りない要素は指定した値（デフォルトは`None`）で埋められます。

```python
from itertools import zip_longest

list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']

for item in zip_longest(list1, list2):
    print(item)
```

出力:
```
(1, 'a')
(2, 'b')
(3, 'c')
(4, None)
(5, None)
```

## fillvalue引数の使用

`zip_longest()`の`fillvalue`引数を使用すると、不足している要素を任意の値で埋めることができます。

```python
for item in zip_longest(list1, list2, fillvalue='N/A'):
    print(item)
```

出力:
```
(1, 'a')
(2, 'b')
(3, 'c')
(4, 'N/A')
(5, 'N/A')
```

## 実践的な例：複数のCSVファイルの結合

`zip_longest()`の実践的な使用例として、長さの異なる複数のCSVファイルを結合する場合を考えてみましょう。

まず、3つの異なるCSVファイルがあるとします：

file1.csv:
```
Name,Age
Alice,30
Bob,25
Charlie,35
```

file2.csv:
```
City,Country
New York,USA
London,UK
```

file3.csv:
```
Occupation
Engineer
Teacher
Doctor
Manager
```

これらのファイルを結合するコードは以下のようになります：

```python
import csv
from itertools import zip_longest

def combine_csv_files(file_paths, output_path):
    with open(output_path, 'w', newline='') as outfile:
        writer = csv.writer(outfile)
        
        readers = [csv.reader(open(f, 'r')) for f in file_paths]
        
        for row in zip_longest(*readers, fillvalue=['']):
            writer.writerow([item for sublist in row for item in sublist])

# 使用例
combine_csv_files(['file1.csv', 'file2.csv', 'file3.csv'], 'combined.csv')
```

この関数を実行すると、`combined.csv`という新しいファイルが作成されます。その内容は以下のようになります：

combined.csv:
```
Name,Age,City,Country,Occupation
Alice,30,New York,USA,Engineer
Bob,25,London,UK,Teacher
Charlie,35,,,Doctor
,,,Manager
```

この結果から以下のことが分かります：

1. 3つのCSVファイルのヘッダーが1行目に結合されています。
2. 各ファイルのデータが順番に結合されています。
3. `file1.csv`と`file3.csv`は4行ありますが、`file2.csv`は3行しかないため、3行目以降は空白で埋められています。
4. `file3.csv`は1列しかないため、他のファイルの列数に合わせて空白が追加されています。

この例では、`zip_longest()`が異なる長さのCSVファイルを効果的に結合し、不足しているデータを空白で埋めていることが分かります。これにより、データの構造を保ちながら、複数のソースからの情報を1つのファイルにまとめることができます。

## 公式ドキュメントと追加情報

`itertools.zip_longest()`の詳細については、Pythonの公式ドキュメントを参照することをお勧めします。以下に関連する情報とリンクを示します：

- 公式ドキュメント: [itertools.zip_longest()](https://docs.python.org/3/library/itertools.html#itertools.zip_longest)
- `zip_longest()`の構文:
  ```python
  itertools.zip_longest(*iterables, fillvalue=None)
  ```

公式ドキュメントによると、`zip_longest()`には以下の特徴があります：

1. 複数のイテラブルを引数として受け取ります。
2. `fillvalue`のデフォルト値は`None`です。
3. 返される要素は、入力イテラブルの対応する要素をまとめたタプルです。
4. 最長のイテラブルが尽きるまで値を生成し続けます。

また、`itertools`モジュールには他にも便利な関数があります。例えば：

- `itertools.cycle()`: イテラブルの要素を無限に繰り返します。
- `itertools.chain()`: 複数のイテラブルを連結します。
- `itertools.combinations()`: イテラブルから指定した長さの組み合わせを生成します。

これらの関数を組み合わせることで、さらに高度なデータ処理が可能になります。

## まとめ

`itertools.zip_longest()`を使用することで、長さの異なるイテラブルを簡単に組み合わせることができます。これは、データ処理やファイル操作など、様々な場面で役立つテクニックです。特に、今回のCSVファイルの結合例で示したように、異なる構造や長さを持つデータセットを統合する際に非常に便利です。

Pythonの標準ライブラリに含まれる`itertools`モジュールには他にも便利な関数がたくさんあるので、ぜひ公式ドキュメントを参照しながら探索してみてください。データ処理の効率と柔軟性を向上させるための強力なツールとなるでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/f4666ac8-9e9e-9be5-b6c1-4b458cab6caa.png)