docs/md/FAQ.md
Python wheels are published for supported Python versions and platforms. On Windows, ensure you have a compatible Python version and architecture. Install with:
pip install perspective-python
If you encounter C++ binding errors or link errors, make sure you are using a
supported Python version and that your pip is up to date. Pre-built wheels
eliminate the need for a C++ compiler in most cases.
import perspective fails with ImportError or undefined symbolThis typically happens when the C++ shared library (libpsp.so) cannot be found
or was built against a different Python version. Ensure your Python version
matches the installed wheel. On Linux, verify that required system libraries are
present. If you see errors about libpsp.so or undefined symbols, try
reinstalling in a clean virtual environment.
On Apple Silicon (M1/M2/M3), make sure you are using a native ARM Python build,
not one running under Rosetta. The published wheels include aarch64 variants
for supported platforms.
Perspective's Python wheels are built against manylinux_2_28 containers (see
.github/workflows/build.yaml), so they
are compatible with most Linux distributions based on glibc 2.28+ (e.g., Debian
10+, Ubuntu 20.04+, RHEL 8+). Use a compatible base image:
FROM python:3.12-slim
RUN pip install perspective-python
Alpine Linux uses musl instead of glibc and is not compatible with the published wheels.
<!-- _Related: [#1201](https://github.com/perspective-dev/perspective/issues/1201)_ -->Perspective no longer exports bundler plugins. Instead, you must manually bootstrap the WASM binaries using your bundler's asset handling. See Importing with or without a bundler for complete examples for Vite, Webpack, esbuild, CDN, and inline builds.
<!-- _Related: [#1734](https://github.com/perspective-dev/perspective/issues/1734), [#2725](https://github.com/perspective-dev/perspective/issues/2725), [#857](https://github.com/perspective-dev/perspective/issues/857), [#1497](https://github.com/perspective-dev/perspective/issues/1497), [#1655](https://github.com/perspective-dev/perspective/issues/1655)_ -->Perspective provides a dedicated React component. You must also still initialize Perspective's WebAssembly as per your bundler — see Importing with or without a bundler.
<!-- _Related: [#865](https://github.com/perspective-dev/perspective/issues/865), [#931](https://github.com/perspective-dev/perspective/issues/931), [#3023](https://github.com/perspective-dev/perspective/discussions/3023)_ -->Perspective relies on Web Workers and WASM, which require client-side rendering.
Use dynamic imports with ssr: false in Next.js to load Perspective components
only on the client.
As a standard Web Component, <perspective-viewer> works in most JavaScript web
frameworks directly via standard HTML/DOM APIs, but does not have dedicated
integration libraries for these frameworks.
Use the expressions config option
in your View to define new columns with ExprTK syntax, which must then be
used somewhere else in your config (like columns) to actually be visible &
calculated. In <perspective-viewer>, expression columns can be created from
the UI column sidebar by clicking the "New Column" button.
No, you must duplicate calculations that are shared between expression columns.
<!-- _Related: [#2148](https://github.com/perspective-dev/perspective/issues/2148)_ -->Yes, but they must be converted to float values first (integer is an i32
which is too small). See
Expressions.
Not in Perspective's built-in engine, but as an alternative, DuckDB supports
rolling and cumulative sums via WINDOW functions,
and DuckDB now has
native Perspective Virtual Server support
which allows arbitrary DuckDB queries (as a TABLE or VIEW) to be
<perspective-viewer> Tables.
Perspective filters are composed with AND logic by default. As an alternative, you can use expression columns to create a boolean column that encodes your OR logic (or any arbitrary multi-column predicate), then filter on that column:
const view = await table.view({
expressions: {
or_filter:
"if (\"State\" == 'Texas') true; else if (\"State\" == 'California') true; else false",
},
filter: [["or_filter", "==", true]],
});
Set the filter
property on a View config, or use the <perspective-viewer>
.restore() method to update filters at
runtime.
Date columns can be
filtered with
comparison operators (>, <, >=, <=) to achieve range-based filtering.
Apply two filters on the same date column for a range.
PerspectiveWidget is not loading in JupyterLabSee the PerspectiveWidget guide for full
setup details. Ensure the JupyterLab extension version matches your
perspective-python version. Make sure you are using a compatible JupyterLab
for your Perspective version (JupyterLab 4+ currently).
Check that the extension is enabled with jupyter labextension list.
Maybe, but please review the
Cleaning up resources docs carefully before
opening an Issue reporting it (and of course review
CONTRIBUTING.md
before opening any Issue). Ensure you call .delete() on Views, Tables, and
<perspective-viewer> instances when they are no longer needed, in reverse
dependency order.
Perspective is designed for large datasets and can handle millions of rows
depending on the number of columns and available memory. Performance also
significantly depends on column types ("string" being slower and larger than
other types due to dictionary interning).
For larger datasets or out-of-memory virtualized datasets, see Virtual Servers.
<!-- _Related: [#341](https://github.com/perspective-dev/perspective/issues/341), [#1719](https://github.com/perspective-dev/perspective/issues/1719), [#1089](https://github.com/perspective-dev/perspective/issues/1089)_ -->perspective-python?The Python library uses a thread pool internally. For advanced threading control, consult the multithreading documentation.
<!-- _Related: [#1145](https://github.com/perspective-dev/perspective/issues/1145), [#1313](https://github.com/perspective-dev/perspective/issues/1313)_ -->Import themes.css (see Theming) and set the
theme via restore():
await viewer.restore({ theme: "Pro Dark" });
Or import just the dark theme directly:
import "@perspective-dev/viewer/dist/css/pro-dark.css";
The datagrid plugin supports custom styling via
column_config
and CSS custom properties, but custom cell renderers require building a custom
plugin.
Chart colors can be customized via
CSS custom properties on the
<perspective-viewer> element.
Use table.update() to push new
data incrementally. For indexed tables,
updates with matching index values will replace existing rows.
table.update() raises "No Running Event Loop"Perspective 3+ is now threadsafe by default and no longer requires special loop integration.
<!-- _Related: [#2801](https://github.com/perspective-dev/perspective/discussions/2801)_ -->Use view.on_update() to register a callback that fires when the underlying
table data changes. See Listening for events
and Advanced View Operations.
See Data Architecture for detailed explanations of each mode.
<!-- _Related: [#2916](https://github.com/perspective-dev/perspective/discussions/2916)_ -->Server safe to expose to untrusted clients?No. The WebSocket Server is not a security boundary. Every connected Client
is treated as the author of the queries it submits, and is permitted to create
and delete Table/View resources, author arbitrary
expression columns, and — for
Virtual Server backends like DuckDB or
ClickHouse — author SQL fragments executed under the configured database
role. The bundled WebSocket adapters
(tornado.py/aiohttp.py/starlette.py/WebSocketServer) are reference
integrations and do not authenticate, authorize, or enforce origin policy.
WebSocket Deployments that need per-user isolation must put an authenticating
proxy in front of the Server, run a least-privileged database role for any
Virtual Server backend, and/or isolate users into separate Server
instances. See SECURITY.md for the full threat model
and deployment guidance.
Obviously, none of this applies to WASM DBs like Perspective and DuckDB.
Virtual Servers?No, by design. Virtual Server backends
interpolate client-supplied view_id, table_id, column_name, expression
strings, and filter operators directly into SQL templates without
parameterization or whitelist validation. The Client is the author of the
queries — there is no privilege boundary inside the engine for sanitization
to enforce. If your deployment needs to restrict the SQL surface area exposed
to a Client, the supported boundary is the database role the Virtual Server
is configured with (read-only etc), or better complete isolation via WASM
backend.
The WebSocketServer does not include
built-in authentication. Implement authentication at the transport layer (e.g.,
via middleware in your HTTP server) before the WebSocket upgrade. For more
complex needs, WebSocketServer is a simple example server based on the
node:http module which can serve as a starting point for a custom server.
Perspective supports Virtual Servers that proxy queries to external data sources, with built-in implementations for e.g. DuckDB.
<!-- _Related: [#1255](https://github.com/perspective-dev/perspective/issues/1255), [#1361](https://github.com/perspective-dev/perspective/discussions/1361)_ -->Yes, by creating a duplicate/alias for your column via
expressions:
await viewer.restore({
columns: ["Sales", "Sales 2"],
expresions: { "Sales 2": '"Sales"' },
aggregate: {
Sales: "sum",
"Sales 2": "avg",
},
});
Use expression columns on an aggregated View to compute ratios. Define an expression that divides one column by another.
<!-- _Related: [#2994](https://github.com/perspective-dev/perspective/discussions/2994), [#3096](https://github.com/perspective-dev/perspective/discussions/3096)_ -->Perspective natively accepts
Apache Arrow format. Pass an
ArrayBuffer containing Arrow IPC data directly to table() or
table.update().
Perspective accepts (see Loading data):
ArrayBufferdict, list, pandas.DataFrame, pyarrow.Table, CSV strings,
Apache Arrow bytesWhen updating a table created with a schema, ensure the CSV column names and types match the schema exactly. Mismatched column names or types will cause update failures.
<!-- _Related: [#2524](https://github.com/perspective-dev/perspective/issues/2524)_ -->HTML and PNG exports are available via viewer.export("html") and
viewer.export("png"), respectively. For PDF, render the viewer and use browser
or headless browser screenshot capabilities.
Perspective does not have built-in Excel export. Export data via
view.to_csv(), view.to_json(), or view.to_arrow() (see
Serializing data) and convert to Excel
using a library like xlsx (JavaScript) or openpyxl (Python).
Use the "text" export mode when data is selected:
await viewer.export("text").
table.remove() does not update the viewerThe remove() method requires an
indexed table. Ensure your table was created
with an index option, and pass the index values to remove.
Use
viewer.save() and viewer.restore() to
serialize and deserialize the full viewer configuration.
The settings panel can be toggled programmatically via
await viewer.restore({ settings: false }).
Row group can be closed imperatively via
view.set_depth(). Expansion state is not
persisted or configurable via the save/restore API currently.
Perspective's UI text is defined via CSS variables, which can be customized per theme. See the Icons and Translation section of the theming guide for details.
<!-- _Related: [#1934](https://github.com/perspective-dev/perspective/issues/1934), [#2358](https://github.com/perspective-dev/perspective/issues/2358)_ -->See the Getting Started guide for Rust. The Rust crate wraps
the C++ engine and requires a C++ toolchain. You need cmake installed and on
your path to build the engine.
<perspective-viewer>?Yes. The perspective library (data engine) can be used independently for
server-side data processing without any UI. Use
table() and view() directly to query data.
There is an emscripten wheel published via Releases, but it must be downloaded and hosted manually and is only built for specific pyodide versions.
<!-- _Related: [#2880](https://github.com/perspective-dev/perspective/discussions/2880)_ -->Listen for
perspective-click and perspective-select
events on the <perspective-viewer> element.