Buyer’s guide Sourced and dated

What's the best way to explore a local Parquet file visually?

If you want to drag fields around a Parquet or CSV file you already have on disk, with no server and no repo, use xmetric: it opens the file in a browser tab and runs the queries there. Pick Evidence or Observable Framework when the result should be a versioned report in Git, and Superset when a team needs a shared portal on a warehouse.

Updated

Our pick

xmetric

It opens the file in a browser tab with nothing to install or host, the data stays on your machine, and you pivot on shelves with Tableau-style formulas instead of rewriting a GROUP BY for every question.

Pick something else if

The output is a report that lives in Git and deploys on CI (Evidence or Observable Framework), or a team needs a shared, permissioned portal on Postgres, Snowflake or Trino (Superset or Preset).

xmetric makes this page. Every vendor fact below links to the vendor’s own page and the date we checked it.

How they compare

xmetric Our pickEvidenceObservable FrameworkApache Superset / Preset
Cost to startxmetricFree in your browser. No account needed to open a file.Saving a workbook online and sharing a link need a free account.Source · checked October 20EvidenceEvidence Core framework, MIT licence, free to self-host, no account needed.You build and host the static site yourself; docs do not state a $0 price explicitly. Hosted plan has only a 30-day trial.Source · checked October 17Observable FrameworkObservable Framework, free static site generator (ISC licence), free to self-host.Code-first (JavaScript plus Markdown); you build and host the static site yourself.Source · checked October 17Apache Superset / PresetApache Superset is free to self-host under the Apache License (Docker Compose, Kubernetes, PyPI)Source · checked October 17
What you runxmetricAny modern browser on Mac, Windows or Linux. Nothing to install.Source · checked October 17EvidenceCore builds a static site you host anywhere (Vercel, Netlify, Docker, own servers); hosted option is Evidence Studio/Cloud.Source · checked October 17Observable FrameworkBuilds a static site hosted on any static hosting service or your own server; docs walk through GitHub Pages.Source · checked October 17Apache Superset / PresetPreset Cloud (fully managed SaaS); Enterprise adds Managed Private Cloud; also certified Superset for any infrastructureSource · checked October 17
Where the data is processedxmetricIn your browser. Files are not uploaded.Source · checked October 17EvidenceHosted: extracted data cached in Evidence's query engine in a chosen region; direct connectors query the user's warehouse per request.Source · checked October 17Observable FrameworkData loaders run at build time where the build runs (project directory); the browser gets static data snapshots.Source · checked October 17Apache Superset / PresetQueries run against the customer's own database, connected to Preset's hosted cloudSource · checked October 17

Try it: the first look at a new events table

Here is a month of events from a habit app's pipeline. Count rows per user and Android users log 19.7 habits each against 14.3 on iOS, a 38% lead. Then compare rows with distinct event ids: 307 of Android's 995 rows are repeats of an event already stored, from a client that retries uploads. Count distinct events and Android lands at 13.7, just behind iOS. Nothing about engagement changed; the table had duplicates.

Are Android users really more engaged?

Computed by the xmetric engine

The data

Tally habit app, client events, September 2025

Synthetic dataevents.csv

A habit-tracking app with 60 users on iOS, Android and web. Four weeks of events as the ingestion pipeline wrote them to Parquet, retries included.

event_iduser_idplatformevent_dateevent_type
e000040u0002ios2025-09-01session_start
e000083u0003ios2025-09-01session_start
e000084u0003ios2025-09-01habit_logged
e000106u0004ios2025-09-01session_start

Step 1

Habits logged per user, by platform

Android looks well ahead of iOS. Before you tell the mobile team, check what a row is.

Point at a bar to read it.
051015androidiosweb

Tableau

SUM(IF [event_type] = "habit_logged" THEN 1 END) / COUNTD([user_id])

SQL

SELECT
  platform,
  COUNT(DISTINCT user_id) AS users,
  ROUND(COUNT(*) FILTER (WHERE event_type = 'habit_logged') * 1.0 / COUNT(DISTINCT user_id), 1) AS habits_per_user
FROM input
GROUP BY platform
ORDER BY habits_per_user DESC

Rows

PlatformUsersHabits per user
android2619.7
ios2614.3
web85.6

