Charts

Table of contents

Overview

Any query result can be visualized as a chart without leaving Pharos. Switch a result to Chart view, map columns to chart roles, and Pharos aggregates and renders the data — either from the rows already loaded in the grid, or by pushing an aggregation query down to PostgreSQL to chart the full dataset. Charts support interactive drill-down back into the grid, customizable series colors, image export, and per-card persistence.

Switching Between Grid and Chart

The results area has a **Grid Chart Plan** control at the front of the result action bar (Plan shows a card’s query plan; see Explain). A new result always opens in Grid view; the chosen view mode is remembered per query card, so you can keep one card’s results as a chart while another stays a grid.

Chart configuration is saved along with your workspace, so a charted result reopens as the same chart after relaunching Pharos. If the cached rows for an older result are no longer stored, the chart shows a prompt to re-run the query — your column mappings are preserved.

Chart Types

Seven chart types are available, each with its own set of column roles:

Chart Type Roles Notes
Bar Category (X), Value (Y), Series (optional) Aggregating; a Series stacks the bars, or groups them side by side (Display ▸ Stacked / Grouped)
Line Category (X), Value (Y), Series (optional) Aggregating
Area Category (X), Value (Y), Series (optional) Aggregating
Pie Category (X), Value (Y) Aggregating; one ring, one slice per category
Scatter X, Y, Size (optional) Plots raw points, no aggregation; a Size column scales each point
Gantt Label, Start, End, Bars by (optional) One lane per Label value; rows in a lane merge into bars (see Gantt Charts)
Heatmap X (columns), Y (rows), Value (color, optional) Aggregating; no Value column means cell counts

Column eligibility is type-aware: Value, Y, and Size roles accept numeric columns; X, Start, and End accept numeric or temporal columns; Category, Series, Label, and Bars by accept any column. A text column whose values all parse as numbers counts as numeric, and one whose values all parse as dates counts as temporal — the pickers, the recommender and the aggregator all read the column the same way.

When you first open Chart view, Pharos applies its top recommendation for the result’s columns (see Suggested Charts). Before, this was always a bar of the first two columns.

Suggested Charts

The rail opens with a Suggest section. Pharos ranks the ways a result can be charted from the shape of its columns — the rules charting tools such as Tableau’s Show Me, Excel’s Recommended Charts and Vega-Lite share:

Columns Recommendation
A time column and a measure Line over time; a second line by a low-cardinality column when one exists
A category (up to 25 values) and a measure Bar, largest first; Pie when there are seven or fewer non-negative slices
Two measures Scatter; a third measure becomes the point Size
Two categories, with or without a measure Heatmap of the measure, or of counts
A label with a start and an end time Gantt
One measure alone Histogram — a bar of counts over automatic bins
One category alone Bar of counts

A measure is a numeric column that is not an identifier (id, order_id, a uuid) and not a boolean. Identifiers are never plotted as values. A measure named like an average — avg_price, rate, pct, score — is averaged rather than summed. Time buckets start on Auto, which picks the unit from the data’s span (see the rail below).

  • Suggest chart applies the top recommendation. When Apple Intelligence is available, the button carries its badge and asks the on-device model instead: the model reads the column names, types and shape statistics, the ranked candidates and the SQL, and answers with a chart, a title, axis titles and one sentence of reason. Its answer is checked against the columns; one that does not fit falls back to the top candidate. Nothing is asked of the model until you press the button.
  • More layouts lists every ranked candidate by name — “Line — revenue over order_date”, “Heatmap — count by region and status” — so a second choice is one click away.
  • The reason for the applied chart is shown under the button. A model-written one sits under the Generated by Apple Intelligence header with its thumbs.

Applying a suggestion keeps your server-aggregation setting and legend switch; it resets per-chart colors, because the series have changed.

Configuration Rail

