Skip to content

Replaces original da-barplot with ANCOMBC2-visualizer#166

Merged
colinvwood merged 17 commits into
qiime2:devfrom
Macabe222:ancomb-refactor
Jul 7, 2026
Merged

Replaces original da-barplot with ANCOMBC2-visualizer#166
colinvwood merged 17 commits into
qiime2:devfrom
Macabe222:ancomb-refactor

Conversation

@Macabe222

@Macabe222 Macabe222 commented May 18, 2026

Copy link
Copy Markdown
Contributor

Description

Adds transformer of type DataLoafPackageDirFmt to type ANCOMBC2OutputDirFmt allowing ancombc2_visualizer to accept outputs from ancombc. Renames ancombc2_visualizer to da_barplot and deletes original da_barplot.

Addresses #156

AI Disclosure

  • NO AI USED.
  • AI USED.

AI Usage Details

@lizgehret

Copy link
Copy Markdown
Member

Hey @Macabe222, let us know when this one needs a review!

@lizgehret lizgehret moved this from Needs Triage to Awaiting Info in QIIME 2 - Triage 🚑 May 19, 2026
@Macabe222

Copy link
Copy Markdown
Contributor Author

@lizgehret this one should be good now

@Macabe222 Macabe222 changed the title Adds transformer from DataLoafPackageDirFmt to ANCOMBC2OutputDirFmt Replaces original da-barplot with ANCOMBC2-visualizer May 19, 2026

@lizgehret lizgehret left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Macabe222, this all generally lgtm; just some test coverage changes mentioned below!

Comment thread q2_composition/tests/test_ancombc2.py Outdated
self.assertEqual(len(variable_with_spaces_columns), 2)

