Jupyter 노트북과 chDB로 ClickHouse Cloud 데이터 탐색하기

Jupyter 노트북과 chDB로 ClickHouse Cloud 데이터 탐색하기

ClickHouse Cloud의 데이터를 Jupyter 노트북에서 탐색하는 방법을 배워봐요. 이 과정에서는 ClickHouse 기반의 고속 인프로세스 SQL OLAP 엔진인 chDB를 활용해서, 클라우드 데이터와 로컬 데이터를 하나의 노트북에서 자유롭게 오가며 분석하고 시각화하는 흐름을 직접 따라 해 봐요.

출처: 문서

본문

이 가이드에서는 chDB 덕분에 Jupyter 노트북에서 ClickHouse Cloud 데이터를 손쉽게 탐색하는 방법을 배워 봐요.

사전 준비 사항:

  • 가상 환경 (virtual environment)
  • 동작 중인 ClickHouse Cloud 서비스와 연결 정보

아직 ClickHouse Cloud 계정이 없다면 가입해서 시작할 수 있어요. 트라이얼에 가입하면 시작할 수 있는 $300 크레딧이 제공돼요.

배우게 될 내용:

  • chDB를 사용해서 Jupyter 노트북에서 ClickHouse Cloud에 연결하기
  • 원격 데이터셋 쿼리하고 결과를 Pandas DataFrame으로 변환하기
  • 클라우드 데이터와 로컬 CSV 파일을 결합해 분석하기
  • matplotlib를 사용해서 데이터 시각화하기

ClickHouse Cloud에서 제공하는 스타터 데이터셋 중 하나인 UK Property Price 데이터셋을 사용할 거예요. 이 데이터는 1995년부터 2024년까지 영국에서 판매된 주택 가격에 대한 정보를 담고 있어요.

셋업 (Setup)

기존 ClickHouse Cloud 서비스에 이 데이터셋을 추가하려면 console.clickhouse.cloud에 계정으로 로그인해요. 왼쪽 메뉴에서 Data sources를 클릭하고 Predefined sample data를 선택해요.

UK property price paid data (4GB) 카드에서 Get started를 선택해요.

그다음 Import dataset을 클릭해요.

ClickHouse가 default 데이터베이스에 pp_complete 테이블을 자동으로 생성하고, 2,892만 개의 가격 데이터 행을 채워줘요.

자격 증명이 노출될 가능성을 줄이기 위해, 로컬 머신에서 Cloud 사용자 이름과 비밀번호를 환경 변수로 추가하는 걸 권장해요. 터미널에서 다음 명령을 실행해서 사용자 이름과 비밀번호를 환경 변수로 설정해요:

export CLICKHOUSE_USER=default
export CLICKHOUSE_PASSWORD=your_actual_password

위 환경 변수는 터미널 세션이 유지되는 동안만 유효해요. 영구적으로 설정하려면 셸 설정 파일에 추가해 두면 돼요.

이제 가상 환경을 활성화해요. 가상 환경 안에서 다음 명령으로 Jupyter Notebook을 설치해요:

pip install notebook

다음 명령으로 Jupyter Notebook을 실행해요:

jupyter notebook

localhost:8888에서 Jupyter 인터페이스가 있는 새 브라우저 창이 열릴 거예요. 새 노트북을 만들려면 File > New > Notebook을 클릭해요.

커널 선택 화면이 나타나면, 사용 가능한 Python 커널 중 아무거나 선택해요. 이 예시에서는 ipykernel을 선택할게요.

빈 셀에 다음 명령을 입력해서, 원격 ClickHouse Cloud 인스턴스에 연결할 때 사용할 chDB를 설치해요:

pip install chdb

이제 chDB를 가져와서 간단한 쿼리를 실행해 모든 게 제대로 설정됐는지 확인할 수 있어요:

import chdb

result = chdb.query("SELECT 'Hello, ClickHouse!' as message")
print(result)

데이터 탐색하기 (Exploring the data)

UK price paid 데이터가 준비되고 Jupyter 노트북에서 chDB가 실행되고 있으니, 이제 데이터 탐색을 시작할 차례예요. 영국에서 특정 지역(수도인 런던 같은 곳)의 가격이 시간에 따라 어떻게 변했는지 확인한다고 상상해 봐요.

