- Python
Python itertoolsの使い方|全関数の一覧と用途別コード例まとめ
itertools とは、ループ処理でよく使う「つなぐ・切り出す・まとめる・組み合わせる」操作を、メモリを節約しながら行うための Python 標準ライブラリです。 import itertools だけで使え、どの関数も結果をまとめて作らず、必要になった分だけ1つずつ返すイテレータ(next() で順に値を取り出せるオブジェクト)を返します。
この記事では、itertools の全21関数を「無限イテレータ・終端するイテレータ・組合せ」の3分類で一覧表にし、それぞれ1行のコピペ例と出力を Python 3.11.15 と 3.14.7 で実行して載せています。 さらに、groupby がソートしないとまとまらない落とし穴の再現、ページ分割・移動平均・総当たり・重複除去・フラット化などの用途別レシピ、イテレータが1回しか回せないことと tee の注意点まで、実行結果つきで解説します。
itertools全関数の早見表
下の表のコードは from itertools import *(accumulate の例は import operator も)を前提に、そのまま実行できます。 出力は Python 3.14.7 の実行結果です。3.11 でも batched 以外は同じ出力でした。
無限イテレータ(止めないと終わらない)
| 関数 | 何をするか | コピペ例 | 出力 |
|---|---|---|---|
count(start, step) |
数を無限に数える | list(islice(count(10, 5), 4)) |
[10, 15, 20, 25] |
cycle(it) |
要素を無限に繰り返す | list(islice(cycle('AB'), 5)) |
['A', 'B', 'A', 'B', 'A'] |
repeat(x, n) |
同じ値を n 回(省略で無限) | list(repeat('x', 3)) |
['x', 'x', 'x'] |
終端するイテレータ(入力が尽きたら終わる)
| 関数 | 何をするか | コピペ例 | 出力 |
|---|---|---|---|
accumulate(it, func) |
累積(既定は合計) | list(accumulate([1, 2, 3, 4])) |
[1, 3, 6, 10] |
| 〃(関数指定) | 累積の最大値など | list(accumulate([3, 1, 4, 1, 5], max)) |
[3, 3, 4, 4, 5] |
〃(initial) |
初期値つき(3.8+) | list(accumulate([1, 2, 3], operator.mul, initial=10)) |
[10, 10, 20, 60] |
batched(it, n) |
n 個ずつに分ける(3.12+) | list(batched('ABCDEFG', 3)) |
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)] |
〃(strict=True) |
端数をエラーに(3.13+) | list(batched('ABCDEFG', 3, strict=True)) |
ValueError: batched(): incomplete batch |
chain(*its) |
複数をつなぐ | list(chain('AB', [1, 2])) |
['A', 'B', 1, 2] |
chain.from_iterable(it) |
入れ子を1段平らに | list(chain.from_iterable([[1, 2], [3], []])) |
[1, 2, 3] |
compress(it, selectors) |
真の位置だけ残す | list(compress('ABCD', [1, 0, 1, 0])) |
['A', 'C'] |
dropwhile(pred, it) |
条件が真の間は捨てる | list(dropwhile(lambda x: x < 3, [1, 2, 3, 1, 4])) |
[3, 1, 4] |
filterfalse(pred, it) |
条件が偽のものだけ | list(filterfalse(lambda x: x % 2, range(6))) |
[0, 2, 4] |
groupby(it, key) |
連続する同じ値をまとめる | [(k, list(g)) for k, g in groupby('AAABBA')] |
[('A', ['A', 'A', 'A']), ('B', ['B', 'B']), ('A', ['A'])] |
islice(it, start, stop, step) |
スライス | list(islice('ABCDEFG', 1, 6, 2)) |
['B', 'D', 'F'] |
pairwise(it) |
隣どうしのペア(3.10+) | list(pairwise([1, 2, 3, 4])) |
[(1, 2), (2, 3), (3, 4)] |
starmap(func, it) |
タプルを展開して関数へ | list(starmap(pow, [(2, 3), (3, 2)])) |
[8, 9] |
takewhile(pred, it) |
条件が真の間だけ取る | list(takewhile(lambda x: x < 3, [1, 2, 3, 1, 4])) |
[1, 2] |
tee(it, n) |
1つから n 個に複製 | [list(t) for t in tee([1, 2, 3], 2)] |
[[1, 2, 3], [1, 2, 3]] |
zip_longest(*its, fillvalue) |
長いほうに合わせて zip | list(zip_longest('ABC', [1], fillvalue='-')) |
[('A', 1), ('B', '-'), ('C', '-')] |
組合せイテレータ(総当たり・順列・組合せ)
| 関数 | 何をするか | コピペ例 | 出力 |
|---|---|---|---|
product(*its, repeat) |
直積(入れ子 for の代わり) | list(product('AB', [1, 2])) |
[('A', 1), ('A', 2), ('B', 1), ('B', 2)] |
〃(repeat) |
同じものを repeat 回 | len(list(product([0, 1], repeat=3))) |
8 |
permutations(it, r) |
順列(順番を区別) | list(permutations('ABC', 2)) |
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')] |
combinations(it, r) |
組合せ(順番を区別しない) | list(combinations('ABC', 2)) |
[('A', 'B'), ('A', 'C'), ('B', 'C')] |
combinations_with_replacement(it, r) |
重複ありの組合せ | list(combinations_with_replacement('AB', 2)) |
[('A', 'A'), ('A', 'B'), ('B', 'B')] |
やりたいことから引く逆引き表です。
| やりたいこと | 使う関数 |
|---|---|
| リストを n 件ずつに分けたい(ページ分割・バッチ処理) | batched(3.11 以前は後述の代替コード) |
| 2重・3重の for ループを1つにしたい | product |
| 入れ子のリストを1段平らにしたい | chain.from_iterable |
| 前の要素との差分・隣どうしを見たい | pairwise |
| 累計・累積最大を出したい | accumulate |
| 同じキーの要素をまとめて集計したい | sorted → groupby |
| 長さの違うリストを揃えて並べたい | zip_longest |
| 先頭 n 件だけ取りたい(ジェネレータ) | islice |
| 条件を満たす間だけ処理したい | takewhile / dropwhile |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python itertoolsの使い方|全関数の一覧と用途別コード例まとめ」
3分類を図で理解する
itertools の関数は、入力と出力の関係で3つに分けると覚えやすくなります。
flowchart TB
A["itertools"] --> B["無限イテレータ<br>count / cycle / repeat"]
A --> C["終端するイテレータ<br>入力が尽きたら終わる<br>chain / islice / groupby など"]
A --> D["組合せイテレータ<br>product / permutations<br>combinations など"]
B --> E["islice や zip で<br>止める必要がある"]
C --> F["入力1つに対して<br>加工した値を返す"]
D --> G["件数が急に増えるので<br>件数を先に計算する"]
| 分類 | 特徴 | 注意点 |
|---|---|---|
| 無限 | 自分では止まらない | list(count()) は終わらないので、islice・zip・break で止める |
| 終端 | 入力の長さに合わせて終わる | 入力がイテレータなら、消費すると元に戻らない |
| 組合せ | 件数が掛け算で増える | math.comb(n, r)・math.perm(n, r) で件数を先に確認する |
組合せの件数は math モジュールで事前に計算できます。
import math
from itertools import combinations
print(len(list(combinations(range(10), 3))), math.comb(10, 3), math.perm(10, 3))
実行結果(Python 3.11.15):
120 120 720
batchedの使い方とPython 3.11以前の代替コード
batched は Python 3.12 で追加された「n 件ずつに分ける」関数で、3.13 で strict 引数が追加されました。 Python 3.11 で import すると次のエラーになります。
ImportError: cannot import name 'batched' from 'itertools' (unknown location)
3.11 以前でも、islice を使った同じ動きの関数で代用できます。
from itertools import islice
def batched(iterable, n):
it = iter(iterable)
while batch := tuple(islice(it, n)):
yield batch
print(list(batched("ABCDEFG", 3)))
実行結果(Python 3.11.15):
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)]
strict=True(3.13+)は、最後の組が n 件に満たないときに ValueError を出します。 「必ず3件ずつの組でないと困る」データ(3列で1レコードなど)の検査に便利です。
| バージョン | batched の状態 |
|---|---|
| 3.11 以前 | なし(上の代替コードを使う) |
| 3.12 | batched(iterable, n) |
| 3.13 以降 | batched(iterable, n, *, strict=False) |
groupbyはソートしないとまとまらない
groupby は「隣り合って連続している同じキー」だけをまとめます。 SQL の GROUP BY のように全体から同じキーを集めるわけではないので、先に同じキーで sorted() しておく必要があります。
ソートせずに使った場合(同じキーが分かれる)
from itertools import groupby
from operator import itemgetter
orders = [
{"item": "pen", "qty": 2},
{"item": "note", "qty": 1},
{"item": "pen", "qty": 3},
{"item": "note", "qty": 4},
{"item": "clip", "qty": 1},
]
for k, g in groupby(orders, key=itemgetter("item")):
print(k, sum(o["qty"] for o in g))
実行結果(Python 3.11.15 / 3.14.7):
pen 2
note 1
pen 3
note 4
clip 1
pen と note が2回ずつ出ています。並び順のまま、キーが変わるたびに新しいグループになるためです。
同じキーで先にソートした場合(正しくまとまる)
key = itemgetter("item")
for k, g in groupby(sorted(orders, key=key), key=key):
print(k, sum(o["qty"] for o in g))
clip 1
note 5
pen 5
ソートの key と groupby の key は同じものを使うのがポイントです。並べ替えの詳しい書き方は「Python sorted・sortのkey指定」の記事でまとめています。
グループは次に進むと中身が消える
groupby が返すグループ g は、次のグループに進んだ時点で使えなくなります。 後で使うなら、その場で list(g) にしておきます。
from itertools import groupby
groups = [(k, g) for k, g in groupby("AABBB")]
print([(k, list(g)) for k, g in groups])
groups = [(k, list(g)) for k, g in groupby("AABBB")]
print(groups)
[('A', []), ('B', [])]
[('A', ['A', 'A']), ('B', ['B', 'B', 'B'])]
「連続している」性質を活かす使い方
逆に、連続する同じ値を数えたいとき(ランレングス)は、ソートせずにそのまま使います。
from itertools import groupby
print([(k, len(list(g))) for k, g in groupby("AAABCCDDDD")])
[('A', 3), ('B', 1), ('C', 2), ('D', 4)]
| 目的 | ソート | 代わりの手段 |
|---|---|---|
| 同じキーをすべて集めて集計 | 必要 | collections.defaultdict(list)、Counter |
| 連続する同じ値をまとめる | 不要(並びを保つ) | なし(groupby が最適) |
| 表データの集計 | ― | pandas の groupby |
ソートの手間(O(n log n))が気になる大量データの「全体集計」は、defaultdict で1回のループにまとめる方法も選べます。
用途別レシピ集(すべて実行結果つき)
ここからは、よくある処理を itertools で書くレシピです。Python 3.14.7 で実行しました(batched を使うものは 3.12 以降、それ以外は 3.11 でも同じ結果)。
リストをページ分割する(n件ずつ)
from itertools import batched, islice
articles = [f"記事{i}" for i in range(1, 8)]
for page, rows in enumerate(batched(articles, 3), start=1):
print(page, rows)
def page_of(iterable, page, size):
start = (page - 1) * size
return list(islice(iterable, start, start + size))
print(page_of(articles, 3, 3))
1 ('記事1', '記事2', '記事3')
2 ('記事4', '記事5', '記事6')
3 ('記事7',)
['記事7']
全ページを順に処理するなら batched、特定のページだけ欲しいなら islice です。islice はリストでなくジェネレータやファイルにも使えます。
移動平均・スライディングウィンドウを作る
sliding_window は公式ドキュメントの「Itertools Recipes」に載っている関数で、deque(maxlen=n) で直近 n 件だけを保持します。
from collections import deque
from itertools import islice, accumulate, pairwise
def sliding_window(iterable, n):
it = iter(iterable)
window = deque(islice(it, n - 1), maxlen=n)
for x in it:
window.append(x)
yield tuple(window)
sales = [10, 20, 30, 40, 50]
print(list(sliding_window(sales, 3)))
print([sum(w) / len(w) for w in sliding_window(sales, 3)])
# 累積和を使う移動平均(件数が多いとき速い)
def moving_avg(xs, n):
s = list(accumulate(xs, initial=0))
return [(s[i + n] - s[i]) / n for i in range(len(xs) - n + 1)]
print(moving_avg(sales, 3))
# 前日との差分
print([b - a for a, b in pairwise(sales)])
[(10, 20, 30), (20, 30, 40), (30, 40, 50)]
[20.0, 30.0, 40.0]
[20.0, 30.0, 40.0]
[10, 10, 10, 10]
accumulate(xs, initial=0) で累積和 [0, 10, 30, 60, ...] を作ると、区間の合計が「引き算1回」で求まるため、ウィンドウが大きくても計算量が増えません。
総当たり(入れ子のforを1つにする)
from itertools import product
sizes = ["S", "M"]
colors = ["赤", "青"]
for combo in product(sizes, colors):
print(combo)
# 0〜9 の数字3つで合計が25になる組み合わせ
hit = [(a, b, c) for a, b, c in product(range(10), repeat=3) if a + b + c == 25]
print(hit)
('S', '赤')
('S', '青')
('M', '赤')
('M', '青')
[(7, 9, 9), (8, 8, 9), (8, 9, 8), (9, 7, 9), (9, 8, 8), (9, 9, 7)]
product(range(10), repeat=3) は for a in range(10): for b in range(10): for c in range(10): と同じ順番で値を返します。入れ子が深くなるほど読みやすさの差が大きくなります。
| 選び方 | 関数 | 3つから2つ選ぶ件数 |
|---|---|---|
| 順番を区別する、同じものは選ばない | permutations |
6 |
| 順番を区別しない、同じものは選ばない | combinations |
3 |
| 順番を区別しない、同じものも選べる | combinations_with_replacement |
6 |
| 各位置で全部から選ぶ | product(..., repeat=2) |
9 |
順序を保ったまま重複を取り除く
公式レシピの unique_everseen は、最初に出た順番を保ったまま重複を除きます。 key で「大文字小文字を無視して重複判定」などもできます。
def unique_everseen(iterable, key=None):
seen = set()
for x in iterable:
k = x if key is None else key(x)
if k not in seen:
seen.add(k)
yield x
print(list(unique_everseen("ABBCA")))
print(list(dict.fromkeys("ABBCA")))
print(list(unique_everseen(["Pen", "pen", "Note"], key=str.lower)))
['A', 'B', 'C']
['A', 'B', 'C']
['Pen', 'Note']
key が不要で要素がハッシュ可能(dict のキーにできる)なら、list(dict.fromkeys(...)) の1行が一番手軽です。set() は順番が保たれないので、順番を保ちたいときはこの2つを使います。
| 方法 | 順序 | key 指定 | 巨大データ(ストリーム) |
|---|---|---|---|
list(set(xs)) |
保たない | 不可 | 全件をメモリへ |
list(dict.fromkeys(xs)) |
保つ | 不可 | 全件をメモリへ |
unique_everseen(xs, key) |
保つ | 可 | 1件ずつ処理できる |
[k for k, _ in groupby(xs)] |
保つ | 可 | 連続する重複だけ除く |
入れ子のリストをフラットにする
1段だけ平らにするなら chain.from_iterable が速く、読みやすい書き方です。 何段も入れ子になっているなら再帰のジェネレータを使います。
from itertools import chain
nested = [[1, 2], [3], [4, 5]]
print(list(chain.from_iterable(nested)))
def deep_flatten(xs):
for x in xs:
if isinstance(x, list):
yield from deep_flatten(x)
else:
yield x
print(list(deep_flatten([1, [2, [3, [4]]]])))
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
1段のフラット化を3つの書き方で計測しました(10要素×1000個のリスト、Python 3.11.15、100回×5セットの最小値)。
| 書き方 | 1回あたり |
|---|---|
list(itertools.chain.from_iterable(data)) |
0.099 ms |
[x for row in data for x in row] |
0.137 ms |
sum(data, []) |
10.299 ms |
sum(data, []) はリストを足すたびに新しいリストを作り直すため、件数が増えると急に遅くなります。 chain.from_iterable か内包表記を使うとよいです。
縦横を入れ替える(転置)
from itertools import zip_longest
print(list(zip(*[(1, 2, 3), (4, 5, 6)])))
print(list(zip_longest(*[[1, 2, 3], [4]], fillvalue=0)))
[(1, 4), (2, 5), (3, 6)]
[(1, 4), (2, 0), (3, 0)]
長さが違うと zip は短いほうに合わせて切り捨てます。 足りない部分を埋めたいときは zip_longest を使います。逆に「長さが違ったらエラーにしたい」場合は、組み込みの zip(..., strict=True)(3.10+)が使えます。
条件を満たす最初のn件だけ取る
from itertools import islice, count
def take(n, it):
return list(islice(it, n))
print(take(3, (x * x for x in count(1) if x % 2)))
[1, 9, 25]
count(1) は無限に続きますが、islice で3件取った時点で止まるので、無限ループになりません。
公式レシピ集とmore-itertools
Python 公式ドキュメントの itertools ページ末尾には「Itertools Recipes」という関数集があり、コピーして使えます。 主なものは次のとおりです(3.14 のドキュメントで確認)。
| 分類 | レシピ名の例 |
|---|---|
| 基本 | take、prepend、tail、consume、nth、quantify、first_true、all_equal、flatten |
| データ処理 | unique_everseen、unique_justseen、sliding_window、grouper、roundrobin、iter_except |
| 数学 | powerset、sum_of_squares |
| 統計(移動統計) | running_mean、running_min、running_max、running_median |
これらを毎回コピーするのが手間なら、PyPI の more-itertools にまとまっています。 標準ライブラリではないので pip install more-itertools で入れます。
pip install more-itertools
import more_itertools as mi
print(list(mi.chunked(range(7), 3))) # リストで n 件ずつ
print(list(mi.windowed(range(5), 3))) # スライディングウィンドウ
print(list(mi.unique_everseen("ABBCA"))) # 順序を保った重複除去
print(mi.first([], default="なし")) # 先頭(空なら既定値)
print(list(mi.collapse([1, [2, [3, [4]]]]))) # 何段でもフラット化
print(mi.ilen(x for x in range(10) if x % 3 == 0)) # イテレータの件数
実行結果(more-itertools 11.1.0 / Python 3.11.15):
[[0, 1, 2], [3, 4, 5], [6]]
[(0, 1, 2), (1, 2, 3), (2, 3, 4)]
['A', 'B', 'C']
なし
[1, 2, 3, 4]
4
標準の batched はタプル、more_itertools.chunked はリストを返します。 Python 3.12 以降なら、まずは標準の batched で足りるか検討するとよいです。
イテレータは1回しか回せない
itertools の関数が返すイテレータは、最後まで読むと空になり、もう一度最初から読むことはできません。 map・zip・ジェネレータ式も同じです(ジェネレータそのものの仕組みは「Pythonジェネレータとyield」の記事を参照してください)。
it = map(str.upper, ["a", "b"])
print(list(it))
print(list(it))
g = (x * x for x in range(3))
print(sum(g), sum(g))
z = zip([1, 2], [3, 4])
print(len(list(z)), len(list(z)))
実行結果(Python 3.11.15 / 3.14.7):
['A', 'B']
[]
5 0
2 0
エラーにならず空になるだけなので、気づきにくいのがつまずきポイントです。何度も使うなら list() で一度リストにしておきます。
| 状況 | 対処 |
|---|---|
| 結果を2回以上使う、件数が少ない | list() にして保存する |
| 件数が多い、1回のループで済む | イテレータのまま使う |
| 2つの処理で並行して読みたい | tee を使う(下記の注意あり) |
| 最初からやり直したい | 元のリストから iter() を作り直す |
teeの注意点:元のイテレータを触らない
tee は1つのイテレータから独立した複数のイテレータを作ります。片方が先に進んでも、もう片方は最初から読めます。
from itertools import tee
src = iter([1, 2, 3, 4])
a, b = tee(src)
print(next(a), next(a))
print(list(b))
1 2
[1, 2, 3, 4]
ただし tee した後に元の src を直接進めると、その値は a にも b にも届きません。
src = iter([1, 2, 3, 4])
a, b = tee(src)
next(src) # 元を直接進めてしまう
print(list(a), list(b))
[2, 3, 4] [2, 3, 4]
tee のポイント |
内容 |
|---|---|
| 元のイテレータ | tee した後は使わない(変数を上書きするとよい:src, copy = tee(src)) |
| メモリ | 進み具合の差の分だけ値を内部に保持する。片方を全部読んでからもう片方を読むなら list() のほうが速くて簡単 |
| スレッド | 公式ドキュメントでは、tee のイテレータはスレッドセーフではないとされている |
Python 3.14ではitertoolsのpickle・copyが使えなくなった
Python 3.12 で非推奨になっていた itertools オブジェクトの pickle・copy・deepcopy 対応が、3.14 で削除されました。 同じコードを両方で動かした結果です。
import pickle, copy
from itertools import count
pickle.dumps(count(5))
c = copy.copy(count(5))
| バージョン | pickle.dumps(count(5)) |
copy.copy(count(5)) |
|---|---|---|
| 3.11.15 | 成功 | 成功 |
| 3.14.7 | TypeError: cannot pickle 'itertools.count' object |
TypeError: cannot pickle 'itertools.count' object |
itertools のオブジェクトを保存・複製していたコードは、list() で値にしてから保存するか、作り直すための引数(count なら現在の数)を保存する形に書き換えます。
よくある質問
Q. itertoolsを使うと速くなりますか?
A. 多くの関数は C 言語で実装されているため、同じ処理を Python の for ループで書くより速いことが多いです。 フラット化の計測でも chain.from_iterable が内包表記より約3割速い結果でした。ただし一番の利点は、全件をリストにせず1件ずつ処理できる「メモリの節約」と「読みやすさ」です。
Q. groupbyで同じキーがまとまらないのはなぜですか?
A. groupby は隣り合った同じキーしかまとめないためです。 先に sorted(data, key=同じkey) で並べ替えてから使います。並べ替えたくない場合は collections.defaultdict(list) で集める方法が向いています。
Q. batchedが使えません(ImportError)。
A. batched は Python 3.12 以降の関数です。 3.11 以前では、記事内の islice を使った代替関数か、more_itertools.chunked を使います。strict=True は 3.13 以降です。
Q. itertoolsの結果を表示すると <itertools.chain object at ...> と出ます。
A. イテレータは中身をまだ計算していない状態なので、そのままでは値が表示されません。 確認するときは list() で囲みます。無限イテレータ(count・cycle・引数なしの repeat)は list(islice(..., 10)) のように件数を区切ってから表示します。
まとめ
- itertools は「無限(count・cycle・repeat)」「終端(chain・islice・groupby など)」「組合せ(product・permutations・combinations)」の3分類、全21関数で覚えると引きやすい
groupbyは連続する同じキーだけをまとめる。全体を集計するなら同じkeyで先にsorted()するbatchedは 3.12+、strictは 3.13+。3.11 以前はisliceの代替関数で同じことができる- ページ分割は
batched、移動平均はsliding_windowかaccumulate、総当たりはproduct、フラット化はchain.from_iterable - イテレータは1回しか回せない。何度も使うなら
list()、teeした後は元のイテレータを使わない - 3.14 では itertools オブジェクトの pickle・copy が削除されたので、値にしてから保存する
- 公式の Itertools Recipes と more-itertools で、よく使う処理を短く書ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python itertoolsの使い方|全関数の一覧と用途別コード例まとめ」
株式会社RJCでは、itertools のような標準ライブラリの使い方を、仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。