注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
グラフ XY ウィジェットで必要な機能や書式設定を実現できない場合は、Vega Chart ウィジェットの使用を検討してください。
グラフ XY ウィジェットは、オブジェクトをインタラクティブなグラフとして可視化するために使用します。グラフ XY ウィジェットを設定するモジュール作成者は、次のことができます。
以下のスクリーンショットは、Flight Alerts データを表示する、設定済みの3個のグラフ XY ウィジェットの例です。

以下の画像では、青い矢印の左側に、新しく追加された(まだ設定されていない)グラフ XY ウィジェットと、その初期設定パネルが表示されています。青い矢印の右側には、元データとなる Flight Alerts のオブジェクトセットがすでに入力された、個別のレイヤー設定パネルが表示されています。

グラフ XY ウィジェットにデータを追加するには、レイヤーの設定が必要です。レイヤーでは、次の設定オプションを利用できます。
グラフ XY の一部の設定オプションは、ウィジェットの設定パネルではなく、実行時にグラフのツールバーから利用できます。
グラフレイヤーで日付またはタイムスタンプのプロパティを X 軸のプロパティとして使用し、関数ベースではなく基本集計を使用している場合、実行時にグラフのツールバーに歯車(設定)ボタンが表示されます。このボタンは、これらの条件を満たす場合にのみ表示されます。このアイコンを選択すると、バケットサイズのセレクターが開き、タイムスタンプ値を日、週、月などの単位でどのようにグループ化するかを設定できます。
上記のレイヤーの設定オプションに加えて、Chart XY のメイン設定パネルには、チャート全体の設定オプションがあります。
タイトルを表示
数値の形式設定の有効化
並べ替え
関数ベースのレイヤーを設定するには、TwoDimensionalAggregation または ThreeDimensionalAggregation を返す関数を記述する必要があります。
このセクションのコード例は、TypeScript v1、TypeScript v2、Python で利用できます。関数のバージョンに対応するタブを選択します。続く2つの具体例は TypeScript のみで示しています。Python で記述する場合は、Python 関数でカスタム集計を作成するに沿って進めてください。TypeScript v1 では、エクスポートされたクラスのメソッドとして各関数を定義し、@foundry/functions-api の @Function() デコレーターを付けます。TypeScript v2 では、各関数をファイルのデフォルトエクスポートとして定義し、@osdk/functions から型をインポートします。詳しい比較については、TypeScript v1 と TypeScript v2 の比較を参照してください。
どの関数バージョンにも、次の型パラメーターを持つ2つの集計型が存在します:TwoDimensionalAggregation<Key, Value> と ThreeDimensionalAggregation<Key, Segment, Value>。ただし、TypeScript v1 ではバケットをオブジェクトの buckets キーの下に格納するのに対し、TypeScript v2 ではバケットの配列をそのまま返します。3次元集計では、TypeScript v2 は内側の各バケット配列も value キーではなく groups キーの下に格納します。Python ではラッパーを保持し、functions.api の SingleBucket クラスと NestedBucket クラスから構築します。これらの違いが適用されるのは、関数本文内で構築するリテラルのみです。宣言された戻り値の型が同じであれば、どのバージョンでも同じ出力にシリアライズされるため、移行後にウィジェットを変更する必要はありません。TypeScript v1 の { buckets: ... } リテラルが残っていると、TypeScript v2 の配列型の ThreeDimensionalAggregation を満たさないため、関数内で失敗します。
次の最小限の関数は、各バージョンで同じ2次元集計を返します。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13import { Double, Function, TwoDimensionalAggregation } from "@foundry/functions-api"; export class MyFunctions { @Function() public myTwoDimensionalAggregation(): TwoDimensionalAggregation<string, Double> { return { buckets: [ { key: "bucket1", value: 5.0 }, { key: "bucket2", value: 6.0 }, ], }; } }
Copied!1 2 3 4 5 6 7 8 9 10import { Double, TwoDimensionalAggregation } from "@osdk/functions"; function myTwoDimensionalAggregationFunction(): TwoDimensionalAggregation<string, Double> { return [ { key: "bucket1", value: 5.0 }, { key: "bucket2", value: 6.0 }, ]; } export default myTwoDimensionalAggregationFunction;
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15from functions.api import ( function, Double, TwoDimensionalAggregation, SingleBucket ) @function def my_two_dimensional_aggregation_function() -> TwoDimensionalAggregation[str, Double]: return TwoDimensionalAggregation( buckets=[ SingleBucket(key="bucket1", value=Double(5.0)), SingleBucket(key="bucket2", value=Double(6.0)), ] )
3次元集計では、最上位の各バケット内に2つ目のバケットリストをネストします。TypeScript v1 では、最上位のバケットを buckets キーの下に格納し、ネストされたリストを value の下に格納します。TypeScript v2 では両方の階層が変わります。配列をそのまま返し、最上位の各エントリーはネストされたリストを groups の下に格納するため、value がキーになるのは最も内側の階層のみです。Python では、最上位の各バケットは NestedBucket であり、その buckets 引数に内側の SingleBucket リストを格納します。各型の完全な形式については、集計型のリファレンスを参照してください。
TypeScript v1 では、グループ化された集計の結果自体が TwoDimensionalAggregation であるため、.groupBy(...).sum(...) を直接返すことができます。TypeScript v2 ではそうではありません。.aggregate() はグループごとに1行のフラットな行配列を返し、それらの行を TwoDimensionalAggregation に変換するヘルパーはありません。.aggregate() 呼び出しに集計型の型注釈を付けると型エラーになるため、関数自体で行から戻り値を構築する必要があります。各グループの値は row.$group.<groupByKey> から、メトリクスは row.<propertyApiName>.sum から読み取ります。このメトリクスはプロパティの null 許容性を引き継ぐため、未定義になる可能性があります。
以下の TypeScript v2 の例には、さらに3つの違いが反映されています。
min と max を持つ IRange<Timestamp> を使用します。TypeScript v2 でこれに相当するのは、startValue と endValue を持つ Range<TimestampISOString> です。関数の型である Range と、[start, end] タプルの配列であるグループ化入力 $ranges を混同しないでください。$duration のグループ値は範囲ではなく、バケットの開始を示す単一のスカラー値であるため、この例では1日単位の各バケットの終了を独自に導出します。集計対象のプロパティに値がない場合はメトリクスが存在しないため、各メトリクスに ?? 0 のデフォルト値が必要です。$select エントリーの "unordered" 値は、結果の行順序が定義されていないことを意味し、別々の2回の .aggregate() 呼び出しで、グループが同じ順序で返される保証はありません。そのため、この例では位置ではなくグループ値に基づいて、分子と分母の結果を結合します。TypeScript v2 の関数にはクラスがないため、TypeScript v1 の private divide メソッドは、TypeScript v2 では同じファイル内のモジュールレベルの関数になります。
以下は、ある時系列を別の時系列で割った結果をチャートに表示するために、TwoDimensionalAggregation を返す完全な例です。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33import { Double, Function, TwoDimensionalAggregation, ThreeDimensionalAggregation, IRange, Timestamp } from "@foundry/functions-api"; import { ObjectSet, MyObjectType } from "@foundry/ontology-api" export class TimeseriesAggregations { @Function() public async percentOfTotal(objects:ObjectSet<MyObjectType>): Promise<TwoDimensionalAggregation<IRange<Timestamp>, Double>> { const numerators = await objects.groupBy(e => e.date.byDays()) .sum(e => e.value); const denominators = await objects.groupBy(e => e.date.byDays()) .sum(e => e.total); return this.divide(numerators, denominators); } private divide(numerators:TwoDimensionalAggregation<IRange<Timestamp>, Double>, denominators: TwoDimensionalAggregation<IRange<Timestamp>, Double>): TwoDimensionalAggregation<IRange<Timestamp>, Double> { const percentage = numerators.buckets.map((bucket, i) => { const numerator = bucket.value; const denominator = denominators.buckets[i].value; if (denominator == 0) { return { key: bucket.key, value: 0 }; } return { key: bucket.key, value: numerator / denominator } }); return { buckets: percentage }; } }
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57import { ObjectSet } from "@osdk/client"; import { Double, Range, TimestampISOString, TwoDimensionalAggregation } from "@osdk/functions"; import { MyObjectType } from "@ontology/sdk"; const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; // $duration のグループ値はバケットの開始のみを示すため、Range キーとして使用するには、 // 1日単位のバケットの終了を算出する必要があります。 function toDayRange(start: Date): Range<TimestampISOString> { const end = new Date(start.getTime() + ONE_DAY_IN_MS); return { startValue: start.toISOString(), endValue: end.toISOString() }; } function divide( numerators: TwoDimensionalAggregation<Range<TimestampISOString>, Double>, denominators: Map<string, Double> ): TwoDimensionalAggregation<Range<TimestampISOString>, Double> { return numerators.map(bucket => { const numerator = bucket.value; const denominator = denominators.get(bucket.key.startValue!) ?? 0; if (denominator === 0) { return { key: bucket.key, value: 0 }; } return { key: bucket.key, value: numerator / denominator }; }); } async function percentOfTotal( objects: ObjectSet<MyObjectType> ): Promise<TwoDimensionalAggregation<Range<TimestampISOString>, Double>> { const numeratorRows = await objects.aggregate({ $select: { "value:sum": "unordered" }, $groupBy: { date: { $duration: [1, "days"] } }, }); const denominatorRows = await objects.aggregate({ $select: { "total:sum": "unordered" }, $groupBy: { date: { $duration: [1, "days"] } }, }); const numerators = numeratorRows .filter(row => row.$group.date != null) .map(row => ({ key: toDayRange(new Date(row.$group.date!)), value: row.value.sum ?? 0, })); // 2つの集計には順序がなく、グループが異なる順序で返される可能性があるため、 // 行の位置ではなくバケットの開始をキーにします。 const denominators = new Map<string, Double>( denominatorRows .filter(row => row.$group.date != null) .map(row => [new Date(row.$group.date!).toISOString(), row.total.sum ?? 0]), ); return divide(numerators, denominators); } export default percentOfTotal;
TypeScript v1 では、.segmentBy() によって2つ目の集計次元を生成します。TypeScript v2 には独立したセグメント句がありません。利用可能な構文で最も近いものは、同じ $groupBy オブジェクト内の2つ目のキーであり、以下の例ではこれを使用しています。複数キーの $groupBy は、各日付バケット内にセグメントをネストするのではなく、両方のキーで同時にグループ化します。$groupBy に2個以上のキーが含まれると、並べ替えは許可されなくなり、すべての $select 値を "unordered" にする必要があります。結果は引き続きフラットな行配列であり、グループ値の組み合わせごとに1行になります。TypeScript v2 の例では、行を返す前に対応する日付バケットの下にネストし、位置ではなくグループ値に基づいて分子と分母の結果を結合します。
TypeScript v1 の .topValues() は、TypeScript v2 の "exact" 戦略に対応しますが、デフォルトのバケット数は増加しています。TypeScript v1 では1,000、TypeScript v2 では10,000です。グループ化が正確であるのは、プロパティの一意の値の数がこの件数以内に収まる間のみです。この件数を超えると、返されるグループからオブジェクトが除外され、集計は近似になります。プラットフォームは各結果が正確だったか近似だったかを報告しますが、TypeScript のオントロジー SDK はそのフィールドを公開していません。カーディナリティの高いプロパティでは、{ $exactWithLimit: n } を使用して件数を増やします。
以下は、segmentBy() が返す値ごとに別々の系列をチャートに表示する ThreeDimensionalAggregation を返す完全な例です。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34import { Double, Function, TwoDimensionalAggregation, ThreeDimensionalAggregation, IRange, Timestamp } from "@foundry/functions-api"; import { ObjectSet, MyObjectType } from "@foundry/ontology-api" export class TimeseriesAggregations { @Function() public async percentOfTotalSegmented(objects:ObjectSet<MyObjectType>): Promise<ThreeDimensionalAggregation<IRange<Timestamp>, string, Double>> { const numerators = await objects.groupBy(e => e.date.byDays()) .segmentBy(e => e.groupId.topValues()) .sum(e => e.value); const denominators = await objects.groupBy(e => e.date.byDays()) .segmentBy(e => e.groupId.topValues()) .sum(e => e.total); return this.divideThreeDimensional(numerators, denominators); } private divideThreeDimensional(numerators:ThreeDimensionalAggregation<IRange<Timestamp>, string, Double>, denominators: ThreeDimensionalAggregation<IRange<Timestamp>, string, Double>): ThreeDimensionalAggregation<IRange<Timestamp>, string, Double> { var percentage = numerators.buckets; //コピー for (let i = 0; i < numerators.buckets.length; i++) { for (let j = 0; j < numerators.buckets[i].value.length; j++) { percentage[i].value[j].value = numerators.buckets[i].value[j].value / denominators.buckets[i].value[j].value; } } return { buckets: percentage }; } }
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88import { ObjectSet } from "@osdk/client"; import { Double, Range, ThreeDimensionalAggregation, TimestampISOString } from "@osdk/functions"; import { MyObjectType } from "@ontology/sdk"; const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; // $duration のグループ値はバケットの開始のみを示すため、Range キーとして使用するには、 // 1日単位のバケットの終了を算出する必要があります。 function toDayRange(start: Date): Range<TimestampISOString> { const end = new Date(start.getTime() + ONE_DAY_IN_MS); return { startValue: start.toISOString(), endValue: end.toISOString() }; } // aggregate() は日付とセグメントの組み合わせごとにフラットな行を1行返すため、 // ネストされた3次元の形式に合わせるには、行を再グループ化する必要があります。 function nestSegmentsByDate( rows: Array<{ date: Date; segment: string; value: Double }> ): ThreeDimensionalAggregation<Range<TimestampISOString>, string, Double> { const byDate = new Map<number, Array<{ key: string; value: Double }>>(); for (const row of rows) { const segments = byDate.get(row.date.getTime()) ?? []; segments.push({ key: row.segment, value: row.value }); byDate.set(row.date.getTime(), segments); } return Array.from(byDate, ([start, groups]) => ({ key: toDayRange(new Date(start)), groups, })); } function divideThreeDimensional( numerators: ThreeDimensionalAggregation<Range<TimestampISOString>, string, Double>, denominators: Map<string, Double> ): ThreeDimensionalAggregation<Range<TimestampISOString>, string, Double> { return numerators.map(bucket => ({ key: bucket.key, groups: bucket.groups.map(segment => { const denominator = denominators.get(`${bucket.key.startValue}|${segment.key}`) ?? 0; return { key: segment.key, value: denominator === 0 ? 0 : segment.value / denominator, }; }), })); } async function percentOfTotalSegmented( objects: ObjectSet<MyObjectType> ): Promise<ThreeDimensionalAggregation<Range<TimestampISOString>, string, Double>> { const numeratorRows = await objects.aggregate({ $select: { "value:sum": "unordered" }, $groupBy: { date: { $duration: [1, "days"] }, groupId: "exact", }, }); const denominatorRows = await objects.aggregate({ $select: { "total:sum": "unordered" }, $groupBy: { date: { $duration: [1, "days"] }, groupId: "exact", }, }); const numerators = nestSegmentsByDate( numeratorRows .filter(row => row.$group.date != null) .map(row => ({ date: new Date(row.$group.date!), segment: row.$group.groupId, value: row.value.sum ?? 0, })), ); // 2つの集計には順序がなく、グループが異なる順序で返される可能性があるため、 // 行の位置ではなく日付とセグメントをキーにします。 const denominators = new Map<string, Double>( denominatorRows .filter(row => row.$group.date != null) .map(row => [ `${new Date(row.$group.date!).toISOString()}|${row.$group.groupId}`, row.total.sum ?? 0, ]), ); return divideThreeDimensional(numerators, denominators); } export default percentOfTotalSegmented;
Python では、functions.api の TwoDimensionalAggregation クラスと ThreeDimensionalAggregation クラスを使用して、同じチャートを記述します。Python のオントロジー SDK には、TypeScript v2 にはない変換機能もあり、Python のオントロジー SDK の v2 を使用する場合にのみサポートされます。TwoDimensionalAggregation.from_osdk() と ThreeDimensionalAggregation.from_osdk(result, "date", "groupId") は、グループ化された集計結果をウィジェットが想定するバケット構造に変換します。どちらもグループ化に使用するプロパティのAPI名を受け取ります。このAPI名は、周囲の Python 識別子が snake_case であっても、camelCase のままです。Python の例については、Python 関数でカスタム集計を作成するを参照してください。
TSv1 のその他の例については、Functions ドキュメントのオブジェクトセットの集計とカスタム集計を作成するを参照してください。