【问题标题】:How do I create a segmented cumulative sum graph in Palantir Foundry Workshop?如何在 Palantir Foundry Workshop 中创建分段累积和图?
【发布时间】:2022-07-05 23:32:12
【问题描述】:

我有一些代表维护工作的对象,如下所示:

Due On Status
2021-12-01 open
2022-06-17 open
2022-07-05 closed
2022-07-05 closed
2022-08-01 open
2023-09-02 open

如何在 Palantir Foundry Workshop 中生成分段累积图,绘制这样的值?

【问题讨论】:

    标签: palantir-foundry foundry-code-repositories foundry-workshop foundry-functions


    【解决方案1】:

    您可以使用 Foundry 函数来执行此操作。创建一个 TypeScript 函数存储库并使用以下代码(有关说明,请参阅内联 cmets):

    import { Function, Double, ThreeDimensionalAggregation, IRange, IRangeable, Timestamp, BucketKey, BucketValue } from "@foundry/functions-api";
    
    // Replace MaintenanceJob with your object
    // To make your object available, add it in the Settings > Ontology tab
    import { ObjectSet, MaintenanceJob } from "@foundry/ontology-api";
    
    export class MyFunctions {
        // You will find this function in Workshop after it's published
        // Replace MaintenanceJob with your object
        @Function()
        public async cumulativeJobsByMonthByStatus(jobs: ObjectSet<MaintenanceJob>): Promise<ThreeDimensionalAggregation<IRange<Timestamp>, string, Double>> {
            const bucketedJobs = await jobs
                .groupBy(j => j.dueOn.byMonth())
                .segmentBy(j => j.status.topValues())
                .count();
    
            const sortedBucketedJobs = sortBuckets3D(bucketedJobs);
            const cumulativeSortedBucketedJobs = cumulativeSum3D(sortedBucketedJobs);
    
            return cumulativeSortedBucketedJobs
        }
    }
    
    /**
     * Calculates a cumulative sum for a ThreeDimensionalAggregation over numbers, along the first axis and segmented by the second axis
     * The order of the buckets into the function matters for how the values are aggregated
     * 
     * Example input 1:
     * { buckets: [
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 123 }, { key: "closed", value: 456 }] },
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 789 }, { key: "closed", value: 910 }] },
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 314 }, { key: "closed", value: 42 }] },
     * ]}
     * 
     * Example output 1:
     * { buckets: [
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 123 }, { key: "closed", value: 456 }] },
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 912 }, { key: "closed", value: 1366 }] },
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 1226 }, { key: "closed", value: 1408 }] },
     * ]}
     * 
     * Example input 2:
     * { buckets: [
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 314 }, { key: "closed", value: 42 }] },
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 789 }, { key: "closed", value: 910 }] },
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 123 }, { key: "closed", value: 456 }] },
     * ]}
     * 
     * Example output 2:
     * { buckets: [
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 314 }, { key: "closed", value: 42 }] },
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 1103 }, { key: "closed", value: 952 }] },
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 1226 }, { key: "closed", value: 1408 }] },
     * ]}
     */
    const cumulativeSum3D = <T extends BucketKey, U extends BucketKey>(buckets: ThreeDimensionalAggregation<T, U, number>): ThreeDimensionalAggregation<T, U, number> => {
        // This holds the running total for each secondary axis value
        // e.g. you `{ open: 123, closed: 456 }` at some point in the examples above
        const cumulativeBuckets = new Map<U, number>();
        
        return {
            buckets: buckets.buckets.map(b => ({
                key: b.key,
                value: b.value.map(v => {
                    // Update the running total with the value we are seeing
                    cumulativeBuckets.set(v.key, (cumulativeBuckets.get(v.key) ?? 0) + v.value)
    
                    return {
                        key: v.key,
                        // Use the running total value
                        value: cumulativeBuckets.get(v.key)!,
                    }
                })
            }))
        }
    }
    
    /**
     * Sort buckets of a 3D aggregation by the first axis in ascending order
     * 
     * Example input:
     * { buckets: [
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 789 }, { key: "closed", value: 910 }] },
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 123 }, { key: "closed", value: 456 }] },
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 314 }, { key: "closed", value: 42 }] },
     * ]}
     * 
     * Example output 1:
     * { buckets: [
     *   { key: { min: "2021-01-01", max: "2021-12-31" }, value: [{ key: "open", value: 123 }, { key: "closed", value: 456 }] },
     *   { key: { min: "2022-01-01", max: "2022-12-31" }, value: [{ key: "open", value: 789 }, { key: "closed", value: 910 }] },
     *   { key: { min: "2023-01-01", max: "2023-12-31" }, value: [{ key: "open", value: 314 }, { key: "closed", value: 42 }] },
     * ]}
     */
    const sortBuckets3D = <T extends BucketKey, U extends BucketKey, V extends BucketValue>(buckets: ThreeDimensionalAggregation<T, U, V>): ThreeDimensionalAggregation<T, U, V> => {
        return {
            // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
            buckets: buckets.buckets.sort(({ key: k1 }, { key: k2 }) => {
                if (typeof k1 !== typeof k2) throw new Error("Inconsistent bucket key types")
    
                // If not objects, these must be either numbers or booleans which can be compared like this
                if (typeof k1 !== "object" || typeof k2 !== "object") return Number(k1) - Number(k2);
    
                // If a bucket doesn't have a minimum, it suggests that it is the global unbounded minimum bucket, so must be lower
                if (!(k1 as IRange<IRangeable>).min) return -1;
                if (!(k2 as IRange<IRangeable>).min) return 1;
    
                // Otherwise, compare both buckets' minimums
                return (k1 as IRange<IRangeable>).min!.valueOf() - (k2 as IRange<IRangeable>).min!.valueOf();
            }),
        };
    }
    

    提交您的更改并发布函数版本。 Palantir Foundry documentation on how to create a repository and publish functions 中有分步指南。

    在您的创意工坊中,您可以创建一个“图表:XY”小部件。作为数据源,选择您创建的函数并传入相关的对象集。还有Palantir Foundry documentation on using a derived aggregation in Workshop

    【讨论】:

      猜你喜欢
      • 2022-07-06
      • 1970-01-01
      • 2016-11-24
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      • 2022-07-11
      • 2022-08-03
      • 2011-11-29
      相关资源
      最近更新 更多