注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

フィルタリング

フィルター、where

DataFrame.filter(expression)

ブール式によって決定される行のサブセットを持つ新しい DataFrame を返します。expression パラメーターは、さまざまな方法で導出できるブール行式です。

トランスフォームの始めに filter を使用し、不要な計算作業を減らし、ビルド時間のパフォーマンスを向上させます。

データセットに多くのエントリが含まれている場合でも、基準に基づいて存在する行のサブセットだけが関心事である場合。

Copied!
1 df = df.filter(F.col("age") >= 21) # データセットを21歳以上の人々に限定する

wherefilter のエイリアスであり、全く同じことを行います(どちらのメソッド名が読みやすいかは、ユーザーまたはチームの好みによります):

Copied!
1 df = df.where(F.col("age") >= 21) # データセットを21歳以上の人々に限定

また、いくつかの異なる方法でフィルター処理を連鎖させることもできます:

Copied!
1 2 df = df.filter(F.col("age") >= 21).filter(F.col("age") < 35) # 21歳以上35歳未満の行をフィルタリングします。 df = df.filter((F.col("age") >= 21) & (F.col("age") < 35)) # 括弧を使用して比較をグループ化し、評価の順序を正しく保証します。

定数(リテラル)

列と定数、または「リテラル」(単一のハードコードされた文字列、日付、または数値など)を比較する際、PySparkはこの基本的なPythonデータ型を「リテラル」(F.lit(value)を宣言するのと同じ)に評価します。リテラルとは、静的な値を持つ列式のことです。これは、進む前に知っておくべき重要な区別であり、リテラル(暗黙的または明示的など)とのすべての比較は、代わりに名前付きの列で置き換えることができることを示しています。これは、同じ行の他の列に依存する動的な比較を容易に行うことができます。

一部のコンテキストではリテラルが正しく解釈されません。たとえば、文字列と比較する場合、文字列と名付けられた列を参照する意図か、文字列自体を指す意図かが曖昧になる可能性があります。

Copied!
1 2 df = df.filter("X" == "Y") # XとYは列を指しているのか、リテラルを指しているのか? dff = df.filter(F.col("X") == F.lit("Y")) # これは曖昧さがない。

論理演算

PySparkには多数の二項論理演算があります。これらは常にブール型の行式のインスタンスに評価され、条件を組み合わせるために使用できます。

論理演算子の引数がPythonのプリミティブではなく全行なので、馴染み深い andor のPython演算子は使用できません。これらの演算子は、両方の引数が単一のブール値にすでに評価されることを期待しています。PySparkは &(ビット単位-and)、|(ビット単位-or)演算子、および ~否定(チルダ)記号を解釈し、すべての行で非常に効率的に実行されるSQLクエリを作成することができます。

これが役立つ一つの方法は、二項演算の値を参照する名前付き変数を許可することです。したがって、特に複数のプロパティをフィルター処理する場合に、各比較を説明的に名前付けすることで、可読性と明確性を向上させることができます:

Copied!
1 2 3 4 5 6 7 8 # ユーザーが21歳以上35歳未満、またはfirst_nameが"John"、またはlast_nameがNone(null)のレコードをフィルタリングします。 at_least_21 = F.col("age") >= 21 # ユーザーの年齢が21歳以上 younger_than_35 = F.col("age") < 35 # ユーザーの年齢が35歳未満 within_age_range = at_least_21 & younger_than_35 # ユーザーの年齢が21歳以上35歳未満 name_is_john = F.col("first_name") == "John" # ユーザーの名前が"John" last_name_is_null = F.isnull(F.col("last_name")) # ユーザーの名字がnull df = df.where(within_age_range | name_is_john | last_name_is_null) # これらの条件のいずれかに一致するレコードをフィルタリング return df

この方法が役立つもう一つの方法は、フィルター処理のロジックを提供するために論理演算を活用できることです:

  • &:And

    Copied!
    1 df = df.filter(condition_a & condition_b)
  • |:Or

    Copied!
    1 df = df.filter(condition_a | condition_b)
  • ^:Exclusive-or

    Copied!
    1 df = df.filter(condition_a ^ condition_b)
  • ~:Negation

    Copied!
    1 df = df.filter(~condition_a)

Python の for-loop を使って条件を生成することも自由に行ってください、ただし、不必要なエラーを避けるために ブール代数についての理解を深めておくことが重要です。以下に、この方法をどのように活用できるかの例を示します:

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 def custom_func(col, value): # 基本的なロジックまたはUDF return output # True/False values = ["a", "b", "c", "d"] condition = F.lit(False) for x in values: # 条件を組み立てる condition = condition | custom_func(F.col("column"), x) # フィルタを適用してデータフレームを返す df = df.filter(condition) return df

Like、rlike

like メソッドと rlike メソッドは、それぞれ、SQL LIKE と Regex の構文を使用してパターンマッチングを可能にします。

  • 単純な部分文字列検索には、like を使用します。
  • より複雑なパターンマッチングには、rlike を使用します。