def test_ancombc2_visualizer(self):
def test_da_barplot(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth adding some additional tests to show that both formats can be used for this visualizer. I also think the tests that were removed with the deletion of test_diff_abundance_plot should be moved/refactored into here so that we aren't just losing all of that test coverage for the visualizer that's now supporting double the inputs/outputs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other drive-by thought - maybe all of the shared tests for this visualizer should get moved into test_diff_abundance_plot and then the only tests in this file are for the ancombc2 method.

@github-project-automation github-project-automation Bot moved this to Backlog in 2026.7 🐐 May 20, 2026
@lizgehret lizgehret moved this from Backlog to In Development in 2026.7 🐐 May 20, 2026
@lizgehret

lizgehret commented May 20, 2026

Copy link
Copy Markdown
Member

I'm looking back through our release announcements for 2026.1 and 2026.4 and it doesn't look like we added a 'breaking changes' announcement regarding the removal of the ancombc2 visualizer... maybe good to discuss in this week's eng meeting on whether we should merge this for 2026.7 or hold off until 2026.10 (even though this would be a nice one to get squared away).

EDIT: discussed in eng meeting (21 April 2026); this was in our breaking changes for 2025.10

@lizgehret

Copy link
Copy Markdown
Member

Hey @Macabe222 just a heads up, I've implemented a new PR template that's now connected to the lint action in CI for all of our distro plugins. I've retroactively added the template to this PR (since you opened it prior). You'll just need to select the appropriate AI disclosure checkbox & fill in details on that if AI was used. lmk if you have any questions!

@Oddant1

Oddant1 commented Jun 17, 2026

Copy link
Copy Markdown
Member

The failure here is a bit strange. Haven't fully dug through the framework to figure out why exactly this is working in this way, but

Here we call ancombc2 as a raw function

cls.abc2_output = ancombc2(
    table=cls.biom_table,
    metadata=cls.ancombc2_metadata,
    fixed_effects_formula='body-site + year',
    group='body-site',
    structural_zeros=True
)

ancombc2 returns a value from transform here

return transform(data=slices, to_type=ANCOMBC2OutputDirFmt)

This is the transformer it is invoking.

@plugin.register_transformer
def _2(slices: ANCOMBC2SliceMapping) -> ANCOMBC2OutputDirFmt:
    '''
    Transforms a dataframe of ANCOMBC2 model statistics into the ANCOMBC2
    output directory format.
    '''
    format = ANCOMBC2OutputDirFmt()
    for slice_name, slice_df in slices.items():
        format_slice = format.__getattribute__(slice_name)
        format_slice.write_data(slice_df, pd.DataFrame)

    extra_files = ['diff', 'passed_ss']
    if not any(file in slices.keys() for file in extra_files):
        header = {
            "doctype": {
                "name": "table.jsonl",
                "format": "application/x-json-lines",
                "version": "1.0"
            },
            "direction": "row",
            "style": "key:value",
            "fields": [
                {"name": "taxon", "type": "string", "missing": False},
                {"name": "(Intercept)", "type": "number", "missing": False}
            ],
            "index": [],
            "title": "",
            "description": "",
            "extra": {}
        }

        for file in extra_files:
            output_path = os.path.join(format.path, file + '.jsonl')
            with open(output_path, 'w') as f:
                f.write(json.dumps(header, separators=(',', ':')) + "\n")

    return format

We run the following two tests in the following order (some other tests also run and aren't relevant to the failure):

First, test_da_barplot_action_ancombc2 which loads our abc2_out as an Artifact then calls the da_barplot Visualizer.

def test_da_barplot_action_ancombc2(self):
    pm = PluginManager()
    da_barplot = pm.plugins['composition'].actions['da_barplot']

    ancombc2_artifact = Artifact.import_data(
        type='FeatureData[ANCOMBC2Output]', view=self.abc2_output
    )

    viz = da_barplot(ancombc2_artifact)
    viz = viz.visualization

    with tempfile.TemporaryDirectory() as tempdir:
        viz.export_data(tempdir)
        assert os.path.exists(os.path.join(tempdir, 'index.html'))

Second, test_da_barplot_non_overlapping_taxonomy, which uses abc2_out, the Format that was returned from ancombc2 via the transformer and not the Artifact.

def test_da_barplot_non_overlapping_taxonomy(self):
    '''
    Tests that an error is raised when attempting to visualize ancombc2
    data with a taxonomy that contains none of the features present in the
    ancombc2 data.
    '''
    taxonomy_df = pd.DataFrame({
        'Feature ID': ['feat1', 'feat2'],
        'Taxon': ['taxon1', 'taxon2'],
        'Confidence': [0.9, 0.95]
    })

    with tempfile.TemporaryDirectory() as tempdir:
        with self.assertRaisesRegex(
            ValueError, 'No features remained in your taxonomy'
        ):
            da_barplot(tempdir, self.abc2_output, taxonomy_df)

    with tempfile.TemporaryDirectory() as tempdir:
        with self.assertRaisesRegex(
            ValueError, 'No features remained in your taxonomy'
        ):
            da_barplot(tempdir, self.abc_output, taxonomy_df)

I'm not sure I fully understand what's happening yet, but due to the invocation of the transformer at the end of ancombc2, the Format we created previously is deleted from the disk when we load it as an Artifact. I'm not positive why this doesn't happen when I return a Format from ancombc2 without invoking the transformer. Something to do with how the views are coerced.

@lizgehret @ebolyen thoughts? Do we want to ditch invoking the transformer at the end of ancombc2? I can look deeper through the framework and try to refactor it such that invoking the transformer doesn't cause this issue. I've looked at it a bit and there are of course parts in the transformer where we create different views and different objects. We are only loading the original path as read-only but something about the lifecycle of the view created is causing it to go away when loaded as an Artifact. That part I haven't really dug into yet.

@Oddant1 Oddant1 requested a review from colinvwood July 6, 2026 20:27
@Oddant1 Oddant1 assigned colinvwood and unassigned Oddant1 Jul 6, 2026
Comment thread q2_composition/_ancombc2.py Outdated
Comment thread q2_composition/tests/test_ancombc2.py
@colinvwood colinvwood merged commit 14af658 into qiime2:dev Jul 7, 2026
5 checks passed
@github-project-automation github-project-automation Bot moved this from In Development to Changelog Needed in 2026.7 🐐 Jul 7, 2026
@colinvwood colinvwood removed this from 2026.7 🐐 Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants