Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

与 pandas 的关键区别

尽管 DataStore 与 pandas 高度兼容,但两者之间仍有一些重要差异需要了解。

摘要表

Aspect pandas DataStore
执行 立即 惰性
返回类型 DataFrame/Series DataStore/ColumnExpr
行顺序 保持不变 保持不变 (自动) ;在性能模式下不作保证
inplace 支持 不支持
索引 完整支持 简化
内存 所有数据都在内存中 数据保留在源端

1. 惰性与立即执行

pandas (立即)

操作会立即执行:

import pandas as pd

df = pd.read_csv("data.csv")  # Loads entire file NOW
result = df[df['age'] > 25]   # Filters NOW
grouped = result.groupby('city')['salary'].mean()  # Aggregates NOW

DataStore (惰性)

仅在需要结果时才会执行操作:

from chdb import datastore as pd

ds = pd.read_csv("data.csv")  # Just records the source
result = ds[ds['age'] > 25]   # Just records the filter
grouped = result.groupby('city')['salary'].mean()  # Just records

# Execution happens here:
print(grouped)        # Executes when displaying
df = grouped.to_df()  # Or when converting to pandas

为什么这很重要

惰性执行可带来:

  • 查询优化:多个操作会编译成一条 SQL 查询
  • 列裁剪:仅读取所需的列
  • 过滤器下推:过滤器会在源端生效
  • 内存效率:避免加载不需要的数据

2. 返回类型

pandas

df['col']           # Returns pd.Series
df[['a', 'b']]      # Returns pd.DataFrame
df[df['x'] > 10]    # Returns pd.DataFrame
df.groupby('x')     # Returns DataFrameGroupBy

DataStore

ds['col']           # Returns ColumnExpr (lazy)
ds[['a', 'b']]      # Returns DataStore (lazy)
ds[ds['x'] > 10]    # Returns DataStore (lazy)
ds.groupby('x')     # Returns LazyGroupBy

转换为 pandas 数据类型

# Get pandas DataFrame
df = ds.to_df()
df = ds.to_pandas()

# Get pandas Series from column
series = ds['col'].to_pandas()

# Or trigger execution
print(ds)  # Automatically converts for display

3. 执行触发器

DataStore 会在需要实际值时执行:

触发器 示例 说明
print() / repr() print(ds) 显示时需要数据
len() len(ds) 需要行数
.columns ds.columns 需要列名
.dtypes ds.dtypes 需要类型信息
.shape ds.shape 需要维度信息
.values ds.values 需要实际数据
.index ds.index 需要索引
to_df() ds.to_df() 显式转换
Iteration for row in ds 需要进行迭代
equals() ds.equals(other) 需要比较

保持惰性执行的操作

操作 返回值
filter() DataStore
select() DataStore
sort() DataStore
groupby() LazyGroupBy
join() DataStore
ds['col'] ColumnExpr
ds[['a', 'b']] DataStore
ds[condition] DataStore

4. 行的顺序

pandas

始终保留行顺序:

df = pd.read_csv("data.csv")
print(df.head())  # Always same order as file

DataStore

对于大多数操作,会自动保留行顺序:

ds = pd.read_csv("data.csv")
print(ds.head())  # Matches file order

# Filter preserves order
ds_filtered = ds[ds['age'] > 25]  # Same order as pandas

DataStore 会在内部自动记录原始行的位置 (使用 rowNumberInAllBlocks()) ,以确保顺序与 pandas 保持一致。

何时会保留行顺序

  • 文件源 (CSV、Parquet、JSON 等)
  • pandas DataFrame 源
  • 过滤操作
  • 列选择
  • 显式调用 sort()sort_values() 之后
  • 会确定顺序的操作 (nlargest()nsmallest()head()tail())

顺序何时可能发生变化

  • groupby() 聚合之后 (使用 sort_values() 可确保顺序一致)
  • 使用某些 join type 进行 merge() / join() 之后
  • 性能模式 (config.use_performance_mode()) 下:任何操作的行顺序都无法保证。请参见性能模式