The rail on the right side of the chart provides all configuration. Drag its left edge to make it wider (160 to 420 points); double-click the edge to go back to 160. Pharos keeps the width for every chart.

  • Suggest — see Suggested Charts.
  • Chart type — picker over the seven types.
  • Role pickers — one per role for the selected type (labels adapt: “Category (X)”, “Value (Y)”, “Series (optional)”, etc.). Choose “—” to unmap a role.
  • Aggregate — Sum, Avg, Count, Min, or Max (aggregating chart types only). Aggregations other than Count require a Value column.
  • Time bucket — shown when the axis column is temporal: None, Auto, Hour, Day, Week, Month, or Year. Auto picks the unit from the span of the data: hours up to 3 days, days up to 120 days, weeks up to 2 years, months up to 12 years, then years — about 12 to 60 buckets. Server aggregation uses the same unit.
  • Bins — shown when the axis column is numeric: Off, Auto, 10, 20, or 50. Auto picks roughly √n buckets (capped at 50), and an Auto axis with 12 or fewer distinct values stays discrete instead of binning.
  • Heatmap per-axis controls — the heatmap gets independent “X bins” / “Y bins” (or “X time bucket” / “Y time bucket”) controls, so each axis can be bucketed separately.
  • Sort — shown for bar, line, area, and pie: order points by Query order (default), Category (X label), or Value (Y total), each ascending or descending.
  • Display — Legend on or off; Stacked or Grouped bars when a Series is mapped; Log scale for the Y axis (available only when every plotted value is above zero); a chart title; and X and Y axis titles. Empty axis-title fields show the titles Pharos derives — the column name on X, “Sum of revenue”, “Average of price” or “Count” on Y — and those are what the chart draws.
  • Gantt controls — Layout, Merge rows (Runs or Span) and Zoom; see Gantt Charts.
  • Colors — shown for bar, line, area, pie, and scatter, and for a gantt with a Bars by column; see Colors below.
  • Server aggregation — see below.

Reading a Chart

  • Both axes are titled. Values of 10,000 and above are written compactly on the Y axis (12K, 3.4M).
  • When a category axis has more than twelve labels, or a label is longer than twelve characters, the labels turn vertical; labels longer than 24 characters are shortened with an ellipsis.
  • Move the pointer over a bar, line or area chart and a callout follows it, naming the category and the value of each series. On a heatmap the callout names the cell. A scatter shows a point’s coordinates when you click it.
  • The legend can be hidden from the Display section. A single unnamed series draws no legend.

Gantt Charts

A gantt draws one lane for each distinct Label value, in the order the rows first show it. The lane labels stay fixed at the left when the plot scrolls.

  • Label and Bars by take one or more columns. Use Add Column under the picker to add one, and choose “—” in a picker to remove that column. The values of all the columns together make the key, joined with “ · “ (for example 203.0.113.5 · 443).
  • Start and End can be the same column. For example, map a scan’s seen column to both, and each bar runs from the first to the last observation.
  • Layout (shown when a Bars by column is mapped) sets where a lane’s bars go:
    • One Line (default) — all of a lane’s bars share its line. Bars that overlap in time cover each other.
    • Line per Value — a bold header line for the lane, then one line for each Bars by value under it, so no bar hides another.
  • Merge rows sets how several rows with the same lane and value become bars. It changes the chart only when a value has more than one row — for example raw scan rows, one per observation. A query that already groups by the lane and Bars by columns has one row per value, and Runs and Span then draw the same bars.
    • Runs (default) — sort the lane by Start, and start a new bar each time the Bars by value changes. A value that comes back later gets a new bar (A, B, A gives three bars). With no Bars by column, each row is one bar.
    • Span — one bar for each Bars by value, from its earliest Start to its latest End (A, B, A gives one long A bar over B). With no Bars by column, each lane is one bar.
  • Colors — each Bars by value has its own color. Click Legend (top-left corner of the chart) to list every value in the Inspector: its color, each Bars by column’s whole value, and how many bars and rows it has. An exported image lists the legend under the chart, up to the Top-N setting (25), then “+N more”. Colors repeat after the palette ends.
  • Titles — a value’s title is the first line of each Bars by column. When two values start with the same line (for example “HTTP/1.1 200 OK”), the title adds the first line where they differ.
  • Hover over a bar to see the lane, each Bars by column’s full value (up to eight lines), Start, End, and the number of rows in the bar.
  • Zoom — on a temporal axis with a Time bucket, each bucket unit is at least 12 points wide. When the range is wider than the pane, the plot scrolls horizontally. When there are more lanes than fit, it scrolls vertically. Use the magnifier buttons to halve or double the zoom, Fit to show the whole range, and Auto Width to go back to the fixed width. A numeric axis or a Time bucket of None always fits.
  • A bar of one instant (Start equals End) is drawn 3 points wide so that you can see it.
  • Selecting more than one lane filters each column on its own. For example, lanes A · 80 and B · 443 together also match A · 443. One lane or one bar filters exactly.

Grid Filters

The chart plots only the rows that the grid’s filters keep: column filters, the tag funnel, and tagged rows shown with “Show tagged rows”. The banner over the chart shows how many rows it plots, for example “Charting 120 of 5,000 loaded rows that match the grid’s filters.” Change a filter in the grid and the chart follows.

  • The chart’s own selection (“Filter in Grid”) does not narrow the chart. The chart dims the marks you did not select. If the selection replaced one of your filters on a column, your filter still applies to the chart.
  • Find (⌘F) in the grid searches; it does not filter the chart.
  • Server aggregation runs on the database, so grid filters do not apply to it.

