Struct View

Source
pub struct View(/* private fields */);
Expand description

The View struct is Perspective’s query and serialization interface. It represents a query on the Table’s dataset and is always created from an existing Table instance via the Table::view method.

Views are immutable with respect to the arguments provided to the Table::view method; to change these parameters, you must create a new View on the same Table. However, each View is live with respect to the Table’s data, and will (within a conflation window) update with the latest state as its parent Table updates, including incrementally recalculating all aggregates, pivots, filters, etc. View query parameters are composable, in that each parameter works independently and in conjunction with each other, and there is no limit to the number of pivots, filters, etc. which can be applied.

The examples in this module are in JavaScript. See perspective docs for the Rust API.
The examples in this module are in Python. See perspective docs for the Rust API.

§Examples

const table = await perspective.table({
    id: [1, 2, 3, 4],
    name: ["a", "b", "c", "d"],
});

const view = await table.view({ columns: ["name"] });
const json = await view.to_json();
await view.delete();
table = perspective.Table({
  "id": [1, 2, 3, 4],
  "name": ["a", "b", "c", "d"]
});

view = table.view(columns=["name"])
arrow = view.to_arrow()
view.delete()
let opts = TableInitOptions::default();
let data = TableData::Update(UpdateData::Csv("x,y\n1,2\n3,4".into()));
let table = client.table(data, opts).await?;

let view = table.view(None).await?;
let arrow = view.to_arrow().await?;
view.delete().await?;

§Querying data with Table::view

To query the table, create a Table::view on the table instance with an optional configuration object. A Table can have as many Views associated with it as you need - Perspective conserves memory by relying on a single Table to power multiple Views concurrently:

const view = await table.view({
    columns: ["Sales"],
    aggregates: { Sales: "sum" },
    group_by: ["Region", "Country"],
    filter: [["Category", "in", ["Furniture", "Technology"]]],
});
view = table.view(
  columns=["Sales"],
  aggregates={"Sales": "sum"},
  group_by=["Region", "Country"],
  filter=[["Category", "in", ["Furniture", "Technology"]]]
)
use crate::config::*;
let view = table
    .view(Some(ViewConfigUpdate {
        columns: Some(vec![Some("Sales".into())]),
        aggregates: Some(HashMap::from_iter(vec![("Sales".into(), "sum".into())])),
        group_by: Some(vec!["Region".into(), "Country".into()]),
        filter: Some(vec![Filter::new("Category", "in", &[
            "Furniture",
            "Technology",
        ])]),
        ..ViewConfigUpdate::default()
    }))
    .await?;

§Group By

A group by groups the dataset by the unique values of each column used as a group by - a close analogue in SQL to the GROUP BY statement. The underlying dataset is aggregated to show the values belonging to each group, and a total row is calculated for each group, showing the currently selected aggregated value (e.g. sum) of the column. Group by are useful for hierarchies, categorizing data and attributing values, i.e. showing the number of units sold based on State and City. In Perspective, group by are represented as an array of string column names to pivot, are applied in the order provided; For example, a group by of ["State", "City", "Postal Code"] shows the values for each Postal Code, which are grouped by City, which are in turn grouped by State.

const view = await table.view({ group_by: ["a", "c"] });
view = table.view(group_by=["a", "c"])
let view = table.view(Some(ViewConfigUpdate {
    group_by: Some(vec!["a".into(), "c".into()]),
    ..ViewConfigUpdate::default()
})).await?;

§Split By

A split by splits the dataset by the unique values of each column used as a split by. The underlying dataset is not aggregated, and a new column is created for each unique value of the split by. Each newly created column contains the parts of the dataset that correspond to the column header, i.e. a View that has ["State"] as its split by will have a new column for each state. In Perspective, Split By are represented as an array of string column names to pivot:

const view = await table.view({ split_by: ["a", "c"] });
view = table.view(split_by=["a", "c"])
let view = table.view(Some(ViewConfigUpdate {
    split_by: Some(vec!["a".into(), "c".into()]),
    ..ViewConfigUpdate::default()
})).await?;

§Aggregates

Aggregates perform a calculation over an entire column, and are displayed when one or more Group By are applied to the View. Aggregates can be specified by the user, or Perspective will use the following sensible default aggregates based on column type:

  • “sum” for integer and float columns
  • “count” for all other columns

Perspective provides a selection of aggregate functions that can be applied to columns in the View constructor using a dictionary of column name to aggregate function name.

const view = await table.view({
    aggregates: {
        a: "avg",
        b: "distinct count",
    },
});
view = table.view(
  aggregates={
    "a": "avg",
    "b": "distinct count"
  }
)

§Columns

The columns property specifies which columns should be included in the View’s output. This allows users to show or hide a specific subset of columns, as well as control the order in which columns appear to the user. This is represented in Perspective as an array of string column names:

