def _calculate_features_for_dataframe(
        self,
        dataframe_name,
        feature_trie,
        df_trie,
        full_dataframe_trie,
        precalculated_trie,
        filter_column,
        filter_values,
        parent_data=None,
        progress_callback=None,
        include_cutoff_time=True,
    ):
        """
        Generate dataframes with features calculated for this node of the trie,
        and all descendant nodes. The dataframes will be stored in df_trie.

        Args:
            dataframe_name (str): The name of the dataframe to calculate features for.

            feature_trie (Trie): the trie with sets of features to calculate.
                The root contains features for the given dataframe.

            df_trie (Trie): a parallel trie for storing dataframes. The
                dataframe with features calculated will be placed in the root.

            full_dataframe_trie (Trie): a trie storing dataframes will all dataframe
                rows, for features that are uses_full_dataframe.

            precalculated_trie (Trie): a parallel trie containing dataframes
                with precalculated features. The dataframe specified by dataframe_name
                will be at the root.

            filter_column (str): The name of the column to filter this
                dataframe by.

            filter_values (pd.Series): The values to filter the filter_column
                to.

            parent_data (tuple[Relationship, list[str], pd.DataFrame]): Data
                related to the parent of this trie. This will only be present if
                the relationship points from this dataframe to the parent dataframe. A
                3 tuple of (parent_relationship,
                ancestor_relationship_columns, parent_df).
                ancestor_relationship_columns is the names of columns which
                link the parent dataframe to its ancestors.

            include_cutoff_time (bool): If True, data at cutoff time are included
                in calculating features.

        """
        # Step 1: Get a dataframe for the given dataframe name, filtered by the given
        # conditions.

        (
            need_full_dataframe,
            full_dataframe_features,
            not_full_dataframe_features,
        ) = feature_trie.value

        all_features = full_dataframe_features | not_full_dataframe_features
        columns = self._necessary_columns(dataframe_name, all_features)

        # If we need the full dataframe then don't filter by filter_values.
        if need_full_dataframe:
            query_column = None
            query_values = None
        else:
            query_column = filter_column
            query_values = filter_values

        df = self.entityset.query_by_values(
            dataframe_name=dataframe_name,
            instance_vals=query_values,
            column_name=query_column,
            columns=columns,
            time_last=self.time_last,
            training_window=self.training_window,
            include_cutoff_time=include_cutoff_time,
        )

        # call to update timer
        progress_callback(0)

        # Step 2: Add columns to the dataframe linking it to all ancestors.
        new_ancestor_relationship_columns = []
        if parent_data:
            parent_relationship, ancestor_relationship_columns, parent_df = parent_data

            if ancestor_relationship_columns:
                (
                    df,
                    new_ancestor_relationship_columns,
                ) = self._add_ancestor_relationship_columns(
                    df,
                    parent_df,
                    ancestor_relationship_columns,
                    parent_relationship,
                )

            # Add the column linking this dataframe to its parent, so that
            # descendants get linked to the parent.
            new_ancestor_relationship_columns.append(
                parent_relationship._child_column_name,
            )

        # call to update timer
        progress_callback(0)

        # Step 3: Recurse on children.

        # Pass filtered values, even if we are using a full df.
        if need_full_dataframe:
            filtered_df = df[df[filter_column].isin(filter_values)]
        else:
            filtered_df = df

        for edge, sub_trie in feature_trie.children():
            is_forward, relationship = edge
            if is_forward:
                sub_dataframe_name = relationship.parent_dataframe.ww.name
                sub_filter_column = relationship._parent_column_name
                sub_filter_values = filtered_df[relationship._child_column_name]
                parent_data = None
            else:
                sub_dataframe_name = relationship.child_dataframe.ww.name
                sub_filter_column = relationship._child_column_name
                sub_filter_values = filtered_df[relationship._parent_column_name]

                parent_data = (relationship, new_ancestor_relationship_columns, df)

            sub_df_trie = df_trie.get_node([edge])
            sub_full_dataframe_trie = full_dataframe_trie.get_node([edge])
            sub_precalc_trie = precalculated_trie.get_node([edge])
            self._calculate_features_for_dataframe(
                dataframe_name=sub_dataframe_name,
                feature_trie=sub_trie,
                df_trie=sub_df_trie,
                full_dataframe_trie=sub_full_dataframe_trie,
                precalculated_trie=sub_precalc_trie,
                filter_column=sub_filter_column,
                filter_values=sub_filter_values,
                parent_data=parent_data,
                progress_callback=progress_callback,
                include_cutoff_time=include_cutoff_time,
            )

        # Step 4: Calculate the features for this dataframe.
        #
        # All dependencies of the features for this dataframe have been calculated
        # by the above recursive calls, and their results stored in df_trie.

        # Add any precalculated features.
        precalculated_features_df = precalculated_trie.value
        if precalculated_features_df is not None:
            # Left outer merge to keep all rows of df.
            df = df.merge(
                precalculated_features_df,
                how="left",
                left_index=True,
                right_index=True,
                suffixes=("", "_precalculated"),
            )

        # call to update timer
        progress_callback(0)

        # First, calculate any features that require the full dataframe. These can
        # be calculated first because all of their dependents are included in
        # full_dataframe_features.
        if need_full_dataframe:
            df = self._calculate_features(
                df,
                full_dataframe_trie,
                full_dataframe_features,
                progress_callback,
            )

            # Store full dataframe
            full_dataframe_trie.value = df

            # Filter df so that features that don't require the full dataframe are
            # only calculated on the necessary instances.
            df = df[df[filter_column].isin(filter_values)]

        # Calculate all features that don't require the full dataframe.
        df = self._calculate_features(
            df,
            df_trie,
            not_full_dataframe_features,
            progress_callback,
        )

        # Step 5: Store the dataframe for this dataframe at the root of df_trie, so
        # that it can be accessed by the caller.
        df_trie.value = df

        Generate dataframes with features calculated for this node of the trie,
        and all descendant nodes. The dataframes will be stored in df_trie.

        Args:
            dataframe_name (str): The name of the dataframe to calculate features for.

            feature_trie (Trie): the trie with sets of features to calculate.
                The root contains features for the given dataframe.

            df_trie (Trie): a parallel trie for storing dataframes. The
                dataframe with features calculated will be placed in the root.

            full_dataframe_trie (Trie): a trie storing dataframes will all dataframe
                rows, for features that are uses_full_dataframe.

            precalculated_trie (Trie): a parallel trie containing dataframes
                with precalculated features. The dataframe specified by dataframe_name
                will be at the root.

            filter_column (str): The name of the column to filter this
                dataframe by.

            filter_values (pd.Series): The values to filter the filter_column
                to.

            parent_data (tuple[Relationship, list[str], pd.DataFrame]): Data
                related to the parent of this trie. This will only be present if
                the relationship points from this dataframe to the parent dataframe. A
                3 tuple of (parent_relationship,
                ancestor_relationship_columns, parent_df).
                ancestor_relationship_columns is the names of columns which
                link the parent dataframe to its ancestors.

            include_cutoff_time (bool): If True, data at cutoff time are included
                in calculating features.

        
featuretools/computational_backends/feature_set_calculator.py