호환성 접근 방식
# 일반적인 마이그레이션 - import 구문만 변경하세요
- import pandas as pd
+ from chdb import datastore as pd
# 기존 코드를 그대로 사용할 수 있습니다
df = pd.read_csv("data.csv")
result = df[df['age'] > 25].groupby('city')['salary'].mean()
- pandas DataFrame 메서드 209개 모두 구현
- SQL 최적화를 위한 지연 평가
- 자동 타입 래핑(DataFrame → DataStore, Series → ColumnExpr)
- 불변 연산(
inplace=True없음)
속성 및 프로퍼티
| 속성 | 설명 | 실행 트리거 |
|---|---|---|
shape | (행, 컬럼) 튜플 | 예 |
columns | 컬럼 이름(Index) | 예 |
dtypes | 컬럼 데이터 타입 | 예 |
values | NumPy 배열 | 예 |
index | 행 인덱스 | 예 |
size | 요소 개수 | 예 |
ndim | 차원 수 | 아니요 |
empty | DataFrame이 비어 있는지 여부 | 예 |
T | 전치 | 예 |
axes | 축 목록 | 예 |
from chdb import datastore as pd
ds = pd.read_csv("data.csv")
print(ds.shape) # (1000, 5)
print(ds.columns) # Index(['name', 'age', 'city', 'salary', 'dept'])
print(ds.dtypes) # name: object, age: int64, ...
print(ds.empty) # False
인덱싱 및 선택
| 메서드 | 설명 | 예시 |
|---|---|---|
df['col'] | 컬럼 선택 | ds['age'] |
df[['col1', 'col2']] | 여러 컬럼 선택 | ds[['name', 'age']] |
df[condition] | 불리언 인덱싱 | ds[ds['age'] > 25] |
df.loc[...] | 레이블 기반 접근 | ds.loc[0:10, 'name'] |
df.iloc[...] | 정수 기반 접근 | ds.iloc[0:10, 0:3] |
df.at[...] | 레이블 기준 단일 값 | ds.at[0, 'name'] |
df.iat[...] | 위치 기준 단일 값 | ds.iat[0, 0] |
df.head(n) | 처음 n개 행 | ds.head(10) |
df.tail(n) | 마지막 n개 행 | ds.tail(10) |
df.sample(n) | 무작위 샘플 | ds.sample(100) |
df.select_dtypes() | Dtype 기준 선택 | ds.select_dtypes(include='number') |
df.query() | 쿼리 표현식 | ds.query('age > 25') |
df.where() | 조건부 대체 | ds.where(ds['age'] > 0, 0) |
df.mask() | where의 반대 | ds.mask(ds['age'] < 0, 0) |
df.isin() | 값 포함 여부 | ds['city'].isin(['NYC', 'LA']) |
df.get() | 안전한 컬럼 접근 | ds.get('col', default=None) |
df.xs() | 교차 단면 | ds.xs('key') |
df.pop() | 컬럼 제거 | ds.pop('col') |
통계 메서드
| Method | Description | SQL Equivalent |
|---|---|---|
mean() | 평균값 | AVG() |
median() | 중앙값 | MEDIAN() |
mode() | 최빈값 | - |
std() | 표준 편차 | STDDEV() |
var() | 분산 | VAR() |
min() | 최솟값 | MIN() |
max() | 최댓값 | MAX() |
sum() | 합계 | SUM() |
prod() | 곱 | - |
count() | NULL이 아닌 개수 | COUNT() |
nunique() | 고유값 개수 | UNIQ() |
value_counts() | 값별 빈도 | GROUP BY |
quantile() | 분위수 | QUANTILE() |
describe() | 요약 통계 | - |
corr() | 상관 행렬 | CORR() |
cov() | 공분산 행렬 | COV() |
corrwith() | 쌍별 상관관계 | - |
rank() | 순위 | RANK() |
abs() | 절댓값 | ABS() |
round() | 반올림 | ROUND() |
clip() | 값 제한 | - |
cumsum() | 누적합 | 윈도 함수 |
cumprod() | 누적 곱 | 윈도 함수 |
cummin() | 누적 최솟값 | 윈도 함수 |
cummax() | 누적 최댓값 | 윈도 함수 |
diff() | 차이 | 윈도 함수 |
pct_change() | 백분율 변화 | 윈도 함수 |
skew() | 왜도 | SKEW() |
kurt() | 첨도 | KURT() |
sem() | 표준 오차 | - |
all() | 모두 true | - |
any() | 하나라도 true | - |
idxmin() | 최솟값의 인덱스 | - |
idxmax() | 최댓값의 인덱스 | - |
ds = pd.read_csv("data.csv")
# 기본 통계
print(ds['salary'].mean())
print(ds['age'].std())
print(ds.describe())
# 그룹 통계
print(ds.groupby('department')['salary'].mean())
print(ds.groupby('city').agg({'salary': ['mean', 'std'], 'age': 'count'}))
데이터 조작
| Method | Description |
|---|---|
drop() | 행/컬럼 삭제 |
drop_duplicates() | 중복 제거 |
duplicated() | 중복 여부 표시 |
dropna() | 결측값 제거 |
fillna() | 결측값 채우기 |
ffill() | 앞의 값으로 채우기 |
bfill() | 뒤의 값으로 채우기 |
interpolate() | 값 보간 |
replace() | 값 대체 |
rename() | 컬럼/인덱스 이름 변경 |
rename_axis() | 축 이름 변경 |
assign() | 새 컬럼 추가 |
astype() | 타입 변환 |
convert_dtypes() | 타입 추론 |
copy() | DataFrame 복사 |
ds = pd.read_csv("data.csv")
# 삭제 연산
result = ds.drop(columns=['unused_col'])
result = ds.drop_duplicates(subset=['user_id'])
result = ds.dropna(subset=['email'])
# 채우기 연산
result = ds.fillna(0)
result = ds.fillna({'age': 0, 'name': 'Unknown'})
# 변환 연산
result = ds.rename(columns={'old_name': 'new_name'})
result = ds.assign(
full_name=lambda x: x['first_name'] + ' ' + x['last_name'],
age_group=lambda x: pd.cut(x['age'], bins=[0, 25, 50, 100])
)
정렬 및 순위
| 메서드 | 설명 |
|---|---|
sort_values() | 값을 기준으로 정렬 |
sort_index() | 인덱스를 기준으로 정렬 |
nlargest() | 가장 큰 값 N개 |
nsmallest() | 가장 작은 값 N개 |
# 단일 컬럼으로 정렬
result = ds.sort_values('salary', ascending=False)
# 여러 컬럼으로 정렬
result = ds.sort_values(['department', 'salary'], ascending=[True, False])
# 상위/하위 N개 가져오기
result = ds.nlargest(10, 'salary')
result = ds.nsmallest(5, 'age')
형태 변환
| Method | Description |
|---|---|
pivot() | 피벗 테이블 |
pivot_table() | 집계를 포함한 피벗 |
melt() | 언피벗 |
stack() | 컬럼을 인덱스로 쌓기 |
unstack() | 인덱스를 컬럼으로 펼치기 |
transpose() / T | 전치 |
explode() | 리스트를 행으로 펼치기 |
squeeze() | 차원 축소 |
droplevel() | 인덱스 수준 삭제 |
swaplevel() | 인덱스 수준 스왑 |
reorder_levels() | 수준 재정렬 |
# 피벗 테이블
result = ds.pivot_table(
values='amount',
index='region',
columns='product',
aggfunc='sum'
)
# Melt (언피벗)
result = ds.melt(
id_vars=['name'],
value_vars=['score1', 'score2', 'score3'],
var_name='test',
value_name='score'
)
# 배열 분해
result = ds.explode('tags')
결합 / 조인
| 메서드 | 설명 |
|---|---|
merge() | SQL 스타일 머지 |
join() | 인덱스를 기준으로 조인 |
concat() | 연결 |
append() | 행 추가 |
combine() | 함수를 사용해 결합 |
combine_first() | 우선순위에 따라 결합 |
update() | 값 업데이트 |
compare() | 차이점 표시 |
# 머지 (join)
result = pd.merge(df1, df2, on='id', how='left')
result = df1.join(df2, on='id')
# 연결
result = pd.concat([df1, df2, df3])
result = pd.concat([df1, df2], axis=1)
이항 연산
| 메서드 | 설명 |
|---|---|
add() / radd() | 덧셈 |
sub() / rsub() | 뺄셈 |
mul() / rmul() | 곱셈 |
div() / rdiv() | 나눗셈 |
truediv() / rtruediv() | 실수 나눗셈 |
floordiv() / rfloordiv() | 내림 나눗셈 |
mod() / rmod() | 나머지 연산 |
pow() / rpow() | 거듭제곱 |
dot() | 행렬 곱셈 |
# 산술 연산
result = ds['col1'].add(ds['col2'])
result = ds['price'].mul(ds['quantity'])
# 누락된 데이터에 fill_value 사용
result = ds['col1'].add(ds['col2'], fill_value=0)
비교 연산
| 메서드 | 설명 |
|---|---|
eq() | 같음 |
ne() | 다름 |
lt() | 미만 |
le() | 이하 |
gt() | 초과 |
ge() | 이상 |
equals() | 동일성 검사 |
compare() | 차이 표시 |
함수 적용
| 메서드 | 설명 |
|---|---|
apply() | 함수 적용 |
applymap() | 요소별 적용 |
map() | 값 매핑 |
agg() / aggregate() | 집계 |
transform() | 변환 |
pipe() | 함수 연결 |
groupby() | 그룹화 |
# 함수 적용
result = ds['name'].apply(lambda x: x.upper())
result = ds.apply(lambda row: row['a'] + row['b'], axis=1)
# 집계
result = ds.agg({'col1': 'sum', 'col2': 'mean'})
result = ds.agg(['sum', 'mean', 'std'])
# 파이프
result = (ds
.pipe(filter_active)
.pipe(calculate_metrics)
.pipe(format_output)
)
시계열
| 메서드 | 설명 |
|---|---|
rolling() | 롤링 윈도우 |
expanding() | 확장 윈도우 |
ewm() | 지수 가중 |
resample() | 시계열 리샘플링 |
shift() | 값 이동 |
asfreq() | 빈도 변환 |
asof() | 특정 시점 기준 최신 값 |
at_time() | 특정 시간 선택 |
between_time() | 시간 범위 선택 |
first() / last() | 첫/마지막 주기 |
to_period() | 주기로 변환 |
to_timestamp() | 타임스탬프로 변환 |
tz_convert() | 시간대 변환 |
tz_localize() | 시간대 지정 |
# 롤링 윈도우
result = ds['value'].rolling(window=7).mean()
# 확장 윈도우
result = ds['value'].expanding().sum()
# 이동
result = ds['value'].shift(1) # 지연(Lag)
result = ds['value'].shift(-1) # 선행(Lead)
결측 데이터
| 메서드 | 설명 |
|---|---|
isna() / isnull() | 결측값 감지 |
notna() / notnull() | 비결측값 감지 |
dropna() | 결측값 삭제 |
fillna() | 결측값 채우기 |
ffill() | 이전 값으로 채우기 |
bfill() | 다음 값으로 채우기 |
interpolate() | 보간 |
replace() | 값 대체 |
I/O 메서드
| 메서드 | 설명 |
|---|---|
to_csv() | CSV로 내보내기 |
to_json() | JSON으로 내보내기 |
to_excel() | Excel로 내보내기 |
to_parquet() | Parquet로 내보내기 |
to_feather() | Feather로 내보내기 |
to_sql() | SQL 데이터베이스로 내보내기 |
to_pickle() | Pickle로 내보내기 |
to_html() | HTML 테이블 |
to_latex() | LaTeX 테이블 |
to_markdown() | Markdown 테이블 |
to_string() | 문자열 표현 |
to_dict() | 딕셔너리 |
to_records() | 레코드 |
to_numpy() | NumPy 배열 |
to_clipboard() | 클립보드로 복사 |
반복
| 메서드 | 설명 |
|---|---|
items() | (컬럼, Series) 쌍을 순회 |
iterrows() | (인덱스, Series) 쌍을 순회 |
itertuples() | 이름이 지정된 Tuple로 순회 |
Pandas와의 주요 차이점
1. 반환 타입
# Pandas는 Series를 반환합니다
pdf['col'] # → pd.Series
# DataStore는 ColumnExpr를 반환합니다 (지연 실행)
ds['col'] # → ColumnExpr
2. 지연 실행
# DataStore 작업은 지연 실행됩니다
result = ds.filter(ds['age'] > 25) # 아직 실행되지 않음
df = result.to_df() # 여기서 실행됨
3. inplace 매개변수 없음
# Pandas
df.drop(columns=['col'], inplace=True)
# DataStore (항상 새 객체를 반환)
ds = ds.drop(columns=['col'])
4. 결과 비교하기
# 비교를 위해 to_pandas()를 사용합니다
pd.testing.assert_frame_equal(
ds.to_pandas(),
expected_df
)