Pythonのリスト内包表記はデータフレームで代用できる

Pythonのリスト内包表記はデータフレームで代用できる プログラミング

sum([i*10 for i in range(10)])
Pythonのコードで上記のようなモノを見たことがありませんか?

これは、リスト内包表記と言います。
個人的には、このリスト内包表記は好きではありません。
と言うより、ワンライナーでコーディングすることが好きではないです。

好きではない理由は、コードの可読性が格段に落ちるからです。
そのため、Pythonにおけるリスト内包表記も敬遠していました。

でも、便利だから使っていこうかと考えていました。
しかし、データフレームでも同じ事ができると気付きました。

本記事の内容

  • Pythonのリスト内包表記とは
  • データフレームでリスト内包表記と同じことができる

それでは、上記に沿って解説していきます。

Pythonのリスト内包表記とは

Pythonのリスト内包表記がわかっていない場合、本記事を理解できません。
そのため、できる限り簡潔にリスト内包表記を説明します。

まずは、Wikipediaより「リスト内包表記」の定義です。

リスト内包表記とは、一部のプログラミング言語で使用可能な構文構造であり、既存のリストから新たなリストを作成するために用いられるものである。 これは、 map関数やfilter関数などとは異なり、数学における集合内包表記 (en:Set-builder notation) に準拠したものである。

ざっくり言えば、リストからリストを作ることができる表記ということです。
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が使えないケースもあるかもしれません。
その場合は、リスト内包表記を大人しく使いますけどね。
選択肢が増えたという意味でも、有意義な検証になりました。

タイトルとURLをコピーしました