PySpark 3 -์ง‘๊ณ„

no-glass-otackuยท2026๋…„ 6์›” 19์ผ

MS data school

๋ชฉ๋ก ๋ณด๊ธฐ
24/25

[Databricks] PySpark ์ง‘๊ณ„(Aggregation) โ€” groupBy, agg, ๋‚ด์žฅ ํ•จ์ˆ˜ ์ด์ •๋ฆฌ

๐Ÿ—“๏ธ ํ•™์Šต์ผ: 2026.06
๐Ÿ“š ์ถœ์ฒ˜: Databricks ์ˆ˜์—… ์ž๋ฃŒ (5_Aggregation.ipynb)


๐ŸŽฏ ์˜ค๋Š˜ ๋ฐฐ์šด ๊ฒƒ ํ•œ ์ค„ ์š”์•ฝ

"๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ๊ณ (groupBy), ๊ทธ ๊ทธ๋ฃน์— ์ง‘๊ณ„๋ฅผ ์ ์šฉํ•œ๋‹ค(agg / count / sum / avg โ€ฆ)"


1. ์ง‘๊ณ„(Aggregation)๋ž€?

์ง‘๊ณ„๋Š” ์—ฌ๋Ÿฌ ํ–‰(row)์„ ํ•˜๋‚˜์˜ ์š”์•ฝ ๊ฐ’์œผ๋กœ ์••์ถ•ํ•˜๋Š” ์ž‘์—…์ด๋‹ค.

๋น„์œ  ๐Ÿงพ
์—‘์…€์—์„œ ํ”ผ๋ฒ— ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค ๋•Œ๋ฅผ ๋– ์˜ฌ๋ ค๋ณด์ž.
"์ง€์—ญ๋ณ„ ๋งค์ถœ ํ•ฉ๊ณ„"๋ฅผ ๊ตฌํ•  ๋•Œ, ์ˆ˜๋ฐฑ ๊ฐœ์˜ ํ–‰์„ ์ง€์—ญ์œผ๋กœ ๋ฌถ๊ณ , ๋งค์ถœ์„ ๋”ํ•˜์ง€ ์•Š๋‚˜?
PySpark์˜ groupBy + ์ง‘๊ณ„ ํ•จ์ˆ˜๊ฐ€ ๋ฐ”๋กœ ๊ทธ ์—ญํ• ์ด๋‹ค.


2. ํ•ต์‹ฌ ํ๋ฆ„

DataFrame
  โ””โ”€ .groupBy("์—ด์ด๋ฆ„")       โ† ๊ทธ๋ฃนํ™” (GroupedData ๊ฐ์ฒด ์ƒ์„ฑ)
       โ””โ”€ .count()            โ† ์ง‘๊ณ„ ์ ์šฉ
       โ””โ”€ .avg("๋‹ค๋ฅธ์—ด")
       โ””โ”€ .sum("๋‹ค๋ฅธ์—ด")
       โ””โ”€ .agg(ํ•จ์ˆ˜1, ํ•จ์ˆ˜2)  โ† ์—ฌ๋Ÿฌ ์ง‘๊ณ„๋ฅผ ํ•œ ๋ฒˆ์—

3. groupBy โ€” ๊ทธ๋ฃนํ™”ํ•˜๊ธฐ

groupBy๋Š” DataFrame์„ ํŠน์ • ์—ด ๊ธฐ์ค€์œผ๋กœ ๋ฌถ์–ด GroupedData ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.
์ด ์‹œ์ ์—์„œ๋Š” ์•„์ง ๊ณ„์‚ฐ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค. ์ง‘๊ณ„ ๋ฉ”์„œ๋“œ๋ฅผ ๋ถ™์—ฌ์•ผ ์‹ค์ œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค.

# ๋‹จ์ผ ์—ด ๊ธฐ์ค€
df.groupBy("event_name")

# ์—ฌ๋Ÿฌ ์—ด ๊ธฐ์ค€ (์กฐํ•ฉ)
df.groupBy("geo.state", "geo.city")

๐Ÿ’ก ์ค‘์ฒฉ ์—ด(nested column) ์ ‘๊ทผ
"geo.state" ์ฒ˜๋Ÿผ ์ (.)์œผ๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ์˜ ํ•„๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.


