
本教程旨在解决dagster中利用config进行用户自定义参数配置,并实现资产(asset)间数据正确传递的常见问题。文章将详细阐述如何通过函数参数注入上游资产输出,并结合类型注解确保数据流转的准确性,从而避免配置错误,构建高效且可配置的数据管道。
Dagster是一个用于构建、测试和监控数据管道的Python框架。在Dagster中,核心概念是“资产”(Assets),它们代表了数据管道中的逻辑单元或数据实体。每个资产都应该能够独立地生成或转换数据。
在构建Dagster管道时,开发者常遇到的一个挑战是如何在资产之间正确地传递数据,同时又能利用用户自定义的配置参数。原始问题中遇到的 DagsterInvalidConfigError 错误,以及无法将上游资产的结果传递给下游资产,是这种挑战的典型体现。
错误情境复现: 当尝试在一个资产中直接调用另一个资产函数来获取其输出时,或未正确声明资产间的依赖关系时,就容易出现问题。例如,在 filter_data 资产中直接调用 generate_dataset(),以及在 filter_again 资产中直接调用 filter_data(),这并非Dagster推荐的数据流转方式。
# 错误示例片段
import pandas as pd
from dagster import asset, Config
# ... (generate_dataset 函数定义省略,与正确代码相同)
class fruit_config(Config):
fruit_select: str
@asset(deps=[generate_dataset]) # 这里的deps虽然声明了,但内部调用方式是错误的
def filter_data(config: fruit_config):
# 错误:直接调用generate_dataset()会重新执行,且无法获取其物化结果
df = generate_dataset()
df2 = df[df['fruit'] == config.fruit_select]
return df2
@asset(deps=[filter_data]) # 这里的deps虽然声明了,但内部调用方式是错误的
def filter_again():
# 错误:直接调用filter_data()同样会导致问题
df2 = filter_data()
df3 = df2[df2['units'] > 5]
return df3这种方式会导致以下问题:
Dagster设计了一套优雅的机制来处理资产间的数据流转和配置注入。核心思想是利用Python的函数参数来声明依赖和接收数据。
在Dagster中,一个资产的输出可以作为另一个资产的输入。实现这一点的标准方式是将上游资产的名称作为参数添加到下游资产的函数签名中。Dagster运行时会自动将上游资产的物化结果注入到这些参数中。
例如,如果 generate_dataset 资产产生一个 pd.DataFrame,并且 filter_data 资产需要这个DataFrame作为输入,那么 filter_data 的函数签名应包含一个名为 generate_dataset 的参数。
为了增强代码的可读性、可维护性以及帮助Dagster更好地理解数据类型,强烈建议为资产函数的输入和输出添加类型注解。这不仅让代码意图更清晰,也有助于在开发阶段捕获潜在的类型不匹配错误。
对于需要用户自定义参数的资产,可以通过在函数签名中添加一个类型为 Config 子类的参数来注入配置。Dagster会在执行时提示用户提供这些配置。
正确的数据流转和配置注入原则:
下面是根据上述原则修正后的Dagster资产定义,它展示了如何正确地传递数据和注入配置。
import pandas as pd
import random
from datetime import datetime, timedelta
from dagster import asset, Config, materialize
# 1. 定义生成原始数据的资产
@asset
def generate_dataset() -> pd.DataFrame:
"""
生成一个包含水果、单位和日期的随机数据集。
"""
def random_dates(start_date, end_date, n=10):
date_range = end_date - start_date
random_dates_list = [start_date + timedelta(days=random.randint(0, date_range.days)) for _ in range(n)]
return random_dates_list
random.seed(42) # 确保结果可复现
num_rows = 100
fruits = ['Apple', 'Banana', 'Orange', 'Grapes', 'Kiwi']
fruit_column = [random.choice(fruits) for _ in range(num_rows)]
units_column = [random.randint(1, 10) for _ in range(num_rows)]
start_date = datetime(2022, 1, 1)
end_date = datetime(2022, 12, 31)
date_column = random_dates(start_date, end_date, num_rows)
df = pd.DataFrame({
'fruit': fruit_column,
'units': units_column,
'date': date_column
})
print("Generated Dataset Head:\n", df.head())
return df
# 2. 定义用户配置类
class fruit_config(Config):
"""
用于指定要筛选的水果类型。
"""
fruit_select: str
# 3. 定义筛选数据的资产,接收上游数据和配置
@asset
def filter_data(generate_dataset: pd.DataFrame, config: fruit_config) -> pd.DataFrame:
"""
根据用户配置的水果类型筛选数据集。
参数:
generate_dataset (pd.DataFrame): 来自上游 generate_dataset 资产的原始数据。
config (fruit_config): 包含用户选择水果的配置对象。
"""
# generate_dataset 参数会自动接收上游资产 generate_dataset 的输出
filtered_df = generate_dataset[generate_dataset['fruit'] == config.fruit_select]
print(f"Filtered Data (fruit_select='{config.fruit_select}') Head:\n", filtered_df.head())
return filtered_df
# 4. 定义再次筛选的资产,接收上游筛选后的数据
@asset
def filter_again(filter_data: pd.DataFrame) -> pd.DataFrame:
"""
在已筛选的数据集上,进一步筛选单位数量大于5的记录。
参数:
filter_data (pd.DataFrame): 来自上游 filter_data 资产的已筛选数据。
"""
# filter_data 参数会自动接收上游资产 filter_data 的输出
final_df = filter_data[filter_data['units'] > 5]
print("Final Filtered Data (units > 5) Head:\n", final_df.head())
return final_df
# 如果需要在本地直接运行测试,可以使用 materialize
if __name__ == "__main__":
# 示例:如何通过代码提供配置并物化资产
# 注意:在Dagster UI中运行,配置将通过UI界面输入
result = materialize(
[generate_dataset, filter_data, filter_again],
run_config={
"ops": { # 对于资产,配置是在 "ops" 键下,尽管现在推荐使用 "assets" 键,但旧版本或某些情况下仍兼容
"filter_data": {
"config": {
"fruit_select": "Banana"
}
}
}
}
)
assert result.success
print("\nPipeline execution successful!")以上就是Dagster资产间数据流转与用户自定义参数的正确姿势的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号