const view = await table.view({
    columns: ["a"],
});
view = table.view(columns=["a"])

§Sort

The sort property specifies columns on which the query should be sorted, analogous to ORDER BY in SQL. A column can be sorted regardless of its data type, and sorts can be applied in ascending or descending order. Perspective represents sort as an array of arrays, with the values of each inner array being a string column name and a string sort direction. When column-pivots are applied, the additional sort directions "col asc" and "col desc" will determine the order of pivot columns groups.

const view = await table.view({
    sort: [["a", "asc"]],
});
view = table.view(sort=[["a", "asc"]])

§Filter

The filter property specifies columns on which the query can be filtered, returning rows that pass the specified filter condition. This is analogous to the WHERE clause in SQL. There is no limit on the number of columns where filter is applied, but the resulting dataset is one that passes all the filter conditions, i.e. the filters are joined with an AND condition.

Perspective represents filter as an array of arrays, with the values of each inner array being a string column name, a string filter operator, and a filter operand in the type of the column:

const view = await table.view({
    filter: [["a", "<", 100]],
});
view = table.view(filter=[["a", "<", 100]])

§Expressions

The expressions property specifies new columns in Perspective that are created using existing column values or arbitary scalar values defined within the expression. In <perspective-viewer>, expressions are added using the “New Column” button in the side panel.

A custom name can be added to an expression by making the first line a comment:

const view = await table.view({
    expressions: { '"a" + "b"': '"a" + "b"' },
});
view = table.view(expressions=['"a" + "b"'])

§Flattening a Table::view into a Table

In Javascript, a Table can be constructed on a Table::view instance, which will return a new Table based on the Table::view’s dataset, and all future updates that affect the Table::view will be forwarded to the new Table. This is particularly useful for implementing a Client/Server Replicated design, by serializing the View to an arrow and setting up an on_update callback.

const worker1 = perspective.worker();
const table = await worker.table(data);
const view = await table.view({ filter: [["State", "==", "Texas"]] });
const table2 = await worker.table(view);
table.update([{ State: "Texas", City: "Austin" }]);
table = perspective.Table(data);
view = table.view(filter=[["State", "==", "Texas"]])
table2 = perspective.Table(view.to_arrow());

def updater(port, delta):
    table2.update(delta)

view.on_update(updater, mode="Row")
table.update([{"State": "Texas", "City": "Austin"}])
let opts = TableInitOptions::default();
let data = TableData::Update(UpdateData::Csv("x,y\n1,2\n3,4".into()));
let table = client.table(data, opts).await?;
let view = table.view(None).await?;
let table2 = client.table(TableData::View(view)).await?;
table.update(data).await?;

Implementations§

Source§

impl View

Source