4. GroupedData ์ง‘๊ณ„ ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ์„ค๋ช…
count()๊ฐ ๊ทธ๋ฃน์˜ ํ–‰ ์ˆ˜
avg("์—ด")๊ฐ ๊ทธ๋ฃน์˜ ํ‰๊ท 
sum("์—ด")๊ฐ ๊ทธ๋ฃน์˜ ํ•ฉ๊ณ„
max("์—ด")๊ฐ ๊ทธ๋ฃน์˜ ์ตœ๋Œ“๊ฐ’
min("์—ด")๊ฐ ๊ทธ๋ฃน์˜ ์ตœ์†Ÿ๊ฐ’
agg(...)์—ฌ๋Ÿฌ ์ง‘๊ณ„๋ฅผ ํ•œ ๋ฒˆ์— ์ ์šฉ

์ฝ”๋“œ ์˜ˆ์‹œ

# ์ด๋ฒคํŠธ ์ด๋ฆ„๋ณ„ ํ–‰ ์ˆ˜
event_counts_df = df.groupBy("event_name").count()
display(event_counts_df)

# ์ฃผ(state)๋ณ„ ํ‰๊ท  ๊ตฌ๋งค ์ˆ˜์ต
avg_state_purchases_df = df.groupBy("geo.state").avg("ecommerce.purchase_revenue_in_usd")
display(avg_state_purchases_df)

# ์ฃผ + ๋„์‹œ ์กฐํ•ฉ๋ณ„ ์ด ์ˆ˜๋Ÿ‰ & ์ˆ˜์ต ํ•ฉ๊ณ„
city_purchase_quantities_df = df.groupBy("geo.state", "geo.city").sum(
    "ecommerce.total_item_quantity",
    "ecommerce.purchase_revenue_in_usd"
)
display(city_purchase_quantities_df)

5. ๋‚ด์žฅ ํ•จ์ˆ˜(Built-in Functions)์™€ agg

pyspark.sql.functions ๋ชจ๋“ˆ์—๋Š” ๋” ๋‹ค์–‘ํ•œ ์ง‘๊ณ„ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค.
์ด ํ•จ์ˆ˜๋“ค์€ agg()์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ฒฐ๊ณผ ์—ด์— ๋ณ„์นญ(alias) ๋„ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.

์ฃผ์š” ์ง‘๊ณ„ ๋‚ด์žฅ ํ•จ์ˆ˜

ํ•จ์ˆ˜์„ค๋ช…
sum("์—ด")ํ•ฉ๊ณ„
avg("์—ด")ํ‰๊ท 
approx_count_distinct("์—ด")๊ณ ์œ ๊ฐ’ ๊ฐœ์ˆ˜ (๊ทผ์‚ฌ์น˜, ๋น ๋ฆ„)
collect_list("์—ด")๊ทธ๋ฃน ๋‚ด ๊ฐ’์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฌถ๊ธฐ
stddev_samp("์—ด")ํ‘œ๋ณธ ํ‘œ์ค€ํŽธ์ฐจ

โ“ approx_count_distinct vs count(distinct โ€ฆ)
์ •ํ™•ํ•œ ๊ณ ์œ ๊ฐ’ ์ˆ˜๋ฅผ ์„ธ๋ ค๋ฉด ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค ๋ด์•ผ ํ•ด์„œ ๋А๋ฆฌ๋‹ค.
approx_count_distinct๋Š” HyperLogLog ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๊ทผ์‚ฌ์น˜๋ฅผ ๊ตฌํ•œ๋‹ค.
๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ์—์„œ๋Š” ์ด ํ•จ์ˆ˜๊ฐ€ ํ›จ์”ฌ ์‹ค์šฉ์ ์ด๋‹ค.

์ฝ”๋“œ ์˜ˆ์‹œ

from pyspark.sql.functions import sum

# ๋‹จ์ผ ์ง‘๊ณ„ + alias
state_purchases_df = df.groupBy("geo.state").agg(
    sum("ecommerce.total_item_quantity").alias("total_purchases")
)
display(state_purchases_df)
from pyspark.sql.functions import avg, approx_count_distinct

# ์—ฌ๋Ÿฌ ์ง‘๊ณ„๋ฅผ ํ•œ ๋ฒˆ์—
state_aggregates_df = (df
    .groupBy("geo.state")
    .agg(
        avg("ecommerce.total_item_quantity").alias("avg_quantity"),
        approx_count_distinct("user_id").alias("distinct_users")
    )
)
display(state_aggregates_df)

6. ์ˆ˜ํ•™ ๋‚ด์žฅ ํ•จ์ˆ˜ (๋ณด๋„ˆ์Šค)

์ง‘๊ณ„ ์™ธ์—๋„ ์—ด(column) ๋‹จ์œ„ ์ˆ˜ํ•™ ์—ฐ์‚ฐ ํ•จ์ˆ˜๋„ ์žˆ๋‹ค.

