Numeric Array Calculation Workshop Workflow

Hello!

Please tell me there is a better / cooler way. I do have some functions but have not had much luck given the volume and complexity of data. I want to know if there is an easy button for array calculations / way to do calculations on numeric arrays w/ functions. If so, function examples …ect.

First off, I have a workflow as follows:

  1. User selects a specific object they are interested in out of millions. (from a large DB)

  2. User “adds” the selection of said object to a list within Workshop that they deem interested in. (project items that include cost…ect)

  3. The list contains both ints & strings , but require an array because there are multiple object properties. (single selection is easy, can just use metrics/ect)

  4. Create variable int/string array that holds the selected obj items user selected. (Metric cards can’t calculate array values?)

  5. Create an empty obj with T.structs in code repo that have empty int array/string array cols/parameters. (fusion sheet has no int array option for a new dataset for an obj). When the user clicks a button called “create project” …the action form of the empty parameters takes the selected property arrays from the old objects they selected from the list from steps 1-4.

  6. New objects are created and take in the selected object properties and is created with materializations updating.

  7. Create a code repo to input the materialization of user inputs and perform calculations on said array data into a new obj with new python calculations that give product cost/advanced calculations for random project outcomes.

  8. Add a next button that goes to a new page and the user selects their project name and see the calculations from the materializations (the new obj from materialization takes the project name column from the input form in Workshop and makes that the new name for each object so users can view the advanced calcs in Workshop. Basically, users don’t know they are going to a different object and never see the calculations on the materializations.

Eek!!

I’m confused about going from Step 2 to Step 3. Why does the list contain strings and integers instead of (a) objects or (b) strings that represent object primary keys? Seems like that would be way simpler as a starting point.

Overall, this feels pretty complicated and I have a hunch it would be easier to parse and provide feedback on if you add a high-level summary of the workflow you want to enable in addition to the more detailed steps you’ve already provided.

Attempting to do complex calculations on specific object properties from the objects a user selects. User selects (Obj 1 - Rand Obj) in Workshop. Trying to do calculations on specific properties within the specific selected objects. Metric widget can’t take numeric arrays or list of ints. I have a list of costs: 1, 30,000, 24,000…ect. Trying to run simulations / models on the user selected obj property like cost and generate some outcome. @taylor Also, thanks for the reply !!

I’m still pretty confused. Here’s where I’m coming from:

  • In workshop, opt for simpler data structures when possible. In this case, instead of dealing with arrays of things, for which you don’t have a ton of capabilities for in Workshop, let the user pick the object they care about and ship those objects off to a TypeScript Function.
  • In the Function, get whatever properties you care about and compute whatever aggregation you need
  • Then return the aggregation and display it in the app

It seems like that isn’t what you’re after…but that’s where I get confused

Was trying to avoid Typescript and just use some Python modules for some more advanced computation…ect .

However, for this use case, I believe I found :" Configure multiple function-backed properties" fits the bill in the documentation. What you eluded to sounds like the most straightforward way. Thanks!

Yeah, I would stick with TypeScript in this case, even if it’s a bit unfamiliar. One thing I do is to leverage GitHub copilot to write code against similar notional data structures, especially if it uses an NPM package I’m unfamiliar with. Then it’s not so hard to translate to the Functions-on-Objects syntax.

1 Like

Thanks for the response . Follow on question.

To create a function based on unique user input/selection from a multi select table. Still having issues linking a function to the specific user selection. How would I do this in Typescript , as you described? @taylor

In Workshop, the user selection should result in an Object Set variable. In that case I would pass that Object Set into your Function.

It’s a little hard to follow what you’re building, but adding columns doesn’t strike me as the obvious choice if you’re computing some metrics for only certain objects. The Function-backed property would be computed for all objects in that table whether they’re selected or not.

1 Like

Following up here with a simple example:

Workshop Object Table Object Set and Function-backed Aggregation

As Taylor mentioned, the Object Table widget stores a user’s selection(s) in an Object Set variable. In this example, it’s called “(o) Selected Ticket Sales”.

In the Metric Card widget above, I want the right-most card to display the average ticket price (simple example) for the selected tickets. To do this, I create a new Number variable that calls a TypeScript function I define, test, and publish in Code Repositories.

This function accepts an input called tickets, but its type is an array/list of the compatible or expected Object Type (in this case, TicketSale[] or Array<TicketSale>). When defining this Number variable on Workshop, I then pass in the (o) Selected Ticket Sales variable from earlier.

1 Like

@josh I believe this is for per-object calculations, not an overall aggregate, but @Ngundrum can chime in with any corrections

This is correct, how would you do it per object, not an aggregation. This is a great example though @josh @taylor

Each object has a unique calc we are interested in ** . After we get individual calcs per obj, we would then aggregate later . Want to see individual impact to each object, prior to aggregate (if that makes sense)

Could do individual selection and aggregate later…ect. Have to change user exp first though

I think you need two functions that work in parallel. One that produces a new column(s) with the per-object calculations. Another that accepts the selected objects and recomputes those same calculations before it then computes the aggregate you care about. This is because I think that Workshop won’t let you use a Function-backed column in an object table as an input to a metric. In other words, the values of a property for all objects in an object set is not a valid variable type.

1 Like

Hmm, I think you confirmed our suspicions. An edge case that concerns us is a user selecting 50+ objects to see massive impact on projects above X millions / billions of dollars. The individual selection would require a lot of pre built functions to take or match X users selection? We can make a work flow that allows the user to individually select X projects and get individual computation. However, we feel the experience for the user is clunky and not intuitive. Prefer to have the user select all at once.

With that being said, I think I may ditch functions and use the materialization workflow.

@taylor @josh Thanks for the insight , I did learn a lot!!

It’s not clear to me where the issue lies with selecting 50+ objects, but you’re welcome to leverage materializations instead. One thing to consider is using Quiver dashboards with materializations (charts, metrics, tables, etc. made with materializations in Quiver), which would act like Contour (scale, logic) and which are embeddable in Workshop. That would be instead of Python transforms on a materialization.

1 Like

How would this work exactly?

You’ll need to author custom TypeScript functions. Once published, the function can be called in the Object Table widget configuration. Below are two TypeScript functions that serve as examples; feel free to copy-paste the code and refactor for your use case. You’ll need to import FunctionsMap from “@foundry/functions-api” at the top of your TS file.

1A. Single Column TypeScript Function

@Function()
public ticketSaleOneColumn(
    sales: ObjectSet<TicketSale>
): FunctionsMap<TicketSale, Double> {
    const map = new FunctionsMap<TicketSale, Double>();
    const discount = 0.25;
    sales.all().forEach(sale => {
        map.set(
            sale,
            sale.ticketPrice! * (1 - discount)
        );
    });
    return map;
}

1B. Multiple Column TypeScript Function
If you need to store more than one aggregation, you’ll need to create a custom interface (what you name it is arbitrary and up to you). Each field/entry in this interface will correspond to a new column on the frontend. Use this interface in the function’s return type and returned value.

// This should be written OUTSIDE your exported class
interface CustomReturnColumns {
    appliedDiscount: Double;
    reducedPrice: Double;
    expectedInflation: Double;
    inflatedPrice: Double;
}

// ... other code here

// This function should be written INSIDE your exported class
@Function()
public ticketSaleMultipleColumns(
    sales: ObjectSet<TicketSale>
): FunctionsMap<TicketSale, CustomReturnColumns> {    
    const map = new FunctionsMap<TicketSale, CustomReturnColumns>();
    const discount = 0.25;
    const inflationRate = 0.07;
    sales.all().forEach(sale => {
        map.set(
            sale,
            {
                appliedDiscount: discount,
                reducedPrice: sale.ticketPrice! * (1 - discount),
                expectedInflation: inflationRate,
                inflatedPrice: sale.ticketPrice! * (1 + inflationRate)
            }
        )
    });
    return map;
}

2. Invoking Published Function(s) in Workshop

Function-backed Columns in Workshop

Note that these custom functions that add these column(s) do not store the data in the Ontology; their utility is limited to displaying computed values in the frontend.

Palantir’s official documentation on this can be found here.