5. 没有 inplace 参数

pandas

df.drop(columns=['col'], inplace=True)  # Modifies df
df.fillna(0, inplace=True)              # Modifies df
df.rename(columns={'old': 'new'}, inplace=True)

DataStore

不支持 inplace=True。请始终将结果赋值给变量:

ds = ds.drop(columns=['col'])           # Returns new DataStore
ds = ds.fillna(0)                       # Returns new DataStore
ds = ds.rename(columns={'old': 'new'})  # Returns new DataStore

为什么没有 inplace?

DataStore 使用不可变操作,以便实现:

  • 查询构建 (惰性求值)
  • 线程安全
  • 更容易调试
  • 更简洁的代码

6. 索引支持

pandas

完整的索引支持:

df = df.set_index('id')
df.loc['user123']           # Label-based access
df.loc['a':'z']             # Label-based slicing
df.reset_index()
df.index.name = 'user_id'

DataStore

简化了对索引的支持:

# Basic operations work
ds.loc[0:10]               # Integer position
ds.iloc[0:10]              # Same as loc for DataStore

# For pandas-style index operations, convert first
df = ds.to_df()
df = df.set_index('id')
df.loc['user123']

DataStore 的数据来源很重要

  • DataFrame 来源:保留 pandas 索引
  • File 来源:使用简单的整数索引

7. 比较行为

与 pandas 对比

pandas 无法识别 DataStore 对象:

import pandas as pd

from chdb import datastore as ds

pdf = pd.DataFrame({'a': [1, 2, 3]})
dsf = ds.DataFrame({'a': [1, 2, 3]})

# This doesn't work as expected
pdf == dsf  # pandas doesn't know DataStore

# Solution: convert DataStore to pandas
pdf.equals(dsf.to_pandas())  # True

使用 equals()

# DataStore.equals() also works
dsf.equals(pdf)  # Compares with pandas DataFrame

8. 类型推断

pandas

使用 numpy/pandas 数据类型:

df['col'].dtype  # int64, float64, object, datetime64, etc.

DataStore

可使用 ClickHouse 类型:

ds['col'].dtype  # Int64, Float64, String, DateTime, etc.

# Types are converted when going to pandas
df = ds.to_df()
df['col'].dtype  # Now pandas type

显式类型转换

# Force specific type
ds['col'] = ds['col'].astype('int64')

9. 内存模型

pandas

所有数据都存储在内存中:

df = pd.read_csv("huge.csv")  # 10GB in memory!

DataStore

数据会一直保留在源端,直到需要时才使用:

ds = pd.read_csv("huge.csv")  # Just metadata
ds = ds.filter(ds['year'] == 2024)  # Still just metadata

# Only filtered result is loaded
df = ds.to_df()  # Maybe only 1GB now

10. 错误信息

不同的错误来源

  • pandas 错误:来自 pandas 库
  • DataStore 错误:来自 chDB 或 ClickHouse
# May see ClickHouse-style errors
# "Code: 62. DB::Exception: Syntax error..."

调试技巧

# View the SQL to debug
print(ds.to_sql())

# See execution plan
ds.explain()

# Enable debug logging
from chdb.datastore.config import config
config.enable_debug()

迁移清单

从 pandas 迁移时:

  • 修改 import 语句
  • 移除 inplace=True 参数
  • 在需要 pandas DataFrame 的地方显式添加 to_df()
  • 如果行顺序很重要,请添加排序
  • 使用 to_pandas() 进行对比测试
  • 使用具有代表性的数据规模进行测试

快速参考

pandas DataStore
df[condition] 相同 (返回 DataStore)
df.groupby() 相同 (返回 LazyGroupBy)
df.drop(inplace=True) ds = ds.drop()
df.equals(other) ds.to_pandas().equals(other)
df.loc['label'] ds.to_df().loc['label']
print(df) 相同 (会触发执行)
len(df) 相同 (会触发执行)
Navigation