# Visualize Operator

## Overview

{% hint style="info" %}
The `visualize` operator is in open beta starting with Panther version 1.110, and is available to all customers. Please take special note of the [Limitations](#limitations) listed below, and share any bug reports and feature requests with your Panther support team.
{% endhint %}

Use the `visualize` operator to generate a bar, line, pie chart, or table of your query results.

```kusto
| visualize <bar|line|pie|table> <annotation>=<expression>[, ...] 
```

You can use `visualize` with any results set that has two fields where at least one one field is numeric. It's particularly useful to use `visualize` with [`summarize`](https://docs.panther.com/pantherflow/operators/summarize) and aggregations like [`agg.count()`](https://docs.panther.com/functions/aggregation#agg.count). The `visualize` operator must be used last in a PantherFlow query, and does not filter or transform data.

{% hint style="info" %}
After you generate a visualization, you can add it to a [custom dashboard](https://docs.panther.com/search/visualization-and-dashboards/custom).
{% endhint %}

By default, `visualize` displays a vertical bar chart, but you can use `line`, `bar`, `pie`, or `table` to set the type. Further customize the visualization using the [supported annotations](#supported-annotations).

### Limitations

* For charts (`line`, `bar`, and `pie` visualizations), the wrong column may be selected when declaring a `series=` but not an `xcolumn=`, or vice versa (`xcolumn=` but not a `series`)
* To move a vertical bar chart horizontally (left/right), you must scroll vertically (up/down)
* A maximum of 999 data points can be visualized
* Data is displayed in sorted order. For timeseries data, you must sort the data by time if you want it to appear in time order in the graph
* In bar charts, columns are ordered alphabetically and cannot be rearranged

## Supported visualization types

* `bar` (default): Categorical data is represented using rectangular bars.
* `line`: Commonly used to show trends over time, data points are connected by straight lines.
  * To create a `line` chart, the field represented on the x-axis must have a date/time data type.
* `pie`: Displays data as slices of a circular chart, useful for showing proportions and parts of a whole.
  * Use this chart type for categorical data to visualize the relative size or percentage distribution across categories.
* `table`: Data is represented in a tabular format.
  * While this is similar to the results table already displayed in [Search](https://docs.panther.com/search/search-tool), using `table` may be useful if you'd like to add tabular data to a [custom dashboard](https://docs.panther.com/search/visualization-and-dashboards/custom).

## Supported annotations

Use annotations with `visualize` to customize the resulting visualization. Separate multiple annotations with a comma, for example:

```kusto
| visualize bar legend=left, orientation=horizontal, title="My Chart"
```

{% hint style="info" %}
In order to use any of the annotations below, your query must also explicitly set a visualization type (`bar`, `line`, or `pie`).
{% endhint %}

<table><thead><tr><th width="133.1796875">Annotation</th><th width="288.7109375">Description</th><th width="201.9119873046875">Supported visualization types</th><th width="176.8359375">Supported values</th><th width="143.1015625">Example</th></tr></thead><tbody><tr><td><code>title</code></td><td>The title of the chart. If not provided, the default is <code>&#x3C;name of x-axis field> vs &#x3C;name of y-axis field></code>. If value contains spaces, it must be enclosed in quotation marks.</td><td><code>bar</code>, <code>line</code>, <code>pie</code>, <code>table</code></td><td><code>&#x3C;String></code></td><td><code>title="My Chart"</code></td></tr><tr><td><code>orientation</code></td><td>The direction of the chart. If <code>xcolumn</code>, <code>ycolumn</code>, or <code>series</code> are set, they may take precedence over <code>orientation</code>.</td><td><code>bar</code></td><td><code>vertical</code> (default)<br><code>horizontal</code></td><td><code>orientation=horizontal</code></td></tr><tr><td><code>legend</code></td><td><p>The existence and position of a chart legend.<br><br>The default value is <code>hidden</code> for single-series data and <code>bottom</code> for multi-series data.</p><p>To hide the legend, use <code>hidden</code>.</p></td><td><code>bar</code>, <code>line</code>, <code>pie</code></td><td><code>hidden</code> (default for single-series data)<br><code>visible</code> (defaults to <code>bottom</code>)<br><code>top</code><br><code>bottom</code> (default for multi-series data)<br><code>left</code><br><code>right</code></td><td><code>legend=right</code></td></tr><tr><td><code>xcolumn</code></td><td>Name of field that should be represented on the x-axis. (This is not the label of the x-axis.)</td><td><code>bar</code>, <code>line</code></td><td><code>&#x3C;String></code></td><td><code>xcolumn=mean</code></td></tr><tr><td><code>ycolumn</code></td><td>Name of field that should be represented on the y-axis. (This is not the label of the y-axis.)</td><td><code>bar</code>, <code>line</code></td><td><code>&#x3C;String></code></td><td><code>ycolumn=detectionId</code></td></tr><tr><td><code>series</code></td><td><p>Name of field that should be used to group data.<br><br>A chart can be:</p><ul><li>Single-series: Represented by a single line for <code>line</code> charts and bars made up of a single color for <code>bar</code> charts</li><li>Multi-series: Represented by multiple lines, each with a different color, for <code>line</code> charts, and bars made up of multiple colors for <code>bar</code> charts</li></ul><p>If <code>series</code> is not provided, PantherFlow makes an assumption on which field should be treated as the <code>series</code> value.</p></td><td><code>bar</code>, <code>line</code></td><td><code>&#x3C;String></code></td><td><code>series=email</code></td></tr></tbody></table>

## Examples

{% hint style="info" %}
Example data

```kusto
let my_table = datatable [
  { "actionName": "SIGN_IN", "events": 12 },
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 2 },
  { "actionName": "CREATE_USER", "events": 4 },
  { "actionName": "CREATE_RULE", "events": 10 }
];
```

{% endhint %}

### Default bar chart

```kusto
my_table
| visualize
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-91651dde32ef254c20c511ea121c8d72115728ad%2FScreenshot%202024-11-08%20at%2012.41.01%E2%80%AFPM.png?alt=media" alt="A table with the title &#x22;actionName vs events&#x22; is shown, with four vertical columns."><figcaption></figcaption></figure>

### Bar chart with `legend`, `orientation`, and `title` set

```kusto
my_table
| visualize bar legend=left, orientation=horizontal, title="My Chart"
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-bfc149d3f22f1483783ea68587349f25c177c4c9%2FScreenshot%202024-11-08%20at%2012.42.40%E2%80%AFPM.png?alt=media" alt="A table titled &#x22;My Chart&#x22; with four horizontal columns are shown."><figcaption></figcaption></figure>

### Bar chart with `agg.count()`

This example uses sample data that has more than two fields (like most real-world data sets). The query uses `summarize events = agg.count() by actionName` to generate a results set with two fields (`actionName` and `events`) before `visualize` is used. Learn more about aggregations on [PantherFlow Functions](https://docs.panther.com/functions#aggregations) and `summarize` on [Summarize Operator](https://docs.panther.com/pantherflow/operators/summarize).

This query has unique example data:

{% hint style="info" %}
Example data

```kusto
let panther_audit = datatable [
  { "actionName": "SIGN_IN", "id": "222ef2375b1bf394f687ea842065", "p_event_time": time.parse_timestamp("2024-11-14 00:00:00") },
  { "actionName": "CREATE_ALERT_DESTINATION", "id": "320a3b11b854c5ceebb2d08420d99d09", "p_event_time": time.parse_timestamp("2024-10-16 00:00:00") },
  { "actionName": "CREATE_ALERT_DESTINATION", "id": "86a6c88c5b39ede087d3e98420e022", "p_event_time": time.parse_timestamp("2024-11-04 00:00:00") },
  { "actionName": "CREATE_USER", "id": "5a0cbd047f5bf380c281d68420bec709", "p_event_time": time.parse_timestamp("2024-11-12 00:00:00") },
  { "actionName": "CREATE_USER", "id": "4b0d0f487a2ae459c3976f8420d1a810", "p_event_time": time.parse_timestamp("2024-11-11 00:00:00") },
  { "actionName": "CREATE_USER", "id": "7c1e1a59f6bcf4b6d5b83f8420baf911", "p_event_time": time.parse_timestamp("2024-10-27 00:00:00") },
  { "actionName": "CREATE_USER", "id": "9d2f2b60a7cdb58ff6a96f8420fa0b12", "p_event_time": time.parse_timestamp("2024-11-09 00:00:00") },
  { "actionName": "CREATE_RULE", "id": "1e3f3c7189eec6c1e8ca7f8420e3c213", "p_event_time": time.parse_timestamp("2024-11-13 00:00:00") },
  { "actionName": "CREATE_RULE", "id": "2f404d82aa1fe7d3f9d58f8420e5d314", "p_event_time": time.parse_timestamp("2024-10-31 00:00:00") },
  { "actionName": "CREATE_RULE", "id": "603f5e93bb20f8e509e79f8420e7e415", "p_event_time": time.parse_timestamp("2024-11-03 00:00:00") }
];
```

{% endhint %}

```kusto
panther_audit
| summarize events = agg.count() by actionName
| sort actionName desc
| limit 4
| visualize
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-380c4d2cba962d5b359c474ed60834546bd4d873%2FScreenshot%202024-11-08%20at%2012.46.42%E2%80%AFPM.png?alt=media" alt="A table titled &#x22;actionName vs events&#x22; is shown with four vertical columns."><figcaption></figcaption></figure>

### Line chart with `title` set

This query has unique example data:

{% hint style="info" %}
Example data

```kusto
let my_table = datatable [
  { "p_event_time": time.parse_timestamp("2024-08-14 00:00:00"), "events": 120 },
  { "p_event_time": time.parse_timestamp("2024-08-13 00:00:00"), "events": 20 },
  { "p_event_time": time.parse_timestamp("2024-08-12 00:00:00"), "events": 150 },
  { "p_event_time": time.parse_timestamp("2024-08-11 00:00:00"), "events": 200 },
  { "p_event_time": time.parse_timestamp("2024-08-10 00:00:00"), "events": 50 },
  { "p_event_time": time.parse_timestamp("2024-08-09 00:00:00"), "events": 80 },
  { "p_event_time": time.parse_timestamp("2024-08-08 00:00:00"), "events": 10 }
];
```

{% endhint %}

```kusto
my_table
| visualize line title="Last week's events"
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-98ae322d2a1f4a39d9ec348127e409813c698f13%2FScreenshot%202024-11-08%20at%2012.48.28%E2%80%AFPM.png?alt=media" alt="A line chart titled &#x22;Last week&#x27;s events&#x22; is shown, with seven data points."><figcaption></figcaption></figure>

### Line chart measuring action count by hour

The query below displays the [Panther Audit logs](https://docs.panther.com/data-onboarding/supported-logs/panther-audit-logs) action count per hour for the past two days, bucketing the data by hour. A separate chart line is added for each `actionName`.

{% hint style="info" %}
Unlike the other example queries on this page, which use a `datatable` to provide mock data, the query below pulls from live data in your `panther_logs` database.
{% endhint %}

```kusto
panther_logs.public.panther_audit
| where p_event_time >= time.ago(2d)
| summarize count = agg.count() by actionName, hour = time.trunc('hour', p_event_time)
| sort hour asc
| visualize line legend=bottom, title='Action count by hour'
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-56a7ccc721b579d17ceb74ae729a59afce5a52a9%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

### Line chart measuring the number of alerts by detectionId per day

The query below displays the number of alerts by `detectionId` per day for the past two weeks.

{% hint style="info" %}
Unlike the other example queries on this page, which use a `datatable` to provide mock data, the query below pulls from live data in your `panther_signals` database.
{% endhint %}

```kusto
panther_signals.public.signal_alerts
| where p_event_time >= time.ago(14d)
| summarize count = agg.count() by detectionId, hour = time.trunc('day', p_event_time)
| sort hour asc
| visualize line title="Alert count by detectionId per day"
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-f7cd5b52dca525bf2ac78af455381c218efcee71%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

### Bar chart measuring mean time to detection

The query below displays the mean time between when an event is ingested and when a detection is triggered, for the 15 slowest rules by `detectionId`.

{% hint style="info" %}
Unlike the other example queries on this page, which use a `datatable` to provide mock data, the query below pulls from live data in your `panther_signals` database.
{% endhint %}

```kusto
panther_signals.public.signal_alerts
| where p_event_time >= time.ago(7d) and ingestTimeToDetectionSeconds != null
| summarize mean = agg.avg(ingestTimeToDetectionSeconds) by detectionId
| sort mean desc
| limit 15
| visualize bar xcolumn=mean, ycolumn=detectionId, legend=right, orientation=horizontal, title='Mean Time to Detect'
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-e9dfdf149b2e999631c8b511ae8954a7acb4f5ad%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

### Bar chart using `series`

This query has unique example data:

{% hint style="info" %}
Example data:

```kusto
let my_table = datatable [  
  { "actionName": "SIGN_IN", "events": 12 , "user": "alice"},
  { "actionName": "SIGN_IN", "events": 7 , "user": "bob"},
  { "actionName": "SIGN_IN", "events": 4 , "user": "charlie"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 2, "user": "alice"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 3, "user": "chris"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 5, "user": "emily"},
  { "actionName": "CREATE_USER", "events": 1, "user": "alice" },
  { "actionName": "CREATE_USER", "events": 4 , "user": "frankie"},
  { "actionName": "CREATE_USER", "events": 3 , "user": "ross"},
  { "actionName": "CREATE_RULE", "events": 12, "user": "betsy" },
  { "actionName": "CREATE_RULE", "events": 6, "user": "bob" },
  { "actionName": "CREATE_RULE", "events": 6, "user": "casey" }
];
```

{% endhint %}

```kusto
my_table
| visualize bar series=user
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-1ed621cdf285688cb874229b9450b5e6ecbcc835%2FScreenshot%202025-01-06%20at%205.41.49%E2%80%AFPM.png?alt=media" alt="Under an actionName vs events header is a bar chart. There are four vertical columns, and each column in separated into three colors."><figcaption></figcaption></figure>

### Bar chart displaying logins by city, country

{% hint style="info" %}
Unlike the other example queries on this page, which use a `datatable` to provide mock data, the query below pulls from live data in your `okta_systemlog` database.
{% endhint %}

```kusto
panther_logs.public.okta_systemlog
| where eventType == 'user.session.start' and p_event_time > time.ago(90d)
| extend country = client.geographicalContext.country, city = client.geographicalContext.city
| summarize events = agg.count() by country, city
| project country, events, city
| sort events desc
| limit 10
| visualize bar orientation=horizontal,title='Logins by city, country'
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-762c7b32956ce14d5c5729f256fd72f4ef2f54d3%2FScreenshot%202025-03-07%20at%2011.31.12%E2%80%AFAM.png?alt=media" alt="A bar char is shown, titled &#x22;Logins by city, country.&#x22; There are four bars, labeled Atlanta, Maplewood, Athens, and Thessaloniki."><figcaption></figcaption></figure>

### Pie chart visualization with proportional data

This example displays the distribution of different action types as proportional data:

{% hint style="info" %}
Example data

```kusto
let my_table = datatable [
  { "actionName": "SIGN_IN", "events": 12 },
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 2 },
  { "actionName": "CREATE_USER", "events": 4 },
  { "actionName": "CREATE_RULE", "events": 10 }
];
```

{% endhint %}

```kusto
my_table
| visualize pie title="Action Distribution"
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2FT2D68pDu9dE4kZkxZZiY%2FScreenshot%202026-02-25%20at%2010.30.28%E2%80%AFAM.png?alt=media&#x26;token=6a5be008-039e-4f56-9bca-b3cb78220639" alt="" width="563"><figcaption></figcaption></figure>

### Pie chart with aggregated data

The query below displays the distribution of Panther audit actions by type for the past week:

```kusto
panther_logs.public.panther_audit
| where p_event_time >= time.ago(7d)
| summarize events = agg.count() by actionName
| sort events desc
| limit 8
| visualize pie title="Action Types Distribution (Last 7 Days)"
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2FVIfmbdIHmtbUG3VmO9Ro%2FScreenshot%202026-02-25%20at%209.37.05%E2%80%AFAM.png?alt=media&#x26;token=1b22ec71-2b04-47d2-b5d9-88b47e97daa8" alt="" width="563"><figcaption></figcaption></figure>

### Table visualization with `title` set

This query has unique example data:

{% hint style="info" %}
Example data:

```kusto
let my_table = datatable [  
  { "actionName": "SIGN_IN", "events": 12 , "user": "alice"},
  { "actionName": "SIGN_IN", "events": 7 , "user": "bob"},
  { "actionName": "SIGN_IN", "events": 4 , "user": "charlie"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 2, "user": "alice"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 3, "user": "chris"},
  { "actionName": "CREATE_ALERT_DESTINATION", "events": 5, "user": "emily"},
  { "actionName": "CREATE_USER", "events": 1, "user": "alice" },
  { "actionName": "CREATE_USER", "events": 4 , "user": "frankie"},
  { "actionName": "CREATE_USER", "events": 3 , "user": "ross"},
  { "actionName": "CREATE_RULE", "events": 12, "user": "betsy" },
  { "actionName": "CREATE_RULE", "events": 6, "user": "bob" },
  { "actionName": "CREATE_RULE", "events": 6, "user": "casey" }
];
```

{% endhint %}

```kusto
my_table
| visualize table title='Actions by user'
```

<figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-34f6abdf0e9d5786d3ba7901d84788cadb011189%2Fimage.png?alt=media" alt="Under an &#x22;Actions by user&#x22; title is a table with three columns, labeled actionName, events, and user."><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.panther.com/pantherflow/operators/visualize.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
