2025年5月21日水曜日

X軸に時間を割り当てたバー・チャートでシリーズの積み上げを行う

Oracle APEXのバー・チャートで、X軸に時間、つまりDATE型(またはTIMESTAMP型およびその派生型)のデータを割り当て、複数のシリーズを積み上げ表示してみます。

色々なチャートの属性があり、その組み合わせで適切に表示できたりできなかったりします。適切な組み合わせとそうでない組み合わせについて、以下より紹介します。

バー・チャートのソースとしてサンプル・データセットのEMP/DEPTに含まれるビューEMP_DEPT_Vを使用します。採用月を時系列としてX軸に割り当て、部門ごとの採用人数をバー・チャートに積み上げ表示します。

意図しているバー・チャートの表示は以下のようになります。


シリーズのソースSQL問合せは以下です。
select dname, mon, sum(cnt) cnt from (
    select dname, trunc(hiredate,'Month') mon, 1 cnt from emp_dept_v
)
group by mon, dname order by mon asc, dname
列のマッピングシリーズ名は部門名である列DNAMEを割り当てます。積み上げはこの部門ごとに行われます。ラベルはX軸となる採用月MONです。時間軸として認識させるため、ラベルに割り当てる列はDATE型またはTIMESTAMP型(WITH TIME ZONEやWITH LOCAL TIME ZONE含む)である必要があります。今回は月ごとに集計するためにTRUNC関数で月単位にHIREDATEを丸めています。月への丸めにTO_CHAR関数を使用すると、VARCHAR2型になるため時間軸として扱うことができなくなるので注意が必要です。には列CNTを指定します。


チャートの属性は以下のように設定しています。

  • シリーズごとに積み上げを行うため外観積上げオンにします。
  • 複数のシリーズ間で時間軸を一致させるために、マルチシリーズ・チャート・データチャート・データのギャップを埋めるオンにします。
  • 時間軸が左から右になるように、ソート順序ラベル - 昇順にします。
  • バー・チャートなのでギャップをゼロとしてレンダリングオンでもオフでも結果は同じです。折れ線グラフの場合、ギャップをゼロとしてレンダリングするとその時刻の値が0になります。オフの場合はその時刻は無視されます。
  • 設定時間軸タイプ有効にします。時間として扱いたい場合は時間軸タイプ有効または混合頻度にします

X軸の日付データの書式は軸X書式で設定します。このラベルの書式を指定するためにソースのSELECT文でTO_CHAR関数を使用すると、時間軸として認識できなくなります。

書式が無指定(- 選択 -)のときは、自動的に書式が決まります。


書式のドロップダウン・リストから事前定義された書式を選択することもできます。また、書式を選択するとパターンを設定ができるようになります。パターンに記述するのはオラクル・データベースの日付書式ではなく、Unicode CLDR(Common Locale Data Repository)による日付フォーマット指定です。パターンを設定すると、選択した書式よりパターンが優先されます。


以上を設定すると、バー・チャートは以下のように表示されます。


隣り合うバーが重なっています。重なり合いを解消するために、初期化JavaScriptファンクションbarGapRatioを設定します。

初期化JavaScriptファンクションに以下を記述します。
function ( options ) {
    options.styleDefaults = options.styleDefaults || {};
    options.styleDefaults.barGapRatio = 0.75;
    return options;
}

以上で想定した通りにバー・チャートが表示されます。

このチャートの設定では、マルチシリーズ・チャート・データソート順序を設定しています。


時間軸でのデータのソートはOracle JETのチャートが行うため、ソースのSELECT文のORDER BY句は必ずも必要はありません。ただし、チャートのソースに関わる設定としてパフォーマンス処理する最大行数があります。

仮に最大行数に10を設定します。


ソースにORDER BYの指定があると、時間軸の最後の方がまとめて表示されません。そのため、最大行数の上限に達していることに容易に気が付きます。


ORDER BY句がないと、どのデータが処理対象外になったのか、一見しただけでは分かりません。


そのため、JETチャートが時間軸でソートするとしても、SQLでもソートしておいた方が安全でしょう。

