> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Pandas 实用手册

> 常见 pandas 用法及其对应的 DataStore 写法

常见的 pandas 用法及其对应的 DataStore 写法。大多数代码都可直接复用，无需修改！

## 数据加载

### 读取 CSV

```python theme={null}
# Pandas
import pandas as pd
df = pd.read_csv("data.csv")

# DataStore - 相同！
from chdb import datastore as pd
df = pd.read_csv("data.csv")
```

### 读取多个文件

```python theme={null}
# Pandas
import glob
dfs = [pd.read_csv(f) for f in glob.glob("data/*.csv")]
df = pd.concat(dfs)

# DataStore - 使用 glob pattern 效率更高
df = pd.read_csv("data/*.csv")
```

***

## 筛选

### 单一条件

```python theme={null}
# Pandas 和 DataStore - 相同
df[df['age'] > 25]
df[df['city'] == 'NYC']
df[df['name'].str.contains('John')]
```

### 多个条件

```python theme={null}
# 与
df[(df['age'] > 25) & (df['city'] == 'NYC')]

# 或
df[(df['age'] < 18) | (df['age'] > 65)]

# 非
df[~(df['status'] == 'inactive')]
```

### query() 的用法

```python theme={null}
# Pandas 和 DataStore - 相同
df.query('age > 25 and city == "NYC"')
df.query('salary > 50000')
```

### isin()

```python theme={null}
# Pandas 和 DataStore - 相同
df[df['city'].isin(['NYC', 'LA', 'SF'])]
```

### between()

```python theme={null}
# Pandas 和 DataStore - 写法相同
df[df['age'].between(18, 65)]
```

***

## 选择列

### 单列

```python theme={null}
# Pandas 和 DataStore - 相同
df['name']
df.name  # 属性访问
```

### 多列

```python theme={null}
# Pandas 和 DataStore - 相同
df[['name', 'age', 'city']]
```

### 选择和过滤

```python theme={null}
# Pandas 和 DataStore - 相同
df[df['age'] > 25][['name', 'salary']]

# DataStore 也支持 SQL 风格
df.filter(df['age'] > 25).select('name', 'salary')
```

***

## 排序

### 单列

```python theme={null}
# Pandas 和 DataStore - 相同
df.sort_values('salary')
df.sort_values('salary', ascending=False)
```

### 多列

```python theme={null}
# Pandas 和 DataStore - 相同
df.sort_values(['city', 'salary'], ascending=[True, False])
```

### 获取排名前/后 N 的项

```python theme={null}
# Pandas 和 DataStore - 相同
df.nlargest(10, 'salary')
df.nsmallest(5, 'age')
```

***

## GroupBy 与聚合

### 简单的 GroupBy

```python theme={null}
# Pandas 和 DataStore - 完全相同
df.groupby('city')['salary'].mean()
df.groupby('city')['salary'].sum()
df.groupby('city').size()  # 计数
```

### 多重聚合

```python theme={null}
# Pandas 和 DataStore - 相同
df.groupby('city')['salary'].agg(['sum', 'mean', 'count'])

df.groupby('city').agg({
    'salary': ['sum', 'mean'],
    'age': ['min', 'max']
})
```

### 命名聚合

```python theme={null}
# Pandas 和 DataStore - 完全相同
df.groupby('city').agg(
    total_salary=('salary', 'sum'),
    avg_salary=('salary', 'mean'),
    employee_count=('id', 'count')
)
```

### 多个 GroupBy 键

```python theme={null}
# Pandas 和 DataStore - 相同
df.groupby(['city', 'department'])['salary'].mean()
```

***

## 连接数据

### 内连接

```python theme={null}
# Pandas
pd.merge(df1, df2, on='id')

# DataStore - 相同的 API
pd.merge(df1, df2, on='id')

# DataStore 同样支持
df1.join(df2, on='id')
```

### 左连接

```python theme={null}
# Pandas 和 DataStore - 相同
pd.merge(df1, df2, on='id', how='left')
```

### 基于不同列进行 连接

```python theme={null}
# Pandas 和 DataStore - 完全相同
pd.merge(df1, df2, left_on='emp_id', right_on='id')
```

### 拼接

```python theme={null}
# Pandas 和 DataStore - 完全相同
pd.concat([df1, df2, df3])
pd.concat([df1, df2], axis=1)
```

***

## 字符串操作

### 大小写转换

```python theme={null}
# Pandas 和 DataStore - 相同
df['name'].str.upper()
df['name'].str.lower()
df['name'].str.title()
```

### 子串

```python theme={null}
# Pandas 和 DataStore - 相同
df['name'].str[:3]        # 前 3 个字符
df['name'].str.slice(0, 3)
```

### 搜索

```python theme={null}
# Pandas 和 DataStore - 相同
df['name'].str.contains('John')
df['name'].str.startswith('A')
df['name'].str.endswith('son')
```

### 替换