ClickHouse의 remoteSecure 함수를 사용하면 ClickHouse Cloud에서 데이터를 간편하게 가져올 수 있어요. chDB에 이 데이터를 프로세스 안에서 Pandas 데이터 프레임으로 반환하도록 지시할 수 있는데, 이는 데이터를 다루는 익숙하고 편리한 방식이에요.

ClickHouse Cloud 서비스에서 UK price paid 데이터를 가져와서 pandas.DataFrame으로 바꾸는 다음 쿼리를 작성해 봐요:

import os
from dotenv import load_dotenv
import chdb
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# Load environment variables from .env file
load_dotenv()

username = os.environ.get('CLICKHOUSE_USER')
password = os.environ.get('CLICKHOUSE_PASSWORD')

query = f"""
SELECT 
    toYear(date) AS year,
    avg(price) AS avg_price
FROM remoteSecure(
'****.europe-west4.gcp.clickhouse.cloud',
default.pp_complete,
'{username}',
'{password}'
)
WHERE town = 'LONDON'
GROUP BY toYear(date)
ORDER BY year;
"""

df = chdb.query(query, "DataFrame")
df.head()

위 스니펫에서 chdb.query(query, "DataFrame")은 지정한 쿼리를 실행하고 결과를 Pandas DataFrame으로 터미널에 출력해요. 쿼리에서는 ClickHouse Cloud에 연결하기 위해 remoteSecure 함수를 사용하고 있어요. remoteSecure 함수는 다음을 매개변수로 받아요:

  • 연결 문자열
  • 사용할 데이터베이스와 테이블 이름
  • 사용자 이름
  • 비밀번호

보안 모범 사례로, 사용자 이름과 비밀번호 매개변수는 함수에 직접 지정하는 것보다 환경 변수를 사용하는 걸 권장해요. 물론 원하면 직접 지정하는 것도 가능해요.

remoteSecure 함수는 원격 ClickHouse Cloud 서비스에 연결해서 쿼리를 실행하고 결과를 반환해요. 데이터 크기에 따라 몇 초가 걸릴 수도 있어요. 이 경우 연도별 평균 가격 지점을 반환하고 town='LONDON'으로 필터링했어요. 결과는 df라는 변수의 DataFrame에 저장돼요. df.head는 반환된 데이터의 첫 몇 행만 표시해요.

새 셀에서 다음 명령을 실행해 열의 타입을 확인해 봐요:

df.dtypes
year          uint16
avg_price    float64
dtype: object

date는 ClickHouse에서 Date 타입이지만, 결과 데이터 프레임에서는 uint16 타입이라는 걸 확인할 수 있어요. chDB는 DataFrame을 반환할 때 가장 적절한 타입을 자동으로 추론해요.

이제 익숙한 형태로 데이터를 얻었으니, 런던 부동산 가격이 시간에 따라 어떻게 변했는지 살펴볼게요. 새 셀에서 다음 명령을 실행해서 matplotlib로 런던의 시간-가격 차트를 만들어 봐요:

plt.figure(figsize=(12, 6))
plt.plot(df['year'], df['avg_price'], marker='o')
plt.xlabel('Year')
plt.ylabel('Price (£)')
plt.title('Price of London property over time')

# Show every 2nd year to avoid crowding
years_to_show = df['year'][::2]  # Every 2nd year
plt.xticks(years_to_show, rotation=45)

plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

놀랍지 않게도, 런던의 부동산 가격은 시간이 지나면서 크게 상승했어요. 동료 데이터 과학자가 주택 관련 추가 변수가 담긴 .csv 파일을 보내주면서, 런던에서 판매된 주택 수가 시간에 따라 어떻게 변했는지 궁금해하고 있어요. 이 데이터를 주택 가격과 함께 플로팅해서 상관관계를 발견할 수 있는지 확인해 볼게요.

file 테이블 엔진을 사용하면 로컬 머신의 파일을 직접 읽을 수 있어요. 새 셀에서 다음 명령을 실행해서 로컬 .csv 파일로 새 DataFrame을 만들어요:

query = f"""
SELECT 
    toYear(date) AS year,
    sum(houses_sold)*1000
    FROM file('/Users/datasci/Desktop/housing_in_london_monthly_variables.csv')
WHERE area = 'city of london' AND houses_sold IS NOT NULL
GROUP BY toYear(date)
ORDER BY year;
"""

df_2 = chdb.query(query, "DataFrame")
df_2.head()

한 번에 여러 소스에서 읽기

한 단계로 여러 소스에서 읽는 것도 가능해요. JOIN을 사용하는 아래 쿼리로 그렇게 할 수 있어요:

