Do you have an example of applying `@subdag` to a ...
# hamilton-help
s
Do you have an example of applying
@subdag
to a
Parallelizable
node? I am trying to do the following but and missing some of the plumbing.
Copy code
@config.when(split_by_dimension=True)
@extract_columns(*
    ["portfolio_name"]
    + RAW_COLS_TO_EXTRACT
)
def split_trades_by_dimension(trades: pd.DataFrame) -> Parallelizable[pd.DataFrame]:
    """Group trades by dimension."""
    for dim, cols in DIMENSION_SCHEMES.items():
        grp = (
            trades
            .pipe(_create_portfolio_name_dimensions, columns=cols)
            .groupby(["portfolio_name"] + cols, as_index=False, dropna=False)
        )
        for dimension_df in grp:
            yield dimension_df

@config.when(split_by_dimension=True)
@subdag(prepare_trades_for_request_obj, external_inputs="split_trades_by_dimension")
def trades_by_dimension(portfolios_validated: Collect[pd.DataFrame]) -> pd.DataFrame:: # coming from prepare_trades_for_request_obj
    """Extract dimension data from formatted positions."""
    return pd.concat(portfolios_validated, ignore_index=True)
Copy code
raise base.InvalidDecoratorException(
hamilton.function_modifiers.base.InvalidDecoratorException: Error <function split_trades_by_dimension at 0x000001AD9D53B420> does not output a type we know about. Is it a dataframe type we support?
Am I not allowed to
@extract_columns
of a
Parallelizable
node?
If I move
@extract_columns
to this intermediate node
dimension_df
then I get a new error.
Copy code
@extract_columns(*
    ["portfolio_name"]
    + RAW_COLS_TO_EXTRACT
)
def dimension_df(split_trades_by_dimension: pd.DataFrame) -> pd.DataFrame:
    """Extract dimension data from formatted positions."""
    return split_trades_by_dimension
Error:
Copy code
File "C:\codebase\venv\Lib\site-packages\hamilton\function_modifiers\expanders.py", line 677, in extractor_fn
    if column_to_extract not in df:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\codebase\venv\Lib\site-packages\pandas\core\generic.py", line 1576, in __nonzero__
    raise ValueError(
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Is this a reasonable thing to want to so?
s
what is
RAW_COLS_TO_EXTRACT
?
The last approach should work
> if column_to_extract not in df: in the stack trace this seems to indicate the column_to_extract isn't a string value, but a dataframe?
s
This is the corrected code, But here, where can I apply the
subdag
?
Copy code
@config.when(split_by_dimension=True)
def split_trades_by_dimension(trades: pd.DataFrame) -> Parallelizable[pd.DataFrame]:
    """Group trades by dimension."""
    for dim, cols in DIMENSION_SCHEMES.items():
        grp = (
            trades
            .pipe(_create_portfolio_name_dimensions, columns=cols)
            .groupby(["portfolio_name"] + cols, as_index=False, dropna=False)
        )
        for _, dimension_df in grp:
            yield dimension_df.assign(dimension=dim)

@config.when(split_by_dimension=True)
def dimension_df(split_trades_by_dimension: pd.DataFrame) -> pd.DataFrame:
    """Extract dimension data from formatted positions."""
    return split_trades_by_dimension


@config.when(split_by_dimension=True)
@extract_columns(*
    ["portfolio_name"]
    + RAW_COLS_TO_EXTRACT
)
def trades_by_dimension(dimension_df: Collect[pd.DataFrame]) -> pd.DataFrame:
    """Extract dimension data from formatted positions."""
    return pd.concat(dimension_df, ignore_index=True)
s
It could depend on dimension_df
But I'm not following the need for the extract_columns?
s
extract_columns
is needed for one of the
load_from=*modules
that I would like to apply to each
dimension_df
The challenge is that there is a
subdag
that calls a
dataflow
with
extract_columns
inside it. The
extract_columns
must be encapsulated inside the
dataflow
- so I figured out that much so far. Now the challenge I am having is that there a
pipe
that I want applied outside of the
dataflow
inside of
apply_subdag*
to
extract_columns
.
s
@Seth Stokes the way I would break it down is by working on the "inner" graphs independently, and then wiring them into larger graphs. E.g. work on the graph that you want to "book end" with parallelizeable and collect first independently. Once you have that graph working for an individual case, it should be straightforward to then apply parallelizable and collect. I think you're trying to combine too many decorators at once. Otherwise I am trying to read into what you want, and came up with the following structure from the code above:
Copy code
def split_trades_by_dimension(trades: pd.DataFrame) -> Parallelizable[pd.DataFrame]:
    """bookend start"""
    for dim, cols in DIMENSION_SCHEMES.items():
        grp = (
            trades
            .pipe(_create_portfolio_name_dimensions, columns=cols)
            .groupby(["portfolio_name"] + cols, as_index=False, dropna=False)
        )
        for dimension_df in grp:
            yield dimension_df

@extract_columns(*
    ["portfolio_name"]
    + RAW_COLS_TO_EXTRACT
)
def dimension_df(split_trades_by_dimension: pd.DataFrame) -> pd.DataFrame:  # if this is important you could move this function into the subdag module, or a separate one rather than have it here
    """Extract dimension data from formatted positions."""
    return split_trades_by_dimension


@subdag(prepare_trades_for_request_obj, 
   inputs={
     input_name: source(input_name)
     for input_name in  ["portfolio_name", "dimension_df"] + RAW_COLS_TO_EXTRACT  
   }
)
def trades_by_dimension(portfolios_validated: pd.DataFrame) -> pd.DataFrame:: # coming from prepare_trades_for_request_obj
    """Extract dimension data from formatted positions."""
    return portfolios_validated

def all_trades_by_dimendion(trades_by_dimension: Collect[pd.DataFrame]) -> pd.DataFrame:
    # bookend end
    return pd.concat(portfolios_validated, ignore_index=True)
s
Thanks for this. I think what you did should work. Also, good to hear that iterating and crating a new module to add to the pipeline is the preferred method