```python theme={null}
# Pandas 和 DataStore - 相同
df['text'].str.replace('old', 'new')
df['text'].str.replace(r'\d+', '', regex=True)  # 删除数字
```

### 拆分

```python theme={null}
# Pandas 和 DataStore - 相同
df['name'].str.split(' ')
df['name'].str.split(' ', expand=True)
```

### 时长

```python theme={null}
# Pandas 和 DataStore - 相同
df['name'].str.len()
```

***

## DateTime 运算

### 提取各部分

```python theme={null}
# Pandas 和 DataStore - 相同
df['date'].dt.year
df['date'].dt.month
df['date'].dt.day
df['date'].dt.dayofweek
df['date'].dt.hour
```

### 格式化

```python theme={null}
# Pandas 和 DataStore - 相同
df['date'].dt.strftime('%Y-%m-%d')
```

***

## 数据缺失

### 查看缺失值

```python theme={null}
# Pandas 和 DataStore - 相同
df['col'].isna()
df['col'].notna()
df.isna().sum()
```

### 删除缺失值

```python theme={null}
# Pandas 和 DataStore - 相同
df.dropna()
df.dropna(subset=['col1', 'col2'])
```

### 填充缺失值

```python theme={null}
# Pandas 和 DataStore - 相同
df.fillna(0)
df.fillna({'col1': 0, 'col2': 'Unknown'})
df.fillna(method='ffill')
```

***

## 创建新列

### 简单赋值

```python theme={null}
# Pandas 和 DataStore - 完全相同
df['total'] = df['price'] * df['quantity']
df['age_group'] = df['age'] // 10 * 10
```

### 使用 assign()

```python theme={null}
# Pandas 和 DataStore - 完全相同
df = df.assign(
    total=df['price'] * df['quantity'],
    is_adult=df['age'] >= 18
)
```

### 条件筛选 (where/mask)

```python theme={null}
# Pandas 和 DataStore - 完全相同
df['status'] = df['age'].where(df['age'] >= 18, 'minor')
```

### 用于自定义逻辑的 apply()

```python theme={null}
# 有效，但会触发 pandas 执行
df['category'] = df['amount'].apply(lambda x: 'high' if x > 1000 else 'low')

# DataStore 替代方案（保持惰性执行）
df['category'] = (
    df.when(df['amount'] > 1000, 'high')
      .otherwise('low')
)
```

***

## 数据重塑

### 透视表

```python theme={null}
# Pandas 和 DataStore - 完全相同
df.pivot_table(
    values='amount',
    index='region',
    columns='product',
    aggfunc='sum'
)
```

### 宽转长 (Unpivot)

```python theme={null}
# Pandas 和 DataStore - 完全一致
df.melt(
    id_vars=['name'],
    value_vars=['score1', 'score2', 'score3'],
    var_name='test',
    value_name='score'
)
```

### 展开

```python theme={null}
# Pandas 和 DataStore - 完全一致
df.explode('tags')  # 展开数组列
```

***

## 窗口函数

### 滚动窗口

```python theme={null}
# Pandas 和 DataStore - 完全相同
df['rolling_avg'] = df['price'].rolling(window=7).mean()
df['rolling_sum'] = df['amount'].rolling(window=30).sum()
```

### 展开

```python theme={null}
# Pandas 与 DataStore：完全一致
df['cumsum'] = df['amount'].expanding().sum()
df['cummax'] = df['amount'].expanding().max()
```

### 移位

```python theme={null}
# Pandas 和 DataStore - 完全相同
df['prev_value'] = df['value'].shift(1)   # 滞后
df['next_value'] = df['value'].shift(-1)  # 超前
```

### 差异

```python theme={null}
# Pandas 和 DataStore - 完全相同
df['change'] = df['value'].diff()
df['pct_change'] = df['value'].pct_change()
```

***

## 输出

### 输出为 CSV

```python theme={null}
# Pandas 和 DataStore - 完全相同
df.to_csv("output.csv", index=False)
```

### 转为 Parquet

```python theme={null}
# Pandas 和 DataStore - 完全一致
df.to_parquet("output.parquet")
```

### 转为 pandas DataFrame

```python theme={null}
# DataStore 专用
pandas_df = ds.to_df()
pandas_df = ds.to_pandas()
```

***

## DataStore 附加功能

### 查看 SQL

```python theme={null}
# 仅适用于 DataStore
print(ds.to_sql())
```

### 执行计划

```python theme={null}
# 仅适用于 DataStore
ds.explain()
```

### ClickHouse 函数

```python theme={null}
# 仅限 DataStore：额外访问器
df['domain'] = df['url'].url.domain()
df['json_value'] = df['data'].json.get_string('key')
df['ip_valid'] = df['ip'].ip.is_ipv4_string()
```

### 通用 URI

```python theme={null}
# 仅限 DataStore - 可从任意位置读取
ds = DataStore.uri("s3://bucket/data.parquet")
ds = DataStore.uri("mysql://user:pass@host/db/table")
```
