Aner Plot
Compose native scientific charts, offline USA and world maps, reusable light, dark and company themes, and SVG outputs beside their notebook analysis.
Implemented in developmentimport aner.plot brings scientific charts, offline geographic maps, reusable themes and SVG export into the native Aner workflow. Compose immutable chart values with ordinary method chains, then display them beside the analysis they explain.
Development availability: this module requires a chart enabled native Aner 0.0.2-dev.1 build and development VS Code extension 0.1.46 for notebook display. The published 0.0.2 runtime and 0.1.45 extension predate this feature. Development packages are not offered on this website. A matching runtime remains necessary even when an extension is installed. See release and development status.
Start with the complete example
Download the Aner source or the VS Code notebook. With a compatible installed aner command, check and run the source:
aner check plot_story.aner
aner run plot_story.anerThe source emits nine charts: a comparison line, scatter, bar, histogram, box, heatmap, a custom geographic join, world map and USA map. It needs no external dataset, connection or credentials. All values are synthetic teaching data. Command line execution emits the chart display data; use the notebook to view each chart beside its producing cell, or call .save("fresh-chart.svg") to export one.
Open the notebook in VS Code, select the compatible Aner runtime, restart the notebook session and choose Run All. The notebook also shows a dark version of the same comparison chart. Its setup cell stores values without drawing a chart. Subsequent chart expressions and .show() calls produce outputs below their own cells, including two separate outputs in the distribution cell.
API reference
Chart constructors return a Chart; the plot.geojson loader returns String text. Supply finite numeric List, Array, DynamicArray, NDArray or Tensor inputs. Series accept vectors; heatmaps accept a flat sequence with explicit dimensions or a matching matrix. Numeric lists may mix Int64 and Float64. Strings, Booleans, nulls and nonfinite values are rejected as numeric observations. Integers outside the exact Float64 range from negative 2^53 through positive 2^53 are rejected.
| Constructor | Input and meaning |
|---|---|
plot.line(x: Any, y: Any) | Equal length coordinates, connected in supplied order |
plot.scatter(x: Any, y: Any) | Equal length coordinates, one point per observation |
plot.bar(labels: List, values: Any) | String categories and their supplied numeric values |
plot.histogram(values: Any, bins: Int64) | Counts in equal width bins; omitted bins defaults to 10 |
plot.box(values: Any) | Observed median, quartiles, whiskers and outliers |
plot.heatmap(values: Any, rows: Int64, columns: Int64) | Numeric cells in row major order, matching the declared shape |
plot.choropleth(geojson: String, property: String, keys: List, values: Any) | Explicit String key join to supplied GeoJSON regions |
plot.world(keys: List, values: Any) | Offline world regions joined by Natural Earth ADM0_A3 |
plot.usa(keys: List, values: Any) | Offline states and DC joined by uppercase postal code |
plot.geojson(path: String) | Read bounded local GeoJSON text for a custom choropleth |
import aner.plot
let weeks = List(1.0, 2.0, 3.0, 4.0)
let response = List(12.0, 15.0, 14.0, 19.0)
let trend = plot.line(x: weeks, y: response)
.title("Response increased in this synthetic example")
.labels(x: "Week", y: "Response (arbitrary units)")
.caption("Synthetic measurements; four observations, no causal claim.")
.annotate(x: 4.0, y: 19.0, text: "Last observation")
trend.show()Construction snapshots the inputs. Later collection mutations do not change an existing chart. Sort time coordinates deliberately: a line follows the given order. Bars do not aggregate a table, and histograms report counts rather than probability density. Explicitly collect and filter the desired data before constructing a chart.
Axes and color scales disclose shared numeric offsets or powers of ten when close values have large magnitudes. Tooltips retain the full actual values so compact labels do not hide the supplied measurements.
Box quartiles use linear interpolation at sorted position (n - 1) * p. Whiskers reach observed values within 1.5 interquartile ranges; points beyond them are drawn individually. Whiskers describe sample spread, not confidence intervals. Histogram bins include their left boundary and exclude their right boundary, except the final bin includes the maximum.
Compose a clear scientific story
Every modifier returns another chart. Retain the result with an assignment or chain. There is no implicit current figure and no global plotting theme.
| Method | Result |
|---|---|
.title(text: String) | Set a supported takeaway |
.labels(x: String, y: String) | Name variables and units |
.caption(text: String) | Record source, population, exclusions and uncertainty definition |
.annotate(x: Float64, y: Float64, text: String) | Mark a supplied data coordinate |
.add_line(x: Any, y: Any, name: String) | Add a named line to a line or scatter chart |
.theme(theme: ChartTheme) | Attach an immutable presentation snapshot |
.size(width: Int64, height: Int64) | Choose bounded SVG dimensions |
.show() | Emit one chart output for the current execution |
.svg() | Return the SVG as String |
.summary() | Return a plain text chart description |
.save(path: String) | Write a new SVG file; existing files are not replaced |
Company theme and named comparison
let corporate = plot.theme("light").company(
name: "Example Research",
background: "#FFFFFF",
foreground: "#17233B",
colors: List("#007C91", "#C6601D", "#6950A1")
)
let comparison = trend
.add_line(weeks, List(10.0, 12.0, 13.0, 14.0), name: "Reference")
.theme(corporate)
comparison.show()The gallery uses the six observations in the complete notebook; the shorter code above uses four. Reference values are supplied rather than fitted. The company name identifies the theme; it does not add a logo. Include the organization in the title or caption when the artwork must show it.
Light, dark and custom palettes
plot.theme("light"), plot.theme("dark") and plot.theme("auto") return ChartTheme. Explicit light and dark choices are stable presentation decisions; auto follows the host where supported. Attach a theme per chart so execution order does not silently change later figures.
let dark = plot.theme("dark").palette(
colors: List("#79D2E0", "#FFBC78", "#C6ACFF")
)
comparison.theme(dark).show()
comparison.theme(plot.theme("light")).show()Theme colors use checked hexadecimal values. Palette and company modifiers return new theme values. Legends, labels and annotations should explain meaning alongside color. A valid hexadecimal palette is not a contrast certification or evidence of perceptual uniformity.
Offline USA and world maps
Bundled Natural Earth 1:110m boundaries require no online tiles, geocoding or map service. plot.world joins Natural Earth ADM0_A3 codes such as USA, CAN, FRA and NOR. These codes are not strictly ISO country codes; this source also includes KOS and SOL. Its 177 features follow the source geographic grouping rather than defining a sovereign country count.
plot.world(keys: List("USA", "CAN", "FRA"), values: List(12.0, 9.0, 15.0))
.title("Synthetic values on an offline world map")
.caption("Synthetic values; boundaries: Natural Earth 1:110m.")
.show()plot.usa joins uppercase two letter postal codes for 50 states and the District of Columbia. Alaska and Hawaii retain their geographic positions; this is not a relocated inset map or county catalogue.
plot.usa(keys: List("CA", "CO", "NY", "TX"), values: List(18.0, 12.0, 15.0, 9.0))
.title("Synthetic values for four USA states")
.caption("Synthetic values; other states have no data, not zero.")
.theme(corporate)
.show()For custom boundaries, load licensed local GeoJSON with plot.geojson(path: "regions.geojson"), then join a stable String property with plot.choropleth. GeoJSON coordinates are longitude, latitude, in that order. Polygon holes and MultiPolygon parts are supported. Preserve identifiers with leading zeros. Confirm join coverage, denominators and geographic vintage before interpreting results. Missing regions are no data, never fabricated zeros.
The initial projection is an equirectangular preview with limited cartographic control. Do not infer area or distance. Supplied rings crossing the antimeridian must already be split; unsplit edges are rejected except the legitimate Antarctic pole boundary.
Boundary source and attribution
Made with Natural Earth, whose terms of use place the geographic data in the public domain. Credit includes Tom Patterson, Nathaniel Vaughn Kelso and contributors. The bundled source is fixed at commit ca96624a56bd078437bca8184e78163e5039ad19, June 2, 2022. The official 110m layer pages identify version 5.1.1; the repository version at that commit reads 5.2.0-pre.
| Boundary file | Features | SHA256 |
|---|---|---|
world-110m.geojson | 177 | 6866c877d39cba9c357620878839b336d569f8c662d3cfab4cb1dbe2d39c977f |
usa-110m.geojson | 51 | 0067dec6a2c4f9c7a644bf7a9a46163d8d881595953326d0c67d89934190aefe |
Notebook placement and export
Use .show() in the cell whose analysis the chart supports. Each call emits a separate output in execution order. In a shared native notebook session, a bare chart expression also displays; let stores a chart without displaying it. Inside loops and functions, call .show() explicitly. Put explanatory Markdown immediately before or after the producing cell.
Notebook outputs are saved historical results. Editing code or changing later values does not recompute a chart. Restart and run cells in order to verify a fresh analysis. Saving SVG outputs does not preserve live runtime variables. The optional native Jupyter adapter uses the same SVG through its MIME display mechanism.
For a shareable figure, call comparison.save("response-comparison.svg") with a fresh filename. SVG keeps marks and text scalable. Native PNG and PDF exporters, font embedding, animation and interactive selection are not implemented.
Errors, bounds and next steps
Mismatched lengths, unsupported ranks, invalid colors, nonfinite values and malformed geometry produce diagnostics. Data ingestion, table aggregation, fitting and uncertainty estimation remain explicit upstream decisions.
Limits are 100,000 observations, 32 series, 250 bar categories or histogram bins, 10,000 heatmap cells, 10,000 geographic features or joins, 100 annotations and 32 palette colors per chart. Text permits 4,096 UTF-8 bytes. Width ranges from 320 to 4,096 pixels, and height from 240 to 4,096 pixels. GeoJSON permits 8 MiB and 100,000 coordinates within parser limits. SVG output is bounded to 32 MiB; notebook envelopes additionally permit at most 64 charts and 4 MiB per cell. Exceeding a bound produces a diagnostic instead of silent sampling.
The implemented families are line, scatter, bar, histogram, box, heatmap and choropleth. World and USA are convenience constructors for choropleths. Figure grids, facets, temporal scales, statistical intervals, sequential and diverging scale controls, additional geographic projections, 3D and further exporters remain future work.