Skip to main content
We’ll see how to:
  • Load normalized data in multiple tables to create a multidimensional cube.
  • Define aggregated measures to provide application-specific and high-level insights.
  • Build no-code interactive charts and tables in JupyterLab.
  • Create dashboards in the built-in web app.

Getting started

From CSV to Cube

In this part of the tutorial, you will create your first cube from a CSV file and learn multidimensional concepts such as cube, dimension, hierarchy, measure. Let’s begin by starting a new session:
We can now load the data from a CSV file into an in-memory table called a table:
We can have a look at the loaded data. They are sales from a company:
We will come back to tables in details later, for now we will use the one we have to create a cube:
That’s it, you have created your first cube! But what’s a cube exactly and how to use it?

Multidimensional concepts

A cube is a multidimensional view of some data, making it easy to explore, aggregate, filter and compare. It’s called a cube because each attribute of the data can be represented as a dimension of the cube: Multidimensional cube concept The axes of the cube are called hierarchies. The purpose of multidimensional analysis is to visualize some numeric indicators at specific coordinates of the cube. These indicators are called measures. An example of measure would be the quantity of products sold. We can list the hierarchies in our cube:
  • Dimensions
    • Sales
      • Date
        1. Date
      • Product
        1. Product
      • Sale ID
        1. Sale ID
      • Shop
        1. Shop
