mcpField
A
alterlab-dask

alterlab-dask

github.com/alterlab-ieu数据与数据库7867 次安装5 周前更新
代码仓库

关于 alterlab-dask

alterlab-dask 是面向数据处理与科学计算的 Dask 技能,帮助将 pandas、NumPy 和自定义 Python 工作扩展到内存之外,并利用多核或多机执行。它覆盖惰性集合和动态任务等不同计算模式。

主要功能

  1. 01扩展 pandas 数据帧计算
  2. 02并行处理 NumPy 数组
  3. 03流式处理文本与 JSON
  4. 04构建带依赖的任务工作流
  5. 05选择线程、进程或分布式调度

适用场景

  1. 01处理超出内存容量的数据集
  2. 02并行读取和转换多个文件
  3. 03运行自定义并行计算任务

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/alterlab-ieu/alterlab-academic-skills --skill alterlab-dask

安装后在 Claude Code 中输入/alterlab-dask确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
arrays.md
bags.md
best-practices.md
dataframes.md
futures.md
schedulers.md

详细介绍

Dask

概述

Dask 是一个用于并行和分布式计算的 Python 库,支持三项关键能力:

  • 在单台机器上执行超出内存容量的数据
  • 并行处理,利用多个核心提升计算速度
  • 分布式计算,支持跨多台机器处理 TB 级数据集

Dask 可从笔记本电脑(处理约 100 GiB)扩展到集群(处理约 100 TiB),同时保持熟悉的 Python API。

何时使用此技能

在以下情况下应使用此技能:

  • 处理超出可用 RAM 容量的数据集
  • 将 pandas 或 NumPy 操作扩展到更大的数据集
  • 并行化计算以提升性能
  • 高效处理多个文件(CSV、Parquet、JSON、文本日志)
  • 构建带有任务依赖关系的自定义并行工作流
  • 在多个核心或机器之间分发工作负载
不适用的情况
场景改用
数据可放入 RAM,且目标是获得单机 DataFrame 的最高速度alterlab-polars
在单台机器上进行十亿行的外存探索和大数据绘图,且不使用集群alterlab-vaex
设计分块、压缩的 Zarr 存储和编解码器(而非对其进行计算)alterlab-zarr
版本说明

自 2025.1 起,采用查询规划的 DataFrame(以前称为 dask-expr)是唯一的 Dask DataFrame 实现,并包含在 dask 中;dask.config.set({"dataframe.query-planning": False}) 已不再存在。文本列会以字符串 dtype 加载(在 Dask 中由 PyArrow 支持;在 pandas 3 中为 str),且归约操作不会静默跳过这些列:存在非数值列时,ddf.mean() 会引发 TypeError,而 ddf.groupby(...).mean() 会引发 NotImplementedError,因此请筛选列或传入 numeric_only=True。

核心能力

Dask 提供五个主要组件,分别适用于不同的用例:

1. DataFrames — 并行 Pandas 操作

用途:通过并行处理,将 pandas 操作扩展到更大的数据集。

适用情况:

  • 表格数据超出可用 RAM 容量
  • 需要同时处理多个 CSV/Parquet 文件
  • Pandas 操作速度较慢,需要并行化
  • 从 pandas 原型扩展到生产环境

参考文档:如需全面了解 Dask DataFrames,请参阅 references/dataframes.md,其中包括:

  • 读取数据(单个文件、多个文件、glob 模式)
  • 常见操作(筛选、groupby、连接、聚合)
  • 使用 map_partitions 执行自定义操作
  • 性能优化技巧
  • 常见模式(ETL、时间序列、多文件处理)

快速示例:

import dask.dataframe as dd

# Read multiple files as single DataFrame
ddf = dd.read_csv('data/2024-*.csv')

# Operations are lazy until compute()
filtered = ddf[ddf['value'] > 100]
result = filtered.groupby('category').mean(numeric_only=True).compute()

