跳转到主内容
跳转到主内容

与 pandas 的主要区别

虽然 DataStore 与 pandas 高度兼容,但仍有一些重要区别需要了解。

摘要表

方面pandasDataStore
执行方式Eager(立即执行)Lazy(延迟执行)
返回类型DataFrame/SeriesDataStore/ColumnExpr
行顺序保留保留(自动);在性能模式下不保证
inplace支持不支持
索引完全支持简化实现
内存所有数据驻留内存数据驻留在数据源

1. 惰性执行 vs 急切执行

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)需要行数
.columnsds.columns需要列名
.dtypesds.dtypes需要类型信息
.shapeds.shape需要维度信息
.valuesds.values需要实际数据
.indexds.index需要索引
to_df()ds.to_df()显式转换
Iterationfor 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() 以确保顺序一致)
  • 在使用某些连接类型执行 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 索引
  • 文件数据源:使用简单的整数型索引

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() 进行对比测试
  • 在具有代表性的数据规模下进行测试

快速参考

pandasDataStore
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)相同(会触发执行)