전체 그래프
Spark

RDD Transformations

data-engineeringsparkrddtransformation

상위: Spark

요약

RDD Transformation은 기존 RDD를 새로운 RDD로 변환하는 연산입니다. map, filter, flatMap 등의 기본 연산부터 groupBy, join 등의 집계 연산까지 다양하며, Lazy Evaluation 방식으로 동작합니다.

MAP

각 요소에 함수를 적용하여 1:1 변환

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize(['b', 'a', 'c'])
y = x.map(lambda z: (z, 1))
print(x.collect())
print(y.collect())

출력:

x: ['b', 'a', 'c']
y: [('b', 1), ('a', 1), ('c', 1)]

FLATMAP

각 요소를 여러 요소로 확장 (1:N 변환)

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([1, 2, 3])
y = x.flatMap(lambda x: (1*x, 2*x, 3*x, 100))
print(x.collect())
print(y.collect())
print(y.mean())

출력:

x: [1, 2, 3]
y: [1, 2, 3, 100, 2, 4, 6, 100, 3, 6, 9, 100]
y.mean(): 28.0

FILTER

조건을 만족하는 요소만 필터링

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([1, 2, 3])
y = x.filter(lambda x: x % 2 == 1)  # 홀수만 필터링
print(x.collect())
print(y.collect())

출력:

x: [1, 2, 3]
y: [1, 3]

MAPPARTITIONS

파티션 단위로 함수를 적용

RDD Transformations

x = sc.parallelize([1, 2, 3], 2)

def f(iterator):
    yield sum(iterator)
    yield 42

y = x.mapPartitions(f)

print(x.glom().collect())  #  파티션의 원소 확인
print(y.glom().collect())  # 파티션별 결과

출력:

x: [[1], [2, 3]]
y: [[1, 42], [5, 42]]

MAPPARTITIONS WITH INDEX

파티션 인덱스와 함께 처리

RDD Transformations

x = sc.parallelize([1, 2, 3], 2)

def f(partitionIndex, iterator):
    yield (partitionIndex, sum(iterator))

y = x.mapPartitionsWithIndex(f)

print(x.glom().collect())
print(y.glom().collect())

출력:

x: [[1], [2, 3]]
y: [[(0, 1)], [(1, 5)]]

KEYBY

각 요소에서 키를 추출하여 (key, value) 형태로 변환

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize(['John', 'Fred', 'Anna', 'James'])

y = x.keyBy(lambda w: w[0])

print(y.collect())

출력:

x: ['John', 'Fred', 'Anna', 'James']
y: [('J', 'John'), ('F', 'Fred'), ('A', 'Anna'), ('J', 'James')]

GROUPBY

키를 기준으로 그룹화

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize(['John', 'Fred', 'Anna', 'James'])

y = x.groupBy(lambda w: w[0])
print([{t[0]: [i for i in t[1]]} for t in y.collect()])

출력:

x: ['John', 'Fred', 'Anna', 'James']
y: [{'A': ['Anna']}, {'J': ['John', 'James']}, {'F': ['Fred']}]

GROUPBYKEY

(key, value) RDD를 키로 그룹화

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([('B', 5), ('B', 4), ('A', 3), ('A', 2), ('A', 1)])

y = x.groupByKey()
print(x.collect())
print([(t[0], [i for i in t[1]]) for t in y.collect()])

출력:

x: [('B', 5), ('B', 4), ('A', 3), ('A', 2), ('A', 1)]
y: [('B', [5, 4]), ('A', [3, 2, 1])]

Word Count using GROUPBYKEY

words = sc.parallelize(['one', 'two', 'two', 'three', 'three', 'three'])
wordPairsRdd = words.map(lambda w: (w, 1))
wordCounts = wordPairsRdd.groupByKey().map(lambda pair: (pair[0], sum(pair[1])))

print(words.collect())
print(wordPairsRdd.collect())
print(wordCounts.collect())

출력:

