注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
DataFrame.filter(expression)ブール式によって決まる行のサブセットを持つ新しい DataFrame を返します。expression パラメーターは、さまざまな方法で生成できるブール列の式です。
不要な計算処理を減らし、ビルド時間のパフォーマンスを向上させるために、トランスフォームの終盤ではなく先頭で filter を使用します。
データセットに多数のエントリーが含まれていても、条件に基づく一部の行だけが必要な場合は、次のようにします。
Copied!1df = df.filter(F.col("age") >= 21) # データセットを21歳を超える人のみに絞り込みます
where は filter のエイリアスであり、まったく同じ処理を行います(どちらのメソッド名が読みやすいかは、ご自身やチームの好みに応じて選択できます)。
Copied!1df = df.where(F.col("age") >= 21) # データセットを21歳を超える人のみに絞り込みます
フィルターは、いくつかの異なる方法で連結することもできます。
Copied!1 2df = df.filter(F.col("age") >= 21).filter(F.col("age") < 35) # または... df = df.filter((F.col("age") >= 21) & (F.col("age") < 35)) # 正しい評価順序を確保するため、括弧で比較をグループ化します。
列を定数、つまりハードコードされた単一の文字列、日付、数値などの「リテラル」と比較するとき、PySpark は実際にはこの基本的な Python データ型を「リテラル」として評価します(F.lit(value) を宣言するのと同じです)。リテラルは、単に固定値を持つ列の式です。先に進む前に、この違いを理解しておくことが重要です。リテラルとの比較はすべて、暗黙的なものでも明示的なものでも、リテラルを名前付きの列に置き換えられるからです。つまり、同じ行のほかの列に基づく動的な比較を簡単に行えます。
コンテキストによっては、リテラルが正しく解釈されないことがあります。たとえば、文字列と比較する場合、その文字列を名前とする列を参照するのか、文字列自体を参照するのかが曖昧になる場合があります。
Copied!1 2df = df.filter("X" == "Y") # X と Y は列を指しますか、それともリテラルを指しますか? dff = df.filter(F.col("X") == F.lit("Y")) # 曖昧さがありません。
PySpark には、いくつかの二項論理演算があります。これらは常にブール列の式のインスタンスとして評価され、条件を組み合わせるために使用できます。
論理演算子への引数は Python のプリミティブ値ではなく列全体であるため、使い慣れた Python の and や or 演算子は使用できません。これらの演算子は、両方の引数がすでに単一のブール値として評価されていることを前提としています。PySpark は、&(ビット単位の AND)、|(ビット単位の OR)演算子、および ~ 否定(チルダ)記号を解釈して、すべての行に対して非常に効率的に実行される SQL クエリを組み立てられます。
この仕組みの利点の1つは、二項演算の値を参照する名前付き変数を使用できることです。そのため、特にいくつかのプロパティで絞り込む場合には、読みやすさと明確さを高めるために、各比較に内容のわかる名前を付けられます。
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 younger_than_35 = F.col("age") < 35 within_age_range = at_least_21 & younger_than_35 name_is_john = F.col("first_name") == "John" last_name_is_null = F.isnull(F.col("last_name")) df = df.where(within_age_range | name_is_john | last_name_is_null) return df
もう1つの利点は、論理演算を利用してフィルタリングのロジックを定義できることです。
&:論理積
Copied!1df = df.filter(condition_a & condition_b)
|:論理和
Copied!1df = df.filter(condition_a | condition_b)
^:排他的論理和
Copied!1df = df.filter(condition_a ^ condition_b)
~:否定
Copied!1df = df.filter(~condition_a)
Python の for ループを使用して条件式を生成できますが、不要なエラーを避けるために、ブール代数 ↗を復習しておいてください。次に、この方法を活用する例を示します。
Copied!1 2 3 4 5 6 7 8 9 10def 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 メソッドでは SQL LIKE 構文を、rlike メソッドでは正規表現構文を使用して、パターンマッチングを行えます。
- 単純な部分文字列の検索には、
likeを使用します。- より複雑なパターンマッチングには、
rlikeを使用します。
Column.like(sql_like)文字列リテラルまたは列で指定された SQL LIKE のマッチング結果に基づくブール列を返します。
Copied!1df = df.filter(F.col('name').like('Al%'))
| age | name |
|---|---|
| 2 | "Alice" |
SQL LIKE のワイルドカード:
%:0文字以上を表します_:1文字を表します例(w3schools ↗より):
| LIKE 演算子 | 説明 |
|---|---|
Column.like('a%') | 「a」で始まる値を検索します |
Column.like('%a') | 「a」で終わる値を検索します |
Column.like('%or%') | 任意の位置に「or」を含む値を検索します |
Column.like('_r%') | 2文字目が「r」である値を検索します |
Column.like('a_%_%') | 「a」で始まる3文字以上の値を検索します |
Column.like('a%o') | 「a」で始まり「o」で終わる値を検索します |
Column.rlike(regex)文字列リテラルまたは列で指定された正規表現のマッチング結果に基づくブール列の式を返します。
Copied!1df = df.filter(F.col('phone').rlike('[0-9]{3}(?:.+)?[0-9]{3}(?:.+)?[0-9]{4}'))
| name | phone |
|---|---|
| "Alice" | "412-512-1234" |
| "John" | "(555) 123-5123" |
| "Jane" | "4121234444" |
正規表現は、適切に活用すれば非常に強力です。はじめる際に役立つリソースを次に示します。
Column.startswith(string)列の文字列値が、パラメーターで指定された文字列(リテラルまたはほかの列)で始まるかどうかを示すブール列の式を返します。
Copied!1df = df.filter(F.col("id").startswith("prefix-"))
Column.endswith(string)列の文字列値が、パラメーターで指定された文字列(リテラルまたはほかの列)で終わるかどうかを示すブール列の式を返します。
Copied!1df = df.filter(F.col("id").endswith("-suffix"))
Column.contains(string)列の文字列値が、パラメーターで指定された文字列(リテラルまたはほかの列)を含むかどうかを示すブール列の式を返します。
Copied!1df = df.filter(F.col("id").contains("string"))
Column.substr(startPos, length)列の値の部分文字列を評価する文字列列の式を返します。
パラメーター:
部分文字列の列の作成
Copied!1df = df.select(F.col("name").substr(1, 3).alias("col"))
| col |
|---|
| "Ali" |
| "Bob" |
部分文字列によるフィルタリング
Copied!1df = df.filter(F.col("phone").substr(5, 3) == "555")
| phone |
|---|
| "323-555-1234" |
| "897-555-4126" |
| ... |
Column.isin(*cols)列の値が、引数を評価した値に含まれる場合に True と評価されるブール式を返します。引数は、Column またはリテラルの引数リストまたは配列の形式で指定します。
Copied!1df = df.filter(F.col("name").isin("Bob", "Mike"))
| age | name |
|---|---|
| 5 | "Bob" |
| ... | ... |
Copied!1df = df.filter(F.col("age").isin([1, 2, 3]))
| age | name |
|---|---|
| 2 | "Alice" |
| ... | ... |
Column.between(lowerBound, upperBound)式の値が、リテラルまたは列で指定された lowerBound と upperBound の間にある場合(両端を含む)に True と評価されるブール式を返します。
Copied!1 2within_range = F.col("age").between(10, df.upperBound).alias("age_within_range") df = df.select(df.name, df.upperBound, df.age, within_range)
| name | upperbound | age | age_within_range |
|---|---|---|---|
| "Taylor" | 30 | 35 | False |
| "Sally" | 40 | 34 | True |
| "Lucy" | 28 | 28 | True |