The cube has automatically created a hierarchy for each non numeric column: Date, Product, Sale ID and Shop. You can see that the hierarchy are grouped into dimensions. Here we have a single dimension called Sales, which is the name of the table the columns come from. We will see how to move hierarchies between dimensions later. Hierarchies are also made of levels. Levels of the same hierarchy are attributes with a parent-child relationship. For instance, a city belongs to a country so Country and City could be the two levels of a Geography hierarchy. At the moment, we only have single-level hierarchies.
Let’s have a look at the measures of the cube that have been inferred from the data:
  • Measures
    • Quantity.MEAN
      • formatter: DOUBLE[#,###.00]
    • Quantity.SUM
      • formatter: DOUBLE[#,###.00]
    • Unit price.MEAN
      • formatter: DOUBLE[#,###.00]
    • Unit price.SUM
      • formatter: DOUBLE[#,###.00]
    • contributors.COUNT
      • formatter: INT[#,###]
The cube has automatically created the sum and mean aggregations for all the numeric columns of the dataset. Note that a measure isn’t a single result number, it’s more a formula that can be evaluated for any coordinates of the cube. For instance, we can query the grand total of Quantity.SUM, which means summing the sold quantities over the whole dataset: Grand total
But we can also dice the cube to get the quantity for each Shop, which means taking one slice of the cube for each Shop: Dicing the cube
We can slice on a single Shop: Slicing the cube
We can dice along 2 different axes and take the quantity per product and date. Pivot table
We can even combine these operations to slice on one hierarchy and dice on the two others: Slice and dice

Interactive widget

So far we have used cube.query() which returns a pandas DataFrame but a better way to visualize multidimensional data is a pivot table. With Atoti’s JupyterLab extension, you can create interactive widgets such as pivot tables and charts directly into your notebook. This will create a widget and open the Atoti tab on the left with tools to manipulate the widget. Let’s start by creating a pivot table:
  • Run session.widget.
  • In the left panel, click on a measure such as Quantity.SUM to add it.
  • Click on a hierarchy such as Date to get the quantity per date.
  • Drag and drop another hierarchy such as Product to the Columns section to get the quantity sold per day and per product.
First pivot table Tables can be switched to charts. For instance let’s switch to a line chart. First chart

Drilldown and filters

Multidimensional analysis is meant to be done from top to bottom: start by visualizing the indicators at the top level then drilldown to explain the top figures with more details. For instance, we can visualize some measures per date then drilldown on Shop for a specific date, then see the products sold by a specific shop on this date. Using the previous cube representation, this is like zooming more and more on a part of the cube. Drilldown the cube Drilldown Hierarchies can be filtered when building widgets. Let’s apply a filter on the previous chart and only visualize the quantity for a group of selected products. Chart filter

Dashboarding app

Being able to quickly build widgets inside a notebook without coding is nice to rapidly explore the data, iterate on your model and share some results. However, to provide richer insights, dashboards are even better. That’s why Atoti comes with a web app that can be accessed outside of the notebook and where widgets can be laid out to form dashboards. The app can be accessed with this link:
Widgets built in the notebook can be saved in the app by right clicking on them and selecting Save widget in app. They will then be available in the Saved widgets section. Saving widget in app

Enriching the cube

In the previous section, you have learned how to create a basic cube and manipulate it. We will now enrich this cube with additional attributes and more interesting measures.

Join

Currently, we have very limited information about our products: only the ID. We can load a CSV containing more details into a new table:
Note that a table can have a set of keys. These keys are the columns which make each line unique. Here, it’s the product ID. If you try to insert a new row with the same keys as an existing row, it will override the existing one.
This table contains the category, subcategory, size, color, purchase price and brand of the product. Both tables have a Product column we can use to join them.
Note that this is a database-like join and not a pandas-like join. All the details from products_table won’t be inlined into sales_table. Instead, this just declares a reference between these two tables that the cube can use to provide more analytical axes. You can visualize the structure of the session’s tables:
The new columns have been automatically added to the cube as hierarchies, in a dimension with the same name as the new table:
  • Dimensions
    • Products
      • Brand
        1. Brand
      • Category
        1. Category
      • Color
        1. Color
      • Size
        1. Size
      • Sub category
        1. Sub category
    • Sales
      • Date
        1. Date
      • Product
        1. Product
      • Sale ID
        1. Sale ID
      • Shop
        1. Shop
You can use them directly in a new widget. For instance, let’s create a bar chart to visualize the mean price per subcategory of product: Price per category We can also make a donut chart to see how all the sales are distributed between brands: Donut chart brands Note that after the join we can add a new measure called Purchase price.VALUE based on the corresponding column of the joined table. This measure represents the value of the column so it is only defined when all the keys of the joined table are expressed in the query.
For instance we can check the purchase price per Product:
In a similar way, we can enrich the data about the shops:

New measures

So far we have only used the default measures which are basic aggregations of the numeric columns. We can add new custom measures to our cube.

Max

We’ll start with an aggregation taking the maximum price of the sales table:
This new measure is directly available:

Fact-level operations

To compute aggregates based of data which comes directly from the columns of a table, you can pass the calculation directly to the desired aggregation function. This is more efficient than first converting the columns to measures before the aggregation. Let’s use this to compute the total amount earned from the sale of the products, as well as the average.
We can now plot the evolution of the sales per country over time: Amount per country over time

Margin

Now that the price of each product is available from the products table, we can compute the margin. We use the OriginScope to perform the multiplication of the quantity sold by the purchase price for each Product and then do the sum.
We can also define the margin rate which is the ratio of the margin by the the sold amount:
Let’s use this margin rate to do a Top 10 filter to see the products with the best rate. Note that you don’t need to put the rate measure and the product level in the pivot table to apply the filter. top10 filter on the margin rate

Cumulative sum over time

A cumulative sum is the partial sum of the data up to the current value. For instance, a cumulative sum over time can be used to show how some measure changes over time.
Cumulative amount

Average per shop

Aggregations can also be combined. For instance, we can sum inside a Shop: then take the average of this to see how much a table sales on average:

Multilevel hierarchies

So far, all our hierarchies only had one level but it’s best to regroup attributes with a parent-child relationship in the same hierarchy. For example, we can group the Category, SubCategory and Product ID levels into a Product hierarchy:
And let’s remove the old hierarchies:
  • Dimensions
    • Products
      • Brand
        1. Brand
      • Color
        1. Color
      • Product
        1. Category
        2. Sub category
        3. Product
      • Size
        1. Size
    • Sales
      • Date
        1. Date
      • Product
        1. Product
      • Sale ID
        1. Sale ID
      • Shop
        1. Shop
    • Shops
      • City
        1. City
      • Country
        1. Country
      • Shop size
        1. Shop size
      • State or region
        1. State or region
We can also do it with City, State or Region and Country to build a Geography hierarchy. Note that instead of using existing levels you can also define a hierarchy with the columns of the table the levels will be based on:
As we are restructuring the hierarchies, let’s use this opportunity to also change the dimensions. A dimension regroups hierarchies of the same concept. We can move the new Geography hierarchy to its own dimension:
  • Dimensions
    • Location
      • Geography
        1. Country
        2. State or region
        3. City
    • Products
      • Brand
        1. Brand
      • Color
        1. Color
      • Product
        1. Category
        2. Sub category
        3. Product
      • Size
        1. Size
    • Sales
      • Date
        1. Date
      • Product
        1. Product
      • Sale ID
        1. Sale ID
      • Shop
        1. Shop
    • Shops
      • Shop size
        1. Shop size
With that, we can define new measures taking advantage of the multilevel structure. For instance, we can create a measure indicating how much a product contributes to its subcategory:
Percent of parent

Polishing the cube

Deleting or hiding measures

Some measures have been automatically created from numeric columns but are not useful. For instance, Unit Price.SUM does not really make sense as we never want to sum the unit prices. We can delete it:
Other measures have been used while building the project only as intermediary steps but are not useful to the end users in the app. We can hide them from the UI (they will remain accessible in Python):

Measure folders

Measures can be rearranged into folders.
  • Measures
    • 📁 Amount
      • Amount.MEAN
        • formatter: DOUBLE[#,###.00]
      • Amount.SUM
        • formatter: DOUBLE[#,###.00]
      • Average amount per shop
        • formatter: DOUBLE[#,###.00]
      • Cumulative amount
        • formatter: DOUBLE[#,###.00]
      • Percent of parent amount
        • formatter: DOUBLE[#,###.00]
    • Margin
      • formatter: DOUBLE[#,###.00]
    • Margin rate
      • formatter: DOUBLE[#,###.00]
    • Max price
      • formatter: DOUBLE[#,###.00]
    • Purchase price.VALUE
      • formatter: DOUBLE[#,###.00]
    • Quantity.MEAN
      • formatter: DOUBLE[#,###.00]
    • Quantity.SUM
      • formatter: DOUBLE[#,###.00]
    • Unit price.MEAN
      • formatter: DOUBLE[#,###.00]
    • contributors.COUNT
      • formatter: INT[#,###]

Measure formatters

Some measures can be formatted for a nicer display. Classic examples of this is changing the number of decimals or adding a percent or a currency symbol. Let’s do this for our percent of parent amount and margin rate:

Before

After

Simulations

Simulations are a way to compare several scenarios and do what-if analysis. This helps understanding how changing the source data or a piece of the model impact the key indicators. In Atoti, the data model is made of measures chained together. A simulation can be seen as changing one part of the model, either its source data or one of its measure definitions, and then evaluating how it impacts the following measures.

Source simulation

Let’s start by changing the source. With pandas or Spark, if you want to compare two results for a different versions of the entry dataset you have to reapply all the transformations to your dataset. With Atoti, you only have to provide the new data and all the measures will be automatically available for both versions of the data. We will create a new scenario using pandas to modify the original dataset.
For instance, we can simulate what would happen if we had managed to purchase some products at a cheaper price.
We can now load this new dataframe into a new scenarios of the products table.
The session now has two scenarios and the only differences between them are the lines corresponding to the products with better prices, everything else is shared between the scenarios and has not been duplicated: source scenarios in Atoti are memory-efficient. Source simulation Using the Source Simulation hierarchy we can display the margin of the scenario and compare it to the base case. Source simulation comparison Note that all the existing measures are immediately available on the new data. For instance, the margin rate still exists, and we can see that in this scenario we would have a better margin for the Furniture products. Margin rate per product category and scenario

Parameter simulations

The other simulation technique is to create a parameter measure whose value can be changed for some coordinates. When creating the simulation, you can choose at which granularity the modification applies. For instance we can create a parameter measure whose value will change depending on the country. Doing that, we can answer questions such as “What happens if there is a crisis in France and we sell 20% less?”
This has created a measure named Country parameter and added it to the cube. For now, its value is 1 everywhere, but using the country_simulation we can change that. By adding values in the table you can change the value of the parameter measure depending on the levels used in the simulation and the scenario.
Let’s replace the existing Quantity.SUM and Amount.SUM measures with new ones using the parameter measure from the simulation.
We can query the cube using the new Country Simulation level to compare the quantity and amount between the base case and our new scenario:
Here for example, as the amount has been modified, the measures depending on it such as the cumulative amount are also impacted:
Let’s try adding a different scenario:
The two scenarios can be visualized in the same widget: Cumulative amount per scenario Finally, we can even combine the different simulations (the source one and the measure one) to create a matrix of scenarios: Matrix of scenarios

Going further

You’ve learned all the basics to build a project with Atoti, from the concept of multidimensional analysis to powerful simulations. We now encourage you to try the library with your own data. You can also start to learn more advanced features such as SessionConfig, endpoint(), and array.