正規表現を使って数字表記を動的に変更する


前回の投稿からしばらく間が空きましたので、リハビリも兼ねて、軽めの記事を書きます。

今回は正規表現を使用して数字表記を動的に変更する方法について書きます。


今回使用したWorkbookはこちらから:

https://tabsoft.co/3wWjz2y


元ネタはこちら:

2020 Week 2: Can you build a beautiful and dynamic bar chart?


正規表現についての解説は参考資料が多くありますので、今回は実装についてのみ書きます。

正規表現に馴染みのない方はぜひお目通しください。正規表現はとても便利です。IF文などで書いている長々とした文字列検索/置換を簡単な実装で置き換えることができます。絶対覚えた方が良いです。


参考資料 (正規表現の基本)

参考資料 (今回使用する「先読み」と「置換」について)


参考資料 (置換についての参考資料を見つけている際に見つけました。今回扱う内容と同等の事項を扱われています)

 
やりたいこと&正規表現を使わない実装

以下のようなKPIダッシュボードがあるとします。


ここで、年月推移で表示するKPIをパラメータで変更するとします。

このとき、KPIの種類に合わせて表示されているラベルの書式を変えたいとします。

例えばSalesなら$表記、Profit Ratioなら%表記というような具合です。

更に、1000以上の数字について、カンマを付けたいとします。


正規表現を使わずに実装するとしたら、以下のような計算式を用意して、すべてラベルに入れる実装が可能です。ここでMeasune Name Parameterは整数型パラメータです。


Sales Label

CASE [Measure Name Parameter]
WHEN 1 THEN SUM([Sales])
END
Profit Ratio Label

CASE [Measure Name Parameter]
WHEN 2 THEN [Profit Ratio]
END
Orders Label

CASE [Measure Name Parameter]
WHEN 3 THEN [Orders]
END
Items Per Order Label

CASE [Measure Name Parameter]
WHEN 4 THEN [Items per Order]
END

そしてそれぞれをラベルに入れ、横一列に並ぶようにします。

各計算フィールドはパラメータの値に対応して数値を出す or NULLを返すので、4つの計算フィールドがラベルに使用されていたとしても、値が出るのは常に1つです。

そして各計算フィールドの書式設定を変更すれば、以下のようにパラメータに対応した値が、それぞれの書式設定に沿って表示されます。


この方法でも問題ありませんが、ラベル表示のために

  • 計算フィールドが表示したい指標の数だけ増える

  • 各ラベルに書式設定を行わないといけない

ことは、ちょっと面倒くさいですね。


ということで正規表現を利用して、1つの計算フィールドにまとめてしまおう、ということが今回の目的です。

 
正規表現を使った実装

正規表現を使うので、まずは文字列に変換しないといけません。

Salesで一度見てみましょう。

STR(Sales)

STR(SUM([Sales]))

文字列に変換すると、まずカンマが消えますね。そして小数点も結構な桁数が出てきます。

まずは小数点を消してみましょう。

STR(Sales, Integer)

STR(ROUND(SUM([Sales]),0))

これにカンマが付けば完璧ですね。


ここでカンマを付ける場所について確認してみましょう。

カンマを付けたい位置、言い換えれば「カンマを付けた形で置換したい数字の位置」とは、以下の条件を満たします。

  • その数字の右側には、末尾から数えて3の倍数個の数字が並んでいる 例:1234567 における1および4。


この条件を満たす文字を正規表現で見つけるために「肯定先読み」を使います。

参考資料:先読みと後読みを使ったパターン


今回の場合、上記の条件は以下で記述できます。


(?=(\d{3})+$)

  • (\d{3}): 任意の数字3文字

  • +: 直前の文字/パターンの1回以上連続

  • $: 行末/文末

なので、すべて組み合わせると上記は「任意の数字3文字の組み合わせが1回以上連続し、それが文末まで続く」パターンを探していることになります。


そしてこのパターンで探したいのは数字1文字なので、「カンマを付けた形で置換したい数字の位置」を正規表現で記述すると


\d(?=(\d{3})+$)

となります。\dは数字1文字ですね。


そして、この正規表現にマッチする文字を、カンマを付けた形で置き換えたい。

そこで置換について知らなければいけないことがあります。


参考資料


今回はマッチした数字一文字をそのまま置換してカンマを足せば良いので、最終的に以下のように置換します。

STR(Sales, Integrer, Comma)

REGEXP_REPLACE(
    STR(ROUND(SUM([Sales]),0))
    ,"\d(?=(\d{3})+$)","$0,"
)

ということで、正しくカンマを付けることができました。


これを応用して、各KPIをまとめたラベル用計算フィールドを作成します。

Measure to Show

CASE [Measure Name Parameter]
WHEN 1 THEN SUM([Sales])
WHEN 2 THEN [Profit Ratio]
WHEN 3 THEN [Orders]
WHEN 4 THEN [Items per Order]
END
Label (with RegEx)

CASE [Measure Name Parameter]
WHEN 1 THEN "$" 
    + REGEXP_REPLACE(
        STR(ROUND([Measure to Show],0))
        , "(\d)(?=(\d{3})+$)"
        , "$0,")
WHEN 2 THEN STR(ROUND(ROUND([Measure to Show],4)*100,1))+"%"
WHEN 3 THEN 
    REGEXP_REPLACE(
        STR([Measure to Show])
        , "(\d)(?=(\d{3})+$)"
        , "$0,")
WHEN 4 THEN STR(ROUND([Measure to Show],1))
END

この実装であれば、表示するKPIが増えた際にも変更する計算フィールドおよび書式設定の数を少なく保てます。

正規表現は他にも様々な応用がありますが、今回は文字列にカンマを足すための方法をご紹介しました。


 
ちなみに:小数込みの場合

ところで今回の実装については、整数を前提にする or 整数に丸めたうえで正規表現を使用した置換を行いました。


それでは小数含む1000以上の値について、同様のアプローチを試したい場合にどうなるか。見ていきましょう。


まずはそのまま、先ほどの計算フィールドからROUNDを抜きます。

STR(Sales, Float, Comma)

REGEXP_REPLACE(
    STR(SUM([Sales]))
    ,"\d(?=(\d{3})+$)","$0,"
)


カンマの付く位置がおかしいですね。ここでもう一度パターンを見てみます。


(?=(\d{3})+$)

末尾から数えているので、この状態ではカンマがつくのはむしろ小数点以降の数字になります。

ということで、少数が付く場合には別途正規表現のパターンを考えないといけないことが王道だとは思いつつ…


小数点で区切ったら良いのでは?と思いました。

以下では小数点2桁まで表示させています。

STR(Sales, Integer + Float, Comma)

REGEXP_REPLACE( //小数点2桁で丸めたあとの整数部分
    SPLIT(STR(ROUND(SUM([Sales]),2)), '.', 1)
    ,"\d(?=(\d{3})+$)","$0,"
)
+
REPLACE( //小数点2桁で丸めたあとの小数部分
    STR(ROUND(SUM([Sales]),2))
    , SPLIT(STR(ROUND(SUM([Sales]),2)),'.',1)
    , ''
)

文字列変換とSPLITを多用するので、内部の処理が複雑になっていそうな印象はありつつ…アイデアだけ置いておきます。

 
まとめ

正規表現を使った場合のテクニックをご紹介しました。

正規表現を完全に理解するのは相当難しそうですが、触りだけでも学習し知識を得ておけば、実務でも役立つケースが多いように思います。

(ちなみにWorkout Wednesdayでは、このテクニックを使った方が楽に解ける問題がたまに出ています)


質問などありましたらTwitterかLinkedinでお願いします。それでは。