要点:

  • 操作是惰性的(构建任务图),直到调用 .compute()
  • 使用 map_partitions 执行高效的自定义操作
  • 处理来自其他来源的结构化数据时,尽早转换为 DataFrame
2. Arrays — 并行 NumPy 操作

用途:通过分块算法,将 NumPy 的能力扩展到大于内存容量的数据集。

适用情况:

  • 数组超出可用 RAM 容量
  • 需要并行化 NumPy 操作
  • 处理科学数据集(HDF5、Zarr、NetCDF)
  • 需要并行线性代数或数组操作

参考文档:如需全面了解 Dask Arrays,请参阅 references/arrays.md,其中包括:

  • 创建数组(来自 NumPy、随机数据或磁盘)
  • 分块策略与优化
  • 常见操作(算术、归约、线性代数)
  • 使用 map_blocks 执行自定义操作
  • 与 HDF5、Zarr 和 XArray 集成

快速示例:

import dask.array as da

# Create large array with chunks
x = da.random.random((100000, 100000), chunks=(10000, 10000))

# Operations are lazy
y = x + 100
z = y.mean(axis=0)

# Compute result
result = z.compute()

要点:

  • 分块大小至关重要(目标是每块约 100 MB)
  • 操作会并行处理各个块
  • 必要时重新分块,以提高操作效率
  • 对于 Dask 不支持的操作,使用 map_blocks
3. Bags - 非结构化数据的并行处理

用途:使用函数式操作处理非结构化或半结构化数据(文本、JSON、日志)。

适用场景:

  • 处理文本文件、日志或 JSON 记录
  • 在结构化分析前进行数据清理和 ETL
  • 处理不适合数组或数据框格式的 Python 对象
  • 需要节省内存的流式处理

参考文档:如需全面了解 Dask Bags,请参阅 references/bags.md,其中包括:

  • 读取文本和 JSON 文件
  • 函数式操作(map、filter、fold、groupby)
  • 转换为数据框
  • 常见模式(日志分析、JSON 处理、文本处理)
  • 性能注意事项

快速示例:

import dask.bag as db
import json

# Read and parse JSON files
bag = db.read_text('logs/*.json').map(json.loads)

# Filter and transform
valid = bag.filter(lambda x: x['status'] == 'valid')
processed = valid.map(lambda x: {'id': x['id'], 'value': x['value']})

# Convert to DataFrame for analysis
ddf = processed.to_dataframe()

要点:

  • 用于初步数据清理,然后转换为 DataFrame/Array
  • 使用 foldby 代替 groupby,以获得更好的性能
  • 操作采用流式处理,且节省内存
  • 复杂操作应转换为结构化格式(DataFrame)
4. Futures - 基于任务的并行化

用途:通过精细控制任务执行和依赖关系,构建自定义并行工作流。

适用场景:

  • 构建动态且不断演进的工作流
  • 需要立即执行任务(而非延迟执行)
  • 计算依赖于运行时条件
  • 实现自定义并行算法
  • 需要有状态计算

参考文档:如需全面了解 Dask Futures,请参阅 references/futures.md,其中包括:

  • 设置分布式客户端
  • 提交任务并使用 futures
  • 任务依赖关系和数据传输
  • 高级协调(队列、锁、事件、actor)
  • 常见模式(参数扫描、动态任务、迭代算法)

快速示例:

from dask.distributed import Client

client = Client()  # Create local cluster

# Submit tasks (executes immediately)
def process(x):
    return x ** 2

futures = client.map(process, range(100))

# Gather results
results = client.gather(futures)

client.close()

要点:

  • 需要分布式客户端(即使仅在单台机器上运行)
  • 提交任务后立即执行
  • 预先分发大型数据,避免重复传输
  • 每个任务约有 1ms 开销(不适合数百万个微小任务)
  • 使用 actor 实现有状态工作流
5. Schedulers - 执行后端

用途:控制 Dask 任务的执行方式和位置(线程、进程、分布式)。

