SKY BLOGBLOG

近似直線をSalesforceで実装する

みなさん、こんにちは🖐

今月ブログを担当させていただきます、アプリケーション部小出でございます。

この前、「小樽」から「札幌」まで自転車で帰ってきました🚴💨

天気が良かったのですが、太陽の光、向かい風、峠越えと大変でした。

自転車に乗る目的の一つに『自信を取り戻す』という目的もあります。

仕事でも失敗すると、自信をなくすことも多いです。

でも、自分が少し背伸びすることで、できる小さな大きなことを考えて、

試して、できたときの達成感は大きいです。

自転車で走行することで、景色を眺めたり、空気を感じたりするのも、心の面でも良いと思ってます。

さて、ブログのネタ、いくつかあったのですが、

今回はこちらにしたいと思います。

「近似直線をSalesforceで実装する📈

数学です。苦手ですよね。嫌ですね。でも、やります。

この仕事をしていると、時々数学が出てきます。(ド・モルガンとか)

とあるサービスの2月と3月の「日別件数」と「累計」のグラフ📈です。

が「日ごとの件数」、折れ線が「累計件数」とします。

どちらが忙しかった」をわかる方法はないでしょうか。

  • 3月のほうが日別件数の総件数が多い
  • 3月は、一日当たり10件以上の日が2日間あった

と言うところから、3月と言うのはわかるのですが、

何か別の視覚的なもので表現、確認できないでしょうか。

色々考えてみた結果、

「近似直線」を利用してみると判るのでは、と考えました。

