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.
perspective docs for the Rust API.
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
integerandfloatcolumns - “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
impl View
Sourcepub fn column_paths(&self, py: Python<'_>) -> PyResult<Vec<String>>
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.
Sourcepub fn to_columns_string(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<String>
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.
Sourcepub fn to_json_string(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<String>
pub fn to_json_string( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>
Render this View as a JSON string.
Sourcepub fn to_ndjson(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<String>
pub fn to_ndjson( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>
Renders this View as an NDJSON
formatted String.
pub fn to_records<'a>( &self, py: Python<'a>, window: Option<Py<PyDict>>, ) -> PyResult<Bound<'a, PyAny>>
Sourcepub fn to_json<'a>(
&self,
py: Python<'a>,
window: Option<Py<PyDict>>,
) -> PyResult<Bound<'a, PyAny>>
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.
Sourcepub fn to_columns<'a>(
&self,
py: Python<'a>,
window: Option<Py<PyDict>>,
) -> PyResult<Bound<'a, PyAny>>
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.
Sourcepub fn to_csv(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<String>
pub fn to_csv( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<String>
Serializes this View to CSV data in a standard format.
Sourcepub fn to_dataframe(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<Py<PyAny>>
pub fn to_dataframe( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>
Serialize the data to a pandas.DataFrame.
Sourcepub fn to_pandas(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<Py<PyAny>>
pub fn to_pandas( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>
Serialize the data to a pandas.DataFrame.
Sourcepub fn to_polars(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<Py<PyAny>>
pub fn to_polars( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyAny>>
Serialize the data to a polars.DataFrame.
Sourcepub fn to_arrow(
&self,
py: Python<'_>,
window: Option<Py<PyDict>>,
) -> PyResult<Py<PyBytes>>
pub fn to_arrow( &self, py: Python<'_>, window: Option<Py<PyDict>>, ) -> PyResult<Py<PyBytes>>
Serializes a View to the Apache Arrow data format.
Sourcepub fn delete(&self, py: Python<'_>) -> PyResult<()>
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.
Sourcepub fn expand(&self, py: Python<'_>, index: u32) -> PyResult<u32>
pub fn expand(&self, py: Python<'_>, index: u32) -> PyResult<u32>
Expands the row at index.
This is used during a pivot.
Sourcepub fn collapse(&self, py: Python<'_>, index: u32) -> PyResult<u32>
pub fn collapse(&self, py: Python<'_>, index: u32) -> PyResult<u32>
Collapses the row at index.
This is used during a pivot.
Sourcepub fn dimensions(&self, py: Python<'_>) -> PyResult<Py<PyAny>>
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 underlyingcrate::Table.num_table_columns- The number of columns in the underlyingcrate::Table(including theindexcolumn if thiscrate::Tablewas constructed with one).num_view_rows- The number of rows in thisView. If thisViewhas agroup_byclause,num_view_rowswill also include aggregated rows.num_view_columns- The number of columns in thisView. If thisViewhas asplit_byclause,num_view_columnswill include all column paths, e.g. the number ofcolumnsclause times the number ofsplit_bygroups.
Sourcepub fn expression_schema(
&self,
py: Python<'_>,
) -> PyResult<HashMap<String, String>>
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.
Sourcepub fn get_config(&self, py: Python<'_>) -> PyResult<Py<PyAny>>
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.
Sourcepub fn get_min_max(
&self,
py: Python<'_>,
column_name: String,
) -> PyResult<(String, String)>
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.
Sourcepub fn schema(&self, py: Python<'_>) -> PyResult<HashMap<String, String>>
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"}Sourcepub fn on_update(
&self,
py: Python<'_>,
callback: Py<PyAny>,
mode: Option<String>,
) -> PyResult<u32>
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, anddelta, whose value is dependent on the mode parameter.options- If this is provided asOnUpdateOptions { mode: Some(OnUpdateMode::Row) }, thendeltais an Arrow of the updated rows. Otherwisedeltawill beOption::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" });Sourcepub fn remove_update(&self, py: Python<'_>, callback_id: u32) -> PyResult<()>
pub fn remove_update(&self, py: Python<'_>, callback_id: u32) -> PyResult<()>
Unregister a previously registered update callback with this View.
§Arguments
id- A callbackidas returned by a recipricol call toView::on_update.
§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 HasPyGilRef for View
impl HasPyGilRef for View
Source§type AsRefTarget = PyCell<View>
type AsRefTarget = PyCell<View>
Source§impl PyClassImpl for View
impl PyClassImpl for View
Source§const IS_BASETYPE: bool = true
const IS_BASETYPE: bool = true
Source§const IS_SUBCLASS: bool = false
const IS_SUBCLASS: bool = false
Source§const IS_MAPPING: bool = false
const IS_MAPPING: bool = false
Source§const IS_SEQUENCE: bool = false
const IS_SEQUENCE: bool = false
Source§type ThreadChecker = SendablePyClass<View>
type ThreadChecker = SendablePyClass<View>
Source§type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::MutableChild
type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::MutableChild
Source§type BaseNativeType = PyAny
type BaseNativeType = PyAny
PyAny by default, and when you declare
#[pyclass(extends=PyDict)], it’s PyDict.fn items_iter() -> PyClassItemsIter
fn lazy_type_object() -> &'static LazyTypeObject<Self>
fn dict_offset() -> Option<isize>
fn weaklist_offset() -> Option<isize>
Source§impl PyClassNewTextSignature<View> for PyClassImplCollector<View>
impl PyClassNewTextSignature<View> for PyClassImplCollector<View>
fn new_text_signature(self) -> Option<&'static str>
Source§impl PyMethods<View> for PyClassImplCollector<View>
impl PyMethods<View> for PyClassImplCollector<View>
fn py_methods(self) -> &'static PyClassItems
Source§impl PyTypeInfo for View
impl PyTypeInfo for View
Source§fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
Source§fn type_object(py: Python<'_>) -> &PyType
fn type_object(py: Python<'_>) -> &PyType
PyTypeInfo::type_object will be replaced by PyTypeInfo::type_object_bound in a future PyO3 versionSource§fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType>
fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType>
Source§fn is_type_of(object: &PyAny) -> bool
fn is_type_of(object: &PyAny) -> bool
PyTypeInfo::is_type_of will be replaced by PyTypeInfo::is_type_of_bound in a future PyO3 versionobject is an instance of this type or a subclass of this type.Source§fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool
object is an instance of this type or a subclass of this type.Source§fn is_exact_type_of(object: &PyAny) -> bool
fn is_exact_type_of(object: &PyAny) -> bool
PyTypeInfo::is_exact_type_of will be replaced by PyTypeInfo::is_exact_type_of_bound in a future PyO3 versionobject is an instance of this type.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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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