Top Categories and “Other”

Categorical axes are capped at the top 25 categories by aggregate total; remaining categories are folded into a single Other category, and the chart is marked as truncated. Heatmaps apply the cap per axis (up to 25 × 25 cells). Binned numeric and temporal axes are not capped.

Colors

Series colors come from a global default palette, with an optional per-chart override.

  • Default palette — Settings → Charts holds an ordered list of colors used across all charts: a red-led, accessibility-considered six-color set out of the box. Each slot has a native color well; add or remove slots, or use Reset to defaults to restore the built-in set.
  • Per-chart override — the rail’s Colors section (bar, line, area, pie, and scatter) shows one color well per series, slice, or — for scatter, which plots a single color — per point. Picking a color there overrides the global palette for that chart only; Reset to palette clears the override and reverts to the default palette.
  • Heatmaps use their own value-based color gradient instead of the palette.

Client-Side vs Server-Side Aggregation

By default, charts aggregate the rows currently loaded for the card. If more rows exist than are loaded, an orange banner reports “Charting N of M loaded rows, aggregated client-side” with a Load all rows button that fetches the remaining rows into memory (up to 200,000).

For large datasets, enable Aggregate on server in the rail’s Server aggregation section. Pharos wraps your query in a generated GROUP BY statement and runs it on PostgreSQL, so the chart reflects the full dataset regardless of how many rows are loaded in the grid.

  • Server aggregation requires a single SELECT or WITH query with a category and value mapped. Gantt charts never aggregate server-side.
  • On a tab with its own connection, the aggregation runs on that connection, so it sees the tab’s temporary tables and uncommitted rows, and it never ends or breaks an open transaction.
  • Scatter charts in server mode plot a deterministic sample (up to 5,000 points, chosen by a stable hash so re-runs reproduce the same sample) and are flagged as sampled.
  • While the query runs, a banner shows “Running server aggregation…”; on success it reports “Aggregated server-side over the full dataset, as of <time>” (with “truncated” or “sampled” appended when applicable). Errors appear in the same banner.
  • Changing any mapping or bin setting while server mode is on re-runs the aggregation automatically (debounced).
  • A reopened workspace never re-queries the database silently — the banner offers an explicit Run server aggregation button instead.
  • Copy Generated SQL in the rail copies the generated push-down query to the clipboard, so you can inspect or reuse it.

Drill-Down and Selection

Clicking a chart stages a selection rather than filtering immediately. Selected marks stay lit while everything else dims, and a commit button appears in the action bar.

Gestures

Chart Type Tap Drag
Bar / Line / Area Select a category (series-precise on stacked bars) Horizontal brush across categories
Pie Select a slice —
Heatmap Select a cell Rectangular marquee over cells
Scatter Show a coordinate callout (no drill) Marquee brush over an X/Y range
Gantt Click a bar to select its rows (lane and Bars by value); click beside the bars to select the whole lane Drag across the plot to brush a time window (selects overlapping bars)

Modifier Keys

  • Click — replace the selection
  • ⌘-click — toggle an item in or out of the selection
  • ⇧-click — extend the selection contiguously from the anchor
  • Esc — clear the staged selection

Committing a Selection

The commit button’s label describes exactly what will happen:

  • Filter in Grid — … (client mode) — translates the selection into column filters, switches back to Grid view, and shows a Filtered by chart chip. Clicking the chip removes the chart filter and restores any manual filters it displaced.
  • Query Selected Rows — … (server mode) — adds a new card to the active tab, running your original query wrapped in a WHERE clause matching the selection. The query goes through the normal execution path, so it appears in query history and can be re-run independently.

Exporting Charts

The export button in the action bar offers chart-specific options while Chart view is active:

  • Export Chart as PNG… — retina PNG
  • Export Chart as PDF… — single-page PDF
  • Copy Chart as Image — PNG on the clipboard
  • View / Copy Generated SQL — shown when server aggregation is on

Exports render the chart alone, with no caption or footer. File metadata is limited to generic, non-sensitive fields: PNG exports set Software to “Pharos” and a creation timestamp; PDF exports set Creator to “Pharos”. No connection name, row counts, or SQL are embedded. Gantt exports render every lane and the whole time range, with no scrolling, so no bars are clipped.


Pharos — A native macOS PostgreSQL client built with Swift and Rust.

This site uses Just the Docs, a documentation theme for Jekyll.