행 필터와 불리언 열 표현식
df.filter(condition)과 df.where(condition)는 같은 행 선택 기능입니다. 조건이 True인 행을 남기며 False나 NULL인 조건의 행은 남기지 않습니다. 계산 의미를 바꾸지 않는 범위에서 필요한 필터를 일찍 적용하면 이후 처리량을 줄일 수 있습니다.
범위 조건과 리터럴
from pyspark.sql import functions as F
def adults(df):
return df.filter(F.col("age") >= 21)
def young_adults(df):
return df.where((F.col("age") >= 21) & (F.col("age") < 35))
def chained_filters(df):
return df.filter(F.col("age") >= 21).filter(F.col("age") < 35)
21은 포함하고 35는 제외합니다. 같은 조건을 나누어 호출하거나 &로 결합할 수 있습니다. 비교식은 괄호로 묶어 Python의 연산자 우선순위를 명확히 합니다.
열과 숫자/문자열을 비교하면 리터럴 표현식을 만들 수 있습니다. 이름이 열인지 고정 값인지 명시하려면 F.col()과 F.lit()을 구분합니다. Python 표현식 "X" == "Y"는 Spark에 전달되기 전에 False로 계산되므로 열 비교가 아닙니다.
from pyspark.sql import functions as F
def literal_match(df):
return df.filter(F.col("X") == F.lit("Y"))
def column_match(df):
return df.filter(F.col("X") == F.col("Y"))
논리식 결합
| 의미 | Column 표현식 |
|---|---|
| AND | a & b |
| OR | a | b |
| NOT | ~a |
| 배타적 OR | (a & ~b) | (~a & b) |
Python의 and/or/not는 단일 Python 불리언을 평가하므로 분산 Column 조건의 결합에 사용하지 않습니다. 배타적 OR도 위의 불리언 정의식으로 작성합니다.
from pyspark.sql import functions as F
def selected_people(df):
age_range = (F.col("age") >= 21) & (F.col("age") < 35)
named_john = F.col("first_name") == "John"
missing_last_name = F.col("last_name").isNull()
return df.filter(age_range | named_john | missing_last_name)
def exactly_one_condition(df):
adult = F.col("age") >= 21
active = F.col("is_active_member")
return df.filter((adult & ~active) | (~adult & active))
첫 함수는 나이 범위를 만족하거나 이름이 John이거나 성이 NULL인 행을 남깁니다. 두 번째는 두 조건 중 정확히 하나가 True인 행을 선택하며 NULL에는 SQL의 불리언 규칙을 적용합니다.
Python 반복문으로 작은 조건 목록을 조합할 수 있습니다. 다음 반복은 데이터 행을 수집하지 않고 a/b/c/d 중 한 접두사로 시작하는 조건식을 만듭니다.
from pyspark.sql import functions as F
def allowed_prefixes(df):
column = F.col("column")
condition = F.lit(False)
for value in ["a", "b", "c", "d"]:
condition = condition | column.startswith(value)
return df.filter(condition)
LIKE와 정규식
Column.like(pattern)은 SQL LIKE 문자열 패턴, Column.rlike(pattern)은 정규식 패턴을 받습니다. PySpark 3.5의 이 Column 메서드에서는 문자열 패턴을 사용합니다. 다른 열에 들어 있는 패턴을 사용할 때는 함수형 F.like(value_column, pattern_column) 또는 F.rlike(value_column, pattern_column) 인터페이스와 구분합니다.
LIKE의 %는 0개 이상 문자, _는 정확히 한 문자입니다.
| 패턴 | 의미 |
|---|---|
a% | a로 시작 |
%a | a로 끝남 |
%or% | 어느 위치에든 or 포함 |
_r% | 두 번째 글자가 r |
a_%_% | a로 시작하며 전체 길이가 최소 3 |
a%o | a로 시작하고 o로 끝남 |
from pyspark.sql import functions as F
def names_starting_al(df):
return df.filter(F.col("name").like("Al%"))
def formatted_phone_numbers(df):
pattern = r"[0-9]{3}(?:.+)?[0-9]{3}(?:.+)?[0-9]{4}"
return df.filter(F.col("phone").rlike(pattern))
def per_row_like_pattern(df):
return df.filter(F.like(F.col("name"), F.col("pattern")))
Al%는 Alice와 일치합니다. 전화번호 예제는 412-512-1234, (555) 123-5123, 4121234444처럼 숫자 묶음 사이의 문자가 다른 문자열과 일치할 수 있습니다. 정규식이 전체 문자열에 맞아야 한다면 ^와 $ 경계를 사용합니다. 이 패턴은 전화번호 유효성의 모든 규칙을 검사하는 기능은 아닙니다.
시작, 끝과 포함 여부
Column.startswith(value), endswith(value), contains(value)는 문자열 값이나 다른 문자열 열로 비교할 수 있습니다. 정규식이나 LIKE 와일드카드를 해석하는 메서드는 아닙니다.
from pyspark.sql import functions as F
def required_id_parts(df):
return df.filter(
F.col("id").startswith("prefix-")
& F.col("id").endswith("-suffix")
& F.col("id").contains("string")
)
substr
Column.substr(startPos, length)는 1부터 세는 시작 위치와 길이를 사용합니다. 정수 위치 또는 열 표현식으로 부분 문자열을 계산할 수 있습니다.
from pyspark.sql import functions as F
def first_three_letters(df):
return df.select(F.col("name").substr(1, 3).alias("col"))
def middle_phone_prefix(df):
return df.filter(F.col("phone").substr(5, 3) == "555")
Alice와 Bob의 첫 세 글자는 Ali와 Bob입니다. 두 번째 함수는 323-555-1234, 897-555-4126 같은 값에서 5~7번째 문자를 비교합니다.
isin과 between
Column.isin(*values)는 값 목록 또는 펼친 인자로 포함 여부를 검사합니다.
from pyspark.sql import functions as F
def selected_names(df):
return df.filter(F.col("name").isin("Bob", "Mike"))
def selected_ages(df):
return df.filter(F.col("age").isin([1, 2, 3]))
Column.between(lowerBound, upperBound)는 양쪽 경계를 포함합니다. 경계에 고정 값이나 열을 사용할 수 있습니다.
from pyspark.sql import functions as F
def age_bounds(df):
return df.select(
"name", "upperBound", "age",
F.col("age").between(10, F.col("upperBound")).alias("age_within_range"),
)
| name | upperBound | age | age_within_range |
|---|---|---|---|
| Taylor | 30 | 35 | False |
| Sally | 40 | 34 | True |
| Lucy | 28 | 28 | True |