diff --git a/lenskit/topn.py b/lenskit/topn.py
index 5c0dc13..cccd928 100644
--- a/lenskit/topn.py
+++ b/lenskit/topn.py
@@ -148,9 +148,21 @@ class RecListAnalysis:
             ug_cols = [c for c in rec_key if c not in truth_key]
             tcount = truth.groupby(truth_key)['item'].count()
             tcount.name = 'ntruth'
+            _log.debug('truth data:\n%s', tcount)
             if ug_cols:
                 _log.debug('regrouping by %s to fill', ug_cols)
-                res = res.groupby(ug_cols).apply(lambda f: f.join(tcount, how='outer', on=truth_key))
+                _log.debug('pre-group series:\n%s', res)
+
+                rdict = {}
+
+                for key, df in res.groupby(ug_cols):
+                    df2 = df.drop(columns=ug_cols).join(tcount, how='outer', on=truth_key)
+                    rdict[key] = df2
+
+                res = pd.concat(rdict, names=ug_cols)
+                res = res.reset_index()
+                _log.debug('joined result:\n%s', res)
+
             else:
                 _log.debug('no ungroup cols, directly merging to fill')
                 res = res.join(tcount, how='outer', on=truth_key)

diff --git a/tests/test_topn_analysis.py b/tests/test_topn_analysis.py
index 64549ee..cfe2ee7 100644
--- a/tests/test_topn_analysis.py
+++ b/tests/test_topn_analysis.py
@@ -197,6 +197,7 @@ def test_fill_users():
     algo.fit(train)
 
     rec_users = test['user'].sample(50).unique()
+    assert len(rec_users) < 50
     recs = batch.recommend(algo, rec_users, 25)
 
     scores = rla.compute(recs, test, include_missing=True)
@@ -252,6 +253,10 @@ def test_adv_fill_users():
     assert scores['recall'].isna().sum() > 0
     _log.info('scores:\n%s', scores)
 
+    ucounts = scores.reset_index().groupby('algo')['user'].agg(['count', 'nunique'])
+    assert all(ucounts['count'] == 100)
+    assert all(ucounts['nunique'] == 100)
+
     mscores = rla.compute(recs, test)
     mscores = mscores.reset_index().set_index(inames)
     mscores.sort_index(inplace=True)

include_missing fails in a few cases
I have seen include_missing fail for ML20M user-user in the LK demo experiment. Still debugging why.
tests/test_topn_analysis.py::test_adv_fill_users
tests/test_topn_analysis.py::test_split_keys tests/test_topn_analysis.py::test_split_keys_gcol tests/test_topn_analysis.py::test_run_one tests/test_topn_analysis.py::test_run_two tests/test_topn_analysis.py::test_inner_format tests/test_topn_analysis.py::test_spec_group_cols tests/test_topn_analysis.py::test_java_equiv tests/test_topn_analysis.py::test_fill_users tests/test_topn_analysis.py::test_pr_bulk_match[False] tests/test_topn_analysis.py::test_pr_bulk_match[True]