近似直線の勾配で図れそうです。(紫の線

3月は1日は、0近辺からスタートしてますが、

2月は1日から少し高さがある状態からスタートしていることが判ります。

※2月は1日から忙しかった、でも3月は勾配がきついので全体通じると忙しかった、と言えるのかな。

複数月分のデータを集めて、角度を比較してみるのも面白いです。

※2月と4月ではお問い合わせ数は同じくらいですが、勾配が違いますね。

線形近似、ExcelやSpreadSheetのグラフ機能には、近似直線(トレンドライン)を描画する機能があります。

Salesforceのグラフにはその機能がありません。

実装するしかありません・・・。

どのような計算をすれば良いのか、調べました。

y = ax + b
a = (ΣXiYi - nX'Y') / (ΣXi^2 - nX'^2)
b = Y'-aX'
(i=1,2,3・・・)

だそうです。

判らないので、判りやすくします。

y = ax + b
a = (X×Yの合計 - 標本数 × Xの平均 × Yの平均) ÷ (Xを二乗した合計 - 標本数 × Xの平均の二乗)
b = b = (Yの平均 - a × Xの平均)

まだ、よくわかりにくいです。

簡単な数字で試してみましょう。

 

以下は、SpreadSheet で作ったグラフです。紫の線は、SpreadSheetの機能を使った近似直線です。

このようなグラフになります。

  • 標本数は、x と y の組み合わせ数=4
  • x の平均
    • (10 + 12 + 14 + 16 ) ÷ 4 = 13
  • y の平均
    • (6 + 9 + 10 + 10) ÷ 4 = 8.75
  • x の 2乗 と x × y の合計

  • x の 2乗の合計(ΣXi^2) = 696
  • x × y の合計(ΣXiYi) = 468

式に当てはめます

a = (X×Yの合計 - 標本数 × Xの平均 × Yの平均) ÷ (Xを二乗した合計 - 標本数 × Xの平均の二乗)
a = (468 - 4 × 13 × 8.75) ÷ (696 - 4 × 13 × 13)
a = 0.65b = (Yの平均 - a × Xの平均)
b = (8.75 - 0.65 × 13)
b = 0.3

つまり

y = ax + b
y = 0.65 × x + 0.3

となります。

例に当てはめると以下のようになります。

赤線が今回計算して算出した線グラフ

紫の線の棒グラフに対する、近似直線(トレンドライン)です。

赤線紫の線なっています。

 

また、Excelにて、同様の計算でグラフを作ってみる(右のグラフ)と、

右のグラフのピンクの線と左のグラフのの線も一致しているようなので、

計算上間違いなさそうです。

 

次にApexクラスとVisualforceページのコードを添付しておきます。

■近似直線計算クラス

ApproximationStraightLine.cls
 

publicclassApproximationStraightLine{
    publicDoublex{get;set;}
    publicDoubley{get;set;}
    publicDoubleay{get;set;}

    publicApproximationStraightLine(Doublex,Doubley){
        this.x=x;
        this.y=y;
        this.ay=0;
    }

    publicstaticList<ApproximationStraightLine>calcurate(List<ApproximationStraightLine>listData){
        DoublenumberOfSamples=numberOfSamples(listData);
        DoubleaverageX        =getAverageX(listData);
        DoubleaverageY        =getAverageY(listData);
        DoublesigmaXiYi       =getSigmaXiYi(listData);
        DoublesigmaXiPow2     =getSigmaXiPow2(listData);

        // a = (ΣXiYi-標本数×Xの平均×Yの平均)÷(ΣXi^2-標本数×Xの平均の二乗)
        Doublea=(sigmaXiYi-numberOfSamples*averageX*averageY)/(sigmaXiPow2-numberOfSamples*Math.pow(averageX,2));

        // b = (Yの平均-a×Xの平均)
        Doubleb=averageY-a*averageX;

        for(ApproximationStraightLinedata:listData){
            // y = xa + b
            data.ay=data.x*a+b;
        }

        returnlistData;
    }

    privatestaticDoublenumberOfSamples(List<ApproximationStraightLine>listData){
        Doublesize=(Double)listData.size();
        returnsize;
    }

    privatestaticDoublegetAverageX(List<ApproximationStraightLine>listData){
        Doubletotal=0;
        Integercount=0;

        for(ApproximationStraightLinedata:listData){
            total+=data.x;
            count++;
        }

        returntotal/count;
    }

    privatestaticDoublegetAverageY(List<ApproximationStraightLine>listData){
        Doubletotal=0;
        Integercount=0;

        for(ApproximationStraightLinedata:listData){
            total+=data.y;
            count++;
        }

        returntotal/count;
    }

    privatestaticDoublegetSigmaXiYi(List<ApproximationStraightLine>listData){
        Doublevalue=0;

        for(ApproximationStraightLinedata:listData){
            value+=(data.x*data.y);
        }

        returnvalue;
    }

    privatestaticDoublegetSigmaXiPow2(List<ApproximationStraightLine>listData){
        Doublevalue=0;

        for(ApproximationStraightLinedata:listData){
            value+=Math.pow(data.x,2);
        }

        returnvalue;
    }

}

■Visualforceページのコントロールクラス

ChartTestController.cls
 
publicclassChartTestController{

    publicList<ApproximationStraightLine>getData(){
        List<ApproximationStraightLine>listData=newList<ApproximationStraightLine>();
        listData.add(newApproximationStraightLine(10,6));
        listData.add(newApproximationStraightLine(12,9));
        listData.add(newApproximationStraightLine(14,10));
        listData.add(newApproximationStraightLine(16,10));

        returnApproximationStraightLine.calcurate(listData);
    }
}

■Visualforceページ

ChartTest.vfpage
<apex:pagecontroller="ChartTestController">
    <apex:chartheight="400"width="700"data="{!data}">
        <apex:axistype="Numeric"position="left"fields="y"  title="Y"grid="true"minimum="0"maximum="12"/>
        <apex:axistype="Category"position="bottom"fields="x"  title="X"/>

        <apex:barSeriesorientation="vertical"axis="left"  xField="x"yField="y">
            <apex:chartTipsheight="20"width="120"/>
        </apex:barSeries>
        <apex:lineSeriesaxis="left"xField="x"yField="ay"  markerType="circle"markerSize="4"markerFill="#00FF00"/>
    </apex:chart>
</apex:page>



実行した結果が以下になります。近似直線を実装できました。