行.like(sql_like)

リテラル文字列または行によって提供されるSQL LIKEマッチに基づいてブール行を返します:

Copied!
1 2 # 'name'列の値が'Al'で始まる行だけをフィルタリングします df = df.filter(F.col('name').like('Al%'))
年齢名前
2"Alice"

SQL LIKE ワイルドカード:

  • %: 0 文字以上を表す
  • _: 1 文字を表す

例 ( w3schoolsより):

LIKE 演算子説明
行.like('a%')"a" で始まる任意の値を見つける
行.like('%a')"a" で終わる任意の値を見つける
行.like('%or%')"or" を含む任意の位置の値を見つける
行.like('_r%')2 番目の位置に "r" を持つ任意の値を見つける
行.like('a_%_%')"a" で始まり、3 文字以上の任意の値を見つける
行.like('a%o')"a" で始まり、"o" で終わる任意の値を見つける

行.rlike(regex)

リテラル文字列または行で提供される正規表現に基づいて、ブール値の行式を返します:

Copied!
1 2 3 # 'phone'というカラムについて、正規表現'[0-9]{3}(?:.+)?[0-9]{3}(?:.+)?[0-9]{4}'に一致する行だけをフィルタリングします。 # この正規表現は、3つの数字の群が3回出現する電話番号形式に一致します。数字の間には任意の文字列(存在する場合)が来ます。 df = df.filter(F.col('phone').rlike('[0-9]{3}(?:.+)?[0-9]{3}(?:.+)?[0-9]{4}'))
名前電話番号
"Alice""412-512-1234"
"John""(555) 123-5123"
"Jane""4121234444"

適切に活用すれば、正規表現は非常に強力です。以下にいくつかのリソースを紹介します:

開始文字、終了文字、含む

行.startswith(string)

提供されたパラメーター(文字列リテラル、または他の行)で行の文字列値が始まるかどうかを示すブール行式を返します。

Copied!
1 2 # "prefix-"から始まる"id"を持つ行だけをフィルタリングして、データフレームdfを更新します。 df = df.filter(F.col("id").startswith("prefix-"))

Column.endswith(string)

行の文字列値がパラメーターで提供された文字列(リテラルまたは別の行)で終わるかどうかを示すブール行式を返します。

Copied!
1 2 # "id"カラムの値が"-suffix"で終わる行だけをフィルタリングします df = df.filter(F.col("id").endswith("-suffix"))

Column.contains(string)

行の文字列値がパラメーターで提供された文字列(リテラル、または他の行)を含むかどうかを示すブール型の行式を返します。

Copied!
1 2 # "id" カラムが "string" を含むデータのみ抽出 df = df.filter(F.col("id").contains("string"))

サブストリング

Column.substr(startPos, length)

行の値の部分文字列を評価する文字列行式を返します。

パラメーター:

  • startPos - 1 から始まる開始位置(intまたは行)
  • length - 部分文字列の長さ(intまたは行)
  1. 部分文字列の行を作成する

    Copied!
    1 df = df.select(F.col("name").substr(1, 3).alias("col"))
    col
    "Ali"
    "Bob"
  2. 部分文字列でフィルター処理する

    Copied!
    1 df = df.filter(F.col("phone").substr(5, 3) == "555")
    phone
    "323-555-1234"
    "897-555-4126"
    ...

存在するか

Column.isin(*cols)

列の値が引数の評価された値に含まれている場合、True に評価されるブール式を返します(列またはリテラルの引数リスト、または配列の形式で)。

Copied!
1 2 3 # dfというデータフレームに対して、filterを使用しています。この操作は特定の条件に一致する行を抽出します。 # ここでは、"name"というカラムの値が"Bob"または"Mike"である行を抽出しています。 df = df.filter(F.col("name").isin("Bob", "Mike"))
年齢名前
5"Bob"
......
Copied!
1 2 # DataFrameの"age"カラムが1, 2, 3のいずれかに一致する行をフィルタリングする df = df.filter(F.col("age").isin([1, 2, 3]))
年齢名前
2"Alice"
......

Between

Column.between(lowerBound, upperBound)

lowerBound と upperBound リテラルまたは行 (含む) の間に式の値がある場合、True に評価されるブール式を返します。

Copied!
1 2 3 4 5 # "age"カラムの値が10とdf.upperBoundの間にあるかどうかをチェックします。結果を"age_within_range"という新たなカラムとして保存します。 within_range = F.col("age").between(10, df.upperBound).alias("age_within_range") # dfから"name", "upperBound", "age", "age_within_range"のカラムを選択して新たなデータフレームを作成します。 df = df.select(df.name, df.upperBound, df.age, within_range)
nameupperboundageage_within_range
"Taylor"3035False
"Sally"4034True
"Lucy"2828True