words: ['one', 'two', 'two', 'three', 'three', 'three']
wordPairsRDD: [('one', 1), ('two', 1), ('two', 1), ('three', 1), ('three', 1), ('three', 1)]
wordCounts: [('one', 1), ('two', 2), ('three', 3)]

REDUCEBYKEY

Grouping + Aggregation을 한 번에 수행

words = sc.parallelize(['one', 'two', 'two', 'three', 'three', 'three'])
wordPairsRdd = words.map(lambda w: (w, 1))
wordCounts = wordPairsRdd.reduceByKey(lambda cnt1, cnt2: cnt1 + cnt2)

print(words.collect())
print(wordPairsRdd.collect())
print(wordCounts.collect())

출력:

words: ['one', 'two', 'two', 'three', 'three', 'three']
wordPairsRDD: [('one', 1), ('two', 1), ('two', 1), ('three', 1), ('three', 1), ('three', 1)]
wordCounts: [('one', 1), ('two', 2), ('three', 3)]

REDUCEBYKEY vs GROUPBYKEY

  • 두 함수가 모두 사용 가능하다면, ReduceByKey 사용 권장
  • ReduceByKey는 셔플 전에 행을 결합하여 셔플해야 할 행의 수를 줄일 수 있음
    • 로컬 집계 수행
    • 중간 결과의 크기를 줄일 수 있음

REDUCEBYKEY: 집계하고 Shuffle → 효율적

RDD Transformations

GROUPBYKEY: Shuffle하고 집계 → 비효율적

RDD Transformations

JOIN

두 RDD를 키 기준으로 조인

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([("a", 1), ("b", 2)])
y = sc.parallelize([("a", 3), ("a", 4), ("b", 5)])

z = x.join(y)
print(z.collect())

출력:

x: [("a", 1), ("b", 2)]
y: [("a", 3), ("a", 4), ("b", 5)]
z: [("a", (1, 3)), ("a", (1, 4)), ("b", (2, 5))]

UNION

두 RDD를 합침 (중복 제거 안 함)

RDD Transformations

x = sc.parallelize([1, 2, 3], 2)
y = sc.parallelize([3, 4], 1)
z = x.union(y)

print(z.glom().collect())

출력:

x: [[1], [2, 3]]
y: [3, 4]
z: [[1], [2, 3], [3, 4]]

DISTINCT

중복을 제거 (Shuffle 발생)

RDD Transformations

RDD Transformations

x = sc.parallelize([1, 2, 3, 3, 4])
y = x.distinct()

print(y.collect())

출력:

x: [1, 2, 3, 3, 4]
y: [1, 2, 3, 4]

SAMPLE

데이터를 샘플링

형식: sample(withReplacement, fraction, seed=None)

RDD Transformations

x = sc.parallelize([1, 2, 3, 4, 5])
y = x.sample(False, 0.4, 42)

print(x.collect())
print(y.collect())

출력:

x: [1, 2, 3, 4, 5]
y: [1, 3]

COALESCE

파티션 수를 줄임 (Shuffle 없이)

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([1, 2, 3, 4, 5], 3)
y = x.coalesce(2)

print(x.glom().collect())
print(y.glom().collect())

출력:

x: [[1], [2, 3], [4, 5]]
y: [[1], [2, 3, 4, 5]]

COALESCE vs REPARTITION

  • COALESCE: 파티션 줄이기 (merge), Shuffle 없음
  • REPARTITION: 파티션 늘리기 or 균등 재분배, Shuffle 발생

PARTITIONBY

커스텀 파티셔너로 재분배

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

RDD Transformations

x = sc.parallelize([('J', 'James'), ('F', 'Fred'), ('A', 'Anna'), ('J', 'James')], 3)
y = x.partitionBy(2, lambda w: 0 if w[0] < 'H' else 1)

print(x.glom().collect())
print(y.glom().collect())

출력:

x: [[('J', 'James')], [('F', 'Fred'), ('A', 'Anna')], [('J', 'James')]]
y: [[('A', 'Anna'), ('F', 'Fred')], [('J', 'James'), ('J', 'James')]]