ํ•จ์ˆ˜์„ค๋ช…
sqrt("์—ด")์ œ๊ณฑ๊ทผ
cos("์—ด")์ฝ”์‚ฌ์ธ
ceil("์—ด")์˜ฌ๋ฆผ
round("์—ด")๋ฐ˜์˜ฌ๋ฆผ
log("์—ด")์ž์—ฐ ๋กœ๊ทธ
from pyspark.sql.functions import cos, sqrt

display(
    spark.range(10)          # 0~9 ๋ฒ”์œ„์˜ id ์—ด์„ ๊ฐ€์ง„ DataFrame ์ƒ์„ฑ
         .withColumn("sqrt", sqrt("id"))
         .withColumn("cos", cos("id"))
)

7. ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šด ํฌ์ธํŠธ ์ •๋ฆฌ

Q. groupBy ๋ฉ”์„œ๋“œ์˜ ์ง‘๊ณ„(avg, sum)์™€ ๋‚ด์žฅ ํ•จ์ˆ˜์˜ ์ง‘๊ณ„(avg, sum)์˜ ์ฐจ์ด๋Š”?

A.
๋‘˜ ๋‹ค ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด์ง€๋งŒ, ๋‚ด์žฅ ํ•จ์ˆ˜ ๋ฐฉ์‹(agg + ๋‚ด์žฅ ํ•จ์ˆ˜)์ด ๋” ๊ฐ•๋ ฅํ•˜๋‹ค.

๊ตฌ๋ถ„๋ฐฉ๋ฒ•ํŠน์ง•
GroupedData ๋ฉ”์„œ๋“œ.avg("์—ด")๊ฐ„๋‹จํ•˜์ง€๋งŒ, alias ๋ถˆ๊ฐ€ / ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ง‘๊ณ„๋งŒ ์‰ฝ๊ฒŒ ๊ฐ€๋Šฅ
๋‚ด์žฅ ํ•จ์ˆ˜ + agg.agg(avg("์—ด").alias("๋ณ„์นญ"))alias ๊ฐ€๋Šฅ, ์—ฌ๋Ÿฌ ์ง‘๊ณ„ ๋™์‹œ์—, ๋” ๋‹ค์–‘ํ•œ ํ•จ์ˆ˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

Q. alias๋Š” ์™œ ์“ฐ๋Š”๊ฐ€?

์ง‘๊ณ„๋ฅผ ํ•˜๋ฉด ์—ด ์ด๋ฆ„์ด avg(ecommerce.total_item_quantity) ์ฒ˜๋Ÿผ ๊ธธ๊ณ  ๋ชป์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค.
alias("avg_quantity") ๋กœ ์ฝ๊ธฐ ์ข‹์€ ์ด๋ฆ„์„ ๋ถ™์—ฌ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.


8. ์˜ค๋Š˜์˜ Q&A

(์งˆ๋ฌธ์„ ํ•˜๋ฉด ์ด ์„น์…˜์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค)


9. ์ „์ฒด ๊ตฌ์กฐ ํ•œ๋ˆˆ์— ๋ณด๊ธฐ

[๋ฐ์ดํ„ฐ ์ง‘๊ณ„ ์ „๋žต]

โ‘  ๋‹จ์ˆœ ์ง‘๊ณ„
   df.groupBy("A").count()
   df.groupBy("A").avg("B")
   df.groupBy("A", "B").sum("C", "D")

โ‘ก agg + ๋‚ด์žฅ ํ•จ์ˆ˜ (์ถ”์ฒœ โญ)
   from pyspark.sql.functions import avg, approx_count_distinct
   df.groupBy("A").agg(
       avg("B").alias("ํ‰๊ท B"),
       approx_count_distinct("C").alias("๊ณ ์œ C์ˆ˜")
   )

โ‘ข ์—ด ๋‹จ์œ„ ์ˆ˜ํ•™ ํ•จ์ˆ˜
   from pyspark.sql.functions import sqrt, cos
   df.withColumn("sqrt_id", sqrt("id"))

โœ๏ธ ์ดˆ์•ˆ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์งˆ๋ฌธ ํ›„ Q&A ์„น์…˜๊ณผ ์„ค๋ช…์ด ๋ณด๊ฐ•๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

profile
์ด์ œ ๊ฐœ๋ฐœํ•ด์•ผ์ง€...

0๊ฐœ์˜ ๋Œ“๊ธ€