Step 2

Rows versus distinct events

Every iOS and web row is a distinct event. Android has hundreds of repeats: the client retries uploads on a bad connection and the pipeline keeps both copies.

PlatformRowsEventsDuplicate rows
android995688307
ios7367360
web1341340

Tableau

COUNT([event_id]) - COUNTD([event_id])

The xmetric engine ran this formula and got the SQL’s Duplicate rows column, row for row.

SQL

SELECT
  platform,
  COUNT(*) AS ROWS,
  COUNT(DISTINCT event_id) AS events,
  COUNT(*) - COUNT(DISTINCT event_id) AS duplicate_rows
FROM input
GROUP BY platform
ORDER BY duplicate_rows DESC

Rows

PlatformRowsEventsDuplicate rows
android995688307
ios7367360
web1341340

Step 3

Count events, not rows

Counted by distinct event id, Android drops just behind iOS. The engagement gap was a retry bug.

Point at a bar to read it.
0510iosandroidweb

Tableau

COUNTD(IF [event_type] = "habit_logged" THEN [event_id] END) / COUNTD([user_id])

SQL

SELECT
  platform,
  ROUND(COUNT(DISTINCT CASE WHEN event_type = 'habit_logged' THEN event_id END) * 1.0 / COUNT(DISTINCT user_id), 1) AS habits_per_user
FROM input
GROUP BY platform
ORDER BY habits_per_user DESC

Rows

PlatformHabits per user
ios14.3
android13.7
web5.6

That's the case for a visual first pass: one drag of event_id onto the view as a count and a distinct count shows the gap before anyone builds a dashboard on it.

Common mistake

Adding up weekly active users

Each week counts its own distinct users, so a user active in all four weeks is counted four times. Count distinct users over the whole period instead.

Wrong
Point at a bar to read it.
229Active users

Tableau

WINDOW_SUM(COUNTD([user_id]))
// view at week level

SQL

WITH w AS (
  SELECT
    date_trunc('week', event_date) AS week,
    COUNT(DISTINCT user_id) AS users
  FROM input
  GROUP BY week
)
SELECT SUM(users) AS active_users
FROM w
Right
Point at a bar to read it.
60Active users

Tableau

COUNTD([user_id])
// view at month level, or { FIXED : COUNTD([user_id]) }

SQL

SELECT COUNT(DISTINCT user_id) AS active_users
FROM input

Run it on your own file

Drop a CSV export of your own table and the same steps run on it, in this browser. For Parquet, open the file in the xmetric app; nothing is uploaded either way.

Drop a CSV anywhere on this page

Up to 10,000 rows. The sample, events.csv, shows the shape.

When another tool is the better choice

  • If this is you

    A Parquet or CSV file on your laptop and a question to answer today

    xmetric

    Open it in a tab and drag fields; no server, no repo, no upload.

  • If this is you

    A recurring report that should be reviewed in pull requests and deployed by CI

    Evidence

    SQL and Markdown in Git, built to a static site. Its hosted Team plan costs $2,500 / month, unlimited users, monthly billing; Core is free to self-host.

  • If this is you

    A custom data app with hand-built charts in JavaScript

    Observable Framework

    Data loaders in any language precompute snapshots at build time; you write the charts.

  • If this is you

    A team portal on a warehouse with roles and a shared SQL editor

    Apache Superset / Preset

    Free to self-host; Preset hosts it, with Professional at $20/user/month billed annually; $25/user/month billed monthly.

Questions

Does xmetric upload my Parquet file?

No. Parquet, CSV and Excel files are read inside your browser tab. Nothing leaves your machine unless you choose to save the workbook online.

Do I need to convert Parquet to CSV first?

No. The xmetric app opens .parquet files directly. The demo on this page uses a CSV copy of the same table so it loads instantly here.

Can I paste the SQL into the DuckDB CLI?

Yes. Every step shows DuckDB SQL over a table called input. Replace input with read_parquet('events.parquet') and it runs as is.

Why does COUNT give a different answer from COUNTD on event ids?

Because the same event can arrive twice. Pipelines that deliver at least once, and mobile clients that retry uploads, write duplicate rows with the same id. COUNT counts rows; COUNTD counts events.

Sources