如何选择调度器:

  • Threads(默认):适用于 NumPy/Pandas 操作、释放 GIL 的库,以及可从共享内存中受益的场景
  • Processes:适用于纯 Python 代码、文本处理和受 GIL 限制的操作
  • Synchronous:适用于使用 pdb 调试、性能分析和理解错误
  • Distributed:适用于需要仪表板、多机器集群或高级功能的场景

参考文档:如需全面了解 Dask Schedulers,请参阅 references/schedulers.md,其中包括:

  • 调度器的详细说明和特性
  • 配置方法(全局、上下文管理器、每次计算)
  • 性能注意事项和开销
  • 常见模式和故障排查
  • 用于获得最佳性能的线程配置

快速示例:

import dask
import dask.dataframe as dd

# Use threads for DataFrame (default, good for numeric)
ddf = dd.read_csv('data.csv')
result1 = ddf.mean(numeric_only=True).compute()  # Uses threads

# Use processes for Python-heavy work
import dask.bag as db
bag = db.read_text('logs/*.txt')
result2 = bag.map(python_function).compute(scheduler='processes')

# Use synchronous for debugging
dask.config.set(scheduler='synchronous')
result3 = problematic_computation.compute()  # Can use pdb

# Use distributed for monitoring and scaling
from dask.distributed import Client
client = Client()
result4 = computation.compute()  # Uses distributed with dashboard

要点:

  • Threads:开销最低(每个任务约 10 µs),最适合数值计算
  • Processes:避免 GIL(每个任务约 10 ms),最适合 Python 任务
  • Distributed:提供监控仪表板(每个任务约 1 ms),可扩展至集群
  • 可以针对单次计算或全局切换调度器

最佳实践

如需全面了解性能优化、内存管理策略和常见陷阱,请参阅 references/best-practices.md。主要原则包括:

从更简单的解决方案入手

使用 Dask 前,请考虑:

  • 更好的算法
  • 高效的文件格式(Parquet 优于 CSV)
  • 编译代码(Numba、Cython)
  • 数据采样
关键性能规则

1. 不要先在本地加载数据,再交给 Dask

# Wrong: Loads all data in memory first
import pandas as pd
df = pd.read_csv('large.csv')
ddf = dd.from_pandas(df, npartitions=10)

# Correct: Let Dask handle loading
import dask.dataframe as dd
ddf = dd.read_csv('large.csv')

2. 避免重复调用 compute()

# Wrong: Each compute is separate
for item in items:
    result = dask_computation(item).compute()

# Correct: Single compute for all
computations = [dask_computation(item) for item in items]
results = dask.compute(*computations)

3. 不要构建过大的任务图

  • 如果任务数达到数百万,增大分块大小
  • 使用 map_partitions/map_blocks 合并操作
  • 检查任务图大小:len(ddf.__dask_graph__())

4. 选择合适的分块大小

  • 目标:每个分块约 100 MB(或每个核心在工作节点内存中容纳 10 个分块)
  • 过大:内存溢出
  • 过小:调度开销过大

5. 使用仪表板

from dask.distributed import Client
client = Client()
print(client.dashboard_link)  # Monitor performance, identify bottlenecks

常见工作流模式

ETL 管道
import dask.dataframe as dd

# Extract: Read data
ddf = dd.read_csv('raw_data/*.csv')

# Transform: Clean and process
ddf = ddf[ddf['status'] == 'valid']
ddf['amount'] = ddf['amount'].astype('float64')
ddf = ddf.dropna(subset=['important_col'])

# Load: Aggregate and save. Named aggregation keeps flat string column names;
# a dict-of-lists .agg() creates MultiIndex columns that Parquet rejects.
summary = ddf.groupby('category').agg(
    amount_sum=('amount', 'sum'),
    amount_mean=('amount', 'mean'),
)
summary.to_parquet('output/summary.parquet')
非结构化到结构化数据管道
import dask.bag as db
import json