チャートの設定時間軸タイプで、時間軸として扱われる設定は有効混合頻度間隔のスキップです。ただし、時間軸タイプ間隔のスキップを指定すると、時刻に関係なく等間隔でデータが表示されます。


積み上げチャートの場合、まったく意味のないチャートになります。


シリーズが1つだけのときに、以下のように経過時間に関係なく、データを等間隔で表示する場合に間隔のスキップを使用します。


X軸が時間軸で複数のシリーズがある場合は、マルチシリーズ・チャート・データチャート・データのギャップを埋めるオンにします。

オフの場合(時間軸タイプ間隔のスキップのときと同様に)、以下のような意味のない積み上げチャートになります。


時間軸タイプ混合頻度を選択すると、時間軸がシリーズごとに扱われます。そのため、マルチ・シリーズ・チャート・データチャート・データのギャップを埋めるオフでも、それぞれのシリーズのバーは、その採用月に表示されます。ただし、X軸の時刻の扱いがシリーズごとであるため、時間軸タイプ混合頻度の場合、積み上げはできません。


外観積上げオンにしても、以下の表示は変わらずシリーズは積み上がりません。


マルチシリーズ・チャート・データチャート・データのギャップを埋めるオフにし、ソースのSQLでギャップを埋めてみます。ビューEMP_DEPT_Vの列HIREDATEの最大値と最小値の間で、従業員の採用がない月を0で埋めます。

バインド変数P1_START_DATEおよびP1_END_DATEに、列HIREDATEの最小値および最大値を設定します。

シリーズのソースのSQL問合せとして以下を記述します。
with timeseries_v as (
    select trunc(add_months(:P1_START_DATE, level - 1), 'Month') AS mon
    from dual
    connect by level < months_between(:P1_END_DATE, :P1_START_DATE) + 1
),
dept_v as (
    select dname from emp_dept_v group by dname
),
all_timeseries_v as (
    select dname, mon from timeseries_v, dept_v
)
select dname, mon, case sum(cnt) when 0 then null else sum(cnt) end cnt from
(
    select dname, trunc(hiredate, 'Month') mon, 1 cnt from emp_dept_v
    union all
    select dname, mon, 0 cnt from all_timeseries_v
)
group by mon, dname order by mon asc, dname
ソースのSQLでギャップを埋めているため、マルチシリーズ・チャート・データチャート・データのギャップを埋めるオフでも、シリーズの積み上げができます。


チャートは以下のように表示されます。


何らかの理由でマルチシリーズ・チャート・データチャート・データのギャップを埋めるオンにできない、設定時間軸タイプ混合頻度にできない、といった際に使用を検討できます。

今回の記事は以上になります。

説明に使用したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/new-employees-by-month.zip

余談ですが、Oracle APEXでもOracle JETでも、ChatGPTに聞くと、そこそこ回答してくれます。

質問その1「Oracle JETのチャートのラベルを45度傾ける方法を教えて。」


チャートの属性の初期化JavaScriptファンクションで、xAxis.tickLabel.rotationに45を設定すれば良い、との回答です。

バー・チャートにxAxis.tickLabel.rotationという設定は存在しますが、設定できるのはnoneとautoで数値は設定できません。

質問その2「Orale JETのばー・チャートのバーの太さを変更する方法を教えて。」


チャートの属性の初期化JavaScriptファンクションで、plotArea.barGapRatioを設定すれば良い、との回答です。

barGapRatioは合っているのですが、plotAreaではなくstyleDefaultsです。

質問その3「横軸が時間の場合のステップの指定は?」


xAxisにtickStepという設定はなく、stepがあります。また、stepに設定できるのは数値のみなので、時間軸についてはステップは設定不可の模様です。

正しくない回答を取り上げましたが、それでも、正解は回答の近くにありました。Oracle JETのように誰に何を聞けばいいのかさえ分からないようなライブラリでは「マニュアルのこの辺を当たればいい。」というのが分かるだけでも大幅な時短になります。

質問が大変短いですが、私はChatGPTでたくさんAPEXについて聞いているため、ChatGPTは私の質問がAPEX関連だと判断しているようです。APEXに関する質問が履歴にないと、同じ質問でも回答の精度は大幅に落ちると思います。

Oracle APEXのアプリケーション作成の参考になれば幸いです。