Charts and tables

A notebook cell that ends in a chart draws it. Both languages build the same chart: Python with the flint module, C# with Plot. A query becomes the data in one call, dataframe() in Python and ToRows() in C#. This page shows both side by side, one chart type at a time. The exact signatures are in the reference for Python and C#.

From a query to a chart

df = dataframe(q().start_at("Ticket"), "Status", "Priority")
counts = df.groupby("Status").size().reset_index(name="Tickets")
flint.bar(counts, x="Status", y="Tickets", title="Tickets by status")

Each chart takes the data first, then the field for each axis. A chart is not drawn until it is the last expression of a cell, so a cell can build one, adjust it, and end with it.

How it works

A chart is a Flint spec: the data, what each field means, the chart type and which field is on which channel. Flint works out scales, axes, number formats and layout, and the theme sets the look. The notebook draws the spec with the same chart component the chat uses for ```flint fences, so a spec a model wrote for a chat reply draws in a notebook unchanged.

  • Both languages produce the output type application/vnd.flint+json.
  • The Flint library, about 1.8 MB, is fetched the first time a notebook draws a chart in a browser session.
  • A spec that cannot be drawn shows Flint's reason in place of the chart, for example when a field is not in the data.
  • A chart carries at most 50,000 rows. Aggregate first; a chart of more points than that is not readable anyway.

Data

A chart takes rows: one object per record, with a value per field.

Language Accepted data
Python A pandas DataFrame, a list of dicts, or a dict of lists (one list per column).
C# Any sequence of objects: anonymous types, records, dictionaries, or the output of ToRows(). Each row is serialized to JSON, so public properties become fields.

Dates, ids and enum values travel as text. Numbers stay numbers, which is what decides whether a field is plotted as a measure or as a category; name a field's meaning with a semantic type when the values alone are not enough.

Chart types

Each convenience function builds one chart type. Any of the 39 types Flint draws is available through the general function, which takes the type's name.

sales = [
{"Product": "Notebooks", "Sales": 1840, "Cost": 900},
{"Product": "Pens", "Sales": 2650, "Cost": 1200},
{"Product": "Ink", "Sales": 730, "Cost": 400},
]

flint.bar(sales, x="Product", y="Sales", title="Sales per product")
flint.line(sales, x="Product", y=["Sales", "Cost"], title="Sales and cost")
flint.scatter(sales, x="Cost", y="Sales")
flint.pie(sales, category="Product", value="Sales")
flint.histogram(sales, x="Sales")
flint.heatmap(grid, x="Day", y="Hour", color="Visits")

Only the last expression of a cell is drawn, so these are separate cells.

Python C# Chart Channels
flint.bar Plot.Bar Bar Chart x, y
flint.stacked_bar Plot.StackedBar Stacked Bar Chart x, y, color
flint.line Plot.Line Line Chart x, y (several y fields draw a series each)
flint.area Plot.Area Area Chart x, y
flint.scatter Plot.Scatter Scatter Plot x, y
flint.pie Plot.Pie Pie Chart color (category), size (value)
flint.donut Plot.Donut Donut Chart color (category), size (value)
flint.histogram Plot.Histogram Histogram x
flint.heatmap Plot.Heatmap Heatmap x, y, color
flint.chart(type, data, …) Plot.Of(type, rows) Any of the 39 Any channel

Any chart type

The general function takes the type's name, matched without regard to case, and the channels as keywords in Python or through Encode in C#:

flint.chart("Boxplot", measurements, x="Machine", y="Temperature")
flint.chart("Candlestick Chart", prices, x="Day", open="Open", high="High", low="Low", close="Close")

An unknown type fails with the list of valid names. The 39 types are:

Group Types
Points Scatter Plot, Marginal Plot, Regression, Connected Scatter Plot, Ranged Dot Plot, Strip Plot
Bars Bar Chart, Grouped Bar Chart, Stacked Bar Chart, Lollipop Chart, Waterfall Chart, Gantt Chart, Bullet Chart
Distributions Histogram, Density Plot, Ridgeline Plot, ECDF Plot, Violin Plot, Boxplot, Pyramid Chart, Candlestick Chart
Lines and areas Line Chart, Sparkline, Bump Chart, Slope Chart, Area Chart, Streamgraph, Range Area Chart
Circular Pie Chart, Donut Chart, Rose Chart, Radar Chart
Tables and maps Heatmap, Calendar Heatmap, Bar Table, KPI Card, Table, Map, Choropleth

The channels each type reads are listed in the Tesserae FlintChart reference. Map and Choropleth fetch their base map from vega.github.io, so they draw only where the browser can reach it.

Refine a chart

The convenience functions return a chart object, so you can keep adding to it before the cell ends:

(flint.bar(sales, x="Product", y="Sales")
.title("Sales per product")
.color("Product")
.semantic("Sales", "Amount")
.property("cornerRadius", 4))
Python C# Effect
.x(field) .X(field) The field on the x axis.
.y(*fields) .Y(params fields) The field or fields on the y axis.
.color(field) .Color(field) A series per value of the field.
.size(field) .Size(field) Sizes the marks by the field.
.group(field) .Group(field) Groups bars side by side.
.encode(channel, *fields) .Encode(channel, params fields) Any channel, by name.
.title(text) .Title(text) The title above the chart.
.semantic(field, kind) .Semantic(field, kind) What a field means.
.property(name, value) .Property(name, value) A property of the chart type, such as innerRadius, showPoints, stackMode or binCount.

Semantic types

A semantic type says what a field means, which decides its scale, number format and colors. Flint infers one from the values when you give none. Name it when the values are ambiguous, for example an integer year or a numeric code:

flint.line(rows, x="Year", y="Revenue").semantic("Year", "Year").semantic("Revenue", "Amount")

Flint defines 44 semantic types, among them Date, Month, Year, Quarter, Amount, Price, Percentage, Count, Quantity, Score, Rank, Category, Country, State, City, Latitude and Longitude. The Flint authoring guide lists all of them.

Draw a spec you already have

A Flint spec written elsewhere, such as the JSON of a ```flint fence a model returned, draws in Python with flint.from_spec:

flint.from_spec("""
{
  "data": { "values": [ { "q": "Q1", "plan": 120, "actual": 108 }, { "q": "Q2", "plan": 130, "actual": 141 } ] },
  "semantic_types": { "q": "Quarter", "plan": "Amount", "actual": "Amount" },
  "chart_spec": { "chartType": "Line Chart", "encodings": { "x": "q", "y": ["plan", "actual"] } }
}
""")

from_spec accepts the JSON text or a dict. To see the spec a chart builds, call chart.to_json() in Python or chart.ToJson() in C#.

Tables

A query's nodes become a table in one call. The result is plain data, so it works with the rest of the notebook and with charts.

df = dataframe(q().start_at("Ticket"))                       # every field
df = dataframe(q().start_at("Ticket"), "Status", "Priority") # only these, in this order
rows = query_rows(q().start_at("Ticket"), "Status")          # a list of dicts

The rows hold UID, Type and the fields; a node without a field has None or null. Fields hidden by the schema are not included. Node types that never leave the server are dropped, as an Emit drops them. At most 100,000 nodes: a larger query fails and asks you to narrow it. See the notes for Python and C#.

Errors

Case Result
Unknown chart type ValueError in Python, ArgumentException in C#, listing the valid names.
More than 50,000 rows The same error, asking you to aggregate.
A C# row that is not an object, such as a bare number ArgumentException: each row needs a value per field.
A field named in a channel that is not in the data The chart shows Flint's reason in its place.