Tableau×落とし穴 ~小数点~
DATA Saber になるための試練に、「Tableau Community Forumsに投稿されている質問に回答する」というものがあります。
そこでこれなら回答できるかな、で選んだ質問がこちら。
ここから沼にはまっていきました。
Tableauの小数点の扱い=浮動小数点数
Tableauは小数点を「浮動小数点数」で持っています。
浮動小数点数とは
ざっくり説明すると、コンピュータで処理しやすくするように、小数点に見えても実は裏で機械語に翻訳しているのでちょっとした誤差が生じる、といった感じでしょうか。
ここで、同じくDATA Saber Bridgeに挑戦中の同期?のこんなツイートが。
Tableauで浮動小数点数を乗り越える
おなじみの「サンプルスーパーストア」を使います。
購買データの明細ごとに設定されている割引率が、市区町村単位で集計した場合、どうなっているか可視化してみます。この場合の「割引率の平均」を使います。
見た目は同じ、でも何かが違う
AVG(割引率)>=0.4で色を付けてみる
STR関数で実際の値をのぞいてみる
メジャーフィルターをかけてみる
仮説:
メジャーフィルターは小数点第3位で四捨五入した「後」の数値でフィルターをかけているが、アドホック計算(色の部分)のAVG(割引率)>=0.4は、丸めていない浮動小数点数として判断している
計算フィールドで判定させる
仮説:
デフォルトのメジャーフィルターと、計算フィールドの結果は異なる。計算フィールドの場合は、小数点以下を厳密に扱っているように思われる。
ROUND関数
(ADVANCE)%の整数部分を取り出す
ここでは、ROUNDの小数点以下桁数を4桁に設定していますが、ケースによっては4桁では足りない可能性があります。その場合はこの桁数を増やす対応になるのだと思います。
まとめ
浮動小数点数は、直接扱う場合、変数に格納した場合、でも挙動が変わるように、プログラムでも気を遣う部分です。
実務だと、時間計算するような場合、シリアル値の比較計算を行ったりするので想定外の挙動が出てくる箇所でもあります。
サンプルスーパーストアの割引率の平均、を扱う場合は何となくできそうな感じでしたが、実際には、境界値付近のデータを正しく判定できているかを検証しながら、個別具体的に処理を進めていくのだと思います。
noteにまとめる前は、STR(AVG([割引率]))をSPLITして、整数部分と小数部分に分け、小数部分をぐりぐり計算、などしてました。(↓こんな感じ笑)
INT(
SPLIT(
STR(
ROUND(
(
INT(
//処理につかう桁数
LEFT(
//小数点部分
SPLIT(STR(AVG([割引率])),".",2)
+
//桁数合わせるために0詰め
LEFT("00000000000000000000",20-
LEN(SPLIT(STR(AVG([割引率])),".",2))
),15)
)
//+1することで0.99999999を1.00000に変換
+1
)
//小数点以下に戻す
//処理に使う桁数と連動
/10^13//15=15-2=13
//小数点を保持しないと過剰な繰り上がりになる
,15)
)
//整数部分を取り出す
,".",1)
)
ただ、順を追って検証していくと、しかるべき箇所でROUNDを使うことでかなりシンプルに処理できそうな気がしています。
皆さんも小数点以下を扱う場合は注意してくださいね!
ちなみに、実務だと、10^nをかけて整数にして、判定後に小数点以下に戻す、といったことをやったりもします(Excelだと頻出)
もっと簡単にできるよ、とか、これだとまだ不十分、などご意見ある方は是非、コメントお願いします!