# Start with Bag for unstructured data
bag = db.read_text('logs/*.json').map(json.loads)
bag = bag.filter(lambda x: x['status'] == 'valid')

# Convert to DataFrame for structured analysis
ddf = bag.to_dataframe()
result = ddf.groupby('category').mean(numeric_only=True).compute()
大规模数组计算
import dask.array as da

# Load or create large array
x = da.from_zarr('large_dataset.zarr')

# Process in chunks
normalized = (x - x.mean()) / x.std()

# Save result
da.to_zarr(normalized, 'normalized.zarr')
自定义并行工作流
from dask.distributed import Client

client = Client()

# Scatter large dataset once
data = client.scatter(large_dataset)

# Process in parallel with dependencies
futures = []
for param in parameters:
    future = client.submit(process, data, param)
    futures.append(future)

# Gather results
results = client.gather(futures)

选择合适的组件

使用此决策指南选择合适的 Dask 组件:

数据类型:

  • 表格数据 → DataFrames
  • 数值数组 → Arrays
  • 文本/JSON/日志 → Bags(然后转换为 DataFrame)
  • 自定义 Python 对象 → Bags 或 Futures

操作类型:

  • 标准 pandas 操作 → DataFrames
  • 标准 NumPy 操作 → Arrays
  • 自定义并行任务 → Futures
  • 文本处理/ETL → Bags

控制级别:

  • 高级、自动化 → DataFrames/Arrays
  • 低级、手动 → Futures

工作流类型:

  • 静态计算图 → DataFrames/Arrays/Bags
  • 动态、不断演进 → Futures

集成注意事项

文件格式
  • 高效:Parquet、HDF5、Zarr(列式、压缩、适合并行处理)
  • 兼容但较慢:CSV(仅用于初始导入)
  • 适用于数组:HDF5、Zarr、NetCDF
集合之间的转换
# Bag → DataFrame
ddf = bag.to_dataframe()

# DataFrame → Array (for numeric data)
arr = ddf.to_dask_array(lengths=True)

# Array → DataFrame
ddf = dd.from_dask_array(arr, columns=['col1', 'col2'])
与其他库集成
  • XArray:为 Dask 数组封装带标签的维度(地理空间、成像)
  • Dask-ML:使用兼容 scikit-learn 的 API 进行机器学习
  • Distributed:高级集群管理和监控

调试与开发

迭代式开发工作流
  1. 使用同步调度器在小型数据上测试:
dask.config.set(scheduler='synchronous')
result = computation.compute()  # Can use pdb, easy debugging
  1. 使用线程在样本数据上验证:
sample = ddf.head(1000)  # Small sample
# Test logic, then scale to full dataset
  1. 使用 distributed 扩展规模并进行监控:
from dask.distributed import Client
client = Client()
print(client.dashboard_link)  # Monitor performance
result = computation.compute()
常见问题

内存错误:

  • 减小分块大小
  • 有策略地使用 persist(),完成后删除数据
  • 检查自定义函数是否存在内存泄漏

启动缓慢:

  • 任务图过大(增大分块大小)
  • 使用 map_partitions 或 map_blocks 减少任务数

并行化效果不佳:

  • 分块过大(增加分区数)
  • 使用线程执行 Python 代码(改用进程)
  • 数据依赖阻碍并行处理

参考文件

如需详细信息,可按需阅读以下参考文档:

  • references/dataframes.md - 完整的 Dask DataFrame 指南
  • references/arrays.md - 完整的 Dask Array 指南
  • references/bags.md - 完整的 Dask Bag 指南
  • references/futures.md - 完整的 Dask Futures 和分布式计算指南
  • references/schedulers.md - 完整的调度器选择与配置指南
  • references/best-practices.md - 全面的性能优化与故障排除指南

当用户需要了解此处快速指南未涵盖的特定 Dask 组件、操作或模式的详细信息时,请加载这些文件。

评论

支持 Markdown