query = f"""
SELECT 
    toYear(date) AS year,
    avg(price) AS avg_price, housesSold
FROM remoteSecure(
'****.europe-west4.gcp.clickhouse.cloud',
default.pp_complete,
'{username}',
'{password}'
) AS remote
JOIN (
  SELECT 
    toYear(date) AS year,
    sum(houses_sold)*1000 AS housesSold
    FROM file('/Users/datasci/Desktop/housing_in_london_monthly_variables.csv')
  WHERE area = 'city of london' AND houses_sold IS NOT NULL
  GROUP BY toYear(date)
  ORDER BY year
) AS local ON local.year = remote.year
WHERE town = 'LONDON'
GROUP BY toYear(date)
ORDER BY year;
"""

2020년 이후 데이터는 빠져 있지만, 1995년부터 2019년까지 두 데이터셋을 서로 비교해 플로팅할 수 있어요. 새 셀에서 다음 명령을 실행해요:

# Create a figure with two y-axes
fig, ax1 = plt.subplots(figsize=(14, 8))

# Plot houses sold on the left y-axis
color = 'tab:blue'
ax1.set_xlabel('Year')
ax1.set_ylabel('Houses Sold', color=color)
ax1.plot(df_2['year'], df_2['houses_sold'], marker='o', color=color, label='Houses Sold', linewidth=2)
ax1.tick_params(axis='y', labelcolor=color)
ax1.grid(True, alpha=0.3)

# Create a second y-axis for price data
ax2 = ax1.twinx()
color = 'tab:red'
ax2.set_ylabel('Average Price (£)', color=color)

# Plot price data up until 2019
ax2.plot(df[df['year'] <= 2019]['year'], df[df['year'] <= 2019]['avg_price'], marker='s', color=color, label='Average Price', linewidth=2)
ax2.tick_params(axis='y', labelcolor=color)

# Format price axis with currency formatting
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'£{x:,.0f}'))

# Set title and show every 2nd year
plt.title('London Housing Market: Sales Volume vs Prices Over Time', fontsize=14, pad=20)

# Use years only up to 2019 for both datasets
all_years = sorted(list(set(df_2[df_2['year'] <= 2019]['year']).union(set(df[df['year'] <= 2019]['year']))))
years_to_show = all_years[::2]  # Every 2nd year
ax1.set_xticks(years_to_show)
ax1.set_xticklabels(years_to_show, rotation=45)

# Add legends
ax1.legend(loc='upper left')
ax2.legend(loc='upper right')

plt.tight_layout()
plt.show()

플로팅된 데이터를 보면, 판매량은 1995년 약 16만 건에서 시작해 1999년 약 54만 건으로 급증하고, 그 후 2000년대 중반까지 급격히 감소하다가 2007-2008 금융위기 동안 크게 떨어져 약 14만 건으로 줄었어요. 반면 가격은 1995년 약 £150,000에서 2005년 약 £300,000까지 꾸준하고 일관된 성장을 보였어요. 성장은 2012년 이후 크게 빨라져 2019년에는 약 £400,000에서 £1,000,000 이상으로 가파르게 상승했어요. 판매량과 달리 가격은 2008년 위기의 영향을 거의 받지 않고 상승 궤도를 유지했어요.

요약 (Summary)

이 가이드는 chDB가 ClickHouse Cloud와 로컬 데이터 소스를 연결함으로써 Jupyter 노트북에서 원활한 데이터 탐색을 가능하게 해 준다는 걸 보여줬어요. UK Property Price 데이터셋을 사용해, remoteSecure() 함수로 원격 ClickHouse Cloud 데이터를 쿼리하고, file() 테이블 엔진으로 로컬 CSV 파일을 읽고, 결과를 분석과 시각화를 위해 Pandas DataFrame으로 직접 변환하는 방법을 살펴봤어요. chDB를 통해 데이터 과학자들은 Pandas와 matplotlib 같은 익숙한 Python 도구와 ClickHouse의 강력한 SQL 기능을 함께 활용해서, 여러 데이터 소스를 결합한 종합적인 분석을 쉽게 수행할 수 있어요. 런던에 사는 데이터 과학자분들 중 상당수는 당분간 자신의 집이나 아파트를 살 여유가 없을지 몰라도, 적어도 자신을 가격으로 밀어낸 시장은 분석할 수 있겠네요!

더 알아보기 (Learn more)