diff --git a/benchmarks/single/analyze.cxx b/benchmarks/single/analyze.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c99e15f1e34187a72fb7b685e42a8b9582fdcf07
--- /dev/null
+++ b/benchmarks/single/analyze.cxx
@@ -0,0 +1,38 @@
+#include <iostream>
+#include <string>
+
+#include <ROOT/RDataFrame.hxx>
+
+#include <eicd/ReconstructedParticleData.h>
+
+int analyze(std::string name)
+{
+  // open dataframe
+  ROOT::RDataFrame df("events", url, {"mcparticles2", "GeneratedParticles", "ReconstructedParticles"});
+  // count total events
+  auto count = df.Count();
+  if (count == 0) {
+    std::cout << "Error: No events found" << std::endl;
+    return -1;
+  }
+
+  auto n_tracks = [](const std::vector<eic::ReconstructedParticleData> &p) { return (int) p.size(); };
+
+  auto d = df
+  .Define("n_tracks_gen", n_tracks, {"GeneratedParticles"})
+  .Define("n_tracks_rec", n_tracks, {"ReconstructedParticles"})
+  ;
+
+  auto stats_n_tracks_gen = d.Stats("n_tracks_gen");
+  auto stats_n_tracks_rec = d.Stats("n_tracks_rec");
+  if (stats_n_tracks_gen->GetMean() < 1.0
+   || stats_n_tracks_rec->GetMean()) {
+    std::cout << "Error: too few tracks per events " << std::endl;
+    stats_n_tracks_gen->Print();
+    stats_n_tracks_rec->Print();
+    return -1;
+  }
+
+  // success
+  return 0;
+}
diff --git a/benchmarks/single/single.sh b/benchmarks/single/single.sh
index 3f475abafbc9ed8bdd9879b286aeac2705fa2d89..cb153366bae8694164fbcc5ad90e7089a739ccef 100644
--- a/benchmarks/single/single.sh
+++ b/benchmarks/single/single.sh
@@ -31,7 +31,7 @@ if [[ "$?" -ne "0" ]] ; then
 fi
 
 # Analysis
-root -l -b -q ${JUGGLER_REC_FILE} -e 'events->Scan("@ReconstructedParticles.size()","","",10)'
+root -l -b -q "benchmarks/single/analyze.cxx+(\"${JUGGLER_REC_FILE}\")"
 if [[ "$?" -ne "0" ]] ; then
   echo "ERROR analysis failed"
   exit 1