pub fn column_paths(&self, py: Python<'_>) -> PyResult<Vec<String>>

Returns an array of strings containing the column paths of the View without any of the source columns.

A column path shows the columns that a given cell belongs to after pivots are applied.

Source

pub fn to_columns_string( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>

Serializes this View to a string of JSON data. Useful if you want to save additional round trip serialize/deserialize cycles.

Source

pub fn to_json_string( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>

Render this View as a JSON string.

Source

pub fn to_ndjson( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>

Renders this View as an NDJSON formatted String.

Source

pub fn to_records<'a>( &self, py: Python<'a>, window: Option<Py<PyDict>>, ) -> PyResult<Bound<'a, PyAny>>

Source

pub fn to_json<'a>( &self, py: Python<'a>, window: Option<Py<PyDict>>, ) -> PyResult<Bound<'a, PyAny>>

Serializes this View to JSON data in a row-oriented format.

Source

pub fn to_columns<'a>( &self, py: Python<'a>, window: Option<Py<PyDict>>, ) -> PyResult<Bound<'a, PyAny>>

Serializes this View to JSON data in a column-oriented format.

Source

pub fn to_csv( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>

Serializes this View to CSV data in a standard format.

Source

pub fn to_dataframe( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>

Serialize the data to a pandas.DataFrame.

Source

pub fn to_pandas( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>

Serialize the data to a pandas.DataFrame.

Source

pub fn to_polars( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>

Serialize the data to a polars.DataFrame.

Source

pub fn to_arrow( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyBytes>>

Serializes a View to the Apache Arrow data format.

Source

pub fn delete(&self, py: Python<'_>) -> PyResult<()>

Delete this View and clean up all resources associated with it. View objects do not stop consuming resources or processing updates when they are garbage collected - you must call this method to reclaim these.

Source

pub fn expand(&self, py: Python<'_>, index: u32) -> PyResult<u32>

Expands the row at index.

This is used during a pivot.

Source

pub fn collapse(&self, py: Python<'_>, index: u32) -> PyResult<u32>

Collapses the row at index.

This is used during a pivot.

Source

pub fn dimensions(&self, py: Python<'_>) -> PyResult<Py<PyAny>>

Returns this View’s dimensions, row and column count, as well as those of the crate::Table from which it was derived.

  • num_table_rows - The number of rows in the underlying crate::Table.
  • num_table_columns - The number of columns in the underlying crate::Table (including the index column if this crate::Table was constructed with one).
  • num_view_rows - The number of rows in this View. If this View has a group_by clause, num_view_rows will also include aggregated rows.
  • num_view_columns - The number of columns in this View. If this View has a split_by clause, num_view_columns will include all column paths, e.g. the number of columns clause times the number of split_by groups.
Source

pub fn expression_schema( &self, py: Python<'_>, ) -> PyResult<HashMap<String, String>>

The expression schema of this View, which contains only the expressions created on this View. See View::schema for details.

Source

pub fn get_config(&self, py: Python<'_>) -> PyResult<Py<PyAny>>

A copy of the config object passed to the Table::view method which created this View.

Source

pub fn get_min_max( &self, py: Python<'_>, column_name: String, ) -> PyResult<(String, String)>

Calculates the [min, max] of the leaf nodes of a column column_name.

§Returns

A tuple of [min, max], whose types are column and aggregate dependent.

Source

pub fn num_rows(&self, py: Python<'_>) -> PyResult<u32>

The number of aggregated rows in this View. This is affected by the “group_by” configuration parameter supplied to this view’s contructor.

§Returns

The number of aggregated rows.

Source

pub fn schema(&self, py: Python<'_>) -> PyResult<HashMap<String, String>>

The schema of this View.

The View schema differs from the schema returned by Table::schema; it may have different column names due to expressions or columns configs, or it maye have different column types due to the application og group_by and aggregates config. You can think of Table::schema as the input schema and View::schema as the output schema of a Perspective pipeline.

§JavaScript Examples
const [`View`] = await table.view({ columns: ["a", "b"] });
const schema = await view.schema(); // {a: "float", b: "string"}
Source

pub fn on_delete(&self, py: Python<'_>, callback: Py<PyAny>) -> PyResult<u32>

Register a callback with this View. Whenever the View is deleted, this callback will be invoked.

§JavaScript Examples
// attach an `on_delete` callback
view.on_delete(() => console.log("Deleted!"));
Source

pub fn remove_delete(&self, py: Python<'_>, callback_id: u32) -> PyResult<()>

Unregister a previously registered delete callback with this View.

§Example
// remove an `on_delete` callback
const callback = () => console.log("Deleted!");
view.remove_delete(callback);
Source

pub fn on_update( &self, py: Python<'_>, callback: Py<PyAny>, mode: Option<String>, ) -> PyResult<u32>

Register a callback with this View. Whenever the view’s underlying table emits an update, this callback will be invoked with an object containing port_id, indicating which port the update fired on, and optionally delta, which is the new data that was updated for each cell or each row.

§Arguments
  • on_update - A callback function invoked on update, which receives an object with two keys: port_id, indicating which port the update was triggered on, and delta, whose value is dependent on the mode parameter.
  • options - If this is provided as OnUpdateOptions { mode: Some(OnUpdateMode::Row) }, then delta is an Arrow of the updated rows. Otherwise delta will be Option::None.
§JavaScript Examples
// Attach an `on_update` callback
view.on_update((updated) => console.log(updated.port_id));
// `on_update` with row deltas
view.on_update((updated) => console.log(updated.delta), { mode: "row" });
Source

pub fn remove_update(&self, py: Python<'_>, callback_id: u32) -> PyResult<()>

Unregister a previously registered update callback with this View.

§Arguments
§JavaScript Examples
const callback = () => console.log("Updated!");
const id = await view.on_update(callback);
await view.remove_update(id);
§Python Examples
callback = lambda x: print(x)
cid = await view.on_update(callback)
await view.remove_update(cid)
§Examples
let callback = |_| async { print!("Updated!") };
let cid = view.on_update(callback, OnUpdateOptions::default()).await?;
view.remove_update(cid).await?;

Trait Implementations§

Source§

impl IntoPy<Py<PyAny>> for View

Source§

fn into_py(self, py: Python<'_>) -> PyObject

👎Deprecated since 0.23.0: IntoPy is going to be replaced by IntoPyObject. See the migration guide (https://pyo3.rs/v0.23.0/migration) for more information.
Performs the conversion.
Source§

impl<'py> IntoPyObject<'py> for View

Source§

type Target = View

The Python output type
Source§

type Output = Bound<'py, <View as IntoPyObject<'py>>::Target>

The smart pointer type to use. Read more
Source§

type Error = PyErr

The type returned in the event of a conversion error.
Source§

fn into_pyobject( self, py: Python<'py>, ) -> Result<<Self as IntoPyObject<'_>>::Output, <Self as IntoPyObject<'_>>::Error>

Performs the conversion.
Source§

impl PyClass for View

Source§

type Frozen = False

Whether the pyclass is frozen. Read more
Source§

impl PyClassBaseType for View

Source§

type LayoutAsBase = PyClassObject<View>

Source§

type BaseNativeType = <View as PyClassImpl>::BaseNativeType

Source§

type Initializer = PyClassInitializer<View>

Source§

type PyClassMutability = <View as PyClassImpl>::PyClassMutability

Source§

impl PyClassImpl for View

Source§

const IS_BASETYPE: bool = true

#[pyclass(subclass)]
Source§

const IS_SUBCLASS: bool = false

#[pyclass(extends=…)]
Source§

const IS_MAPPING: bool = false

#[pyclass(mapping)]
Source§

const IS_SEQUENCE: bool = false

#[pyclass(sequence)]
Source§

type BaseType = PyAny

Base class
Source§

type ThreadChecker = SendablePyClass<View>

This handles following two situations: Read more
Source§

type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::MutableChild

Immutable or mutable
Source§

type Dict = PyClassDummySlot

Specify this class has #[pyclass(dict)] or not.
Source§

type WeakRef = PyClassDummySlot

Specify this class has #[pyclass(weakref)] or not.
Source§

type BaseNativeType = PyAny

The closest native ancestor. This is PyAny by default, and when you declare #[pyclass(extends=PyDict)], it’s PyDict.
Source§

fn items_iter() -> PyClassItemsIter

Source§

fn doc(py: Python<'_>) -> PyResult<&'static CStr>

Rendered class doc
Source§

fn lazy_type_object() -> &'static LazyTypeObject<Self>

Source§

fn dict_offset() -> Option<isize>

Source§

fn weaklist_offset() -> Option<isize>

Source§

impl PyClassNewTextSignature<View> for PyClassImplCollector<View>

Source§

fn new_text_signature(self) -> Option<&'static str>

Source§

impl<'a, 'py> PyFunctionArgument<'a, 'py> for &'a View

Source§

type Holder = Option<PyRef<'py, View>>

Source§

fn extract( obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder, ) -> PyResult<Self>

Source§

impl<'a, 'py> PyFunctionArgument<'a, 'py> for &'a mut View

Source§

type Holder = Option<PyRefMut<'py, View>>

Source§

fn extract( obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder, ) -> PyResult<Self>

Source§

impl PyMethods<View> for PyClassImplCollector<View>

Source§

fn py_methods(self) -> &'static PyClassItems

Source§

impl PyTypeInfo for View

Source§

const NAME: &'static str = "View"

Class name.
Source§

const MODULE: Option<&'static str>

Module name, if any.
Source§

fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject

Returns the PyTypeObject instance for this type.
Source§

fn type_object(py: Python<'_>) -> Bound<'_, PyType>

Returns the safe abstraction over the type object.
Source§

fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType>

👎Deprecated since 0.23.0: renamed to PyTypeInfo::type_object
Deprecated name for PyTypeInfo::type_object.
Source§

fn is_type_of(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type or a subclass of this type.
Source§

fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool

👎Deprecated since 0.23.0: renamed to PyTypeInfo::is_type_of
Deprecated name for PyTypeInfo::is_type_of.
Source§

fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type.
Source§

fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool

👎Deprecated since 0.23.0: renamed to PyTypeInfo::is_exact_type_of
Deprecated name for PyTypeInfo::is_exact_type_of.
Source§

impl DerefToPyAny for View

Auto Trait Implementations§

§

impl Freeze for View

§

impl !RefUnwindSafe for View

§

impl Send for View

§

impl Sync for View

§

impl Unpin for View

§

impl !UnwindSafe for View

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<'py, T> IntoPyObjectExt<'py> for T
where T: IntoPyObject<'py>,

Source§

fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>

Converts self into an owned Python object, dropping type information.
Source§

fn into_py_any(self, py: Python<'py>) -> Result<Py<PyAny>, PyErr>

Converts self into an owned Python object, dropping type information and unbinding it from the 'py lifetime.
Source§

fn into_pyobject_or_pyerr(self, py: Python<'py>) -> Result<Self::Output, PyErr>

Converts self into a Python object. Read more
Source§

impl<T> PyErrArguments for T
where T: for<'py> IntoPyObject<'py> + Send + Sync,

Source§

fn arguments(self, py: Python<'_>) -> Py<PyAny>

Arguments for exception
Source§

impl<T> PyTypeCheck for T
where T: PyTypeInfo,

Source§

const NAME: &'static str = <T as PyTypeInfo>::NAME

Name of self. This is used in error messages, for example.
Source§

fn type_check(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of Self, which may include a subtype. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> Ungil for T
where T: Send,