sum([i*10 for i in range(10)])
Pythonのコードで上記のようなモノを見たことがありませんか?
これは、リスト内包表記と言います。
個人的には、このリスト内包表記は好きではありません。
と言うより、ワンライナーでコーディングすることが好きではないです。
好きではない理由は、コードの可読性が格段に落ちるからです。
そのため、Pythonにおけるリスト内包表記も敬遠していました。
でも、便利だから使っていこうかと考えていました。
しかし、データフレームでも同じ事ができると気付きました。
本記事の内容
- Pythonのリスト内包表記とは
- データフレームでリスト内包表記と同じことができる
それでは、上記に沿って解説していきます。
Pythonのリスト内包表記とは
Pythonのリスト内包表記がわかっていない場合、本記事を理解できません。
そのため、できる限り簡潔にリスト内包表記を説明します。
まずは、Wikipediaより「リスト内包表記」の定義です。
ざっくり言えば、リストからリストを作ることができる表記ということです。
Pythonにおいては、リストとはイテレータのことを指すと考えてください。
「イテレータ?」
いろいろと用語が出てきますね。
イテレータとは、「イテラブルオブジェクトを使用した連続データ」のことです。
具体的には、イテレータはリスト型・辞書型・データフレームなどのデータ型のことを言います。
つまり、for文でクルクルと回せるデータのことだと考えればOKです。
理屈はこれぐらいにして、実際の動きを確認します。
before_list = [1, 2, 3, 4, 5] after_list = [x * 10 for x in before_list] print(after_list)
上記コードを実行した、結果は以下。
[10, 20, 30, 40, 50]
リスト(before_list)から新たにリスト(after_list)を作成できました。
リスト内包表記を用いずに、同じ処理をプログラミングする場合は以下のコードになります。
after_list = [] for x in before_list: data = x * 10 after_list.append(data) print(after_list)
3行分を1行でコーディングできています。
確かに、リスト内包表記でコーディングすればスマートかもしれません。
また、Pythonの場合であれば、リスト内包表記はパフォーマンスが良くなります。
appendでリスト型に追加するのに、そこそこのコストがあるようです。
このことに加えて、all関数やany関数も活用していきたいです。
活用していくためには、リスト内包表記が必須かもしれないと想定していました。
なお、all関数やany関数については次の記事が参考になります。
データフレームでリスト内包表記と同じことができる
だんだんとリスト内包表記に誘惑されていきそうでした。
ただ、やはりリスト内包表記の記述方法が合わないのですよね。
そのため、all関数やany関数を活かす方法をいろいろな角度で検証しました。
その結果、データフレームでリスト内包表記と同じことができると確認できました。
データフレームの利用には、外部ライブラリ・モジュールを必要とします。
しかし、Pandasはもう必須レベルですよね。
ゴチャゴチャ能書きをたれるよりも、実際にコードで確認しましょう。
以下のようなリスト型データがあるとします。
正確には、辞書データのリストです。
list_data = [] list_data.append({"col_1":True, "col_2":1}) list_data.append({"col_1":True, "col_2":2}) list_data.append({"col_1":True, "col_2":3}) list_data.append({"col_1":True, "col_2":4}) list_data.append({"col_1":False, "col_2":5})
「データのcol_1がすべてTrueかどうかをチェックする」
あなたなら、この処理をどうコーディングしますか?
以下の3つのパターンに分けて、説明をしていきます。
- all関数を使わない場合
- all関数を使うためにリスト内包表記を使う場合
- all関数を使うためにデータフレームを使う場合
all関数を使わない場合
次のようなコーディングになると思います。
少なくとも、私はこのような書き方をしてきました。
all関数を知る以前のことですけどね。
flg = True for row in list_data: col_1 = row["col_1"] if not col_1: flg = False break if(flg): print("すべてTrue") else: print("すべてTrueではない")
ベタベタなら、次もありでしょう。
でも、データが追加されたら終わりですけどね。
if(list_data[0]["col_1"] and list_data[1]["col_1"] and list_data[2]["col_1"] and list_data[3]["col_1"] and list_data[4]["col_1"]): print("すべてTrue") else: print("すべてTrueではない")
all関数を使うためにリスト内包表記を使う場合
なんと、all関数を使うとこんなにスマートになるのです。
その前提には、リスト内包表記を使っているのですけどね。
if all(d["col_1"] for d in list_data): print("すべてTrue") else: print("すべてTrueではない")
ただ、for文がワンライナーで記述されるのは・・・
やはり、個人的には好みません。
完全に好きか嫌いかのレベルです。
all関数を使うためにデータフレームを使う場合
import pandas as pd df = pd.DataFrame(list_data) if(all(df["col_1"])): print("すべてTrue") else: print("すべてTrueではない")
all関数を利用するために、リストをデータフレームに変換しています。
どうでしょうか?
かなり、わかりやすくなっていませんか?
そもそも、for文が消えています!!
個人的には、こちらが好みです。
まとめ
all関数利用時に、比較演算を行うことも可能です。
リスト内包表記とデータフレームの両方ともです。
リスト内包表記の場合
if all(d["col_2"] > 0 for d in list_data): print("すべて正の数")
データフレームの場合
if all(df["col_2"] > 0): print("すべて正の数")
この場合を見ても、やはりデータフレームの方が好みです。
「どっちでもいいじゃん」という意見もあるでしょう。
しかし、こうしたちょっとした部分で効率性が変わってきます。
好きか嫌いかというのは、プログラミングをする上でも重要なポイントだと考えています。
ただ、Pandasが使えないケースもあるかもしれません。
その場合は、リスト内包表記を大人しく使いますけどね。
選択肢が増えたという意味でも、有意義な検証になりました。