데이터 통합PythonPySpark Reference필터링

본 번역은 검증되지 않았습니다. AIP를 통해 영문원문으로부터 번역되었습니다.

필터링

필터, where

DataFrame.filter(표현식)

불리언 표현식에 따라 결정되는 행의 부분 집합이 있는 새로운 DataFrame을 반환합니다. 표현식 파라미터는 여러 가지 방법으로 파생될 수 있는 불리언 열 표현식입니다.

변환 시작 부분에서 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 3 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 원시 값이 아닌 전체 열이므로, 이미 단일 부울로 평가되는 두 인수를 기대하는 and 또는 or 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 # 위 두 조건을 모두 만족하는 사용자를 필터링하는 조건입니다. name_is_john = F.col("first_name") == "John" # 이름이 'John'인 사용자를 필터링하는 조건입니다. last_name_is_null = F.isnull(F.col("last_name")) # last_name이 null인 사용자를 필터링하는 조건입니다. df = df.where(within_age_range | name_is_john | last_name_is_null) # 위 세 가지 조건 중 하나라도 만족하는 사용자를 필터링합니다. return df # 필터링된 데이터 프레임을 반환합니다.

이것이 우리에게 도움이 되는 또 다른 방법은 논리 연산을 활용하여 필터링 로직을 제공할 수 있다는 것입니다:

  • &: 그리고

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

    Copied!
    1 df = df.filter(condition_a | condition_b)
  • ^: 배타적 또는

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

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

파이썬의 for-loops를 사용하여 조건문을 생성하는 것이 자유롭지만, 불필요한 오류를 피하기 위해서는 부울 대수에 대해 확실히 이해해야 합니다. 이를 활용하는 방법에 대한 예는 다음과 같습니다:

Copied!
1 2 3 4 5 6 7 8 9 10 11 def custom_func(col, value): # 기본 로직 또는 UDF return output # 참/거짓 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

likerlike 메서드는 각각 SQL LIKE 및 Regex 구문을 사용한 패턴 일치를 허용합니다.

  • 간단한 부분 문자열 검색에는 like를 사용하세요.
  • 보다 복잡한 패턴 일치에는 rlike를 사용하세요.

Column.like(sql_like)

리터럴 문자열 또는 열에서 제공되는 SQL LIKE 일치를 기반으로 한 부울 열을 반환합니다:

Copied!
1 2 # 'name' 열의 값이 'Al'로 시작하는 행만 필터링하여 df에 다시 할당합니다. df = df.filter(F.col('name').like('Al%'))
나이이름
2"Alice"

SQL LIKE 와일드카드:

  • %: 0개 이상의 문자를 나타냅니다.
  • _: 단일 문자를 나타냅니다.

예제 (w3schools에서 가져옴(https://www.w3schools.com/sql/sql_wildcards.asp)):

LIKE 연산자설명
Column.like('a%')"a"로 시작하는 값을 찾습니다.
Column.like('%a')"a"로 끝나는 값을 찾습니다.
Column.like('%or%')"or"가 포함된 값을 찾습니다.
Column.like('_r%')두 번째 위치에 "r"이 있는 값을 찾습니다.
Column.like('a_%_%')"a"로 시작하고 3개 이상의 문자가 있는 값을 찾습니다.
Column.like('a%o')"a"로 시작하고 "o"로 끝나는 값을 찾습니다.

Column.rlike(regex)

리터럴 문자열이나 열로 제공되는 정규식 기반의 불린 열 표현식을 반환합니다:

Copied!
1 2 3 4 5 # 'phone' 열에서 정규 표현식을 사용하여 특정 패턴을 가진 값을 필터링합니다. # 해당 패턴은 '[0-9]{3}(?:.+)?[0-9]{3}(?:.+)?[0-9]{4}'이며, # 이는 3자리 숫자, 임의의 문자(0개 이상), 3자리 숫자, 임의의 문자(0개 이상), 그리고 마지막으로 4자리 숫자를 나타냅니다. # 이 패턴은 일반적으로 전화번호를 나타내는데 사용됩니다. 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"

올바르게 활용하면 regex는 매우 강력합니다. 다음은 시작하는 데 도움이 될 몇 가지 리소스입니다:

시작하는, 끝나는, 포함하는

열.startswith(스트링)

열의 스트링 값이 파라미터로 제공된 스트링(리터럴 또는 다른 열)으로 시작하는지 여부를 나타내는 불리언 열 표현식을 반환합니다.

Copied!
1 2 # "id" 컬럼이 "prefix-"로 시작하는 행들만 필터링하여 df에 저장합니다. df = df.filter(F.col("id").startswith("prefix-"))

Column.endswith(스트링)

열의 문자열 값이 파라미터로 제공된 문자열(리터럴 또는 다른 열)로 끝나는지 여부를 나타내는 불리언 열 표현식을 반환합니다.

Copied!
1 2 # id 컬럼의 값이 "-suffix"로 끝나는 행들만 필터링하여 df에 저장합니다. df = df.filter(F.col("id").endswith("-suffix"))

Column.contains(string)

열의 문자열 값이 파라미터로 제공된 문자열(리터럴 또는 다른 열)을 포함하는지를 나타내는 불리언 열 표현식을 반환합니다.

Copied!
1 2 # "id" 열에 "string"이 포함된 행만 필터링하여 df에 재할당합니다. df = df.filter(F.col("id").contains("string"))

부분 문자열

Column.substr(startPos, length)

열 값의 부분 문자열을 평가하는 문자열 열 표현식을 반환합니다.

파라미터:

  • startPos - 시작 위치, 1부터 계산 (int 또는 Column)
  • length - 부분 문자열의 길이 (int 또는 Column)
  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 # "name" 열의 값이 "Bob" 또는 "Mike"인 행만 필터링하여 df에 저장합니다. df = df.filter(F.col("name").isin("Bob", "Mike"))
나이이름
5"Bob"
......
Copied!
1 2 # df라는 DataFrame에서 age 열의 값이 1, 2, 3 중 하나인 행만을 선택하여 다시 df에 저장합니다. df = df.filter(F.col("age").isin([1, 2, 3]))
나이이름
2"Alice"
......

사이

열.between(하한, 상한)

표현식의 값이 하한과 상한 리터럴 또는 열 사이에 있으면 True로 평가되는 불리언 표현식을 반환합니다. (포함).

Copied!
1 2 3 4 # "age" 열 값이 10과 "upperBound" 사이에 있는지 확인하고 "age_within_range"라는 별칭을 사용합니다. within_range = F.col("age").between(10, df.upperBound).alias("age_within_range") # 새로운 데이터 프레임에 name, upperBound, age, age_within_range 열을 선택합니다. df = df.select(df.name, df.upperBound, df.age, within_range)
이름상한선나이나이_범위_내
"Taylor"3035False
"Sally"4034True
"Lucy"2828True