← 학습 카테고리

Learn

Spark

38개 모듈 · 현재 18번째

Spark 모듈 18/38 spark-learn-18

문자열과 정규표현식 다루기

Spark: The Definitive Guide (Excerpts, Databricks Preview, 2017) — Chapters 2-6 — Bill Chambers & Matei Zaharia Chapter 6: Working with Strings, Regular Expressions 절 (PDF pp.96-105)

이 모듈을 다 읽으면

  • initcap, lower/upper, trim 계열 함수의 동작을 구분해 설명할 수 있다
  • regexp_extract와 regexp_replace의 용도 차이 및 translate와의 차이를 설명할 수 있다
  • varargs를 활용해 동적으로 다수의 boolean 컬럼을 생성하는 패턴을 설명할 수 있다

문자열 처리는 로그 파싱, 정규식 추출/치환, 대소문자 변환 등 거의 모든 데이터 파이프라인에 등장한다. initcap은 단어별 첫 글자를 대문자화하고, lower/upper는 대소문자를 통일하며, ltrim/rtrim/trim/lpad/rpad는 공백을 다룬다. Java 정규식을 그대로 활용하는 regexp_extract/regexp_replace로 문자열 추출·치환을 하고, translate는 문자 단위 치환을, contains/instr은 존재 여부 확인을 담당한다. varargs를 활용하면 리스트로부터 임의 개수의 컬럼을 동적으로 생성할 수 있다.

대소문자와 공백 처리

initcap 함수는 공백으로 구분된 각 단어의 첫 글자를 대문자로 바꾼다. lower와 upper는 각각 문자열 전체를 소문자·대문자로 바꾼다.

문자열 앞뒤 공백을 다루는 함수로는 ltrim(왼쪽 공백 제거), rtrim(오른쪽 공백 제거), trim(양쪽 공백 제거), lpad(왼쪽에 지정 문자로 패딩), rpad(오른쪽에 지정 문자로 패딩)가 있다. 주의할 점은, lpad나 rpad에 넘긴 목표 길이가 원래 문자열 길이보다 짧으면 항상 문자열의 오른쪽부터 잘라낸다는 것이다.

핵심 포인트

  • initcap은 단어 단위 첫 글자만 대문자화하고, lower/upper는 전체 문자열의 대소문자를 통일한다
  • ltrim/rtrim/trim은 공백 제거, lpad/rpad는 패딩이며, 목표 길이가 원래 문자열보다 짧으면 오른쪽부터 잘려나간다

정규표현식: regexp_extract와 regexp_replace

Spark는 Java 정규표현식의 전체 기능을 그대로 활용하지만 문법이 다른 언어와 조금 다를 수 있어 프로덕션에 쓰기 전 검토가 필요하다. 핵심 함수 두 가지는 regexp_replace(값 치환)와 regexp_extract(값 추출)다.

regexp_replace는 매칭되는 패턴을 다른 문자열로 치환한다. 예를 들어 색상 이름들을 OR로 묶은 정규식(BLACK|WHITE|RED|GREEN|BLUE)에 매칭되는 부분을 "COLOR"로 치환할 수 있다. regexp_extract는 반대로 패턴에 매칭되는 부분을 그룹 번호(괄호로 묶은 캡처 그룹)로 지정해 추출한다.

from pyspark.sql.functions import regexp_extract
extract_str = "(BLACK|WHITE|RED|GREEN|BLUE)"
df.select(regexp_extract(col("Description"), extract_str, 1).alias("color_cleaned"), col("Description")).show(2)

핵심 포인트

  • Spark의 정규식은 Java 정규식 문법을 그대로 따르므로 다른 언어와 미묘한 차이가 있을 수 있다
  • regexp_replace는 패턴 매칭 부분을 다른 문자열로 치환하고, regexp_extract는 캡처 그룹으로 지정한 부분을 추출한다

문자 단위 치환(translate)과 존재 확인(contains/instr)

여러 문자를 각각 다른 문자로 바꾸는 작업을 정규식으로 구현하면 번거로울 수 있어, Spark는 translate 함수를 제공한다. translate(컬럼, "LEET", "1337")처럼 첫 번째 문자열의 각 문자를 같은 위치의 두 번째 문자열 문자로 1:1 치환한다(문자 단위 매핑이지 부분 문자열 치환이 아니다).

단순히 특정 문자열이 포함되어 있는지 확인하려면 Scala/Java에서는 Column의 contains 메서드를, Python에서는 instr 함수(1부터 시작하는 위치를 반환하며 >= 1로 존재 여부를 판단)를 쓴다.

핵심 포인트

  • translate는 두 문자열을 문자 단위로 1:1 매핑해 치환하는 함수로, 부분 문자열 치환과는 다르다
  • 존재 여부 확인은 Scala/Java에서 contains 메서드, Python에서 instr(1부터 시작하는 위치 반환)로 한다

varargs로 동적 컬럼 생성하기

색상이 두세 개면 containsBlack.or(containsWhite)처럼 하드코딩해도 되지만, 값이 많아지면 이런 방식은 번거로워진다. Spark는 varargs(가변 인자) 언어 기능을 지원해 임의 길이의 배열을 함수 인자로 풀어(unravel) 전달할 수 있다.

예를 들어 색상 리스트를 map으로 순회하며 각 색상마다 is_black, is_white 같은 컬럼 표현식을 만들고, select(selectedColumns:_*)(Scala) 또는 select(*selectedColumns)(Python)처럼 배열을 풀어서 select에 전달하면, 리스트 길이에 따라 동적으로 여러 개의 boolean 컬럼을 생성할 수 있다. Python에서는 locate 함수(1-based 위치를 반환)를 boolean으로 캐스팅해 비슷한 효과를 낸다.

핵심 포인트

  • varargs를 이용하면 리스트를 함수 인자로 풀어서 전달할 수 있어, select로 임의 개수의 컬럼을 동적으로 생성할 수 있다
  • Python에서는 locate 함수로 위치를 구한 뒤 boolean으로 캐스팅해 Scala의 contains와 동등한 동적 컬럼 생성을 구현한다